Compare commits

..
4832 Commits
Author SHA1 Message Date
Lauri Ojansivu 58ad81e301 continuousBackup integration suite: stop each engine before its directory is removed.
Three tests registered the hook that removes their temp directory before the hook
that stops their engine. node:test runs after-hooks in the order they were
registered, not last-in first-out, so the directory was gone when stop() sealed
into it, and the ENOENT (files/index.ndjson) surfaced after the test as
"generated asynchronous activity after the test ended" - which failed the whole
suite although all 8 tests passed. One hook now stops the engine first and then
removes the directory. Five runs in a row, and the suite runner, pass.

Thanks to xet7 !
2026-11-04 19:20:58 +02:00
Lauri Ojansivu 2912f4b080 Close #6870: attachments are already off site-wide in Admin Panel / Attachments / Transfer limits.
How: Admin Panel / Attachments / Transfer limits, set "Attachments upload" and
"API upload" to Blocked. That is the site-wide "Disable attachments" the issue
asks for, as two settings instead of one checkbox.

Comment for the issue:

  A site-wide switch for this already exists, so a new "Disable attachments"
  setting is not needed. Open Admin Panel / Attachments / Transfer limits and set
  "Attachments upload" and "API upload" to Blocked. While they are on, the server
  refuses an upload on every path - the upload pipeline, the api.attachment.upload
  and api.attachment.copy methods, and the /api/attachment upload,
  upload-background and copy routes - on every board, including boards created
  later, and no board's allowsAttachments flag has to be touched. Download, move
  and delete are not affected, so files uploaded earlier stay reachable. What was
  missing is that a card still showed the "+" button and accepted a drop for an
  upload that would then be refused; since d8b3579117 the card asks the server
  once a minute and hides both. There is no second setting, so nothing can
  disagree with what the server enforces. Two settings instead of one checkbox
  is the one difference from the request.

Thanks to Raymond-Voogt and xet7 !

Fixes #6870,
2026-11-04 19:17:53 +02:00
Lauri Ojansivu 97d1eca09d CHANGELOG: In short for the upcoming release. 2026-11-04 19:12:14 +02:00
Lauri Ojansivu 95b75177b8 CHANGELOG: continuous backup runs on one server at a time, with a lease. 2026-11-04 19:12:06 +02:00
Lauri Ojansivu 880ec2d707 Continuous backup runs on one server at a time, with a lease.
With several WeKan servers on one database, every server applied the continuous
backup setting, so each of them tailed the oplog and wrote its own archives into
the same target directory (#6861, item 3).

The leader holds a lease: one continuousBackupLease document with its id and an
expiry. Taking it is one atomic operation (an insert while there is none, else
an update that matches only an unheld, own or expired lease), the leader renews
it every 15 seconds and it lapses after 45, so another server takes over within
a minute when the leader stops. A server that loses the lease stops its stream.
Startup, saving the settings and a change saved on another server all converge
through that loop, and a restore or a fetch from the cloud takes the lease and
waits one renewal so it is the only writer. One server is always its own leader.

Checked against the real driver on FerretDB (SQLite): 24 clients at once give
exactly one leader; renewal, expiry, loss of the old leader and release behave.
tests/continuousBackupLease.test.cjs pins the same and the wiring in
server/continuousBackup.js. docs/Backup/Continuous-Backup.md no longer says
single-server only.

Thanks to Raymond-Voogt and xet7 !

Refs #6861, not closed: the remaining items of the issue were fixed in 4e28465964.
2026-11-04 19:11:32 +02:00
Lauri Ojansivu dbc7cccea3 CHANGELOG: a card no longer offers the attachment plus when uploads are blocked for the whole site. 2026-11-04 19:07:11 +02:00
Lauri Ojansivu d8b3579117 A card no longer offers the attachment "+" when uploads are blocked for the whole site.
#6870 asks for a site-wide switch that turns attachments off, because the
per-board allowsAttachments flag has to be set on every board. That switch is
there already: Admin Panel / Attachments / Transfer limits, "Attachments upload"
and "API upload" set to Blocked, and the server refuses on every path - the
upload pipeline, api.attachment.upload and .copy, and the /api/attachment
upload, upload-background and copy routes. What was missing is that the card
still showed the "+" button and accepted a drop for an upload that would be
refused.

attachmentUploadPolicy tells a signed-in client the one boolean (nothing about
the limits), asked at most once a minute, and the "+" and the drop/paste upload
follow it. No second setting is added, so there is nothing that could disagree
with what the server enforces. tests/attachmentUploadPolicy.test.cjs pins the
client, the method, and the server enforcement it relies on, and that no
"disableAttachments" setting exists.

Thanks to Raymond-Voogt and xet7 !

Refs #6870, not closed: the reporter asked for a single Features checkbox, and the
two Blocked limits are two settings.
2026-11-04 19:07:09 +02:00
Lauri Ojansivu c7e53e9f38 CHANGELOG: an integration update for a missing or wrong-board integration returns 404. 2026-11-04 19:04:14 +02:00
Lauri Ojansivu 6012521ccc An integration update for a missing or wrong-board integration returns 404.
PUT /api/boards/:boardId/integrations/:intId answered 200 with the requested id
when no integration matched that id and board (PR #6871). The two activities
routes (POST and DELETE .../activities) had the same fault: they updated
nothing and answered 200 with `data: null`. All three now look the integration
up on that board after board-admin authorization and answer the JSON 404 the GET
route already uses (#6862), before validating or writing anything.

tests/restIntegrationUpdate.test.cjs and the Playwright spec are the PR's.
tests/restIntegrationWriteRoutes404.test.cjs adds the activities routes and
pins, for the whole file, that every handler that updates an integration looks
it up first and only after authorization.

Thanks to AhmedLukman and xet7 !
2026-11-04 19:04:12 +02:00
Lauri Ojansivu 0195a5b3d4 Merge remote-tracking branch 'origin/main' 2026-11-04 15:02:36 +02:00
Lauri Ojansivu b12e761afb CHANGELOG: missing-board exports return 404 and an over-long export token 400. 2026-11-04 14:50:49 +02:00
Lauri Ojansivu 0c0d666ffa Missing-board exports return 404, and an over-long export token returns 400.
The board Excel export answered a missing board with HTTP 200 and the text
"Board not found" (PR #6869). The two PDF export routes had the same fault, and
the Excel and PDF routes answered an over-long auth token with 200 too, while
export.js already used 404 and 400. They now choose their status the same way.

tests/boardExcelNotFound.test.cjs and the Playwright spec are the PR's.
tests/exportErrorStatuses.test.cjs scans every models/export*.js route for a
text answer without a status, so the fault cannot return anywhere there.

Thanks to AhmedLukman and xet7 !
2026-11-04 14:50:33 +02:00
Lauri Ojansivu fc2ad0a48b CHANGELOG: nodemailer, body-parser, qs, uuid and postcss-selector-parser raised in the bundle. 2026-11-04 14:45:57 +02:00
Lauri Ojansivu dad03959d9 Raise nodemailer, body-parser, qs, uuid and postcss-selector-parser in the bundle.
A Grype scan of the image found advisories in packages that Meteor's own
packages carry in programs/server/npm/node_modules/meteor/<package>, which
package.json cannot reach.

nodemailer 8.0.11 -> 10.3.1: meteor/email only calls createTransport, the
'stream' plugin hook, nodemailer/lib/well-known and nodemailer/lib/mail-composer,
and all four work in 10.3.1 (checked with the bundle's own package specifiers,
through its exports map). body-parser 1.20.6/1.20.8 -> 1.20.9 and 2.3.0 ->
2.4.0: a minimum may now be "name@MAJOR", so each major is raised inside
itself. qs -> 6.16.1, postcss-selector-parser 6.1.4 -> 6.1.5.

uuid 8.3.2 and 9.0.1 -> 11.1.1: 11 moved its entry point, which is why it was
refused before. The bumper now writes the recorded dist/index.js back as an
ESM re-export of dist/esm/index.js, which require() loads and which leaves
uuid's own ESM and CJS entry points as they are.

glob 14 and minimatch 11 are already in package.json. Run against a copy of
the published image's bundle, the bumper raises all of these and every raised
copy loads.

Thanks to the Grype scan reporter and xet7 !
2026-11-04 14:45:04 +02:00
github-actions[bot] ac960e7d81 Bump versions for v12.94 2026-11-04 01:07:15 +00:00
Lauri Ojansivu 3213d7fa1d Prepare v12.94 release 2026-11-04 03:06:07 +02:00
Lauri Ojansivu 4757676abf CHANGELOG: on a phone in desktop mode the opened card starts below the header bar. 2026-11-04 03:04:05 +02:00
Lauri Ojansivu efaf94242e On a phone in desktop mode the opened card starts below the header bar.
The header wraps to several rows on a phone, far taller than the 8px the
floating card docks at, so the card's top row sat among the header's rows and
was only seen by dragging. Below 801px a card without an inline top, popup
cards included, starts at --wekan-header-height + 8px; a dragged place is an
inline top and is never overridden.

Thanks to the iPhone 12 mini reporter and xet7 !
2026-11-04 03:03:59 +02:00
Lauri Ojansivu d87baa0244 CHANGELOG: the opened card covers the header bar on an iPad. 2026-11-04 02:56:44 +02:00
Lauri Ojansivu 006d24502c The opened card covers the header bar on an iPad: the header drops to the content's level.
On iPad Chrome and Safari (both WebKit) the opened card's top row - the collapse
caret, the card number, the menu, maximize and X - was behind the header bar,
although the card has z-index 2001 and the header 1000. iOS WebKit makes every
scrolling element (#content, the board wrapper) a stacking context of its own, so
the card's z-index is compared only inside it while the header bar, a flex item of
body and a stacking context too, is painted over all of it. While a card is open
the header bar (#header-quick-access) is now at z-index 0, the level of the
content, which comes after it in the page and so paints over it; with no card open
it keeps 1000.

A browser spec makes #content and the board wrapper stacking contexts with
isolation: isolate, which is the same trap, and shows the card over the header open
and collapsed, its close button reachable, in Chromium and WebKit; without the rule
it fails with the card's top at 8 behind a header 88 high, as in the screenshot.

Thanks to xet7 !
2026-11-04 02:56:33 +02:00
Lauri Ojansivu 8f4efbca57 Merge remote-tracking branch 'origin/main' 2026-11-04 01:35:33 +02:00
Lauri Ojansivu dde09dead5 CHANGELOG: where the time goes when a WeKan board is opened. 2026-11-04 01:21:48 +02:00
Lauri Ojansivu 3621413d6f Speed test: where the time goes when a WeKan board is opened, on FerretDB and on MongoDB.
app/board-open.cjs seeds a board of 200, 2000 or 5000 cards, opens it in Chromium and
records the wall clock to the first card and until the DDP WebSocket is quiet, what
crossed it, the Meteor server's CPU by who spends it (Node inspector) and FerretDB's
CPU (pprof). Two runs on each database: the Meteor server's CPU is the same on both
(0.1-0.5 s) and the DDP server is 1% of it; a 5000-card board takes FerretDB (SQLite)
400-600 ms longer to open, the data sent to the browser being the same ~210 KB, a
small board 70-100 ms. FerretDB uses about 4 s of CPU for the 5000-card open: SQLite
39%, filtering in Go 34%, find/getMore/aggregate 0.7-0.8 s each. The README says what
was measured and what was not: the development build, one machine, the browser's own
time not separated, and the aggregate's source not identified.

Thanks to Raymond-Voogt and xet7 !
2026-11-04 01:21:16 +02:00
github-actions[bot] 3c3fc07b3c Bump versions for v12.93 2026-11-03 23:04:07 +00:00
Lauri Ojansivu c1e8976b28 Prepare v12.93 release 2026-11-04 01:02:51 +02:00
Lauri Ojansivu dcce935310 CHANGELOG: the opened card and popups move under a finger. 2026-11-04 01:00:21 +02:00
Lauri Ojansivu 1b05673200 The opened card and every popup move under a finger: Pointer Events and touch-action.
On an iPad the opened card did not move when dragged by a finger from the collapse
caret, the space beside it, the card number, or the drag handle, while the page
behind it scrolled. The card was moved by mousedown / mousemove / mouseup, and a
finger sends none of them while it moves; the drag areas also lacked
touch-action: none, so the browser took the gesture for a scroll. The card window
is now moved by Pointer Events: a press on the header row or the handle moves it, a
press on a link or button in the row (the caret, close, the menu) waits for a real
drag so a tap still does its job and a drag does not also collapse or close, the
title still edits, and with drag handles on the handle alone moves the window. The
card number is above the title's edit zone so it drags too, the window is kept
within reach, and touch-action: none is on the card header, its handle and every
popup's title bar and handle. Blaze hands over a jQuery event without isPrimary, so
the native event is read.

A browser spec drags with a real touch (DevTools protocol, Pointer Events) from the
caret, the number and the space beside the title, with handles on and off, a tap
that still collapses, a mouse that still drags and a popup; without the fix the
finger tests fail.

Thanks to xet7 !
2026-11-04 01:00:19 +02:00
Lauri Ojansivu dafb63299f CHANGELOG: a popup card covers the header bars. 2026-11-04 00:42:59 +02:00
Lauri Ojansivu 84b0480aa8 An opened card opened as a popup covers the header bars, in every browser.
A card opened as a popup (a search result, a table row, a Roadmap/Gantt bar) got
the generic wide-layout rule top: 97px and z-index 30, because the desktop-mode
rules that dock an opened card at the top (top: 8px, z-index 2001) skip a popup.
So it opened BELOW the header, and collapsed it was a bar floating over the
swimlane, while the same file's popup wrapper rule says it must cover the header
bars. The popup card now docks at top: 8px with z-index 2001 in the wide layout;
mobile mode keeps its own full-window rule. A browser spec shows the card over
the header, open and collapsed, in Chromium and WebKit, and fails without the fix
(top 97 against a header 88 high).

Thanks to xet7 !
2026-11-04 00:42:57 +02:00
Lauri Ojansivu f6fc97e77a The full screen button says Minimize while the page is in full screen.
The same button leaves full screen; its title and name were still Maximize, which
does not say so. The existing Minimize translation is used, so no language file
changes.
2026-11-04 00:29:00 +02:00
Lauri Ojansivu d7105e531d CHANGELOG: a full screen button in the header bar. 2026-11-04 00:27:30 +02:00
Lauri Ojansivu f25957dcf3 A full screen button in the header bar, to the right of the logo.
It puts the whole page in full screen with the Fullscreen API (the unprefixed one
and the webkit-prefixed one) and takes it out again; the icon follows the
browser's own events, so Esc is understood. The label is the existing Maximize
translation, so no language file changes. It is drawn only where the browser
offers the API and the page may use it: not on iPhone, not in an iframe without
allowfullscreen. Unit tests drive the pure part with fake documents, including a
refusal; a browser spec checks the position, the label, entering, leaving, Esc,
and that nothing is drawn where the API is not enabled, in Chromium and WebKit.

Thanks to xet7 !
2026-11-04 00:27:28 +02:00
Lauri Ojansivu 984ea93aa8 CHANGELOG: Dependabot alerts 180 and 183-187, and the SQLite membership test fix. 2026-11-04 00:13:58 +02:00
Lauri Ojansivu 2441563008 Speed test: SQLite after the membership test fix (68.3 ms, 4.1x), the Due Cards profile and what was tried.
Thanks to Raymond-Voogt and xet7 !
2026-11-04 00:13:34 +02:00
Lauri Ojansivu f3950b9adc Dependabot alerts 180 and 183-187: minimatch in the other lockfiles, and the Go modules of the embedded benchmark.
minimatch before 11.0.1 (GHSA-g3hv-75wg-m249): npm-packages/exceljs still had
10.2.6 through readdir-glob 3, and the vendored jade lockfile under
meteor-jade-loader had three copies through glob 13 and test-exclude; both get
an override to minimatch 11, as the root lockfile has. A test now reads every
package-lock.json of the repository, not only the reported ones.

The benchmark module of embedded engines pulled klauspost/compress 1.18.0,
golang.org/x/net 0.23.0 and golang/glog 1.0.0 (GHSA-259r-337f-4rfw,
GHSA-5cv4-jp36-h3mw, GHSA-qxp5-gwg8-xv66, GHSA-vvgc-356p-c3xw,
GHSA-6wxm-mpqj-6jpf). They are raised to 1.18.7, 0.62.0 and 1.2.4, and
govulncheck then found more in x/net, x/text and OpenTelemetry, which are raised
too (x/net 0.62.0, x/text 0.43.0, otel 1.42.0): it now reports none. A test
holds every go.mod of the repository at or above these floors.

Thanks to dependabot and xet7 !
2026-11-04 00:12:45 +02:00
Lauri Ojansivu 5e07b8db6c Docs: the speed test, version history and embedded engines READMEs link directories, not README.md files. 2026-11-03 23:59:03 +02:00
Lauri Ojansivu eeeb0dbec9 CHANGELOG: Go databases compared with FerretDB's SQLite. 2026-11-03 23:47:33 +02:00
Lauri Ojansivu 96d56ecc6c Go databases that could be linked into FerretDB, against its SQLite: none is faster where it counts.
A benchmark of its own (docs/Databases/FerretDB/1/SpeedTest/embedded, a Go module,
no cgo) loads the speed test's cards into SQLite set up as FerretDB sets it and
into Pebble, Badger, bbolt, BuntDB, Nutsdb and Clover, and asks each the six
questions WeKan asks of cards, at 15,377 and 153,768 cards. A board's cards take
the same time on every engine that is not slower, because decoding the JSON
dominates; the key-value stores win single reads and writes by microseconds that
a FerretDB call hides; Pebble is 12x slower on fetching a board, Clover 90-190x
slower in total, and bbolt with its default fsync 100-160x slower on writes. The
README accounts for every entry of the awesome-go section.

Thanks to Raymond-Voogt and xet7 !
2026-11-03 23:47:13 +02:00
Lauri Ojansivu aadfd9b6b3 CHANGELOG: Dolt in the speed test and the version history experiment. 2026-11-03 23:19:00 +02:00
Lauri Ojansivu 1c44876beb Speed test: Dolt, and what a database with version history would give WeKan's undo and redo.
Dolt runs under the existing MySQL handler with no new handler and gives the same
answers, but is by far the slowest: 20,470 ms in total, every card query a
2.8 s whole-table scan. The VersionHistory scripts drive WeKan-shaped changes
through FerretDB onto Dolt (a commit per write, undo and redo with DOLT_REVERT,
AS OF, +0.4 ms a write, a conflict when the same document is restored again) and
through the REST API of TerminusDB (author and message per commit, field-level
restore). ClickHouse and StarRocks were tried with the MySQL handler and need a
handler of their own; the README says where each fails.

Thanks to Raymond-Voogt and xet7 !
2026-11-03 23:18:51 +02:00
Lauri Ojansivu bd86b518ea CHANGELOG and tests: the small-scale speed test, pgrust, TiDB and the databases that cannot run FerretDB. 2026-11-03 22:47:16 +02:00
Lauri Ojansivu f576a0cca9 Speed test: TiDB added, and the README says what was tried: pgrust, TiDB, Noria, YugabyteDB, CockroachDB.
TiDB (MySQL protocol, Apache-2.0) runs FerretDB after its metadata table's
generated columns became VIRTUAL, and gives the same answers as MongoDB; it is
a speed test row and opt-in for the conformance run (WEKAN_CONFORMANCE_TIDB=1).
Noria has no JSON type, triggers or generated columns, which the MySQL handler
needs; YugabyteDB refuses a CREATE INDEX on a jsonb expression, which every
FerretDB v1 PostgreSQL index is; CockroachDB is not open source. The README
also records that analyzing a PostgreSQL table after its indexes are created
took the PostgreSQL row from 454 to 145 ms, and that the same on MySQL and
MariaDB did nothing.

Thanks to Raymond-Voogt and xet7 !
2026-11-03 22:45:56 +02:00
Lauri Ojansivu 46c0ad43e8 Speed test: small-scale results ordered fastest to slowest, with times slower, and pgrust.
Results.md now opens with an At a glance table (rank, total, how many times
slower than the fastest, the same for the slowest user), every table is ordered
by its own numbers, the header says the real data size and the real operating
system, and the load table is ordered by the whole load time. pgrust
(PostgreSQL in Rust, malisper/pgrust:v0.4-beta) is one more row through the
same PostgreSQL handler; in the conformance run it is opt-in with
WEKAN_CONFORMANCE_PGRUST=1. The run is 1/20 of a production WeKan so it takes
minutes.

Thanks to Raymond-Voogt and xet7 !
2026-11-03 22:36:56 +02:00
Lauri Ojansivu 499772e4c2 Speed test README: sorting in PostgreSQL was tried and was not faster.
The ten newest of 30,116 users took 52 ms with an ORDER BY in the database and
38-49 ms decoded and sorted in Go, so the change was not kept; the README says
what was measured so it is not repeated.

Thanks to Raymond-Voogt and xet7 !
2026-11-03 21:53:31 +02:00
Lauri Ojansivu 90fe133363 Merge remote-tracking branch 'origin/main' 2026-11-03 21:37:36 +02:00
Lauri Ojansivu b812d36871 CHANGELOG: the speed test README. 2026-11-03 21:24:06 +02:00
Lauri Ojansivu d05a42a5af Speed test README: the FerretDB v1.100-v1.105 and WeKan improvements since the last run.
SQLite sorts and stores JSONB, PostgreSQL uses a GIN index for membership and
pushes down $or, $elemMatch and $and, MySQL and MariaDB read through their
indexes with multikey side tables, and WeKan stopped scanning whole collections
every hour or at every start. Results.md still shows v1.99.0, which the README
now says, and the open items are updated to match.

Thanks to Raymond-Voogt and xet7 !
2026-11-03 21:24:03 +02:00
github-actions[bot] 42507de1d0 Bump versions for v12.92 2026-11-03 18:51:08 +00:00
Lauri Ojansivu 131b0cc509 Prepare v12.92 release 2026-11-03 20:48:47 +02:00
Lauri Ojansivu 93ae5ac6ee Merge remote-tracking branch 'origin/main' 2026-11-03 20:46:48 +02:00
Lauri Ojansivu 34bb91d1f5 CHANGELOG: a missing REST checklist answers a JSON 404. 2026-11-03 20:43:21 +02:00
Lauri Ojansivu d231df56fc REST API: a missing checklist, or one under another card, answers a JSON 404, not an empty 500.
GET /api/boards/:boardId/cards/:cardId/checklists/:checklistId answered 500 with
no body when the checklist did not exist or belonged to another card. It now
answers 404 with the same error text the checklist update and delete routes
use; board access and the parent-card check are unchanged. Route tests drive
the registered handler (found, missing, other card, other board, denied, lookup
failure) and a browser spec reads the checklist over REST and after a reload.

Thanks to AhmedLukman and xet7 !
2026-11-03 20:43:06 +02:00
github-actions[bot] e422ab691e Bump versions for v12.91 2026-11-03 18:01:08 +00:00
Lauri Ojansivu 3f712a0445 Prepare v12.91 release 2026-11-03 19:59:16 +02:00
Lauri Ojansivu d99dadadee Merge remote-tracking branch 'origin/main' 2026-11-03 19:42:04 +02:00
Lauri Ojansivu a58b7bee2c CHANGELOG: code scanning alert 600, a test replacing getLabel with itself. 2026-11-03 19:17:12 +02:00
Lauri Ojansivu 62da564a1e Code scanning #600: a test no longer replaces getLabel with itself, and the guard knows anchored patterns.
tests/boardLabelLookup.test.cjs sliced the getLabel method out of models/boards.js
and then ran .replace(/^getLabel/, 'getLabel') on it, which does nothing: the
slice already starts at getLabel. tests/noIdentityReplacement.test.cjs skipped
every regular expression with an anchor, so it could not see it; a leading ^ or
a trailing $ over plain characters is now compared like any other.

Thanks to GitHub CodeQL and xet7 !
2026-11-03 19:16:44 +02:00
github-actions[bot] 2ada2e2d61 Bump versions for v12.90 2026-11-03 15:58:18 +00:00
Lauri Ojansivu 5de1acf496 Prepare v12.90 release 2026-11-03 17:57:04 +02:00
Lauri Ojansivu 3008bb5606 Merge remote-tracking branch 'origin/main' 2026-11-03 17:55:41 +02:00
Lauri Ojansivu 4f7521ae3c Tigre: the login page background label uses the Tigre word for color, not the Tigrinya one.
Thanks to Raymond-Voogt and xet7 !
2026-11-03 17:31:40 +02:00
Lauri Ojansivu 1c63049913 CHANGELOG: login page background color and a readable support page. 2026-11-03 17:20:07 +02:00
Lauri Ojansivu 6b27764452 Admin Panel: a login page background colour, and a readable layout for the login text and the support page.
Lay-out has a new Login page background color (loginBackgroundColor). Only a
hex colour is accepted, so the value in the CSS custom property can never carry
CSS; the text under the login logo turns white on a dark colour and black on a
light one. The colour is on body only while the sign-in layout shows. The
support page and the text under the login logo get a centred column with sized,
bold headings and space between paragraphs. The label is translated into every
language file.

Thanks to Raymond-Voogt and xet7 !

Fixes #6866,
2026-11-03 17:19:22 +02:00
Lauri Ojansivu ae49937b44 Tests: the Playwright specs expect the accessibility-audit colours and drop below the last checklist row.
The belize and nephritis buttons, the limegreen header, the red-string and
typed-relation lines are now the darker shades that keep their contrast, and
the checklist drag released on the sortable's own bottom edge.
2026-11-03 16:48:44 +02:00
Lauri Ojansivu 7f27ea1f26 CHANGELOG: Show focus outline, and Escape closes the add-list composer. 2026-11-03 16:04:58 +02:00
Lauri Ojansivu 144777ae6b Member Settings: a per-member switch to hide the focus outline.
Change Settings has Show focus outline, on by default: the blue ring round the
focused control is an accessibility feature (WCAG 2.4.7), and it also shows
after a click on a minicard. A member who turns it off - profile.hideFocusOutline,
a toggle method that takes the signed-in user only - gets a class on body that
hides the outline and its halo for them and nobody else. The label is translated
into every language file (best effort; the low-confidence ones are listed in the
changelog).

Thanks to mimZD and xet7 !

Refs #6755,
2026-11-03 16:04:44 +02:00
Lauri Ojansivu f4b5025a7b Escape closes the inline add-list composer.
The composer opened after a list (or in an empty swimlane) stays open after a
list is created, so several can be added, and only its X closed it. Escape now
closes it at the level of an inlined form, with the same reset as the X, and not
on a click elsewhere.

Thanks to mimZD and xet7 !

Fixes #6865,
2026-11-03 15:39:24 +02:00
github-actions[bot] cdcf8ec39d Bump versions for v12.89 2026-11-03 13:35:18 +00:00
Lauri Ojansivu 739f9ed7a0 Prepare v12.89 release 2026-11-03 15:33:40 +02:00
Lauri Ojansivu f06d082e34 Merge remote-tracking branch 'origin/main' 2026-11-03 15:33:13 +02:00
Lauri Ojansivu 4036b16774 CHANGELOG: shortcuts run once on a non-Latin keyboard. 2026-11-03 15:30:41 +02:00
Lauri Ojansivu 0110e84ce5 Keyboard shortcuts run once on a non-Latin layout.
On a Farsi (or Cyrillic) keyboard the d shortcut opened the Due date popup and
closed it at a glance. keyboard.js re-dispatched every key typed on another
script as its Latin key, a leftover from Mousetrap; hotkeys-js resolves the key
from event.code itself, so the shortcut ran twice and the repeat clicked the
same control again, closing it. The re-dispatch is removed, and a test drives
real key events for Latin, Farsi and Cyrillic at the library.

Thanks to mimZD and xet7 !

Fixes #6840,
2026-11-03 15:30:39 +02:00
github-actions[bot] 00a50ae139 Bump versions for v12.88 2026-11-03 12:58:20 +00:00
Lauri Ojansivu c67a67bc9a Prepare v12.88 release 2026-11-03 14:57:20 +02:00
Lauri Ojansivu 0b8a7face3 CHANGELOG: every theme and chosen color is readable; padded REST labels. 2026-11-03 12:37:32 +02:00
Lauri Ojansivu 8b2114cd0f Themes: every board theme and every color a member chooses is readable.
Eight themes (nephritis, pumpkin, moderatepink, strongcyan, limegreen, relax,
corteza, modern) had white text on an accent at 2.1 to 4.3:1: their accents and
hover, selected and emphasis shades are darkened until it reads at 4.5:1. The
filter sidebar's headings, the clear themes' quiet text and cleanlight's
add-dependency link are darker. A color a member chooses is made legible: a
custom theme accent or slide end is darkened under the header's white text, the
text color is moved to read on the theme's surfaces and no longer reaches the
header, labels and primary buttons, a dependency line is drawn at 3:1, Kanri's
tag text is the better of dark and light, and a custom label, card, list or
swimlane color already derives its text. All 25 themes show no colour contrast
failure on the board, the sidebars, Card Settings, the card and its popups.

Thanks to Samunosuke and xet7 !
2026-11-03 12:37:10 +02:00
Lauri Ojansivu 0426c7741a Board labels: a padded name finds the stored label, in every way a label is added.
A label is looked up by name and color before one is added, and the lookup
compared the name as sent while the schema stores it trimmed: a padded ' Bug '
missed the stored 'Bug', and each repeat created another label with the same
name and color. The REST builder now trims, as the pull request did, and so
does the lookup itself, which the card's label form, the importers and the list
sync share. A legacy label stored with padding is still found.

Thanks to AhmedLukman and xet7 !
2026-11-03 12:37:10 +02:00
Lauri Ojansivu c2a28a9233 CHANGELOG: the dark board themes are readable again. 2026-11-03 11:42:09 +02:00
Lauri Ojansivu a6b10e3592 Dark themes: the quiet greys, Card Settings, popups and sidebars are readable again.
The accessibility audit made the quiet text greys dark for every theme, and
old rules never followed the dark themes: Card Settings drew white text on
light grey blocks, popup items, the sidebar's shortcuts row, quiet buttons and
sidebar labels were black on dark, and an !important list header beat each
theme's own. The quiet text is a variable with a light value on the dark
surfaces; Card Settings' sections, popup items and the other rules follow the
theme. axe-core finds no colour contrast failure in cleandark, moderndark, dark
and exodark on the board, sidebar, Card Settings, an opened card and the custom
fields popup.

Thanks to Samunosuke and xet7 !

Fixes #6863,
2026-11-03 11:41:58 +02:00
github-actions[bot] 1ac4d1b1f9 Bump versions for v12.87 2026-11-02 22:34:10 +00:00
Lauri Ojansivu 222492f313 Prepare v12.87 release 2026-11-03 00:33:10 +02:00
Lauri Ojansivu 4f95b10f0d Merge remote-tracking branch 'origin/main' 2026-11-03 00:26:43 +02:00
Lauri Ojansivu 86a18665f0 CHANGELOG: REST single-item GETs answer 404 when nothing is found. 2026-11-03 00:22:26 +02:00
Lauri Ojansivu 1ded34dfdd REST API: a single-item GET that finds nothing answers 404, not 200 with an empty body.
The scoped card GET (/api/boards/:boardId/lists/:listId/cards/:cardId) sent
200 with no body for a missing, wrong-list, wrong-board or archived card. The
list, swimlane, custom field, comment and integration GETs did the same, and
the user GET threw a TypeError for an unknown name. Each now answers 404 after
the board check, so a stranger still learns nothing about which ids exist.

Thanks to AhmedLukman and xet7 !
2026-11-03 00:22:24 +02:00
github-actions[bot] db791e1d40 Bump versions for v12.86 2026-11-02 17:03:48 +00:00
Lauri Ojansivu e8e22dc25e Prepare v12.86 release 2026-11-02 19:02:52 +02:00
Lauri Ojansivu 08d20930de Merge remote-tracking branch 'origin/main' 2026-11-02 18:56:24 +02:00
Lauri Ojansivu 10f244de77 CHANGELOG: daily jobs run once per installation. 2026-11-02 18:49:22 +02:00
Lauri Ojansivu 4e28465964 Daily jobs run once per installation: notification cleanup and the file integrity audit.
The notification cleanup ran at every start and daily on every server, reading
and rewriting the same users 24 times at a rollout; a notification added by
another server between a read and its write was lost. A run now claims a
document per job and day in dailyJobRuns, and the timer looks hourly for an
unclaimed day. The file integrity audit read 'lastRun', checked it, then wrote
it, so two servers could both scan; one atomic update now claims the run.

Continuous backup has no leader: that is documented as single-server, and the
lease it needs is not built, so #6861 item 3 stays open and this commit does
not close it.

Thanks to Raymond-Voogt and xet7 !
2026-11-02 18:49:19 +02:00
github-actions[bot] 2936a682ce Bump versions for v12.85 2026-11-02 15:27:08 +00:00
Lauri Ojansivu ab80aaf9a3 Prepare v12.85 release 2026-11-02 17:26:05 +02:00
Lauri Ojansivu 0fb943b6fb Merge remote-tracking branch 'origin/main' 2026-11-02 17:25:22 +02:00
Lauri Ojansivu 6d8b0c15ec CHANGELOG: a cleared connection pool is a lost connection; the board key backfill uses the key index. 2026-11-02 17:18:17 +02:00
Lauri Ojansivu abc23a7eda Database problems: a cleared connection pool is a lost connection; the board key backfill uses the key index.
collection2 wraps a failed write as 'ValidationError: Failed validation <driver
text>', and the driver's 'Connection pool for <host> was cleared' names a
replica set member that MONGO_URL does not list, so the schema rule matched.
The text is now a lost connection. The board key backfill looked for boards
without a key with an $or including an exists-false branch, a scan of every
board at every start of every server; one $in on the key index finds the same
boards.

Thanks to Raymond-Voogt and xet7 !

Fixes #6860,
2026-11-02 17:17:58 +02:00
Lauri Ojansivu d9aa210012 Accessibility: the results of the fixes in Audit.md, page by page. 2026-11-02 17:17:07 +02:00
Lauri Ojansivu 2e8e1e2ce2 CHANGELOG: the accessibility audit findings are fixed in the default theme. 2026-11-02 16:08:47 +02:00
Lauri Ojansivu c8bcc4817d Accessibility: fix the findings of the axe-core audit in the default theme.
The default theme's blue is #2573a7, so the header's white and #f2f2f2 text
passes WCAG AA; label colours, quiet greys and grey tiles have text that
passes too. The Starred menu, the user menu and initials avatars carry their
visible text in their accessible name. The card's Pomodoro field and the
comment and description editors have a name. A card section heading is an h3
holding a real button with aria-expanded, and its menu and layout toggle sit
beside it. The sign-in form is the main landmark with a level-one title, every
page has a level-one heading, and the shortcuts list is reachable by keyboard.

After the fixes axe-core finds no violation on 22 pages. Spec 45 runs the scan
and tests/accessibilityAuditFixes.test.cjs pins each fault by its shape.

Thanks to xet7 !
2026-11-02 16:08:32 +02:00
Lauri Ojansivu 2dd25c733e Accessibility: the result of an automated axe-core audit of seven pages.
Thanks to xet7 !
2026-11-02 15:03:54 +02:00
github-actions[bot] cb6c6533c5 Bump versions for v12.84 2026-11-02 12:45:02 +00:00
Lauri Ojansivu 292b804474 Prepare v12.84 release 2026-11-02 14:43:46 +02:00
Lauri Ojansivu 623fccf06e Merge remote-tracking branch 'origin/main' 2026-11-02 14:39:48 +02:00
Lauri Ojansivu cfd7ad2e5f Catch two unhandled rejections found by the wider promise lint.
The instance-editing hook looked up the board of an edit without a catch,
and the service worker registration had none.

Thanks to xet7 !
2026-11-02 14:37:12 +02:00
Lauri Ojansivu 762e5a9432 CHANGELOG: npm run lint:promises. 2026-11-02 14:30:59 +02:00
Lauri Ojansivu f1d2a497bc Lint for promises that are not awaited, handled or marked void.
npm run lint:promises runs typescript-eslint's no-floating-promises and
no-misused-promises over models/lib, server/lib and imports/lib, with type
information read from tsconfig.lint.json (nothing is type-checked). The
eleven findings were promises that never reject (their own catch) and now
say so with void, and three checks that used a promise as a boolean.

Thanks to xet7 !
2026-11-02 14:30:49 +02:00
Lauri Ojansivu 2f9c972d46 CHANGELOG: the code-quality findings. 2026-11-02 14:15:17 +02:00
Lauri Ojansivu 582a22c291 Fix GitHub code scanning code-quality findings.
Real faults: a card's original-position description formatted a promise
because it did not await the history; the attachment swipe distances and
two drag-scroll locals were undeclared globals; a file that cannot be read
during a GridFS upload left the upload open; api.py called exit() instead of
sys.exit(); a rules test redefined a property and a security-log test kept
an unreachable branch. Also 31 missing semicolons, unused imports in the
card details, exporters and attachment API, unused Python imports, and
comments on empty except clauses. The list-sync scanner test cut its source
at the last Meteor.startup, which the new index startup of #6855 had moved.

Thanks to GitHub CodeQL and xet7 !
2026-11-02 14:15:02 +02:00
github-actions[bot] d200234535 Bump versions for v12.83 2026-11-02 11:40:29 +00:00
Lauri Ojansivu 8548bae527 Prepare v12.83 release 2026-11-02 13:38:58 +02:00
Lauri Ojansivu d6b2d55c36 Merge remote-tracking branch 'origin/main' 2026-11-02 13:35:16 +02:00
Lauri Ojansivu f7e3533b13 CHANGELOG: LDAP account binding. 2026-11-02 13:32:03 +02:00
Lauri Ojansivu e91c14cd9a LDAP: a reused login name no longer opens the previous owner's account.
An account found by username or e-mail that records another directory
entry's identifier (same attribute, other value) is refused at login and
skipped by the import. An existing account now records its identifier and
its attribute, so LDAP_UNIQUE_IDENTIFIER_FIELD takes effect for it.
LDAP_EMAIL_MATCH_REQUIRE looks users up by username instead of _id, which
never matched. The background sync skips an account whose username matches
several entries instead of binding it to the first.

Thanks to Raymond-Voogt and xet7 !

Fixes #6859,
2026-11-02 13:31:40 +02:00
github-actions[bot] e7439e1d45 Bump versions for v12.82 2026-11-02 11:00:45 +00:00
Lauri Ojansivu 4a34b91ecf Prepare v12.82 release 2026-11-02 12:59:36 +02:00
Lauri Ojansivu d9984d2069 Merge remote-tracking branch 'origin/main' 2026-11-02 12:54:11 +02:00
Lauri Ojansivu e982282954 CHANGELOG: card numbers on a move, a restore and a set number. 2026-11-02 12:42:43 +02:00
Lauri Ojansivu e68938698c Card numbers: a card moved to another board, restored or given a number cannot take one another card has.
#6815 checked the number of every new card, but a card also enters a board's
numbering by an update: a move from the browser, which computed the
destination number from the cards it had loaded (often none, so 1), a restore
from the archive, and a number set by the REST API or an import. A before-update
hook now does what the insert hook does: a taken number is replaced by a fresh
one from the board's counter, a free one is kept and the counter raised past it.

Thanks to Raymond-Voogt and xet7 !

Fixes #6858,
2026-11-02 12:42:33 +02:00
github-actions[bot] 55299a5172 Bump versions for v12.81 2026-11-02 10:22:59 +00:00
Lauri Ojansivu 8bbc6fcda1 Prepare v12.81 release 2026-11-02 12:20:06 +02:00
Lauri Ojansivu 71463fb445 Merge remote-tracking branch 'origin/main' 2026-11-02 12:17:05 +02:00
Lauri Ojansivu b60a1e5a85 CHANGELOG: the database health probe reports a finding once. 2026-11-02 12:15:29 +02:00
Lauri Ojansivu 114e9ecd5a Database health: one server probes the database per period, so a finding is reported once.
Every server read the same MongoDB log and counters and recorded each
finding itself: one collection scan became up to 24 identical rows in Admin
Panel / Problems on 24 replicas. A probe now claims the period in a shared
document, and the state that compares one probe with the next lives there.
A claim that cannot be made at all does not stop the probe.

Thanks to Raymond-Voogt and xet7 !

Fixes #6857,
2026-11-02 12:15:09 +02:00
github-actions[bot] c6739fe452 Bump versions for v12.80 2026-11-02 10:01:31 +00:00
Lauri Ojansivu efd45c651e Prepare v12.80 release 2026-11-02 12:00:25 +02:00
Lauri Ojansivu fdc2c57b8f Merge remote-tracking branch 'origin/main' 2026-11-02 11:52:22 +02:00
Lauri Ojansivu 2c75b95908 CHANGELOG: minimatch 11.0.4 for GHSA-g3hv-75wg-m249. 2026-11-02 11:42:44 +02:00
Lauri Ojansivu a3630d96ce Update minimatch to 11.0.4, GHSA-g3hv-75wg-m249 (Dependabot alerts #180 and #182).
minimatch before 11.0.1 grows its glob parts without bound while it
preprocesses a pattern, then compares them pairwise. archiver (readdir-glob),
@typescript-eslint/typescript-estree and @babel/cli's glob used 10.2.6; they
now use the root minimatch 11.0.4.

Thanks to dependabot and xet7 !
2026-11-02 11:41:52 +02:00
github-actions[bot] db2aafe16c Bump versions for v12.79 2026-11-02 09:35:57 +00:00
Lauri Ojansivu c4b90d08a0 Prepare v12.79 release 2026-11-02 11:34:58 +02:00
Lauri Ojansivu cb5278ddb3 CHANGELOG: a collection-scan row says which query it is. 2026-11-02 11:31:54 +02:00
Lauri Ojansivu bc79de8c57 Database health: a collection scan row names the command, the operators per field and the queryHash.
A row said only which fields a slow query filtered on, which is not enough
to find the query in the code or to run explain(). It now shows the command
(for a slow getMore, the query it continues, instead of 'filter on nothing'),
the operators used on each field - never values - and MongoDB's queryHash.

Thanks to Raymond-Voogt and xet7 !

Fixes #6856,
2026-11-02 11:31:47 +02:00
Lauri Ojansivu 81714cd695 CHANGELOG: the schema re-check and due-date reminders run once. 2026-11-02 11:28:59 +02:00
Lauri Ojansivu d42369e96a Full collection scans run once, not at every start of every server; due-date reminders are sent once.
The startup schema re-check claims a run in _wekan_migration so one server
re-checks while the others skip (a claim older than 2 hours is taken over),
and a run that leaves work unresolved is retried once a day instead of at
every start. The daily due-date reminder scan is claimed per day in
dueReminderRuns, so 24 replicas no longer send 24 reminders per card.
A partial index on lists serves the 15-minute list sync scan, and a sparse
index on boards.dueReminderDays serves the due-date lookup.

Thanks to Raymond-Voogt and xet7 !

Fixes #6855,
2026-11-02 11:28:44 +02:00
Lauri Ojansivu efff590f74 CHANGELOG: the announcement shows on Home and All Boards. 2026-11-02 11:26:12 +02:00
Lauri Ojansivu 64b5762020 Announcement: shown on Home and every All Boards list, not only on a board.
The banner waited for membership of the current board, and Home, Remaining,
Starred, Templates and the other All Boards pages have none. It now shows to
every signed-in user there, and to the members of the open board on a board.
Signed-out visitors still do not see it.

Thanks to titver968 and xet7 !

Fixes #6854,
2026-11-02 11:25:57 +02:00
Lauri Ojansivu c29a59eb95 CHANGELOG: any year by number in the date picker. 2026-11-02 11:17:04 +02:00
Lauri Ojansivu dcaa316a01 Date picker popup: the year is a number field, any year by typing or stepping.
A list of years had to end somewhere. The year is now a whole number
(0 is 1 BC) found by jumping through the calendar in use, so thousands of
years back or ahead are one entry away, up to the range a date holds.

Thanks to brlin-tw and xet7 !

Fixes #6853,
2026-11-02 11:17:02 +02:00
Lauri Ojansivu 7721c5f655 CHANGELOG: year, month and day drop-downs in the date picker. 2026-11-02 11:10:08 +02:00
Lauri Ojansivu 492895200b Date picker popup: year, month and day drop-downs under the calendar.
The heading above the calendar shows the month and year only, not the
YYYY-MM-DD of the selected day. Under the calendar, year, month and day
drop-downs pick any past, present or future date, in every calendar
system.

Thanks to brlin-tw and xet7 !

Fixes #6853,
2026-11-02 11:09:34 +02:00
Lauri Ojansivu 1b346dc465 CHANGELOG: the date picker heading names the year. 2026-11-02 10:50:22 +02:00
Lauri Ojansivu 45c291b406 Date picker popup: the heading names the year as well as the month.
Thanks to brlin-tw and xet7 !

Fixes #6853,
2026-11-02 10:50:13 +02:00
Lauri Ojansivu c3af2a08a9 CHANGELOG: the macOS sandbox keychain and Claude Code token. 2026-11-02 10:16:03 +02:00
Lauri Ojansivu 21c94ce098 Fix macOS Sandbox for VSCodium.
Thanks to xet7 !
2026-11-02 10:14:47 +02:00
Lauri Ojansivu 2d8a28c2f6 Merge remote-tracking branch 'origin/main' 2026-11-02 09:44:15 +02:00
Lauri Ojansivu c58b2896d1 Fix macOS Sandbox for VSCodium.
Thanks to xet7 !
2026-11-02 09:43:32 +02:00
github-actions[bot] 6b98272272 Bump versions for v12.78 2026-11-02 07:14:42 +00:00
Lauri Ojansivu ee22fb1a3b Prepare v12.78 release 2026-11-02 09:13:43 +02:00
Lauri Ojansivu 154c9a6e52 Updated Changelog. 2026-11-02 09:11:57 +02:00
Lauri Ojansivu 121f37be91 Tests: drawing refit, Due Cards board scope, dependency import counts, and SAML's one Issuer.
Changes since October whose commits lacked a unit, negative or browser
test:
- 2929b61c85: a new drawing refits when its window changes, and is left
  alone once drawn on, edited, or zoomed or resized by hand.
- c64b3a3b1d: Due Cards, in "me" and "all users", shows an assigned-only
  member only their assigned cards; the owner sees them all.
- 8a6aeba7d1: the All Boards dependency import says how many lines were
  added, already there and unmatched.
- 5c50f38e25: a signed SAML Response with two Issuers is refused, which
  node-saml 5.1.0 accepted; the same Response with one Issuer logs in.
The browser specs pass in Chromium.

Thanks to xet7 !
2026-11-02 00:54:59 +02:00
Lauri Ojansivu 79c40407d1 Tests: Admin Panel pane titles, audit log alignment, GCS help, Data health "Never", card reports.
October Admin Panel changes whose commits had no test of their own:
- 85af731c1b and 2017f2871f: the Password length and Audit log panes
  name themselves in the title bar, the audit log has its own URL, and
  its cells align to the start - right in Hebrew - with no left/right
  rule left in its CSS.
- e6f7d226ca: no help line of a storage field is shown twice.
- 77956c98ed: Data health's "Never" is its own key, also when an
  administrator translates the List Sync one differently.
- 69f3358154: the card reports stay instance-wide and admin-only.
tests/playwright/specs/admin-pane-titles-and-help.e2e.js passes in
Chromium.

Thanks to xet7 !
2026-11-02 00:31:24 +02:00
Lauri Ojansivu 148d18d456 Tests: OpenSSL 4 decrypt refusal, the Scrum rollover lease, and the backup catch-up scan.
October changes whose commits had no node test of their own:
- ffe49dd2fd: decryptFile() refuses both the OpenSSL 4 and the OpenSSL 3
  wording of a failed authentication as "changed or the key is wrong",
  on any OpenSSL; an unrelated error (ENOSPC) is passed through.
- 0218cd823f: the rollover and batch-job leases are claimed before every
  write, another server's live lease is refused, and each lease field is
  written in one place in the whole tree.
- 8cbe2605fe: on macOS, a stopped engine and one with watching off run no
  catch-up scan.

Thanks to xet7 !
2026-11-02 00:30:57 +02:00
Lauri Ojansivu 15877dd1bc Source server: a local package fork that a prebuilt package implies is loaded first.
ecmascript implies the local dynamic-import fork, so compiling ddp-server,
listed before it in .meteor/versions, stopped the source-loaded server
with "isopack dynamic-import not yet loaded". The loader now loads the
local package asked for and retries. The source server still stops later,
on "Cannot find module 'meteor/accounts-base'"; the bundle server is what
the browser tests ran against.

Thanks to xet7 !
2026-11-02 00:24:55 +02:00
Lauri Ojansivu 3ccb465c3f Tests: the #6755 browser test focuses the minicard link itself.
Chromium does not leave the focus on the minicard after its menu popup
is closed with Escape - the menu's opener is a link without href, which
cannot take the focus back - so the test asserted a state the browser
had not reached. It now focuses the minicard's link, the state of the
report, and checks that d still opens the Due date editor from there.
Passes in Chromium and WebKit.

Thanks to mimZD and xet7 !
2026-11-02 00:24:55 +02:00
Lauri Ojansivu 71d9776b61 CHANGELOG: the Rules page has a Back to the board.
Thanks to mimZD and xet7 !
2026-11-02 00:07:50 +02:00
Lauri Ojansivu 34d9817110 Rules page: Back to the board.
The Rules page lost its Back with its own sidebar; only the trigger, action
and rule details pages kept one, to the rules list, so the way back to the
board was the Board View menu. The rules list and its Workflow, Bots,
Blocks and History views now have a Back link to the board at the top. The
three sub-pages keep their own Back and show no second one.

Thanks to mimZD and xet7 !

Fixes #6852.
2026-11-02 00:07:42 +02:00
Lauri Ojansivu aa939d7bec Tests: CrossBoardExportBleed and UnassignedAttachBleed are in the security regression inventory.
Both are published on the Hall of Fame, so the inventory counts 143 and
names each one's unit and browser regression tests. CrossBoardExportBleed
was published without being added, which failed the whole node-suite run.

Thanks to xet7 !
2026-11-02 00:05:44 +02:00
Lauri Ojansivu 194e5b0a51 CHANGELOG: UnassignedAttachBleed, and shortcuts after a click on a minicard or a closed popup.
Thanks to Javier García Herrera, mimZD and xet7 !
2026-11-02 00:05:10 +02:00
Lauri Ojansivu 532a96da11 Security: UnassignedAttachBleed - attachment API writes obey assigned-only card scope.
An assigned-only member whose card assignment was removed kept the card and
attachment ids and, through POST /api/attachment/upload and
DELETE /api/attachment/delete/:id, still added a file to that card and
soft-deleted its existing one: both asked only for board write access. The
DDP methods api.attachment.upload/delete, and copy and move onto a target
card over REST and DDP, had the same shape.

mayWriteCardAttachment (server/lib/assignedOnlyAttachments.js) applies the
same card rule the ordinary card Delete already used to all eight handlers.
A refusal is recorded in Admin Panel / Problems as UnassignedAttachBleed
(authz.assigned-attachment-write, medium: never disables the account).
tests/unassignedAttachBleed.test.cjs reproduces the reported sequence and
fails if any attachment API write lacks the card check;
tests/playwright/specs/unassigned-attach-bleed.e2e.js replays it over REST.

GHSA-w26g-rc43-q6cm.

Thanks to Javier García Herrera and xet7 !
2026-11-02 00:04:03 +02:00
Lauri Ojansivu ba0de93f79 Keyboard shortcuts keep working after a click on a minicard or a closed popup.
The shortcut filter refused every key while a link or button had the focus.
The minicard is itself a link, so a click on its date badge or its menu left
that link focused, and closing a popup returns focus to the button that
opened it: hovering a card and pressing d then did nothing until a reload.

A focused control now keeps only the keys it uses itself - Enter for any of
them, Space for a button, summary or checkbox, End/Home/arrows for a tab -
so Space still activates a focused button (audit A01) and every other key
is a shortcut again.

Thanks to mimZD and xet7 !

Fixes #6755.
2026-11-02 00:03:54 +02:00
Lauri Ojansivu 9a15491985 CHANGELOG: sign-in errors kept through a redraw, and the Firefox full-run test fixes.
Thanks to xet7 !
2026-11-01 20:35:06 +02:00
Lauri Ojansivu c8ed3e0fd0 Tests: the bulk export popup is opened until it stays, in all five Haitian Creole tests.
Each test navigates to All Boards and at once opens Export all boards.
All Boards is still drawing just after the route changes, and in
EVERYTHING's full Firefox run the popup opened then was closed again: no
header title to read (the first test; it passed in the run before).

All five tests open the popup again until its title is there. The spec in
Firefox, three times beside the running suite: 15 of 15.

Thanks to xet7 !
2026-11-01 18:56:50 +02:00
Lauri Ojansivu 4a9c3f21d2 Tests: the drawing surface's width is waited for, not read once after its viewBox.
The test waited until the drawing's viewBox had grown past the old 800,
then read the surface's box once. The element's layout follows the
viewBox a frame later, and in EVERYTHING's full Firefox run it was still
800 px against more than 1088 expected (it passed in the run before).

It polls the surface's width as well. The spec in Firefox, three times
beside the running suite: 9 of 9.

Thanks to xet7 !
2026-11-01 18:23:13 +02:00
Lauri Ojansivu 3a2fef4dc7 Sign-in: login errors are rendered from template state, and the SAML error test clicks a finished form.
saml-popup-error's first test failed in EVERYTHING's full Firefox run and
2 of 3 times alone: #login-error-message stayed empty after the SAML
button was clicked with a stub that fails at once.

The test clicked #at-saml (force) as soon as Meteor connected, while the
sign-in form was still drawing: the click landed on a button being
replaced, or - in another attempt - below the window ("Element is
outside of the viewport"), and no handler ran. It now waits for the form,
scrolls the button into view and clicks until the error shows. With one
worker both tests pass 6 of 6 in Firefox against a rebuilt bundle.

Found on the way: #login-error-message sits inside `if isLoading`, and
showLoginError wrote into the element - when that block is drawn again
the element is new and empty, so an error shown just before was lost. The
template now renders the message from a ReactiveVar (loginErrorText) as
escaped text, so a redraw keeps it and it can still never be HTML.
oauthProviders.test.cjs pins the same region, the translated codes and
text-only output in that form.

The other sign-in specs (44 files) pass in Firefox with it, apart from
runs where two copies of a settings-changing spec ran at once.

Thanks to xet7 !
2026-11-01 17:49:21 +02:00
Lauri Ojansivu f241681e80 Tests: background notification recovery checks its own mail, not the first one the sink caught.
The SMTP sink captures every mail the shared test server sends while it
listens. The test waited until user2 had exactly one mail, then read
sink.messages[0] - in EVERYTHING's full Firefox run another worker's
comment notification ("Please look", to another user) had arrived first,
so "Frozen recovery body 0" was looked for in the wrong mail.

It reads the mail addressed to user2. The whole spec in Firefox, twice:
24 passed, 2 conditionally skipped.

Thanks to xet7 !
2026-11-01 17:32:09 +02:00
Lauri Ojansivu c71c875649 Tests: the custom field settings popup is opened until its Create button shows.
The String Template help test opened a card and at once its custom field
settings popup. The opened card was still drawing, and in Firefox under
the whole suite the popup sometimes closed again before Create could be
clicked: ve-CC and rup failed in EVERYTHING's full Firefox run, 42
others passed.

The test opens the popup again until its Create button is there. ve-CC,
rup and en, twice each in Firefox beside the running suite: 6 of 6.

Thanks to xet7 !
2026-11-01 17:06:02 +02:00
Lauri Ojansivu 0da86171e9 CHANGELOG: TODO Later - the whole EVERYTHING run has passed in the browsers, conformance and FerretDB.
Thanks to xet7 !
2026-11-01 14:15:06 +02:00
Lauri Ojansivu b80e21fd48 CHANGELOG: Android bundles start again.
Thanks to xet7 !
2026-11-01 14:11:20 +02:00
Lauri Ojansivu 24a7270b7f Android bundles start again: npm 12 runs argon2's and bcrypt's install scripts.
All four Android bundle jobs failed: WeKan crash-looped on

  No native build was found for platform=android arch=x64 runtime=node
  abi=147 ... loaded from: .../meteor/accounts-password/node_modules/argon2

until the 240 s smoke test gave up. Meteor's npm rebuild had printed
"install scripts blocked because they are not covered by allowScripts",
though prepare-bundle-npm.mjs writes argon2 and bcrypt into every
rebuild directory's allowScripts.

npm 12 applies allowScripts to a project's DECLARED dependencies only.
The package.json that prepare-bundle-npm writes there declared none, so
argon2 and bcrypt were "extraneous" and their scripts stayed blocked -
reproduced with npm 12.2.1 on this release's bundle, and with the two
declared the same rebuild runs both install scripts. Linux, macOS and
Windows never noticed: prebuilds load there, and the Windows jobs compile
the two themselves. Android has no prebuild, and check-addon-arch passed
the Linux ones it cannot load.

prepare-bundle-npm.mjs now declares each reviewed installer a rebuild
directory has installed, at its installed version, and leaves a declared
one as it is. The Android build runs it itself first - the Linux bundle
it rebuilds may predate this - and loads argon2 and bcrypt with Android's
Node.js right after the install, so a missing addon stops the build with
its name instead of a smoke-test timeout.

prepareBundleNpm.test.cjs and androidBundles.test.cjs pin both; they fail
on the code before. Not run on Android here: the build needs Termux on
an Android-CPU runner.

Thanks to xet7 !
2026-11-01 14:11:10 +02:00
Lauri Ojansivu 73ea323da1 CHANGELOG: CrossBoardExportBleed, GHSA-8885-xrq4-j47h.
Thanks to ccsimplyspolit and xet7 !
2026-11-01 14:01:07 +02:00
Lauri Ojansivu be6727607c CrossBoardExportBleed: a checklist scope stays on its board, and every export obeys Disable all export and Anonymize export users.
GHSA-8885-xrq4-j47h, reported by ccsimplyspolit, both findings confirmed
in the current code:

1. GET /api/boards/<board>/export?checklistId=<id> (and exportZip)
   selected the scoped checklist and its items by id alone. A checklist
   of another board - a private one too - was streamed with every item
   and its creator, whose profile the anonymization map did not cover.
   Both selectors are now bound to the exported board's cards, and a
   checklist of another board is recorded in Admin Panel / Problems
   (authz.export-checklist-scope).

2. The streaming CSV/TSV export asked neither Disable all export nor
   Anonymize export users. Neither did, it turned out, Excel (board,
   card, all boards), PDF (board and card), the chart exports, the
   calendar feed or the external formats: only the WeKan JSON, ZIP and
   Trello exports did, though the setting promises that "the server
   rejects any export request" and names CSV and Excel.
   - refuseDisabledExport() answers 403 before anything is written, and
     all 13 HTTP export routes call it first; assertExportEnabled() still
     guards the builders. Both record the attempt (authz.export-disabled,
     never blocking the account: a page opened before the switch still
     shows the menus).
   - anonymizeExportIdentities() anonymizes what one exporter writes -
     users, @mentions, requested-by / assigned-by, comments - with the map
     build() uses, board members included. The CSV/TSV stream, Excel,
     PDF, chart exports (forExport: the chart views keep names), the
     calendar feed and every external format call it.

tests/crossBoardExportBleed.test.cjs drives the gate and the anonymizer
and scans the tree: no scoped checklist selected by id alone, no export
route without the gate, no exporter naming people without anonymizing.
All 7 fail on the code before. The new spec
security-cross-board-export.e2e.js replays the reporter's requests and
checks every export route while export is disabled and the CSV and
Kanboard exports while anonymizing; it passes with export-access and
scrum-export-anonymization against a rebuilt bundle.

Thanks to ccsimplyspolit and xet7 !
2026-11-01 14:00:19 +02:00
Lauri Ojansivu 9c5fa760c3 CHANGELOG: glob 14.0.3, checked: no WeKan code loads it, no telemetry, tarballs match the lockfile.
Thanks to dependabot and xet7 !
2026-11-01 13:41:40 +02:00
Lauri Ojansivu 27040c6f2f Merge pull request #6851 from wekan/dependabot/npm_and_yarn/npm_and_yarn-0e36e05768
Bump glob from 13.0.6 to 14.0.3 in the npm_and_yarn group across 1 directory
2026-11-01 13:36:11 +02:00
Lauri Ojansivu c15e1ab4b2 Tests: the Card Settings splitter is dragged once the popup holds still.
The splitter test measured the splitter straight after opening Card
Settings, while the popup was still placing itself. In WebKit the press
landed where the splitter had been and dragged nothing: the pane stayed
526 px against more than 606 expected (the first attempt in EVERYTHING's
WebKit stage; the retry passed).

It now measures once two reads of the splitter and the panes 150 ms apart
agree. WebKit 5 of 5, Chromium 3 of 3.

Thanks to xet7 !
2026-11-01 11:58:06 +02:00
Lauri Ojansivu b676cdc169 CHANGELOG: the WebKit date-range refusal and the language and push fixes of the browser tests.
Thanks to xet7 !
2026-11-01 11:25:20 +02:00
Lauri Ojansivu 4ad13db5ef Filters: the date-range forms are checked by WeKan, not refused by WebKit after Clear.
After Clear emptied a refused range, WebKit kept a half-edited date
segment in the From field and fired `invalid` on it at the next submit -
though its validity read valid - so the form never submitted and the new
range never applied, with no message (card-date-range.e2e.js in WebKit,
also on retry: 0 submit events, `invalid` on .js-card-date-from). The old
Clear handler emptied the fields the same way, so this was there before.

Both range forms are novalidate now. The submit handler already decides
whether a range is valid (rangeRefused), marks the end date invalid with
the reason and says it in the form, so nothing is lost.

filterDateRangeRefusal.test.cjs pins novalidate on both forms. Against a
rebuilt bundle card-date-range and card-movement-filter pass twice each
in WebKit, Firefox and Chromium.

Thanks to xet7 !
2026-11-01 11:24:59 +02:00
Lauri Ojansivu efe0e9287c Tests: a language switch is waited for, and WebKit does not freeze on asking for a push subscription.
Two causes behind most of EVERYTHING's WebKit failures so far, both in the
test pages rather than in WeKan:

- 94 specs switch language with setLanguage and then click at once. The
  client loads that language afterwards and draws everything again, which
  closes a popup opened before that: the board view popup of the flow and
  report help specs (Latin monte-carlo in WebKit; Corsican and Scottish
  Gaelic in earlier Firefox runs) had no entry left to click. The
  fixture's context now wraps Meteor.callAsync in each page so that a
  setLanguage call resolves once <html lang> - which client/lib/i18n.js
  sets in the same flush that redraws - names the language. It waits at
  most 10 s and then resolves anyway, so it can only make a spec patient,
  never fail one. A page of a context a spec makes itself uses
  untilLanguageShown().
- Playwright's WebKit freezes the page's main thread on
  PushManager.getSubscription(): after it not even a timer runs. Member
  Notification Settings asks it on opening, to show whether this device
  gets push (client/lib/webPush.js), so every WebKit spec that opened it
  hung in its next click - notification-activity-mute and the member
  scope of notification-delivery-settings, in every language. In WebKit
  the page now answers as a device with no subscription does. Chromium,
  Firefox and Safari answer the real call; no spec subscribes.

WebKit: those notification specs 10 of 10, and the Latin, Corsican and
Product Backlog specs 13 of 13; Chromium and Firefox 11 of 11 each with
the same subset. playwrightPageInit.test.cjs runs both page scripts on a
fake page: setLanguage waits for the language and keeps its result,
other methods are not held, a page whose Meteor arrives later is
wrapped, the wait is capped; WebKit's getSubscription answers null and
Chromium's and Firefox's are left alone; the fixture installs both.

Thanks to xet7 !
2026-11-01 11:21:45 +02:00
Lauri Ojansivu cfcead6a1a CHANGELOG: the Firefox fixes - date range refusal, history Retry, whiteboard line width - and the browser test fixes.
Thanks to xet7 !
2026-11-01 09:23:34 +02:00
Lauri Ojansivu 40f60582b7 Whiteboard: a line is drawn at its own width, not the whiteboard's 800.
Inside `each strokes` the template drew stroke-width=width, and Spacebars
looks a name up among the template's helpers before the line's own
fields: the width helper is the drawing's width, so every line was drawn
800 wide - one black shape over the whole whiteboard, in every browser.
The lines are mapped with strokeWidth now.

whiteboard.test.cjs: every name the line's path uses is a field of the
line and none is a helper of the template, which would win - it fails on
the code before. whiteboard.e2e.js checks each drawn path is the pen's
width (4) in both browsers.

The same spec drew the second person in another context of the same
browser. In Firefox two contexts share the pointer capture the first pen
takes: one line zig-zagged between both paths and the other page drew
nothing, 4 of 4. The second person now has a browser of their own, as
two people do; both pens are still down at once. Firefox 6 of 6 and
Chromium 4 of 4 against a rebuilt bundle.

Thanks to xet7 !
2026-11-01 09:22:05 +02:00
Lauri Ojansivu 96845f6f4b History recovery: Retry is enabled only once the board lets this person write on it.
After a reload the notice for an unanswered keyboard undo or redo shows
as soon as its board is open - before the board and its members have
arrived. undoRedoLast does nothing while Utils.canModifyBoard() is false,
so a Retry clicked in that moment was dropped without a word and the
notice stayed (history-recovery-notice.e2e.js, Firefox).

Retry is disabled until the retry can act; Forget needs no right and
stays usable. For someone who may only read the board Retry stays
disabled, as it could never do anything for them.

historyRecoveryNotice.test.cjs pins the binding and the guard it mirrors,
and that Forget is not gated - it fails on the code before. The spec
waits for Retry to be enabled before it clicks; the spec passes in
Firefox against a rebuilt bundle.

Thanks to xet7 !
2026-11-01 09:22:05 +02:00
Lauri Ojansivu c5f5ad2338 Filters: a refused date range says why in the form, so Clear still works in Firefox.
The filter sidebar's two date-range forms (card dates, card movement)
reported a reversed range with reportValidity(). Firefox opens that
bubble just under the end date - over the Apply and Clear buttons - and
while the field has focus it takes every click on them: no mousedown and
no click reached the page, twice in a row, so a refused range could not
be cleared at all (card-date-range.e2e.js, 4 of 4 in Firefox; with the
bubble suppressed it passed).

The end date is still marked invalid with the reason (setCustomValidity,
for :invalid and screen readers), and the reason is shown in the form as
a role="alert" line under the buttons. Typing in the range or Clear takes
it back.

filterDateRangeRefusal.test.cjs: both forms report through rangeRefused,
show the line and take it back on input and Clear, and nothing in the
filter sidebar calls reportValidity() - it fails on the code before. The
card-date-range and card-movement-filter specs check the line appears
with the reason, is gone after Clear or an edit, and is absent for an
accepted range; both pass in Firefox against a rebuilt bundle.

Thanks to xet7 !
2026-11-01 09:22:05 +02:00
Lauri Ojansivu 0c8c254dc5 Tests: the viewport matrix cleans up only what it seeded.
viewport-matrix is Chromium-only (device emulation), so in Firefox and
WebKit every test of a viewport is skipped and its beforeAll never runs -
but its afterAll still does, and read board.boardId of a board that was
never seeded: each of the twelve viewports failed in EVERYTHING's Firefox
stage, on the last test of the group, in 0 ms.

afterAll returns when nothing was seeded. Firefox: 132 skipped, none
failed. Chromium: phone-portrait 11 of 11, and its board is cleaned up.

Thanks to xet7 !
2026-11-01 09:09:14 +02:00
Lauri Ojansivu 0a1f8584ee Tests: the board view popup is opened until it stays open for the Product Backlog.
The test switches language and then opens the board, and when the language
arrives the board header draws again and closes a popup opened before
that. In Firefox the click on the board view toggle often came first: no
popup at all, and no Product Backlog entry to open - 2 of 6 runs alone,
the st variant in EVERYTHING's Firefox stage.

The test opens the popup again until its Product Backlog entry is there.
9 of 9 in Firefox (st, en, haw, three times each).

Thanks to xet7 !
2026-11-01 09:01:09 +02:00
Lauri Ojansivu 2042fd5102 Tests: the Blocks drag test grabs the block's body, not its edge.
In Firefox a Khmer label overflows its Blocks block and widens the box the
test measures, so the first point of the block it found, scanning from the
left, was the block's outline - and a press there dragged nothing: km and
km-KH failed every time (4 of 4), every other language passed.

The grab point is now one with the block's own body on both sides of it.
The whole spec in Firefox: 104 of 104, Khmer, Georgian, Fijian,
Kinyarwanda (the long labels the search was written for) and Arabic
right-to-left among them.

Thanks to xet7 !
2026-11-01 08:23:41 +02:00
Lauri Ojansivu 0403e27c62 Tests: the notification delivery sections are opened until they stay open.
The member-scope test switches language just before it opens Notification
Settings, and when the language arrives the popup draws again with every
section closed. Under a whole-suite run in Firefox that came after the
click on the section's summary, so the text checks passed and the select
the test then used was hidden: vi and zh_SG failed, 101 others passed,
and vi passed 3 of 3 alone.

expand() opens the section when it is closed and waits until its content
shows, again if a redraw closes it. vi, zh_SG, en and fi twice each, and
the board-scope, webhook-form and tray tests: all pass.

Thanks to xet7 !
2026-11-01 08:04:55 +02:00
Lauri Ojansivu d9e48a909d Tests: the list-sync specs' mock servers are reachable from WeKan when the browsers run in Docker on macOS.
The list-sync specs answer as Jira or GitLab from a server in the test
process, and WeKan's server fetches from it. With the browsers in Docker
on macOS that server was on the container's loopback, which Docker
Desktop keeps from the host: "fetch failed", and every test of
list-sync-estimate, -planning, -source and -time-estimates failed in
EVERYTHING's Firefox stage.

build.sh already forwarded the test SMTP port from the host into the
container through docker exec; the same forwarder carries any TCP. On
macOS it now also forwards a small pool, 39101-39104 by default
(WEKAN_TEST_REVERSE_PORTS), and names it to the specs, which listen
through helpers/reachable-server.cjs: the first free port of the pool,
or any port when there is none, as before. Linux forwards nothing.

30 of 30 of those specs pass in Firefox in Docker now.
playwrightReachableServer.test.cjs: the pool is read strictly, a port in
use is skipped, no pool listens anywhere, and when every forwarded port
is taken it fails clearly instead of listening where WeKan cannot reach.
playwrightDockerHost.test.cjs: macOS forwards the SMTP port and the pool
(or the one configured), names it to the container and removes its ready
files; Linux forwards nothing.

Thanks to xet7 !
2026-11-01 07:50:40 +02:00
Lauri Ojansivu 0cea141702 Tests: the duplicate relation picker is opened once the card has opened.
The test clicked Add dependency straight after the minicard. Under a
whole-suite run in Firefox that click landed while the card was still
opening, and the picker it opened was closed again when the card's route
and details settled - no popup, and the wait for its type select ran out
(the ve-PP variant; 59 of 60 passed, and it passed 3 of 3 alone).

The test waits for the opened card, then opens the picker again until its
type select shows. 6 of 6 (en, ve-PP, tlh, twice each) beside the suite.

Thanks to xet7 !
2026-11-01 07:21:43 +02:00
Lauri Ojansivu 9188843369 Tests: the checklist drag tests drag once the opened card's rows hold still.
The opened card draws the sections above its checklist for a moment after
it opens, and they push the rows down: in Firefox they moved 50 px between
the test measuring them and releasing the mouse. With handles off the
release landed on item 1's own title and opened its editor, so the titles
read 2,3 (EVERYTHING's Firefox stage); with handles on the enabled drag
dropped item 1 between 2 and 3. 2 of 6 in a row failed that way.

drag() waits until two reads of the rows' positions 150 ms apart agree,
then measures and drags. 10 of 10, both variants five times, while the
whole suite ran beside it.

Thanks to xet7 !
2026-11-01 06:57:18 +02:00
Lauri Ojansivu dbf07aadf9 Tests: the activity recovery report is searched again until no worker lease covers it.
The report is a snapshot taken when the search runs, and the live
one-second recovery worker leases each pending intent while it looks at
it. A search made during that lease shows the seeded orphan as "Delivery
reservation active" - correct - and the page keeps showing it, so the
wait for "Original activity is missing" ran out in EVERYTHING's Firefox
stage (the en variant).

The test searches again until a snapshot falls between leases, up to 30 s,
and then asserts the same status and the disabled Retry. 10 of 10 in
Firefox, every test twice, while the whole suite ran beside it.

Thanks to xet7 !
2026-11-01 06:40:42 +02:00
Lauri Ojansivu e001c4f9c2 CHANGELOG: the Docker test proxy redials an unanswered connect.
Thanks to xet7 !
2026-11-01 06:23:55 +02:00
Lauri Ojansivu ffe207e191 Tests: the Docker test proxy dials the host again when a connect is never answered.
Even on the bridge network, EVERYTHING's Firefox stage lost a test now and
then to a page whose document never came (RuleBleed's cross-board test,
Red Strings, shared templates). Inside the container the cause was in
plain sight: a connect to host.docker.internal:3000 in SYN_SENT after nine
retransmits, while the next connection went straight through. Through the
container's proxy 9 of 1000 requests went unanswered; from the Mac itself
none did.

The proxy no longer waits on such a connect. It reads nothing from the
browser until a connection to the host is up (pauseOnConnect), drops a
dial that has not connected in 1.5 s - a normal one takes milliseconds -
and dials again, five times, so no byte is lost or sent twice. If none
connects the browser's connection is closed rather than left open.

playwrightDockerHostProxy.test.cjs: two unanswered dials, then one that
connects - the request arrives once and is answered, and the abandoned
dials are closed; when every dial is unanswered the client is closed after
exactly the attempts, in well under the old 30-60 s wait.

Thanks to xet7 !
2026-11-01 06:23:34 +02:00
Lauri Ojansivu b2198cb61b CHANGELOG: the Firefox stage's Docker network, host paths and phone invitation test.
Thanks to xet7 !
2026-11-01 06:10:30 +02:00
Lauri Ojansivu a6bec88266 Tests: the phone invitation tile is measured in one layout with its buttons.
43-mobile-allboards read the invitation tile's box, then each control's
box, one round trip at a time. Firefox draws the row above the board list
later than Chromium, and in between it moved the whole list down 29.4 px:
the Decline button (1102-1162) was compared with where the tile had been
(853-1139) and failed, 2 of 2, in EVERYTHING's Firefox stage. The tile was
never clipped - settled, it runs 882-1168 around all three controls.

The tile and its message and two buttons are measured in one layout now,
polled until they hold, and a missing or zero-height control fails too.
Firefox: 3 of 3. With the original #6488 bug put back (the tile forced to
4rem) it still fails, naming the message and both buttons.

Thanks to xet7 !
2026-11-01 06:10:03 +02:00
Lauri Ojansivu 8a1f7a14a9 Tests: the Playwright container sees the repo and the files root at their own host paths.
A spec and the server share files by path. admin-continuous-backup gives
the server a key file it wrote under .tools/tmp, and reads the stream.json
the server wrote to its target - and with the repository mounted at /repo
and the files root at /wekan-files, every such path named a file only one
side had: "The encryption key file cannot be read", and no stream.json, in
both of its tests in EVERYTHING's Firefox stage (the only browser there that
runs in Docker on this Mac, and on Linux arm64 every browser does).

Both are mounted at the path they have on the host now, the specs are told
that path in WEKAN_FILES_PATH, and the in-container script finds the proxy
and the Playwright binaries from its working directory. Both tests pass in
the container now.

playwrightDockerHost.test.cjs drives run_playwright_docker on macOS and
Linux: the repo and the files root are mounted at their own paths,
WEKAN_FILES_PATH and HOME follow, and the function names no container-only
path. It fails on the code before.

Thanks to xet7 !
2026-11-01 06:08:05 +02:00
Lauri Ojansivu 7f948bf438 Tests: the macOS Playwright container uses Docker's bridge network, not the host network.
Three Firefox tests of one EVERYTHING run waited the full 60 s in
waitForMeteor: their page's own document never arrived. Measured from
containers during that run, on Docker Desktop for macOS:

- host network, through the WeKan proxy:          3 of 600 requests lost
- host network, straight to host.docker.internal: 8 of 600 connects never
  completed
- bridge network, through the same proxy:          0 of 1000
- from the Mac itself:                             0 of 300

So it is Docker Desktop's host network, not WeKan and not the proxy. On
macOS nothing needs that network - WeKan and MongoDB are reached through
host.docker.internal and SMTP through docker exec - so the container runs
on the bridge network, where the proxy still serves localhost:3000 and the
browser keeps ROOT_URL's origin. Linux keeps the host network, which is
the real one there.

playwrightDockerHost.test.cjs drives run_playwright_docker with uname
stubbed: Darwin gets bridge and the proxy, Linux gets host and none, and
the function names no network of its own. It fails on the code before.

Thanks to xet7 !
2026-11-01 06:06:04 +02:00
Lauri Ojansivu 6d0ab6cf11 CHANGELOG: continuous backup, single-file builds and the EVERYTHING test fixes.
The seven commits since the Dependabot entries: two bug fixes (continuous
backup watching before its first scan; single-file Mach-O signing and
start/backup waiting for WeKan) and five test-run fixes, grouped by topic.
The short changelog guard is a group of its own now, so the developer-
tooling subsection is grouped throughout. CHANGELOG.md regenerated.

Thanks to xet7 !
2026-11-01 05:47:52 +02:00
Lauri Ojansivu 89b64966c6 Tests: the password minimum specs wait until the server has the policy they wrote.
password-min-length-signup-login.e2e.js writes the password policy straight
into the settings document. The server learns it from its settings observer,
which polls on the standalone test MongoDB EVERYTHING runs: for up to ten
seconds it still answered the old policy. A sign-up form loaded meanwhile kept
the old minimum (6 instead of 10), and an administrator's Set password was
refused under the minimum the test before had left enforced - two failures in
EVERYTHING's Chromium stage that never showed on a dev server, whose MongoDB
is a replica set.

untilServerHas() asks the server (getPasswordPolicy) until it has the policy
the test wrote; the sign-up form is loaded again after it. 6 of 6 with two
workers, each test twice.

Thanks to xet7 !
2026-11-01 05:01:18 +02:00
Lauri Ojansivu d5e3325e87 Tests: Card Settings section order waits for the published board before reading the card.
"with every field allowed, the card draws exactly the Card pane's
sections" writes the board's Scrum visibility straight to the database and
reloads. The board publication's cursor has limit: 1, which Meteor's
change-stream driver does not follow, so it polls: a reload could still be
served the board as it was for up to ten seconds, and the card drew no
Scrum section - failing every time in EVERYTHING's Chromium stage. Live in
the browser the update arrived after 9.8 s; in the app itself a write
through a method re-polls at once, so only this direct write waited. The
test waits for the published board (up to 15 s) before it reads the card.

Thanks to xet7 !
2026-11-01 03:27:46 +02:00
Lauri Ojansivu 897376ba54 Tests: the Board Settings button theme test checks the settings that still have action buttons.
button-theme-foreground.e2e.js opened Board Settings / Swimlane, List and
Card and asked each for its column-actions buttons. Card Settings has had
none since 3e5bc58105 ("Card Settings: a checkbox per section, ...":
card actions live in the card's own section hamburgers), so every theme
failed on Card, in EVERYTHING's Chromium stage. Swimlane and List are
checked, in all four themes, and the test says why Card is not.

Thanks to xet7 !
2026-11-01 03:17:38 +02:00
Lauri Ojansivu 16103f9ad2 Tests: the whole-suite run keeps its test database in .tools/, not beside the checkout.
build.sh and build.bat started the test mongod on :3001 with its dbpath
in the checkout's parent directory (../mongodb-test-3001). That is the
one thing in a test run that was written outside the repository, against
the rule that everything WeKan's scripts write stays in .tools/ - and a
sandboxed editor, which may write only the repository, could not create
it, so EVERYTHING's browser stages had no database to run against. It is
.tools/mongodb-test-<port> now, which git and Meteor already ignore.

toolsDirOnly.test.cjs pins the new path in both scripts and fails on a
test database beside the checkout.

Thanks to xet7 !
2026-11-01 02:51:41 +02:00
Lauri Ojansivu 8cbe2605fe Continuous backup: files are watched before the first scan, and a macOS start catches up once.
The file engine scanned its sources first and started watching them
after, so a change made between the two - after the scan had passed a
file, before the watcher began - waited for the next scan, five minutes
by default. And macOS reports file events through FSEvents, which
delivers nothing for a moment after a watcher is created: an EVERYTHING
run on a loaded Mac lost a log line appended right after the start
(tests/integration/continuousBackup.test.cjs, "the appended log line was
streamed by watching", 4 !== 5).

The watchers start first now; a change seen by the scan and a watcher
too is recorded once, as they share one queue. On macOS one catch-up
scan runs two seconds after the start. Not elsewhere: inotify is
watching when fs.watch returns, and a second full scan of a large store
would double the start-up cost.

A new test, on macOS and Linux, with a watcher that never reports:
watching starts before the first scan; on macOS the catch-up scan
records a change made right after the start; on Linux no second scan
runs. It fails on the code before this change.

Thanks to xet7 !
2026-11-01 02:42:03 +02:00
Lauri Ojansivu f5eb4ecf61 Mac smoke test: a process group's live members come from libproc, not ps.
When macOS refuses to signal a process group (EPERM, which it returns for
a group of zombies), the smoke test checked with `ps` whether any member
was still alive. A Mac that will not let the build run `ps` - "Operation
not permitted", as here, where EVERYTHING's node suites run - failed the
check itself, and with it a correct app: macAppPackaging.test.cjs, every
EVERYTHING run on this machine.

On macOS it asks the kernel through libproc now (proc_listpids for the
group, proc_pidinfo for each member: a zombie's info cannot be read, a
live one's status is not SZOMB), with no process started. Elsewhere it
uses ps exactly as before.

tests/mac-smoke-app.py: the ps cases run as the non-macOS path; on macOS
an EPERM asks libproc and never ps; and a real group - a live child, then
a zombie leader alone - is told apart. macAppPackaging.test.cjs passes,
three runs in a row.

Thanks to xet7 !
2026-11-01 02:32:31 +02:00
Lauri Ojansivu 8d4f4caa2f Single file: a macOS build is signed whatever its target is called, and start/backup return once WeKan runs.
EVERYTHING's node suites run under Meteor's Node.js, which has single
executable applications, so singleFile.test.cjs built and ran a real
single executable - and it failed twice:

- The builder ad-hoc signed only a target named mac*/darwin*. Built from
  this Mac's Node.js under another label (the test builds 'amd64'), the
  file is still a Mach-O binary, the injected archive had invalidated its
  signature, and macOS killed it (SIGKILL) before it printed anything:
  status null. It signs every Mach-O output now, read from the file's
  magic number.
- `start --background` and `backup` returned as soon as they had spawned
  WeKan, before it wrote its pid file - which it does only after reading
  its settings and extracting FerretDB. A `stop` right after them said
  "WeKan was not running" and left it running. Both wait now
  (status.waitUntilStarted) until the pid file names the process they
  started, give up at once if it ended, and say so if it did not start.

singleFile.test.cjs: 24 of 24 under Meteor's Node.js, the real single
executable included; a new test pins waitUntilStarted, and that both
commands use it.

Thanks to xet7 !
2026-11-01 02:23:36 +02:00
dependabot[bot] 9a74138a84 Bump glob
Bumps the npm_and_yarn group with 1 update in the / directory: [glob](https://github.com/isaacs/node-glob).


Updates `glob` from 13.0.6 to 14.0.3
- [Changelog](https://github.com/isaacs/node-glob/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/node-glob/compare/v13.0.6...v14.0.3)

---
updated-dependencies:
- dependency-name: glob
  dependency-version: 14.0.3
  dependency-type: direct:production
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-31 23:51:54 +00:00
Lauri Ojansivu fa7e447536 CHANGELOG: ten Dependabot updates, checked; node-saml 5.2.0 where logins run; @smithy/core 3.38.1.
Thanks to dependabot and xet7 !
2026-10-31 23:06:40 +02:00
Lauri Ojansivu 05cc2460ea Tests: the short changelog guard holds between releases too.
changelogShort.test.cjs asserted an Upcoming section always, and one
Upcoming in CHANGELOG.md - but right after a release there is none until
the next real entry (CLAUDE.md, Making a release), so it failed after
every release, v12.77 included. With no Upcoming it checks that the
newest release is first and that nothing links an Upcoming that does
not exist; with one, what it checked before.

Thanks to xet7 !
2026-10-31 23:06:08 +02:00
Lauri Ojansivu 5c50f38e25 SAML logins are verified by @node-saml/node-saml 5.2.0, as package.json says.
Dependabot's 5.1.0 -> 5.2.0 bump (#6850) changed the app's package.json,
but a SAML login is verified by the wekan-accounts-saml package's own
copy, pinned by its Npm.depends to 5.1.0 - so nothing that runs changed.
5.2.0 is a hardening release a login relies on: it rejects a message
with more than one Issuer, checks idpIssuer on the Response and its
assertion, rejects a parsed query whose RelayState is not the signed
one, and checks a POST LogoutResponse's status and issuer.

The package pins 5.2.0 now. samlLogin.test.cjs required '5.1.0' in one
place and '^5.1.0' in the other; it requires the package and package.json
to name the SAME version, and at least 5.2.0, so a bump of one alone
fails.

A login roundtrip with the package's own copy (node-saml 5.2.0,
xml-crypto 6.4.0) and WeKan's options: a signed Response and a signed
Assertion log in; a NameID changed after signing, a signature by another
key, an unsigned response, and an answer to a request WeKan never made
are refused. saml-popup-error.e2e.js completes a real signed login in
the browser.

Thanks to xet7 !
2026-10-31 23:06:08 +02:00
Lauri Ojansivu 338afc15f8 @smithy/core 3.38.1: the AWS SDK's logger redaction fix, and the telemetry gate passes again.
The @aws-sdk/client-s3 3.1159.0 bump (#6844) resolved @smithy/core to
3.37.0, which npm marks deprecated: "a bug in custom logger data
redaction" (aws/aws-sdk-js-v3#8393). With a custom logger passed to an
AWS client, sensitive fields nested in a list or map reached the logger
unredacted. @smithy/core 3.24.0 to 3.38.0 have it, so 3.36.2 before the
bump did too; 3.38.1 is the fix.

WeKan's only AWS client is the S3 backup (server/methods/backup.js), and
it passes no logger, so nothing was logged. Updated all the same, in the
lockfile only and within the ranges the SDK already allows - one package,
nothing else moved. Its tarball matches the lockfile's integrity hash,
and compared with 3.37.0 it adds no network host, telemetry, process
spawning or install script. A multipart upload to a local S3 stand-in
still arrives signed AWS4-HMAC-SHA256, whole.

releases/check-telemetry.py --source failed on it: the deprecation text
links github.com, a URL origin new to package-lock.json. It passes now.

Thanks to xet7 !
2026-10-31 23:06:08 +02:00
Lauri Ojansivu dd2082b0ea Merge pull request #6846 from wekan/dependabot/npm_and_yarn/isomorphic-git-1.44.8
Bump isomorphic-git from 1.44.3 to 1.44.8
2026-10-31 22:39:27 +02:00
Lauri Ojansivu 4a86e9c19e Merge pull request #6850 from wekan/dependabot/npm_and_yarn/node-saml/node-saml-5.2.0
Bump @node-saml/node-saml from 5.1.0 to 5.2.0
2026-10-31 22:38:24 +02:00
dependabot[bot] 568cd9b6ec Bump isomorphic-git from 1.44.3 to 1.44.8
Bumps [isomorphic-git](https://github.com/isomorphic-git/isomorphic-git) from 1.44.3 to 1.44.8.
- [Release notes](https://github.com/isomorphic-git/isomorphic-git/releases)
- [Commits](https://github.com/isomorphic-git/isomorphic-git/compare/v1.44.3...v1.44.8)

---
updated-dependencies:
- dependency-name: isomorphic-git
  dependency-version: 1.44.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-31 20:37:49 +00:00
Lauri Ojansivu fea468471d Merge pull request #6849 from wekan/dependabot/npm_and_yarn/typescript-eslint/eslint-plugin-8.72.1
Bump @typescript-eslint/eslint-plugin from 8.72.0 to 8.72.1
2026-10-31 22:37:36 +02:00
Lauri Ojansivu 83199b9af7 Merge pull request #6848 from wekan/dependabot/npm_and_yarn/rspack/dev-server-2.2.2
Bump @rspack/dev-server from 2.2.1 to 2.2.2
2026-10-31 22:37:10 +02:00
Lauri Ojansivu 3839ee698d Merge pull request #6845 from wekan/dependabot/npm_and_yarn/mailparser-3.12.4
Bump mailparser from 3.12.2 to 3.12.4
2026-10-31 22:36:48 +02:00
dependabot[bot] a60b996c92 Bump @node-saml/node-saml from 5.1.0 to 5.2.0
Bumps [@node-saml/node-saml](https://github.com/node-saml/node-saml) from 5.1.0 to 5.2.0.
- [Release notes](https://github.com/node-saml/node-saml/releases)
- [Changelog](https://github.com/node-saml/node-saml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/node-saml/node-saml/compare/v5.1.0...v5.2.0)

---
updated-dependencies:
- dependency-name: "@node-saml/node-saml"
  dependency-version: 5.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-31 20:36:39 +00:00
Lauri Ojansivu 1230164c16 Merge pull request #6847 from wekan/dependabot/npm_and_yarn/i18next-26.5.11
Bump i18next from 26.5.4 to 26.5.11
2026-10-31 22:36:29 +02:00
dependabot[bot] 8008f410fe Bump @typescript-eslint/eslint-plugin from 8.72.0 to 8.72.1
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 8.72.0 to 8.72.1.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.72.1/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.72.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-31 20:36:27 +00:00
Lauri Ojansivu bd90782da2 Merge pull request #6844 from wekan/dependabot/npm_and_yarn/aws-sdk/client-s3-3.1159.0
Bump @aws-sdk/client-s3 from 3.1155.0 to 3.1159.0
2026-10-31 22:35:45 +02:00
dependabot[bot] 5eadc45563 Bump @rspack/dev-server from 2.2.1 to 2.2.2
Bumps [@rspack/dev-server](https://github.com/rstackjs/rspack-dev-server) from 2.2.1 to 2.2.2.
- [Release notes](https://github.com/rstackjs/rspack-dev-server/releases)
- [Commits](https://github.com/rstackjs/rspack-dev-server/compare/v2.2.1...v2.2.2)

---
updated-dependencies:
- dependency-name: "@rspack/dev-server"
  dependency-version: 2.2.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-31 20:35:25 +00:00
Lauri Ojansivu 3785a13f6a Merge pull request #6842 from wekan/dependabot/npm_and_yarn/imapflow-2.9.1
Bump imapflow from 2.8.0 to 2.9.1
2026-10-31 22:35:18 +02:00
Lauri Ojansivu 97cabdeca4 Merge pull request #6843 from wekan/dependabot/github_actions/helm/chart-releaser-action-1.8.0
Bump helm/chart-releaser-action from 1.7.0 to 1.8.0
2026-10-31 22:34:58 +02:00
Lauri Ojansivu 2802326888 Merge pull request #6841 from wekan/dependabot/github_actions/docker/login-action-4.7.0
Bump docker/login-action from 4.6.0 to 4.7.0
2026-10-31 22:34:17 +02:00
dependabot[bot] 1f46af0639 Bump i18next from 26.5.4 to 26.5.11
Bumps [i18next](https://github.com/i18next/i18next) from 26.5.4 to 26.5.11.
- [Release notes](https://github.com/i18next/i18next/releases)
- [Changelog](https://github.com/i18next/i18next/blob/master/CHANGELOG.md)
- [Commits](https://github.com/i18next/i18next/compare/v26.5.4...v26.5.11)

---
updated-dependencies:
- dependency-name: i18next
  dependency-version: 26.5.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-31 20:33:51 +00:00
dependabot[bot] 1bcb1da92f Bump mailparser from 3.12.2 to 3.12.4
Bumps [mailparser](https://github.com/nodemailer/mailparser) from 3.12.2 to 3.12.4.
- [Release notes](https://github.com/nodemailer/mailparser/releases)
- [Changelog](https://github.com/nodemailer/mailparser/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodemailer/mailparser/compare/v3.12.2...v3.12.4)

---
updated-dependencies:
- dependency-name: mailparser
  dependency-version: 3.12.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-31 20:33:31 +00:00
dependabot[bot] 067efac7f8 Bump @aws-sdk/client-s3 from 3.1155.0 to 3.1159.0
Bumps [@aws-sdk/client-s3](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-s3) from 3.1155.0 to 3.1159.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.1159.0/clients/client-s3)

---
updated-dependencies:
- dependency-name: "@aws-sdk/client-s3"
  dependency-version: 3.1159.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-31 20:33:21 +00:00
dependabot[bot] 2bdd16060e Bump imapflow from 2.8.0 to 2.9.1
Bumps [imapflow](https://github.com/postalsys/imapflow) from 2.8.0 to 2.9.1.
- [Release notes](https://github.com/postalsys/imapflow/releases)
- [Changelog](https://github.com/postalsys/imapflow/blob/master/CHANGELOG.md)
- [Commits](https://github.com/postalsys/imapflow/compare/v2.8.0...v2.9.1)

---
updated-dependencies:
- dependency-name: imapflow
  dependency-version: 2.9.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-31 20:32:54 +00:00
dependabot[bot] 433bc62211 Bump helm/chart-releaser-action from 1.7.0 to 1.8.0
Bumps [helm/chart-releaser-action](https://github.com/helm/chart-releaser-action) from 1.7.0 to 1.8.0.
- [Release notes](https://github.com/helm/chart-releaser-action/releases)
- [Commits](https://github.com/helm/chart-releaser-action/compare/cae68fefc6b5f367a0275617c9f83181ba54714f...df287104677278f869ca5480bd260358397cc3f9)

---
updated-dependencies:
- dependency-name: helm/chart-releaser-action
  dependency-version: 1.8.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-31 20:32:54 +00:00
dependabot[bot] 29f9c62a64 Bump docker/login-action from 4.6.0 to 4.7.0
Bumps [docker/login-action](https://github.com/docker/login-action) from 4.6.0 to 4.7.0.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/dbcb813823bdd20940b903addbd779551569679f...81c6f93d4bc991e510b342a00b419b3bb4efc5c6)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-version: 4.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-31 20:32:36 +00:00
github-actions[bot] 21766cf38d Bump versions for v12.77 2026-10-31 19:47:43 +00:00
Lauri Ojansivu 101cdb7579 Prepare v12.77 release 2026-10-31 21:46:40 +02:00
Lauri Ojansivu 54cf951d9c Merge remote-tracking branch 'origin/main' 2026-10-31 21:46:38 +02:00
Lauri Ojansivu 57a43ee8e1 CHANGELOG: the date-popup guards follow movable popups and the date picker's empty cells.
Thanks to xet7 !
2026-10-31 21:44:13 +02:00
Lauri Ojansivu ff2c0ed7f6 Tests: the date-popup guards follow movable popups and the date picker's empty cells.
Two node guards still described v12.76's behaviour before it changed on
purpose. calendarPopupResize asserted that a popup without a date editor
does not drag; every popup drags by its title bar now (#6839), and with
drag handles on only by its handle - both pinned instead, its mock no
longer answering every selector as if a drag handle were there.
calendarTodayMarker picked a month's days with filter(Boolean), which an
empty cell passes now that it is an object with its own _id (#6838); it
skips the cells marked empty.

Thanks to xet7 !
2026-10-31 21:44:11 +02:00
github-actions[bot] 63d022fe5f Bump versions for v12.76 2026-10-31 19:44:06 +00:00
Lauri Ojansivu e9cf825b22 Prepare v12.76 release 2026-10-31 21:43:03 +02:00
Lauri Ojansivu 4ce8514e36 CHANGELOG: open issues #6834-#6840 - Today in the date picker, the card title and movable popups, Change Settings, the d shortcut, minicard dates.
#6834 is fixed in part and #6837 needs a decision: both in TODO Later.

Thanks to mimZD and xet7 !
2026-10-31 21:32:28 +02:00
Lauri Ojansivu 429fdc5319 Tests: a card-button rule is a button on the minicard (#6836 was already there).
"Show rule button on minicard" is Board Settings / Card, Card buttons,
Show on Minicard (allowsCardButtonsOnMinicard), in WeKan since v12.16:
the board's card-button rules are drawn under each minicard, and a click
runs the rule on that card without opening it. Off by default, which is
why it was not seen. It had no test.

minicard-rule-button-6836.e2e.js (Chromium and WebKit): with it on, a
card-button rule's button is on the minicard and a click archives the
card without opening it; with it off, no minicard has one.

Thanks to mimZD and xet7 !

Fixes #6836,
2026-10-31 21:31:36 +02:00
Lauri Ojansivu ffd5cbd2ad Change Settings: icons in a column on the first line, compact wrapped lines, rows divided.
Member Settings / Change Settings used the shared popup list as it is:
a flex row whose every line is 33px high with its icon centred. Its
settings are sentences, and most wrap - in Persian, in German - so a
setting became two tall lines with its icon floating between them and
the next setting right under it, with nothing to tell where one ended.
Each row is now an icon column, the words and the check mark at the
end; the icon sits on the first line, a wrapped line is an ordinary
line high, a rule divides the rows, and the Card settings checkbox has
the same space beside its words as an icon has.

Scoped to the popup's own list (change-settings-list): a popup keeps
the screens before it - the member menu - in the same .pop-over, so its
data-popup would have restyled those too.

change-settings-popup-6835.e2e.js, in Persian and English, Chromium and
WebKit: each row's icon is on its first line, a line is under 28px high
and a row only as tall as its lines, rows are divided, and the member
menu beside it is untouched.

Thanks to mimZD and xet7 !

Fixes #6835,
2026-10-31 21:29:31 +02:00
Lauri Ojansivu cf4b48df09 Minicard: a due date uses the card's width instead of three lines beside an empty strip.
The dates row of a minicard reserved 84px at its trailing edge - a third
of a 250px card - for nothing: the avatars float after it, and clear:
both starts it below the menu. A due date with its "1 day left" was
broken over three lines beside that empty strip, in a Persian board of
the report. It reserves the drag handle's room now, 24px, and the same
badge takes two lines at the default list width.

The board header bar wrapping to a second row, and the list header icons
under the card count, in the same report, did not reproduce on the
current code at the same window width, in Persian, with the card count
on; they are in CHANGELOG TODO Later until the reporter can say which
WeKan and which browser zoom showed them.

minicard-dates-width-6834.e2e.js (Chromium and WebKit, English and
Persian): the dates row reserves at most 24px, and the due date badge is
at most two lines high.

Thanks to mimZD and xet7 !
2026-10-31 21:26:15 +02:00
Lauri Ojansivu 73c21dfe3e Tests: typing d in the card title types at the end of the title in WebKit too.
WebKit puts the caret at the start of a field on focus and Chromium at
the end, so the d typed into the title landed at its start in WebKit and
the check for a title ending in dd failed there - while what the test is
for, that d typed in a field opens no Due date editor, held in both.
The test puts the caret at the end itself now.

Thanks to xet7 !
2026-10-31 21:23:04 +02:00
Lauri Ojansivu 22a12dc7f8 Opened card: the whole title edits; the window and every popup move by their bar or handle.
The opened card's title was split like a minicard's: the leading half
opened the editor and the trailing half dragged the window, so clicking
the right of the title, or beside a short last line, did not edit. Now
all of the title edits. With drag handles off, the window is moved from
the empty space of its header row above the title - between the collapse
caret and the menu; with drag handles on, only by the drag handle. A
reader who may not edit has no edit zone and still drags by the title.

Every popup now moves the same way - by its title bar with drag handles
off, by the handle before its title alone with them on - where only the
Drawing, Board View and Board Card settings popups and the date popups
moved before. A dragged popup is kept inside the window as it grows. The
drag pins a popup where it is before moving it: one placed from the
right edge (the member menu) jumped across the window, and one with a
margin by that margin, as the drag began.

card-title-edit-and-move-6839.e2e.js, in Chromium and WebKit: a two-line
title edits from its right, middle and left; the empty header row moves
the window by exactly the drag with handles off and not at all with them
on, when the handle does; and the card menu, Labels and the member menu
move by their title bar, or with handles on by the handle alone.
titleClickToEdit, popupMoveMaximize and popupBounds pin it.

Thanks to mimZD and xet7 !

Fixes #6839,
2026-10-31 21:23:04 +02:00
Lauri Ojansivu b1403ca7af The d shortcut opens the Due date editor of a card that is still being opened.
On a slower server a card's details render a few hundred milliseconds
after the click that opens it. `d` pressed in that moment found no Due
date control yet and did nothing - only the hovered-minicard path (#6755)
waited for it. Both paths now wait for THAT card's control, stop if
another card was opened meanwhile, click it once, and give up after 3
seconds.

The card details also closed any popup of their card when they were
destroyed (#6686), which a re-render of the card still open does too: a
popup could open and shut again at once. They close it now only when
the card is no longer the open one - closed, or another card opened.

The Playwright test emulates a 300 ms connection in Chromium, clicks
each card open and presses d at once: the editor opens and is still
open 1.5 s later, and closing the card still closes it. It fails on the
code before this change. dueDateKeyboardShortcut and
labelsPopupStaleCardContext pin both halves.

Thanks to mimZD and xet7 !

Fixes #6840,
2026-10-31 21:06:53 +02:00
Lauri Ojansivu bc21c901f4 Date picker: a Today button, and a month navigated back to is drawn whole.
The board Calendar view already has Today (and Calendar mode too), but
the date picker of every date popup - Due, Start, End, Received and the
rest - had only « ‹ › », so a user who paged to another month had to
page back. Today, between the month and ›, shows today's month and
focuses today; it changes nothing until a day is clicked and saved.

Testing it found a picker bug that was already there: going back to a
month drew it wrong - October came back as "29, empty, 30, empty", with
no 31st. Blaze matches list items by _id, and the weeks and days had
none; the empty cells, all alike, were moved around. Every week and
cell, empty ones too, now has an _id that names its month.

calendarPickerControls.test.cjs checks the ids, that Today shows today's
month and focuses today, and that it selects nothing; the Playwright test
pages two months ahead, presses Today, and checks every day of the month
in order, today focused, and the card's due date unchanged.

Thanks to mimZD and xet7 !

Fixes #6838,
2026-10-31 21:06:53 +02:00
Lauri Ojansivu 88fef5dc08 CHANGELOG: the Password length strings in every language, and its title bar.
Thanks to xet7 !
2026-10-31 15:18:23 +02:00
Lauri Ojansivu 3f9379ac86 Translations: the Password length page and Change your password, in 234 languages.
The 18 new strings of Admin Panel / Problems / Password length and of the
"Change your password" dialog, written in the language and script each
locale tag names, with the terms each file already uses for password,
account, save and sign in, and __min__ kept exactly. Only those 18 keys
changed in each file, written with fill-translations.mjs --apply, which
fills English placeholders only.

Low confidence, for a speaker to correct on Transifex: ace, ak, an, ary,
ast-ES, ay, ba, bho, bi, bm, bo, br, bua, chr, co, csb, cv, cy, dz, ee,
ff, fj, fo, fur, ga, gd, gn, gv, ha, haw, hsb, ig, iu, jv, kl, kok, ks,
kw, lg, lld, mg, mi, mt, nah, nap, nd, nso, ny, om, pap, qu, rm, rn,
rup, rw, sah, sc, scn, sd, se, si, sm, sn, so, ss, st, szl, te-IN, tg,
ti, tig, tk_TM, tlh, tn, to, tpi, ts, tt, uz-AR, ve, ve-CC, ve-PP, vo,
wa, wa-RR, wal, wo, wuu-Hans, xh, yi, zgh.

Thanks to xet7 !
2026-10-31 15:18:13 +02:00
Lauri Ojansivu 85af731c1b Admin Panel / Problems / Password length names itself in the title bar.
The title bar keeps its own copy of the menu labels, and the new pane
had none: tests/adminUrls.test.cjs failed on problems/password-length.

Thanks to xet7 !
2026-10-31 15:18:11 +02:00
Lauri Ojansivu bb32a50550 CHANGELOG: sign-up works again, Admin Panel / Problems / Password length, and a changed password signs every session out.
Thanks to xet7 !
2026-10-31 14:56:22 +02:00
Lauri Ojansivu c99aa7f845 Admin Panel / Problems / Password length, and a changed password signs every session out.
PASSWORD_MIN_LENGTH (default 8) is now the length below which a sign-in
is REPORTED, in the new Admin Panel / Problems / Password length, and
PASSWORD_MIN_LENGTH_ENFORCE (off by default) is what makes it a rule: a
shorter new password is refused, and an account that signs in with an
older shorter one sees only "Change your password" until it has a long
enough one. Signing in is never refused, enforced or not, so nobody is
locked out by a minimum set after their password. LDAP, OAuth2, OIDC,
SAML, CAS and Sandstorm accounts are neither reported nor asked: their
password is their provider's. An administrator's Set password no longer
applies WeKan's policy to such an account.

Meteor sends a password hashed, so only the browser knows its length: it
notes it on the sign-in form and, after a sign-in that worked, says "it
was short" - never the password or its length.

The page has both settings and a table, one row per account: username,
first and last short-password login, the latest IPv4 and IPv6 address
with the country's flag and city when a CDN says where it is, the count,
and when it was fixed, with the Too short (default) / Fixed / All filter.
Fixed is a changed password not seen short since; a short one seen
again reopens the row.

Changing one's own password - Member Settings, the forced change, a
reset link - now signs EVERY session of the account out, the one that
changed it too, and only the new password signs in again. The browser
goes to the sign-in page as soon as it is signed out, since the server
closes the connection before the answer can arrive.

Documented in docs/Features/Admin-Panel/Problems/Password-length.md, the
settings on every platform. The new strings are in every locale in
English for now. Tests: passwordPolicy, passwordChangeRequired and
shortPasswordLogins (node); password-min-length-signup-login,
password-length-report and password-change-other-sessions (Playwright,
Chromium and WebKit), which change each setting on the page and check
what it does, and that sign-up with no mail server registers and signs
the user in.

Thanks to xet7 !
2026-10-31 14:54:22 +02:00
Lauri Ojansivu 508484ef1e Sign-up, and every method and publication, pass audit-argument-checks again.
Registering answered "Internal server error" - and created the account
anyway, so the next try said it already existed - and the
starredPublicBoards and translation publications failed for everybody:
"Did not check() all arguments". audit-argument-checks asks,
synchronously, the moment a handler returns, whether check() saw every
argument. The REQUIRE_2FA wrapper of every method and publication, and
the Audit log's wrapper of the administrators' methods, were async and
awaited before they called the handler - even with REQUIRE_2FA off - so
the handler's own check() came after the audit had already failed the
call, and the handler went on running.

A wrapper now calls the handler at once when it needs no lookup
(REQUIRE_2FA off, nobody signed in, a cached answer). When it must await
first, it hands the audit over (server/lib/argumentChecks.js): the
arguments count as seen for the outer audit, and the handler runs under
an audit of its own that it must satisfy exactly as unwrapped.

tests/wrappedHandlerArgumentChecks.test.cjs runs both wrappers under a
model of the check package's audit: a checking handler passes, off and
on, a refusal stays a refusal, a handler that does not check still fails
on every path, and no wrapper in server/ awaits before it has checked.

Thanks to xet7 !
2026-10-31 14:13:40 +02:00
Lauri Ojansivu 548196c9a9 CHANGELOG: Meteor in .tools/.meteor no longer springboards forever.
Thanks to xet7 !
2026-10-31 13:23:36 +02:00
Lauri Ojansivu 9a118c83b2 Meteor in .tools/.meteor no longer springboards forever.
A build from the menu printed uname -p and sysctl hw.cpu64bit_capable
over and over and never reached its first package. With
METEOR_WAREHOUSE_DIR set and no SANDBOX, Meteor takes the running tool
to be wherever the warehouse's "meteor" link points - 3.5.4, the release
it was installed with - so with .meteor/release at 3.6-rc.1 every start
springboarded to 3.6.0-rc.1, found itself the wrong tool again and
springboarded again: 28 times in 20 seconds of meteor --version. The
earlier 1.1 GB build log was the same loop, its module trace printed
anew on every exec.

tools-env.sh and tools-env.bat now set SANDBOX=true beside the
warehouse, as Meteor's own self-test does; that one check is all it
changes. The Nextcloud test scripts and the dev-source runner, which set
the warehouse themselves, do the same. meteor --version now springboards
once, from the linked 3.5.4 to 3.6.0-rc.1.

tests/meteorSpringboardLoop.test.cjs checks both env scripts, that
nothing sets the warehouse without SANDBOX, and runs the installed
Meteor to count its springboards.

Thanks to xet7 !
2026-10-31 13:23:24 +02:00
Lauri Ojansivu d442bd8a7e CHANGELOG: builds trace module resolution only with WEKAN_BUILD_TRACE_MODULES=1.
Thanks to xet7 !
2026-10-31 13:02:19 +02:00
Lauri Ojansivu 5a13e927bc Builds trace module resolution only with WEKAN_BUILD_TRACE_MODULES=1.
Turning the NODE_DEBUG=module trace off for EVERYTHING alone was not
enough: a dev build from the build.sh menu wrote 1.1 GB, three million
lines, in its first three minutes, before it was interrupted. The trace
is now opt-in in build.sh and build.bat alike, and the diagnostics line
says whether it is on and how to turn it on. The other build diagnostics
(resolver commands, npm verbose, METEOR_PROFILE) are unchanged.

Thanks to xet7 !
2026-10-31 13:01:57 +02:00
Lauri Ojansivu c78af24a36 CHANGELOG: EVERYTHING builds without the per-require module trace.
Thanks to xet7 !
2026-10-31 12:46:14 +02:00
Lauri Ojansivu 6551062492 EVERYTHING builds without the per-require module trace.
The build's NODE_DEBUG=module trace wrote over 12 GB in the first half
hour of an EVERYTHING run on macOS - twice, through tee - and slowed the
build it traced, so the run would fill the disk before reaching the tests.
A build from the menu still traces by default; WEKAN_BUILD_TRACE_MODULES=0
turns it off, and EVERYTHING sets that unless the caller asked for 1. The
other build diagnostics (resolver commands, npm verbose, METEOR_PROFILE)
are unchanged.

Thanks to xet7 !
2026-10-31 04:15:01 +02:00
Lauri Ojansivu a0ed5de6f1 CHANGELOG: the breached-password check's lookup digest (CodeQL #599).
Thanks to GitHub CodeQL and xet7 !
2026-10-31 03:52:47 +02:00
Lauri Ojansivu f406d46fe7 Breached-password check: one Web Crypto lookup digest for the browser and the server.
CodeQL #599 (js/insufficient-password-hash, CWE-916) flagged the SHA-1
the server computed for PASSWORD_CHECK_PWNED. That digest is not password
storage - accounts-password keeps passwords with bcrypt - and it is never
stored or logged: Have I Been Pwned's range API is keyed by SHA-1, its
first 5 hex digits are the question and the rest is compared in memory.
A slow hash cannot be used, because the service could not match it.

The browser and the server now compute it with one implementation,
breachLookupDigest in models/lib/passwordPolicy.js, which says what it is
and is not; the server no longer hashes a password with a hash of its
own, and a digest that cannot be computed lets the password through.
Tests pin that the digest is SHA-1 for ASCII and UTF-8, that it reaches
only the lookup and the comparison, and that nothing in WeKan hashes a
password-named value with a fast hash. Unreleased hardening, so no Hall
of Fame entry: docs/Security/Password-Breach-Check-Digest-2026-10-31.md.

Thanks to GitHub CodeQL and xet7 !
2026-10-31 03:52:32 +02:00
Lauri Ojansivu 4b0aa715eb Merge remote-tracking branch 'origin/main' 2026-10-31 03:36:43 +02:00
Lauri Ojansivu f5af33ac38 CHANGELOG: Audit log documentation.
Thanks to xet7 !
2026-10-31 00:44:17 +02:00
Lauri Ojansivu f165a24192 docs: Admin Panel / People / Audit log.
Thanks to xet7 !
2026-10-31 00:44:14 +02:00
Lauri Ojansivu e6ea524541 CHANGELOG: password policy, compulsory 2FA, idle sign-out, audit log, HSTS, cross-site guard and S3 encryption.
Thanks to xet7 !
2026-10-31 00:33:31 +02:00
Lauri Ojansivu de23dece66 Translations: the password policy, two-factor, audit log and S3 encryption strings in 234 languages.
The 16 strings the security features added were English placeholders in
every locale; each is now in the language and script its locale tag
names, with the terms the file already uses for password, account,
attachment, backup and two-factor authentication. __min__ is kept
character for character. Where the right word is spelled like the
English ("Administrator"), the column header says "Admin" or "Who"
instead, because a value equal to English is not a translation.

fy and fy-NL: the existing two-factor strings were Dutch in a Frisian
file, and are Frisian now; tests/frisianTwoFactorLanguage.test.cjs.
tig: Tigre spells storage መክዘን, not Tigrinya's መኽዘን.

Low confidence, for a speaker to correct on Transifex: ace, ak, an, ary,
as, ast-ES, ay, ba, bho, bi, bm, bo, br, bua, chr, ckb, co, csb, cv, dz,
ee, ff, fj, fy, gn, gv, haw, hsb, ht, ig, iu, kl, kok, ks, kw, la, lg,
lld, mg, mi, nah, nd, nso, ny, om, pap, ps, qu, rm, rn, rup, rw, sah, sc,
scn, sd, se, si, sm, sn, so, ss, st, szl, tig, tlh, tn, to, tpi, ts,
uz-AR, ve, ve-CC, ve-PP, vo, wa, wa-RR, wal, wo, xh, yi, zgh.

Thanks to xet7 !
2026-10-31 00:32:43 +02:00
Lauri Ojansivu 2017f2871f Audit log: its own Admin Panel URL and title, and right-to-left table alignment.
/admin/people/audit-log opens the pane and names it in the title bar like
every other People pane, and the table aligns to the start of the line,
so it reads right to left in Arabic, Hebrew, Persian and the rest.

Thanks to xet7 !
2026-10-31 00:32:33 +02:00
Lauri Ojansivu 302b47db6f docs: Security settings, every hardening option in one page.
Thanks to xet7 !
2026-10-31 00:15:13 +02:00
Lauri Ojansivu 482c2c29f4 Attachments, avatars and backups in S3 can be encrypted at rest.
Admin Panel / Attachments / S3 / Server-side encryption: SSE-S3 (AES256)
or SSE-KMS (aws:kms, with a KMS key or the account's default). S3
encrypts each object as it is written and decrypts it for WeKan on read,
so reads, range requests and moves are unchanged. Every attachment and
avatar upload, every renamed object and every backup written to S3 now
carries the choice; a value that is not one of them, or a malformed key,
adds nothing.

On the filesystem, encryption at rest stays with the disk or volume
(LUKS, an encrypted cloud volume): exports, PDF, thumbnails, virus scans,
backups and WebDAV all read the stored files directly, and encrypting
them inside WeKan would change every one of those and convert every file
already stored.

The new interface strings are pending translation.

Thanks to xet7 !
2026-10-31 00:12:09 +02:00
Lauri Ojansivu 4d5b832499 Admin Panel / People / Audit log: what site administrators did.
Nothing recorded who set somebody's password, made somebody an admin,
changed a login setting or restored a backup. Every call of those
administrative methods by a site administrator is now kept, one document
each, for a year: when, who, which action, the outcome - a refused call
too - its arguments and the address. A password or a secret never
reaches the trail: values under a credential-named key, the arguments
that are a password by position, and a password inside a URL are
replaced by [redacted]. An administrator editing cards is not in it,
the trail is shown to site administrators only, and a trail that cannot
be written never changes what the method did. It and the REQUIRE_2FA
wrapper keep each other's marks, so neither wraps the other twice.

The new interface strings are pending translation.

Thanks to xet7 !
2026-10-31 00:09:20 +02:00
Lauri Ojansivu 41173f0f6b Sign a browser session out after it has been idle for LOGOUT_IDLE_MINUTES.
LOGOUT_WITH_TIMER ends a login at a deadline however busy its user is;
nothing ended a session somebody walked away from. LOGOUT_IDLE_MINUTES
(Admin Panel / People / Login, every platform; empty or 0 is off) does:
the browser reports a key press, click, touch or scroll at most once a
minute, the server keeps the time per login token in idleSessions, and
once a minute every token idle past the limit is removed, which signs its
connections out. A tab that reconnects and resumes its login by itself -
a laptop waking up - is not activity and does not extend the session.
REST API tokens have no session record and are never removed. The time
is kept beside the user rather than on the token, because updating one
element of the token array needs the positional $ operator, which not
every database WeKan runs on supports.

Thanks to xet7 !
2026-10-31 00:08:20 +02:00
Lauri Ojansivu d7abe9910a Password policy for new passwords, and compulsory two-factor authentication.
New passwords: PASSWORD_MIN_LENGTH (default 8) and PASSWORD_CHECK_PWNED
(off by default), in Admin Panel / People / Login and on every platform.
The browser sends a password to Meteor hashed, so the server checks every
password that reaches it as text - Admin Panel Set password and Add user,
POST /users/register - and the sign-up, Change Password, reset and
enrolment forms check in the browser. Signing in is never checked, so an
existing shorter password keeps working until it is changed. A breached
password is found with Have I Been Pwned's k-anonymity range API: only 5
hex digits of the SHA-1 leave WeKan, and an unreachable service never
refuses a password.

REQUIRE_2FA: off (default), admins or all. It covers accounts that sign in
with a WeKan password; LDAP, OAuth2, SAML, CAS and Sandstorm accounts sign
in through an identity provider and are not asked. Such an account can
still sign in, sees only the 2FA set-up until it has 2FA, and until then
the server refuses every method and publication but signing in and
enrolling, HTTP routes do not accept its login and the REST API gives it
no token. Once required, 2FA cannot be switched off.

The new interface strings are pending translation.

Thanks to xet7 !
2026-10-31 00:07:58 +02:00
Lauri Ojansivu 48a2e97ea3 A state-changing request that logs in only with the cookie must come from WeKan itself.
POST /import-trello and other HTTP routes accept the login cookie as the
login, so a page on another site could make a signed-in browser send one.
The cookie is SameSite=Strict, which keeps it off such a request in
today's browsers; this is the second line, for older browsers, same-site
sibling hosts and routes added later. A POST, PUT, PATCH or DELETE whose
only login is the cookie must carry an Origin, or Referer, naming
ROOT_URL's host or the Host / X-Forwarded-Host it was sent to, which on
Sandstorm is the grain's own host. Token-authenticated API calls, Meteor's
cookie routes and DDP are not affected. A refusal is recorded in Admin
Panel / Problems as csrf.cross-site-cookie and never blocks the account
whose cookie it was.

Thanks to xet7 !
2026-10-30 23:52:09 +02:00
Lauri Ojansivu 4993242c5f Strict-Transport-Security on every response of an HTTPS site.
WeKan sent no HSTS header, so a link typed as plain HTTP or a downgrade on
a hostile network could strip TLS from the next visit unless the reverse
proxy added it. It is now sent whenever ROOT_URL is https: 180 days, this
host only. HSTS_MAX_AGE, HSTS_INCLUDE_SUBDOMAINS and HSTS_PRELOAD (Admin
Panel / People / Login, every platform) change it; 0 stops it, and preload
is sent only with includeSubDomains and at least a year. Every response
also carries X-Content-Type-Options: nosniff and
Referrer-Policy: strict-origin-when-cross-origin.

Thanks to xet7 !
2026-10-30 23:50:02 +02:00
Lauri Ojansivu 12eef31162 CHANGELOG: snap backup and restore instructions work on AlmaLinux, Rocky Linux and Fedora.
Thanks to Group-Deed and xet7 !
2026-10-30 23:14:01 +02:00
Lauri Ojansivu 0b5c855820 Snap backup and restore instructions work on AlmaLinux, Rocky Linux and Fedora.
snapd keeps the snaps in /var/lib/snapd/snap on Fedora, RHEL, AlmaLinux,
Rocky Linux, CentOS Stream and Arch Linux, which have no /snap, so the
instructions that exported /snap/wekan/current/bin and its lib directory
did not find mongodump and mongorestore there. They now pick the directory
with one S= line and the CPU's library directory with $(uname -m), and the
scripts that can be run from a host shell find the snap in either place.

Thanks to Group-Deed and xet7 !
2026-10-30 23:13:50 +02:00
Lauri Ojansivu 0c53ef6960 CHANGELOG: boards from before the templates feature are shown on All Boards again.
Thanks to xet7 !
2026-10-30 22:56:38 +02:00
Lauri Ojansivu 2ab0afdc93 Old boards without a type are shown on All Boards again.
Boards, swimlanes, lists and cards from before the templates feature have
no `type`: the schemas' defaultValue fills it on insert only. All Boards
publishes `type: 'board'` and the card queries ask for 'cardType-card', so
a Sandstorm grain migrated from MongoDB 3 opened onto an empty All Boards
page although its board was intact in the database. The new
type-defaults-backfill startup step sets the schema default on every
document whose type is missing, null or empty, and never touches an
explicit type.

Thanks to xet7 !
2026-10-30 22:56:24 +02:00
github-actions[bot] f2a1d7b9ba Bump versions for v12.75 2026-10-30 20:26:45 +00:00
Lauri Ojansivu cd1f1b8f05 Prepare v12.75 release 2026-10-30 22:25:30 +02:00
Lauri Ojansivu b941f36e4c Merge remote-tracking branch 'origin/main' 2026-10-30 22:25:01 +02:00
Lauri Ojansivu 487a528806 CHANGELOG: a changed password signs the account out in every other browser.
Thanks to xet7 !
2026-10-30 21:39:35 +02:00
Lauri Ojansivu dc9a7bb1ac Tests: a changed password signs the account out in every other browser.
A report described "failure to invalidate session on password change": the
same account signed in on two browsers, the password changed in one, and
the other's session "updated" rather than ended. WeKan does not do that.
Member Settings / Change Password is Meteor's changePassword, which removes
every login token but the changing session's; the admin's Set password is
setPasswordAsync, which removes them all, as resetPassword does; and a
session whose token is gone is closed, while WeKan's HTTP routes accept only
a token still in the list.

password-change-other-sessions.e2e.js runs the report in two browser
contexts: both signed in with the password, the password changed through
the form in one, and the other is signed out within seconds, stays signed
out after a reload, and only the new password signs in. It passes three
times in a row in Chromium and in WebKit. passwordChangeSessions.test.cjs
pins those paths and fails on a password change with logout: false, the
LDAP sign-in's password sync being the one stated exception.

Thanks to xet7 !
2026-10-30 21:39:20 +02:00
github-actions[bot] 696a1a2c8a Bump versions for v12.74 2026-10-30 08:15:31 +00:00
Lauri Ojansivu ef1e683cdd Prepare v12.74 release 2026-10-30 10:14:18 +02:00
Lauri Ojansivu 55163d0ce6 Merge remote-tracking branch 'origin/main' 2026-10-30 10:14:15 +02:00
Lauri Ojansivu b974ba426d CHANGELOG: Caddy and FerretDB run with no MPTCP listener.
Thanks to xet7 !
2026-10-30 04:31:14 +02:00
Lauri Ojansivu e9751b65d4 Caddy and FerretDB run with no MPTCP listener (CVE-2026-98432).
CVE-2026-98432 is a Linux kernel use-after-free in IPv4 TCP connection
handling, shown to give remote code execution through an MPTCP listener
(golang-announce, 2026-10). Since Go 1.24 every Linux listen socket of a
Go program is MPTCP by default, and WeKan ships two Go servers: Caddy, the
snap's web server on ports 80 and 443, and FerretDB, the default database.

Every place WeKan starts one sets GODEBUG=multipathtcp=0: the snap's
environment (every app, Caddy and FerretDB among them), the Docker
entrypoint and the bundle launcher - which keep an operator's own
multipathtcp setting - every docker-compose FerretDB service (the commented
FerretDB 2 example too), and the FerretDB the Sandstorm grain starts. Caddy
is an upstream binary, so no rebuild is needed: Go reads GODEBUG at start.
wekan/FerretDB builds with `godebug multipathtcp=0` as well. The real fix
is a patched host kernel.

Checked on Linux: a Go server's default listener reports MPTCP true;
GODEBUG=multipathtcp=0, or the go.mod setting, makes it false.
tests/goMptcpOff.test.cjs pins every launcher, runs the launcher line, and
fails when something turns MPTCP back on.

Thanks to xet7 !
2026-10-30 04:30:39 +02:00
github-actions[bot] 5abc2809b9 Bump versions for v12.73 2026-10-30 02:18:18 +00:00
Lauri Ojansivu 03f3a197cf Prepare v12.73 release 2026-10-30 04:17:07 +02:00
Lauri Ojansivu 6ed3f5cfd0 CHANGELOG: v12.73 was not released, so its section is Upcoming again.
"Prepare v12.73 release" renamed Upcoming, but its push was refused and
the release workflow was never dispatched: there is no v12.73 tag and no
v12.73 GitHub Release. As CLAUDE.md says for a release that published
nothing, the heading goes back to "# Upcoming WeKan ® release", so the next
release-all.sh takes v12.73 again. It also gains the entry for the fix that
keeps this from happening again.

Thanks to xet7 !
2026-10-30 04:16:04 +02:00
Lauri Ojansivu e372016826 Release All pulls before releasing, and pulls and retries when its push is refused.
v12.73 was prepared on a main that origin had moved past: the release
bot's v12.72 version bump had landed meanwhile. release-all.sh renamed
Upcoming and committed "Prepare v12.73 release", its bare git push was
refused, and set -e stopped it before gh workflow run - so no release ran,
and no tag was made, while the CHANGELOG already said v12.73.

release-all.sh now runs build.sh's git pull right after its checks, before
it chooses the version or renames anything, and checks the Upcoming section
again after it; and it pushes with build.sh's git push, which repairs moved
CHANGELOG links and pulls and pushes once more when origin moved meanwhile.
If the push still does not finish, it says that the workflow was NOT
dispatched, and the command to dispatch it.

releaseUpcomingPreflight pins the order - the pull before the version and
the rename, the push before the dispatch - and fails on a bare git push;
releaseTelemetry and the version-selection fixture follow.

Thanks to xet7 !
2026-10-30 04:14:48 +02:00
Lauri Ojansivu fd699b67e9 Merge remote-tracking branch 'origin/main' 2026-10-30 04:07:50 +02:00
Lauri Ojansivu 827641193f Prepare v12.73 release 2026-10-30 04:07:36 +02:00
Lauri Ojansivu fb7bd9c6de CHANGELOG: CodeQL alert #598, the Sandstorm host test.
Thanks to xet7 !
2026-10-30 04:02:02 +02:00
Lauri Ojansivu fcd995095d Tests: the Sandstorm host check names no host but sandstorm.org.
CodeQL alert #598 (incomplete URL substring sanitization, CWE-20) flagged
tests/sandstormOrgHosts.test.cjs, which skipped files that did not include
the old Sandstorm host name as a substring. The file no longer names that
host at all. It finds every Sandstorm host on a top-level domain other than
org by its shape, written as a URL - a scheme or // before it, or a port or
path after it - so property paths such as services.sandstorm.id, file names
such as sandstorm.js and the domains reserved for tests are not hosts. The
demo and the store workflow's older-market publish step stay allowed by
their subdomain and place.

A new self-test checks what it finds and what it does not; run against the
docs and workflows from before the sandstorm.org switch it finds 13 old
hosts, the installer address of release-all.yml among them.

Thanks to xet7 !
2026-10-30 04:01:36 +02:00
Lauri Ojansivu b7b5ec3711 CHANGELOG: the short CHANGELOG.md starts with the upcoming release.
Thanks to xet7 !
2026-10-30 03:56:32 +02:00
Lauri Ojansivu 1af642069c The short CHANGELOG.md starts with the upcoming release's In short.
CHANGELOG.md showed only the five newest releases, so what was about to be
released was only in CHANGELOG-big.md. changelog-short.mjs now puts the
unreleased # Upcoming WeKan ® release first - its In short and a link to
its full section - and the five newest releases below it; right after a
release, when there is no Upcoming yet, none is invented.

CLAUDE.md and AGENTS.md say what "update wekan changelog" means: find the
commits not in the changelog yet, add an Upcoming section when a release
was cut meanwhile, write the entries and the In short into
CHANGELOG-big.md, run changelog-short.mjs, run the changelog tests, and
commit both files.

tests/changelogShort.test.cjs: Upcoming first, whole, above the releases,
its link resolves, and no Upcoming appears after a release.

Thanks to xet7 !
2026-10-30 03:56:11 +02:00
Lauri Ojansivu 0343e6614b CHANGELOG: the short CHANGELOG.md, written from CHANGELOG-big.md.
Thanks to xet7 !
2026-10-30 03:30:23 +02:00
Lauri Ojansivu f1c0db247a CHANGELOG.md is the short changelog, written from CHANGELOG-big.md.
CHANGELOG-big.md is the changelog as it was: # Status, # Upcoming and every
release in full, with its <details> entries. Everything that writes or edits
the changelog uses it - release-all.sh, release-notes.sh and the release
workflow, the commit-link repair in build.sh and fix-changelog-hashes.sh,
the archive, the wrapper, the version scripts - and the release notes'
"More details at ChangeLog" link goes to its section. remote-release.py,
shared with FerretDB and the patch repositories, takes the file from its
config, and only WeKan's names CHANGELOG-big.md.

CHANGELOG.md is new: no Status and no Upcoming, only the In short paragraph
of each of the five newest releases and a link to its full section, in plain
Markdown - no <details>, <summary>, </summary> or </details>, and HTML links
written as Markdown links. releases/changelog-short.mjs writes it;
release-all.sh runs it right after naming the release, so it is in the
release commit, and changelog.sh does too. The Sandstorm App Market keeps
embedding CHANGELOG.md, now the short one.

old-CHANGELOG/'s headers say they moved out of CHANGELOG-big.md. References
to Upcoming or TODO Later in code comments and docs name CHANGELOG-big.md.
CLAUDE.md and AGENTS.md describe both files: CHANGELOG-big.md is written,
CHANGELOG.md is generated and never edited by hand.

tests/changelogShort.test.cjs: CHANGELOG.md is exactly what the generator
writes, the five newest releases with their whole In short, links that
resolve, and none of the four tags or an HTML link; the changelog tests
read CHANGELOG-big.md.

Thanks to xet7 !
2026-10-30 03:29:48 +02:00
Lauri Ojansivu 61e838df46 Rename CHANGELOG.md to CHANGELOG-big.md.
Only the rename, in a commit of its own, so that git log --follow and blame
keep the changelog's history; the next commit adds the new short
CHANGELOG.md and points the release tooling at CHANGELOG-big.md.

Thanks to xet7 !
2026-10-30 03:29:34 +02:00
github-actions[bot] 057cbc26d9 Bump versions for v12.72 2026-10-30 00:37:33 +00:00
Lauri Ojansivu 22e8b9bc86 Prepare v12.72 release 2026-10-30 02:36:31 +02:00
Lauri Ojansivu 602b47f06a CHANGELOG: new Sandstorm app market screenshots.
Thanks to xet7 !
2026-10-30 02:35:31 +02:00
Lauri Ojansivu a086323a1f Updated Sandstorm WeKan screenshots.
Thanks to xet7 !
2026-10-30 02:33:12 +02:00
Lauri Ojansivu 22140739ab CHANGELOG: card links never launch an OS handler, the Apache guide and Sandstorm publishing.
Thanks to ocdtrekkie and xet7 !
2026-10-30 01:26:40 +02:00
Lauri Ojansivu 55b71ce19e Tests: the Sandstorm store workflow may publish to apps.sandstorm.io too.
#6833 publishes WeKan to both Sandstorm app markets, apps.sandstorm.org
and the older apps.sandstorm.io, whose API is alpha-api.sandstorm.io.
sandstormOrgHosts.test.cjs refused every sandstorm.io host; it allows
that publish step in sandstorm-store.yml only, and checks that both
markets are published to.

Thanks to ocdtrekkie and xet7 !
2026-10-30 01:25:31 +02:00
Lauri Ojansivu 1a97c8bd86 Apache guide: no request captures in proxy targets, and httpd 2.4.60 or newer.
Checked against the 2024 Confusion Attacks on Apache httpd
(blog.orange.tw, CVE-2024-38472 to -38477 and CVE-2024-39573, fixed in
2.4.60). The guide's examples were reverse proxies only, with no
RewriteRule, but each passed the WebSocket with ProxyPassMatch and $1 - a
request capture in a proxy target, the shape those attacks abuse - and in
the sub-URL example that rule looked for ^/sockjs while the requests come
to /wekan/sockjs, so its WebSocket never connected.

- Both examples pass the WebSocket with "ProxyPass ... upgrade=websocket"
  on their fixed prefix (2.4.47+); no ProxyPassMatch remains.
- The guide asks for httpd 2.4.60 or newer, drops NameVirtualHost (a no-op
  in 2.4) and the path in the sub-URL ServerName, which is not valid.
- A "What not to add" section: no RewriteRule or ProxyPassMatch with a
  request capture in the target, no <Files>/<Location> ACL in front of
  WeKan, AllowEncodedSlashes Off and MergeSlashes On, <Directory /> denied.

Verified with Apache 2.4.67: both virtual hosts parse, and in front of a
WebSocket server both pass HTTP and the WebSocket, at / and at /wekan;
the old sub-URL example fails its WebSocket ("Unexpected server response:
200"). tests/apacheReverseProxy.test.cjs pins it and fails on the old guide.

WeKan's own redirects stay on its origin (CAS checks the callback's origin
against ROOT_URL; the file and avatar redirects build their own paths), so
a backend-chosen Location cannot be steered elsewhere.

Thanks to xet7 !
2026-10-30 01:24:48 +02:00
Lauri Ojansivu b4e5f90afb Card links never launch an OS handler with a code-execution history.
Custom URL schemes in card text link only when Admin Panel -> Features ->
URL lists them, and never javascript:, data: or another scheme that runs
code in the page. A listed operating-system handler was linked, though, and
some have a public code-execution history: ms-msdt (Follina,
CVE-2022-30190), search-ms and search, ms-officecmd, ms-appinstaller,
ms-cxh and ms-cxh-full, and telnet - the last step of the 2026 Edge full
chain, where Windows ran telnet.exe from a user-writable PATH directory.
An administrator listing one would let any board member plant a link that
runs something on whoever clicks it.

- They are never linked, listed or not, by the app's parser and by the
  markdown package's copy of the rule (the two lists must be identical),
  and a click on one does nothing should such a link reach the page.
- A card title or description, comment, checklist or checklist item that
  ADDS a link to one is shown in Admin Panel -> Problems (HandlerLink,
  CWE-939), with who wrote it; the text is saved as written and the writer
  is told nothing. The word in prose, or a link the text already had, is
  not recorded.
- docs/Features/Admin-Panel/Settings/Visibility.md lists both kinds of
  never-linked scheme, and says what a listed file: does on Windows (an SMB
  link outside the network sends the clicker's NTLM hash).

Tests: tests/urlSchemeAllowlist.test.cjs (positive and negative, through
the real DOMPurify, both sanitizer passes and the detector; fails on the
old code), and custom-url-schemes.e2e.js, which passes in Chromium and
WebKit: listed handlers do not link, a description saved with one through
the editor is recorded for its writer, and a kept link or a comment that
only mentions the word is not. Firefox does not start in this
environment.

Thanks to xet7 !
2026-10-30 01:19:16 +02:00
Lauri Ojansivu 2c1fc39f61 Fix Sandstorm store spk publish syntax.
Thanks to ocdtrekkie and xet7 !
2026-10-30 01:14:55 +02:00
Lauri Ojansivu ee82c21718 Merge pull request #6833 from ocdtrekkie/patch-1
Sandstorm: Publish to both app markets
2026-10-30 01:08:19 +02:00
Jacob Weisz 10851cd3f4 Sandstorm: Publish to both app markets 2026-10-29 18:04:45 -05:00
github-actions[bot] a937ed2180 Bump versions for v12.71 2026-10-29 22:39:44 +00:00
Lauri Ojansivu 22be2f2dc0 Prepare v12.71 release 2026-10-30 00:38:34 +02:00
Lauri Ojansivu 833c0bc021 CHANGELOG: two entries made after v12.70 go to Upcoming, not into v12.70.
The v12.70 release renamed Upcoming to v12.70 at 23:59, and the entries for
the Hall of Fame guards (43c4d41877) and the Sandstorm install address
(ca2dc5fae5), committed after it, were added to its Tests and Releases
groups - inside a released section, for commits that release does not
contain, and with no Upcoming section left for release-all.sh to take the
next version from. They move to a new Upcoming section; the v12.70 section is
again exactly what v12.70 shipped. changelogEntriesBelongToTheirRelease
passes.

Thanks to xet7 !
2026-10-30 00:36:01 +02:00
Lauri Ojansivu 1251c1c8b3 Merge remote-tracking branch 'origin/main' 2026-10-30 00:33:58 +02:00
Lauri Ojansivu 5b428faea6 CHANGELOG: the Sandstorm build installs from install.sandstorm.org.
Thanks to xet7 !
2026-10-30 00:28:08 +02:00
Lauri Ojansivu ca2dc5fae5 Sandstorm: build from install.sandstorm.org, and link sandstorm.org everywhere.
v12.69's build-sandstorm in release-all.yml stopped because the
certificate of install.sandstorm.io had expired. It fetches the installer
from install.sandstorm.org, as sandstorm.yml and sandstorm-store.yml
already did, and so does releases/install-sandstorm.sh. That installer
downloads Sandstorm from dl.sandstorm.org and configures a development
server at local.sandstorm.org, so the docs follow: docs, apps, dl, install
and local.sandstorm.io links and addresses become sandstorm.org in the docs,
SECURITY.md, code comments and the accounts-sandstorm README, each URL
checked to answer there. demo.sandstorm.io stays, as it has no sandstorm.org
name; released CHANGELOG sections are records and stay as they are. The
release audit's baseline allows the two code comments' docs.sandstorm.org.

tests/sandstormOrgHosts.test.cjs: every Sandstorm build installs from
install.sandstorm.org, and no tracked file names a sandstorm.io host
outside those kept on purpose; it fails on the old release-all.yml.

Thanks to xet7 !
2026-10-30 00:27:54 +02:00
Lauri Ojansivu b9f6eff3d9 CHANGELOG: the Hall of Fame guards read linked vulnerability names.
Thanks to xet7 !
2026-10-30 00:20:59 +02:00
Lauri Ojansivu 43c4d41877 Tests: the Hall of Fame guards read a linked vulnerability name too.
hallOfFameProblemsCoverage and securityRegressionCoverage collected the
names from <td valign="top"><b>Name</b></td>, so a name that was a link to
its page was never seen. Now that every name is one, the match takes the
link; the four it had missed - ImportPlacementBleed, SamlSubjectBleed,
StampBleed and UploadBleed - are listed as guarded by the suites that name
them, and the inventory count goes from 137 to 141.

Thanks to xet7 !
2026-10-30 00:20:47 +02:00
github-actions[bot] 609c6d3d64 Bump versions for v12.70 2026-10-29 22:00:25 +00:00
Lauri Ojansivu 8097935f5d Prepare v12.70 release 2026-10-29 23:59:10 +02:00
Lauri Ojansivu 4f7222f541 CHANGELOG: AppImages, Launchpad snap source, certificate errors and three test fixes.
Thanks to xet7 !
2026-10-29 23:50:04 +02:00
Lauri Ojansivu 6bbb4d39bc Downloads: an unverifiable TLS certificate is named as that, not as an outage.
v12.69's build-sandstorm stopped because install.sandstorm.io's certificate
expired that day (Let's Encrypt, notAfter 2026-10-29 19:02 GMT), and
fetch.sh reported "The server is having an outage" for curl's exit 60. It
now says the certificate could not be verified and that only that server's
owner can fix it. dl.sandstorm.io, where Sandstorm itself is downloaded
from, is valid.

Thanks to xet7 !
2026-10-29 23:49:36 +02:00
Lauri Ojansivu a1f2c006b4 Snap on Launchpad: fetch.sh is uploaded with the tools-env.sh it sources.
releases/prepare-launchpad-source.sh uploads only the files the snap recipe
reads, and listed releases/fetch.sh without releases/tools-env.sh, which
fetch.sh sources for its TMPDIR. In v12.69's armhf build the caddy part's
fetch.sh had no TMPDIR, mktemp made nothing, and curl got an empty -o seven
times ("option -o: is badly used here"), with pauses of up to 480s, before
it was reported as a server outage.

- tools-env.sh is uploaded, and it makes .tools/tmp in a tree that has only
  snapcraft.yaml and releases/, as the Launchpad source does.
- fetch.sh stops at once when it has no temporary file, and does not retry
  curl's exit 2 (it could not start) or call it an outage.
- tests: every releases/ file a listed script sources or runs must be
  listed; tools-env.sh and fetch.sh work in such a tree with no .git and no
  TMPDIR; both fail on the old list. fetch.sh's two new refusals are tested.

s390x failed earlier, on Launchpad: even a new build answered "Could not
find repository at path" three times.

Thanks to xet7 !
2026-10-29 23:48:30 +02:00
Lauri Ojansivu dfdadaa8ca AppImages: FerretDB and mongo-tools are readable by every user again.
releases/fetch.sh downloaded into a mktemp file and moved it into place, so
every download kept mktemp's 0600, and the bundled ferretdb and mongo-tools
were 0711 after chmod +x. A user other than the owner gets "Permission
denied" from such a file, and since today's appimagetool checks for it, all
six v12.69 AppImages (i686, x86_64, aarch64, armhf and both musl ones)
failed: "has mode 0711, but it must be readable for all users".

A download now gets the mode its umask gives a new file, and the AppDir is
made a+rX before it is packed. tests/releaseDownloads.test.cjs fails on the
old fetch.sh with 711 instead of 755 and checks that a stricter umask is
still followed; tests/appImageRuntime.test.cjs pins both.

Thanks to xet7 !
2026-10-29 23:44:34 +02:00
Lauri Ojansivu 856a32968b Tests: board drag settings open Card's Draggable table in its section hamburger.
Since Board Settings / Card became the card's Card Settings (52de57c844),
the Card Draggable table is in the Card section's hamburger, not in the
popup, so board-drag-settings.e2e.js failed in every language waiting for a
table that is no longer there. It opens Card the way the cog menu does and
then that hamburger, as board-settings-columns.e2e.js already does.

Thanks to xet7 !
2026-10-29 23:42:36 +02:00
Lauri Ojansivu e4b83d2274 Tests: the keyboard card move waits for the reset sorts to reach the client.
F02 F06 reset three cards to sort 0 in the database and waited only for the
minicards to show in id order. gamma 0, alpha .125, beta .25 already is id
order one time in six, so the wait passed while the client still had the old
sorts, the move was computed from them, and the card landed last instead of
second (1 in 3 runs here). The test waits for the client's own copy of all
three cards to read sort 0; 8 repeats pass.

Thanks to xet7 !
2026-10-29 23:28:31 +02:00
Lauri Ojansivu d06b468629 Tests: ReplyBleed's mocha test counts the attempt in every ReplyBleed row.
ReplyBleed is one Problems row per source, and the webhook's source is the
client address, so the bounce and board-address tests leave rows of their
own in the test database, which persists between runs. The test's findOne
picked one of those (source "ses"), whose count the spoofed reply never
moves, and failed although the attempt was recorded under 127.0.0.1. It sums
the count over all ReplyBleed rows, the way emailBounces.tests.js already
does.

Thanks to xet7 !
2026-10-29 23:07:04 +02:00
Lauri Ojansivu 379058bc9d CHANGELOG: OpenSSL 4 backup errors, Bun assets on Python 3.9, snap build logs, macOS test fixes.
Thanks to xet7 !
2026-10-29 22:52:42 +02:00
Lauri Ojansivu 5534c8830c Tests: the macOS suites run inside the sandboxed editor too.
mac-smoke-app.py checked that a killed descendant was gone with ps, which a
sandboxed macOS app may not run; it waits for os.kill(pid, 0) to find the
process gone and uses ps only to tell a zombie from a live one. macosSandbox
skips its live sandbox-exec check only when even an (allow default) profile
cannot be applied, which is the case inside the sandbox it is testing.

Thanks to xet7 !
2026-10-29 22:51:29 +02:00
Lauri Ojansivu 48a8937ff0 Snap on Launchpad: this attempt's build log is found by name, not only by mtime.
Whether a Launchpad build log belonged to the current attempt was decided by
[ "$log" -nt "$attempt_marker" ] alone. bash 3.2 compares whole seconds, so a
log written in the marker's second was not "newer": it was neither printed
nor checked for an infrastructure failure. this_attempts_log() counts a log
whose name did not exist when the attempt started, or one rewritten after the
marker; an earlier attempt's untouched log still does not count. Used by
infra_failure() and by the log printing, and tested for all three cases.

Thanks to xet7 !
2026-10-29 22:51:29 +02:00
Lauri Ojansivu 2dcccf0d4a Bun release assets: runs on Python 3.9, which macOS ships as python3.
hashlib.file_digest is Python 3.11+ and Path.write_text(newline=) is 3.10+,
so releases/bun/assets.py failed with AttributeError/TypeError on macOS's
python3 3.9. It hashes in chunks and writes the LF-only .sha256sum through
open(newline='\n'), and the test still pins LF on every platform.

Thanks to xet7 !
2026-10-29 22:51:29 +02:00
Lauri Ojansivu bf266fb4da DB conformance: a live run is still recognised where ps is not allowed.
In a sandboxed macOS app ps fails with "operation not permitted", so every
live conformance run looked dead and a second run would remove its
containers. command_line_of() falls back to pgrep -lf, which still works
there. The test pins the fallback.

Thanks to xet7 !
2026-10-29 22:51:29 +02:00
Lauri Ojansivu ffe49dd2fd Continuous backup: a changed encrypted file is reported as changed, also with OpenSSL 4.
A Node.js linked to OpenSSL 4 reports an AES-GCM tag mismatch as
ERR_OSSL_BAD_DECRYPT ("bad decrypt") instead of OpenSSL 3's "Unsupported
state or unable to authenticate data", so decryptFile() let the raw OpenSSL
error through instead of "An encrypted backup file has changed or the key is
wrong". Both are recognised now; tests/continuousBackupEncryption.test.cjs
passes with OpenSSL 4.1.0 and 3.5.9.

Thanks to xet7 !
2026-10-29 22:51:29 +02:00
github-actions[bot] ee16bdce6b Bump versions for v12.69 2026-10-29 20:03:10 +00:00
Lauri Ojansivu 44bae91f8e Prepare v12.69 release 2026-10-29 22:02:00 +02:00
Lauri Ojansivu ae13eab23e SECURITY.md, CLAUDE.md, AGENTS.md: a report without a GHSA goes from 3 to 5 stars when its GHSA arrives.
A researcher who sent a report to security@wekan.fi, or some other way, and
is later able to add it as a GitHub security advisory: the maintainer
requests the CVE through that GHSA, and the Hall of Fame row's 3 stars
become 5. SECURITY.md says so to researchers. CLAUDE.md and AGENTS.md tell
an AI updating or auditing the Hall of Fame to check each such 3-star row
against the advisories, match by the report's subject and reporter, and
upgrade the index row, its own page, the CVE cell, the Process cell and the
star audit together.

Thanks to xet7 !
2026-10-29 21:52:15 +02:00
Lauri Ojansivu f5f6ad4d4d CHANGELOG: ImportPlacementBleed, and Release all missing builds after a failed rerun.
Thanks to Zachariah Mithani (AploSec) and xet7 !
2026-10-29 20:27:59 +02:00
Lauri Ojansivu 0d6d481fcb ImportPlacementBleed: an import into a board is placed only beside that board's own list, swimlane or card.
The scoped importer, reached by POST /api/import/zip and the importScoped
method, authorized the import on boardId alone. The listId and swimlaneId
beside it were written onto the new cards as given, and a cardId was looked
up with no board filter, so a writer of board A could create cards on A
whose list and swimlane were board B's, and could probe whether a card id
existed on a board they cannot see.

Before anything is written, the list, swimlane and card are now looked up
together with the board's id; an id of another board is refused (400 over
REST, error-import-placement from the method), with the same answer for a
foreign card and a missing one. Cards are placed only by what was found on
the board. A refusal is recorded in Admin Panel / Problems as
ImportPlacementBleed (authz.import-placement), without blocking the account.

Tests: the decision with the reporter's ids, the check before every write,
a negative scan for a target id written or looked up without the board, and
a browser test of the reporter's attack by both routes, which fails on the
previous code.

Thanks to Zachariah Mithani (AploSec) and xet7 !
2026-10-29 20:27:14 +02:00
Lauri Ojansivu 99d9958e39 Release all missing: the missing assets are built even when the rerun of failed jobs fails again.
The planner needed recover, so one job failing again in the rerun of the
full release skipped the planner and every build after it, and assets that
could be filled were not. For v12.68 the rerun failed on four Launchpad
snaps (503 / timeout) and on the OpenBSD bundle, whose start-wekan.sh fault
is in the tagged code and so in every rerun of it. The planner now runs
unless the run is cancelled, and each job after it checks the planner's own
result, since an implicit success() also counts recover. recover stays red
and now names the jobs that failed again.

Thanks to xet7 !
2026-10-29 20:15:32 +02:00
Lauri Ojansivu daae36b313 CHANGELOG: Invite via Email adds the invited new user, not the first user of the database.
Thanks to momosari and xet7 !
2026-10-29 17:36:01 +02:00
Lauri Ojansivu 8556459f97 Invite via Email adds the invited new user, not the first user of the database.
Meteor 3's server Accounts.createUser is createUserAsync. inviteUserToBoard
called it without await, so the new user's id was a Promise; findOneAsync
read the Promise as the empty selector {} and returned the FIRST user,
usually the site administrator. That user was added to the board and got
the invitee's language and invitedBoards, while the invitee was created but
not added. It was not FerretDB: any database did the same.

createUser and sendEnrollmentEmail are awaited (an enrollment email that
fails is logged, as the invitation email is), and the user found must be
the one just created. Every server ReactiveCache getter now refuses a
Promise as its id or selector, so a missing await fails loudly instead of
picking the first document. Tests: the guard and the reporter's case in
memory, a scan that no server file calls an async Accounts API without
await, and a browser test that inviting a new address adds exactly that new
account - it fails on the previous code.

Thanks to momosari and xet7 !

Fixes #6831.
2026-10-29 17:35:21 +02:00
Lauri Ojansivu 7f9e8ea7a3 CHANGELOG: History on FerretDB, date paths as strings, migration reports refused documents.
Thanks to vijlautens and xet7 !
2026-10-29 16:58:42 +02:00
Lauri Ojansivu 01968fc637 History on FerretDB: date paths are stored as strings, and the migration reports refused documents.
A History row of a date-valued custom field held datePaths as an array of
path arrays, [["3","value"]]. FerretDB v1 does not store an array inside an
array, so on FerretDB the change was not recorded, and migrateTextDatabase
to FerretDB dropped those rows while it swallowed the per-document error and
still reported success.

Each path is now a JSON string, '["3","value"]'. Rows already written in
the array form on MongoDB still restore, and the migration to FerretDB
rewrites them to strings. The migration counts every document the target
refuses and ends as failed with the count per collection and the first
error, instead of success: true.

Thanks to vijlautens and xet7 !

Fixes #6832.
2026-10-29 16:58:21 +02:00
Lauri Ojansivu 04c2598cf4 Merge remote-tracking branch 'origin/main' 2026-10-29 10:55:23 +02:00
Lauri Ojansivu 77ded27994 CHANGELOG: OpenBSD bundle starts again.
Thanks to xet7 !
2026-10-29 08:25:49 +02:00
Lauri Ojansivu ec6cf66bff OpenBSD bundle starts again: start-wekan.sh takes only a number from sysctl.
OpenBSD's sysctl prints nothing and exits 0 for a name it does not have, so
`sysctl -n hw.memsize || sysctl -n hw.physmem` stopped at the empty
hw.memsize and $(( / 1048576 )) ended start-wekan.sh before WeKan answered.
Both start-wekan.sh copies now try hw.memsize, hw.physmem64 (NetBSD, whose
hw.physmem is 32-bit) and hw.physmem in turn and take the first numeric one.
The test runs the lines against fake OpenBSD, NetBSD and macOS sysctls, and
shows the old chain failing on the OpenBSD one.

Thanks to xet7 !
2026-10-29 08:25:38 +02:00
github-actions[bot] 8561f49ed9 Bump versions for v12.68 2026-10-29 05:16:53 +00:00
Lauri Ojansivu 749e9b76a7 Prepare v12.68 release 2026-10-29 07:15:43 +02:00
Lauri Ojansivu ad43de3c01 CHANGELOG: Card Settings, two panes, the card and the minicard follow its order, menus inline.
Thanks to xet7 !
2026-10-29 02:03:11 +02:00
Lauri Ojansivu 2bd5d1180a Card Settings: two panes, the card and the minicard follow their order, menus inline.
The opened card and the minicard now draw Card Settings' sections, in
Card Settings' order, each section's fields in the order of its rows:
- Section order is per side: cardSettingsSectionOrder for the card,
  the new minicardSettingsSectionOrder for the minicard. A board that
  stores none: the card starts with Labels, Date, Members, Card, ...
  (the card's historical order as far as the grouping allows), the
  minicard with what it drew first before.
- CARD_LAYOUT's and MINICARD_LAYOUT's reorderable sections are Card
  Settings' sections, so dragging a section or a row in Card Settings
  moves it on the card or the minicard. Stored field orders map: a
  field keeps its place relative to the others of its new section.
- On the card every section has the shared heading (its icon, title,
  caret and hamburger) - Voting and Planning Poker too, which had a
  heading of their own. The minicard stays compact: no headings.

Card Settings shows both sides at once: the minicard's pane first (on
the left), the card's second, each with a caret that folds it, each
scrolling on its own, stacked when the popup is too narrow for two.
The Show radio is gone. A splitter between the panes changes their
widths by dragging, like spreadsheet columns (clamped, kept per user
as a fraction, reset by a double-click, moved by the arrow keys). The
popup is wider again for two panes and its corner grip resizes it,
the panes following.

A hamburger's content opens under its line inside the pane, not in a
popup: the hamburger shows it is open (aria-expanded, highlighted, the
panel attached to it); its button, the panel's X or Escape closes it.
A custom field's edit form and the + create form open inline the same
way and close only their panel when saved. The popup layer added in
9c9742e09d is removed, as nothing uses it now.

Docs: Card-Settings.md, Card-Fields.md and Board Settings / Card.md.
New tests cardSectionsOnCard, minicardSections, cardSettingsSplit,
cardSettingsInlinePanels and card-settings-sections-order.e2e.js; the
guards of the old layouts and orders say why they changed.

Thanks to xet7 !
2026-10-29 02:02:54 +02:00
Lauri Ojansivu eddfd515ea CHANGELOG: Card Settings, hamburger menus open on top of it, next to their button.
Thanks to xet7 !
2026-10-28 23:23:18 +02:00
Lauri Ojansivu 9c9742e09d Card Settings: hamburger menus open on top of it, next to their button.
A hamburger of Card Settings opened its menu in Card Settings' place:
the popup slid to the menu and Card Settings disappeared until its back
arrow. Its menus, and the forms it opens (a custom field's edit form,
the + under Custom Fields, Subtask settings), now open as a popup
LAYER: on top of Card Settings, next to the button that opened them,
with Card Settings visible under it and scrolled where it was.

Popup.open(name, { layer: true }), from inside a popup, pushes the
layer on the stack as before, so Popup.back() - the layer's X, Escape,
a form's Save - closes the layer alone. The popup template draws the
popup under it with the layer on top, outside the scrolling content;
depth counts the sliding popups only, so a confirmation opened from a
layer still slides. One layer at a time: another replaces it, its own
button closes it, a press elsewhere in the popup closes it.

A layer opens toward the middle of the popup: a hamburger at the end of
a line gets its right edge, the + on the left its left edge, so the
create form no longer ran out of the popup's left side where its Save
could not be clicked; below the button, or above when there is more
room, its height and width capped to the room
(models/lib/popupLayerPosition.js). A menu's checkboxes start at its
edge rather than .check-div's form indent.

tests/popupLayer.test.cjs runs the placement and pins the Popup wiring
and that every Card Settings menu opens as a layer;
card-add-fields.e2e.js checks Card Settings stays visible under the
menu, the menu sits by its button, toggles, closes on a press elsewhere
and by its X alone, and that the + form opens inside Card Settings.

Thanks to xet7 !
2026-10-28 23:23:08 +02:00
Lauri Ojansivu 0250e54a07 CHANGELOG: Card Settings, a checkbox per section, every field always shown, only in Board Settings.
Thanks to xet7 !
2026-10-28 22:44:02 +02:00
Lauri Ojansivu 3e5bc58105 Card Settings: a checkbox per section, always every field, only in Board Settings.
Card Settings is now only Board Settings / Card. The opened card has no
Card Settings button any more: it simply shows every field its board
shows on cards, empty or not.

- On this card, Show empty and Show empty fields are removed, with the
  hamburgers' Select all / Select none rows, their handlers, helpers,
  board methods (setCardFieldAlwaysShown, setShowEmptyCardFields), CSS
  and the three translation keys. The card no longer reads its own
  shownFields / hiddenFields, nor the board's cardFieldsAlwaysShown /
  showEmptyCardFields; the schema keeps them so old documents validate.
- Every section has a checkbox before its title: whether the whole
  section is shown on the chosen side, whatever its rows are ticked.
  Stored per side in cardSettingsHiddenOnCard / cardSettingsHiddenOnMinicard
  (setCardSettingsSectionShown, board admins). The opened card's view
  of the board and a new minicard view read false for every row of a
  hidden section, Scrum fields included; Card Settings itself reads the
  raw board, so the rows keep their ticks.
- A section with one row on the chosen side is one line: one checkbox,
  the name, one hamburger.
- A hamburger is drawn only when it holds something.
- Custom Fields: its items are the board's custom fields, in their sort
  order, reordered by drag and drop within the section, with a + under
  them that creates one. Show on Minicard sets a field's showOnCard,
  Show on Card its alwaysOnCard; its hamburger edits the field. The
  section's checkbox is the board's Custom Fields flag of that side.
- Card actions (Filter: Subtasks, Edit voting, Card kind, ...) live in
  the card's own section hamburgers; Card Settings keeps only board
  actions.

Docs: docs/Features/Cards/Card-Settings.md replaces
Card-Settings-On-Card.md; Card-Fields.md and Board Settings / Card.md
rewritten. New tests cardSettingsSectionShown and
cardSettingsCustomFields; the guards of the removed behaviour say why
they changed; card-add-fields.e2e.js covers section hiding per side, an
empty card showing every field, empty hamburgers, the one-row line, the
Custom Fields items and +, and that the opened card has no Card
Settings.

Thanks to xet7 !
2026-10-28 22:43:29 +02:00
Lauri Ojansivu 039935dd0b CHANGELOG: Card Settings, a space between a checkbox and its label.
Thanks to xet7 !
2026-10-28 20:46:50 +02:00
Lauri Ojansivu 5adfc1b9db Card Settings: a space between a checkbox and its label.
On the rows of Card Settings - on the opened card and in Board Settings /
Card - and in their hamburgers' popups, a checkbox touched its label:
the rows' grid had lost its column gap with the old pane's CSS, and a
hamburger's option put the box and the text side by side with none.
Both now have a space's width (0.3em) between them.

A ticked box still touched the label, because a tick is drawn rotated
and reaches about 3px past its box. Left to right it is drawn 3px
further left, so a ticked box keeps the same space and the label does
not move when it is ticked; right to left the tick leans away from the
label and stays as it was.

tests/cardSettingsOnCard.test.cjs pins the gaps and the tick rule, and
card-add-fields.e2e.js measures at least 3px for ticked and unticked
boxes on the rows and in Card's hamburger, with every label in the same
place.

Thanks to xet7 !
2026-10-28 20:46:42 +02:00
Lauri Ojansivu f269b1ef16 CHANGELOG: Board Settings / Card shows the same checklists as the opened card, by the same templates.
Thanks to xet7 !
2026-10-28 20:23:42 +02:00
Lauri Ojansivu 52de57c844 Board Settings / Card is the opened card's Card Settings, same templates.
Board Settings / Card had its own layout: an Admin Panel left menu of
sections and a pane with two lists, column headings and per-column Select
all. It now renders the same template as the opened card's Card Settings
(cardSettingsInline), with no card: Show on Minicard / Show on Card, the
sections as checklists, their rows as the items, and the same section and
row hamburgers. The popup's header is the title and the close button, so
the inline's own title and X are not drawn there, and a row's hamburger
has no On this card. The popup is 600px wide, about an opened card,
instead of 1200px.

The popup no longer registers the Card Settings handlers itself: the
template inside it does, and registering them on both would handle every
click twice. The left menu, the pane, the column Select all, the
in-place action forms and the personal-only mode are removed with their
helpers and CSS. A non-admin sees what a card shows them: the board's
rows disabled, their own rows (Labels text, List title) usable.

Dragging, on the card and in Board Settings / Card:
- With drag handles on, a row or a section is dragged only by its
  handle; the rest of the line scrolls the page. With them off, by its
  icon and name.
- A row dropped on another section goes back where it was and nothing
  is saved: the sections are unrelated kinds of fields. jQuery UI fires
  a delayed "out" just before "stop", so where it was dropped is read
  in beforeStop.

Found while testing both open at once:
- Each Card Settings has its own Show radio group name; one name made
  the card's and the popup's radios one group.
- A section hamburger's Select all acts on the Card Settings it was
  opened from.
- Rows are keyed, so switching Show moves each row with its side instead
  of relabelling rows in place a flush before they reorder, when a
  click could land on the neighbouring field.

Docs: Card-Settings-On-Card.md, Card-Fields.md and Board Settings /
Card.md. The node guards that pinned the old layout now pin the shared
template, with negatives that the old layout and the double handler
registration are gone; card-add-fields.e2e.js compares the two views
section by section and row by row on both sides, and tests the drag
rules, and the specs that drove the old pane drive the new one.

Thanks to xet7 !
2026-10-28 20:23:05 +02:00
Lauri Ojansivu 81e6988491 Southern Sotho: "dikarolo", not "masimo", for data fields.
Three Southern Sotho strings called a data field "masimo", which means
farm fields: Show empty fields in Card Settings, the five fields of a
cron schedule, and the webhook's optional fields. They now use
"dikarolo", the word the file already uses for card and custom fields.
sesotho-popup-titles.e2e.js, which checks that Card Settings names data
fields as such, now reads Card's "Show empty fields" in Board Settings /
Card and caught the first. The cron and webhook wording is low
confidence; a Southern Sotho speaker's correction on Transifex replaces
it.

Thanks to xet7 !
2026-10-28 20:22:56 +02:00
Lauri Ojansivu 8b4987d5bd CHANGELOG: the Data health entry no longer repeats its group's name.
Thanks to xet7 !
2026-10-28 18:50:35 +02:00
Lauri Ojansivu e12106ce7a CHANGELOG: Meteor 3.6-rc.1, with WeKan's six Meteor package forks rebased onto it.
Thanks to xet7 !
2026-10-28 18:50:00 +02:00
Lauri Ojansivu 120f484a30 Meteor 3.6-rc.1, with WeKan's six Meteor package forks rebased onto it.
meteor update --release 3.6-rc.1 failed with "No version of
shell-server satisfies all constraints" (and the same for
dynamic-import, allow-deny, ddp-server, facebook-oauth and
google-oauth): those six are WeKan's forks in packages/, made because
Meteor 3.6-rc.0 lacked the 3.5.3 security fixes, and pinned to rc.0's
versions.

Meteor 3.6-rc.1 carries the security fixes of 3.5.3 and 3.5.4, but not
what is WeKan's own in the forks: the Admin Panel / Problems reports of
refused attempts, and the crypto-RNG keys of dynamic-import and meteor
shell (rc.1 still makes them with Math.random()). So the forks are kept
and rebased: each is rc.1's package with WeKan's changes over rc.0's
package merged three-way onto it. The 3.5.3 hunks merged away, as rc.1
has them; what remains over rc.1 is only the hooks and the keys. The
versions are rc.1's with a _1 wrap number.

@meteorjs/rspack moves to 3.0.0-rc.2 with the release. The Dockerfile's
METEOR_RELEASE and the snap description follow. The experimental Bun
build's Meteor patch applies unchanged to 3.6-rc.1; prepare-meteor.sh
now checks for rc.1's tag commit and applies meteor-3.6-rc.1.patch, and
the rc.0 copy is removed.

tests/meteor353SecurityFixes.test.cjs and meteorForkRandomKeys.test.cjs
are moved to rc.1 with their assertions kept;
meteor-353-security.e2e.js passes on rc.1 in Chromium and Firefox.

Thanks to xet7 !
2026-10-28 18:49:59 +02:00
Lauri Ojansivu 08ff3327c4 Card fields guard: the Scrum section filter asks the board first.
tests/cardShownFields.test.cjs still pinned the Scrum filter of the
opened card's sections as it was before #6830, which now requires the
board's Scrum visibility before asking the card.

Thanks to jullbo and xet7 !
2026-10-28 18:49:18 +02:00
Lauri Ojansivu d74dbbe409 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-10-28 18:43:49 +02:00
Lauri Ojansivu 2fd0c77164 Merge remote-tracking branch 'origin/main' 2026-10-28 18:43:48 +02:00
Lauri Ojansivu 6e18251e5f CHANGELOG: Show empty fields shows no Scrum section the board has turned off (#6830).
Thanks to jullbo and xet7 !
2026-10-28 18:28:57 +02:00
Lauri Ojansivu 0a224b3cea Show empty fields shows no Scrum section the board has turned off.
With Board Settings / Card's "Show empty fields" on, every opened card
showed the "Scrum settings" heading and its sections although Board
Settings / Scrum had none of its fields turned on. The Scrum fields are
the card's only sections with no board flag in the template, and
orderedSectionsOf asked only whether the card shows the field, which
Show empty fields answers yes for every field. A Scrum section now
needs the board's Scrum visibility (boardAllows) first, as Members,
Vote and Poker already do.

Thanks to jullbo and xet7 !

Fixes #6830.
2026-10-28 18:28:31 +02:00
Lauri Ojansivu a834211ab6 CHANGELOG: Card Settings on the opened card, its sections as checklists.
Thanks to xet7 !
2026-10-28 14:39:48 +02:00
Lauri Ojansivu c0ec28e82f Card Settings on the opened card, its sections as checklists.
The opened card's "+ Add" button is now "Card Settings". Clicking it no
longer opens the Board Settings / Card popup: the button and everything
below it on the card are replaced by Card Settings itself, until the X at
the end of its title puts the card back. Top to bottom:

- Card Settings, with its X (close) at the right of the title.
- "Show" and two radio buttons, Show on Minicard and Show on Card, which
  choose what the checkboxes below are.
- The sections (Card, Date, Time, ... Comments) drawn like checklists: the
  section is the checklist's title, with a count of the ticked rows and a
  hamburger; its rows are the checklist items. An item's checkbox is the
  board-wide Show on Card or Show on Minicard of that field.
- A row's hamburger holds On this card and Show empty; a section's holds
  Select all / Select none of each column, the section's card actions and,
  for Card, Show empty fields, Ask for a reason and the Draggable table.

Sections and rows reorder by drag and drop like checklists and their items,
and by the Up and Down keys. A section's order is the board's new
cardSettingsSectionOrder (normalised by sectionOrder() in
models/lib/cardSettingsSections.js); a row's is the card's or minicard's
field order, as in the popup. Sections collapse like checklists, kept per
user and board. The rows, checkboxes and handlers are the popup's, shared
rather than copied, and Board Settings / Card still opens the popup.

Design: docs/Features/Cards/Card-Settings-On-Card.md.

New strings: "Show" (card-settings-show) and the two hamburgers' titles,
"Actions". Every language takes its existing translation of the same
words; "Show" was written directly, with low confidence, for an, ary, bi,
csb, cy, cy-GB, fur, hsb, hy, jv, ka, kk, km, km-KH, km_KH, kok, lb, lld,
mai, mn, oc, or_IN, pa, sc, sn, so, szl, te-IN, tg, tk_TM, tl, tpi and tt.

Tests: tests/cardSettingsOnCard.test.cjs (the order rules, the layout, the
shared handlers, the translations) and
tests/playwright/specs/card-add-fields.e2e.js rewritten for the card's Card
Settings, the popup's own tests now opening it from Board Settings. The
guards that pinned the card template's indentation and the old opener say
why they changed.

Thanks to xet7 !
2026-10-28 14:39:29 +02:00
Lauri Ojansivu 6dcb9fa6e2 CHANGELOG: Data health cleans up custom fields whose boards were all deleted (#6829).
Thanks to Raymond-Voogt and xet7 !
2026-10-28 13:51:17 +02:00
Lauri Ojansivu 9dc3fc3f31 Data health: custom fields whose boards were all deleted get their own check.
customfield-dead-board counted every custom field that names a deleted
board, but its Repair only removes dead boards from a field that is also on
a live board; a field whose boards were ALL deleted was skipped on purpose,
since removing them would leave it on no board. So those fields stayed in
the count with a Repair button that could never change it: one instance
showed 510 and "Repaired: 0" after every click.

- New check customfield-boards-deleted: fields whose boards no longer exist
  (through boardIds or the legacy boardId). No existing board shows them.
  Repair deletes the definitions after the confirming second click, like
  the other repairs that delete data. A value a card on a live board still
  holds is left alone and stays listed under card-dead-field-values.
- customfield-dead-board counts only fields that still have a live board,
  so its count goes to zero when its Repair has run.

docs/Features/Admin-Panel/Problems/Data-Health.md lists the new check.
Test: tests/dataHealth6818.test.cjs.

Thanks to Raymond-Voogt and xet7 !

Fixes #6829.
2026-10-28 13:50:49 +02:00
github-actions[bot] e0eeae82d6 Bump versions for v12.67 2026-10-28 03:56:20 +00:00
Lauri Ojansivu b349576a15 Prepare v12.67 release 2026-10-28 05:55:24 +02:00
Lauri Ojansivu d519091909 Merge remote-tracking branch 'origin/main' 2026-10-28 05:13:33 +02:00
Lauri Ojansivu 55d64e6493 CHANGELOG: WeKan vs Trello vs Jira dates.
Thanks to xet7 !
2026-10-28 05:10:26 +02:00
Lauri Ojansivu cbdff47edb Docs: WeKan vs Trello vs Jira says when WeKan added the features it listed as missing.
The page listed Trello's Email Inbox, Complete Card Checkbox, Workspaces,
Butler scheduled and repeating tasks and Card Repeater as not yet in WeKan,
and Sync Lists and Map Card as WeKan ideas not yet implemented. Each now has
the WeKan release that added it: Workspaces v8.17 (2025-11-06), the complete
checkbox and card location v9.42 (2026-06-13), Scheduled Rules and recurring
cards v9.46 (2026-06-15), list sync v11.69 (2026-09-11), the Map view v12.10
(2026-09-29) and Email to card v12.40 (2026-10-14). The links to the 2018 and
2021 releases point to old-CHANGELOG/, where those sections are now.

Thanks to xet7 !
2026-10-28 05:10:21 +02:00
Lauri Ojansivu b36092896b CHANGELOG: the Add card form closes after a card (#6827), and the Bun guard's mongo:9.0.4.
Thanks to brlin-tw and xet7 !
2026-10-28 04:47:19 +02:00
Lauri Ojansivu 70de255333 Add card: the form closes after the card is added, unless Member Settings keeps it open.
A list's Add card form stayed open after a card was added, for the next
card, which the reporter found unexpected. It now closes once the card - or
every card of a multi-line paste - is added. Member Settings has a new
switch, "Keep the Add card form open after adding a card", off by default,
for who types several cards in a row. The close happens after the last
title, not at submit, because addCard still reads the form between the
titles of a paste; a failed add keeps the form and its text.

The two new strings are translated into every locale. Low confidence: ace,
ak, am, an, ary, as, ast-ES, ay, ba, bho, bi, bm, bo, br, bua, chr, ckb, co,
csb, cv, dz, ee, ff, fj, fo, fur, gn, gv, ha, haw, hsb, ig, iu, kl, kok, ks,
kw, lb, lg, lld, mai, mg, nah, nap, nd, nso, ny, om, or_IN, pap, ps, qu, rn,
rup, rw, sah, sc, scn, sd, se, sm, sn, so, ss, st, szl, ti, tig, tlh, tn,
to, tpi, ts, uz-AR, ve, ve-CC, ve-PP, vo, wa, wa-RR, wal, wo, wuu-Hans, zgh.

tests/keepAddCardFormOpen6827.test.cjs pins the switch, its default and
where the form is closed, with negative cases for a paste and a failure.
tests/playwright/specs/keep-add-card-form-open-6827.e2e.js drives it: closed
after one card and after a paste, kept open with an empty title once the
switch is on. multiline-titles opens the form again for its second card, and
assist-due-from-title keeps it open to check the next card's form has no
leftover hint.

Thanks to brlin-tw and xet7 !

Fixes #6827,
2026-10-28 04:46:52 +02:00
Lauri Ojansivu bce686ce46 Tests: the Bun smoke-test guard pins mongo:9.0.4, not the mongo:8 that stopped starting.
tests/bunSmokeMongo.test.cjs required the workflow's docker run to say
mongo:8, the floating tag that refused to start on the runner's 6.19+ kernel
in v12.66. It now requires mongo:9.0.4, and fails if mongo:8 comes back.

Thanks to xet7 !
2026-10-28 04:42:42 +02:00
Lauri Ojansivu facfc125ee CHANGELOG: the v12.66 release build is whole again.
Thanks to xet7 !
2026-10-28 04:31:25 +02:00
Lauri Ojansivu 1167cbd3b0 Bun bundles: the smoke-test MongoDB starts on a 6.19+ kernel, and Chromium is found.
linux-x64: mongo:8 refused to start on the runner's kernel ("Linux kernel
versions 6.19 and newer has a known incompatibility"). The smoke test pins
mongo:9.0.4, which recognises Ubuntu kernels that have the fix
(docs/Databases/Supported.md).

linux-arm64: Chromium was installed into ~/.cache/ms-playwright, while
build-target.sh loads tools-env.sh, which points Playwright at
.tools/ms-playwright, so the browser smoke test found no browser. The install
step uses the same path.

Thanks to xet7 !
2026-10-28 04:30:46 +02:00
Lauri Ojansivu d24598ce25 Snap variants: a copy of the source tree without .git gets its own .tools/tmp.
tools-env.sh made .tools/tmp only in a git checkout or where .tools already
existed, so that scripts mounted alone into a container (/releases) do not
make a /.tools there. The wekan-ondra and wekan-gantt-gpl snaps build from a
copy of the tree without .git (/root/project), so tools-env.sh found no
TMPDIR, and every fetch.sh in snapcraft.yaml stopped at "TMPDIR: unbound
variable" - reported as the Caddy download failing (v12.66). A tree with
package.json beside releases/tools-env.sh is a source tree too.

tests/toolsDirOnly.test.cjs sources it under set -euo pipefail with no
TMPDIR in such a copy, and, as the negative case, in releases/ alone, where
no .tools/ is made and no TMPDIR is invented.

Thanks to xet7 !
2026-10-28 04:30:46 +02:00
Lauri Ojansivu f68b130f15 Sandstorm: meteor-spk is assembled where the .spk is packed from, in .tools.
sandstorm-src/build-deps.sh still built meteor-spk.deps in
~/projects/meteor-spk, and the workflows put that on PATH, while
sandstorm-slim-spk.sh had moved to .tools/projects/meteor-spk with
aeff4900df: "no meteor-spk.deps at .tools/projects/..." and the v12.66 .spk
did not pack. build-deps.sh sources tools-env.sh and defaults to
WEKAN_METEOR_SPK_DIR, and the Sandstorm, Sandstorm store and meteor-spk
workflows use $GITHUB_WORKSPACE/.tools/projects/meteor-spk.

tests/toolsDirOnly.test.cjs scans sandstorm-src/ too, and fails on a workflow
that names ~/projects.

Thanks to xet7 !
2026-10-28 04:30:46 +02:00
Lauri Ojansivu 290e525b46 start-wekan.sh starts again where there is no /proc: FreeBSD, OpenBSD and macOS.
The memory budget read _memory_mb=$(awk ... /proc/meminfo) with no fallback
since 1f9ada5599. awk fails where there is no /proc, and under set -eu an
assignment from a failed substitution ends the script without a word, so the
FreeBSD and OpenBSD bundles and the Mac app never started WeKan (v12.66).
It is `|| true` again, and sysctl then gives the memory.

tests/macosShellPortability.test.cjs runs the memory lines under set -eu
without /proc and requires a number, with the unguarded line as the negative
case, and fails on any set -e script with a /proc read in $(...) that has no
fallback.

Thanks to xet7 !
2026-10-28 04:30:46 +02:00
Lauri Ojansivu 427d212627 Release: the emulated-arch builds mount .tools/tmp as the container's /tmp.
install-node-for-arch.sh refuses to run without TMPDIR, because the
container's own /tmp is a small part of its writable layer.
repack-bundle-for-arch.sh mounts this checkout's .tools/tmp as /tmp and sets
TMPDIR; the workflow's own docker run did not, so armhf, armv6, armv7, i386,
ppc64le, riscv64 and s390x all stopped at "TMPDIR must be the mounted
.tools/tmp" in v12.66, and win32, which builds from the i386 bundle's
Node.js, had nothing to build with.

Thanks to xet7 !
2026-10-28 04:30:46 +02:00
github-actions[bot] c1fe35bf6c Bump versions for v12.66 2026-10-28 01:02:24 +00:00
Lauri Ojansivu 0dee1da686 Prepare v12.66 release 2026-10-28 03:01:09 +02:00
Lauri Ojansivu 8ff34b423b CHANGELOG: the Drawing popup's width, File status's list of findings, and three Firefox specs.
Thanks to xet7 !
2026-10-28 02:57:47 +02:00
Lauri Ojansivu 0c40503a2b Tests: three Firefox specs wait for what they test, and drag inside the window.
- assist-search-phrases: the feature is turned off in the database, and a
  search sent before the browser had the published setting still ran the
  phrase. The search is repeated until the browser has it; a phrase that is
  never turned off still fails.
- board-settings-columns: a non-admin member's count of 0 passed in Chromium
  before the pane had rendered, and in Firefox counted Show on Card's
  Select all / Select none, which share the class and are a member's like the
  checkboxes under them. It waits for the pane and counts only the board-admin
  column actions.
- card-drawing-canvas: a maximized drawing fills the window, so its corner
  handle is at x 1236 of 1280 and a drag 120px further was not delivered by
  Firefox. The drawing is zoomed out to 80% first, which leaves room.

Thanks to xet7 !
2026-10-28 02:57:29 +02:00
Lauri Ojansivu 35ba67ee69 File status: one kind of finding no longer fills the whole list and hides the others.
The report lists at most 500 findings and counts the rest. They were listed
first come, first served, so on the test server 14358 baseline-move-candidate
rows of the integrity baseline took all 500, and the extension-mismatch and
mime-mismatch a type check is run to find were only numbers in the totals -
the Firefox stage's file-status-audit.e2e.js failed on it, and an
administrator would not have seen which file it was.

When the list is full, a kind with fewer rows listed now takes the place of
the last row of the kind with the most, so every kind found is listed and the
totals still count all of them. fileStatusAudit.test.cjs: 8 rows are 8
kinds with 40 orphan files crowding them, every finding is listed or counted
as omitted, and with room for all nothing is dropped.

Thanks to xet7 !
2026-10-28 02:57:29 +02:00
Lauri Ojansivu 58751aa339 Drawing: the popup is as wide as its drawing, so opened from the card's menu nothing is cut off.
The previous fix assumed the Drawing popup grew to its drawing's width and
ran off the window. Measured in the rebuilt app, it did not grow at all:
every popup is min(380px, 55vw) wide, the drawing's min(820px, 90vw) overflowed
inside it and the popup clipped it - the larger pen sizes and Save were cut
off by the popup, not by the window. Its box never changed, so the
ResizeObserver that keeps a movable popup inside had nothing to react to.

popup.css gives the Drawing popup min(860px, calc(100vw - 20px)), not
!important so that maximized `width: auto !important` still wins. Opened from
the card's menu at the right of a 1280px window it is now 860px wide at
x 408, 12px from the edge.

popup-drawing-window.e2e.js waits for the drawing and a box that stays put
before measuring: the Drawing popup is the card menu's popup element renamed,
which for a moment still had the 960px menu's place, and a drag pressed at
that place selected the page's text and closed the popup. The first drag
goes toward the side that has room.

Thanks to xet7 !
2026-10-28 02:57:21 +02:00
Lauri Ojansivu 2844fa07f7 CHANGELOG: autofocus, the drawing's size and place, the storage settings' English, the Helm chart reference, and four specs.
Thanks to xet7 !
2026-10-28 00:32:41 +02:00
Lauri Ojansivu bb614743fd Drawing: maximized it takes the window, and opened at the right it is not cut off.
Two faults the Chromium stage found, measured in the browser:

- A maximized new drawing was sized to its stage (1212 x 563 in a
  1280 x 720 window) but drawn 784px wide. popup.css still had the rule of
  the older design that kept the drawing at 800 x 500's shape -
  width min(100%, (100vh - 230px) * 1.6), aspect-ratio 8 / 5 - which
  overrode the surface's own size. d5d027a6f2 replaced that design; the
  rule is removed. (2929b61c85's refit was right, and is kept.)
- Opened from the card's menu at the right of the window, the Drawing
  popup grows to min(820px, 90vw) in the same popup the narrow menu was
  placed for, and its right part - the larger pen sizes and Save - was
  cut off by the window until it was dragged. A movable popup that grows
  is now kept inside the window by the rule a drag keeps it there, 12px
  from each edge (client/lib/popupBounds.js); not while maximized.

tests/popupBounds.test.cjs drives the bounds and fails on the 8 x 5 rule
coming back; popupMoveMaximize and popup-drawing-window.e2e.js pin the
drawing taking the window when maximized instead of its old shape.

Thanks to xet7 !
2026-10-28 00:31:58 +02:00
Lauri Ojansivu 9549388dd0 Tests: the smaller font preset is measured on the Card Settings heading text, not its line.
c8a15b514f moved the headings' smaller font from the heading line to its
text (.card-settings-pane-head > span, sidebar.css): on the line, the
em-sized columns shrank and each heading drifted off its cells. The
clean-task-boards spec still measured the line, which inherits the
body's 11.2px, and failed in Chromium; it measures the text, 9.6px. All
four clean-board specs pass.

Thanks to xet7 !
2026-10-28 00:28:35 +02:00
Lauri Ojansivu 9c0f408a00 Autofocus no longer takes the keyboard from a field being typed in, nor replaces its text.
client/lib/autofocus.js focused an [autofocus] element whenever a re-render
added one: the LAST [autofocus] anywhere in the document, selecting its
text, 50 ms later - and decided before those 50 ms whether it was already
focused. On the import page, typing the attachment token after pasting a
Jira export moved the focus to the pasted JSON, all of it selected, and the
token replaced it: the import then said "Your text is not valid JSON"
(import-attachment-auth.e2e.js in Chromium). A focus() then select() on the
textarea, recorded in the browser and found at autofocus.js in the running
bundle, showed it.

Now only an [autofocus] element that was itself just added is a candidate,
and whether to focus it is decided when the focus happens: never while
the user is typing in another field (an input, textarea, select or
contenteditable). Opening a popup or a form still focuses its field -
focus is then on the button that opened it, or on a composer field the
re-render removed. client/lib/autofocusDecision.js is the decision;
tests/autofocusDecision.test.cjs drives it with stand-in elements.

Thanks to xet7 !
2026-10-28 00:27:24 +02:00
Lauri Ojansivu 3f72fcd7f9 Tests: the Move and Copy Card helpers wait for the destination picker before reading it.
CardPage._selectBoard asked whether the popup had a boards select with
count(), which answers at once. Asked while the popup was still rendering,
it said 0 - which the helper reads as "a worker, who has no boards
select" - so the move went to the named list of the SAME board, and
#6793's Move Card spec waited in vain for the card's new boardId. A trace
showed it: one select, and that for the list. The helper now waits for
the lists select, which every picker draws, before asking about the
boards one. The #6793 specs and 03-cards-operations (29 tests) pass in
Chromium.

Thanks to xet7 !
2026-10-28 00:21:29 +02:00
Lauri Ojansivu 3e998651ea Admin Panel / Attachments: a storage setting's English text is not shown twice.
Each storage setting (S3, Azure, Google Cloud, WebDAV ...) shows its
description and menu path in English and in the user's language, so the
product's own terms stay recognizable. The English line was typed into
the template a second time above the {{_ key}} line: in English, and in
every language without that translation yet, the same sentence stood
twice in a row - which is also why Playwright's strict getByText found
two and storage-settings-translations.e2e.js failed in Chromium.

The English now comes from en.i18n.json (englishText; all 26 typed copies
were identical to it, so no text changes), and the translated line is
shown only when it differs (translatedDiffers), reactively with the
language. tests/attachmentSettingsBilingual.test.cjs fails on a typed
English twin coming back and on a translation shown unconditionally.

Thanks to xet7 !
2026-10-28 00:15:32 +02:00
Lauri Ojansivu cbf9d2f843 Tests: the UploadBleed and StampBleed specs do the legitimate case first, and check the block.
Both failed in Chromium on their negative case: an upload onto one's own
card answered 403 "Please sign in to upload.", and a checklist without a
foreign stamp "Access denied". The guards were right. The attempt came
first, and a signed-in account that attempts a vulnerability is blocked
(server/lib/blockOnSecurityEvent.js): loginDisabled, and the page's
session ended - so the legitimate call that followed ran signed out.
Printing Meteor.userId() around the first call showed it.

Each spec now does the legitimate case first and the attempt last, and
checks that the attempt blocked the account (services.securityBlock names
UploadBleed or StampBleed). The stamped checklist comes before the
stamped upload: its refusal names no account, so the user is still
signed in for the upload. Both pass in Chromium.

Thanks to xet7 !
2026-10-28 00:11:35 +02:00
Lauri Ojansivu 9994e82bae Helm chart: values.yaml's settings reference is generated from docker-compose.yml.
The chart's commented reference of every WeKan setting was copied from
docker-compose.yml by hand once and had fallen 74 settings behind, which
tests/chartsFerretdb.test.cjs reported as soon as .tools/charts was there.
releases/charts-values-reference.mjs regenerates the block from the wekan
service's environment, and release-charts.sh runs it on every chart
release. A comment after a value goes on the line above, not into the
value.

The chart change itself, and the 512 MiB request / 2 GiB limit #6606 had
asked of it, are committed in wekan/charts. All 19 chart tests pass.
tests/chartsValuesReference.test.cjs runs the generator on a temporary
values.yaml: every compose setting written, nothing outside the block
touched, no comment inside a value, and a second run changes nothing.

Thanks to xet7 !
2026-10-28 00:06:39 +02:00
Lauri Ojansivu e13a5a59ad CHANGELOG: .tools/ only, the macOS and Windows editor sandboxes, and the mirror source.
Thanks to xet7 !
2026-10-28 00:04:07 +02:00
Lauri Ojansivu 6ac70c4cdd Windows sandbox: VSCodium in Windows Sandbox, seeing only Downloads\repos\wekan.
The Windows counterpart of the Linux Flatpak sandbox and the macOS
sandbox-exec one. Windows Sandbox is a disposable Hyper-V virtual machine
that comes with Windows 10 and 11 Pro, Enterprise and Education; it sees
nothing of the host but the folders mapped into it.

- docs/Security/Sandboxes/Windows/sandbox.ps1 (on the host; sandbox.bat
  starts it) offers to turn the Windows Sandbox feature on, copies
  user.name and user.email into the checkout's .git\config, and writes
  .tools\sandbox\wekan-vscodium.wsb: the checkout as the only mapped
  folder, read-write at the same path under the sandbox's user; network
  on; microphone, camera and printers off; ProtectedClient; half of the
  host's memory, 4 to 16 GiB.
- sandbox-logon.ps1 runs inside at logon: TEMP, TMP and TMPDIR in
  .tools\tmp, git's configuration in .tools\home, PortableGit and the
  VSCodium zip downloaded once into .tools - each verified (VSCodium's
  .sha256, the SHA-256 in Git for Windows' release notes) before it is
  unpacked - and VSCodium opened on the checkout, its settings and
  extensions in .tools\vscodium.
- README.md is the design: what is refused and allowed, how, and the
  trade-offs.

Not run on Windows yet: written and reviewed on macOS, where PowerShell is
not available. tests/windowsSandbox.test.cjs reads the scripts and pins
the single mapping, the .tools locations, verification before unpacking,
and that native commands cannot stop Windows PowerShell 5.1.

Thanks to xet7 !
2026-10-28 00:03:42 +02:00
Lauri Ojansivu 42ed50413f macOS sandbox: VSCodium under sandbox-exec, reaching only ~/Documents/repos/wekan.
The macOS counterpart of the Linux Flatpak sandbox
(docs/Security/Sandboxes/vscodium). docs/Security/Sandboxes/macOS/sandbox.sh
installs VSCodium with Homebrew when it is missing, writes a Seatbelt
profile for this checkout's path to .tools/sandbox/vscodium-macos.sb, a
Finder launcher beside it, and starts VSCodium under sandbox-exec:

- every file under /Users and /Volumes is refused - the home directory,
  ~/.ssh, ~/.gitconfig, other users and /Users/Shared, other disks - except
  the checkout, and the login session's ssh-agent socket is refused;
- HOME is .tools/home, TMPDIR .tools/tmp, VSCodium's settings and
  extensions are in .tools/vscodium, and env -i drops SSH_AUTH_SOCK and an
  editor's ELECTRON_RUN_AS_NODE;
- --no-sandbox, because Chromium's own helper sandbox cannot start inside
  sandbox-exec ("Operation not permitted"); every helper is inside this
  one instead;
- user.name and user.email are copied into the checkout's .git/config,
  since ~/.gitconfig cannot be read inside.

Verified on macOS 27 with --check (home, ~/.ssh, ~/.gitconfig and
/Users/Shared refused; the checkout, git and the network allowed) and by
starting VSCodium in it: the window, its helpers and the extension host
ran. README.md is the design: what is protected, how, and the trade-offs.
tests/macosSandbox.test.cjs pins the rules and, on macOS, runs a profile
against a shell.

Thanks to xet7 !
2026-10-28 00:00:20 +02:00
Lauri Ojansivu 8af1e8aec8 mirror.sh: the WeKan checkout is the source, not a second clone in .tools/wekan-github.
Syncing wekan/wekan cloned the whole GitHub repository - 365,000 objects,
500 MB - into .tools/wekan-github, although it is this checkout
(~/repos/wekan on Linux, ~/Documents/repos/wekan on macOS,
Downloads\repos\wekan on Windows). Only the destination mirrors
(wekan-gitlab, wekan-codeberg ...) need checkouts of their own.

The source's branches and tags are fetched into this checkout instead,
under the private namespace refs/mirror-source/<source>/, which already
holds nearly every object: 1.7 seconds for 1,278 refs, measured. That
namespace is not a branch; the working tree and local main are untouched,
so unpushed local commits never reach a mirror. The fetched refs are
deleted again in a finally, after the push and also when the fetch, the
push or the merge-preserving retry fails.

tests/activeForgeMirror.test.cjs and tests/mirrorGitDirectories.test.cjs
run syncGit against a stand-in checkout (WEKAN_MIRROR_SOURCE_CHECKOUT for
the CLI), assert that GitHub's wekan is not cloned, that only the
destination is, and that the source refs are cleared - also after a failed
fetch.

Thanks to xet7 !
2026-10-27 23:59:26 +02:00
Lauri Ojansivu e79ec61bcc Windows: build.bat and the releases\ batch files keep every file in .tools\ too.
releases\tools-env.bat is the counterpart of releases/tools-env.sh: TEMP,
TMP and TMPDIR in .tools\tmp, npm's cache and global installs, Go's caches
and the Playwright browsers in .tools\, METEOR_WAREHOUSE_DIR at
.tools\.meteor once Meteor is there, and those tools first on PATH, so no
user or system PATH setting has to change. build.bat and every
releases\ batch file call it; the bundle launchers (start-wekan.bat) do
not, as they run a finished bundle where there is no .tools\.

build.bat's install option no longer installs a system-wide Node.js with
Chocolatey: it unpacks the Dockerfile's version from nodejs.org's zip
into .tools\node-v<version>-win-<arch>, verified against SHASUMS256.txt
first, and Meteor goes into .tools\.meteor (LOCALAPPDATA is .tools for the
installer only). build-bundle-win64.bat works in .tools\tmp\win64-bundle-
<version> and says where the zip is. tools-env.sh also keeps Go's caches
and the Playwright browsers in .tools/, as tools-env.bat does.

tests/toolsDirOnly.test.cjs pins tools-env.bat, its callers and the .tools
install; tests/buildScriptParity.test.cjs reads the Playwright directory
and Node.js version from their new places.

Thanks to xet7 !
2026-10-27 23:53:23 +02:00
Lauri Ojansivu aeff4900df Every file the scripts write stays in .tools/: temporary files, downloads, Meteor, Node.js, npm.
build.sh and the releases/ scripts wrote all over the machine: temporary
files to /tmp and macOS's /var/folders, downloads and release zips to
~/Downloads or ~/Lataukset, Meteor to ~/.meteor, Node.js to /usr/local
(sudo n) or ~/.nvm, the npm cache to ~/.npm, Volta to ~/.volta, the
Sandstorm toolchain to ~/projects, and PATH lines into ~/.zshrc and
~/.bashrc. A sandboxed editor (Flatpak on Linux, sandbox-exec on macOS)
can reach only the checkout, and a container's own /tmp is a few hundred
megabytes. Everything is in .tools/ now, which git and Meteor ignore:

- releases/tools-env.sh, sourced by build.sh and every script that writes
  files, sets TMPDIR/TMP/TEMP to .tools/tmp (also downloads and release
  zips), NVM_DIR, npm_config_cache, VOLTA_HOME and the meteor-spk
  directory to .tools/, METEOR_WAREHOUSE_DIR to .tools/.meteor once Meteor
  is installed there, and puts those tools first on PATH, so no shell rc
  file is edited. HOME is not moved: git needs ~/.gitconfig and ~/.ssh.
  A TMPDIR already inside a .tools/ is kept, so a test's own directory
  there stays its own; without a writable one it stops rather than fall
  back to /tmp. It never fails a caller running set -euo pipefail.
- macOS's mktemp ignores TMPDIR when given no template, so every mktemp
  names one: mktemp "$TMPDIR/name.XXXXXX". Fixed /tmp files are in
  $TMPDIR.
- build.sh installs Node.js on Linux into .tools/n (N_PREFIX, no sudo),
  nvm on macOS into .tools/nvm (PROFILE=/dev/null: no rc file edited), and
  Meteor with HOME=.tools for the installer only, into .tools/.meteor.
  build.sh is replaced, not edited in place, while it may be running.
- Containers get this checkout's .tools/tmp as their /tmp: every
  db-conformance database (MariaDB's temporary tables were verified to land
  there, and MongoDB's socket) and the repack container, which empty it
  themselves before they are removed. install-node-for-arch.sh requires
  the mounted TMPDIR.
- tests/run-node-suites.cjs gives every suite TMPDIR in .tools/tmp, and the
  tests that copied a script into a fixture copy tools-env.sh too; tests
  that pinned ~/.meteor, the rc file edits, sudo n or /tmp/manifest.json
  pin the .tools/ places now and say why.

tests/toolsDirOnly.test.cjs runs tools-env.sh against a TMPDIR already
set elsewhere and under set -euo pipefail, and fails on any script that
makes a temporary file without naming .tools/tmp, writes to the home
directory, or edits a shell rc file, or on a container whose /tmp is its
own.

Thanks to xet7 !
2026-10-27 23:49:55 +02:00
Lauri Ojansivu d1b96f65c3 CHANGELOG: the checkout on every system, and companion repositories only in .tools/.
Thanks to xet7 !
2026-10-27 23:15:41 +02:00
Lauri Ojansivu 96ad680cd4 Release scripts find the checkout on every system, not only at Linux's ~/repos/wekan.
The checkout is at ~/repos/wekan on Linux, ~/Documents/repos/wekan on macOS
and Downloads\repos\wekan on Windows (CLAUDE.md). release.sh,
release-bundle.sh, release-snap.sh, release-sandstorm.sh, release-cleanup.sh,
install-sandstorm.sh, node-update.sh, docker-push-gantt.sh, up.sh and
rel.sh hard-coded Linux's ~/repos/wekan (rel.sh also /home/wekan/repos), so
on a Mac they cd'd into nothing - or into a stale copy - and edited or
packed the wrong tree. Each finds the checkout from its own location now
(REPO_DIR, as mirror.sh already did), which is right on every system; the
parent directory is $(dirname "$REPO_DIR").

DOWNLOAD_DIR defaulted to ~/Lataukset, the Finnish Linux name of Downloads.
macOS keeps ~/Downloads in every language, so it is Lataukset where that
exists and Downloads otherwise.

The VirtualBox appliance scripts keep /home/wekan: that image is a Linux VM
with its own fixed layout. tests/repoLocationPerOs.test.cjs pins the three
locations in CLAUDE.md and AGENTS.md and fails on any script that
hard-codes one system's checkout path or assumes Lataukset.

Thanks to xet7 !
2026-10-27 23:15:29 +02:00
Lauri Ojansivu 9927adcb44 Companion repositories are read from .tools/ only, never from ../w/.
wekan.fi, charts and docker-cleanup-volumes used to be checked out in ../w/
beside the repository, and several places kept that as a fallback after
they moved to .tools/ (CLAUDE.md). With no .tools/wekan.fi, the Hall of Fame
tests read a stale ../w/wekan.fi and failed on markup the site no longer
has.

- tests/hallOfFameProblemsCoverage.test.cjs and
  tests/fixedVulnerabilityClasses.test.cjs read only .tools/wekan.fi (and
  skip, saying so, when it is not there).
- release-charts.sh, backfill-charts.sh, reindex-charts.py and version.sh
  look for the charts checkout only in .tools/charts.
- docker-build.sh clones and runs docker-cleanup-volumes in .tools/.

tests/noLegacySiblingRepos.test.cjs fails when a script, test or document
under releases/, tests/, docs/ or tools/, or at the root, looks in ../w/
again; tests/reindexCharts.test.cjs now requires ../w/charts to be gone.

Thanks to xet7 !
2026-10-27 23:13:02 +02:00
Lauri Ojansivu 6675f0e5d3 CHANGELOG: the shell scripts and tests on macOS, and two Card Settings specs.
Thanks to xet7 !
2026-10-27 23:07:27 +02:00
Lauri Ojansivu 43e09631c4 Single file: a Node.js without single executable applications is named; tests run on an arm64 Mac.
A Node.js can be compiled without single executable applications - the
Homebrew one on macOS is - and `node --build-sea` then only says "Single
executable application is disabled" and exits 9, which the build
reported as a bare failed command. build.mjs asks first (seaSupported) and
stops with what to do: build with a Node.js from nodejs.org, given with
--host-node.

tests/singleFile.test.cjs skips the end-to-end build on such a Node.js
and says why, tests seaSupported against stand-ins of both kinds, and
expects the 32-bit refusal to name the host's own arch - it expected
"x64", so it failed on an arm64 Mac.

Thanks to xet7 !
2026-10-27 23:06:49 +02:00
Lauri Ojansivu 1f9ada5599 Shell scripts on macOS: no /proc, so command lines and memory are asked another way.
macOS and the BSDs have no /proc.

- releases/db-conformance.sh decided whether a container's run was still
  going from /proc/<pid>/cmdline. On a Mac every live run looked dead, so
  a second conformance run removed the first one's containers and
  networks. The command line comes from ps -o command= where there is no
  /proc (tests/dbConformanceStaleContainers.test.cjs passes on macOS now).
- start-wekan.sh and releases/ferretdb/start-wekan.sh sized the Node.js
  and FerretDB heaps from /proc/meminfo, so on a Mac they assumed 2 GiB
  whatever the machine had. They ask sysctl hw.memsize (macOS) or
  hw.physmem (BSD) there, as build.sh already did.
- releases/debug-speed-watch.sh shows the load average from sysctl
  vm.loadavg where there is no /proc/loadavg.

tests/macosShellPortability.test.cjs fails on a /proc memory, load or
command-line read without such a fallback (the Docker entrypoint, which
runs only on Linux, excepted). releases/nextcloud-pack.sh is listed in
tests/buildScriptParity.test.cjs as run by nextcloud.yml.

Thanks to xet7 !
2026-10-27 23:06:49 +02:00
Lauri Ojansivu 0ed8fb20f2 Tests: strip-node.sh is exercised on a Mac, and the ELF cases skip there.
tests/stripNode.test.cjs built its ELF fixture with `gcc`, which on macOS
is clang and writes a Mach-O, so the section reader found nothing and the
test failed with "the fixture has symbols and debug data". The ELF cases
now skip unless the compiler really wrote an ELF, and a new case runs
releases/strip-node.sh on a real Mach-O where there is a Mac: the local
symbol is gone, the exported one stays, the binary is re-signed ad hoc,
passes codesign --verify --strict and still runs.

Thanks to xet7 !
2026-10-27 23:02:53 +02:00
Lauri Ojansivu 283e51eac0 Shell scripts: the root and releases/ scripts run on macOS too.
Started from zsh on macOS, a script gets bash 3.2 as /bin/bash and BSD
tools. Every .sh at the repository root and under releases/ was checked
with bash 3.2 and for GNU-only tool options; what failed:

- test-menu.sh: declare -A (bash 4) is a case function, runner_for, and
  an empty match list no longer trips set -u in bash 3.2.
- releases/ferretdb-latest-tag.sh: with no GitHub token, "${auth[@]}"
  was an unbound variable under set -u in bash 3.2; it uses
  ${auth[@]+...} as ferretdb-asset.sh already did.
- find-replace.sh: never worked anywhere ('$1' in single quotes searched
  for the text $1) and used GNU grep -Z and sed -i. It replaces literal
  text with perl now, skipping the directories find.sh skips.
- sha256sum (missing on older macOS) falls back to shasum -a 256 in
  release-x2.sh, repack-bundle-for-arch.sh, install-node-for-arch.sh and
  sandstorm-capnp-schemas.sh; -c works with both.
- sandstorm-capnp-schemas.sh: nproc falls back to sysctl hw.ncpu, and
  sed -i takes a .bak suffix (GNU and BSD alike) that is then removed.
- sandstorm-slim-spk.sh: ln -sfT is ln -sfn, stat -c%s is wc -c.
- virtualbox/ipaddress.sh: ifconfig where there is no ip.

tests/macosShellPortability.test.cjs parses every bash script with
bash 3.2 when /bin/bash is 3.x, and fails on any of these shapes coming
back: associative arrays or mapfile, flock without a fallback, tar
--transform, a GNU-only sed -i, sha256sum / nproc / ip without a
fallback, ln -T or stat -c.

Thanks to xet7 !
2026-10-27 23:02:14 +02:00
Lauri Ojansivu fa1bb293f9 UCS migration and Nextcloud packing work on macOS: no flock, no GNU tar.
The UCS App Center preinst hook locked with flock(1), which is util-linux
and missing on macOS and the BSDs, so tests/ucsMigration.test.cjs could not
run the hook there ("flock: command not found"). Without flock it now takes
the same lock with perl's flock() on the inherited fd 9: a flock(2) lock
belongs to the open file, so it is held until the hook closes fd 9, exactly
as flock -n 9 holds it. Verified: a second locker is refused and the lock
frees when the first one closes. perl is on UCS (Debian) and macOS.

The Nextcloud ExApp archive was packed with GNU tar's --transform, which
bsdtar (macOS, the BSDs) does not have. releases/nextcloud-pack.sh copies
the source under a staging <app-id>/ and packs that directory, with the
same exclusions, using only options both tars share; COPYFILE_DISABLE=1
keeps macOS tar from adding ._* AppleDouble files. The workflow and
tests/nextcloudRelease.test.py both use it, and
tests/ucsNextcloudWorkflows.test.cjs fails if --transform comes back.

Thanks to xet7 !
2026-10-27 22:57:57 +02:00
Lauri Ojansivu d8d7b01334 Tests: the Card Settings both-sides and Scrum title specs follow the current Card Settings.
card-settings-both-sides read row[side].needsCard for every row, but six
rows have no minicard side by design (suggested members, checklist from
description, attachment previews and details, Drawing, action items), so
it threw "Cannot read properties of undefined". It expects what
rowsForSide lists now: rows that have the side, less the card-only List
title.

The Scrum settings title spec read the pane title right after clicking
Scrum. Card Settings opens on Card now, so it read "Card" and failed on
the "Card button" label. It waits for the pane title to be Scrum's menu
name first.

Both failed in the Chromium stage of the 2026-10-27 EVERYTHING run.

Thanks to xet7 !
2026-10-27 22:25:33 +02:00
Lauri Ojansivu b1370fbc5e CHANGELOG / TODO Later: the six maintainer-decision items are done.
API JSON uploads and the site logo stored again, the validate-and-move
method moves again, REST card placement checked, WIP limits admin-only,
AuthBleed attempts recorded and Data health's "Never" with a key of its
own - each removed from TODO Later now that its fix is in Upcoming. The
browser runs and WebKit stay there.

Thanks to xet7 !
2026-10-27 21:46:31 +02:00
Lauri Ojansivu a40c4495d4 Lists: a WIP limit is set by a board admin, on every path.
The applyWipLimit, enableWipLimit and enableSoftLimit methods have always
required a board admin ("You must be a board admin to modify WIP
limits."), but a member with write access could still set wipLimit with a
plain list update - the Lists allow rule grants write members every field
- or with REST PUT /api/boards/:boardId/lists/:listId. A WIP limit is a
board-wide workflow rule, so the methods' rule now holds everywhere:

- a Lists deny rule refuses an update touching wipLimit unless the caller
  is admin of the list's board. Every WeKan client path goes through the
  methods, so it refuses nothing in ordinary use;
- the REST PUT asks for a board admin (checkBoardAdmin) when the body
  carries a wipLimit; write access still edits title, color and starred.

tests/wipLimitAdminOnly.test.cjs runs the real deny rule, pins the REST
check before the write, and fails on any list method that changes a WIP
limit without a board admin check first.

This was in CHANGELOG TODO Later as needing a maintainer decision;
decided: board admins only.

Thanks to xet7 !
2026-10-27 21:45:43 +02:00
Lauri Ojansivu 02bb0814c3 Tests: linkedWritePolicy and webhookBleed read the code as it is now.
linkedWritePolicy sliced POST cards' linked-card branch up to the getCard
that #6823 removed, so the slice ran to the end of the file and found
checkBoardAccess elsewhere; it ends at the branch's author-spoof note now,
and fails if that is missing. webhookBleed (GHSA-7w2h-g83c-jqrp) pinned
copyBoard's signed-out refusal as one line; it records an AuthBleed
attempt first now, so the guard accepts the block and still requires the
refusal before the board is read.

Thanks to xet7 !
2026-10-27 21:42:47 +02:00
Lauri Ojansivu 77956c98ed Admin Panel / Problems: Data health's "Never" has a key of its own.
The Data health status line ("Last Run: Never") reused List Sync's
list-sync-last-synced-never, so a translator who words that one as "never
synced" would make Data health read wrong. It is data-health-never-run
now, added after data-health-repair with add-pending-keys.mjs
--all-locales (the completeness suites require every locale to carry
English's keys in order) and listed in pending-transifex.json.

Every locale already translates the identical English "Never" under
list-sync-last-synced-never; fill-translations.mjs --apply filled the new
key from that in 234 locales, writing only the English placeholders. A
real Transifex translation still replaces it on the next pull.

This was in CHANGELOG TODO Later as needing a maintainer decision.

Thanks to xet7 !
2026-10-27 21:42:47 +02:00
Lauri Ojansivu 314c153c9b Admin Panel / Problems: AuthBleed attempts are recorded.
copyBoard, copyCard, copySwimlane and createCardWithDueDate - four of the
AuthBleed methods - refused with plain not-authorized errors, so an
attempt left no trace in Admin Panel / Problems. AuthBleed was on the
PENDING list of tests/hallOfFameProblemsCoverage.test.cjs.

A new catalog key, authz.copy (AuthBleed, high, CWE-862), is recorded
where no legitimate client can be:

- a signed-out caller of any of the four - no WeKan client copies while
  signed out, and with no userId nobody can be blocked;
- any refusal of createCardWithDueDate, which no WeKan client calls.

The membership refusals of copyBoard, copyCard and copySwimlane are
deliberately NOT recorded: a member who is not admin of a shared or
default board template, or a stale Copy Card / Copy Swimlane dialog,
reaches them in ordinary use, and a high-severity record naming the
account disables that login (server/lib/blockOnSecurityEvent.js). Each
record is wrapped so logging cannot break the guard.

tests/authBleedCopyMethods.test.cjs runs the real method bodies and pins
both: what is recorded, and that the ordinary-use refusals are not.
AuthBleed leaves the PENDING list.

This was in CHANGELOG TODO Later as needing a maintainer decision.

Thanks to xet7 !
2026-10-27 21:29:17 +02:00
Lauri Ojansivu e7505a4fef Attachments: the API's JSON uploads, copies and the site logo are stored again.
The JSON branches of REST /api/attachment/upload, upload-background and
copy, their Meteor-method twins in server/attachmentApi.js, and Admin
Panel's site logo upload created the file with Attachments.insertAsync.
Meteor-Files (ostrio:files 3) has insertAsync only on the client; on the
server it is not a function, so every one of them failed - a REST 500
"Attachments.insertAsync is not a function". Only the multipart branches,
which use addAttachmentFromStream, worked.

All seven store with writeAsync(buffer, { fileName, type, userId, meta },
true) now, as email-to-card and the importers already do. Its
after-upload hook validates the file - type, size and the external
scanner, which these paths skipped - and moves it to the default storage,
or to meta.copyStorage for a copy, so the second deferred moveToStorage
each branch made is removed. The file records who stored it.

tests/attachmentApiServerWrite.test.cjs pins the seven calls and fails on
any server-side Attachments.insertAsync or Avatars.insertAsync.

This was in CHANGELOG TODO Later as needing a maintainer decision; the
site logo upload was found by the new test's tree-wide check.

Thanks to xet7 !
2026-10-27 21:26:30 +02:00
Lauri Ojansivu d3e80af1ac Attachments: validateAttachmentAndMoveToStorage moves the file again.
After validating, the method moved the file from Meteor.defer by calling
the moveAttachmentToStorage method. A deferred call has no method
invocation and so no userId, and that method's first check is "You must be
logged in.": every move was refused, the error went nowhere, and the file
stayed where it was.

Every check moveAttachmentToStorage makes - a site admin, the board
visible, the destination on the allowlist - is made by this method before
the defer, so the deferred step now calls moveToStorage itself, as the
upload path already does, and logs a failure instead of dropping it.

tests/moveStorageBleed.test.cjs pins it; its existing guard still requires
the admin check and the allowlist before every caller-named move, and a
new one fails on any Meteor.defer or Meteor.setTimeout in server/ or
models/ that calls a Meteor method.

This was in CHANGELOG TODO Later as needing a maintainer decision.

Thanks to xet7 !
2026-10-27 21:23:54 +02:00
Lauri Ojansivu c355926767 REST: a new card's list and swimlane must belong to the board in the URL.
POST /api/boards/:boardId/lists/:listId/cards and its bulk sibling wrote the
URL's listId and the body's swimlaneId onto the new card without checking
either against the board. A list or swimlane id of ANOTHER board gave a
card that pointed at a list and a swimlane its own board does not have -
the #6572 shape, a card that shows on neither board - and the list
permission check was evaluated against the wrong board's rules. The PUT
route has refused this since #6572; the create routes now do the same with
a 400 that names the field, and the bulk route reports it per entry. An id
that is not a string (such as { $ne: null }) is refused rather than looked
up. The linked-card branch was already checked by createLinkedCardFor.

tests/restCardPlacement.test.cjs pins both routes and fails on any REST
handler in server/ that inserts a card without the check.

This was in CHANGELOG TODO Later as needing a maintainer decision; the PUT
route's #6572 rule already decided it.

Thanks to xet7 !
2026-10-27 21:22:49 +02:00
Lauri Ojansivu 6abcc8fa1f CHANGELOG: Problems reports collection scans and read rate, literal board search, one "created" activity, and less database load.
The six fixes from Raymond-Voogt's reports #6821-#6826. CHANGELOG.md went
over its 450,000-byte budget with them, so v12.40 is moved to
old-CHANGELOG/2026/10.md by releases/changelog-archive.mjs.

Thanks to Raymond-Voogt and xet7 !
2026-10-27 21:20:58 +02:00
Lauri Ojansivu b87858baaf Problems / Database: report queries without an index and a high read rate.
The database health probe (server/lib/databaseHealth.js) saw restarts, slow
storage and a full disk, but not what made one instance slow: a 10 GB
collection scanned three times per search, and an hourly job reading all
1.1 million cards (#6824, #6825). Both were visible only in MongoDB's own
log, as "Slow query" lines with COLLSCAN, and in serverStatus: 1.6 billion
documents read in two hours.

Two more checks, every 5 minutes with the others:

- db.collection-scan: the "Slow query" lines MongoDB keeps (getLog
  'global'), newer than the last probe, where a query read a whole
  collection of at least 10,000 documents. Grouped by collection and the
  field names filtered on - never values - with count, documents read and
  time. Small tables read whole by design (settings, roles) and internal
  namespaces are left out. Needs the getLog privilege; without it, and on
  FerretDB, the check finds nothing and is skipped.
- db.scan-rate: serverStatus.metrics.queryExecutor.scannedObjects between
  two probes above 50,000 documents per second. A database restart, which
  resets the counter, is not counted.

Both are rows in Admin Panel / Problems / Database with what to do;
docs/Databases/MongoDB/Storage-Requirements.md lists them.

tests/databaseHealthScans.test.cjs drives the log parsing and the rate,
including that a filter's values never reach the row. From the reporter's
patch.

Thanks to Raymond-Voogt and xet7 !

Fixes #6826.
2026-10-27 21:19:51 +02:00
Lauri Ojansivu d481bffdfe History audit and position-history cleanup: once per installation.
Both were Meteor.setInterval jobs, so every WeKan process behind one
database ran them. On a 24-pod installation (54,234 History rows, 148,660
board memberships, 1,760 position-history entries):

- the change history integrity audit read the whole History 24 times an
  hour, and
- UserPositionHistory.cleanup ran 24 times a day, each run one query per
  board membership of every user, each fetching up to 1000 whole
  documents, to trim nothing - outside the CPU governor.

Both are SyncedCron jobs now, the way the other periodic jobs already
are: synced-cron's { intendedAt, name } lock runs each scheduled time on
one pod only, and its running-job check (indexed in #6769) skips a run
while the previous one is still going. No new collection is needed for
it. The audit stays hourly; the cleanup runs at 4:15 am.

The cleanup asks the database which user/board pairs hold more than 1000
non-checkpoint entries (aggregate) and trims only those: the 1000th
newest entry's date with one indexed query, everything older in one
remove. The result is the same as before - the newest 1000 per user and
board are kept, checkpoints never - also for pairs whose user has since
left the board. It waits for a quiet CPU and pauses between pairs.

models/lib/positionHistoryCleanup.js holds the plan with the database
calls passed in; tests/positionHistoryCleanup.test.cjs runs it on an
in-memory history against the old algorithm's result, counts the queries,
and fails if anything in server/ or models/ drives either job from a
per-process timer again. From the reporter's patch, with the claim moved
onto the existing SyncedCron.

Thanks to Raymond-Voogt and xet7 !

Fixes #6821.
2026-10-27 21:18:08 +02:00
Lauri Ojansivu dad16609c9 Board search: match the typed text, not a regular expression.
The sidebar card search, the link-card popup and the list, swimlane and
board pickers (Board.searchCards / searchLists / searchSwimlanes /
searchBoards, models/lib/cardSearch.js) and the import member search
(UserSearchIndex) compiled the typed term AS a regular expression:

- "C++", "(2026)", "[x" or a case number like "PL0900-2026(1)" threw
  "Invalid regular expression" and the search showed nothing;
- "." matched any character and "*" or "?" changed what was searched for.

Every server-side search (global search, publications, boardTextSearch)
already escapes the term and matches it literally. These now do the same,
still case-insensitive, through literalSearchRegex() in cardSearch.js,
which the user search reuses rather than carrying its own escape. The
Advanced Filter's /regex/ syntax is a separate path and is unchanged.

tests/noUnescapedDynamicRegExp.test.cjs no longer needs its reviewed
exceptions for models/boards.js, models/lib/cardSearch.js and
models/users.js; tests/searchLiteralTerm.test.cjs drives the matching and
fails on any model, client or imports file that compiles a raw term into
a RegExp. From the reporter's patch.

Thanks to Raymond-Voogt and xet7 !

Fixes #6822.
2026-10-27 21:15:51 +02:00
Lauri Ojansivu 5f3b211b37 New issue and REST linked card: one "created" activity, not two.
A card inserted through the Cards collection gets its "created" activity
from Cards.after.insert. Two paths inserted through the collection AND
called cardCreation() themselves:

- Board View / Version Control, New issue (vcsCreateIssue), and
- POST /api/boards/:boardId/lists/:listId/cards with linkedId
  (createLinkedCardFor).

Each such card got two "created" activities, and every watcher two
notifications and two emails. The other explicit callers - REST create,
bulk create, email view, email to card - insert with Cards.direct, where
no hook runs, and are right to record it themselves.

Both extra calls are removed. Outside a method (a REST route, a server
job) the hook has no user, so the REST linked card's activity would have
lost its author: the hook now falls back to the card's own userId, which
createLinkedCardFor sets to the session user. Activities never exclude
their actor from notifications, so the fallback only names an author
where there was none, such as a recurring card's next copy.

tests/noDoubleCreateActivity.test.cjs walks server/ and models/ and fails
on any explicit cardCreation that follows a hooked insert. The
GHSA-6jr3-42jf-vhm5 guard in restApiIdorBatch now pins the linked card's
attribution through createLinkedCardFor and the hook. From the reporter's
patch.

Thanks to Raymond-Voogt and xet7 !

Fixes #6823.
2026-10-27 21:14:23 +02:00
Lauri Ojansivu 2be842351b Search sessions: indexes, and stale sessions of every user are removed.
Every global search and card report (server/publications/cards.js) upserts
its search session by { userId, sessionId }, reads it back by the same two
fields, and removed the user's sessions older than a day by
{ userId, modifiedAt }. The sessiondata collection had only its _id index,
so each search scanned every stored session three times.

And only the searching user's OWN old sessions were removed: a user who
never searched again kept theirs forever. Each session holds the pickled
selector of the search, which lists every board the user can see - large
for an administrator. One instance had 4,751 sessions taking 10 GB, the
biggest collection in its database.

- Indexes { userId: 1, sessionId: 1 } and { modifiedAt: 1 }.
- Sessions older than a day are removed for everybody on each search, as
  one indexed delete. Paging (next/previous) works within the day as before.

Test: tests/searchSessionIndexes.test.cjs. From the reporter's patch.

Thanks to Raymond-Voogt and xet7 !

Fixes #6824.
2026-10-27 21:12:45 +02:00
Lauri Ojansivu e9440d503e Recurring cards: an index for the hourly job.
server/cardRecurrenceSchedule.js runs every hour and asks for the cards
with a recurrenceInterval ($exists: true, not null or 'none'). There was no
index on the field, so the query read every card in the database to find
the few that recur. One instance logged it as a slow query: COLLSCAN,
1,108,017 documents examined, 1,155 ms.

A partial index on recurrenceInterval holds only the cards that have one,
the same way activities indexes commentId and attachmentId. The job's
$exists: true lets MongoDB use it; tests/recurrenceIndex.test.cjs pins both.
From the reporter's patch.

Thanks to Raymond-Voogt and xet7 !

Fixes #6825.
2026-10-27 21:12:45 +02:00
Lauri Ojansivu 66b4ad8dcb CHANGELOG / TODO Later: the unverified browser runs, WebKit, and five behaviours that need a maintainer decision.
Thanks to xet7 !
2026-10-27 21:01:51 +02:00
Lauri Ojansivu aae0490b71 CHANGELOG: FrameBleed follow-up - a 304 kept a file's own CSP - and the Scrum, Sesotho and drag reorder specs.
Thanks to xet7 !
2026-10-27 20:59:24 +02:00
Lauri Ojansivu 4a656baea3 Tests: the Scrum, Sesotho and drag reorder specs read one list per side, and open the SMTP fields first.
Card Settings draws each row once per list, and its minicard list head is
empty; the Date section's card list starts with the fixed Mark as complete
row; the SMTP host field is shown only while email settings are enabled and
SMTP is chosen.

Thanks to xet7 !
2026-10-27 20:57:38 +02:00
Lauri Ojansivu e8a1106782 FrameBleed follow-up: a 304 Not Modified no longer replaces a stored file's own Content-Security-Policy.
Files are served private, no-cache, so every later view of an attachment,
avatar or thumbnail revalidates and gets a 304, and a browser updates the
stored response's headers with the 304's. The app-wide frame-ancestors 'self'
that FrameBleed's restore puts on every response therefore replaced the
file's own policy - an SVG's default-src 'none' and sandbox - from the second
view on. The SVG body is still sanitized on every serve, so this lost a layer
of defence rather than opening script execution. The app-wide framing headers
are now left off a 304, so the stored 200 keeps the policy its route chose; a
header a route set on the 304 itself is kept. Not recorded in Admin Panel /
Problems: it fires on ordinary revalidation, not on an attempt.

Thanks to xet7 !
2026-10-27 20:57:38 +02:00
Lauri Ojansivu a599e066b3 CHANGELOG: Apple Glass Pastel muted text, and the S3 settings spec.
Thanks to xet7 !
2026-10-27 20:46:59 +02:00
Lauri Ojansivu 9679aaf636 Apple Glass Pastel: muted text is darker, and the contrast spec waits for the theme.
The theme's quiet text, #6b7280, was 4.51:1 on #f7f7f7 and 4.44:1 on the
tinted card header, at or under the 4.5:1 it needs; #4b5563 is about 7:1.
The opened card and sidebar contrast tests measured right after a reload,
before the board's colour had reached the page, and so measured WeKan's stock
colours; they now wait for the theme class as the header test does.

Thanks to xet7 !
2026-10-27 20:32:47 +02:00
Lauri Ojansivu 13d3a74b3f Tests: the S3 settings spec expects both of its translated Save buttons.
The S3 pane has had two Save buttons since #6465, one under the Enabled and
Read switches and one after the connection fields; the spec matched one.

Thanks to xet7 !
2026-10-27 20:30:13 +02:00
Lauri Ojansivu 06e27909df CHANGELOG: #6820, and the oldest release moved to old-CHANGELOG/2026/10.md to stay under the size budget.
Thanks to mimZD and xet7 !
2026-10-27 20:26:11 +02:00
Lauri Ojansivu 66ee2d12f3 Card Settings: a ticked Draggable checkbox is a checkbox, not a dot.
forms.css gives every input the 9px 14px padding of a text field. The
Draggable table brought its native checkboxes back without removing it, so a
16px box left the browser about 2px to draw in and a ticked one was a small
blue dot beside an unticked square. The same was true of the WIP limit groups
popup and of task-list checkboxes in a description. All three have no padding
now, and a test fails on any stylesheet that brings back a native checkbox
with it.

Thanks to mimZD and xet7 !

Fixes #6820.
2026-10-27 20:25:48 +02:00
Lauri Ojansivu 9e47a188ce CHANGELOG: the browser test run's fixes - opened card, editors, visibility, drawing, and the specs.
Thanks to xet7 !
2026-10-27 20:14:39 +02:00
Lauri Ojansivu a8f65adff2 Tests: browser specs follow deliberate changes, and their fake SMTP server waits for its port.
LinkedWriteBleed over REST is refused and recorded but no longer disables the
account (#6798); notification intents are seeded recent, past the expiry of
#6768; quoted custom field values; the Login and Windows SSO forms each have
a Save; the Bots view under Rules; the popup stub has onCreated and helpers;
the board view panel fills the window first. An invite mail goes to a running
SMTP sink, a sink waits for a busy port, and the allowed AssignedBleed read
retries on a socket reset only. The first checklist is added with the title
line's +.

Thanks to xet7 !
2026-10-27 20:14:03 +02:00
Lauri Ojansivu 2929b61c85 Drawing: a new drawing fills the window it is shown in, also once maximized.
A new drawing was fitted to the popup before it was maximized and kept that
size. It is refitted whenever its room changes, until it is zoomed or resized
by hand.

Thanks to xet7 !
2026-10-27 20:14:03 +02:00
Lauri Ojansivu 69a8bae0c6 Editors: text typed right after a form opens is no longer selected and replaced.
50 ms after an inlined form opens it focuses the field and selects what it
holds, so the first key replaces a title being edited. Text typed or pasted in
those 50 ms was selected too, and the next key - Enter for a new line - left
only a line break. The selection is skipped when the field already changed.

Thanks to xet7 !
2026-10-27 20:14:03 +02:00
Lauri Ojansivu 978af29b33 Tests: the language specs read the option values, tabs, addresses and texts the app has.
The Gregorian option's value is gregorian; the date-changed triggers are on
the card triggers tab; the shared templates and security panes have their own
addresses; Shortcuts opens as a modal; the roles pane has two descriptions;
saved rule descriptions are lowercased; the feature list is FEATURE_KEYS long;
a plain member is not shown the Rules editor at all (#2217), and the server
still refuses its rule writes.

Thanks to xet7 !
2026-10-27 20:07:01 +02:00
Lauri Ojansivu e6f7d226ca Admin Panel / Attachments: the Google Cloud Storage key help is shown once.
A leftover copy of gcs-credentials-description, from before the English and
translated pairs, showed the same sentence twice under the key field.

Thanks to xet7 !
2026-10-27 20:07:01 +02:00
Lauri Ojansivu 437da1d9c7 Board visibility: the Private and Public descriptions are shown again.
The helper handed TAPi18n.__ over unbound; __ reads this, so it threw and both
descriptions of the visibility popup were an empty line in every language
since the custom descriptions of Admin Panel / Settings / Visibility. It is
passed bound, and a test fails on any TAPi18n.__ passed as a bare value.

Thanks to xet7 !
2026-10-27 20:07:01 +02:00
Lauri Ojansivu a4197f228f Tests: the Card Settings specs open the section a row is in, and pick the list by its side.
The popup opens on its Card section and draws only the open section's rows,
each once per list (Show on Card, Show on Minicard). The specs looked for rows
of other sections and matched a row twice. The linked timer spec reloads after
its database writes, so the board's spent time has reached the page.

Thanks to xet7 !
2026-10-27 20:05:44 +02:00
Lauri Ojansivu f852cb7032 Add card: the due date hint follows the board's switch while the form is open.
The hint was recomputed only on typing, so turning Due date from title off in
Board Settings / Card left a hint already shown.

Thanks to xet7 !
2026-10-27 20:05:44 +02:00
Lauri Ojansivu 8f9103c336 Opened card: it stays visible after Maximize and Minimize, or Collapse.
The class that keeps the window unpainted until it is placed beside its
minicard was a static class of the template, removed by hand once placed.
Blaze writes a tag's static classes again whenever its class attribute
changes, so maximizing and minimizing, or collapsing, brought it back and left
the card open but invisible. It is now a reactive class, off for good once the
window is placed.

Thanks to xet7 !
2026-10-27 20:05:44 +02:00
Lauri Ojansivu 6241be9053 Tests: the browser specs add a first checklist with the title line's +, where #6796 put it.
Since #6796 a card with no checklist has only the + of the Checklists title
line (a.add-checklist-top); the bottom a.add-checklist appears once there is
a checklist. The specs looked for the bottom one only, so every spec that adds
a card's first checklist failed, and 12-checklists skipped its checks silently.

Thanks to xet7 !
2026-10-27 19:45:29 +02:00
Lauri Ojansivu c438682700 CHANGELOG: documentation of Show empty fields, the [label] switch and Data health.
Thanks to xet7 !
2026-10-27 19:42:29 +02:00
Lauri Ojansivu 30e960e129 Docs: Show empty fields of Card Settings, the [label] title switch of Member Settings, and Data health repairs.
Thanks to xet7 !
2026-10-27 19:42:20 +02:00
Lauri Ojansivu 9bea13f152 CHANGELOG: #6818 Data health, and the shared-template spec fix.
Thanks to Raymond-Voogt and xet7 !
2026-10-27 17:08:29 +02:00
Lauri Ojansivu d9cf523f9e Admin Panel / Problems: Data health, a nightly scan of broken references, with repairs.
The scan counts what points at nothing: cards on a missing or another board's
list or swimlane, ghost and duplicate board members, boards with no active
admin, custom field values and boards that no longer exist, checklist items
without a checklist, and subtasks or linked cards whose card is gone. It runs
nightly when the CPU is low, claimed so that only one pod scans or repairs.

A repair that only moves data reuses the upgrade steps; one that deletes asks
twice, and the server refuses it without that confirmation. Every repair edits
with $pull or a guarded update, so a concurrent edit is not overwritten, and a
member dedupe keeps the admin entry. Legacy scalar boardId and string boardIds
of custom fields are understood.

Translations done with low confidence: ay, csb, lld, kok, wa-RR, ve-CC, bi, tpi.

Thanks to Raymond-Voogt and xet7 !

Fixes #6818.
2026-10-27 17:08:15 +02:00
Lauri Ojansivu c1c9d3fb03 CHANGELOG: #6808 follow-up, linked card fields, search fixes, browser spec seed and Tigre/Tsonga wording.
Thanks to xet7 !
2026-10-27 16:44:06 +02:00
Lauri Ojansivu 00cf8f0958 Tests: the shared-template language specs read the description beside the scope list, where it is.
The Corsican and Southern Sotho specs looked for .templates-description and
.shared-templates-hint inside ul#templates-setting, which holds only the three
scope checkboxes; both paragraphs are its siblings in .templates-settings.

Thanks to xet7 !
2026-10-27 16:44:06 +02:00
Lauri Ojansivu 125e1659f8 Search: attachments are found again, by has:attachment, by their filename, and has:-attachment / has:-checklist work.
Found by the Haitian attachment-search browser test. Every attachment
lookup of the global search read the attachment's card from a top-level
cardId, which attachments do not have - they name it in meta.cardId - so
has:attachment, a free-text match on a filename and attachment:<name>
found no card at all; tests/databaseQueryBounds.test.cjs had pinned that
projection. The negated predicates (has:-attachment, has:-checklist)
were ignored and returned the cards WITH one. Soft-deleted attachments
do not count for has:attachment.

Thanks to xet7 !
2026-10-27 16:39:00 +02:00
Lauri Ojansivu 6c2edd6752 Board search: one result highlighted at a time.
Stepping to the next result with Enter highlighted it while the last
one stayed lit for the rest of its three seconds, so two cards were
highlighted at once and it was not clear which was current - found by
the #2171 browser test. Revealing a result removes the highlight from
any other.

Thanks to xet7 !
2026-10-27 16:36:01 +02:00
Lauri Ojansivu 8788458a87 Tests: the browser specs' boards show every field, as the specs were written for.
The specs predate an opened card showing only the fields it has data
in: a checklist spec found no Add checklist on an empty card (and five
others skipped their assertions the same way). The seeded board turns
Card Settings / Card's Show empty fields on - what an opened card did
before - and the specs of the empty-field rules (card-add-fields) turn
it off.

Thanks to xet7 !
2026-10-27 16:35:24 +02:00
Lauri Ojansivu 1f5a7b8220 A linked card shows the fields its source card has.
An opened card shows the fields it has data in, but a linked card keeps
its data on its source card - members, labels, dates, custom fields,
checklists - and the decision read the linked card's own, empty
document: a linked card hid every field of its source. Found by the
#1946/#6613 browser test, whose source member's avatar was missing.

shownFieldsOf() takes the data, and the counts of checklists, subtasks,
text notes, attachments and comments, from the source card, and the
card's own choices (On this card) from the card opened. The opened
card, its sections and Card Settings' On this card all decide through
it. tests/linkedCardShownFields.test.cjs runs it.

Thanks to xet7 !
2026-10-27 16:34:50 +02:00
Lauri Ojansivu 036ac4f56b #6808 follow-up: a card deleted elsewhere also leaves the card's address.
Found by the #6808 browser test: when an opened card really is deleted,
its window is destroyed with its data - usually before the server has
answered whether the card is gone - and the confirmation was dropped
for a destroyed window, so the window closed but the address stayed
the card's. The question is asked outside the window now
(confirmGoneCard, waiting for a settled connection and login), and the
window's end asks it too when the card is missing; the close
(closeGoneCard) clears the window, the popup and the address. A card
that only left for a reconnect still answers available and stays.

Thanks to brlin-tw and xet7 !
2026-10-27 16:32:52 +02:00
Lauri Ojansivu c7145402af Translations: Tigre card noun in the Retention help line of #6819.
The Retention help text came with Tigrinya's bare ካርድ for card; Tigre
says ወረቀት ካርድ, which tests/tigreAllBoardCardContexts.test.cjs holds.

Thanks to xet7 !
2026-10-27 16:30:59 +02:00
Lauri Ojansivu f615d06e47 CHANGELOG: StampBleed, UploadBleed, the regression audit, #6817 and #6819.
Thanks to mimZD, Raymond-Voogt and xet7 !
2026-10-27 16:28:52 +02:00
Lauri Ojansivu ebcd02be6f Hall of Fame regression audit: every fixed vulnerability still fixed, siblings of six of them closed.
Every Hall of Fame vulnerability without a test naming it (24 names, 30
advisories) was checked against the current source. No fix had been
reverted. Looking for the same mistake elsewhere found siblings:

- FloppyBleed: the fs-path-heal upgrade step joined an attachment's
  stored name into candidate paths as-is; a record a client inserted
  named ../../../etc/passwd made the next upgrade copy that file into
  storage - an arbitrary file read. Only the name's last component is
  used now; the bulk move limits a legacy name's extension the same way.
- FilePathBleed (GHSA-g6vm-7757-pr88): the insert guard looked only
  inside versions; with versions left out ostrio:files falls back to a
  top-level path, which removing the record deletes and validation
  reads. A client's top-level path is refused for attachments and
  avatars, recorded by the existing canaries.
- MoveStorageBleed: validateAttachment, which deletes a file failing the
  current upload rules, was open to anybody who could see the board, a
  public one included. Only who may change the file, or a site admin,
  validates now; a refusal is recorded (authz.attachment-validate).
- HookUrlBleed / IntegrationBleed: the REST reads of a board's
  integrations gave the webhook URL - whose path is its secret - to
  every member reading the board; non-admins get what the publication
  gives them.
- AttachmentMigrationBleed: four migration methods answered any
  logged-in user about any board, deleteDuplicateEmptyLists.getStatus
  with a private board's list titles; they require a visible board and
  record refusals (authz.migration-board).
- LegacyAttachBleed (GHSA-xcxp-hx9w-q5q9) and MimeStorageBleed
  (GHSA-rv5g-5p4g-3v89): the legacy route judged a name dangerous only
  for .svg, the response policy missed x-javascript, ecmascript, xsl and
  +xml types, and the sniffers missed <tag/onx= handlers.

OIDCBleed refusals are now recorded in Admin Panel / Problems too. New
suites name the vulnerabilities they guard, existing ones name the
advisories they already covered, and tests/securityRegressionCoverage
counts 128 of 137 published vulnerabilities with a named test (was 125).

Thanks to xet7 !
2026-10-27 16:28:12 +02:00
Lauri Ojansivu 2799eb900f Admin Panel / Attachments / Retention: readable labels, and the default shown.
The Retention page (#6789) showed only the variable names
(ATTACHMENTS_RETENTION_DAYS, ...), the raw choices uploaded and
card-inactive, and Default () with nothing between the brackets
whenever the variable was set but empty - which is what a Helm chart or
a docker-compose file listing the variable does.

Catalog fields (models/lib/authConfigCatalog.js) can name a labelKey, a
helpKey and choiceLabelKeys: the form shows the translated label with
the variable name beside it, a help line, and the choices by name.
Fields without them look as before. An empty variable is unset for the
value shown, as the retention code itself reads it, so the default is
shown again; yes/no defaults are translated. The five retention fields
use them; seven new strings in every language (low confidence for the
rarest and constructed ones). From the reporter's patch.

Thanks to Raymond-Voogt and xet7 !

Fixes #6819.
2026-10-27 16:24:58 +02:00
Lauri Ojansivu ccc67ddf68 Show on Card and an empty field: already handled by Show empty fields, Show empty and On this card.
#6817: on v12.65 the reporter ticked Due in Board Settings / Card's
Show on Card and an opened card without a due date showed no Due field.
Show on Card says what a card can show; since v12.65 an opened card
shows a field when it has data. Showing an empty field is done since
#6806 (Upcoming) by Card Settings / Card's Show empty fields, the Show
empty column, or + Add / On this card. The test pins the reporter's
board through each, and that Show on Card off still wins.

Thanks to mimZD and xet7 !

Fixes #6817.
2026-10-27 16:14:34 +02:00
Lauri Ojansivu 51111e1958 StampBleed and UploadBleed: documents on one's own card stamped with another board, and unauthorized attachment uploads.
StampBleed (found reviewing the patch for #6813): checklists, checklist
items and text notes carry a denormalized boardId, by which the board
publication publishes them, and items a checklistId, by which a
checklist draws them. The insert allow rules check write access on the
document's card only, and the server's insert hooks kept a boardId the
client sent. So a member of any board could insert, on their own card,
a document stamped with a private board's id - published to that
board's members - and an item with that board's checklist id, drawn in
it. 4bd6a7f088 made the server take boardId from the card; now the
forged insert is refused outright and recorded, for those three and for
a direct attachment insert with a foreign meta.boardId. WeKan's own
client never sends a mismatch.

UploadBleed: Meteor-Files' upload pipeline (_FilesCollectionStart_
attachments / Write and its HTTP twin) inserts the stored attachment
SERVER-side, so the allow rules were never asked, and the attachments'
onBeforeUpload only cleaned the file name: any DDP client, signed in or
not, could put a file on any card of any board - published to its
members with an addAttachment activity and its notifications - and the
admin's uploads-blocked switch was skipped too. The upload is
authorized on every chunk now: signed in, allowed to edit the card (or
write the board, for a background or map image), uploads not blocked,
and a card attachment's board, list and swimlane are its card's.

Both are recorded in Admin Panel / Problems (authz.card-child-stamp,
authz.attachment-upload), once per upload. tests/stampBleed.test.cjs and
tests/uploadBleed.test.cjs run the real decisions, and fail on a card
child with no insert board check or a FilesCollection without a server
upload check; upload-stamp-bleed.e2e.js attacks the running server.

Thanks to xet7 !
2026-10-27 15:38:58 +02:00
Lauri Ojansivu eade07c914 CHANGELOG: #6809, #6810, #6811, #6812, #6813, #6814, #6815 and #6816.
Thanks to brlin-tw, jullbo, Raymond-Voogt and xet7 !
2026-10-27 15:23:55 +02:00
Lauri Ojansivu 595e26cfa8 Translations: Tigre and Tsonga wording of the [label] switch of Member Settings.
The new texts of f89c8f8030 took Tigrinya's word for name (ስም) in Tigre,
which has its own (ስሜት), and repeated Tsonga's nhloko-mhaka (title)
three times in one sentence - the shape the guards against generated
filler catch. tests/tigreAllEstablishedNounContexts.test.cjs and
tests/wrongLanguageCorrections20261014.test.cjs pass again.

Thanks to xet7 !
2026-10-27 15:23:55 +02:00
Lauri Ojansivu 32031c64c4 Upgrade: cards moved to another board before #5874 are shown again.
#5874 fixed new cross-board moves (server/lib/cardBoardConsistency.js),
but a card moved before that fix can still point at a list of the board
it LEFT. Every view of its own board selects cards by that board's
lists, so the card is shown nowhere, and swimlane-structure's
dangling-listId rescue does not see it because the list exists. One
production database (1.1 million cards) had 706 such cards on 383
boards, 391 not archived.

A startup upgrade step, card-list-other-board, gives each such card its
own board's first unarchived list (and that list's swimlane when it is
bound to one), or a Rescued Data list when the board has none. Cards are
streamed with two fields, one updateMany per (list, board) pair,
idempotent. From the reporter's patch, with one change: the step runs
BEFORE swimlane-structure, so a rescued card whose new list is not bound
to a swimlane - it kept the swimlane of the board it left - is put on a
swimlane of its board in the same pass, not hidden in the Swimlanes view
until the next release re-runs the checks.

Thanks to Raymond-Voogt and xet7 !

Fixes #6814.
2026-10-27 15:09:38 +02:00
Lauri Ojansivu f2908f34e7 Card numbers stay unique: the server checks the number a browser sends.
#5813 / #4743 made server-side card numbers atomic with a per-board
counter, but a card added in the browser - the add-card form, a card
from a template, a link, a checklist item turned into a card - still
brings the number the browser computed: the highest among the cards it
has, plus one. With lazy card loading the browser has only the cards in
view, so it reissued numbers other cards already carried, and the
counter never saw them, so the server reissued them as well. One
production database had 842 numbers used by two or more open cards.

Cards.before.insert on the server: a number another card on the board
already has is replaced from the counter, after raising the counter to
the board's highest number; a free number the counter did not issue
raises the counter to it. A board with no counter yet is seeded from its
highest number first, as getNextCardNumber's lazy seed does - the
reporter's patch created it from the browser's number alone, which can
be below numbers outside that browser's view, and the seed would then
never run. 0 / missing stays no number. Existing duplicates are not
renumbered: a number may be written down elsewhere.

Thanks to Raymond-Voogt and xet7 !

Fixes #6815.
2026-10-27 15:08:21 +02:00
Lauri Ojansivu 742996dcce Deleting a checklist deletes its items.
Every way to delete a checklist - the card's checklist menu, DELETE
/api/boards/:boardId/cards/:cardId/checklists/:checklistId, the
removeChecklist rule action and the durable rule plans - removed only the
checklist document; Checklists.before.remove writes the removeChecklist
activity and nothing else. Every item stayed in the database with a
checklistId pointing nowhere: invisible on the card and never cleaned
up. One production database had 2335 of them from 690 deleted
checklists.

Checklists.after.remove now removes the checklist's items with
ChecklistItems.direct.removeAsync, as deleting a card already removes
its children: no activity per item, the checklist's own removeChecklist
activity records the deletion. Items orphaned before this are left for
the administrator to decide. From the reporter's patch.

Thanks to Raymond-Voogt and xet7 !

Fixes #6816.
2026-10-27 15:06:32 +02:00
Lauri Ojansivu f89c8f8030 Add card: [Text] at the start of a title becomes a label only when turned on in Member Settings.
Since #3986 a new card's title starting with [Text] followed by more
text lost the prefix and got the label Text, created on the board when
it had none - for everybody, without a hint. Titles pasted from ticket
systems, mails and monitoring tools ([Follower] #123456 - ...,
[EXTERNAL] ...) were changed and filled the board with unwanted labels.

It is a Member Settings switch now, Turn [label] at the start of a new
card title into a label (profile.titleBracketLabel), off by default: a
title is kept as typed. Who wants #3986's behaviour turns it on. Both
texts are translated in every language; low confidence for the
constructed and rarest ones (tlh, vo and some 35 others).

Thanks to jullbo and xet7 !

Fixes #6810.
2026-10-27 15:04:31 +02:00
Lauri Ojansivu 6f5aab875b Date popups: Save without picking a day saves today instead of Invalid date.
A date popup of a card without that date opens the calendar on today,
marks today (#6751) and names it in the calendar's heading - so today
is what the form shows. But the form's date field was empty, and Save
without clicking a day answered Invalid date.

The form starts from today now (draftDate in
imports/lib/datePickerTime.js): the date field, the calendar's selected
day and its heading agree, and Save saves today at the popup's default
time (17:00 for a due date, #1502). A card's own date is kept as before,
and an emptied date field is still refused.

Thanks to brlin-tw and xet7 !

Fixes #6809.
2026-10-27 14:56:21 +02:00
Lauri Ojansivu 4bd6a7f088 Dragging a checklist item on one's own board is no longer reported as a cross-board attack.
Dragging a checklist item within its card sets cardId and checklistId
to where the item already is. The ChecklistBleed deny rule
(GHSA-gv8h-5p3p-6hx7) checked only that the destination board was
writable, so for a comment-only member or a Worker - whose own board is
one they cannot write to - an ordinary drag tripped the high-severity
canary checklist-item.cross-board-move and named them in Admin Panel /
Problems / Security as if they had attacked another board.

The board the document is already on is no destination now. That board
is the one of its SOURCE CARD, which the allow rule decides on - not
the document's own denormalized boardId, as the reporter's patch read
it: the insert hooks kept a boardId the client sent, so an item on the
attacker's card could carry a victim board's id and would have passed
as staying while moved onto a victim card. Those hooks (checklists,
checklist items, and now text notes on the server) set boardId from the
card always. A move to another unwritable board, and an unknown
destination, are still denied and still reported.

Thanks to Raymond-Voogt and xet7 !

Fixes #6813.
2026-10-27 14:52:06 +02:00
Lauri Ojansivu 2dfdf77c2b Tests: the Card Settings drag and drop spec turns drag handles on and off for its user by user.id.
The fixture's user has an id, not an _id: setHandles(user._id, ...) of
8224852de1 matched no user, so the handle half of the spec never had
drag handles on.

Thanks to xet7 !
2026-10-27 14:52:06 +02:00
Lauri Ojansivu f6fd560817 Upgrade: boards with a Show on card custom field keep it on their minicards.
Custom fields on minicards became a Board Settings / Card switch,
allowsCustomFieldsOnMinicard (1d4ffd4080), opt-in, with false written
into every board. Before it, a custom field marked Show on card was
always drawn on the minicard, so after the upgrade those fields
disappeared from every minicard until a board admin found the switch -
reported as custom fields being visible only after opening the card.

A startup upgrade step, custom-fields-minicard-on, turns the switch on
for each board that has at least one custom field with showOnCard: that
board's own earlier choice. Boards without one keep the opt-in default.
Once ever, like cover-on-card-on: it records itself in _wekan_migration,
so a board turned off afterwards stays off on later upgrades. The test
db's distinct() returns array elements one by one, as MongoDB does.
From the reporter's patch.

Thanks to Raymond-Voogt and xet7 !

Fixes #6812.
2026-10-27 14:48:19 +02:00
Lauri Ojansivu a24da2a039 Minicard: only the custom fields that have a value, as before.
Until the minicard's sections were rewritten (1d4ffd4080), a minicard
drew a custom field marked Show on card only when it had a value - the
2019 check of e2a374f0aa. The rewrite dropped it, so every field a card
carries was drawn, also the ones added empty by Automatically on card /
Always on card: boards with several such fields got a stack of empty
rows on every minicard.

hasCustomFieldValue() in models/lib/customFieldsWD.js: null, undefined,
an empty or blank string and an empty multi-select are empty; 0 and an
explicitly unticked checkbox are values (the old check hid 0). The
minicard draws a field only when it has one; the opened card is
unchanged. From the reporter's patch.

Thanks to Raymond-Voogt and xet7 !

Fixes #6811.
2026-10-27 14:47:25 +02:00
Lauri Ojansivu d05e4db865 CHANGELOG: Card Settings drag and drop lists, and #6808.
Thanks to brlin-tw and xet7 !
2026-10-27 10:05:27 +02:00
Lauri Ojansivu 7ed3ca7c5c A card opened from its link stays open when the connection drops and comes back while the page loads.
Behind a reverse proxy the DDP connection can drop and come back while a
card's link is still loading (a WebSocket upgrade the proxy refuses,
SockJS falling back). Every subscription re-runs on the way back, one
answers before the resumed login with nothing, and the card leaves
Minimongo for a moment. #3114's close-on-removal took that moment for a
deletion: it closed the card and went to the board's address.

The window now waits while the subscription, the connection or the login
is settling, and when the card looks gone with everything settled, asks
the server (cards.openCardAvailable, the 'card' publication's own rule,
now one function in server/lib/openCardAccess.js) and closes only on its
no, if Minimongo still agrees. A card really deleted, archived or moved
elsewhere still closes. Not reproduced here: no proxy in this
environment; the browser test drops and restores the connection.

Thanks to brlin-tw and xet7 !

Fixes #6808.
2026-10-27 10:05:01 +02:00
Lauri Ojansivu 8224852de1 Card Settings: a Show on Card and a Show on Minicard list per section, reordered by drag and drop again.
On 2026-10-25 each row became one line with both sides' cells and order
arrows. But the lines could follow only one side's order, and the
number beside an arrow was the field's place among ALL of that side's
fields: an arrow often changed the number and nothing visibly moved,
the minicard's arrows never moved a line, and an arrow that did nothing
was not disabled. Drag and drop, which the arrows had replaced, had been
there since 2026-10-02.

Each section has the two lists again, each in its own order and
reordered by drag and drop: Show on Card - with On this card and Show
empty beside each row - and Show on Minicard. A row is dragged by its
handle (drag handles on) or its icon or name; Up and Down on a focused
one move it one row in the list. A dropped row goes right after the row
now above it in the side's whole order, through placeKey, so a pinned
header stays first and a section moves as one. The arrows and their
numbers are gone.

Thanks to xet7 !
2026-10-27 10:05:01 +02:00
Lauri Ojansivu 8d61402f5b CHANGELOG: Ask for a reason and the select buttons in Card Settings.
Thanks to xet7 !
2026-10-27 09:17:10 +02:00
Lauri Ojansivu 37b3c8f599 Card Settings: Ask for a reason is the same checkbox as Show empty fields, and Select all / Select none are Draggable's buttons.
Ask for a reason when moving cards was a Font Awesome square a few
pixels off Show empty fields above it, its classes toggled by hand. It
is the same animated checkbox now, in the same place, read reactively
from the board and set by a board admin. Select all and Select none
under the columns' headings were links; they are the Draggable table's
buttons - white text on the theme's colour - in its container.

Thanks to xet7 !
2026-10-27 09:17:00 +02:00
Lauri Ojansivu 9b65aed902 Merge remote-tracking branch 'origin/main' 2026-10-27 08:30:03 +02:00
Lauri Ojansivu 37e9253c93 Merge pull request #6807 from ocdtrekkie/sandstorm-org
Switch Sandstorm build to new fork
2026-10-27 08:23:40 +02:00
Jacob Weisz 3f718859bc Switch Sandstorm build to new fork 2026-10-27 01:09:09 -05:00
Lauri Ojansivu 831b2bd8b5 CHANGELOG: Card Settings Select all and Select none, aligned columns.
Thanks to xet7 !
2026-10-26 16:40:33 +02:00
Lauri Ojansivu c8a15b514f Card Settings: Select all and Select none per column, headings aligned with their cells, labels whole.
Under each column heading of every pane - On this card, Show on
Minicard, Show on Card, Show empty - Select all and Select none set
that column for the open section's rows at once, each the way its own
checkbox would: On this card in one write of the card, Show empty in
one write of the board's list, the Scrum rows in one Scrum write (one
by one, each would carry the same revision), the others through their
own checkbox's handler. Who may change the column gets the buttons; a
personal row (Labels text, List title) is left as it is.

The headings drifted left of their cells, further each column, and the
labels were cut: the heading line had the smaller font, and the columns
are in em, so its columns were narrower than the rows'. The font is on
the headings now, every heading and cell starts at its column's start,
and the popup is up to 1200px wide instead of 900px, so a row's whole
label fits where the window has the room.

Thanks to xet7 !
2026-10-26 16:40:14 +02:00
Lauri Ojansivu c687fe5577 CHANGELOG: Card Settings opens on Card, Show empty fields, Show empty.
Thanks to jullbo and xet7 !
2026-10-26 16:23:01 +02:00
Lauri Ojansivu 1aa9df6075 Card Settings: opens on Card, Show empty fields for the board, and the Show empty column.
Card Settings opens on its topmost entry, Card - also from + Add,
which opened on the first section of fields. Under the Card pane's
title, Show empty fields (the board's showEmptyCardFields, off by
default): ticked, every card of the board shows all its fields, the
empty ones too, and the Show empty columns are hidden - by CSS only, so
their ticks stay saved and come back as they were when it is unticked.
On this card still hides a field from one card, and Show on Card off
still hides it from all.

The Always show column is called Show empty; it is stored as before, in
cardFieldsAlwaysShown. Both headings are translated in every language.

Thanks to jullbo and xet7 !

Fixes #6806.
2026-10-26 16:22:29 +02:00
Lauri Ojansivu cca9ce36cf CHANGELOG: Always show in Board Settings / Card.
Thanks to jullbo and xet7 !
2026-10-26 15:33:30 +02:00
Lauri Ojansivu b54525abbe Board Settings / Card: Always show, for every field - the description, attachments and comments by default.
Show when empty became Always show and covers every per-card field,
the description, attachments and comments included: those were always
shown by a fixed list, and are now the default ticks of the board's
cardFieldsAlwaysShown. A board that never set it shows them as before;
unticked, they are shown when the card has a description, an
attachment or a comment, like any other field. The board's list is
exactly what is ticked once set, an empty list too. On this card still
hides an always-shown field from one card.

The first name, cardFieldsShownEmpty, was never pushed, so there is
nothing to migrate. tests/cardFieldsAlwaysShown6806.test.cjs pins the
default, unticking the four, the storage and the column; the browser
test unticks Comments.

Thanks to jullbo and xet7 !

Fixes #6806.
2026-10-26 15:33:12 +02:00
Lauri Ojansivu 5056790c05 CHANGELOG: Show when empty in Board Settings / Card, and the heading colour guard.
Thanks to jullbo and xet7 !
2026-10-26 15:06:52 +02:00
Lauri Ojansivu 80b936223f Board Settings / Card: Show when empty, the fields every card of the board shows even while empty.
Since an opened card shows only the fields it has data in, a board where
nearly every card gets labels, a due date and a checklist needed + Add,
the section and On this card for each new card before a field could be
filled in. The new Show when empty column of Board Settings / Card (and
of + Add, the same popup) is the board's list, cardFieldsShownEmpty: a
ticked field counts like data and is on every card from the start. On
this card still hides it from one card, Show on Card off still hides it
from all, and the four always-shown fields have no such checkbox.

It is a board admin's setting, stored normalised by
setCardFieldShownEmpty, and translated in every language.
tests/cardFieldsShownEmpty6806.test.cjs pins the rule, its storage, the
column, and fails on any caller of isShown or cardFieldsAfter that does
not pass the board; card-add-fields.e2e.js ticks it in the browser.

Thanks to jullbo and xet7 !

Fixes #6806.
2026-10-26 15:05:10 +02:00
Lauri Ojansivu 21b2da9ad2 Tests: the title heading colour guard reads the heading block itself.
It read from the LAST comment in minicard.css and cardDetails.css to the
end of the file, so it passed only while the heading rules' neighbours
happened to set no colour - and failed once the Card kind check mark,
appended at the end of cardDetails.css with its own colour, came after
them. It reads the heading block, from its comment to the blank line
after it, and still fails on a colour there.

Thanks to xet7 !
2026-10-26 15:05:09 +02:00
github-actions[bot] 43c8606548 Bump versions for v12.65 2026-10-25 16:13:18 +00:00
Lauri Ojansivu e5ed5d16dc Prepare v12.65 release 2026-10-25 18:12:06 +02:00
Lauri Ojansivu 213af1c1bd CHANGELOG: Custom Fields forms in the pane, Card kind shows the selected one, #6805.
Thanks to mimZD and xet7 !
2026-10-25 18:02:36 +02:00
Lauri Ojansivu ca53019d96 Card Settings / Scrum settings and Card kind popup: the selected card kind is shown.
A check mark at the end of its line, bold, and aria-checked in a radiogroup.
Before, the selected kind only had an is-active class that nothing styled.

Thanks to xet7 !
2026-10-25 18:02:14 +02:00
Lauri Ojansivu 37024aa9f9 Right-to-left: the comment form's avatar is inside the card.
The comment form keeps 38px free for the writer's avatar, which is drawn
at the reading start (inset-inline-start: -38px) - but the room was a
physical LEFT margin, so on a right-to-left page (Persian in the report)
the avatar was drawn on the right, outside the card. The room is
margin-inline now, and the other margins of the comments' layout are
logical too, so all of it mirrors with the page.
tests/commentFormRtl6805.test.cjs pins the avatar to the room kept for it
and fails on any left/right-only margin or padding in comments.css.

Thanks to mimZD and xet7 !

Fixes #6805.
2026-10-25 17:59:52 +02:00
Lauri Ojansivu f33987931f Card Settings / Custom Fields: Create Field and Link fields to another card are forms in the pane, not popups.
Under Edit custom fields, the Custom Fields pane has the two forms its
buttons opened: Create Field (also from Board Settings, for a board
admin) and Link fields to another card. In the pane the list's own two
buttons are not repeated; a new field saved there empties the form for
the next one, and Card Settings stays open. Opened as their own popup -
the card's Custom Fields hamburger - they step back when saved, as
before (client/lib/popupBack.js, now for a form drawn by more than one
popup, and telling whether it stepped back).

Thanks to xet7 !
2026-10-25 17:59:52 +02:00
Lauri Ojansivu 80d0b2c47e CHANGELOG: Card Settings pane columns line up.
Thanks to xet7 !
2026-10-25 17:34:54 +02:00
Lauri Ojansivu ede0d68b27 Card Settings: the pane's columns line up, and an inline form's buttons no longer push the rows aside.
Each line of a section pane is its own grid, and its side columns were
sized by their content, so no two lines - nor the heading line - agreed.
They have fixed widths now (On this card 7em, Show on Minicard and Show
on Card 9em each), a longer heading wraps in its column. The inline forms
float their Save left and Delete right (Change spent time, ...); the form
keeps those floats inside it (flow-root), where they had pushed the
heading and the first row aside.

Thanks to xet7 !
2026-10-25 17:34:52 +02:00
Lauri Ojansivu a433d20189 CHANGELOG: Card Settings sections and + Add as one popup, card actions in their sections, the card menu's order.
Thanks to xet7 !
2026-10-25 17:31:02 +02:00
Lauri Ojansivu c69f3fe25a Card Settings: Card is the first entry, Subtasks and Planning Poker are sections of their own, and each section's settings are forms in its pane.
- The first entry is Card, with the card icon: Ask for a reason when
  moving cards, the Draggable table, and the card's own rows that were
  the last entry (Card number, Collapse, Parent card, ...).
- Subtasks is its own section under Checklists, Planning Poker its own
  under Voting.
- Each section's settings are the form itself under the pane's title,
  not a link to a popup: Subtask settings, Edit custom fields, Change
  spent time, Card kind, Edit voting, Edit Planning Poker. Saved in the
  pane Card Settings stays open; the section hamburgers of the opened card
  still open the same forms as their own popup, which step back when saved
  (client/lib/popupBack.js).

Thanks to xet7 !
2026-10-25 17:30:37 +02:00
Lauri Ojansivu 65621f44cc Card actions menu in the maintainer's order; Card Settings: Time under Date, Edit voting and Edit Planning Poker in the Voting pane, Select all centred under Draggable.
- The opened card's actions menu is one list, in this order: Move Card,
  Copy Card, Save as Template, Copy Template to Many Cards, Card
  recurrence, Move to Top, Move to Bottom, Watch, Star | Set color,
  Drawing | Copy link to clipboard, Link to board, QR code, History,
  Export, Import, Move Card to Archive, More. Each item keeps the
  permission it had; the two copies of the menu for the two kinds of
  member are one list (cardMenuWrites). tests/cardActionsOrder.test.cjs.
- Card Settings: Time is under Date in the left menu.
- Card Settings / Voting: Edit voting and Edit Planning Poker are the forms
  themselves, under the pane's title; saved there, Card Settings stays
  open, saved in their own popup (the section hamburgers) they step back.
- Select all / Unselect all are centred under their Draggable heading.

Thanks to xet7 !
2026-10-25 17:13:36 +02:00
Lauri Ojansivu 4f20f24201 Card actions in their sections: Filter: Subtasks, Edit voting, Edit Planning Poker, Change spent time and Card kind in the opened card's section hamburgers and in Card Settings' panes.
- The five leave the card's actions menu. On the opened card each is in
  the hamburger of its section: Subtasks (Filter: Subtasks, and the
  subtask settings), Voting, Planning Poker, Spent time, and a new Scrum
  settings heading over the Scrum fields the card shows (Card kind).
  Voting, Planning Poker and Spent time show their heading when ticked
  "On this card" before there is anything in them, so the hamburger can
  fill them in.
- Card Settings: everything in a section's hamburger is also at the top
  of that section's pane, under its title - the card's actions when it
  is opened from a card, Subtask settings also from Board Settings.
  tests/cardSectionActions.test.cjs fails for a hamburger that is not.
- Filter: Subtasks is one function, client/lib/filterSubtasks.js.
- Card Settings: no select-all rows for Show on Minicard and Show on Card
  in its first entry, where there is nothing under them; the popup is
  high enough for every entry of its menu.

Docs: docs/Features/Cards/Card-Fields.md.

Thanks to xet7 !
2026-10-25 16:55:03 +02:00
Lauri Ojansivu 20e2273c07 Board Settings / Card and the opened card's "+ Add" are one popup: sections in the Admin Panel's left menu, each row with Show on Minicard, Show on Card and On this card.
- Board Settings / Card is the Admin Panel's left menu and pane. The menu
  lists the sections (models/lib/cardSettingsSections.js): Card Settings
  first (Ask for a reason when moving cards, the select-all of each side,
  Draggable), then Date, Members, Labels, Description, Checklists,
  Attachments, Time, Custom Fields, Voting, Scrum settings, Comments and
  Card. Every row of the table is in exactly one section.
- The pane shows the open section's rows, one line each, in the reading
  order On this card, Show on Minicard, Show on Card, the field: each side
  cell the board-wide checkbox, the field's position on that side and its
  up and down arrows (the Up and Down keys on a focused arrow too; the
  maintainer's choice, replacing the drag and drop of the two long lists).
  Every checkbox is the same animated one, and only the one just clicked
  animates.
- The opened card's "+ Add" opens this same popup for the card, with the
  "On this card" column; the separate Add popup is gone. A reader who may
  not change the board sees its checkboxes disabled.
- A row's name leaves out what its section says: under Scrum settings the
  rows are Sprint, Release, ..., not "Scrum settings: Sprint".
- The popup moves by its title bar, maximizes, is 900px wide (popupOffset
  and popup.css agree) and high enough for its menu.
- A maximized card's comments, at the top of the right column, have no
  separator line above them.
- New string "On this card" in every language. Low confidence: ace, ak, ay,
  bi, bm, bua, chr, csb, dz, ff, fj, gn, gv, iu, kl, ks, kw, lld, nah, nap,
  nso, om, pap, rn, rup, rw, sc, scn, se, sm, ss, st, tig, tlh, to, tpi, ts,
  ve, ve-PP, vo, wal, wo, wa-RR, zgh, nd, xh, zu, ja-HI.

Docs: docs/Features/Cards/Card-Fields.md and
docs/Features/Board-Sidebar/Board-Settings/Card.md. The Playwright specs of
the old two-list layout are rewritten for the sections and arrows.

Thanks to xet7 !
2026-10-25 16:12:19 +02:00
Lauri Ojansivu cef5ddc506 CHANGELOG: cover on by default, new drawing as cover, + Add popup, maximized card comments.
Thanks to xet7 !
2026-10-25 15:13:55 +02:00
Lauri Ojansivu 5d80b07bed Opened card: when maximized, the comments section is in the right column.
The comments are their own template, cardCommentsSection, in the left
column of the opened card and, on a maximized card, at the top of the
right one beside the fields; the right column is as wide as the left.

Thanks to xet7 !
2026-10-25 15:13:44 +02:00
Lauri Ojansivu c4f528159b Opened card: cover on by default, a new drawing is the cover, one line under + Add, no empty Similar cards or Custom Fields; the + Add popup moves, maximizes and fits its menu and labels.
- The cover image on the opened card is on by default. The schema default
  is true, and the once-ever upgrade step cover-on-card-on turns it on for
  the boards created while it was opt-in; a board turned off later stays
  off, also on an install that had no board yet.
- A new drawing becomes the card's cover; saving an edit leaves the cover.
- One separator line under "+ Add": the Sort group's gate read the board
  itself, not the card's view, and drew its rule over fields the card does
  not show.
- Similar cards: asked when the card opens, and the section is there only
  when there are some, with how many.
- Custom Fields: only on a card that has a custom field value or ticked
  Custom Fields in "+ Add" - which is now how a card with none gets the
  section and its menu.
- The "+ Add" popup moves by its title bar (and by the drag handle, shown
  only while drag handles are on, for every movable popup), has maximize /
  minimize beside its close button, is wide enough for the pane's labels
  and high enough for every menu entry, and a pane opened shows its ticks
  without animating them - only the clicked one animates.

Thanks to xet7 !
2026-10-25 15:11:03 +02:00
Lauri Ojansivu 0782bda994 CHANGELOG: Opened card shows only the fields it has data in, with + Add for the rest; archive one release to old-CHANGELOG.
Thanks to xet7 !
2026-10-25 14:00:30 +02:00
Lauri Ojansivu 6fe0a7ef94 Opened card shows only the fields it has data in, and "+ Add" under the cover adds the rest, in the Admin Panel's left menu and pane.
An opened card shows only the fields it has data in, plus its
description, attachments and comments, empty or not, and its cover when
it has one. "+ Add", under the cover, opens a popup with the Admin
Panel's own left menu and pane title: the sections (Date, Members,
Labels, Description, Checklists, Attachments, Time, Custom Fields,
Voting, Scrum settings, Comments, Card) on the left, the open section's
fields on the right - the items of Board Settings / Card - each with a
checkbox. Ticking an empty field shows it on this card; unticking a field
hides it from this card, also when it has data. The card keeps its
choices in shownFields and hiddenFields, which accept only field keys.

Board Settings / Card still decides what a card CAN show: a field turned
off there is on no card and not offered. The opened card and its section
templates get their own currentBoard, the card's view of the board with
the flags of the fields it does not show reading false, so the existing
template checks apply without being edited; the minicard is unchanged.

The design is docs/Features/Cards/Card-Fields.md. Seeded e2e cards show
every field, as the specs written before expect.

Thanks to xet7 !
2026-10-25 14:00:05 +02:00
Lauri Ojansivu 40dbb829c9 CHANGELOG: Drawing custom colors kept with the account when signed in.
Thanks to xet7 !
2026-10-25 12:10:05 +02:00
Lauri Ojansivu 91c12ebcce Drawing: custom colors are kept with the user's account when signed in, and in the browser on a public board without signing in.
The custom colors added to the Drawing popup's palette are stored in the
user's profile (profile.drawingColors, through the new setDrawingColors
method), so they follow the user to every browser. The method takes only
#rrggbb codes, each once, at most 16, and refuses a caller who is not
signed in; the schema holds only #rrggbb. Someone drawing on a public board
without signing in keeps them in this browser, as before.

Thanks to xet7 !
2026-10-25 12:09:52 +02:00
Lauri Ojansivu c486518f03 CHANGELOG: Drawing on the whole window with zoom and any color; Romanian, Tamazight and Urdu in their own languages.
Thanks to xet7 !
2026-10-25 08:56:46 +02:00
Lauri Ojansivu d5d027a6f2 Drawing: the whole window to draw on, growing from its corner, with zoom, any color and Save above it; Drawing in Add Card's Search / Template / Drawing / More options.
The drawing area takes all the room the popup has: a new drawing is as
wide and high as that room, instead of a fixed 800 x 500 in the middle.
A handle at its bottom-right corner (bottom-left right to left) makes it
larger, or smaller without cutting off a line, and the magnifier buttons
zoom it, scrolling when it no longer fits. A drawing keeps its size when
saved; older ones are 800 x 500. Save is above the drawing, at the end of
the tools.

Colors: clicking a color opens a color wheel, a brightness slider and the
HTML color code; custom colors are added to the palette, kept in this
browser; the eyedropper picks from the screen where the browser can
(EyeDropper API) and from the drawing elsewhere. Any #rrggbb is a color,
nothing else, so a color never becomes anything else in the SVG.

Add Card: Drawing is in the row Search / Template / Drawing / More
options, for one card, and opens the new card's drawing on the whole
window, saved as an SVG attachment.

New strings in every language. Low confidence: tlh, vo, la, chr, iu, kl,
nah, ay, qu, gn, zgh, tig, wal, om, bo, dz, bua, sah, cv, csb, szl, hsb,
rup, lld, fur, nap, scn, sc, co, ve-CC, ve-PP, wa, gv, kw, rm, ak, bm, ee,
ff, lg, rn, rw, ss, ts, ve, nso, tn, st, ny, sn, nd, wo, fj, sm, to, bi,
tpi, haw, pap, ks, uz-AR, ace, wa-RR, ary, yi, wuu-Hans.

Thanks to xet7 !
2026-10-25 08:56:20 +02:00
Lauri Ojansivu d3dead539b Translations: Romanian, Tamazight and Urdu in their own languages.
Romanian (ro 158 values, ro-RO 179): about 160 values each were Italian
("Sposta in cima", "rosso", "ti ha inviato un invito"), with some Spanish
("disparador", "carril") and French ("Connexion", "Erreur"); also cedilla
ş ţ replaced with comma-below ș ț.

Standard Moroccan Tamazight (zgh, 344 values): about 100 values were
Arabic and about 230 Kabyle or French in Latin letters; all now in
Tifinagh. Values already in Tifinagh, including reviewed ones with a
Latin product name, are kept as they were.

Urdu (ur, 2389 values): most of the file was English spelled letter by
letter in Urdu script ("منتقل کو توپ" for Move to Top - توپ is a cannon,
"سیزے" for Size) or English word order word for word; rewritten in
natural Urdu, with the stray "اردو:" prefixes removed.

Search keywords in these four locales are one word of letters, as the
query parser reads them; the underscore, space and zero-width non-joiner
forms could not be typed as operators.

Every placeholder matches English. tests/wrongLanguageCorrections20261025
pins each language, the placeholders and the search keywords; the guards
that pinned the old Arabic and Kabyle values say why they changed.

Low confidence: much of zgh (technical and statistics terms), Urdu
technical and Blockly terms, Romanian search keywords (culoar, termen,
verificare, câmppersonalizat).

Thanks to xet7 !
2026-10-25 08:27:47 +02:00
Lauri Ojansivu da4a0cf013 CHANGELOG: The Drawing lasso: select lines to move, resize, group and ungroup, and change their layer.
Thanks to mimZD and xet7 !
2026-10-25 07:40:48 +02:00
Lauri Ojansivu 1c26e1bcd0 Drawing: a lasso that selects lines to move, resize, group and ungroup, and to bring to front or back or one layer up or down.
A loop drawn with the Lasso selects every line wholly inside it, and a
grouped line brings its whole group. The selection moves together from
inside its box, stopping at the edge; resizes from the handle at its corner,
line widths to the nearest allowed one; groups and ungroups, the group kept
with the saved drawing; and goes to the front, the back, or one layer up or
down. Undo now steps back through every change, a move or a resize being one
step.

New strings in every language. Low confidence: ace, ak, ay, bm, bo, dz, br,
bua, chr, csb, cv, ee, ff, fj, fur, gn, gv, haw, hsb, ig, iu, kl, ks, kw, lg,
lld, nah, nap, nd, ss, ts, ve, ve-CC, ve-PP, qu, rm, rup, sah, sc, scn, se,
szl, tig, tlh, vo, wa, wa-RR, wal, wo, yi, zgh, uz-AR, ja-HI, om, sm, to, tpi,
bi, mi.

Thanks to xet7 !
2026-10-25 07:40:16 +02:00
Lauri Ojansivu 0390468e07 CHANGELOG: An SVG of any size is sanitized whole, streaming.
Thanks to xet7 !
2026-10-25 07:24:13 +02:00
Lauri Ojansivu e748559838 SVG of any size is sanitized whole, streaming: on upload, on validation and on every serve.
A streaming tokenizer (models/lib/svgSanitizer.js) replaces the regular
expressions that read an SVG whole: it removes script, foreignObject,
iframe/object/embed, SMIL set/animate, event handlers, javascript:/vbscript:
URIs after decoding character references, dangerous CSS, DOCTYPE/ENTITY XML
loops and processing instructions, wherever they are and however the file is
split into chunks, with bounded memory. UTF-16 is decoded and checked.

Upload sanitizing streams to a temporary file and renames it; validation
checks the whole SVG instead of rejecting any over its 1 MB scan; serving
pipes through the sanitizer with no 8 MB fallback to an unsanitized
download.

Thanks to xet7 !
2026-10-25 07:23:31 +02:00
Lauri Ojansivu 9e9e1e271f CHANGELOG: Drawing with a pen from Add Card and Add attachment, saved as SVG; the movable, maximizable Drawing popup; SVG as an image again.
Thanks to mimZD and xet7 !
2026-10-25 06:33:03 +02:00
Lauri Ojansivu 54726264d3 Drawing: a new drawing from Add Card and Add attachment, saved as SVG; an eraser and palm rejection.
#6802 asks for handwriting with a pen, saved as a card attachment, and
started while the card is created:

- Add attachment / Drawing, and Add Card's Drawing button (shown while
  the composer adds ONE card: the card is added, opened, and the drawing
  starts on it), open a NEW drawing on the whole window - the Drawing
  popup maximized, without the card's earlier drawings - saved as an SVG
  attachment of the card, its lines kept on the card for editing.
- The eraser takes away every line it touches (measured to the line's
  segments, its width included); Clear now has the trash icon.
- Palm rejection: once a pen has drawn, a touch does not, and while one
  pointer draws no other starts a line. A failed pointer capture no
  longer stops a line from starting.
- A Drawing popup opened from the card menu, which replaces the menu in
  the same popup, now opens maximized when it was left maximized.

The openers live in client/lib/newDrawing.js, which touches no template:
importing the popup's own module from attachments.js ran it before
cardDetails.jade had defined the template, and the client did not start.
Eraser is in every language. docs/Features/Cards/Drawing.md describes
it all. tests/cardDrawingPen.test.cjs tests the eraser and palm rejection
as pure functions and pins the new flows;
tests/playwright/specs/card-drawing-pen.e2e.js draws and saves both new
drawings to SVG attachments, erases a line and rejects a palm in a
browser.

Thanks to mimZD and xet7 !

Fixes #6802.
2026-10-25 06:32:28 +02:00
Lauri Ojansivu 0afb1336db SVG is uploaded and shown as an image: an attachment, a cover, a board background, an avatar.
Since JVN#74210258 (stored XSS) an SVG upload was refused and any stored
SVG was served as a download, so an SVG could not be a card's picture.
Every uploaded SVG is already sanitized on the server before it is
stored - no script, event handler, javascript: URI, foreignObject or
DOCTYPE/ENTITY XML loop (models/lib/sanitizeUploadedFile.js) - so the
refusal goes, for attachments and avatars.

Serving it inline is made safe in one place, server/lib/svgResponse.js,
used by every route that sends file bytes (universalFileServer.js for
attachments, avatars and legacy files, avatarServer.js,
legacyAttachments.js and httpStream.js): the WHOLE file is sanitized
again before it is sent, whatever is stored, and sent as image/svg+xml
under a Content-Security-Policy with default-src 'none' and sandbox, no
script, X-Frame-Options DENY and nosniff, so even opened directly as a
page it runs nothing. A download is sanitized too; a range request gets
the whole file; an SVG over 8 MB stays a sandboxed download. A file whose
active markup is removed on the way out is logged to Admin Panel /
Problems like the stream sanitizer's, and one removed at upload trips
the existing sanitize.dangerous-content canary.

tests/svgServing.test.cjs runs the real sanitizer on a crafted SVG
(script, onload, javascript:, DOCTYPE/ENTITY, foreignObject/iframe) and
checks the body and headers, the download, the oversized case, and that
no file-serving route in server/routes or httpStream.js sends bytes
without the SVG path, and that nothing else sends image/svg+xml;
tests/playwright/specs/svg-attachment.e2e.js uploads one, checks what is
served, that it shows in the card without running anything, and makes
it the cover and the board background.

Thanks to xet7 !
2026-10-25 06:32:25 +02:00
Lauri Ojansivu 4bea319239 Board Settings / Card calls the Whiteboard row Drawing, in its existing translations.
The row shows or hides the Drawing popup's Open as whiteboard button, and
is now named after the card menu's Drawing it belongs to, with that
entry's translations (cardDrawingPopup-title) and its pencil icon.

Thanks to mimZD and xet7 !
2026-10-25 05:41:29 +02:00
Lauri Ojansivu c11976a2e1 The Drawing popup moves by its title bar or a drag handle, and maximizes.
The card's Drawing popup stayed where it opened. Like the opened card it
is now dragged by its title bar or the drag handle beside the title, and
has the opened card's maximize / minimize button: maximized it fills the
window, and minimizing puts it back where it was dragged or resized to; a
maximized popup is not dragged; the drawing keeps its 800 x 500 shape so
the pen lands under the pointer; a popup left maximized opens maximized
again. The shared popup offers both by name (MOVABLE_POPUPS,
MAXIMIZABLE_POPUPS in popup.js), Board View settings staying movable.

The three new strings, Drag to move, Maximize and Minimize, are in every
language. tests/popupMoveMaximize.test.cjs pins the allow-lists, the
header controls, the restored place, the drawing's shape and the strings
in every locale; tests/playwright/specs/popup-drawing-window.e2e.js drags,
maximizes and minimizes it in a browser.

Thanks to mimZD and xet7 !
2026-10-25 05:40:46 +02:00
Lauri Ojansivu c6dfb95002 CHANGELOG: what FerretDB's changes since v12.64 mean for WeKan, and the SQLite conversion at first start.
The oldest release moves to old-CHANGELOG/2026/10.md to stay under the
size budget.

Thanks to xet7 !
2026-10-25 04:48:27 +02:00
Lauri Ojansivu 2c90b26401 CHANGELOG: the speed test report escapes a backslash before the pipe (CodeQL #593-#595).
Thanks to GitHub CodeQL and xet7 !
2026-10-25 04:29:16 +02:00
Lauri Ojansivu a90576bafa The speed test report escapes a backslash before the pipe in a table cell.
CodeQL js/incomplete-sanitization, alerts #593, #594 and #595:
docs/Databases/FerretDB/1/SpeedTest/report.cjs escaped `|` as `\|` in
Markdown table cells but left a backslash alone, so a value ending in a
backslash turned the added one into a literal backslash and its pipe
split the cell. One mdCell() helper escapes the backslash first, then the
pipe, and turns a line break into a space; it is also used for the
database's name and the query's title, which were not escaped at all.
tests/speedTestReportCells.test.cjs checks the escaping, that no value
can add a column to its row, and that nothing in the tree escapes a pipe
without the backslash. The report is generated from local speed test
results at build time, so there is no attempt to record in Admin Panel /
Problems.

Thanks to GitHub CodeQL and xet7 !
2026-10-25 04:29:05 +02:00
Lauri Ojansivu 4c8531589b CHANGELOG: conformance checks the $or, $elemMatch and long $in shapes.
Thanks to xet7 !
2026-10-25 03:59:43 +02:00
Lauri Ojansivu 18878f6f29 Conformance: $or with $elemMatch, an $or of array fields in an $and, and a long $in.
The shapes FerretDB's PostgreSQL and MySQL backends now answer in the
database - the visible boards and the due cards of the dueCards
publication, and an $in longer than the 32 values MySQL compares with
JSON functions - with negatives that must stay in Go (an operator in an
$elemMatch, a regex branch, a missing field, a long list of numbers).
SQLite, PostgreSQL, MySQL and MariaDB answer all 115 cases as MongoDB 8.

Thanks to xet7 !
2026-10-25 03:59:35 +02:00
Lauri Ojansivu ac8889e61c CHANGELOG: @azure/msal-node 7.1.1 by an npm override, for GHSA-xcx6-jx8p-x3h5.
Thanks to xet7 !
2026-10-25 03:27:26 +03:00
Lauri Ojansivu 31332bfed4 Override @azure/msal-node to 7.1.1 for GHSA-xcx6-jx8p-x3h5.
npm audit reported two high severity issues: @azure/msal-node
>= 5.0.0-beta.0, < 7.1.0 may disclose authorization code flow parameters
to an attacker-controlled endpoint, and @azure/identity depends on it.
It reaches WeKan through @tweedegolf/sab-adapter-azure-blob, whose
@azure/identity (4.14.0, the newest, and 4.15.0-beta.1) asks for
msal-node ^6.0.0, so `npm audit fix --force` reported "up to date" and
changed nothing. package.json overrides it to ^7.1.1. Its only breaking
change since 6 removed responseMode from acquireTokenInteractive, which
@azure/identity never passes; DefaultAzureCredential, ManagedIdentity and
ClientSecretCredential construct, and a token request reaches Microsoft
Entra and fails as an authentication error for a made-up tenant. WeKan's
Azure Blob storage uses DefaultAzureCredential, not the authorization
code flow. The lock file also catches up with the installed
@azure/identity 4.14.0. npm audit: 0 vulnerabilities.
tests/msalNodeAdvisory.test.cjs pins the override and that no locked copy
is in the vulnerable range.

Thanks to xet7 !
2026-10-25 03:27:14 +03:00
Lauri Ojansivu d906733f60 CHANGELOG: notification failures say which check failed; the oldest release moves to old-CHANGELOG/2026/10.md to stay under the size budget.
Thanks to xet7 !
2026-10-25 03:18:51 +03:00
Lauri Ojansivu bf7520f972 Activity notification failures say which check failed.
"Activity notification delivery incomplete; pending intent retained" was
logged for nearly every activity - 1,080 times in one test-server run,
and on a production server one line after another - and never said why:
the .catch() dropped the error. It and the recovery and both cleanup
lines now carry the failure's code (activity-notification-intent-invalid,
sync-busy, ...). Only a code is logged as it is; any other error gives its
name and numeric or short code, never its message, which may carry an
address or a card title. tests/notificationFailureReason.test.cjs pins
that, and fails when a notification failure line in server/ drops its
reason again.

Thanks to xet7 !
2026-10-25 03:18:15 +03:00
Lauri Ojansivu d5395fbb4c CHANGELOG: dependency updates, database test changes and the new speed test results in Upcoming.
Thanks to dependabot and xet7 !
2026-10-25 01:55:14 +03:00
Lauri Ojansivu e81d17da80 Speed test results: SQLite, PostgreSQL, MySQL and MariaDB with FerretDB b6c494e9.
Rerun of the four FerretDB v1 backends at twice the production data,
with the MySQL/MariaDB index reads and VIRTUAL index columns, the
multikey side tables for MySQL/MariaDB, the PostgreSQL GIN membership
index and SQLite sorting in the database. MongoDB 6-9 and FerretDB 2
keep their rows from the same directory. Every database returned the
same data as MongoDB.

All Boards count went from 8-9 s to 159 ms on PostgreSQL and from
9.4 s to 1.1 s on MySQL; adding the 44 indexes took 172 s on MySQL and
901 s on MariaDB instead of 20 and 40 minutes. This build is before
the fixes for SQLite's People page (the database sort read its key
three times per row), MySQL/MariaDB All Boards with a starred board, and
Due Cards' $and on PostgreSQL, MySQL and MariaDB, which a rerun now in
progress measures.

Thanks to xet7 !
2026-10-25 01:53:58 +03:00
Lauri Ojansivu 56292d0a0e Merge remote-tracking branch 'origin/main' 2026-10-25 01:53:15 +03:00
Lauri Ojansivu 494cbee598 Conformance: an equality and an $in on an indexed field inside an $and.
WeKan's due cards query wraps its board ids in an $and, which FerretDB's
PostgreSQL, MySQL and MariaDB backends now push down branch by branch;
both shapes answer as on MongoDB 8 on every backend.

Thanks to xet7 !
2026-10-25 01:47:24 +03:00
Lauri Ojansivu 47dcbb5dd2 Merge pull request #6804 from wekan/dependabot/npm_and_yarn/npm_and_yarn-2f01a27508
Bump the npm_and_yarn group across 1 directory with 2 updates
2026-10-25 01:25:50 +03:00
Lauri Ojansivu 353f01a03a Merge pull request #6803 from wekan/dependabot/github_actions/actions/setup-python-7.1.0
Bump actions/setup-python from 7.0.0 to 7.1.0
2026-10-25 01:25:35 +03:00
dependabot[bot] 164aaa03c9 Bump the npm_and_yarn group across 1 directory with 2 updates
Bumps the npm_and_yarn group with 2 updates in the / directory: [fast-uri](https://github.com/fastify/fast-uri) and [undici](https://github.com/nodejs/undici).


Updates `fast-uri` from 3.1.8 to 3.1.9
- [Release notes](https://github.com/fastify/fast-uri/releases)
- [Commits](https://github.com/fastify/fast-uri/compare/v3.1.8...v3.1.9)

Updates `undici` from 7.30.0 to 7.30.1
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v7.30.0...v7.30.1)

---
updated-dependencies:
- dependency-name: fast-uri
  dependency-version: 3.1.9
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: undici
  dependency-version: 7.30.1
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-24 22:08:48 +00:00
dependabot[bot] e2efc8ebf9 Bump actions/setup-python from 7.0.0 to 7.1.0
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 7.0.0 to 7.1.0.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/5fda3b95a4ea91299a34e894583c3862153e4b97...6d4fd22b20465ccc0f7354476588e8df7c9ca0e0)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-version: 7.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-24 20:33:17 +00:00
Lauri Ojansivu 9d2b64273b db-conformance.sh keeps the containers of a run that is still going.
At start it removed every wekan-conformance-db-* container and network as
left behind by an interrupted run, including those of a run still in
progress: a speed test beside a conformance run lost its MySQL in the
middle of Due Cards ("connection refused") and its MariaDB while
loading, and both results were lost. Containers and networks now carry
the process id of their run as a label, and one whose run is alive (a
db-conformance.sh process) is kept; one without the label, of a run that
ended, or whose pid now belongs to something else is removed as before.
tests/dbConformanceStaleContainers.test.cjs runs the script's functions
against a fake docker.

Thanks to xet7 !
2026-10-24 21:17:45 +03:00
Lauri Ojansivu e9fa734853 Conformance: indexed equality, array membership and mixed-type sorting.
Three cases for what FerretDB now answers from indexes or in the
database: an equality or $in on an indexed field (inside an array, a
value differing only by case, special characters, numbers, a long
string), membership in an indexed array of documents in every shape the
array can take and after writes, and a sort over every simple type with
ties, null and missing fields, with and without a limit, then with an
array and a document among the values. SQLite, PostgreSQL, MySQL and
MariaDB answer all 114 cases as MongoDB 8 does.

Thanks to xet7 !
2026-10-24 21:17:13 +03:00
Lauri Ojansivu b3233e5665 CHANGELOG: the import page guard and the Tigre plural.
Thanks to mimZD and xet7 !
2026-10-24 21:03:36 +03:00
Lauri Ojansivu 52bcd74efa Tigre: the import size message uses the attested plural for files.
import-too-large used the provisional ፈይላት, which
tests/tigreCorpusAttestedPlurals.test.cjs rejects in favour of the
corpus-attested ፋይላት used everywhere else in the Tigre file.

Thanks to xet7 !
2026-10-24 21:03:19 +03:00
Lauri Ojansivu 132fecbfd8 The import page guard knows the checked tick is placed physically now.
tests/importPage.test.cjs still required the checked box's 17px label gap
to be margin-inline-end and no margin-right anywhere, which #6786 changed
on purpose: the tick is placed with left/margin-right so it stays on its
square right-to-left, and a [dir="rtl"] rule moves the label gap to the
left. The guard now pins that, and the unchecked box's logical gap; the
comments in import.css name the tick's physical offsets.

Thanks to mimZD and xet7 !
2026-10-24 21:03:19 +03:00
Lauri Ojansivu ae2bb47d3a CHANGELOG: resizing a list follows the pointer right-to-left (#6801).
Thanks to mimZD and xet7 !
2026-10-24 20:37:07 +03:00
Lauri Ojansivu 80208a7141 Resizing a list follows the pointer in right-to-left languages too.
The resize handle is on the list's inline end, its left edge in a
right-to-left language, but the width was the start width plus the
pointer's movement: dragging the handle out of the list narrowed it,
and dragging it in widened it. The movement is now counted with the
list's own direction, as the board sidebar and the left menus already
do. tests/rtlListResize.test.cjs pins it and requires every width
resizer to flip right-to-left; a Playwright spec drags the handle out
and back in, in both directions.

Thanks to mimZD and xet7 !

Fixes #6801.
2026-10-24 20:36:44 +03:00
Lauri Ojansivu 16b9b14898 Merge remote-tracking branch 'origin/main' 2026-10-24 20:30:17 +03:00
Lauri Ojansivu 6d68e9a6ea CHANGELOG: a checked checkbox sits on its own square right-to-left (#6786).
Thanks to mimZD and xet7 !
2026-10-24 20:13:32 +03:00
Lauri Ojansivu 6bb926af19 A checked checkbox sits on its own square in right-to-left languages too.
#6786's first fix drew the tick with physical borders, so it is a check mark
and not "<" right-to-left. But where it sits was still logical: the tick is a
box narrowed from 13px to 7px and rotated about its bottom-right corner, placed
with inset-inline-start: -3px and margin-inline-end: 6px. A right-to-left page
started the narrowed box at the square's RIGHT edge and pushed it further
right, so the tick sat beside its box - "out of box" in the issue's Admin Panel
screenshots. Measured in Chromium, the right-to-left tick spanned 303-323px
beside a square at 291-308px.

The shared tick, the Admin Panel's, the sidebar's and the Clean themes' are
placed with left and margin-right now: 291-311px, on the square, at the same
place as left-to-right, and 7 + 4 + 6 = 17px keeps the label where it was. The
import page, whose rows add a gap before the label, keeps that gap on the
reading side with a right-to-left rule. Toggle switches and the board list's
corner box, which are not this tick, still mirror.

tests/rtlCheckMark.test.cjs fails if any rule that draws a tick places it with
a logical offset or margin; tests/rtl.test.js allows physical margins and a
left offset only inside checked-state rules; the Playwright spec measures that
the tick is on its square in both directions and the label does not move.

Thanks to mimZD and xet7 !

Fixes #6786.
2026-10-24 20:12:50 +03:00
github-actions[bot] 9a4719b67a Bump versions for v12.64 2026-10-24 16:32:58 +00:00
Lauri Ojansivu 76f8d3d3a6 Prepare v12.64 release 2026-10-24 19:31:47 +03:00
Lauri Ojansivu bd049075e1 CHANGELOG: database speed test, Due Cards, FerretDB 2 compose file.
Thanks to xet7 !
2026-10-24 18:19:21 +03:00
Lauri Ojansivu 70485c38df A database speed test: the common WeKan queries on every database, twice a production WeKan of data.
docs/Databases/FerretDB/1/SpeedTest/speedtest.sh (build.sh and build.bat ->
Tests -> Database speed test) is releases/db-conformance.sh with
WEKAN_DB_SUITE=speedtest: the same FerretDB built from source and the same
databases started the same way - SQLite, PostgreSQL, MySQL and MariaDB through
FerretDB v1, MongoDB 6, 7, 8 and 9, and now FerretDB 2 with PostgreSQL and
DocumentDB.

data.cjs generates, deterministically, twice the users, boards, lists,
swimlanes and cards of a production instance (615,070 cards), skewed the way
real ones are; indexes.cjs creates the indexes WeKan creates, read from its
ensureIndex calls; queries.cjs asks 18 common WeKan queries as WeKan's own code
builds them; report.cjs writes Results.md - a row per database, fastest at the
top, a column per query - and fails when a database answered or stored
anything differently from MongoDB. README.md describes it, what it found, what
was improved and what could still be.

db-conformance.sh: WEKAN_DB_BACKENDS runs only the named databases (to repeat
one into an earlier run's WEKAN_LOGDIR), containers are removed with their
volumes, SQLite's data is under .tools/tmp, and FerretDB 2 is a backend of the
speed test, and of the conformance run with WEKAN_CONFORMANCE_FERRETDB2=1. The
conformance catalogue gains a case of writes by string _id and _ids differing
only by case, which found that FerretDB on MariaDB 12.3 lost them.

tests/dbSpeedTest.test.cjs checks the data, the index list, the queries, the
report's order and that a difference fails it, the wiring and the Due Cards
board scope.

Thanks to xet7 !
2026-10-24 18:18:15 +03:00
Lauri Ojansivu f9e1e90131 The FerretDB 2 compose file starts with the current DocumentDB image.
ghcr.io/ferretdb/postgres-documentdb:17 installs pg_cron, which may only be
created in the database cron.database_name names - postgres. With POSTGRES_DB
ferretdb the DocumentDB extension failed to install and the server never
started. POSTGRES_DB and FerretDB's URL use postgres now.

Thanks to xet7 !
2026-10-24 18:17:14 +03:00
Lauri Ojansivu c64b3a3b1d Due Cards reads only the ids and members of the boards it may show, and asks for a plain boardId list.
The publication fetched every board the user may see - every public board of
the instance among them, whole - to read its _id and members. It asks for those
two fields only. And with no assigned-only board, the usual case, its board
scope is { boardId: { $in } } instead of an $or with an empty second branch,
which MongoDB and FerretDB both answer from the { boardId, archived, type,
dueAt } index. A board where the user is assigned-only still shows only the
user's assigned cards. On the database speed test (615,070 cards) Due Cards on
FerretDB + SQLite went from 34 s to 12.8 s, with FerretDB's own changes.

Thanks to xet7 !
2026-10-24 18:17:14 +03:00
Lauri Ojansivu f1e447dd36 The Sandstorm package-import guard escapes the whole package name in its pattern.
tests/incompleteEscaping.test.cjs refuses a RegExp built from a value that only some metacharacters were escaped in; the guard escaped only '-'.

Thanks to xet7 !
2026-10-24 18:17:14 +03:00
Lauri Ojansivu e76b05b12c CHANGELOG: prebuilt SQLite-only FerretDB on every platform.
Moves the oldest release in CHANGELOG.md to old-CHANGELOG/2026/10.md to keep it under the size budget.

Thanks to xet7 !
2026-10-23 21:23:08 +03:00
Lauri Ojansivu 89da0e5be7 Every platform runs the prebuilt SQLite-only ferretdb-sqlite binary from wekan/FerretDB.
wekan/FerretDB now releases ferretdb-sqlite-<arch> beside ferretdb-<arch>: the
same FerretDB with only the SQLite handler, about 5 MB smaller, and SQLite is
all a WeKan bundle, image, snap or .spk runs. releases/ferretdb-asset.sh is the
one place that chooses: the newest release that has ferretdb-sqlite-<arch>,
and, until one does, the full ferretdb-<arch> of the newest release, which
has the SQLite handler too. It says in the log which one it took and why,
downloads it, verifies it against its .sha256sum, and returns FERRET_ASSET,
FERRET_TAG, FERRET_URL and FERRET_SHA for the provenance table (also through
$GITHUB_ENV, cached per job so the check and embed steps agree).

Every FerretDB download goes through it: the amd64, arm64, win64, win-arm64,
win32, mac-arm64, mac-x64 and extra-architecture jobs of release-all.yml (the
Docker images, snap, AppImage, Flatpak, single-file and macOS/Windows
packages are made from those bundles), repack-bundle-for-arch.sh for
release-all-missing.yml, bsd.yml, check-arch-binaries.sh,
build-release-bundle.sh and test-download-urls.sh. require-binaries.sh checks
the resume-login floor against the release the helper chose. The provenance
rows record that release and URL instead of /latest/.

No release workflow builds FerretDB: sandstorm-src/build-deps.sh takes the
prebuilt ferretdb-sqlite-amd64 by default (FERRETDB_SOURCE=download), and
sandstorm.yml and release-all.yml's build-sandstorm no longer set
FERRETDB_SOURCE=build; building from source stays a local option. The default
docker-compose.yml downloads ferretdb-sqlite-<arch> (FERRETDB_BINARY, falling
back to ferretdb-<arch> for a release without it); the PostgreSQL, MySQL,
MariaDB and SAP HANA compose files keep the full binary.

tests/ferretdbSqliteAsset.test.cjs drives the helper against a fake GitHub
(newest, older and no release with the SQLite-only binary, .exe names, a
pinned tag, FERRETDB_VARIANT=full, checksum match and mismatch, a missing
platform, bad names) and checks that every download site uses it, that no
workflow builds FerretDB and that only the SQLite compose file takes the
SQLite-only binary. The tests that pinned the old URLs follow the helper.

Thanks to xet7 !
2026-10-23 21:22:36 +03:00
Lauri Ojansivu 2da820f8ff CHANGELOG: smaller Sandstorm .spk, collection2 before the models.
Thanks to xet7 !
2026-10-23 19:35:00 +03:00
Lauri Ojansivu e413cd4720 The Sandstorm .spk leaves out every login method but Sandstorm's, and FerretDB's other database handlers.
A grain has one login (Sandstorm's), one database (FerretDB on SQLite) and one
mailbox (the grain's own address), but the .spk carried every login package
WeKan has and a FerretDB with the PostgreSQL, MySQL and SAP HANA handlers.

releases/sandstorm-slim-spk.sh, used by sandstorm.yml, release-all.yml's
build-sandstorm job and build.sh's "Make the .spk package", builds with the
package profile of releases/sandstorm-slim.mjs - accounts-password, 2FA,
passwordless, lockout, OAuth2/OIDC, CAS, LDAP, SAML and the seven social
logins removed from a copy of .meteor/packages for that one build, then
restored - trims the bundle for a linux/x64 grain, verifies none of those
packages got in and packs with spk pack (meteor-spk pack rebuilds and would
undo the trim). sandstorm-src/build-deps.sh builds FerretDB from source with
ferretdb_no_postgresql,ferretdb_no_mysql.

Under Sandstorm the Admin Panel hides the E-mail, Login, Locked users, SAML,
LDAP, OAuth2, CAS, Header login, OAuth providers and Passwordless panes,
Attachments / Database migration, the Email-to-card IMAP and webhook settings,
the New user button and the password fields; a hidden pane opened by URL lands
on the first visible one. POST /users/login and /users/register answer 404
sandstorm-sso-only, database migration is refused and the IMAP/webhook
settings are not stored. Without accounts-password the server creates
Accounts.emailTemplates with the package's defaults and marks useraccounts
initialised, which otherwise stopped the first grain.

Against wekan-12.63-sandstorm.spk, built from the same source: 181,307,364
instead of 185,271,476 bytes (-3.8 MiB, -2.1 %), unpacked 26.7 MiB smaller.
Docker, Snap and the bundles build from the full package list, and a test fails
if that list is slimmed. Tested on a local Sandstorm 0.308: a grain starts, a
board, a list and a card are created, and the removed options are not shown.
tests/sandstormSlim.test.cjs and tests/playwright/specs/sandstorm-slim.e2e.js
pin it. Design: docs/Platforms/FOSS/Container/Sandstorm/Smaller-wekan-spk.md.

Thanks to xet7 !
2026-10-23 19:34:34 +03:00
Lauri Ojansivu 2699f4deea Collection2 is loaded before the models, so every write is cleaned and given its defaults.
Collection2's attachSchema lives in the package's lazy main module, which only
aldeed:schema-index loads, through an asynchronous Collection2.load(). Nothing
waited for it: the models attached their schemas with whichever attachSchema
existed first - collection2's, or the bare shim in imports/collectionHelpers.js,
which stores the schema and never cleans a write. The full build happened to
load collection2 first. A build with fewer packages (the Sandstorm .spk) reached
the models sooner, so a new board's swimlane was stored without archived, sort
and type, and the board opened empty. server/main.js now loads
meteor/aldeed:collection2/main before the models in every build.

Thanks to xet7 !
2026-10-23 19:34:12 +03:00
github-actions[bot] a463937cce Bump versions for v12.63 2026-10-22 17:04:47 +00:00
Lauri Ojansivu e1a75d0311 Prepare v12.63 release 2026-10-22 20:00:47 +03:00
Lauri Ojansivu 819ee786ce Merge remote-tracking branch 'origin/main' 2026-10-22 20:00:13 +03:00
Lauri Ojansivu be0d8d9b9c CHANGELOG: copying or linking a card carries only its new board's custom field values (#6800).
Thanks to Raymond-Voogt and xet7 !
2026-10-22 19:58:32 +03:00
Lauri Ojansivu 63ebdb9072 Copying or linking a card carries only the custom field values of its new board.
A card made from a card template, linked from another board, or copied - and
a subtask copied with it - took its whole customFields array along: values of
another board's fields, of deleted fields, and entries with no field id. The
admin-only field guard could not match those to a field of the new card's
board, refused the whole card and recorded AdminFieldBleed against the user.

copy(), its subtask copies and link() now keep only the values of fields
defined on the destination board (models/lib/customFieldsForBoard.cjs), read
from the collection rather than the cache, just before the insert. An
admin-only value of the destination board is kept, so the guard still decides
it. tests/copyForeignCustomFields.test.cjs fails on the code before the fix,
and fails on any card cloned from another and inserted without the filter.

Thanks to Raymond-Voogt and xet7 !

Fixes #6800,
2026-10-22 19:58:16 +03:00
Lauri Ojansivu 6f9454656e The regex-injection guard knows the database connection-lost pattern.
models/lib/databaseErrors.js builds the connection-lost rule's pattern from
CONNECTION_LOST.source (#6799), a constant regex of the same file, so no
caller value is spliced into it. Listed with that reason, as CARD_URL_RE.source
already is.

Thanks to xet7 !
2026-10-22 19:58:16 +03:00
github-actions[bot] c9827049a1 Bump versions for v12.62 2026-10-22 16:33:46 +00:00
Lauri Ojansivu b43c36890c Prepare v12.62 release 2026-10-22 19:32:34 +03:00
Lauri Ojansivu f4c67c6df9 CHANGELOG: refused edits no longer disable accounts (#6798), database pod restart is not a crash (#6799).
Thanks to Raymond-Voogt and xet7 !
2026-10-22 19:31:08 +03:00
Lauri Ojansivu f37204b1c3 A MongoDB pod restarting is a lost connection, not a critical crash.
In Kubernetes a restarting MongoDB pod's DNS name does not resolve for a
few seconds, and the driver throws "getaddrinfo ENOTFOUND
wekan-mongodb-0...". No rule in models/lib/databaseErrors.js knew it, so
it was not transient, and server/lib/uptimeWatch.js recorded every
unhandled rejection as a critical CrashBleed although 00processErrors.js
kept WeKan running: nine "crashes" while WeKan reconnected by itself.

connection-lost now matches getaddrinfo ENOTFOUND and EAI_AGAIN, but only
for the DATABASE's own host - from the caller, or from MONGO_URL and
MONGO_OPLOG_URL (every host of a replica set, mongodb+srv too) - so a
webhook or mail host that does not resolve is still not a database
problem. The crash recorder skips what isTransientDatabaseError already
handles, for both uncaught exceptions and unhandled rejections.

tests/databaseDnsRestart.test.cjs parses connection strings, runs both
guards' decision on the database host and on other hosts, and runs the
crash recorder's own listeners: a database coming back records nothing,
a real crash and a foreign DNS failure still do. It fails on the code
before this commit.

Thanks to Raymond-Voogt and xet7 !

Fixes #6799,
2026-10-22 19:30:41 +03:00
Lauri Ojansivu 425b4ddea7 An ordinary refused edit on one's own board no longer disables the account.
Accounts were disabled for members working on their own board; one
instance lost four in three days, each user then refused at login 81
times.

- A Worker may move a card (#3189) and tick a checklist item (#3307),
  but canUpdateCard and the checklist item rule asked
  canEditCardOrLinkedCard first, which recorded authz.linked-write (high,
  blocked) before the Worker carve-out allowed the change, and that
  record disables the account. Both now ask without recording first, and
  record only a change that is still refused after the carve-outs.
- A read-only or comment-only member whose screen still offered an edit
  (a role changed while the board was open) was refused with the same
  record. authz.linked-write keeps being recorded and the write keeps
  being refused, but it no longer disables the account (blocksAccount:
  false, as ssrf.webhook since #6782).

tests/workerNoSelfBlock.test.cjs runs canUpdateCard and the checklist
item rule from their own source: a Worker's move and tick are allowed
with nothing recorded, other refused edits are still recorded, the
catalog entry never blocks through blockOnSecurityEvent's own decision,
and no rule with a Worker carve-out records before it. It fails on the
code before this commit. boardRoleViews follows the two-step check.

Thanks to Raymond-Voogt and xet7 !

Fixes #6798,
2026-10-22 19:28:49 +03:00
Lauri Ojansivu 368b90d650 Merge remote-tracking branch 'origin/main' 2026-10-22 19:26:24 +03:00
Lauri Ojansivu e417720f8a CHANGELOG: decluttered card and Card Settings (#6796), list move history checksum (#6797).
Thanks to mueschel, Raymond-Voogt and xet7 !
2026-10-22 19:15:51 +03:00
Lauri Ojansivu 76f9d41bf4 A list move with no swimlane no longer fails its own history checksum.
Admin Panel / Problems / Security showed HistoryIntegrity (critical)
81,833 times. A list bound to no one swimlane has swimlaneId '', and the
legacy history append hashed the row with '' and then inserted it
through collection2's default clean, which removes empty strings, after
which the schema's defaultValue stored null: every such list move failed
its own checksum at every audit and was reported as tampering. The
stored-chain path already inserted with { removeEmptyStrings: false,
trimStrings: false }; the legacy path now does too.

Rows already stored that way verify: a null where '' was hashed is
accepted in the id and label fields (boardId, swimlaneId, listId, cardId,
group, batchId, restoredFromId, restoredByUserId) and nowhere else, so a
value replaced by null, a different value, or any change to content, user
or entity still fails.

tests/historyEmptyStringChecksum.test.cjs appends through a collection
that cleans like collection2, verifies already-cleaned rows, fails every
real change, and fails on any ChangeHistory insert in the tree that would
clean or trim; it fails on the code before this commit.

Thanks to Raymond-Voogt and xet7 !

Fixes #6797,
2026-10-22 19:15:36 +03:00
Lauri Ojansivu 2c520ef382 Declutter the opened card, attachments too, and fit Card Settings on the screen.
#6796: an opened card gave most of its height to controls with nothing in
them. Stickers, Location, Dependencies and List already have Show on Card
rows, and Date Format is in Member Settings; what was left:

- A field's add button sits on its title's line (Labels, Stickers,
  Location, the dates, Members, Assignee, Creator, Requested and Assigned
  By, Dependencies); what a field holds follows and wraps. Requested and
  Assigned By put their two buttons side by side.
- Checklists: the controls under the list share a line, and the bottom
  "+" is drawn once there is a checklist; before that it is the title
  line's.
- Attachments: the upload rules ("If filename is invalid, ...", size,
  types) are in the upload popup, not body text above every list. "+" is
  a small button. A file with nothing to preview gets a small file button
  that still opens the preview, instead of a 144x96 box with "txt" in it,
  and the name has a line of its own, so a long one is not hidden behind
  the buttons.
- Two new Board Settings / Card / Show on Card rows, Attachments: Preview
  and Attachments: Details (uploader and date), both on by default, built
  from existing translations.

Board Settings / Card is a centred panel as wide (up to 1800px) and tall
as the window, and each list spreads into as many 10em columns as fit,
read top to bottom: on 1920x1080 nothing is scrolled, on 1366x768 32px.

Its rows are dragged by the handle, the icon or the label when drag
handles are on, and by the icon or the label, with no handle drawn, when
they are off. With handles on only the handle dragged, and the rows were
locked to vertical moves, which the columns made unusable.

tests/declutterCard6796.test.cjs runs the tile decision and the popup
geometry, and checks the gates, the CSS and the drag wiring;
tests/playwright/specs/declutter-card-6796.e2e.js checks the add buttons
on their title's line, the compact attachment, the rules in the upload
popup, Attachments: Details, the new rows saving, no scroll at 1920x1080,
and a drag by label and handle with handles on and by label with them
off. cardSettingsCoverage and narrowWindowLayout follow the new rules.

Thanks to mueschel and xet7 !

Fixes #6796,
2026-10-22 19:15:36 +03:00
github-actions[bot] 3c88e5b13f Bump versions for v12.61 2026-10-22 12:37:41 +00:00
Lauri Ojansivu 8dbc79b89c Prepare v12.61 release 2026-10-22 15:36:25 +03:00
Lauri Ojansivu faa66aecc4 listSortIndexes test escapes every regular expression character of a name.
It escaped only . and $ when building a pattern from an identifier, which
tests/incompleteEscaping.test.cjs reports anywhere in the tree.

Thanks to xet7 !
2026-10-22 15:32:56 +03:00
Lauri Ojansivu 9273d3eb13 CHANGELOG: list sort indexes and wrapped connection errors in Admin Panel / Problems (#6794, #6795).
Thanks to Raymond-Voogt and xet7 !
2026-10-22 15:18:38 +03:00
Lauri Ojansivu a9e680d4b2 A lost database connection inside a validation error is a connection problem.
While MongoDB restarted, Admin Panel / Problems showed
"Exception while invoking method 'usersHeartbeat' ValidationError:
Failed validation Socket 'connect' timed out" as a schema bug, telling
the admin to look for an empty required field and report a WeKan bug.
collection2 wraps whatever failed during a write's validation in
"ValidationError: Failed validation", an unreachable database included;
the schema rule came first, and the driver's connect timeout matched no
rule at all. The same classification decides in 00startupResilience.js
and 00processErrors.js whether an error is transient, so it was fatal at
startup too.

schema-validation now yields when the same message carries a lost
connection, a timeout, a lock or too many connections. connection-lost
also knows the MongoDB driver's own errors: Socket '...' timed out,
MongoNetworkTimeoutError, MongoNetworkError, Server selection timed out,
MongoServerSelectionError, PoolClearedError, NotWritablePrimary and
"not primary" (not SQL's "not primary key").

tests/databaseErrorsWrappedCause.test.cjs runs both guards'
isTransientDatabaseError from their own source on the reported error,
puts every transient cause behind three wrappers and requires its own
rule each time, and keeps a real schema failure a fatal schema failure.

Thanks to Raymond-Voogt and xet7 !

Fixes #6795,
2026-10-22 15:18:30 +03:00
Lauri Ojansivu 058a2dcbce List sort indexes are made from key objects, not from "-modifiedAt" strings.
Admin Panel / Problems showed "Created the missing index "-modifiedAt" on
lists" at every start, from every pod. server/models/users.js passed the
list sort options ('-modifiedAt', 'title', ...) to ensureIndex as
strings: the driver read each as a field name, so lists got indexes on
fields named "-modifiedAt", "-title" and "-sort" (on no document, kept up
to date on every list write), and ensureIndex compared a string's JSON
with an index key object's, which never matched, so all six were
re-created and reported every time.

models/lib/indexKeys.cjs turns the sort options into one single-field
index per field ({ modifiedAt: 1 }, { title: 1 }, { sort: 1 }; one serves
both directions) and finds the stray single-field "-x" indexes, which
startup now drops; a compound index containing such a field is left
alone. ensureIndex turns a string key into the key it means, with a
warning, instead of indexing a field named after it.

tests/listSortIndexes.test.cjs uses the reporter's real index list, runs
ensureIndex itself against a fake collection (a string key creates the
real index once and reports it once), and fails on any ensureIndex call
in the tree whose key is not an index key object.

Thanks to Raymond-Voogt and xet7 !

Fixes #6794,
2026-10-22 15:16:50 +03:00
Lauri Ojansivu 660846dd3c Merge remote-tracking branch 'origin/main' 2026-10-22 15:06:31 +03:00
Lauri Ojansivu e1f8d93d70 CHANGELOG: newest npm dependencies, Blockly 13.4 toolbox search in every language, core26 snap Caddy, ca@valencia.
Thanks to translators and xet7 !
2026-10-22 06:37:58 +03:00
Lauri Ojansivu 6a37160bc8 A saved language with '@' resolves: ca@valencia shows Valencian, not English.
The Valencian file is ca@valencia.i18n.json, but its registry tag is
ca-valencia, and resolveTag and the browser-language matcher only treated
'_' as '-'. A profile saved as the file's name - which is what
tests/playwright/specs/rules-blocks.e2e.js does - resolved to nothing and
the page stayed in English; that case failed on the commit before the
npm updates too. Both now compare through one normalizeLanguageTag
('_' and '@' as '-'), exported from imports/i18n/browserLanguage.js.
tests/browserLanguageFallback.test.cjs uses that function rather than its
own copy of the old rule, checks ca@valencia, and requires every locale
FILE name to resolve to a registry language.

Thanks to xet7 !
2026-10-22 06:34:36 +03:00
Lauri Ojansivu 189adaee52 Update imapflow, mailparser, pdfjs-dist, isomorphic-git and pretty-ms, with runtime tests.
imapflow 2.1.2 -> 2.8.0 and mailparser 3.9.31 -> 3.12.2 (email to card):
bug fixes, and mailparser no longer turns a decoded display name into an
address. pdfjs-dist 6.2.108 -> 6.4.299 (attachment text search),
isomorphic-git 1.42.4 -> 1.44.3 (GitHub wiki import), pretty-ms 9 -> 10
(upload time estimate; its breaking changes are in colonNotation, which
WeKan does not use, and the output it shows is unchanged).

Each now has a test that RUNS it the way WeKan calls it, so the next
update that moves an API fails in seconds:
- imapflowMailparserRuntime: the poller's connect, mailbox lock, unseen
  search, fetchOne, simpleParser and \Seen flag against
  server/lib/tests/fakeImapServer.js; a wrong password is refused, and an
  encoded address in a display name stays a name.
- pdfjsTextRuntime: documentGif.js's legacy build, worker, fonts, CMaps
  and getTextContent on a PDF with known text; a non-PDF is refused.
- isomorphicGitRuntime: githubWiki.js's log, recursive readTree and
  readBlob on a two-commit repository; a missing object is an error.

npm audit reports nothing. The e2e run against a build with these:
rules-blocks (147 languages), attachments, both email specs, and the
security specs pass, except two rules-blocks cases that fail the same way
on a build of the commit before this one.

Thanks to developers of dependencies and xet7 !
2026-10-22 06:34:36 +03:00
Lauri Ojansivu 8a00898252 Update the small npm dependencies, and translate Blockly 13.4's toolbox search.
Within their ranges or pins: @aws-sdk/client-s3 and lib-storage 3.1155.0,
@fastify/busboy 3.2.4, the three @tweedegolf/sab-adapter-* 3.0.5/3.0.3,
blockly 13.4.0, dompurify 3.4.17, file-type 22.2.0, filesize 11.0.28,
i18next 26.5.4, meteor-node-stubs 1.2.32 (pbkdf2 is still 3.1.7 after
the postinstall patch), wasmagic 1.0.12, and for development
@rspack/core 2.2.9, @typescript-eslint/* 8.72.0 and puppeteer 25.15.0.
The lockfile is written by npm 12.2.1, the Dockerfile's. npm audit
reports nothing.

Blockly 13.4 adds a search box to the toolbox, with five new messages.
releases/translations/import-blockly.cjs added them to every locale, and
they are translated directly into all 230 locale files, from each file's
own words for "block" and "search"; %1 is kept in every count. The
importer also replaced Telugu's literal blockly-LOGIC_NULL "null" with a
word meaning "invalid"; it is "null" again, as
tests/vietnameseBulgarianCompletion.test.cjs requires. Corrected while
there: Polish "Szukaj blokad" (search for blockades) from Blockly's own
catalog is "Szukaj bloków", Arabic's singular count drops a stray "(1)",
and Odia's "no-results" had English mixed into it.

Low confidence, for a speaker to correct: ace, ak, an, ary, ay, bi, bm,
bo, bua, chr, co, csb, cv, dz, ee, ff, fj, gn, gv, ha, haw, hsb, ig, iu,
kl, kok, ks, kw, la, lg, lld, mg, mi, nah, nap, nd, nso, ny, om, pap, qu,
rn, rup, rw, sah, sc, se, sm, sn, so, ss, st, szl, tig, tlh, to, tpi,
ts, uz-AR, ve, ve-CC (Venetian), ve-PP (Veps), vo, wa, wa-RR (Waray),
wal, wo, xh, zgh and zu.

All 1988 node suites pass except tigreCorpusAttestedPlurals, which
fails on the commit before this one too.

Thanks to developers of dependencies, translators and xet7 !
2026-10-22 06:01:28 +03:00
Lauri Ojansivu 92001bcfd9 vmactions/openbsd-vm 1.4.8 -> 1.4.9, and actions/setup-node v7.1.0 everywhere.
openbsd-vm 1.4.9 adds OpenBSD 8.0 as the VM's default release and fixes a
non-root user's writes to the NFS share; the BSD bundles keep release 7.8
from releases/bsd/targets.json, so they are still built for 7.8. The three
setup-node@v6 steps in the disabled Playwright workflow are pinned to
v7.1.0 by commit, like the fifteen in the active workflows.

Thanks to xet7 !
2026-10-22 05:23:36 +03:00
Lauri Ojansivu 2ad5999bc7 Snap Caddy: core26 gets the fixed part, and both fall back to 2.11.7.
snapcraft-core26.yaml still had the caddy part snapcraft.yaml had before
v10.71: the version from api.github.com, which answers 403 to a busy CI
runner and, under snapcraft's pipefail, ended the part before its 2.8.4
fallback; a plain curl with no retry and no fallback for a CPU the newest
Caddy does not publish; and a default Caddyfile that proxied to 3000,
where WeKan does not listen when Caddy is on (it listens on
CADDY_BIND_PORT, 3001). Both snaps build from snap-src, so core26 now has
snapcraft.yaml's part unchanged, and both pin 2.11.7 as the fallback.

tests/releaseSnapArches.test.cjs read snapcraft.yaml only, which is how
the core26 copy was missed; it now checks every snapcraft*.yaml, and the
default Caddyfile's port. With the old core26 file it fails.

Thanks to xet7 !
2026-10-22 05:23:36 +03:00
github-actions[bot] 770acdcee6 Bump versions for v12.60 2026-10-22 02:01:02 +00:00
Lauri Ojansivu d30f1dffea Prepare v12.60 release 2026-10-22 04:59:51 +03:00
Lauri Ojansivu cf55c52c64 CHANGELOG: failed imports and exports block only attempts, crypto keys for dynamic-import and meteor shell (CodeQL 591).
Thanks to GitHub CodeQL and xet7 !
2026-10-22 04:57:17 +03:00
Lauri Ojansivu 6efd39a70e dynamic-import and meteor shell keys come from the crypto RNG.
GitHub CodeQL alert 591 (js/insecure-randomness, CWE-338), in the
dynamic-import fork: the key that lets a request read the SERVER's
dynamic modules (?key= on /__meteor__/dynamic-import/fetch) came from
Math.random(), whose xorshift128+ state can be recovered from earlier
outputs, and was compared with ===. meteor shell's connection key, which
runs code in the server process, was made and compared the same way.
Meteor 3.5.3 and 3.6-rc.0 both have this.

Both forks now use crypto.randomBytes (160 and 128 bits) and compare with
crypto.timingSafeEqual. tests/meteorForkRandomKeys.test.cjs runs both, and
fails on any Math.random() in the forks and on a key, secret, token,
nonce, salt, password or code made from Math.random() anywhere in the
tree. A wrong key is not recorded in Admin Panel / Problems: another
WeKan instance behind the same load balancer legitimately sends its own.

Thanks to GitHub CodeQL and xet7 !
2026-10-22 04:56:47 +03:00
Lauri Ojansivu 567f99227b A failed import or export disables an account only when it was an attempt.
secureTransfer recorded every throw as a high "blocked" event naming the
user, and that disables the account. sanitize-html 2.18.0 threw
RangeError on nested <iframe>, so a card planted with the payload locked
out the next member who exported the board; any library fault would do
the same. The transfer boundary now marks its own refusals
(TransferBoundaryError: a cycle, a non-plain object, a size limit), and
models/lib/transferFailure.cjs records only those as blocked - high, and
medium for a size limit as before. Anything else is ImportExportFailure:
detected, still naming whose import or export it was, and never blocking
an account.

tests/transferFailure.test.cjs drives real boundary refusals and a
sanitizer that throws through blockOnSecurityEvent's own decision; with
the old secureTransfer it fails. It also fails if the boundary throws a
plain Error or anything but secureTransfer calls it.

Thanks to xet7 !
2026-10-22 04:56:40 +03:00
Lauri Ojansivu 42965e133b Merge remote-tracking branch 'origin/main' 2026-10-22 04:49:09 +03:00
Lauri Ojansivu 182b6072d0 CHANGELOG: sanitize-html 2.18.1, nested iframes no longer crash imports and exports.
Thanks to xet7 !
2026-10-22 04:33:30 +03:00
Lauri Ojansivu d89bcab8e0 Tests: nested iframes no longer crash the import/export sanitizer.
sanitize-html 2.18.0 re-sanitized the fallback of a discarded <iframe>
with no recursion limit (GHSA-c9rx-x947-fp3h, CWE-674). WeKan's server
sanitizer discards every tag, so a few kilobytes of nested <iframe> threw
RangeError in secureTransfer, on every import and export - and
secureTransfer records a throw as a high blocked event naming the user,
which disables that account. A card planted with the payload locked out
the next member who exported the board. 96ed9ad202 moved to 2.18.1.

tests/sanitizeHtmlNestedIframe.test.cjs runs WeKan's own sanitizeInput
options on three nested-iframe payloads, checks the installed and locked
versions, and fails if anything else imports sanitize-html; with 2.18.0
it fails with RangeError. The Playwright spec exports and imports a board
holding the payload and checks that nobody is locked out.

Thanks to xet7 !
2026-10-22 04:33:04 +03:00
Lauri Ojansivu 96ed9ad202 Updated sanitize-html.
Thanks to developers of dependencies !
2026-10-22 04:29:10 +03:00
github-actions[bot] b5cccd7584 Bump versions for v12.59 2026-10-22 01:18:15 +00:00
Lauri Ojansivu e5d186c2f0 Prepare v12.59 release 2026-10-22 04:17:02 +03:00
Lauri Ojansivu 1624f44ffd CHANGELOG: Meteor 3.5.3's six security fixes on Meteor 3.6-rc.0.
Thanks to Meteor developers, Vext Labs and xet7 !
2026-10-22 04:12:43 +03:00
Lauri Ojansivu e0189bc53b Meteor 3.5.3's six security fixes on Meteor 3.6-rc.0.
Meteor published the same six fixes as 3.3.3, 3.4.2 and 3.5.3 on
2026-10-16. 3.6-rc.0, which WeKan runs, does not have them: its tag is
behind 3.5.3 by exactly the 15 security commits, and neither it nor
Meteor's devel branch has the fixed code. Going back to 3.5.3 would also
mean Node.js 24 and Rspack 1, so WeKan carries the fixes in packages/,
where a package overrides the release's own: 3.6-rc.0's allow-deny,
ddp-server, dynamic-import, facebook-oauth, google-oauth and shell-server
with Meteor's 3.5.3 patch applied unchanged, each marked with a _1 wrap
number. ddp-server leaves out uWebSockets.js, as Meteor's published
package does; it is only for the opt-in uws transport and came from a git
URL.

- DDP: a message named __proto__, toString or not a string, and an async
  handler that rejected, crashed the server.
- allow/deny: a top-level __proto__ in an update was missing from the field
  list the validators saw.
- dynamic import(): requests are bounded in size (413) and complexity
  (400).
- Google and Facebook direct-token login: a token issued to another
  application was accepted; identity now comes from the provider, and a
  Facebook verification error no longer carries the app secret.
- meteor shell: its history and compile cache are private.
- Deploy discovery is in meteor-tool, which an app cannot replace; WeKan is
  never deployed with meteor deploy, and a test keeps it so.

Refused attempts are in Admin Panel / Problems as Meteor DDP, Meteor
allow-deny, Meteor dynamic-import and Meteor OAuth, through a hook the
packages call (server/lib/meteorSecurityHook.js); none blocks an account.
An oversized import() body, an unknown ordinary DDP name and a network
failure are refused without a row, because ordinary use can reach them.

tests/meteor353SecurityFixes.test.cjs runs each fix's code cut out of the
fork and searches the tree for the shape of each fault; all of them fail on
the unfixed 3.6-rc.0 packages. It also fails when .meteor/release moves
on, because a fork would silently keep 3.6-rc.0's code.
tests/playwright/specs/meteor-353-security.e2e.js sends each attack to a
running WeKan built with the forks. The forked files are recorded in the
source audit baseline, and the suites that check WeKan's own methods and
subscriptions skip Meteor's package tests.

Thanks to Meteor developers, Vext Labs and xet7 !
2026-10-22 04:12:13 +03:00
Lauri Ojansivu 8a21e5639b CHANGELOG: every workflow's jobs that run after a cancel end on their own.
Thanks to xet7 !
2026-10-22 03:07:20 +03:00
Lauri Ojansivu 94e1749977 Every workflow's jobs that run after a cancel end on their own.
Cancel workflow does not cancel a job whose if: is still true, and always()
is true in a cancelled run. tests/releaseCancelSafeJobs.test.cjs checked
only release-all.yml; it now fails on a job on always() in any workflow that
is a reusable-workflow call or has no timeout of 60 minutes or less. Every
such job today (release-notes, done, and the AppImage, Flatpak and Mac
summaries) is bounded. node-patches had eleven five-hour resume stages
called this way, and Cancel workflow did not stop them.

Thanks to xet7 !
2026-10-22 03:07:12 +03:00
Lauri Ojansivu be0da24e21 Merge remote-tracking branch 'origin/main' 2026-10-22 02:50:27 +03:00
Lauri Ojansivu 6e1341a2a8 CHANGELOG: release recovery waits out GitHub API errors.
Thanks to xet7 !
2026-10-22 02:39:33 +03:00
Lauri Ojansivu e304e1948a Release recovery waits out GitHub API errors instead of dying on one.
The Recover job of "Release All missing" died after one HTTP 502 from the
GitHub API while `gh run watch --exit-status` listed the rerun's jobs, so
plan and every build after it were skipped. releases/retry-failed-release.py
now polls `gh run view`, retries up to ten API errors in a row, waits for
the new attempt rather than reading the old attempt's failure, and stops
only on the rerun's own conclusion. tests/retryFailedRelease.test.py pins
the 502 case, a failed rerun, a lasting outage and the stale attempt, and
that no `gh run watch` is left.

Thanks to xet7 !
2026-10-22 02:39:18 +03:00
github-actions[bot] 70ede2148a Bump versions for v12.58 2026-10-21 15:29:38 +00:00
Lauri Ojansivu aa26a6adaa Prepare v12.58 release 2026-10-21 18:28:16 +03:00
Lauri Ojansivu 914d95816a CHANGELOG: board to template, large Jira imports, comments follow a moved card, CodeQL alert 590.
Thanks to fabiosalles, GitHub CodeQL and xet7 !
2026-10-21 15:31:40 +03:00
Lauri Ojansivu 696b5b073f Comments, reactions, text notes and attachments follow a card moved to another board.
The board publication finds a board's comments, comment reactions, text
notes, checklists, checklist items and attachments by the board id each
row carries (#6480), but a card that changed board re-homed only its
checklists and checklist items. Its comments stayed published to the
board it left, and came back when the card was moved back. Attachments
followed only when Card.move() ran on the server, and the REST card
move, a hook-less write, re-homed nothing at all.

One list (models/lib/cardChildrenBoard.js) now names every such
collection, and one helper (server/lib/cardChildrenBoardSync.js)
re-homes them from the Cards.after.update hook, the REST move and the
durable rule move. The startup schema upgrade step card-children-boardid
moves rows that a move already left behind to their card's board.

Thanks to fabiosalles and xet7 !

Fixes #6793.
2026-10-21 15:30:41 +03:00
Lauri Ojansivu 14a43da557 Large Jira exports import, and an existing board can be saved as a board template.
From IRC: a Jira import of 25 issues worked and one of 858 failed. The
import boundary refused any document over 250,000 values, and a Jira
search export carries some 250 per issue even without comments - mostly
null custom fields, avatar URLs and status categories - so it stopped at
under 1,000 issues, about 6 MB. The refusal was a plain Error, which the
import page showed as "Internal server error". The cap is now four
million values; a file over it is refused as import-too-large, which the
page translates, with the limit as the reason. The import deadline was a
flat two minutes whatever the file held; it is now WEKAN_IMPORT_TIMEOUT_MS
plus half a second per record (models/lib/importDeadline.js), on the
server and in the import page's watchdog alike. The Jira creator also
collected the file's issue keys once per issue, which was quadratic.

"Is there a way to convert an existing board into a template?" There was
not: a board template could only be built inside the Templates board.
Board menu / Save as Template (saveBoardAsTemplate) copies the board as a
template board, private with the creator as its one member, and adds the
linked card in the user's Templates / Board Templates swimlane that makes
it a template. Board admins only, as Copy Board, and not offered on a
board that already is a template.

The new import-too-large string is translated into every language; the
low-confidence ones are ace, ak, an, ay, bm, bo, bua, chr, csb, cv, dz, ee,
ff, fj, fo, fur, gn, gv, haw, hsb, iu, kl, kok, ks, kw, lg, lld, nah, nap,
nd, rm, rup, sah, sc, scn, se, ss, tig, tlh, to, ve, ve-PP, ve-CC, vo, wa,
wa-RR, wal, wo, wuu-Hans and zgh.

Thanks to xet7 !

Fixes #6792.
2026-10-21 15:30:32 +03:00
Lauri Ojansivu e18794fa46 Invite-by-email e2e escapes the address completely, and a partial character class escape is found anywhere.
CodeQL alert 590 (js/incomplete-sanitization): the #6788 e2e put the
stored address into a $regex with only "." escaped, so a backslash in the
address could cancel the escape. It now uses escape-string-regexp, the
same module the server matches addresses with.

tests/incompleteEscaping.test.cjs only knew single-character patterns
and so missed `.replace(/[.]/g, '\\.')`. It now also reports a character
class without a backslash replaced by a backslash escape, and found this
line before it was fixed.

Thanks to GitHub CodeQL and xet7 !
2026-10-21 14:59:55 +03:00
Lauri Ojansivu 55f1ef7852 Merge pull request #6791 from wekan/dependabot/npm_and_yarn/npm_and_yarn-04be66158d
Bump markdown-it from 15.0.2 to 15.0.3 in the npm_and_yarn group across 1 directory
2026-10-21 14:46:25 +03:00
dependabot[bot] c927b901e8 Bump markdown-it
Bumps the npm_and_yarn group with 1 update in the / directory: [markdown-it](https://github.com/markdown-it/markdown-it).


Updates `markdown-it` from 15.0.2 to 15.0.3
- [Changelog](https://github.com/markdown-it/markdown-it/blob/master/CHANGELOG.md)
- [Commits](https://github.com/markdown-it/markdown-it/compare/15.0.2...15.0.3)

---
updated-dependencies:
- dependency-name: markdown-it
  dependency-version: 15.0.3
  dependency-type: direct:production
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-21 01:15:42 +00:00
github-actions[bot] 647cbd111f Bump versions for v12.57 2026-10-20 15:04:19 +00:00
Lauri Ojansivu 09db4b64fa Prepare v12.57 release 2026-10-20 18:03:05 +03:00
Lauri Ojansivu 4d8df83fdd Admin Panel validation audit lists the Retention pane.
tests/adminPanelDocsMatchMenu.test.cjs requires docs/Features/Admin-Panel/Validation.md
to inventory every live pane; Attachments / Retention (#6789) was missing
from it. It also crashed on the commit before this one, whose test run was
read by its last line only.

Thanks to xet7 !
2026-10-20 14:05:36 +03:00
Lauri Ojansivu 5c2fc294f3 Tests and docs follow this round's changes: Retention documented, a real regex escape, the snap guards.
The full node suite found three breakages in this round's own work:
- adminPanelDocsMatchMenu: Admin Panel / Attachments / Retention was in the
  product and not in docs/Features/Admin-Panel/Attachments/README.md. It has
  its section and its row in the URL table now.
- incompleteEscaping: the test from the #6788 patch hand-wrote a regex escape.
  It now uses the same escape-string-regexp package the server imports.
- releaseSnapArches: two guards read the Launchpad step's text. infra_failure()
  (the v12.56 Launchpad-outage retries) also reads snapcraft-wekan-*.txt
  before the loop, and the fixed `sleep 60` became `sleep "$pause"`. The
  guards now look for the log printing after the no-snap message, and for the
  pause variable, and say why. These two had been failing since that change.
  An earlier run's report that this suite was only "load-related" was wrong:
  the FAIL lines had been filtered out of the output that was read.

Thanks to xet7 !
2026-10-20 14:05:13 +03:00
Lauri Ojansivu 4b95f7f2cc CHANGELOG: attachment retention, backup status, settings on every platform and in the Admin Panel, member invite by email.
Thanks to Raymond-Voogt and xet7 !
2026-10-20 13:50:44 +03:00
Lauri Ojansivu 718fdb5dac Newest features' settings: on every release platform and in the Admin Panel, and kept that way.
An audit of every environment variable the code reads found 30 that are new
since v12.00. Eleven were already Admin Panel settings and on every platform
the release workflows build. Of the rest:

Now Admin Panel settings, through models/lib/authConfigCatalog.js (a value
there wins, an empty field falls back to the variable):
- Email to card limits, beside its own Admin Panel settings:
  INBOUND_IMAP_POLL_SECONDS, INBOUND_EMAIL_PER_ADDRESS_PER_HOUR,
  INBOUND_EMAIL_MAX_BYTES, INBOUND_EMAIL_MAX_ATTACHMENT_BYTES and
  EMAIL_VIEW_SENDS_PER_HOUR. They were module-level constants, read before
  the Admin Panel values loaded; they are read when used now.
  createRateLimiter takes a limit function, and a changed poll interval
  restarts the IMAP poller.
- ACCOUNTS_COOKIE_REFRESH_RATE_LIMIT in People / Login, like the login
  expiry beside it, applied from the next start.
- Windows SSO (WINDOWS_SSO_ENABLED, _DOMAINS, _ACTIVE_DIRECTORY,
  _MERGE_EXISTING_USERS) in People / Login. It was already read through
  authEnv but had no form. Being Windows-only, its catalog section names
  start-wekan.bat as its one platform.

Listed on every platform (Dockerfile, the docker-compose files, start-wekan.sh
and .bat, the snap config and help), where none of these had been: the
variables above, INBOUND_EMAIL_WEBHOOK_SECRET (its own Admin Panel page
already has it), and the per-process switches that stay environment only -
INBOUND_IMAP_DISABLED (one pod of a multi-pod deployment), WEKAN_DISK_MONITOR,
WEKAN_DISK_WATCH_PATHS, WEKAN_RAM_MONITOR and WEKAN_IMPORT_RUN_STALE_MS.
Left unlisted on purpose: NEXTCLOUD_EXAPP, NEXTCLOUD_URL and
WEKAN_NEXTCLOUD_PROXY_KEY (set by Nextcloud's AppAPI), SANDSTORM_MAILDIR (set
by Sandstorm). DEFAULT_AVATAR_URL keeps no Admin Panel UI, as decided in #824.

tests/envVarCoverage.test.cjs keeps it so. It fails for a variable that is in
no Admin Panel catalog, OWN_PANE or ENV_ONLY with its reason; the variables
read before v12.00 are a frozen LEGACY list that can only shrink. An
environment-only or own-page setting a user sets must be on every platform,
and stale entries fail. tests/authConfigCatalog.test.cjs honours a section's
platform list. tests/adminPanelNewSettings.test.cjs pins the reads and the
forms. tests/cookieRefreshRateLimit.test.cjs's guard now expects the Admin
Panel value, and says why. tests/playwright/specs/admin-new-env-settings.e2e.js
opens the forms.

Thanks to xet7 !
2026-10-20 13:50:10 +03:00
Lauri Ojansivu 06b7ce994a Backup status: every run recorded, failures and overdue backups shown, old local backups pruned - in Admin Panel too.
The result of an Admin Panel backup lived only in the memory of the server
process that ran it. After a restart, or on another pod of a multi-pod
deployment, the Backup pane said "idle", so nobody could see whether last
night's backup had worked. There was no history, no warning when a scheduled
backup failed or did not run, and local backups were never cleaned up.

- Every run, scheduled or "Backup now", is recorded in backupRuns: start and
  end, who or the schedule, scope, storage, status, size, duration and error.
  Records are kept 400 days. A run still "running" after 24 hours counts as
  failed, and a scheduled run skipped for another backup is recorded as failed.
- Admin Panel / Problems / Backup shows the last success (overdue when its
  period has passed), the last failure with its error, the next run and the
  last 30 runs. The same status is above the schedule in Attachments / Backup.
- Problems / Summary lists "Backup failed" and "Backup overdue". /metrics adds
  wekan_backup_last_success_timestamp_seconds,
  wekan_backup_last_run_timestamp_seconds, wekan_backup_last_run_success and
  wekan_backup_overdue for alerting.
- BACKUP_KEEP_DAYS: after a successful backup, local backups of the same scope
  older than N days are deleted. The newest is always kept, and only a
  directory inside the backup root holding a backup.zip is removed.

From the patch by Raymond-Voogt, and in Admin Panel as well:
- BACKUP_KEEP_DAYS is the catalog's backup section
  (models/lib/authConfigCatalog.js), overridable beside the schedule in
  Admin Panel / Attachments / Backup and resolved through authEnv at every
  backup.
- It is listed on every platform: the compose files, start-wekan.sh and .bat,
  the snap config and help, and the Dockerfile, which the patch's version
  had missed. Its platform hunks, written before #6789, were re-applied by
  hand.

tests/backupRuns.test.cjs (21): the run records, overdue and pruning against a
real directory, and the Admin Panel override, including that nothing reads the
variable around it. tests/authConfigCatalog.test.cjs checks every platform.
admin-attachment-retention.e2e.js checks the Backup pane shows the field.

Thanks to Raymond-Voogt and xet7 !

Fixes #6790.
2026-10-20 13:30:00 +03:00
Lauri Ojansivu dcf91e4471 Attachment retention: delete card attachments after N days, purge deleted ones after M days - in Admin Panel too.
Organisations that must not keep files forever (storage limits, GDPR data
minimisation) had only scripts working on MongoDB directly, which leave card
references behind, do not know which storage holds a file, and break on
upgrades. Soft-deleted attachments were also kept forever.

A nightly job (server/attachmentRetention.js, rules in
models/lib/attachmentRetention.js), off unless configured:
- ATTACHMENTS_RETENTION_DAYS soft-deletes card attachments older than N days
  through the card's own Delete (cover unset, activity, card history Restore).
- ATTACHMENTS_RETENTION_MODE: uploaded (default), or card-inactive, which also
  requires the card to have had no activity for as long.
- ATTACHMENTS_PURGE_DELETED_AFTER_DAYS removes attachments soft-deleted at
  least M days ago for good, file included, through Attachments.removeAsync
  so every storage backend is cleaned. The write re-checks deletedAt, so one
  restored meanwhile stays.
- ATTACHMENTS_RETENTION_DRY_RUN only logs; ATTACHMENTS_RETENTION_MAX_PER_RUN
  caps each step per night.
Never touched: attachments without a card, template boards and cards, and
attachments with an unknown upload date.

From the patch by Raymond-Voogt, and in Admin Panel as well:
- The five settings are a section of models/lib/authConfigCatalog.js.
  Admin Panel / Attachments / Retention (/admin/attachments/retention) draws
  the shared override form on it: a value there wins, an empty field falls
  back to the variable, and the source in effect is shown.
- The job resolves them through authEnv on every run, so a change needs no
  restart.
- The new menu label attachment-retention is translated in all 246 locales.
- The catalog's platform test then found that the patch had missed the
  Dockerfile and the snap help; both list all five now, beside
  docker-compose, start-wekan and the snap config.

tests/attachmentRetention.test.cjs (14) covers the rules and the Admin Panel
wiring; negative: the job never reads the environment around the Admin Panel.
tests/authConfigCatalog.test.cjs checks every platform lists each variable.
tests/playwright/specs/admin-attachment-retention.e2e.js opens the pane, and
checks that an ordinary user cannot.

Thanks to Raymond-Voogt and xet7 !

Fixes #6789.
2026-10-20 13:28:17 +03:00
Lauri Ojansivu 193fb449bd Adding a board member: email invite is case-insensitive, and search filters before it limits.
A board member who was not a board admin could not add a colleague from
Board / Members / +.

- inviteUserToBoard matched the typed email address exactly. LDAP and OIDC
  store an address as the directory spells it ("Jan.Jansen@Example.com"), so
  "jan.jansen@example.com" found nobody, and WeKan tried to create a new
  account instead: "This user is not created" with registration off,
  otherwise a raw 403 "Email already exists". The match is now
  case-insensitive, anchored and escaped.
- searchUsers read five users and filtered afterwards: disabled accounts
  (which the invite then refuses, #1894) and, for non-admins, people outside
  the user's organization or team (#6116). Five non-matching users ahead of
  the colleague hid them. It reads 50, filters, then returns 5.

From the patch by Raymond-Voogt. tests/inviteMemberEmailCase.test.cjs pins
both, including that the pattern is anchored and its dot literal.
tests/playwright/specs/invite-member-email-case.e2e.js covers:
- an invite with a lower-cased address adds the stored colleague, and no
  second account is made;
- negative: a similar but different address is not taken for them;
- the search returns the colleague past six disabled matches, offering none
  of them.

Thanks to Raymond-Voogt and xet7 !

Fixes #6788.
2026-10-20 13:24:14 +03:00
Lauri Ojansivu 13dfa18e8b Merge remote-tracking branch 'origin/main' 2026-10-20 13:15:20 +03:00
Lauri Ojansivu 31c5ad3727 CHANGELOG: the Security report's false alarms fixed, its labels translated.
Thanks to xet7 !
2026-10-20 10:37:28 +03:00
Lauri Ojansivu 3d703965c4 Admin Panel / Problems: "IPv6 location" and "Instrumentation" are translated.
The Security report in Finnish read "Sijainti" beside the English "IPv6
location", and the menu row and title bar of the Instrumentation report were
English in every language.

- The IPv6 location column is the translated 'location' with the fixed
  technical suffix " (IPv6)" - "Sijainti (IPv6)" - through a new optional
  labelSuffix on table headers, so no new key is needed.
- instrumentationReportTitle names the report in the menu row and in its
  title bar, which tests/adminUrls.test.cjs requires to say the same words.
  It is translated into all 246 locale files, after instrumentation-other
  in en.i18n.json's key order. French uses "Instrumentation logicielle",
  because the bare word is spelled exactly like the English one.

Low-confidence translations, written from the language's general
vocabulary for "measuring tools" where the technical term has no
established form: ak, ay, bm, bua, chr, cv, ee, ff, fj, gn, gv, ha, haw, ig,
iu, kl, kw, lg, mi, nah, nd, ny, om, qu, rn, rw, sah, se, sm, sn, ss, st,
tig, tlh, to, tpi, ts, ve, vo, wal, wo, zgh.

tests/addressReportColumns.test.cjs checks that every address column header
is translated. The admin-office-location UI spec expects "Location (IPv6)".

Thanks to xet7 !
2026-10-20 10:37:03 +03:00
Lauri Ojansivu 8a23306c2e Locations from Cloudflare keep their letters: "Dinkelsbühl", not "Dinkelsbühl".
Admin Panel / Problems / Security showed a sign-in from "Dinkelsbühl, DE".
Cloudflare sends cf-ipcity as raw UTF-8 bytes, and Node hands HTTP header
values over as Latin-1, one character per byte, so every city with a letter
outside ASCII was stored garbled.

geoHeaders reads a header value made only of byte-sized characters as UTF-8
when its bytes are valid UTF-8, before the existing percent-decoding. Real
Latin-1 that is not valid UTF-8, plain ASCII and percent-encoded names are
left as they were. Rows already stored keep the old text until their place is
seen again.

tests/geoHeadersUtf8.test.cjs: Dinkelsbühl, Zürich, Kraków, Москва, 東京, and
an end-to-end HTTP request carrying the UTF-8 bytes. Negatives: ASCII, a
percent-encoded city and genuine Latin-1 are unchanged. It fails without the
fix.

Thanks to xet7 !
2026-10-20 10:24:32 +03:00
Lauri Ojansivu 52f127c0a5 StorageBleed: a clean file-integrity scan is not a security event.
Admin Panel / Problems / Security showed StorageBleed, "checked 12144 files in
900s; 0 changed, 0 with no record saying why; stopped early: this run has
used its time", 41 times: the background scan reported its own summary on
every run. A clean scan is the scan working, and the Security report is for
attempts. A summary that has nothing to sum up hides the row that matters.

The summary is recorded only when the scan found a changed or missing file,
beside those findings, which are still recorded one by one. A clean run,
including one that ran out of time and continues next time, is written to
the server log as "[file-integrity] ...".

tests/fileIntegrity.test.cjs pins the condition. Negatives: no unconditional
summary record remains, and changed and missing files are still recorded.

Thanks to xet7 !
2026-10-20 10:23:47 +03:00
Lauri Ojansivu c7c541ccc5 FileNameBleed: a percent-encoded name is not an exploit, and opening a file is not an upload.
Admin Panel / Problems / Security showed FileNameBleed, "uploaded a filename
carrying an exploit pattern; URL-encoded name", for a user who had only
opened a board of 2019 photos with Cyrillic names. Reading each file
repaired its stored percent-encoded name (the FileBleed "fileRead" rows,
correct and recorded once per file). The repair's reasons were then passed to
the upload canary, where "URL-encoded name" counted as dangerous. Browsers and
older WeKan percent-encode every non-ASCII name, so every such name tripped it.

- "URL-encoded name" is no longer a dangerous reason.
- What an encoding could hide is named from the decoded name instead:
  sanitizationReasons() now reports "path traversal" and "null byte". Before,
  "URL-encoded name" was the only thing an encoded "../../etc/passwd" tripped;
  it now trips the path-traversal canary under its own name, which
  DANGEROUS_REASON already listed but no reason ever produced.
- A name repaired while a file is read (source fileRead) never trips the upload
  canary: the repair is still recorded, but it is not an attempt.

tests/filenameCanaryReasons.test.cjs loads the real module, with its server
dependencies stubbed:
- the Cyrillic name trips nothing;
- negatives: an encoded traversal and an encoded null byte still trip, and
  markup in a name uploaded now still trips;
- a read-time repair is recorded without a canary.

Thanks to xet7 !
2026-10-20 10:23:16 +03:00
Lauri Ojansivu 4f3ec0afa3 History integrity: a row is hashed as the database stores it, so an unset value is not "tampering".
Admin Panel / Problems / Security reported HistoryIntegrity checksum-mismatch,
severity critical, 89472 times, on a board's history row nobody had touched.
Meteor connects to MongoDB/FerretDB with ignoreUndefined, so a content
property that is undefined is dropped when the row is stored. Writers record
such content routinely, for example `deletedAt: list.deletedAt` for a list
that was never deleted. The row's hash covered `"deletedAt":{"$undefined":true}`,
the stored row has no deletedAt, and every background audit found the row
failing its own checksum and reported it again.

- hashHistoryRow() hashes previousContent and newContent as stored: undefined
  properties dropped, undefined array elements as null, as the driver does.
  New rows verify as the database returns them.
- Rows written before this were hashed with the undefined keys. If the normal
  check fails, the keys present in the other side of the change and absent
  here are re-added as undefined, which reproduces exactly what was hashed;
  nothing else is accepted.

tests/changeHistoryIntegrity.test.cjs stores rows through BSON with
ignoreUndefined, exactly as the driver does: a new row and a pre-fix row both
verify, and a changed value, a removed real value, a change on the other side
and an added key are still caught. The test fails without the fix.

A row that still reports checksum-mismatch after this release changed in a
way an unset value cannot explain, and is worth looking at.

Thanks to xet7 !
2026-10-20 10:21:43 +03:00
Lauri Ojansivu 363cd22916 Launchpad retry: match git's transfer error as 'RPC failed;', not by the word curl.
tests/releaseDownloads.test.cjs flags every workflow line that names curl
as a download that must go through releases/fetch.sh; infra_failure()'s grep
pattern named it only as text in git's error. 'RPC failed;' identifies that
error on its own. tests/snapLaunchpadInfraRetry.test.cjs still runs the
function against v12.56's 'RPC failed; curl 56' log.

Thanks to xet7 !
2026-10-20 10:12:08 +03:00
Lauri Ojansivu e73338e54b CHANGELOG: Bun smoke-test MongoDB waited for, Launchpad outages retried longer.
Thanks to xet7 !
2026-10-20 09:58:08 +03:00
Lauri Ojansivu 727e96e527 Snap on Launchpad: an outage there gets more attempts and longer pauses, not three strikes.
v12.56's ppc64el snap failed after 20 minutes of its five-hour budget. Attempt
1's Launchpad builder lost its TLS connection while cloning our source ("RPC
failed; curl 56 GnuTLS recv error ... fatal: early EOF"). Attempts 2 and 3
followed a minute apart each, and both met Launchpad's own "HTTP Error 503:
Service Unavailable" / "Error: Timeout". s390x, riscv64 and armhf each lost an
attempt the same night and recovered.

infra_failure() recognises an attempt that failed on Launchpad rather than on
WeKan: an HTTP 5xx or timeout from its API, or, in that attempt's own build
log, a dropped git/TLS transfer or the build farm's package proxy failing.
Such an attempt earns another one, up to six, after a pause of 5, 10, 15...
minutes, never running past the shared deadline. Every other failure keeps
three attempts and the 60s pause; a missing stage-package is not retried
longer.

tests/snapLaunchpadInfraRetry.test.cjs runs the workflow's own infra_failure()
against logs shaped like v12.56's, and against the negatives: a missing
stage-package, an earlier attempt's log, a 4xx.

Thanks to xet7 !
2026-10-20 09:57:54 +03:00
Lauri Ojansivu 294a9016d3 Bun bundles: the smoke-test MongoDB is waited for, and a lost one shown and replaced.
v12.56's Bun linux-x64 bundle failed its live smoke test: WeKan stopped with
"Topology is closed", because nothing listened on 127.0.0.1:27019. The
smoke-test MongoDB had been started with `docker run -d --rm` and never
checked, so a container that had died left no log behind, and the job could
not say why. linux-arm64 passed in the same run, and linux-x64 in v12.53, so
this happens only sometimes.

- bun.yml starts the container without --rm, and waits until it answers
  a ping. If it does not answer within 60s, its state and log are printed.
- build-target.sh checks the port again right before it starts WeKan, with
  the new releases/bun/wait-mongo.mjs (a plain TCP check, nothing to install).
  The database holds only this test's data, so if it is gone, its log is
  printed and a new one is started once. A second failure fails the job.

tests/bunSmokeMongo.test.cjs: URL parsing, the wait succeeding and timing
out, the workflow step and the single replacement.
buildScriptParity lists wait-mongo.mjs with the other Bun target-build
internals.

Thanks to xet7 !
2026-10-20 09:56:00 +03:00
github-actions[bot] b160d355ff Bump versions for v12.56 2026-10-20 00:54:06 +00:00
Lauri Ojansivu 3ae98f3059 Prepare v12.56 release 2026-10-20 03:52:54 +03:00
Lauri Ojansivu 84bf7d2dd7 Merge remote-tracking branch 'origin/main' 2026-10-20 03:52:25 +03:00
Lauri Ojansivu 78c453a66d CHANGELOG: #6783 also needed the People table's row projection.
Thanks to Raymond-Voogt and xet7 !
2026-10-19 20:21:40 +03:00
Lauri Ojansivu c4376f92a5 People: every row's Last active reaches the table, not only the publication.
The UI test of #6783 (inactive-users-last-active.e2e.js) still found "-" in
another user's row after the people publication started sending
lastConnectionDate. The table builds its rows from a client-side Users.find
with its own field list (peopleDocs), which left the date out. The row's
lastActiveAt read that copy before the full user document, so the date never
reached the cell. The projection keeps the field now. The two presence
helpers read the whole cached user, as userData() already does, so a field
missing from the projection cannot hide them again.

Verified with Playwright on Chromium against the dev server: the spec failed
before this change and passes with it. tests/lastActive.test.cjs pins both
halves and fails without this commit.

Thanks to Raymond-Voogt and xet7 !

Fixes #6783.
2026-10-19 20:21:32 +03:00
Lauri Ojansivu f1b183ac2a rtl test: a check mark's border-left/-right is the one physical side allowed.
A check mark is not mirrored in a right-to-left language (#6786), so the
tick's rule uses border-right. The guard allows border-left/-right only inside
a rule whose selector is a checked state, the same definition
tests/rtlCheckMark.test.cjs uses. A border anywhere else, and any margin,
padding or float inside such a rule, is still refused.

Thanks to mimZD and xet7 !
2026-10-19 17:55:10 +03:00
Lauri Ojansivu faa56aa1a8 docker-compose.yml lists USERS_DISABLE_INACTIVE_AFTER_DAYS where every backend's compose file does.
Its wekan service must match the other compose files line for line
(tests/dockerComposeBackends.test.cjs): the setting moves from beside
ACCOUNTS_COMMON_LOGIN_EXPIRATION_IN_DAYS to after LOGOUT_ON_MINUTES, with the
Admin Panel note the others carry.

Thanks to Raymond-Voogt and xet7 !
2026-10-19 17:55:10 +03:00
Lauri Ojansivu 912d414fdf CHANGELOG: inactive accounts disabled, LDAP sync filter, Last active for everyone, RTL check marks, restored list swimlanes kept.
Thanks to Raymond-Voogt, mimZD and xet7 !
2026-10-19 17:42:08 +03:00
Lauri Ojansivu 3469f84c78 Restored list swimlanes stay restored after a WeKan upgrade.
Admin Panel / Problems / "Lists missing their swimlane" -> Restore binds each
list back to the swimlane positionHistory records it was created in. After
every new WeKan release, the startup schema upgrade re-checks old data, and
its merge-per-swimlane-lists step - written for the v7.98 era, which stamped
lists to the default swimlane and duplicated them per swimlane - took those
lists for that era's damage. It ran when a board had two lists of one title
or one list with cards in another swimlane, and then un-bound every list on
the board, the restored ones included. So after each upgrade the count came
back to exactly the same number.

A list bound to the very swimlane it was created in, with none of its cards
in another swimlane, is per-swimlane by design: it is what Restore puts back,
and not a v7.98 stamp, which was applied after the list existed. The step now
leaves those lists out of its check, its merge and its un-binding, so a board
of only such lists does not trigger it. Stamped lists, lists whose cards are
in other swimlanes, and lists bound anywhere but their record are handled as
before. Lists already un-bound by an earlier upgrade are restored by pressing
Restore once more, and now stay.

tests/schemaUpgradeSteps.test.cjs: the issue's board (restored lists kept, a
stamp on the same board still un-bound), a by-design board not triggering the
step, and the two negatives. The first fails without the fix.

Thanks to mimZD and xet7 !

Fixes #6787.
2026-10-19 17:41:32 +03:00
Lauri Ojansivu b4bcded492 A checked checkbox is a check mark in right-to-left languages too, not "<".
The tick is a box rotated 40 degrees, drawn by its bottom and side borders,
and the side was border-inline-end. A right-to-left page puts that on the
left, so the rotated corner opened the other way and every checked checkbox
showed "<" - the Admin Panel's bounced-email table in Persian, in the issue.
A check mark is not mirrored in a right-to-left language, so all 25 rules
that draw one - the shared materialCheckBox, checklists, subtasks, the
sidebar, the Admin Panel and every board theme's colour - use border-right
and border-left now. Where the box sits stays logical.

tests/rtlCheckMark.test.cjs fails if any checked state anywhere in client/
draws its tick with a logical border. tests/playwright/specs/rtl-check-mark.e2e.js
compares the computed borders in ltr and rtl. customFieldsSectionMenu's guard
now expects border-right, and its comment says why.

Thanks to mimZD and xet7 !

Fixes #6786.
2026-10-19 17:37:53 +03:00
Lauri Ojansivu b933a69cd4 People shows everyone's Last active; inactive users can be disabled; LDAP sync honours its search filter.
Admin Panel / People showed "Last active" as "-" for every user but the
administrator: the people publication did not carry lastConnectionDate.

USERS_DISABLE_INACTIVE_AFTER_DAYS disables, once a night, accounts that have
not used WeKan for that many days, and revokes their logins. Admins are
never disabled. An account with no recorded activity starts its count on the
first run rather than from createdAt, and re-enabling an account restarts it.
The setting is in Admin Panel / People / Login as well as in the
environment, and the job resolves it every night, so a change there needs no
restart. Every platform's settings file lists it.

LDAP background sync looked a user up by id without LDAP_USER_SEARCH_FILTER,
so an account the filter excludes - one disabled in Active Directory - could
not log in but was still "found". It was never disabled, and a manual
disable was undone on the next run. The filter is now ANDed into the lookup.
An account disabled for inactivity is not re-enabled by the sync.

From the patch by Raymond-Voogt, with the setting moved into the Admin
Panel's login settings. Tests: tests/inactiveUsers.test.cjs,
tests/ldapBackgroundSync.test.cjs, tests/ldapUserIdFilter.test.cjs,
tests/authConfigCatalog.test.cjs and
tests/playwright/specs/inactive-users-last-active.e2e.js.

Thanks to Raymond-Voogt and xet7 !

Fixes #6783,
Fixes #6784,
Fixes #6785.
2026-10-19 17:35:51 +03:00
github-actions[bot] e4d025e0b4 Bump versions for v12.55 2026-10-18 20:52:17 +00:00
Lauri Ojansivu 28163c90d1 Prepare v12.55 release 2026-10-18 23:51:09 +03:00
Lauri Ojansivu 2ca5d06709 Merge remote-tracking branch 'origin/main' 2026-10-18 23:50:24 +03:00
Lauri Ojansivu 506067b4a1 CHANGELOG: 32-bit ARM single files injected in a 64-bit process, each musl AppImage built once.
Thanks to xet7 !
2026-10-18 23:35:30 +03:00
Lauri Ojansivu 2cbb038719 AppImage: each musl AppImage built once, and every job has its own name.
release-all's glibc "AppImages" call passed no `only`, so it built the musl
rows too, and both groups had an "AppImage x86_64" and an "AppImage aarch64".
Two jobs of one name write to one directory of the run's log archive, whose
files then ask to be overwritten or renamed when it is unpacked.

- The glibc call builds x86_64, aarch64, i686 and armhf only. The musl pair
  stays with the "musl AppImages" call, which waits for the musl bundles.
- The job is named by its file's architecture, so it is "AppImage
  x86_64-musl", which the publish step's "see the AppImage <arch> build job's
  log" names too.
- The publish step checks only the architectures its own run builds. The musl
  run warned that the four glibc AppImages were missing.

tests/muslBundles.test.cjs checks that no two matrix rows share a name, and
that each row is built by exactly one call.

Thanks to xet7 !
2026-10-18 23:35:14 +03:00
Lauri Ojansivu 29dfe94886 Single files: a 32-bit ELF target's blob is injected in a 64-bit process.
v12.54's armv7 single file failed with std::bad_alloc on all three attempts
of the retry added for armhf in v12.53, so the failure is not intermittent.
--build-sea injects with LIEF inside the Node.js that runs it, and a 32-bit
target's blob must be written by a 32-bit Node.js. The executable, the
180 MiB archive and LIEF's copies of both do not fit that process's address
space.

For a 32-bit ELF target the i386 Node.js now writes only the blob
(--experimental-sea-config: the same blob, no LIEF), and the new
releases/single-file/sea-inject-elf.py injects it with LIEF 1.0.0 from PyPI,
the version Node.js vendors. It ports InjectIntoELF() and MarkSentinel()
from src/node_sea_bin.cc step for step. Its output is byte-identical to
--build-sea's, for a non-PIE x64 Node.js and for the PIE node-armv7. win32
(PE) and 64-bit targets keep --build-sea. The retry is removed.

tests/singleFileSeaInject.test.cjs checks the split and the workflow's LIEF.
Where a Python with LIEF 1.0.0 is available it builds both ways, compares
them byte for byte, runs the result, and checks that an executable that
already carries a blob is refused.

Thanks to xet7 !
2026-10-18 23:35:14 +03:00
github-actions[bot] 734e81fb8c Bump versions for v12.54 2026-10-18 15:57:36 +00:00
Lauri Ojansivu 81e609cb69 Prepare v12.54 release 2026-10-18 18:56:32 +03:00
Lauri Ojansivu f166786e7a Merge remote-tracking branch 'origin/main' 2026-10-18 18:55:54 +03:00
Lauri Ojansivu 1a6a96b667 nodeRuntimeDependency: the glibc libatomic requirement still reaches the AppImage check past the musl NEEDED block.
Thanks to xet7 !
2026-10-18 18:52:49 +03:00
Lauri Ojansivu 939addc77a CHANGELOG: Android bundles, the armhf single file and the x86_64 musl AppImage build again.
Thanks to xet7 !
2026-10-18 18:49:48 +03:00
Lauri Ojansivu df5560ad51 musl bundles carry every library their Node.js needs, libatomic too.
Every x86_64 musl AppImage of v12.53 failed to start in a bare Alpine:
"Error loading shared library libatomic.so.1". node-runtime held only
libstdc++ and libgcc_s; the build container had libatomic through g++, so the
bundle's own smoke test passed.

build-bundle.sh now copies every NEEDED library of bundle/node except musl's
libc, and AppImage.yml checks each is in node-runtime before packing.

Thanks to xet7 !
2026-10-18 18:49:28 +03:00
Lauri Ojansivu fa0c03437c Single files: try --build-sea again when it runs out of memory.
The v12.53 armhf single file failed with std::bad_alloc in --build-sea. A
32-bit target is injected by the 32-bit i386 Node.js, and the executable,
the 180 MiB archive and LIEF's copies of both come near its 4 GiB address
space. armv7 and armv6, same archive and host, built in the same run, and
armhf built in v12.52, so the failure is intermittent.

buildSea() runs it up to three times when, and only when, it fails with
std::bad_alloc, removing the partial output between attempts. Any other
failure stops at once.

Thanks to xet7 !
2026-10-18 18:49:28 +03:00
Lauri Ojansivu 519869b561 Android bundles: preload termux-exec, so bcrypt's install script runs.
Every Android bundle of v12.53 stopped in npm-rebuild.js: "sh: 1:
node-gyp-build: not found". Termux has no /usr/bin/env, so a
"#!/usr/bin/env node" script runs only through termux-exec, an LD_PRELOAD
library that Termux's login exports. The workflow starts bash directly,
without login.

build-bundle.sh exports termux-exec's library the way login does, newest
generation first, and keeps it only if a program still starts with it.

Thanks to xet7 !
2026-10-18 18:49:28 +03:00
github-actions[bot] 8a67ca34d6 Bump versions for v12.53 2026-10-18 11:34:09 +00:00
Lauri Ojansivu 9ba22aaca7 Prepare v12.53 release 2026-10-18 14:33:00 +03:00
Lauri Ojansivu bc0991e06f CHANGELOG: #6782, a card move with an outgoing webhook keeps the mover's account.
Thanks to do4me and xet7 !
2026-10-18 14:29:09 +03:00
Lauri Ojansivu b01c233269 Moving a card with an outgoing webhook no longer counts as HookBleed or disables the mover's account.
The activity hook delivered webhooks through Meteor.call('outgoingWebhooks'),
and that method trusted a call without this.connection as the server's own.
Meteor gives a server-side Meteor.call the userId AND connection of the method
it runs in (ddp-server applyAsync), and a card moved from a browser runs in
that browser's method. So the server's own act-moveCard delivery arrived as a
client forging one: it was recorded as ssrf.webhook-forge (high) with the
mover's userId, and blockOnSecurityEvent set loginDisabled.

- deliverActivityWebhook() in server/notifications/outgoing.js is the server's
  delivery: a function, not a method, so no client reaches it and no inherited
  context changes it. It reads the integration again (enabled) and names the
  activity's actor. server/models/activities.js calls it through safeDeliver.
- The outgoingWebhooks method treats every call as a client's: only the
  card-opened notification, rebuilt from the card, as before for clients.
- Server deliveries no longer need the actor to be a member of the hook's
  board, which also skipped a global webhook ('_global' is no board) and an
  activity with no method behind it (REST, timers).
- securityLog.record() now passes the catalog key and blocksAccount to the
  block decision. The stored row carries neither, so every 'high' blocked row
  disabled whoever it named: a hook refused for its own internal address
  disabled the mover too, and ssrf.redirect/ssrf.attachment's
  blocksAccount: false never applied. ssrf.webhook is now blocksAccount:
  false - the person whose move sent it did not write the hook's URL.

tests/hookBleedServerDelivery.test.cjs: the move delivered with nothing
logged, in an inherited context too; a forged call still refused, logged and
blocking; the card-opened notification; failing and internal hooks blocking
nobody, through record() itself; and a whole-tree scan that no server code
calls a this.connection-gated method from inside a client's method without
clearing the invocation. tests/playwright/specs/hook-bleed.e2e.js moves a
card through the Move dialog with a webhook enabled.

Thanks to do4me and xet7 !

Fixes #6782.
2026-10-18 14:28:19 +03:00
github-actions[bot] 3c9f4ba045 Bump versions for v12.52 2026-10-17 23:49:33 +00:00
Lauri Ojansivu e0f8447524 Prepare v12.52 release 2026-10-18 02:48:13 +03:00
Lauri Ojansivu 6809149ef1 CHANGELOG: the nine dependabot updates, checked, and the guards that follow them.
Thanks to dependabot and xet7 !
2026-10-18 02:44:47 +03:00
Lauri Ojansivu 53729d47f5 Guards follow the merged dependabot updates: freebsd-vm 1.5.9, qrcode-generator 2.0.4.
bsdBundles pinned bsd.yml's FreeBSD VM to the old commit; it now reads the
pin and requires node-patches to use the same one, which it now does.
sqrl required qrcode-generator 1.4.4 exactly; it now requires an exact pin,
and 2.0.4 was checked to give sqrlQrSvg's call the same SVG.

Thanks to dependabot and xet7 !
2026-10-18 02:44:40 +03:00
Lauri Ojansivu f063f5043f Merge pull request #6781 from wekan/dependabot/github_actions/vmactions/freebsd-vm-1.5.9
Bump vmactions/freebsd-vm from 1.5.5 to 1.5.9
2026-10-17 23:48:29 +03:00
Lauri Ojansivu 71e942b146 Merge pull request #6780 from wekan/dependabot/github_actions/actions/setup-python-7.0.0
Bump actions/setup-python from 6.3.0 to 7.0.0
2026-10-17 23:48:15 +03:00
Lauri Ojansivu e365a57649 Merge pull request #6779 from wekan/dependabot/npm_and_yarn/i18next-26.4.5
Bump i18next from 26.4.2 to 26.4.5
2026-10-17 23:47:59 +03:00
Lauri Ojansivu 372192a28d Merge pull request #6778 from wekan/dependabot/npm_and_yarn/swc/core-1.16.18
Bump @swc/core from 1.16.13 to 1.16.18
2026-10-17 23:46:57 +03:00
Lauri Ojansivu 9340b25c03 Merge pull request #6777 from wekan/dependabot/npm_and_yarn/temml-0.14.0
Bump temml from 0.13.5 to 0.14.0
2026-10-17 23:46:42 +03:00
Lauri Ojansivu dbf50d67bd Merge pull request #6776 from wekan/dependabot/npm_and_yarn/tests/playwright/playwright/test-1.64.1
Bump @playwright/test from 1.63.0 to 1.64.1 in /tests/playwright
2026-10-17 23:46:24 +03:00
Lauri Ojansivu b022403a55 Merge pull request #6775 from wekan/dependabot/npm_and_yarn/qrcode-generator-2.0.4
Bump qrcode-generator from 1.4.4 to 2.0.4
2026-10-17 23:46:11 +03:00
Lauri Ojansivu 67d0e4fd94 Merge pull request #6774 from wekan/dependabot/github_actions/actions/download-artifact-8.0.2
Bump actions/download-artifact from 7.0.0 to 8.0.2
2026-10-17 23:45:57 +03:00
Lauri Ojansivu ed1aae7cf0 Merge pull request #6773 from wekan/dependabot/npm_and_yarn/rspack/cli-2.2.9
Bump @rspack/cli from 2.2.8 to 2.2.9
2026-10-17 23:45:41 +03:00
Lauri Ojansivu 161f213de0 Merge remote-tracking branch 'origin/main' 2026-10-17 23:44:43 +03:00
dependabot[bot] bdb6e4f875 Bump vmactions/freebsd-vm from 1.5.5 to 1.5.9
Bumps [vmactions/freebsd-vm](https://github.com/vmactions/freebsd-vm) from 1.5.5 to 1.5.9.
- [Release notes](https://github.com/vmactions/freebsd-vm/releases)
- [Commits](https://github.com/vmactions/freebsd-vm/compare/v1.5.5...c46abacb49f09938ca4e1702d15d836285d694cc)

---
updated-dependencies:
- dependency-name: vmactions/freebsd-vm
  dependency-version: 1.5.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-17 20:33:40 +00:00
dependabot[bot] 4afb4cb0f4 Bump actions/setup-python from 6.3.0 to 7.0.0
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 6.3.0 to 7.0.0.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/ece7cb06caefa5fff74198d8649806c4678c61a1...5fda3b95a4ea91299a34e894583c3862153e4b97)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-17 20:33:37 +00:00
dependabot[bot] 04639dd871 Bump i18next from 26.4.2 to 26.4.5
Bumps [i18next](https://github.com/i18next/i18next) from 26.4.2 to 26.4.5.
- [Release notes](https://github.com/i18next/i18next/releases)
- [Changelog](https://github.com/i18next/i18next/blob/master/CHANGELOG.md)
- [Commits](https://github.com/i18next/i18next/compare/v26.4.2...v26.4.5)

---
updated-dependencies:
- dependency-name: i18next
  dependency-version: 26.4.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-17 20:33:26 +00:00
dependabot[bot] 330cd9dab0 Bump @swc/core from 1.16.13 to 1.16.18
Bumps [@swc/core](https://github.com/swc-project/swc/tree/HEAD/packages/core) from 1.16.13 to 1.16.18.
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/commits/v1.16.18/packages/core)

---
updated-dependencies:
- dependency-name: "@swc/core"
  dependency-version: 1.16.18
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-17 20:33:18 +00:00
dependabot[bot] 034e5ea441 Bump temml from 0.13.5 to 0.14.0
Bumps [temml](https://github.com/ronkok/Temml) from 0.13.5 to 0.14.0.
- [Release notes](https://github.com/ronkok/Temml/releases)
- [Changelog](https://github.com/ronkok/Temml/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ronkok/Temml/compare/v0.13.5...v0.14.0)

---
updated-dependencies:
- dependency-name: temml
  dependency-version: 0.14.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-17 20:33:00 +00:00
dependabot[bot] 7ffe87a677 Bump @playwright/test from 1.63.0 to 1.64.1 in /tests/playwright
Bumps [@playwright/test](https://github.com/microsoft/playwright) from 1.63.0 to 1.64.1.
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.63.0...v1.64.1)

---
updated-dependencies:
- dependency-name: "@playwright/test"
  dependency-version: 1.64.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-17 20:32:54 +00:00
dependabot[bot] ac97edb4aa Bump qrcode-generator from 1.4.4 to 2.0.4
Bumps [qrcode-generator](https://github.com/kazuhikoarase/qrcode-generator) from 1.4.4 to 2.0.4.
- [Release notes](https://github.com/kazuhikoarase/qrcode-generator/releases)
- [Commits](https://github.com/kazuhikoarase/qrcode-generator/compare/js1.4.4...js2.0.4)

---
updated-dependencies:
- dependency-name: qrcode-generator
  dependency-version: 2.0.4
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-17 20:32:52 +00:00
dependabot[bot] 43a646b19e Bump actions/download-artifact from 7.0.0 to 8.0.2
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 7.0.0 to 8.0.2.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v7...9000827ccba6bdab643e8b6fd33ac0654aef8333)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: 8.0.2
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-17 20:32:46 +00:00
dependabot[bot] b0ac3b0629 Bump @rspack/cli from 2.2.8 to 2.2.9
Bumps [@rspack/cli](https://github.com/web-infra-dev/rspack/tree/HEAD/packages/rspack-cli) from 2.2.8 to 2.2.9.
- [Release notes](https://github.com/web-infra-dev/rspack/releases)
- [Commits](https://github.com/web-infra-dev/rspack/commits/v2.2.9/packages/rspack-cli)

---
updated-dependencies:
- dependency-name: "@rspack/cli"
  dependency-version: 2.2.9
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-17 20:32:45 +00:00
Lauri Ojansivu 95a1478c8c CHANGELOG: Android, musl and single-file release builds.
Thanks to xet7 !
2026-10-17 23:25:30 +03:00
Lauri Ojansivu c316616ef8 Single files: skip, with the reason, a Node.js LIEF cannot inject; no error for a target not built.
v12.51's i386 single file stopped with std::bad_alloc and freebsd-x64's with
"Failed to relocate ELF program headers": the i386 Node.js is a PIE with
R_386_32 relocations LIEF does not adjust, and the FreeBSD one a non-PIE with
two bss-like segments. releases/single-file/sea-inject.mjs reads that from
the ELF before anything is built, build.mjs exits 3 with the reason, and
single-file.yml turns it into a notice. wekan/node-patches now builds both
the way the injection needs.

A target that was not built leaves a one-line artifact saying why, so its
Start job no longer logs "Artifact not found" errors and publish does not
warn about it as unstarted.

Thanks to xet7 !
2026-10-17 23:24:49 +03:00
Lauri Ojansivu bce305029a musl bundles carry libstdc++ and libgcc_s, so their Node.js starts on a bare Alpine.
Every musl AppImage of v12.51 died at once in Alpine: "Error relocating
.../bundle/node: _ZTVN10__cxxabiv117__class_type_infoE: symbol not found".
The musl Node.js is linked against GCC's C++ runtime, which musl.yml's own
Alpine had installed for building, and a user's Alpine does not. The bundle's
node-runtime now holds that Alpine's libstdc++.so.6 and libgcc_s.so.1 with a
license notice, start-wekan.sh puts it on the library path as for libatomic,
and the AppImage workflow requires both files of a musl bundle.

Thanks to xet7 !
2026-10-17 23:24:49 +03:00
Lauri Ojansivu 0670cc65a3 Android bundles: hand the checkout to Termux's user, so npm may chmod.
Every Android bundle of v12.51 stopped with EPERM: npm chmods each package's
bin when it installs (uuid/dist/bin/uuid), and only a file's owner may chmod
it - a+rwX on the bind mount was not enough. android.yml reads the uid of
Termux's user from the image, chowns the checkout to it before the rebuild,
and takes it back before packaging. An unreadable uid stops the job.

Thanks to xet7 !
2026-10-17 23:24:49 +03:00
github-actions[bot] 95c99e1b5e Bump versions for v12.51 2026-10-17 15:34:41 +00:00
Lauri Ojansivu c1cb54eb3b Prepare v12.51 release 2026-10-17 18:33:31 +03:00
Lauri Ojansivu 3eee7d54df CHANGELOG: CodeQL alert #588, Apple Glass Pastel as Liquid Glass.
Thanks to GitHub CodeQL and xet7 !
2026-10-17 17:56:05 +03:00
Lauri Ojansivu 6090be4dfd Fix GitHub CodeQL alert #588: remove template declarations to a fixed point.
js/incomplete-multi-character-sanitization in
tests/templatesAreRendered.test.cjs: the test removed template declarations
and Template.<name>.helpers/events/onCreated/onRendered/onDestroyed from the
source it scans with a single replace each, so removing one could splice its
neighbours into another (and CodeQL read the remains as a possible on...
attribute). Both removals now repeat until nothing changes, the fix used for
alerts #545 and #570-#582.

tests/tagStrippingFixedPoint.test.cjs keeps the shape out of the whole tree:
it now also finds a multi-character removal of an on<Capital> name outside a
fixed-point loop, and proves it catches alert #588's code and passes the
loop. The code is a test's source scan, never run on user input, so nothing
reaches Admin Panel -> Problems.

Thanks to GitHub CodeQL and xet7 !
2026-10-17 17:55:41 +03:00
Lauri Ojansivu 0c49f7f51b Apple Glass Pastel is Liquid Glass, and every text on it reads.
The theme follows Apple's Liquid Glass: the two header bars, swimlane
headers, lists, minicards, the opened card, the sidebar and popups are
frosted glass over the pastel mesh - a translucent fill,
blur(24px) saturate(180%), a rim of light along the edges (inset highlights,
brightest on top), a diagonal sheen and larger rounded corners. The header
bars are glass now instead of solid blue, with dark text; blue is kept for
the primary action, glossy, and for what is active. Reduce transparency, or
a browser without backdrop-filter, gets near-opaque surfaces.

A new UI test, theme-appleglasspastel-contrast.e2e.js, measures every text
and icon against what is really painted behind it - it hides the text,
screenshots, and reads the pixels under each box - to WCAG AA (4.5:1 text,
3:1 large text and icons), on the board, the opened card, the sidebar, the
board menu and All Boards, at laptop and phone size. It found and the theme
now fixes: white header buttons, icons, username and the stock logo on the
glass; the sidebar hamburger forced white by boardBody.js's accessibility
style; grey #808080 sidebar headings (3.8:1); #a6a6a6 icons (2.4:1); faint
Add Card; white text on the pale-blue hovered menu row; and the list
headers list.css pins grey with !important.

The header guards headerBars and themeAccents now compare a bar's whole
background, so a glass gradient counts, and list appleglasspastel as a glass
header whose accent is its primary blue.

Thanks to xet7 !
2026-10-17 17:55:34 +03:00
Lauri Ojansivu 321e3d7cbf CHANGELOG: Whiteboard; TODO Later is empty again.
Thanks to xet7 !
2026-10-17 16:57:33 +03:00
Lauri Ojansivu 390007e380 Whiteboard: a card's drawing full screen, that several people draw on at once.
Card menu -> Drawing -> Open as whiteboard opens the drawing under the header
bar, over the opened card, with the drawing's and Red Strings' own tools -
the seven colours and four widths, Undo, Clear and Save picture. The 800 x
500 drawing is scaled to the screen, so a line lands in the same place on a
phone and on a 55" screen; Escape or x closes it.

Several people draw at the same time: each line is sent when the pen lifts
as one atomic $push into that drawing (cards.whiteboardAddStroke), with its
own id and its author, and the card's publication brings it to everyone who
has the drawing open; the line stays drawn locally until it comes back.
Undo and Clear take away only the caller's own lines, Save picture writes
only the drawing's PNG, so nothing anybody else is drawing is overwritten.
Who may draw is who may edit the card; the others watch. A Board Settings /
Card row, Whiteboard, hides it on a board, and the Admin Panel's Card fields
everywhere. The drawing popup's PNG upload is shared with Save picture.
Translated in every language.

Thanks to xet7 !
2026-10-17 16:57:12 +03:00
Lauri Ojansivu d9bb7b4f4f docs/Features: Cards / Whiteboard, a drawing full screen that several people draw on at once.
Thanks to xet7 !
2026-10-17 16:27:33 +03:00
Lauri Ojansivu 813c313b1b CHANGELOG: checklist from description, action items, search phrases; TODO Later keeps the whiteboard.
Thanks to xet7 !
2026-10-17 16:26:17 +03:00
Lauri Ojansivu 3cb089b8ca Checklist from description, action items, and search phrases.
The last three of the features other kanban apps call AI, done with plain
code (docs/Features/General/Assistants/Assistants.md):

- Checklists -> From description: the list lines of a card's description
  ("- ", "* ", "1. ", task boxes; never inside code blocks) previewed, then
  made into a checklist - ticked where the line was [x] - or into subtasks
  through addSubtaskCard.
- Create cards from action items, on a comment and in the card menu for its
  description: "TODO: ...", "- [ ] ..." and "@user ..." lines become cards
  at the top of the card's list, linking back to it, with the mentioned
  user as member when they are an active board member.
- Global search understands plain phrases in the reader's language - "my
  overdue cards", "due this week", "unassigned cards" and seven more - and
  runs them as its operators, saying which; a query with an operator in it
  is never rewritten.

Checklist from description and Action items are Show on Card rows of Board
Settings / Card and of the Admin Panel's Card fields; Search phrases is an
Admin Panel / Settings / Visibility / Features tick. Translated in every
language.

Thanks to xet7 !
2026-10-17 16:25:51 +03:00
Lauri Ojansivu 9fa831fb0a Merge remote-tracking branch 'origin/main' 2026-10-17 16:11:10 +03:00
Lauri Ojansivu 777437bc6a CHANGELOG: Summary, Since your last visit, At risk, Suggested members, Forecast.
Thanks to xet7 !
2026-10-17 15:52:51 +03:00
Lauri Ojansivu 083c863fb7 Summary, Since your last visit, At risk, Suggested members and Forecast.
Five more of the features other kanban apps call AI, done with plain code
(docs/Features/General/Assistants/Assistants.md):

- Board menu -> Summary: what each person did in the last day or week -
  done, moved, added, commented - and what they have overdue, from the
  activities and History; Copy as text gives Markdown with card links.
- Board menu -> Since your last visit (N): what others changed since the
  visit before this one, by card. The visit is kept in profile.boardVisits,
  at most 500 boards.
- At risk: a weighted score of overdue, due soon with little done, blocked,
  idle in a middle list and sent back; a triangle on the minicard and the
  reasons in the opened card.
- Suggested members in the Members and Assignees popups, from who handles
  the same labels, list and swimlane.
- Forecast done date: the 50th and 85th percentile of the board's cycle
  times, counting only the cards that took at least as long as this one
  has been open. The server sends the times once per board, numbers only.

Summary and Since your last visit are features of Admin Panel / Settings /
Visibility / Features; At risk, Suggested members and Forecast are rows of
Board Settings / Card and of the Admin Panel's Card fields. Every server
answer applies the board's read rules and the assigned-only role.
Translated in every language.

The guards of the earlier helpers are brought up to date as well: the date
words of models/lib/titleDueDate.js are a reviewed RegExp site,
server/methods/ruleSuggestions.js is a board-admin-only path, the Rules
page's list branch carries the suggestions, and Tigre's new strings use its
own word for a board.

Thanks to xet7 !
2026-10-17 15:52:26 +03:00
Lauri Ojansivu 9a6b3e8859 CHANGELOG: due date from the title, similar cards, rule suggestions; TODO Later gains the whiteboard.
Thanks to xet7 !
2026-10-17 14:43:43 +03:00
Lauri Ojansivu 2211b24119 Rule suggestions from what people on a board keep doing by hand.
Board menu -> Rules shows Suggested rules above the rule list: when the
same person does one thing to a card and, within two minutes, another, that
pair is counted once per card, and a pair seen on three cards in the last 30
days is offered as a rule (moved to a list -> label, member or archive;
added to a list -> label or member; label added -> member). A pair a rule
already does is not offered. Create goes through rules.createRule with the
same trigger and action the wizard builds; Dismiss hides one suggestion for
that admin on that board (profile.dismissedRuleSuggestions).

Only board admins get suggestions, and Admin Panel / Settings / Visibility /
Features has a Rule suggestions tick that turns them off, on the server too.
Translated in every language.

Thanks to xet7 !
2026-10-17 14:42:59 +03:00
Lauri Ojansivu f8267d704e Due date from the card title, and similar cards and possible duplicates.
Two of the features other kanban apps call AI, done with plain code
(docs/Features/General/Assistants/Assistants.md):

- A due date typed at the end of a new card's title ("... tomorrow 14:00",
  "huomenna klo 14", "friday 9:30", "in 3 days", "23.10.") becomes the card's
  due date, read with the browser's own Intl tables so every language works.
  A hint under the field shows the date, and its x keeps the words. The
  opened card's title reads it too, but only when the title changed.
- Similar cards: letter triples and the Dice coefficient (letter pairs for
  scripts without spaces). The add-card form lists up to three close cards,
  marking a possible duplicate, and the opened card has a Similar cards
  section. The server method applies the board's read rules and the
  assigned-only role.

Both are rows of Board Settings / Card and of Admin Panel / Settings /
Visibility / Features / Card fields, so a board admin or a site admin can
hide them. Translated in every language.

Thanks to xet7 !
2026-10-17 14:39:43 +03:00
Lauri Ojansivu e76f12bcdd docs/Features: guides for the eleven helpers that need no AI, filed by menu, and how each is hidden.
Thanks to xet7 !
2026-10-17 13:57:56 +03:00
Lauri Ojansivu 6f6ae210c6 CHANGELOG: TODO Later lists the features other kanban apps call AI, to be done with plain code.
Thanks to xet7 !
2026-10-17 13:52:35 +03:00
github-actions[bot] 0d726aa725 Bump versions for v12.50 2026-10-17 03:44:38 +00:00
Lauri Ojansivu 5cadb33b1d Prepare v12.50 release 2026-10-17 06:43:30 +03:00
Lauri Ojansivu 31de4b40c5 CHANGELOG: Instrumentation bars by area.
Thanks to xet7 !
2026-10-17 06:04:05 +03:00
Lauri Ojansivu 2b8a33022b Problems / Instrumentation: the whole picture as one bar per area of WeKan.
The page listed up to 200 methods and publications on 11 pages. Above the
table there is now one bar per area - Settings, Login, People, Boards,
Cards, Lists, Notifications, Attachments, Problems and the rest - its length
the area's calls against the busiest area's, its red end the share that
failed, with the calls, the average duration and the errors beside it.
Clicking an area shows only its operations in the table; clicking it again
shows all.

models/lib/instrumentationAreas.js groups an operation by the first area
whose pattern matches its name, and weights an area's average duration by
completed calls. tests/instrumentationAreas.test.cjs checks the grouping
against all of WeKan's 614 methods and publications - "Other" holds three -
and the summary arithmetic. The labels are existing translation keys, apart
from the new instrumentation-other ("Other"), translated into every
language. The Finnish "cards" heading read "Korttia" (the form after a
number) in every place it is used as a heading or label; it is "Kortit".

The bars are HTML and CSS, one grid with subgrid rows so names, bars and
numbers line up; the viewport matrix checks the page at every device size.

Thanks to xet7 !
2026-10-17 06:03:55 +03:00
Lauri Ojansivu d9e91cb59a CHANGELOG: Shared labels colour picker, iPhone Duo, the add-list form's spacing.
The file was over its size budget; releases/changelog-archive.mjs moved its
oldest release to old-CHANGELOG/2026/10.md.

Thanks to xet7 !
2026-10-17 05:33:04 +03:00
Lauri Ojansivu 8c3e80240a Shared labels: the board labels' colour picker, at the size it has on a card.
Admin Panel / Settings / Shared labels chose a colour from a dropdown of
colour names. The board label popup's picker - the palette swatches and the
custom colour wheel - is its own template now, labelColorPicker, and the
popup and every Shared labels row use it; a custom colour is saved as its
hex, which the server already accepted. A pick reaches the row through an
object with set(), because Spacebars calls a function argument instead of
passing it.

The swatches grow to fill their row, so on the wide admin page their width
followed the window. There they have the size they have in an opened card's
label popup, 54 x 33px, at every window width.

tests/sharedLabelsColorPicker.test.cjs pins the shared picker and the size;
tests/playwright/specs/shared-labels-color-picker.e2e.js picks a swatch and a
custom colour, saves both, and measures the swatches at 1366, 1920 and
2560px.

Thanks to xet7 !
2026-10-17 05:32:38 +03:00
Lauri Ojansivu 85ab1b79a3 "or Template or link a list": the space before the second "or", and a clean Jade build.
The add-list form's alternatives are one span.quiet each in .edit-controls,
a flex row; a flex item drops the space its text begins with, so it read
"or Templateor link a list from another board". Adjacent .quiet spans get a
margin between them instead.

Every dev-server start also warned "missing space before text for line 26"
of editor.jade: inlineViewer had a bare {{> UI.contentBlock }} line under
+viewer. It is +Template.contentBlock, as elsewhere.
tests/jadeCompilesClean.test.cjs compiles every client template with the
build's own Jade compiler and fails on any warning or error.

Thanks to xet7 !
2026-10-17 05:15:00 +03:00
Lauri Ojansivu 0242249a1c Viewport matrix: the iPhone Duo, open and closed, instead of a Galaxy Fold.
The foldable in the screen-size test was a Galaxy Z Fold 7 standing in for
the iPhone Duo. Apple's specs (apple.com/iphone-duo/specs) give its inner
7.6" display as 1878 x 2670 px at 430 ppi and its outer 5.4" display as
1398 x 2034 px at 460 ppi; at the 3x of every current iPhone that is
626 x 890 and 466 x 678 CSS pixels, and the screenshots come out at exactly
Apple's pixel sizes. Both displays are tested upright and on their side with
the iPhone user agent and touch; all 40 checks pass.

Thanks to xet7 !
2026-10-17 04:53:54 +03:00
Lauri Ojansivu d4c7167928 CHANGELOG: the new UI checked and fixed at ten screen sizes.
Thanks to xet7 !
2026-10-17 04:06:00 +03:00
Lauri Ojansivu e34a488617 Phone, foldable, tablet, laptop and 55" touchscreen: the new UI checked upright and on its side.
tests/playwright/specs/viewport-matrix.e2e.js opens WeKan as an iPhone 15,
an unfolded Galaxy Z Fold 7, an iPad Pro 11, a 1366x768 laptop and a 55"
4K touch panel, each upright and on its side, and checks the board header,
board menu -> Original positions, Problems -> Memory Usage and Disk usage,
the Table, Calendar Mode, Time, Timeline and Map views, and dragscroll by
mouse or by finger. It measures that icons and texts are on screen and not
drawn in the colour behind them, that the page never scrolls sideways, that
every column can be reached and stays readable; screenshots of each step go
to .tools/log/viewports/. What it found, fixed:

- iPhone: the header bar was width: 100% plus its side padding as a content
  box, 24px wider than the screen, and the sidebar hamburger sat past the
  right edge. It measures its padding inside now.
- Admin Panel left menu on a phone: the list's disc bullets showed as a dot
  after every entry, and a rule between groups shrank to a dash beside an
  entry. No bullets, and each group starts a row of its own.
- Admin tables: 13 equal columns were 25px each on a phone and 42px on an
  upright iPad, every header one letter per line. Up to 1024px a column keeps
  a readable floor and the table scrolls sideways in its wrapper.
- Table view: the title-wrap and swimlane-group toggles were white icons on
  white - two empty squares - stretched to 72px by the global button padding.
  And on an upright iPad or foldable, Members, Labels and the date columns lay
  past the edge of an overflow-hidden board with nothing to scroll; the view
  scrolls sideways itself now.

A swipe that starts beside a list's resize handle resizes the list, as it
should, so the dragscroll check starts where a person pans. The file runs in
Chromium only: device emulation and the touch swipe exist only there.

Thanks to xet7 !
2026-10-17 04:05:49 +03:00
Lauri Ojansivu 0b6942687f diskMonitor: name the watched path, not an archive entry.
fixedVulnerabilityClasses reads entry.path as an archive entry and asks for
a containment check; the disk monitor's loop variable was only named that.

Thanks to xet7 !
2026-10-17 01:35:19 +03:00
Lauri Ojansivu f819932e60 CHANGELOG: Memory and Disk usage, Original positions, board view fixes, shared code.
Thanks to xet7 !
2026-10-17 01:24:22 +03:00
Lauri Ojansivu b9e28a8fbb Original positions: a right-sidebar view from the board menu, in every language.
Where each swimlane, list and card was put when it was made has been
recorded on creation for a long time, and shown by nothing: the two
templates for it were drawn nowhere, every string was English and they
printed raw ids, and the client helpers beside them were never called.

Board menu → Original positions (after Archive, open to everyone who sees
the board) opens originalPositionsSidebar: Swimlanes, Lists and Cards, each
by the name it has now, Moved when it is no longer where it was put, where
it was put by the names those have now, the title it was made with when that
changed, and when. The server already narrows the history to what the
reader may see, assigned-only roles included.

The text is existing translation keys plus one new one, original-positions,
translated into every language. models/lib/originalPositions.js builds the
groups and is tested without Meteor; the old templates, the per-item
component and the uncalled helpers are removed, and the design page is now
the feature's guide.

Thanks to xet7 !
2026-10-17 01:23:15 +03:00
Lauri Ojansivu b6a3d98736 Admin Panel / Problems: Memory Usage and Disk usage reports.
The RAM and Disk usage pages were designs that no code carried out. Both
monitors now run beside the CPU one and, like it, record only the start and
the end of a sustained high-usage period:

- Memory: RAM from /proc/meminfo's MemAvailable - os.freemem() is MemFree,
  which counts the page cache as used and shows a healthy server as full -
  and swap, which alone also starts a period. Without /proc/meminfo it uses
  what Node reports. Settings WEKAN_RAM_* and WEKAN_SWAP_HIGH_PERCENT.
- Disk: each filesystem WeKan writes to - the files root, its temp directory
  for uploads and the snap's data directory - measured once per device, with
  an episode of its own. WEKAN_DISK_WATCH_PATHS replaces the paths.
- Each report has a live line above its rows and a URL
  (/admin/problems/ram, /admin/problems/disk). The menu and titles use the
  existing memory-usage and sandstorm-disk-usage keys, which every language
  translates, and the Summary names the cpu, ram and disk streams.

Observe and report only, as designed. The three monitors share one event
recorder (server/lib/resourceLog.js) and one intEnv (models/lib/intEnv.js),
which three files had each defined. The pure halves are tested without
Meteor, and resourceMonitorsWiring checks the wiring and that no copy of
either shared piece returns.

Thanks to xet7 !
2026-10-17 01:14:16 +03:00
Lauri Ojansivu c046afa807 Guides for the Table, Calendar Mode, Time, Timeline and Map views.
Each was described only in Board Settings / Board View. Their guides are
written from the templates and the server code: what each shows, every
control, what can be changed and who may change it, where its settings
live, and the files and tests behind it. Board-Header/README.md links them,
boardViewSettings fails when a Board View entry has no guide of its own, and
TODO Later loses the gap and the code-to-combine item that is done.

Thanks to xet7 !
2026-10-17 01:02:44 +03:00
Lauri Ojansivu 0fff9d4828 Board view fixes, and failed calls reported in the reader's language.
- Calendar, Calendar Mode and Multi Board Calendar showed archived cards
  once the Archive sidebar had loaded them; every calendar selector leaves
  them out now.
- Timeline offered "Restore to this state" to every viewer, and a refused
  restore vanished, since nothing awaited it. The button is shown to those
  who may change the card, and a refusal is reported.
- Table: a swimlane header row spanned a fixed 11 columns, one short with
  all four date columns and too wide on a phone; it spans the columns the
  table has. Its add-card error was an English sentence.
- Map showed the raw "upload-failed" for a failed upload; it is translated.
- client/lib/errorMessage.js turns a failed call into a message in the
  reader's language: the translation when the error code is a translation
  key, otherwise the translated "Something went wrong" with the server's
  reason after it. 25 alert() calls with their own English fallback use it.

tests/errorMessageI18n.test.cjs runs the helper and fails on an alert()
that falls back to English again; calendarFilter tests the archived cards
for all four selectors.

Thanks to xet7 !
2026-10-17 01:01:34 +03:00
Lauri Ojansivu 483f3cd538 The file integrity scan waits for a quiet CPU again.
fileIntegrityScan.js asked cpuMonitor for currentCpuPercent, which it never
exported, so cpuPercent() was always NaN: the hourly scan never skipped a
busy hour and every finding recorded cpuPercent NaN. It reads
getCurrentCpu().percent now.

Five files imported names their modules do not export - UserAvatar,
AttachmentStorage and Settings as a named import - none used; they are
removed. tests/namedImportsExist.test.cjs checks every named import and
destructured require of an app module against what that module exports.

Thanks to xet7 !
2026-10-17 00:56:00 +03:00
Lauri Ojansivu 5908fab5de The Calendar and Multi Board Calendar views share their calendar options.
Both views wrote out the same FullCalendar options - first day of the week
and the week numbers that follow it, time formats, the one-row toolbar,
translated button labels, height, locale and direction - about fifty lines
each. models/lib/calendarBaseOptions.js builds them once, given the
translation, the user's first day and the display options, and each view
adds only what differs: dragging and selecting, navLinks, business hours
and its events.

The tests that read the toolbar or the direction from boardBody.js read the
shared module, and the Multi Board Calendar runtime test runs it for real.

Thanks to xet7 !
2026-10-17 00:54:02 +03:00
Lauri Ojansivu d444a76af2 One list drag-and-drop setup for a swimlane and the Lists view.
Template.swimlane and Template.listsGroup each built the same jQuery UI
sortable for their row of lists - options, placeholder sizing, dragscroll
pause and the autorun that follows the drag-handle and permission changes -
two copies of about fifty lines. initListsSortable builds it once and, as
listsGroup already did, replaces a sortable already on the element.

The guards that counted three list sortables count the shared one and check
that both templates call it; each is still gated by canModifyBoard().

Thanks to xet7 !
2026-10-17 00:51:54 +03:00
Lauri Ojansivu 9a73bf9173 Describe each card date once for its card and minicard badge.
Received, Start, Due and End were each written twice in cardDate.js, once
for the opened card (card*Date) and once for the minicard (minicard*Date):
the same field, the same colouring, the same title and the same editor,
eight copies of about 40 lines. CARD_DATE_KINDS describes each date once
and both templates are built from it; only the due date counts down, as
before. cardDate.js goes from 596 to 410 lines.

The tests that matched each template's own helpers block read the
CARD_DATE_KINDS entry instead, and still fail if Received, Start or End
gains the countdown.

Thanks to xet7 !
2026-10-17 00:50:41 +03:00
Lauri Ojansivu dd9832ce7e CHANGELOG: TODO Later lists what is built but unused, designed, undocumented or to combine.
Thanks to xet7 !
2026-10-17 00:48:11 +03:00
Lauri Ojansivu 0d3d0a407b CHANGELOG: cloning, visitor Scrum views, removed duplicate code, docs examples.
Thanks to xet7 !
2026-10-17 00:25:51 +03:00
Lauri Ojansivu a6aa3fdfb7 One state helper for the board's organization and team pickers.
The members widget and the add/remove organization and team popups of the
Right sidebar each built the same onCreated state by hand - error and
loading flags, a paged subscription and setError/setLoading/isLoading - five
copies of about 25 lines. initGroupPickerState builds it once; each picker
keeps the publications it subscribed to before.

Thanks to xet7 !
2026-10-17 00:24:59 +03:00
Lauri Ojansivu 73e815b4f4 Share the fixed list width helpers between the list and its header.
#5729's fixed (same) width for all lists is read by list.js and turned on
from the list header menu in listHeader.js, and each carried the same five
localStorage/profile helpers. They are client/lib/fixedListWidth.js now,
with the same storage keys, so a viewer's saved choice still applies.
tests/fixedListWidthShared.test.cjs fails if a client file grows its own
copy again.

Thanks to xet7 !
2026-10-17 00:23:19 +03:00
Lauri Ojansivu a896f3ed8c A visitor's Scrum board view opens as chosen, not as Swimlanes.
Without an account the browser keeps the board view, and client/lib/utils.js
had two hand-written copies of the view list for it: setBoardView stored
any of them, but storedBoardView's copy lacked Product Backlog, Sprints,
Sprint Report and Velocity, so those opened as Swimlanes after the reload.
Both now ask isKnownBoardView, i.e. BOARD_VIEWS, which removes the two
lists (about a hundred lines) and the drift with them.

The guards that counted view names in utils.js check the isKnownBoardView
calls instead, and boardViewMenu fails if a hand-written list returns.

Thanks to xet7 !
2026-10-17 00:20:32 +03:00
Lauri Ojansivu 7dc5399dc7 Remove code that a second copy replaced, and templates nothing draws.
- models/cards.js defined assignMember ... toggleAssigner twice in one
  Cards.helpers({}); in an object literal the last key wins, so the first
  twelve methods (about a hundred lines, with their own linked-board
  branches) never ran. models/users.js had two copies of the swimlane-height
  storage helpers, and the header's event map an empty
  'click .js-open-bookmarks' the real handler replaced. Each first copy is
  removed; what runs is unchanged.
- An Admin Panel attachment settings pane: attachmentSettings.jade (four
  templates) and about 300 lines of settingBody.js for S3, transfer limits
  and storage paths. No menu opened it and no template drew it; Admin Panel /
  Attachments (attachments.js) is the one in use.
- Templates drawn by nothing: dateBadge, dateCustomField and
  cardCustomFieldDate with its JS, orgAvatar and teamAvatar, the three
  *ModalTitle templates of My Cards, Due Cards and Global Search, boardLists,
  boardCreateRulePopup, addMemberPopupTest, and subtasksItems with the two
  templates only it used.
- CSS repeated word for word: accessibility.css opened with a copy of
  myCards.css's first 74 lines; list.css repeated its 768px collapsed-list
  block at 1024px and 1200px, which 768px already covers; boardHeader.css
  repeated its .mobile-mode rules inside a 801px media query. The later
  copies decided nothing, so the result is the same.

tests/noDuplicateObjectKeys.test.cjs parses every app and package file and
fails on an object literal that defines a key twice, and
tests/templatesAreRendered.test.cjs fails on a template nothing draws,
apart from runtime-built names and three listed exceptions. Tests that read
the removed copies now read the code that runs.

Thanks to xet7 !
2026-10-17 00:19:00 +03:00
Lauri Ojansivu 7e29641111 Await the member mapping in cloneBoard.
getMembersToMap (models/wekanmapper.js) is async, and cloneBoard kept its
Promise and called membersMapping.forEach on it, so every clone through
that method stopped with "membersMapping.forEach is not a function". The
board menu clones with copyBoard, so this was the cloneBoard method only.

WekanCreator carried an identical getMembersToMap that nothing called; it
is removed, leaving the one in wekanmapper.js.

tests/asyncResultAwaited.test.cjs resolves, file by file, every variable
assigned from an async function - declared there or imported async - and
fails when it is then used as an array or object without await.

Thanks to xet7 !
2026-10-16 23:54:39 +03:00
Lauri Ojansivu 11babe27f2 Docs: snap keys, setting guide and Docker examples that match this WeKan.
- Three snap set lines named keys the snap does not have, which snapd
  stores without a word: MONGO_OPLOG_URL (the key is mongo-oplog-url),
  oauth-enabled (oauth2-enabled) and "snap set wekan set disable-mongodb".
  tests/docsSnapKeysExist.test.cjs checks every snap set in the docs
  against the KEY_ names in snap-src/bin/config.
- Adding-new-Snap-settings-to-code.md used setting names that do not
  exist (LDAP_ENABLED, LDAP_SEARCH_FILTER, a wrong KEY_ line), the edge
  branch and Travis CI. It now follows the real config and wekan-help
  entries, says that a login setting goes through the Admin Panel catalog
  and authEnv(), and lists the other platform files a setting belongs in.
- The LDAP and Traefik Docker examples ran MongoDB 3.2/4.0 with
  --smallfiles, which MongoDB 4.2 removed, and WeKan images from 2018. They
  use mongo:7 and ghcr.io/wekan/wekan:latest; Traefik stays at v1.7, since
  that guide's labels and traefik.toml are v1 syntax.
- The Windows page no longer sets RICHER_CARD_EDITOR, which WeKan does not
  read, and points to build.bat for building from source.

Thanks to xet7 !
2026-10-16 23:52:46 +03:00
Lauri Ojansivu 2e3cff4d7d CHANGELOG: LockoutBleed follow-up, loaded server features, notification attachments, docs by menu.
Thanks to xet7 !
2026-10-16 23:46:42 +03:00
Lauri Ojansivu 262f9194fc Show notification attachments, and translate six labels that showed their key.
The notifications drawer subscribed to 'notificationsAttachments', while the
server publishes 'notificationAttachments'. Meteor reports nothing for an
unknown subscription, so it never became ready and an attachment named in a
notification was never sent to the drawer.

Six template keys did not exist in en.i18n.json, so the page showed the key
itself: the Due group of the sidebar filter ("dueAt"), the Scrum sprint form
("end", "no-cards", "custom-field"), the Jira estimate field on import, the
passkey trash icon ("remove") and two webhook URL fields ("URL"). Each now
uses the existing key with that wording, so every language that already
translates it shows it translated.

tests/subscriptionsArePublished.test.cjs checks every subscribe() in the
tree against every publication, and tests/templateI18nKeysExist.test.cjs
every literal key of every template against en.i18n.json.

Thanks to xet7 !
2026-10-16 23:45:58 +03:00
Lauri Ojansivu b7589737f5 File docs/Features the way the WeKan menus are.
A feature's guide now sits under the menu it is used from, read from the
templates rather than from where somebody once put it: Header-Bar,
All-Boards, Pages, Board-Header (search and every Board View), Board-Sidebar
(Board Settings, Filter, Members, Labels, Archive), Rules, Swimlanes, Lists,
Cards, Member-Settings, Admin-Panel and Login-Page. Topics used from several
menus (editor, history, theme, translations, email, troubleshooting) are in
General.

- Each area has a README mapping its menu entries to their guides and to the
  template or server module that implements them. Board-Header/README.md
  lists all 40 Board View entries with the template boardBody.jade renders;
  five views (Table, Calendar Mode, Time, Timeline, Map) have no guide of
  their own yet and say so.
- Board-View.md listed 25 views while BOARD_VIEWS has 40; it now lists all
  of them with their English labels.
- Pages keep their file names. Where a released changelog links an old path,
  a "# Moved" page stays there and points to the new one; test-menu.sh skips
  folders holding only those.
- Undo was filed under Login; it is General/Undo now.
- Every link in docs, code comments, scripts and tests follows the move;
  docsLinksResolve checks them all.

Tests that read a doc follow it to its new place. boardViewSettings now fails
when Board-View.md or Board-Header/README.md differs from BOARD_VIEWS, or
names a template boardBody.jade does not render.

Thanks to xet7 !
2026-10-16 23:45:35 +03:00
Lauri Ojansivu 70dcc2a134 Remove files nothing uses, and point their tests at the code that runs.
Found by following every import, re-export and require from client/main.js
and server/main.js (meteor.mainModule: only what that reaches is loaded) and
each local package's package.js:

- packages/wekan-fullcalendar/fullcalendar/ (88 files) and update.sh: the
  vendored FullCalendar v3 and the script that updated it as a git
  submodule; the package takes FullCalendar 5 from npm (Npm.depends).
- packages/wekan-fontawesome/fontawesome-free: js/, sprites/, metadata/,
  attribution.js and the .min.css copies; package.js adds only the plain CSS
  and the webfonts. Its package.json no longer names the removed files.
- models/lib/propagateMembers.js and models/lib/teamBoardMemberSync.js:
  replaced on 2026-10-14 by models/lib/groupBoardMembership.js (memberships
  that record their team or org, #4178), which the server uses. Their tests
  exercised the old modules; the same scenarios now run against planGain /
  groupChanges, and the wiring checks are kept.
- models/lib/recoveryPlan.js and its test: a model of the SQLite recovery
  policy that nothing ran; releases/ferretdb/sqlite-recovery.mjs carries it
  out and tests/sqliteVerifiedRecovery.test.cjs tests that. The Recovery
  design page says so.
- packages/wekan-ldap/server/configResolver.js: a vendored copy nothing
  imported - the LDAP package reads its settings through the app's
  accessor. Its sync test is now a check that no package carries such a
  copy; three comments that sent readers to it explain the reason instead.

docs/Design/TeamsOrganizations.md also said losing a team removes nothing;
since #4178 a team takes back what it gave.

Thanks to xet7 !
2026-10-16 22:54:48 +03:00
Lauri Ojansivu 3f3a61df0c Load three finished server features that were never imported.
package.json sets meteor.mainModule (since 2026-03-22), so the server runs
only what an import chain from server/main.js reaches. Three modules added
after that were never imported, so they never ran, while their tests - which
read the file - passed:

- server/loginTimingNormalization.js: the DDP `login` half of the LockoutBleed
  timing fix (GHSA-2g94-9x3m-hv37). Only the REST login was equalized, so the
  web login's response time still told whether a username exists.
- server/cardRecurrenceSchedule.js: the hourly job behind a card's
  Recurrence setting; the setting was saved and nothing ever recurred.
- server/productNameCache.js: the Admin Panel product name for the snap's
  pages shown while the database is down.

They are imported in server/imports.js beside their related modules. The
Meteor internals the login handler uses (Accounts._loginHandlers,
_checkPasswordAsync, _findUserByQuery) exist in this Meteor release.

tests/serverModulesLoaded.test.cjs follows the import chain from
server/main.js and fails when any server module that registers something at
load time - a startup hook, methods, publications, a cron job, a login
handler or a route - is not reached; it fails on the old imports.js.

Thanks to xet7 !
2026-10-16 22:35:18 +03:00
github-actions[bot] 615d0bb0b8 Bump versions for v12.49 2026-10-16 12:26:53 +00:00
Lauri Ojansivu 68f4e21f98 Prepare v12.49 release 2026-10-16 15:25:40 +03:00
Lauri Ojansivu b2705905e9 CHANGELOG: custom roles apply in every board view.
Thanks to xet7 !
2026-10-16 15:23:24 +03:00
Lauri Ojansivu 1020cd7e86 Custom roles apply in every board view: verified, tested and documented.
A custom role from Admin Panel -> People -> Roles reaches every board view
through the same two questions as a built-in role: what the member may do is
asked of the capability table, which answers from the custom role, and which
cards they see is asked of the member's flags, which the custom role writes.
fallbackRoleFor() picks only among built-in roles that see no more cards, so
a role whose "Sees all cards" is off always mirrors an assigned-only role and
one with it on never does. The flags are written when a member gets the
role, rewritten on every board when the role is saved, replaced with the
custom role by setMemberPermission, and kept when the role is deleted; no
other code writes them.

tests/boardRoleViews.test.cjs builds a member for all 32 capability
combinations as the server writes it and checks that the card scope, the
card rule, REST reads and writes, button rules and the real Email View
selector narrow them exactly when the role does not see all cards, and that
nothing sets a role flag without the custom role following.
server/lib/tests/customBoardRoles.tests.js turns "Sees all cards" off and on
for a role in use and checks its member becomes assigned-only and back
(meteor test; not run here). docs/Features/Members/Roles.md explains it in
"Custom roles in every board view".

Thanks to xet7 !
2026-10-16 15:23:16 +03:00
Lauri Ojansivu 7dbde2ea83 CHANGELOG: document how board roles apply to every board view.
Thanks to xet7 !
2026-10-16 15:19:33 +03:00
Lauri Ojansivu 63117b61d9 Docs: how board roles apply to every board view.
docs/Features/Members/Roles.md gains "Roles in every board view": a role is
the member's on the board, not the view's, so every view, the API and every
write ask the same code. A table names each of the 40 board views and the
server source it reads - the board publication, the table page, Scrum, board
status, activities, the Email and Chat views - and what an assigned-only
member gets from it; the board report charts are refused to them, as exports
are, because they are computed from every card. Then search, All Boards,
notifications, position and change history, exports, REST and writes, what
is recorded in Admin Panel -> Problems, and the tests that keep a new view
from skipping the rule. The old "Which cards they see" paragraph, which
named only the board publications, points to it.

Thanks to xet7 !
2026-10-16 15:19:26 +03:00
Lauri Ojansivu 486b2b3034 CHANGELOG: board role permissions apply to every board view (AssignedBleed follow-up).
Thanks to crypto-nidh, binary-lover and xet7 !
2026-10-16 13:04:34 +03:00
Lauri Ojansivu 5b2ed17e6b Board role permissions apply to every board view and every path to a card.
AssignedBleed follow-up, GHSA-r4gm-mjj9-g3x8. An assigned-only member
(isNormalAssignedOnly, isCommentAssignedOnly, isReadAssignedOnly, and custom
roles through those flags) sees only the cards assigned to them. The Board
Email View checked board.isVisibleBy() alone, so it published the title,
description and email metadata of every card in a list or label, counted
them, and searched them. It now merges assignedOnlyCardScope into its folder
and search selector for the page and the count, and a reply on another card
is refused.

An audit of every path that returns or changes a card found the same shape -
"a member of the board, so every card of it" - elsewhere, all fixed here:

- reads: the board publication's ancestor cards, linked source cards with
  their subtasks and dependency targets, and comment reactions; All Boards
  tile counts; a board's activities from linked boards; the Chat view and
  the All Boards feed, which also let card MEMBERS through (assignees is the
  rule); My Attachments' card; GET /api/user/cards; position history of a
  card; the cross-board subtask picker; Scrum rollover counts; and the
  notification publications, which kept sending the full documents of cards
  named in old notifications - also to members removed from the board;
- writes on a card the member cannot see: the canonical
  canEditCardOrLinkedCard / editableCardIds (every Cards.allow update,
  checklists, items, attachments, text notes, custom fields, the shared
  description, card methods) now ask the card - the role's `write` is a
  board answer, and the card is read when a caller passed it without
  assignees; DDP and REST comments, button rules on a card, checklist moves,
  issue state, watching, the card-opened webhook, moveBoardObjects,
  moveSwimlane and attachment moves;
- REST writes: checkBoardWriteAccess refuses assigned-only members, as
  checkBoardAccess already does for reads - those routes act on any card,
  list or swimlane by id. The GHSA-f396 canary still fires for a role
  without the write capability.

A refusal no view would produce is recorded under AssignedBleed in Admin
Panel -> Problems (authz.assigned, medium: never disables); opening a view,
searching and REST calls are narrowed or refused without a record.

tests/boardRoleViews.test.cjs runs the Email View selector, pins every path
above, and fails when any server file that serves card data has no role
rule and no stated exemption; the "assignee or card member" rule is
rejected anywhere in server/ and models/. server/lib/tests/editableCardIds
adds an assigned-only case for both edit checks (meteor test; not run here).
Seven guards that pinned the old lines now pin the new ones.

Thanks to crypto-nidh, binary-lover and xet7 !
2026-10-16 13:03:34 +03:00
github-actions[bot] 83e39fc8d9 Bump versions for v12.48 2026-10-15 20:30:17 +00:00
Lauri Ojansivu f89ce1950c Prepare v12.48 release 2026-10-15 23:28:46 +03:00
Lauri Ojansivu 303b4a09b0 CHANGELOG: release workflow hardening and the bandit findings, found by Qlty.
Thanks to Qlty and xet7 !
2026-10-15 23:20:15 +03:00
Lauri Ojansivu ee2a657977 Python: the five bandit findings Qlty reports.
- B324 docs/Features/Login/ldap-sync/ldap-sync.py: the MD5 of an LDAP photo
  only notices a changed photo, so it says usedforsecurity=False.
- B202 tests/nextcloud/bootstrap.py: the downloaded WeKan release zip is
  extracted only after every member is checked to land inside the target
  (zipfile also refuses '..' and absolute names; now it is explicit).
- B102 tests/backfill-charts-pagination.py: exec of this repository's own
  backfill-charts.sh planner under test, in an empty namespace with the
  network patched out - kept, and marked with its reason.
- B108 tests/release-telemetry.py: '/tmp/check-telemetry.py' is a fake
  __file__ for the path logic; nothing is written there - marked.
- B106 tests/ucsRelease.test.py: the fake portal's expected value is a test
  fixture, passed through a variable rather than as a password literal.

bandit reports none of the five now; the three tests pass.

Thanks to Qlty and xet7 !
2026-10-15 23:19:57 +03:00
Lauri Ojansivu 7f7cc63cde GitHub Actions: no expressions in scripts, actions pinned, least privilege.
Qlty (zizmor) security findings in the workflows that build and publish
every release:

- template-injection (128): ${{ ... }} pasted into a run: script is expanded
  before the shell sees it, so a crafted workflow_dispatch input, tag or step
  output becomes shell code. 251 expressions in 13 workflows now reach their
  scripts through the step's env: and are used as quoted shell variables
  ($env:NAME in the PowerShell steps); ${{ env.X }} is plain $X. Where a
  value sat in single quotes it is double-quoted now, and six unquoted ones
  are quoted.
- unpinned-uses (20 in Qlty, 160 with current zizmor): every action is
  pinned to a commit, with its version as a comment for Dependabot.
  actions/dependency-review-action@v5 was a branch; it is v5.0.0.
- excessive-permissions (8): AppImage, Flatpak, mac, windows,
  release-all and release-all-missing grant contents: read at the workflow
  level, and each job that writes (a release asset, a git push, the docker
  push, gh run rerun) is granted that on the job. Nothing used actions:
  write except recover, which keeps it.
- artipacked (47 -> 13): actions/checkout gets persist-credentials: false,
  except in the jobs that push with the checkout's credentials, directly or
  through releases/version.sh, release-website.sh, release-charts.sh and
  backfill-charts.sh.

actionlint with shellcheck reports 50 notes, the 56 before minus the six
quoted. Every PowerShell step parses (pwsh 7.6.7). Not run on GitHub.

tests/workflowHardening.test.cjs fails on an expression in a run script,
an unpinned action, a workflow-level write grant in a multi-job workflow,
or a checkout keeping credentials outside the pushing jobs, and runs
zizmor when it is installed. Eleven guards that pinned the old text, or ran
the extracted scripts with the expressions substituted, now pin the env
form and pass the values as the step's environment.

Thanks to Qlty and xet7 !
2026-10-15 23:19:50 +03:00
Lauri Ojansivu 4ea6f5b90c CHANGELOG: AssignedBleed follow-up, assigned-only members' file and document reads.
Thanks to crypto-nidh and xet7 !
2026-10-15 22:42:40 +03:00
Lauri Ojansivu 7f3428db8e Keep assigned-only members to their own cards' files on every read path.
AssignedBleed follow-up, GHSA-3cgr-47wm-9cc9. An assigned-only member
(isNormalAssignedOnly, isCommentAssignedOnly, isReadAssignedOnly) sees only
the cards assigned to them. 1a218ead02 applied that to the attachment API,
but six other paths asked only for board membership, which such a member
has:

- the /cdn/storage/attachments and /cfs/files/attachments downloads
  (universalFileServer isAuthorizedForBoard) served any card's file;
- Attachments.protected, ostrio:files' own download check, likewise;
- the legacy /cfs/files/attachments route likewise;
- searchAttachmentDocumentText matched the extracted text of every card's
  PDF/DOCX/XLSX/PPTX on the board and returned snippets with their
  attachment and card ids - no id needed, and it supplied the ids for the
  downloads;
- legacyBoardAttachments without a cardId published every legacy
  attachment record of the board;
- linkedListCards published every card of the source list.

The downloads now also ask mayReadBoardAttachment(), which names the
refusing path in Admin Panel -> Problems (authz.assigned, medium: never
disables). The document search and the legacy publication are narrowed to
the member's cards (assignedOnlyCardIds); asking the publication for a card
not assigned to them is refused and recorded, as card details always pass
the open card. Linked-list cards take the source board's
assignedOnlyCardScope; searching and viewing a linked list are ordinary
use, so those are not recorded.

The board's own files - background (sidebar or API upload) and map image -
carry no card and are part of the board every member sees; they stay
readable, where the attachment API refused them to an assigned-only
member. Any other card-less file is still refused.

tests/assignedOnlyReads.test.cjs pins each path, the board-file rule and
the refusal source, and fails when any file in server/ or models/ that
serves attachment or document data lacks the rule without a stated
exemption. The Playwright spec checks both downloads refuse the hidden
card's file but not the member's own, that the search finds only the
member's card, and that the owner is unaffected. Four source guards that
pinned the old lines now pin the new ones.

Thanks to crypto-nidh and xet7 !
2026-10-15 22:42:14 +03:00
Lauri Ojansivu ceec2e9c5a CHANGELOG: Caddyfile examples are Caddy 2, and the snap's Caddy proxies to WeKan's port.
Thanks to Group-Deed and xet7 !
2026-10-15 22:06:02 +03:00
Lauri Ojansivu 37d2397603 Caddyfile examples are Caddy 2, and the snap's Caddy proxies to where WeKan listens.
The snap runs Caddy 2, and migrate-caddyfile-v1-to-v2.sh treats a Caddyfile
that `caddy adapt` rejects as Caddy v1: it moves it to Caddyfile.v1.bak-<n>
and, when it cannot convert it, writes the default in its place - on every
start. 19 Caddyfile examples in the docs were Caddy v1 (`proxy / localhost:3001
{ websocket transparent }`, `browse`, `fastcgi`, `tls self_signed`, `ext`,
v1 `log`/`errors`, `root /path`), so a file copied from the snap install page
was renamed each time it was put back. All are now Caddy 2 (reverse_proxy,
file_server browse, php_fastcgi, tls internal, try_files, log output) and pass
`caddy adapt` with Caddy 2.11.7. Also:

- `http://X https://X { tls ... }` became `X {`: Caddy 2 refuses TLS on the
  http address and redirects to https by itself;
- the Windows examples `load` the certs folder - Caddy 2's load takes folders;
- the Docker Meteor3 Caddyfile gave the Let's Encrypt and CloudFlare variants
  of one site the same host, which Caddy 2 refuses as ambiguous; each has its
  own host now;
- Caddy.md's logs example and its systemd unit, installer and setcap are
  Caddy 2 (`caddy run --config`, no getcaddy.com, no -conf/-agree/-email).

The fallback template the migration writes proxied to localhost:3000. With
Caddy enabled the snap runs WeKan on caddy-bind-port (default 3001), so a
migrated site pointed at nothing. Both templates now read the snap settings
from caddy-control's environment - `:{$CADDY_PORT:8080}` and `reverse_proxy
localhost:{$CADDY_BIND_PORT:3001}` - so `snap set wekan port=...` reaches
Caddy on the restart the configure hook already does; that hook's sed lines
only ever matched Caddy v1 lines. The commented TLS example in the template
had `protocols h1` inside a site and a stray `}`, so uncommenting it failed;
it uses `alpn http/1.1` now.

tests/caddyfileExamplesV2.test.cjs finds every Caddyfile in the docs and the
snap, fails on Caddy v1 syntax and on a Caddy 1 command line, checks the
templates' ports, and with a Caddy 2 binary runs `caddy adapt` on all 30.
snapStagePackages.test.cjs pinned the wrong port 3000 and now pins 3001.

Thanks to Group-Deed and xet7 !
2026-10-15 22:05:49 +03:00
Lauri Ojansivu 43be0345e6 CHANGELOG: LDAP background sync imports users and keeps its interval.
Thanks to tromlet and xet7 !
2026-10-15 20:46:03 +03:00
Lauri Ojansivu a944be1817 LDAP background sync imports users again, and keeps the interval it is given.
After the 12.47 fix, #6771 comment 6197931378 showed the background sync
running and importing nobody: "filter": "(&(&(objectclass=person))(uid=\2a))",
"Search result count 0". importNewUsers() asked for every user with the string
'*', and the RFC 4515 escaping added for login names on 2026-09-27
(679a8b3499) turned the wildcard into a literal asterisk. The wildcard is now
reachable only through ALL_LDAP_USERS (userSearchValue.js), a Symbol that a
login request cannot carry, so a typed '*' is still escaped.

The sync also ran far more often, or less often, than set: later.js is a
calendar, and every(n).minute() is "minutes 0, n, 2n ... of each hour", an
interval only when n divides 60. 2 hours and 1 day both became hourly, 90 s
became every minute, and 3600 (seconds, typed as milliseconds) every 4
seconds. Milliseconds now become an interval later keeps exactly - seconds
and minutes dividing 60, hours dividing 24, whole days - pinned to the start
of the unit; anything else is rounded to the nearest one with a warning, and
an interval under a minute is warned about as probably seconds.

tests/ldapImportAllUsers.test.cjs reproduces the reported filter and checks
the wildcard, the still-escaped typed '*', and that no code searches the
string '*'. tests/ldapSyncSchedule.test.cjs checks every interval up to 36
hours reaches later only as an every(n) it keeps, and with the real later.js
that 1 min ... 1 week keep their spacing.

Thanks to tromlet and xet7 !
2026-10-15 20:45:49 +03:00
Lauri Ojansivu bd7f9d2848 Merge remote-tracking branch 'origin/main' 2026-10-15 16:39:50 +03:00
Lauri Ojansivu 62166680f1 CHANGELOG: Windows and NetBSD bundle builds.
Thanks to xet7 !
2026-10-15 16:37:36 +03:00
Lauri Ojansivu fa62479245 NetBSD bundle: node-gyp finds pkgsrc's Python.
The NetBSD x64 bundle of v12.47 stopped in npm install: argon2's
native build ran node-gyp, which looks for `python3` or `python`, and
pkgsrc's python313 installs only /usr/pkg/bin/python3.13 - "You need to
install the latest version of Python". OpenBSD's package provides
python3, so only NetBSD failed.

releases/bsd/build-bundle.sh now, when neither is on PATH and PYTHON is
not set, hands node-gyp the versioned interpreter it finds
(/usr/pkg/bin/python3.N, skipping python3.N-config) through PYTHON and
npm_config_python, before the npm install; with no Python at all it
stops with an ::error:: naming the package. Simulated locally with a
pkgsrc-shaped directory and no python3 on PATH; with python3 present
nothing changes. tests/releaseScriptsUtf8.test.cjs pins the lookup and
its place before the install.

Thanks to xet7 !
2026-10-15 16:37:18 +03:00
Lauri Ojansivu 11f97d016e Windows builds: the release scripts read and write text as UTF-8.
All three Windows bundle builds of v12.47 stopped at the risk audit:
"Automated risk indicators found: server/lib/localizeAvatar.js: new URL
origin http://127.0.0.1". The file had not changed. risk-audit.py read
its baseline with a bare read_text(), which on a Windows runner decodes
cp1252, so the baseline's "http://127.0.0.1/…" (a comment in that file)
became "http://127.0.0.1/…" and no longer matched the source. With a
non-UTF-8 default on Linux (LC_ALL=C, -X utf8=0) the old script misread
the baseline the same way and counted all 16688 files as changed; the
fixed one reads it as the build does.

Every text read and write in releases/*.py now names UTF-8: the risk
audit, the telemetry check, remote-release.py (which also reads and
rewrites CHANGELOG.md, with its ®), the dependency, UCS, Nextcloud, Bun,
Mac and Snap scripts, and the GITHUB_ENV/GITHUB_OUTPUT writes.
tests/releaseScriptsUtf8.test.cjs fails on any read_text(), write_text()
or text-mode open() in releases/ without an encoding, multi-line calls
included; tests/bun-release.py follows the checksum write's new
argument. Every Python and node test of these scripts passes.

Thanks to xet7 !
2026-10-15 16:37:18 +03:00
github-actions[bot] 15f1811c97 Bump versions for v12.47 2026-10-15 13:12:13 +00:00
Lauri Ojansivu d390aba29c Prepare v12.47 release 2026-10-15 16:11:02 +03:00
Lauri Ojansivu 83c2ca9985 Three guards follow the restore and 6.09 test changes.
- sidebarArchives.js imports its own template: cardDetails.js now
  imports it for restoreArchivedCard, and a file that registers on a
  template it does not import may run before the template exists
  (clientBundleImports).
- The 6.09 restore spec makes its test ObjectIds with crypto.randomBytes,
  not Math.random (codeqlInsecureRandomness).
- Tigre's new restore strings say ወረቀት ካርድ for card and ምዱድ for board,
  the file's own words, not the bare Tigrinya ካርድ and ሰሌዳ.

Thanks to xet7 !
2026-10-15 13:29:53 +03:00
Lauri Ojansivu ee9ff3bedb CHANGELOG: Restore from Archive into a full list (#6772).
Thanks to jullbo and xet7 !
2026-10-15 13:16:22 +03:00
Lauri Ojansivu b9edbcc858 Restore from Archive into a full list asks for another list, instead of doing nothing.
Restoring an archived card whose list had reached a hard WIP limit did
nothing at all: the Restore handlers checked canBeRestored() and, when it
said no, returned without a word or a method call, and Restore all
skipped such cards just as quietly.

- Restore, in the Archive sidebar and in an open archived card, now goes
  through one function (restoreArchivedCard): a list that is full - or
  gone - opens the restore-to-list popup, which says the list has reached
  its WIP limit and offers only lists with room; with none left it says
  so instead of offering a list that would fail again.
- Restore all restores every card that has room and then says how many
  it left out and why. Its popup opens against the link kept before the
  loop: after the awaits the browser has cleared evt.currentTarget.
- canBeRestored() decides with models/lib/wipRestore.js, which also
  blocks a list that is OVER its hard limit: the old `===` let a card
  into a list whose limit had been lowered below its count. A soft limit
  still only warns.

Tests: tests/wipRestore.test.cjs (the rule, over-limit and soft/disabled
negatives, both Restore paths, Restore all's count, and a scan that no
client code skips a blocked restore without a word);
restore-wip-limit.e2e.js against the production bundle (restore into
another list with the full one not offered, Restore all restoring what
has room and naming the rest, a soft limit restoring at once), 12 of 12
over three repeats with the existing archive restore test. The four new
strings are translated in every locale.

Thanks to jullbo and xet7 !

Fixes #6772.
2026-10-15 13:16:14 +03:00
Lauri Ojansivu 9e1d5f65bd LDAP: the sync interval crash on Snap 12.46 is fixed (#6771).
"[uncaughtException] WeKan is stopping: TypeError: string.toLowerCase is
not a function" from later.parse.text, called by wekan-ldap's sync
schedule, with the snap restarting in a loop - the report on Alma Linux
8.10 with LDAP sign-in. A numeric LDAP_BACKGROUND_SYNC_INTERVAL reached
later's text parser as a Number; 7dfd942ff6 reads milliseconds or
later.js text and never throws, and tests/ldapSyncSchedule.test.cjs
reproduces this exact error with the real later.js.

Thanks to tromlet and xet7 !

Fixes #6771.
2026-10-15 12:54:31 +03:00
Lauri Ojansivu 025630b552 Tests: a board restored from WeKan 6.09 loads.
The upgrade report (one board with attachments loading forever after a
6.09 to 10.85 dump and restore) came without the board's data. The
fixture tests/fixtures/restored/wekan-6.09-board.json is that board's
SHAPE instead, derived from `git show v6.09:models/...`: CollectionFS
attachments (cfs.attachments.filerecord with its GridFS file and chunk),
a card cover pointing at a filerecord, lists with swimlaneId '', members
without the newer role flags, customFields by boardIds, checklists,
comments and activities as 6.09 wrote them.

restored-609-board.e2e.js restores it into the test database as it is
and requires the board to load: the board publication becomes ready with
the board and its cards, and the page shows its lists and cards, not the
spinner or the "could not be loaded" message. It passes against the
production bundle - and also against a bundle built without the client
cover fix, so neither the 6.09 data shape nor that fault is what hung the
reported board; what did is particular to that board's data, which the
board's page now names through the server log.

Thanks to xet7 !
2026-10-15 04:34:03 +03:00
Lauri Ojansivu defb110b18 CHANGELOG: search results of the search typed; browser specs on the bundle.
Thanks to xet7 !
2026-10-15 04:14:21 +03:00
Lauri Ojansivu 49344f32f4 Email view and Public Boards: a search shows its own results, never the last one's.
Found running the Email view's browser test against the production
bundle: once in eight runs, searching for text that matches nothing still
listed the previous search's card. Both views subscribe in an autorun, so
a new search's subscription starts before the old one stops, and both
published their page's ids into ONE shared index document; DDP's merge
box shows the older subscription's value while both exist, so whenever
the old one stopped late the new search showed the old rows.

Each request now has its own index, keyed by its arguments
(models/lib/reportPageIndex.js reportPageKey), built the same way on the
server and the client (emailViewPageArgs normalizes the Email view's
folder, page and search), and each view reads only the index of the
request it made. Admin Panel / Problems stops its subscription before the
next one, so its requests never overlap and keep a fixed id.

Tests: tests/reportPageKey.test.cjs (one id per request, server and
client agree, no autorun view reads a shared id); emailView and
publicBoardsPage pin the keyed ids. Against a rebuilt bundle the Email
view test passed 8 of 8, and with the Public Boards, Chat, Version
Control and import specs 30 of 30.

Thanks to xet7 !
2026-10-15 04:14:10 +03:00
Lauri Ojansivu 160110d1b3 Tests: the Email and Chat browser specs run against the production bundle.
Run against a freshly built bundle, as build.sh's browser run does, two
faults in the spec itself showed:

- Email view: Compose sends real mail, but the spec started no mail sink,
  so Send answered "The email could not be sent" in any run without an
  SMTP server. It catches the mail locally now, as the other mail specs do
  (helpers/smtpSink.js on WEKAN_TEST_SMTP_PORT), and checks that the
  message reached the recipient under its subject.
- Chat view: after page.reload() the new connection signs in again from
  the login cookie, and openView() called setBoardView before that, so on
  a fast server it failed with "Must be logged in". It waits for the
  sign-in, and a refused call names its code and reason instead of the
  minified "r".

Against the bundle: the Email, Chat, Version Control attachments and
import Attachment files specs pass (Chat 3 of 3 repeated, Email 5 of 5).

Thanks to xet7 !
2026-10-15 03:59:46 +03:00
Lauri Ojansivu 66b0952687 CHANGELOG: LDAP sync interval, attachment rename.
Thanks to B2RN and xet7 !
2026-10-15 03:28:19 +03:00
Lauri Ojansivu 515e63d322 Attachments: renaming one without extensionWithDot keeps its extension.
Running the new browser tests showed it: the rename popup appended
this.extensionWithDot, which a migrated attachment can lack, so the file
became "architectureundefined". It falls back to the extension now.

The browser tests were run against a local source-mode server: the
Version Control attachments spec (which now asks for the storage paths
as an administrator, for that call only) and the import Attachment files
spec pass; the Chat channels spec expects no unread message once the card
was opened in the middle, which reads it.

Thanks to xet7 !
2026-10-15 03:17:49 +03:00
Lauri Ojansivu 7dfd942ff6 LDAP: a sync interval in milliseconds no longer stops WeKan.
"[uncaughtException] WeKan is stopping: TypeError: string.toLowerCase is
not a function at later.parse.text ... at Object.schedule
(wekan-ldap/server/sync.js)", in a snap restart loop. The docs give
LDAP_BACKGROUND_SYNC_INTERVAL in milliseconds, settings_get() turns a
numeric value into a Number, and the schedule handed it straight to
later's text parser, which threw from a timer. Text later could not read
did not throw, but made a schedule that never ran - as a quoted
'every 1 mins' from a compose file did, the second report in #2354.

packages/wekan-ldap/server/syncSchedule.js reads the setting and never
throws: milliseconds (a Number or digits) become that interval, later.js
text is read as before, one pair of surrounding quotes is removed, unset
keeps the original default (recur().on(0).minute()), and anything
unreadable logs why and uses the default. The docs, the snap description
and the compose files say both forms work, instead of "leave this unset
so it does not crash".

Tests: tests/ldapSyncSchedule.test.cjs reproduces the crash with the real
later.js from the build, schedules 3600000 ms an hour apart and 10000 ms
ten seconds apart, reads quoted text, keeps the default, never throws on
any value a setting can hold, and fails if any server file hands a setting
to parser.text directly.

Thanks to B2RN and xet7 !

Fixes #2354.
2026-10-15 03:17:49 +03:00
Lauri Ojansivu fae5886319 CHANGELOG: every node suite passes again.
Thanks to xet7 !
2026-10-15 02:58:53 +03:00
Lauri Ojansivu 75534b5f1a Translations: the last thirteen failing guards, fixed on both sides.
The guards that failed before this release's translation work, each
decided by which side is right:

- The locale was wrong: Bislama's email recovery strings say sanem, the
  word the guard pins; Arabic-script Uzbek writes "e" as ې (26 words);
  Romansh says "chaschuna ina perdita da datas", names its own Menu and
  uses one word for the template container; Tigre's card is ወረቀት ካርድ,
  never the bare Tigrinya ካርድ (six strings); Wolaytta's permanent-delete
  label still held English words, now in the file's own Wolaytta; and
  Tamazight's operator-assignee is the portable search keyword again -
  the 2026-10-13 label sweep had translated a parser keyword, so that
  correction is removed and the earlier one stands.
- The guard was stale: Acehnese settled on kad for card on 2026-10-14,
  so the example cards use the file's own word; Uzbek key names may be
  translated as long as the printed label stays beside them; the README's
  coverage count is 234 now; the Transifex pull test hands its own node to
  the script it runs.
- The last English values of nine locales are their own words or settled
  terms (Neapolitan and Volapük "No", Frisian and Flemish "is", Breton
  "Me", Walloon "Pause", Venetian "Sprint", Veps "minus"), recorded as
  reviewed shared words, so the completeness guards and --missing agree:
  0 languages with untranslated strings.

Thanks to xet7 !
2026-10-15 02:58:42 +03:00
Lauri Ojansivu ccc65e7d89 CHANGELOG: every language translated; TODO Later is empty.
Thanks to xet7 !
2026-10-15 02:22:55 +03:00
Lauri Ojansivu d085a30f2d Translations: the last English strings of 43 locales.
Keyboard key names, Blockly's math names, Scrum roles and the S3 and
webhook labels, which the earlier passes had kept in English: the
established locales translate them (Finnish Välilyönti, German
Leertaste), so these do too. Breton keeps the reviewed "Me", Walloon the
reviewed "Pause", Venetian its "Sprint", and Veps writes minus without
the Finnish long vowel. Low confidence for the key names of Nahuatl,
Buryat, Cherokee, Klingon, Tibetan, Dzongkha, Wolaytta and Tigre.

What stays equal to English is the language's own word (Neapolitan
"No", Frisian "is", Volapük "if"), or in Blockly an operating system,
sin/cos/tan, and key labels the language's keyboards print.

Thanks to xet7 !
2026-10-15 02:22:25 +03:00
Lauri Ojansivu 7cbbec8836 Translations: the remaining strings of 12 more locales, Blockly included.
Greenlandic, Kashmiri, Nahuatl, Tigre, Tibetan, Dzongkha, Oromo,
Wolaytta, Northern Sami, Yakut, Inuktitut and Cherokee: every value still
equal to English, translated directly from each language's existing
wording, placeholders checked against English, only English placeholders
filled. Low confidence for Nahuatl, Wolaytta, Yakut, Inuktitut and
Cherokee; a speaker's Transifex translation replaces any of them.

Thanks to xet7 !
2026-10-15 02:14:29 +03:00
Lauri Ojansivu d698d6a1d6 Translations: the remaining strings of 20 more locales, Blockly included.
Translated directly from each language's existing wording, placeholders
checked against English, only English placeholders filled: Tamazight,
Volapük, Acehnese, Wolof, Fijian, Guarani, Veps, Quechua, Chichewa,
Northern Ndebele, Tigrinya, Samoan, Aymara (its last string, the S3
bucket, too), Fula, Bambara, Tswana, Kirundi, Swati, Chuvash and Buryat.
Low confidence for Volapük, Veps, Buryat and Tamazight; a speaker's
Transifex translation replaces any of them. The Veps guard records "ei
%1" for Blockly's "not": Veps negates with ei, as Finnish does.

Thanks to xet7 !
2026-10-15 02:01:26 +03:00
Lauri Ojansivu 7c6a94c734 Translations: the remaining strings of 50 locales, Blockly included.
Every value still equal to English in these locales, translated directly
from each language's existing wording and kanban terms, Blockly's
editor messages included; placeholders checked against English before
anything was written, and only English placeholders filled, so no human
translation is replaced.

All of Klingon, Ewe, Walloon, Luganda, Tongan, Tsonga, Venda, Kinyarwanda,
and the remaining strings of Bashkir, Pashto, Cantonese, Igbo, Xhosa,
Bhojpuri, Romansh, Maori, Breton, Sorani, Faroese, Northern Sotho,
Asturian, Amharic, Akan, Kyrgyz, Sicilian, Burmese, Hausa, Yoruba,
Frisian, Kurdish, Cornish, Uyghur, Aromanian, Sinhala, Sindhi, Manx,
Neapolitan, Assamese and their regional variants. Low confidence for
the constructed and smallest of them (Klingon, Venda, Tongan, Manx,
Cornish, Aromanian); a speaker's Transifex translation replaces any of
them. Walloon writes tangent as tandjinte, not the Waray-shared French.

Thanks to xet7 !
2026-10-15 01:45:28 +03:00
Lauri Ojansivu 0a58d618ce Tests: archiveSection allows the board subscription's onStop.
The board page passes an onStop callback since a failed load is shown;
the guard still pins that it asks for the live lists (false).

Thanks to xet7 !
2026-10-15 01:38:13 +03:00
Lauri Ojansivu 3c92954ab9 CHANGELOG: import attachment files, board load errors; TODO Later down to translations.
Thanks to xet7 !
2026-10-15 01:28:42 +03:00
Lauri Ojansivu 71b862c28c Tests: two guards follow the Chat view and the live-test log fixes.
customBoardRoles pinned Chat's comment check on the open board; the Chat
view opens a card of any board now, so it asks the card's board, and
the guard says so. liveTestsNoSecretLogs built a RegExp escaping only
`$`; it escapes every metacharacter (incompleteEscaping).

Thanks to xet7 !
2026-10-15 01:27:40 +03:00
Lauri Ojansivu f4126b9514 Translations: attachment files on the import page, a board that failed to load.
The five strings in every locale, translated directly from each
language's existing wording; English variants keep English. Bosnian
uses tabla for board and Tigre its established word for name, as their
guards require.

Thanks to xet7 !
2026-10-15 01:27:40 +03:00
Lauri Ojansivu 31615d7f98 Board: a failed load says so; 6.09 attachments no longer break covers.
An upgrade report: after a 6.09 to 10.85 dump and restore, one board with
attachments showed only the loading animation. The data is not
available, but the code had three faults on that path:

- The board page waited for its publication to be ready and had no
  onStop, so a publication that stopped with an error left the spinner
  up forever. It now shows "This board could not be loaded", pointing at
  the server log, which names the cause.
- The client cache fell back to the legacy (CollectionFS) attachment
  lookup, an async stub on the client: its Promise was taken for an
  attachment, so a 6.09 card's coverId became a cover whose link()
  threw, and the plural lookup handed callers a Promise for an array.
  The client no longer calls it; the server cache awaits the real one.
- The legacy attachments publication called startsWith() on a record's
  type, which throws when a 6.09 record holds something other than a
  string; it is read as a string.

Tests: boardRefreshReadiness drives the error path (shown, cleared on
another board, ignored from a board left behind); legacyBoardLoad pins
that no client cache calls an async lookup and that the server awaits
every one. The board itself is not reproduced: that needs the data.

Thanks to xet7 !
2026-10-15 01:27:37 +03:00
Lauri Ojansivu 51238ed8ad Import: attachment files of Jira, Nextcloud Deck, OpenProject, Asana and GitLab.
Their exports name attachments without carrying the files, so the files
were reported as a loss. Each service documents where a file is
downloaded, behind the same sign-in as its API:

- Jira: fields.attachment[].content (/rest/api/3/attachment/content/<id>),
  Basic email:API token; Jira's redirect to its media store is followed
  without the sign-in.
- Deck: /index.php/apps/deck/api/v1.0|v1.1/boards/../stacks/../cards/..
  /attachments/<id>, Basic user:app password, OCS-APIRequest: true.
- OpenProject: _links.downloadLocation, relative to the instance, Basic
  apikey:<key>.
- Asana: GET /attachments/<gid>?opt_fields=download_url with a personal
  access token, then the signed download_url without it; a file the
  export embeds (export_boards.pl) is taken as it is; one kept on Google
  Drive and the like is a link, reported.
- GitLab: a private project's /uploads/ link through
  /api/v4/projects/<id>/uploads/<secret>/<file> (17.4+), Bearer token.

The import page asks for the address, user and token under "Attachment
files" for these five (the REST import takes the same as attachmentAuth).
They go with that one import: held beside the creator in a WeakMap, never
stored or logged, the token field emptied on Import, and sent ONLY to
the origin the member typed - an export names any URL it likes. Every
download is the import's SSRF-guarded fetch, which validates each hop
and drops Authorization on a cross-origin redirect. A file that cannot
be fetched is on the loss report with its reason. Unticking Attachments
leaves them out (Jira's per-issue field is pruned too).

Zenkit is left out: it publishes no way to download a file.

Tests: importAttachmentAuth.test.cjs (each service's request, the
sign-in only to its origin, malformed sign-ins, never stored or
logged), the parser tests pin the attachment lists, a Meteor test
imports an embedded Asana file onto its card and shows a Jira file is
reported without a sign-in and that the sign-in never reaches an address
the guard refuses, and import-attachment-auth.e2e.js the page (not run
here). Not run against the live services.

Thanks to xet7 !
2026-10-15 01:27:26 +03:00
Lauri Ojansivu f95a891c06 CHANGELOG: Version Control view attachments.
Thanks to xet7 !
2026-10-15 00:51:15 +03:00
Lauri Ojansivu 132b341b8d Version Control view: Attachments have the card's controls, and open in the viewer.
The Attachments tab listed each file as a link to a new browser tab. A
file the attachment viewer can show now opens there on a click on its
thumbnail or name, with next and previous walking the tab's files, and
every file has the controls of an opened card's attachments: view,
download, rename, delete (the soft delete, behind the same confirmation)
and the cover / background menu, the writes for the same members as the
card's gallery.

Thanks to xet7 !
2026-10-15 00:51:09 +03:00
Lauri Ojansivu 7c1cffe4b6 CHANGELOG: Jira closed sprints; TODO Later without them.
Thanks to xet7 !
2026-10-15 00:46:51 +03:00
Lauri Ojansivu cc0b8c60ba Jira import: closed sprints, rebuilt from the issues.
Jira's sprint report has no documented API, so a closed sprint was
reported and skipped. It is now imported as closed, with its start and
close snapshots rebuilt from documented issue data: the Sprint field,
status and resolution at the sprint's start and completion dates, from
each issue's change history (expand=changelog), or from the issues as
they are now when there is none. The snapshots are marked partial. A
closed sprint with no start or completion date is still reported.

Thanks to xet7 !
2026-10-15 00:46:39 +03:00
Lauri Ojansivu 51e1a5c04d CHANGELOG: Chat view card in the middle, carets on folded panes.
Thanks to xet7 !
2026-10-15 00:38:04 +03:00
Lauri Ojansivu 230badf48e Chat view: a card from any board opens in the middle; folded panes keep their carets.
- Clicking a card in the channel tree took the member to that card's
  board. It now opens the card in the middle pane with its conversation
  beside it, whatever board it is on, and the page stays where it is. The
  card's conversation comes from a publication of its own
  (chatViewCardComments) with the tree's limits - a board the member sees,
  a role that reads comments, only their cards on an assigned-only board -
  and its writers' names; the comment box follows the card's own board.
- Folding the Channels or Comments pane left only an icon: the double
  caret stays now, pointing the way the pane opens, mirrored with the
  others right to left.

Tests: chatView.test.cjs (no navigation, the subscription, the card's
board for commenting, the publication's limits, the carets),
email-chat-views.e2e.js (another board's card in the middle without
leaving the page, carets visible on folded panes).

Thanks to xet7 !
2026-10-15 00:38:03 +03:00
Lauri Ojansivu 60317a8501 CHANGELOG: CodeQL #586, Jira sync from a Sandstorm grain, the closed issues.
Thanks to xet7 !
2026-10-15 00:33:59 +03:00
Lauri Ojansivu 1dc5b82369 Sandstorm: Jira List Sync works from a grain.
It was recorded as impossible: Jira signs in only with an Authorization
header, and the bridge proxy takes that header for the grain's grant. But
Sandstorm's HTTP driver can keep the sign-in in the grant itself
(shell/imports/server/drivers/external-ui-view.js in the Sandstorm
source): the member connects the Powerbox to "any address" and enters the
Jira address with the credentials in it - https://EMAIL:API-TOKEN@site
(the e-mail's @ as %40), or https://jira.example.org#TOKEN for a Server /
Data Center token - and Sandstorm moves them into the grant and adds
Authorization: Basic (or Bearer) to every request the grain sends through
it. WeKan never sees them.

- egress.mjs syncApiRoot grants a Jira list at its own address, and the
  Powerbox request asks for Basic authentication
  (ApiSession.PowerboxTag.authentication; apiSessionDescriptor takes it).
- In a grain the Jira fetcher sends no Authorization of its own, and a
  Jira source needs no WeKan token there: a marker credential says the
  grant holds the sign-in. Only for Jira - GitLab sends its token in the
  query, where a marker would reach GitLab.
- The List Sync popup tells a Jira list in a grain what to enter in the
  Powerbox, translated in all locales; the Sandstorm docs say how, and
  that the same #token form may let a private GitHub, Gitea or Forgejo
  through (not tried yet).
- TODO Later drops "Not possible from a Sandstorm grain", its last
  category naming an issue; changelogFormat no longer needs one.

Not run in a real grain here. Tests: sandstormSyncEgress.test.mjs (the
root and the Basic descriptor, and an address with credentials in it
still refused), sandstormJiraGrant.test.cjs (the server, the fetcher,
the marker only for Jira, the popup and the docs).

Thanks to xet7 !
2026-10-15 00:33:30 +03:00
Lauri Ojansivu de95789e45 Live tests: no log line carries anything from a login (CodeQL #586).
GitHub CodeQL js/clear-text-logging: releases/live-tests/basecamp-import.mjs
logged the Basecamp account id read from WEKAN_LIVE_BASECAMP_ACCOUNT. The
live tests run with the maintainer's own logins and keep a log under
.tools/log/, so nothing from a login or its configuration belongs in it.
The same shape was in two more tests and is gone there too: Planka logged
its configured address, and the GitLab write-back test its GitLab address
and scratch project path, also in the message that says it is skipped.
Each now logs what it does in fixed words.

These are human-run scripts, not part of the WeKan server, so there is no
attempt for Admin Panel -> Problems to record.

tests/liveTestsNoSecretLogs.test.cjs follows every value read from the
environment, a login or the credentials into the names it lands in, and
fails on any log line of any live test that interpolates one; a negative
case pins the reported shape.

Thanks to GitHub CodeQL and xet7 !
2026-10-15 00:25:21 +03:00
Lauri Ojansivu aad7e1414a TODO Later: no speaker review, no formats without documentation.
Maintainer decision 2026-10-15:

- Translating from dictionaries and each language's own wording is
  enough; speakers review it later if they have the time. The category
  listing terminology for a speaker to check is removed.
- Formats with no published documentation are not taken on: Restyaboard,
  YouTrack, Airtable, Leantime, Things 3, and Taskcafe (an archived
  project whose GraphQL API has no documentation beyond its source).
  Attachment contents from Jira, Deck, OpenProject, Asana, Zenkit and
  GitHub/GitLab stay: each documents its attachment API.

Thanks to xet7 !
2026-10-15 00:15:56 +03:00
Lauri Ojansivu 0d68c3536b Close #4790: User Filter is covered by what WeKan has.
#4790 was a collection of ideas ("I don't know what use cases it would
cover"), and a split into six new feature requests was prepared on
2026-09-29. Checked against the code, they describe what WeKan already
does under other names, so nothing needs filing:

- labels on organizations, teams, people and boards: organizations and
  teams are the groups of people; boards are grouped into All Boards
  workspaces and have their colours;
- a label from an LDAP group: LDAP groups sync as organizations and teams
  (#4737), with LDAP_SYNC_ADMIN_GROUPS and LDAP_SYNC_GROUP_ROLES;
- adding or keeping users out of boards by label: a board is shared with
  an organization or a team, and a private board not shared with one
  keeps it out;
- a board role for an LDAP group: the group syncs as a team, and the team
  is shared to the board with its role;
- finer roles: custom board roles (#3022, closed);
- users by e-mail domain: done (orgAutoAddByDomain.js, #5850, #4178);
- rules by label, meteor-roles and multitenancy were thoughts in the
  comments, not requests.

The prepared split (docs/Features/User-Filter-4790-Split.md) is removed,
TODO Later no longer waits on it, and docs/Design/TeamsOrganizations.md
says how its rows are covered - also that custom roles exist now.

Thanks to xet7 !

Fixes #4790.
2026-10-15 00:07:39 +03:00
Lauri Ojansivu 99d4ee4d91 Merge remote-tracking branch 'origin/main' 2026-10-14 23:56:55 +03:00
Lauri Ojansivu 707021fee2 TODO Later: only what is not built yet; close the issues that are.
What is built leaves the backlog, also where it is not yet tested against a
live service:

- #5758 Windows SSO: built in a594a58117 (v12.46), for a Windows host in a
  domain; the handshake was not run here.
- #2698 GitLab integration: GitLab sync and import (2026-09-30), and the
  write-back of edits, comments, closing and reopening in d6c1d6fce1
  (v12.46), with a live test in build.sh / build.bat -> Tests -> Live
  tests; GitLab login to WeKan through OAuth2 is documented, and card
  numbers with board keys give the #number references.
- #337 accessibility: move buttons, focus trap, landmarks, labels and
  every icon-only control named and guarded; a screen-reader pass is
  testing, not a missing part.
- #2208 Sandstorm: everything that can work from a grain is built (email
  sending, outgoing webhooks, list sync, the REST API through Sandstorm);
  Jira sync from a grain cannot, and stays recorded as such.

Also out: the continuous backup's live-account tests, the SMTP, browser
and identity-provider test matrix, the UCS / Nextcloud ExApp variable
check, the History switch (decided on 2026-10-08), Zenkit's unverified
export, the translation batch history, and the importers built on
2026-10-14 (Planka, Microsoft To Do, KanbanFlow, Basecamp). Left: Jira
closed sprints, the #4790 split to file, translations and their
terminology review, the attachment-board report, the formats not read
yet, and the Sandstorm limits.

changelogFormat's count of categories that link issues follows the
smaller backlog; it still fails on an issue named without its link.

Thanks to nikalasmd, ndarilek and xet7 !

Fixes #5758,
Fixes #2698,
Fixes #337,
Fixes #2208.
2026-10-14 23:56:19 +03:00
github-actions[bot] 6466686289 Bump versions for v12.46 2026-10-14 20:50:19 +00:00
Lauri Ojansivu 8263ca936d Prepare v12.46 release 2026-10-14 23:49:01 +03:00
Lauri Ojansivu 857b6db4f4 CHANGELOG: the guard fixes after the importers and the feed.
Thanks to xet7 !
2026-10-14 23:23:14 +03:00
Lauri Ojansivu a6a1d3c1a6 Fix three guards the last commits tripped.
- The activity feed's new stylesheet was not imported, so it would not
  have been in the client bundle (clientBundleImports); client/features/
  main.js imports it beside the feed's template.
- The Basecamp and Microsoft To Do importers stripped HTML tags in one
  pass, which a crafted body survives ("<<b>b>" leaves "<b>"); each
  removal now repeats until nothing changes (tagStrippingFixedPoint).
- renderedTitles escaped only the first "." of a field name; it escapes
  every special character now (noIncompleteRegExpEscaping).

Thanks to xet7 !
2026-10-14 23:23:07 +03:00
Lauri Ojansivu d27dbe651f CHANGELOG: API importers, Chat channels, the History fork and the views that showed raw text.
The Upcoming section's In short covers them; TODO Later drops the third
round of decisions, all of which are built now.

Thanks to xet7 !
2026-10-14 23:09:31 +03:00
Lauri Ojansivu 2ee3e83793 Chat view: channels are the cards with a conversation, on every board, with unread counts.
The Chat view's tree listed the open board's swimlanes, lists and cards,
and every other board as a link that left for that board. Now:

- The tree is workspaces > boards > swimlanes > lists > cards, holding
  the cards that have a conversation on every board the member sees
  (server/chatView.js chatViewChannels). Every board without one is under
  Other boards; its first message moves it up to its place.
- Each level has a caret before its icon - after it, mirrored, in a
  right-to-left language - and folds open and shut with a slide (none
  for a member who asked for reduced motion). Clicking a board folds it;
  it no longer leaves the Chat view. The board the member is on, its
  workspaces, swimlanes and lists start open; what they open or close is
  kept in their browser.
- The board the member is on shows all its cards, from the board's own
  data, so a conversation can start on any card and a dragged card moves
  in the tree at once; its button shows only the cards with messages.
  Another board opened from Other boards shows all its cards
  (chatViewBoardChannels) for the same reason.
- Every level shows the messages this member has not read: others'
  messages after they last opened the card, all of them on a card never
  opened; opening the card - or a message arriving while it is open -
  reads it.
- Every name, and the chat's header, renders as Markdown (inlineViewer).
- Only what the member may read: boards they see in All Boards, only
  their own cards on an assigned-only board, no conversation where their
  role reads no comments.

Tests: tests/chatView.test.cjs (the tree, Other boards, a first message
moving a board up, unread sums, the full board, carets, folding rows,
no navigation), server/lib/tests/chatViewChannels.tests.js (unread
counts, never my own, the role and assigned-only limits, a board I do
not see refused), email-chat-views.e2e.js (folding without leaving the
view, the move up, unread badges, Markdown names). New strings
translated in all locales.

Thanks to xet7 !
2026-10-14 23:08:22 +03:00
Lauri Ojansivu 2cc6b93ed9 Version Control, the activity feed and the email list render titles as Markdown.
Titles, names and labels are Markdown in WeKan - links, emphasis, emoji -
but Board View / Version Control showed "Azure [issues](https://...)"
and the like as text, the all-boards activity feed did the same with the
cards and boards it names, and the email view's sidebar showed its lists
and labels raw ("Demo + Wishes 🌈").

inlineViewer (client/components/main/editor.jade) is the viewer itself -
Markdown, emoji, mentions, and the Admin Panel's security settings for
links and code - laid out inline at the size of the line it is in. These
views use it for every issue, milestone, label, release, wiki page and
board title, the activity sentence and its card and board, and the email
folders and row labels. An attachment's file name in its download link
stays a file name.

A rendered title can hold a link, and a link inside a link is not one:
the titles and labels that open or filter are spans with role=link now,
which Enter and Space open too, and a click on a link inside a title
opens that link instead.

tests/renderedTitles.test.cjs fails on any title-like value interpolated
as text in these views and pins the role=link spans.

Thanks to xet7 !
2026-10-14 23:07:30 +03:00
Lauri Ojansivu f12b718d81 Activity feed: every sentence names what it is about.
The all-boards activity feed showed "checked __checklistItem__ of
checklist __checklist__", "from list __oldList__ at swimlane
__oldSwimlane__", "added attachment __attachment__", an empty "at
swimlane", and raw names such as "removedChecklistItem": it filled only
the card, list and board of the notification sentence.

- server/activityFeed.js reads the ids the sentence needs and looks the
  names up a batch at a time: swimlanes, old list and swimlane, checklist
  and item, attachment, label, member and assignee, comment, old board.
  Never a field's value (an admin-only custom field stays out), a
  comment's text only for a member who may read comments, and another
  board only when the member sees it too.
- A rename or a restore names its new title itself, and a swimlane's
  activity keeps the swimlane's id in listId; activityTitleParams fills
  those for the feed and the notifications alike.
- A placeholder nothing could fill - a deleted checklist item - is left
  out instead of shown as __name__.
- Ten kinds of activity had no sentence at all, in the feed or in
  notifications: removedChecklistItem, moveCardBoard, deleteBoard, the
  board, list and swimlane renames and restores, joinAssignee and
  unjoinAssignee. They have one now, translated in all locales; the
  restores, moves and checklist ones join their notification groups.

tests/activityFeed.test.cjs fills every name, refuses comments and
hidden boards, and fails on any stored activity type without a
sentence; activity-feed-sentences.e2e.js checks the feed shows the names
and no __placeholder__.

Thanks to xet7 !
2026-10-14 22:57:31 +03:00
Lauri Ojansivu 46e30f01e4 Import: Planka, Microsoft To Do, KanbanFlow, Basecamp, Google Tasks and Google Keep from their APIs.
Decided on 2026-10-14. On the import page each of the six asks for what
its service needs - a self-hosted Planka its address, username and
password; the others an access token (Basecamp also its account id) -
lists what that account can import, and fetches the chosen board, list
or project. The credentials go with each call and are used for that
call's requests only; they are never stored or logged, and the page
clears secret fields once a fetch has used them. The fetched JSON fills
the import text box and the import goes on as for a pasted export: the
service's parser (models/lib/<key>ApiFormat.js) into the generalized
importer, with the members step and the loss report. The same JSON can
be pasted, or posted to POST /api/boards/import/<key>.

- server/apiImport.js: apiImportList and apiImportFetch, signed in only,
  off with the Admin Panel's import switch, not in Sandstorm, rate
  limited; credentials checked against the service's own fields
  (models/lib/apiImportServices.js).
- server/lib/apiImportRequest.js: every request through the SSRF guard -
  pinned address, no redirects, bounded in time and size - so a Planka
  address cannot point the server at its own network; an administrator
  can allow such a host with API_IMPORT_ALLOWED_PRIVATE_HOSTS. An error
  names only the origin and the status.
- models/lib/apiImport/<key>.js: each service's client, plain Node with
  the request injected, paging bounded and the token only in a header.
- Each has a docs page under docs/Features/ImportExport/, listed in the
  coverage index and External-Tools.md.

Tests: tests/<key>ApiImport.test.cjs (client with a fake request, every
parsed field, wrong shapes), tests/apiImport.test.cjs (credentials, the
gate, the wiring, the shapes refused), api-import.e2e.js (the form for
every service, fetched JSON imported through the page, refusals incl. a
private address). Live tests, read only, run from build.sh / build.bat ->
Tests -> Live tests with your own accounts (releases/live-tests/README.md);
they were not run here. New strings translated in all locales.

Also: the release risk baseline allows the services' API addresses, and
the guards that count import creators, list the import sources and pin
the platform fetch() calls know the new ones.

Thanks to xet7 !
2026-10-14 22:57:15 +03:00
Lauri Ojansivu 5fee137f01 Tests: three guards follow the History fork and GitLab write-back fixes.
- syncHistoryAdmission pinned the line that walked successors to the
  chain's tip inside appendSyncHistoryRow; 66dd9b0938 moved that walk to
  historyChainTip and made the append take turns, so it pins those.
- The write-back's injectable GitLab read was a parameter named fetch,
  which syncBleed's "no new platform fetch()" guard rightly could not
  tell from the real one; it is readIssues now.
- syncClosedByWeKan, the card field the write-back added, is in the card
  e-mail field inventory as internal provenance, excluded like the other
  sync fields.

Thanks to xet7 !
2026-10-14 22:56:34 +03:00
Lauri Ojansivu 66dd9b0938 History: two changes at once no longer fork a board's History chain.
Admin Panel -> Problems showed HistoryIntegrity, critical, "history-fork",
89,062 times. Each History row names the row before it, and the legacy
append (every board not migrated to the coordinated chain) read "the
newest row" and inserted after it as two separate steps: two changes
saved at the same moment on one board - two members, or one action whose
hooks record twice at once - both named the same row. Nothing was
tampered with, but the audit called it tampering, and undo, redo and
restore were refused for both rows. The writer gate around the legacy
append counts its writers for a migration; it does not make them take
turns.

- models/lib/historyLegacyAppend.js: a board's appends take turns in
  this server, and each links after the chain's tip - the newest row,
  then its successors, as Sync History already did - so a createdAt tie
  does not fork either. The ordinary recorder, the Scrum restore writer
  and Sync's legacy append all use it. Several servers writing one board
  at the same moment can still race; the coordinated chain is the answer
  to that.
- The hourly audit reported every broken row again every hour, which is
  where 89,062 came from; each finding is now reported once
  (historyIntegrityReported). It reads the History board by board instead
  of all at once, and a fork of two intact rows saved within a minute of
  each other is reported as what it is - integrity.historyConcurrentFork,
  medium, "a History writer race, not tampering" - while anything else
  stays critical.

Existing forks stay as they are (a row's hash cannot be changed), and
undo of their two rows is still refused.

tests/historyLegacyAppend.test.cjs: the old append forks under
concurrency (negative control), twenty appends at once on two boards
make two straight chains, a createdAt tie follows the chain, a failed
append does not stop the next, the audit tells a race from a changed or
late row, and no append in models/ or server/ links by "newest" without
taking turns.

Thanks to xet7 !
2026-10-14 22:39:36 +03:00
Lauri Ojansivu 5481268558 Email view: subjects and previews render Markdown.
Board View / Email showed a card's title and description as text: "# Demo
card 👍", "**Markdown**", raw <font> tags and links, where every
other view renders them. The row's subject and preview now go through
the viewer like every card title and description in WeKan, so the Admin
Panel's settings for links and code apply to them as everywhere.

The preview was the description squeezed to one line and cut at 120
characters, which cut inside a link or a tag; it is now the start of the
message, the From line left out, cut at a line end (previewMarkdown),
and the row shows two lines of it. Headings and paragraphs in a row read
at the row's size.

tests/emailView.test.cjs pins previewMarkdown and that neither cell is
interpolated raw; email-chat-views.e2e.js checks the bold, the link and
that no ** or ]( is left in the row.

Thanks to xet7 !
2026-10-14 22:36:21 +03:00
Lauri Ojansivu fc24caadcc CHANGELOG: GitLab write-back, and the oauthProviders guard.
TODO Later's third-round block keeps only the API importers.

Thanks to xet7 !
2026-10-14 22:08:54 +03:00
Lauri Ojansivu d6c1d6fce1 List Sync: write WeKan's changes back to GitLab.
#2698. A list synced from GitLab gets a Sync option of its own, off by
default: Write changes back to GitLab. With it, every Sync run first
sends what members did on the list's cards, then reads GitLab, so the
read finds both sides agreeing:

- a title or description edited in WeKan, when the issue still has the
  text both sides last agreed on - the local-only edit the reconcile
  already keeps. The Source footer Sync adds is not sent; when both sides
  changed, nothing is written and it stays a conflict to resolve;
- a card archived in WeKan closes its open issue, and restoring a card
  WeKan closed reopens it (syncClosedByWeKan); an issue GitLab closed is
  never reopened by WeKan;
- a comment written in WeKan becomes a note, and keeps the note's id
  (syncCommentId) so the comments sync does not bring it back twice.

Only what happens after it was switched on is sent (writeBackSince), at
most 100 writes a run. Every write is fenced by the list's Sync
reservation, goes through the same address checks as the reads (no
redirects, PUT or POST to an issue or its notes only), and records on
the WeKan side only while the card or comment is still as planned. A
refused write is shown in the Sync popup (syncWriteBackError, beside
syncSource because the run's list writes match syncSource whole) and the
read sync still runs. Not in Sandstorm, whose bridge only reads.

Tests: tests/listSyncWriteBack.test.cjs (the plan with GitLab's shapes,
the parser's footer, every negative - conflict, legacy card, old
archive or comment, foreign card, issue GitLab closed - and the wiring),
server/lib/tests/listSyncWriteBack.tests.js (end to end through
syncOneList against a fake GitLab). The live test,
releases/live-tests/gitlab-write-back.mjs, runs from build.sh / build.bat
-> Tests -> Live tests with the glab login releases/mirror.sh uses, on one
scratch issue in a private project of that account; it is human-run and
was not run here. releases/live-tests/run.mjs runs every live test and
skips a service with no login.

Also: the English variants get the SQRL and Windows sign-in strings
(their key order follows en.i18n.json); buildScriptParity accounts for
the scripts CI runs inside their own environments; new strings
translated in all locales.

Thanks to xet7 !
2026-10-14 22:08:34 +03:00
Lauri Ojansivu b38532ed72 Tests: oauthProviders follows the translated login-error list.
a539429fce made the sign-in page translate SQRL's sqrl-only and
sqrl-hardlock refusals the way it already translated
oauth-account-conflict, by one list of codes, so the guard's pin on the
old single comparison failed. It now pins the list - the conflict among
the codes, matched on err.error or the reason, shown as text - so the
behaviour it protects is the same.

Thanks to xet7 !
2026-10-14 22:08:19 +03:00
Lauri Ojansivu e18f6db741 CHANGELOG: SQRL locks, Windows SSO, faster Scrum batch undo, #6549 docs.
The Upcoming section opens with them and the Bosnian and Tsonga
terminology; TODO Later keeps only the two decided items still to build
(GitLab write-back, the API importers) and drops the finished batch
undo/redo entry.

Thanks to xet7 !
2026-10-14 21:47:06 +03:00
Lauri Ojansivu a594a58117 Login: Windows single sign-on with Kerberos or NTLM.
#5758. On a WeKan running on Windows in a domain, members already signed
in to Windows can sign in with "Sign in with Windows", without a
password: /sso/windows runs the browser's Negotiate handshake with
node-expose-sspi, WeKan finds or creates the member, and the browser
comes back to /sign-in with a one-time token (random, kept as its
SHA-256, one minute, used once) that the page signs in with through a
login handler - so every login check applies, deactivated accounts and
SQRL's sqrlonly included.

- WINDOWS_SSO_ENABLED=true turns it on, on Windows only.
- WINDOWS_SSO_DOMAINS lists the domains allowed; empty means nobody, and
  machine accounts never.
- WINDOWS_SSO_ACTIVE_DIRECTORY=true takes name and e-mail from AD.
- WINDOWS_SSO_MERGE_EXISTING_USERS=true signs in to the account with the
  same username or e-mail; otherwise a new account is made beside a
  local one of the same name, since a local alice is not necessarily the
  domain's alice. A Windows account signs in to the account it linked to
  before.

node-expose-sspi is Windows-only and not a dependency every platform
downloads: the admin installs it into the bundle, and WeKan looks for it
there (docs/Features/Login/WindowsSSO.md; start-wekan.bat has the
variables).

Built without a Windows domain to test against, as decided: the
decisions - names, domains, profile, which member - are tested in plain
Node in tests/windowsSso.test.cjs, with negatives for every malformed or
foreign name, an empty allowlist and a same-named local account; the
handshake and the server wiring are pinned from source. New strings
translated in all locales.

Thanks to nikalasmd and xet7 !
2026-10-14 21:46:17 +03:00
Lauri Ojansivu 5e671c7034 Scrum: batch undo and redo of a large sprint is 2.6 times faster.
Undoing a 10,500-card sprint close took 282 s and redoing it 578 s, about
27 ms a card: each record of a History row cost some thirty database
round trips - a read per record in the preflight, two ownership checks
(two reads each) around every read, the same again around a readback
after every write, and the whole row read back record by record twice
more at the end.

Now a row's records are read in one query per type (readTargets), and
inspectScrumHistoryWrites takes that as readMany, bracketed by one
ownership check before and one after: the worker's claim and the source
row only ever go from current to displaced, so current before and after
the read means current throughout it. Every write keeps its ownership
check right before it, after the read it is computed from; the readback
after a write confirms the value landed and leaves ownership to the next
write's fence and the batch's final readback. A sprint or release that
thousands of cards name is looked up once, not once a card. The
permission check stays per card.

The 10,500-card start / close / undo / redo test went from 14 min 27 s
to 6 min 7 s on the same machine; all 18 Scrum tests pass.
tests/scrumHistoryWriteState.test.cjs pins one read and three checks
for a bracketed read, ownership lost during it, a short answer, a missing
record and identities checked before any read (negative), and that every
write in the path is still fenced after its read.

Thanks to xet7 !
2026-10-14 21:45:54 +03:00
Lauri Ojansivu 03b83c14c1 Docs: how WeKan, Rocket.Chat and a G Suite SAML login chain together.
WeKan -> Rocket.Chat (OAuth2) -> Google (SAML) is three sign-ins in a row,
and with the popup login style the chain broke whenever Rocket.Chat was
not signed in yet: its SAML login left the authorize page the popup was
waiting on, so WeKan never got its code, although Google and Rocket.Chat
both logged a successful login. Logging in to Rocket.Chat first only
avoided the detour.

docs/Features/Login/OAuth2.md now says how to configure the chain: the
redirect login style (the default now; oauth2-login-style /
OAUTH2_LOGIN_STYLE / Admin Panel), Rocket.Chat's SAML entry point and
issuer, Google's NameID as the e-mail address, the exact
/_oauth/oidc redirect URI, https everywhere, how to read the logs, and the
direct alternatives (Google OIDC, WeKan's own SAML).

tests/oauth2RocketChatSamlChainDocs.test.cjs pins that redirect stays the
default in the server and the snap, and that the documented section names
the settings - and never recommends popup.

Thanks to timmy-mac and xet7 !

Fixes #6549.
2026-10-14 21:45:44 +03:00
Lauri Ojansivu a539429fce SQRL: sqrlonly and hardlock lock an account to SQRL.
The member's SQRL client sends both options with every sign-in, and WeKan
now follows them (it used to say they were not supported):

- sqrlonly: every other way of signing in is refused - password, OIDC,
  LDAP, SAML, CAS, passkey, Nextcloud (validateLoginAttempt), the REST
  /users/login and header login, which mint their tokens without it. A
  resume token stays valid; a session already open stays open.
- hardlock: no password-reset e-mail is sent (the answer is the same as
  for a sent one), a reset link sent before does not sign in, and the
  member cannot change their e-mail address.

An ident records the options only once it succeeds; a flag the client
leaves out is switched off. Neither applies while the identity is disabled
from the SQRL client (the emergency stop), nor while the admin has SQRL
off, and an admin can still change the e-mail, set a password and create
a token: those are the ways back. Member Settings / SQRL shows which locks
are on; the sign-in page and the e-mail change say why they refused.

tests/sqrl.test.cjs drives both options through the protocol with a real
Ed25519 client (set, cleared, never by a query, a refused or a forged
ident), the refusal decision for every login type, and pins every path
that signs a member in - and lists every file that mints a login token,
so a new one cannot skip the check. New strings translated in all
locales.

Thanks to xet7 !
2026-10-14 21:45:34 +03:00
Lauri Ojansivu 0502261090 Translations: Bosnian says Tabla for board, Tsonga keeps archive apart from storage.
Bosnian used both ploča and tabla for a board. Every board string now uses
tabla and its cases (genitive plural tabli); ploča stays only where it
means the dashboard (kontrolna ploča) or the Admin Panel. The board
search operator abbreviation is tb, because t is already traka
(swimlane).

Tsonga used vuhlayiselo for both the Archive and storage. The Archive is
now "vuhlayiselo bya swa khale", while storage stays "vuhlayiselo" and the
database "vuhlayiselo bya data". The archive popups ask their question.

Bosnian and Croatian s3-port translated (S3 port).

The audited-corrections rows, the Bosnian audit test and the archive
filter guard follow the decision. wrongLanguageCorrections20261014 pins
Tabla, the tb abbreviation, the dashboard and Admin Panel exceptions, and
the Tsonga archive word wherever English says archive.

Thanks to xet7 !
2026-10-14 21:26:28 +03:00
Lauri Ojansivu 84c6d2db7f Merge remote-tracking branch 'origin/main' 2026-10-14 21:13:44 +03:00
Lauri Ojansivu ddcdd295bb TODO Later: the third round of design decisions.
GitLab write-back with live tests in the build menus (#2698), Windows SSO
(#5758), documenting the Rocket.Chat SAML chain (#6549), SQRL's sqrlonly
and hardlock (#2460), faster batch undo/redo, API importers for Planka,
Microsoft To Do, KanbanFlow, Basecamp, Google Tasks and Google Keep, and
the Bosnian and Tsonga terms.

Thanks to xet7 !
2026-10-14 21:11:33 +03:00
github-actions[bot] c4c8484738 Bump versions for v12.45 2026-10-14 18:00:50 +00:00
Lauri Ojansivu 7ee94d47d8 Prepare v12.45 release 2026-10-14 20:59:39 +03:00
Lauri Ojansivu ea9007d8a8 Scrum: the 10,500-card test gets the time it takes, measured.
It timed out in every full Meteor run. Profiled at full size it passes, in
15 min 25 s: start and close 2 s, the rollover 36 s, the undo batch 282 s
and the redo 578 s - past the 15 minutes it had. The next test, which
interrupts a rollover, then met this one's unfinished writes and failed
too. The test has 30 minutes now, with the measurements beside the
limit, and the Scrum snapshot rows group passes (3 passing, 14 min).

TODO Later now records the real follow-up: batch undo/redo writes about
27 ms a card.

Thanks to xet7 !
2026-10-14 20:10:07 +03:00
Lauri Ojansivu 686995c204 CHANGELOG: the release build fixes in Upcoming.
musl, Android, FreeBSD, the Windows single files, Bun and the Quay copy,
grouped as Release builds under the bug fixes, with In short updated.

Thanks to xet7 !
2026-10-14 19:36:35 +03:00
Lauri Ojansivu ce3d4310d5 Docker: the Quay copy retries a dropped connection.
v12.44's copy to Quay failed on "stream error: ... PROTOCOL_ERROR;
received from peer" - an HTTP/2 reset on the way, not a refusal. A
transport error is retried up to four times; a refusal still fails at
once. tests/releaseDockerRegistryOutage.test.cjs runs the step with a
docker that resets twice, one that refuses, and one that never recovers.

Thanks to xet7 !
2026-10-14 19:28:27 +03:00
Lauri Ojansivu 41399b7518 Bun bundles: FreeBSD's libvips, a retried download, and LF checksum files.
- FreeBSD: sharp 0.35.5's source refuses libvips older than 8.18.7, and
  FreeBSD packages 8.18.6 (latest and quarterly alike), so both FreeBSD
  Bun builds stopped at that #error. A user's FreeBSD runs the packaged
  libvips through the same soname whatever was compiled against, so a
  patch-release gap is accepted with a notice; a larger one still stops.
- windows-x64: one HTTP 500 from the CDN failed the asset download; a
  server error is now retried, a 4xx is not.
- The .sha256sum files were written in Windows text mode, CRLF, and the
  windows-arm64 Bun single file then looked for "<zip>\r". They are LF on
  every platform now, and the reader strips a CR from the ones already
  published.

Thanks to xet7 !
2026-10-14 19:28:27 +03:00
Lauri Ojansivu 0966a90d39 Windows single files start: Meteor's paths, and 32-bit targets built by a 32-bit Node.js.
The win64, win-arm64 and win32 single files stopped at once since v12.41,
for two reasons:

- win64 and win-arm64: Node.js mounts the archive at \\.\NUL\vfs\<n>, and
  Meteor's mini-files.js turns a path beginning with a backslash into
  C:\... - so Npm.require looked in the wrong place and the server
  stopped with "Cannot find module 'denque'". The single file now carries
  mini-files.js patched to keep a UNC or device path (two leading
  separators) as it is; drive paths are unchanged, and the build stops if
  Meteor's code is not the code the patch was written for. It also makes
  a bundle runnable from a \\server\share.
- win32: the SEA blob stores its lengths as size_t, so a blob written by
  the x64 Node.js on the build host is unreadable to a 32-bit node.exe
  (createZipProvider got no archive). build.mjs now refuses a target whose
  pointer width differs from its builder's, and single-file.yml builds
  win32, i386 and 32-bit ARM with the release's i386 Node.js, checksum
  verified - the i386 and ARM ones had the same fault but no runner starts
  them.

tests/singleFile.test.cjs runs Meteor's path functions on Windows path
semantics before and after the patch, refuses win32 from x64, and builds
and starts a real single executable with the patched file.

Thanks to xet7 !
2026-10-14 19:28:27 +03:00
Lauri Ojansivu 0d23746441 FreeBSD bundles start: cpu-exec is used only where it can run.
start-wekan.sh ran every binary through cpu-exec, a #!/bin/bash script for
Linux's /proc/cpuinfo and qemu-user. FreeBSD has no /bin/bash, so it
failed "cpu-exec: not found" and WeKan never answered (the FreeBSD
bundles since v12.41). It is now used only on Linux with a /bin/bash -
the same holds for Alpine without bash and for Termux - and the BSD
bundles drop it, as the Windows and macOS ones already do.

Thanks to xet7 !
2026-10-14 19:28:14 +03:00
Lauri Ojansivu 5331e06199 musl and Android bundles: sharp gets its platform package where it is.
Both builds failed every release since v12.41. sharp is an application
package (programs/server/npm/node_modules), but the scripts removed its
glibc platform packages from programs/server/node_modules, where they are
not, and the npm install there never touches the application's packages.

- musl: releases/platform-native-packages.mjs and check-addon-arch.mjs
  take --libc musl: the glibc packages go and sharp's
  @img/sharp-linuxmusl-<arch> and canvas's -musl package arrive,
  sha512-checked. "Cannot find module 'sharp'" ended the job, from a
  require() run in programs/server; it runs in programs/server/npm now.
- Android: the glibc packages go from the right directory, and sharp is
  built from source against Termux's libvips in its own directory; should
  that fail it runs as WebAssembly, with a warning. Termux's user does not
  own the bind mount, and "chmod: ... Operation not permitted" stopped
  v12.43 and v12.44 - only what is not already executable is chmodded.

tests/platformNativePackages.test.cjs installs the musl packages through
the real script; muslBundles and androidBundles pin the paths, the
--libc musl call and that nothing chmods unconditionally.

Thanks to xet7 !
2026-10-14 19:28:14 +03:00
Lauri Ojansivu 52b4c87127 TODO Later: the 10,500-card Scrum test, with what was measured.
It times out in every full Meteor run, before this round as well; the
profile at 3,000 cards and why it cannot simply run smaller are recorded
so the next step - profiling the board data read and the rollover at full
size - starts from there.

Thanks to xet7 !
2026-10-14 18:58:39 +03:00
Lauri Ojansivu 25d906d93c attachmentApi test: the upload-limit refusal no longer times out.
It failed in every full Meteor run, before this round too, at Mocha's two
seconds. Timing the handler showed it refuses in 2 ms; the time was its
console.error of the refusal - the first error stack of the run, whose
printing loads the server bundle's source map. The test now captures that
log (and checks it was written) instead of printing it, and the group runs
in under half a second.

Thanks to xet7 !
2026-10-14 18:53:37 +03:00
Lauri Ojansivu 60bd70a636 Merge remote-tracking branch 'origin/main' 2026-10-14 18:37:50 +03:00
Lauri Ojansivu 0440454675 CHANGELOG: Tsonga, Bosnian and Aromanian in Upcoming.
Thanks to xet7 !
2026-10-14 18:34:21 +03:00
Lauri Ojansivu ac2410fead Tsonga, Bosnian and Aromanian: the values left for a speaker, translated.
- Tsonga: 618 values were the filler "Hi Xitsonga: mhaka", and 831 more
  were Xitsonga words padded with a repeated "mhaka" wherever the
  generator had no word ("engetela bodo swirho mhaka ku suka mhaka mhaka
  nhlangano"). All are translated from the English, in the file's own
  words (bodo, khadi, nxaxamelo, ndlela, xirho, nhlangano, ntlawa); the
  planning-poker numbers and search symbols are literal again.
- Bosnian: the 748 values that were Serbian, transliterated in v12.44, are
  ijekavian Bosnian now (premjestio, Neraspoređene, Historija, sedmica),
  with the board, card and list words of the rest of the file instead of
  the court-file metaphor they came with (spisi, predmet, postupak).
  Several mistranslations went with it (Export card said convert, Edit WIP
  limit said remove it).
- Aromanian "calendar" replaces the Italian "Calendario"; it is spelled as
  in English, so it joins Aromanian's list of such words.

Low confidence, for a speaker to correct: Tsonga throughout, especially
colour names, migration (ku rhurhisa), backup, lock/unlock, "Xiluva" for
the letter greeting Dear, and Vuhlayiselo used for both storage and
archive; Bosnian ploča where the board title itself says Tabla.

tests/wrongLanguageCorrections20261014.test.cjs fails on any Tsonga filler
or padding, any ekavian form in the Bosnian file, and a lost placeholder
in either.

Thanks to xet7 !
2026-10-14 18:34:09 +03:00
Lauri Ojansivu dc18a2eb00 CHANGELOG: the Sync notification plan fix in Upcoming.
Thanks to xet7 !
2026-10-14 17:49:00 +03:00
Lauri Ojansivu e85eaa3ad3 Sync notifications are planned again with the clear e-mail layout.
v12.44 made the clear HTML layout the default for notification e-mail
(#4434), and with it every e-mail job carries `text`, its plain-text part.
The Sync notification plan validator accepted exactly seven job fields, so
it refused every plan: a rule or Sync run's notifications failed with
sync-notification-plan-invalid. groupKey and deliverAt, from non-default
delivery settings, were refused the same way before that.

The validator now takes what emailOutbox.enqueue takes - the seven fields
and the three optional ones, checked as enqueue checks them - so a plan
never holds a job its replay would refuse. tests/syncNotificationPlan
.test.cjs plans a job with all three and refuses bad ones, and pins that
the optional list is exactly enqueue's parameters; the full Meteor run
found it (Shared notification preparation, Stored Sync notification
delivery).

Thanks to xet7 !
2026-10-14 17:48:45 +03:00
Lauri Ojansivu 5f78baa324 CHANGELOG: the attachment viewer's alt text in Upcoming, bug fixes before translations.
Thanks to xet7 !
2026-10-14 17:34:07 +03:00
Lauri Ojansivu a530a3c673 Accessibility: the attachment viewer's picture has alt text.
The one image in the templates without alt was the attachment viewer's:
it is "Preview" until a picture opens, and then the file's name.
tests/iconOnlyControlLabels.test.cjs now also fails on any img without
alt. Part of #337.

Thanks to ndarilek and xet7 !
2026-10-14 17:33:48 +03:00
Lauri Ojansivu 439add85f1 CHANGELOG: a new Upcoming for the Latvian and other language fixes.
v12.44 shipped while they were in progress; the rest of the wrong-language
replacements and the two guard updates after it go in a new Upcoming
section, as the release rules say.

Thanks to xet7 !
2026-10-14 17:33:07 +03:00
Lauri Ojansivu c11fd215c4 problemsMenuOrder and sandstormWebhookEgress follow two v12.43 changes.
Both failed before this round, on changes that were deliberate:

- The activity notification recovery list (#6768) has Retry all and Cancel
  older as toolbar buttons, written `actions: [{ id, labelKey }]` like the
  menu's panes; the guard now leaves toolbar actions out and still
  requires every menu pane to render.
- Webhooks may reach internal hosts a site admin lists (b4d2604e61):
  off Sandstorm, webhookFetch is fetchSafe except for those hosts, which
  internalWebhookHosts.test.cjs pins; the guard now matches that branch
  and still requires fetchSafe for everything else.

Thanks to xet7 !
2026-10-14 17:32:51 +03:00
Lauri Ojansivu e0649b0cfb Finish the wrong-language replacements: Latvian, and the same words elsewhere.
The first half shipped in v12.44 with "Updated translations"
(b7346d2429): Spanish Save is Guardar, the Bosnian file is in Latin
script, Romanian's Italian words are Romanian, and single values in a
dozen locales. This finishes it:

- Latvian: 225 values were Lithuanian; they are Latvian now, in the
  file's own words (dēlis, kartiņa, darbvieta, migrācija). Low confidence:
  fuksīns, the Monte Carlo note's statistics wording, Teksta sagatave.
- The same wrong words under other keys: Macedonian Настройки in the All
  Boards menu, the Persian settings typo there too, Cantonese 依戀 in the
  attachment search operator, and Occitan "mot de Santa-Clara" for
  password in six values (senhal).

tests/wrongLanguageCorrections20261014.test.cjs now also fails on any
Lithuanian left in the Latvian file and checks its placeholders. Left for
later, with a speaker: the Tsonga file's 596 "Hi Xitsonga: mhaka" filler
values, Aromanian "Calendario", and Bosnian ekavian forms.

Thanks to xet7 !
2026-10-14 17:32:45 +03:00
github-actions[bot] 3522f68226 Bump versions for v12.44 2026-10-14 14:30:51 +00:00
Lauri Ojansivu 832d83cf7a Prepare v12.44 release 2026-10-14 17:29:37 +03:00
Lauri Ojansivu b7346d2429 Updated translations 2026-10-14 17:29:25 +03:00
Lauri Ojansivu 030c8c524c Update five guards for this round's deliberate changes.
The full node suite run found them; each guard keeps its assertion and
says why it changed:

- boardPermission lists ActivityPub's public-only check (#4198): only a
  public board is a fediverse actor, never an instance board.
- cardSectionCollapse reads the End date item to the next template, and
  cardTitleEditClose lets the close anchor carry attributes: the #337
  titles moved the icon past a fixed window and a bare-anchor pattern.
- oidcAutoRedirectLoop's harness provides FlowRouter, which the sign-in
  page's onCreated now reads for SQRL's one-time sign-in (#2460).
- sharedDescription's own whole-tree check skips every _build* directory,
  rspack's bundled copy of every file.

Thanks to xet7 !
2026-10-14 17:22:39 +03:00
Lauri Ojansivu 1aa6de7356 Fix the seven guards this round's features broke.
Each was checked for which side was wrong:

- The LibreJS labels page sent Cache-Control: public; it is private now,
  as fileCacheHeaders requires of every response.
- Closing a card marks it seen (#2111): that code moved below the #6686
  popup handling, which labelsPopupStaleCardContext reads from the top of
  onDestroyed; unreadChanges now finds it anywhere in onDestroyed.
- The shared header helper (#2805) moved below the swimlane's currentUser
  helper, where largeBoardDataLoading pins it.
- The Story/Task kind, column set places and drawing strokes (#2012, #211,
  #1178) get their rule e-mail dispositions: none is e-mail content.
- Guards updated for deliberate changes, each saying why: the Rules group
  in Admin Panel Visibility (#2217), the rule under the team tabs in All
  Boards (#802, the two around Workspaces still pinned where they are),
  and a linked list's card clearing the title draft only after it was
  added (#2809, every clear checked against the add before it).

Thanks to xet7 !
2026-10-14 17:07:35 +03:00
Lauri Ojansivu 0bac415d87 Admin Panel: the SQRL and ActivityPub switches are in the Features group.
Each came with a group of its own, with an untranslated title and two more
rules in Admin Panel -> Settings -> Visibility. Both are optional features
this WeKan offers, which is what the Features group lists, so they are
there now, each still off by default and saved when switched.
tests/boardsVisibilitySettings.test.cjs counts the groups and their rules
again. The SQRL page and the Upcoming entries name the new place.

Thanks to xet7 !
2026-10-14 17:07:27 +03:00
Lauri Ojansivu 8e57bf6c22 CHANGELOG: the decided TODO Later items, SQRL, the CodeQL and #6770 fixes.
Upcoming gets the work since the last update: the CodeQL alerts first, as
security fixes; SQRL under Sign-in; browser push, the activity feed and the
webhook activity picker under Notifications; the linked list, live shared
descriptions, drawing, unread changes, column sets and the team tabs in
their groups; a new Nextcloud and the fediverse group for CalDAV, WebDAV
and ActivityPub; bug fixes for the subtask index, the cardKinds startup
schema and the icon-only controls; and the translations. In short is
rewritten to cover them.

TODO Later: the "decided, waiting to be implemented" category is gone -
everything in it is built - and #337 keeps a category of its own as
ongoing work. #2460 leaves "needs a maintainer decision". #4790 is linked
where it was named bare.

tests/changelogFormat.test.cjs asked at least half of the TODO Later
categories to link issues. That was a proxy, and it stopped holding once
the decided issues were built: the rest are mostly status records no
single issue tracks. It now checks the thing itself - the triage
categories exist, and no category names an issue without linking it.

Thanks to xet7 !
2026-10-14 17:04:26 +03:00
Lauri Ojansivu b0e47537e0 Translate the SQRL strings into every locale.
The 15 strings of SQRL sign-in - the Admin Panel switch, the sign-in
button and its QR code, and Member Settings -> SQRL - in all 234 locales,
from each locale's own words for sign in, password, add, remove and
Member Settings. "Rescue code" is SQRL's printed recovery code and
"identity" the member's SQRL key, not an ID card. ja-HI is written in
hiragana, as its tag says.

Low confidence: chr, tlh, wal, ay, ace, rn, sm, st, csb, kw, fur, uz-AR,
iu, nah, gn, zgh, wo, sn, tn, rup, se, cv, kl, to, bua, bi, nap, la, ak,
bm, ff, mg, ny, ve, xh, tig, tpi, sah, vo, gv, dz, ee, ig, ha, lg, nd, qu,
mi, wa, yi, ks, fj, haw, ve-PP, wa-RR, ss, ts, om, lld, scn, bo, yo and
ku; "identity", "client" and "rescue code" were coined where the language
has no settled word.

Thanks to xet7 !
2026-10-14 17:03:03 +03:00
Lauri Ojansivu c940b3e301 Sign in with SQRL, from a phone or a computer.
SQRL (https://www.grc.com/sqrl/sqrl.htm) signs in with a SQRL app or client
instead of a password. A site admin turns it on in Admin Panel -> Settings,
off by default; WeKan must be at an https:// ROOT_URL. A member adds their
identity once in Member Settings -> SQRL, by scanning the QR code or
opening their SQRL client; the sign-in page then offers Sign in with SQRL,
shows the sqrl:// link and its QR code, and signs in by itself once the
client has answered. With the client on the same computer (cps), the
client opens WeKan with a one-time token instead.

The protocol is GRC's SQRL Semantics: the client posts signed requests to
/sqrl, each verified with Ed25519 by Node.js's own crypto, no new
dependency for it. Every nut is random and used once, taken atomically;
each request must carry the server's previous reply unchanged; a request
from another address than the sign-in page's is refused unless the client
says noiptest; disable needs the identity's signature, and enable, remove
and rekeying need the rescue code's unlock key. An identity WeKan does not
know can neither make an account nor sign in. The page learns of success
by polling with a secret only it holds, and signing in uses that once.
The identity's public keys are a user field never published and not
writable from the client. Not offered on Sandstorm. sqrlonly and hardlock
are not supported. The QR code is qrcode-generator 1.4.4 (MIT, pinned).

models/lib/sqrl.js is the protocol with storage and signature check passed
in; tests/sqrl.test.cjs drives it with a software SQRL client using real
Ed25519 keys - adding an identity, the waiting page, cps, another network,
forged signatures, replayed nuts, altered replies, unknown identities,
another member's identity, disable/enable, rekey and remove - and pins the
server's single-use and visibility rules and the UI.
server/lib/tests/sqrl.tests.js runs in Meteor (2 passing): off by
default, then an identity added and signed in with once, through the real
/sqrl route and MongoDB. Documented in docs/Features/Login/SQRL.md. The
new strings are English placeholders until the translations that follow.

Thanks to xet7 !

Fixes #2460.
2026-10-14 17:02:20 +03:00
Lauri Ojansivu 8d71c6f925 Accessibility: every icon-only link and button has a name for screen readers.
21 controls showed only an icon and had no title, so a screen reader
announced them as an unnamed link: the swimlane and list drag handles, the
close buttons of the inline forms, the received, start, due and end date
buttons of a card, the add-attachment and edit-custom-field buttons, the
notification drawer's menu and close, the avatar's edit button, the list
open and back carets on a phone, the import's add-member and History's
previous and next. Each now has a translated title, which
client/lib/accessibility.js also makes its aria-label; the existing
translations are used, so no new strings.

tests/iconOnlyControlLabels.test.cjs scans every template for a link or
button whose only content is an icon and fails on one without a name.

This is part of #337, which stays open for the rest.

Thanks to ndarilek and xet7 !
2026-10-14 16:59:57 +03:00
Lauri Ojansivu 9138e85091 Edit a card description together, live, with Yjs.
Everybody who has a card's description open now edits one shared Yjs
document, so two people typing at once both keep what they typed; before,
the later Save replaced the earlier one. The editor stays the Markdown
textarea: each change in it becomes the smallest Y.Text edit, and the
others' changes are written into it with the caret kept where it was.
Save writes card.description exactly as before.

The server keeps the document in a server-only collection and publishes it
only to people who may edit the card. Updates are merged with a write that
is conditional on the state merged into, so two editors at once cannot lose
each other's typing; each update is limited to 256 KB and the document to
1 MB, and rate limited. A description changed some other way (the API, a
rule, an import) starts the document again from the new text, under a new
generation, so an editor never merges the old document into the new one.
A document nobody has touched for a day is started again from the saved
description the next time it is opened.

yjs 13.6.33 (MIT) is pinned. tests/sharedDescription.test.cjs runs the
real Yjs: concurrent edits merge, a repeated state is harmless, the caret
follows remote edits, an emoji is never split; it pins the server's checks
and that nothing else touches the document.

Thanks to dalareo and xet7 !

Fixes #140.
2026-10-14 16:49:21 +03:00
Lauri Ojansivu 5f2dd80f41 noIdentityReplacement: skip every _build* output directory, not only _build.
A test run with METEOR_LOCAL_DIR writes rspack's output to
_build-local-test/, a bundled second copy of every source file. The guard
walked into it and reported a replacement the source had already fixed.

Thanks to xet7 !
2026-10-14 16:47:04 +03:00
Lauri Ojansivu 4234081752 WebDAV attachment storage: attachments in Nextcloud Files or any WebDAV server.
Admin Panel -> Attachments has a WebDAV (Nextcloud Files) pane beside S3,
Azure and Google Cloud Storage: a folder address, a user and an app
password, with the same read and write switches, connection test, default
storage choice and Move Attachment target as the others. Files are stored
with PUT, read with GET and removed with DELETE; the folders are made with
MKCOL as needed, and the connection test is a PROPFIND of the folder.

The password is a secret field like the other backends' keys: never
published, and kept when the form is saved without it. The address is the
site admin's own setting, so a Nextcloud on WeKan's own network can be
used; redirects are refused so it cannot be pointed somewhere else.

With the CalDAV sync of the previous commit this completes #687. The
Nextcloud ExApp is built by .github/workflows/nextcloud.yml; publishing it
to the Nextcloud app store is the maintainer's own step.

Thanks to scriptPilot and xet7 !

Fixes #687.
2026-10-14 16:46:59 +03:00
Lauri Ojansivu c76f3e2e98 CalDAV: a board's card due dates in Nextcloud Calendar or any CalDAV calendar.
Board Settings -> Date has Calendar sync. A board admin gives a calendar
collection's address, a user and an app password made for WeKan, and turns
it on. Each card with a due date is one event, <cardId>.ics, UID
<cardId>@wekan, an hour long, with the card's title and link; a card that
loses its due date or is archived has its event removed. Every 15 minutes
the events are read back, and a due date moved in the calendar comes back
to its card. Sync all now pushes every card at once.

The link and its password are in a server-only collection that is never
published, and only a board admin can read or change it. Requests go
through webhookFetch: the SSRF guard, so a calendar on WeKan's own network
is reached only when a site admin lists it under Internal destinations.
Text values are escaped as RFC 5545 requires - a semicolon included, which
the first version of the test did not catch.

Thanks to scriptPilot and xet7 !
2026-10-14 16:46:29 +03:00
Lauri Ojansivu bf633b9c72 ActivityPub: a public board can be followed from Mastodon and the fediverse.
Admin Panel -> Settings has a switch, off by default. With it on, every
public board is an ActivityPub actor, @board-<id> at this WeKan's address:
webfinger finds it, its actor document carries its public key, and its
outbox and followers collections can be read. New card activity on the
board is sent to its followers as signed Notes.

It is read-only. The inbox acts only on Follow and Undo Follow, and only
with a valid HTTP signature from the actor that sent it; nothing a
fediverse server sends changes a board. Private boards are never actors:
every board route goes through one helper that serves only a public,
unarchived board while the feature is on, and
tests/restBoardDomainsAccess.test.cjs now knows that helper. The keys are
in a server-only collection, and every outgoing request goes through the
SSRF guard.

Thanks to ASLLR and xet7 !

Fixes #4198.
2026-10-14 16:46:15 +03:00
Lauri Ojansivu 2c9df4db88 Add the ActivityPub, CalDAV and WebDAV strings, translated into every locale.
The 16 strings of the next three commits: the Admin Panel switch that lets
public boards publish to the fediverse, Board Settings' calendar sync, and
the WebDAV attachment storage pane - in English, pending on Transifex, and
in all 234 other locales from each locale's own words for board, card,
due date, storage, folder and password.

Low confidence: tlh, chr, wal, ay, ace, rn, sm, st, kw, csb, uz-AR, iu, zgh,
nah, gn, wo, rup, se, sn, tn, kl, bua, bi, to, ti, nso, la, tig, ak, bm, ff,
gv, vo, sah, ve, tpi, dz, qu, ee, lg, ks, wa, ve-CC, bo, fj, haw, lld, om,
ss, ts, ve-PP and wa-RR; "sync", "folder", "server" and "app password" were
coined where the language has no settled word. "WebDAV (Nextcloud Files)"
names the storage in each language, so it is not left in English.

Thanks to xet7 !
2026-10-14 16:45:44 +03:00
Lauri Ojansivu 0c6c0c6021 Index cards.parentIds, so finding subtasks no longer scans every card.
Since a card can be a subtask of several parents (v12.10), its children are
found with { $or: [{ parentId }, { parentIds }] }. Only parentId had an
index, and a $or uses indexes only when every branch has one, so the query
was a collection scan - in the card remove hook for every removed card, in
card copy and in the Excel and PDF exporters. parentIds now has its own
index beside parentId's.

tests/cardQueryIndexes.test.cjs reads the branches of every $or in
models/lib/cardParents.js and requires an index for each field, so a new
branch cannot arrive without one.

Thanks to Raymond-Voogt and xet7 !

Fixes #6770.
2026-10-14 16:43:35 +03:00
Lauri Ojansivu 587efab82b Fix GitHub CodeQL alerts #584 and #585.
#584 (bad HTML filtering regexp): tests/accountEmailHtml.test.cjs checked
that the e-mail layout escapes markup with /<img|<script|<b>/, which a
<SCRIPT> passes. It now lists every tag in the output, in any letter case,
and allows only the layout's own div, p, a and br; an upper- and mixed-case
input is tried as well. The renderer was already escaping them.

#585 (incomplete regular expression for hostnames): the same test made the
reset address into a RegExp; it compares the text instead.

Both are pinned as shapes in tests/securityAlerts20261012.test.cjs, which
found more of them: a case-sensitive <script> pattern in jsWebLabels.test.cjs,
a path spliced into generated code in singleFile.test.cjs (now passed through
the child's environment), and twelve test patterns with a host name's dot
unescaped. No regex literal in the tree leaves one unescaped now.

These are in tests, so there is no attempt at runtime to record in Admin
Panel -> Problems.

Thanks to GitHub CodeQL and xet7 !
2026-10-14 16:43:00 +03:00
Lauri Ojansivu cd4c4ad9ce Close #1273: keeping #label and @member in a new card's title is done.
It shipped in v12.43 (a6b2938e1c): Member Settings -> "Keep #label and
@member in new card titles". This pins the issue's own example: with the
switch on, "fix menu for #PROJECT by @member" stays whole; off, the default,
it still becomes "fix menu for by".

Thanks to biji and xet7 !

Fixes #1273.
2026-10-14 16:31:45 +03:00
Lauri Ojansivu 2b84e6b616 Translate the push, linked-list, column-set, drawing and team-tab strings into every locale.
The 39 strings of browser push notifications, the webhook activity picker,
the activity feed, linked whole lists, column sets, card drawing and the
organization/team tabs, in all 234 non-English locales, from each locale's
own board, card, list and member terms.

Low confidence: ace, ay, chr, csb, kw, rn, sm, st, tlh, wal, zgh, iu, nah,
gn, wo, tn, sn, se, rup, cv, bo, fj, haw, lld, om, ss, ts, ve-PP and wa-RR;
"column", "browser", "device" and "drawing" were coined where the language
has no settled word.

Wrong-language values corrected: Macedonian had the Russian "Уведомления"
and the Bulgarian "Сваляне" (now Известувања, Преземи); Catalan, its Spain
variant and Valencian had "Membres d'Afegeix" for Add members; Klingon had
s3-bucket in English. Catalan "No" is the Catalan word and is now accepted
as complete, as Aragonese "no" already was.

Thanks to xet7 !
2026-10-14 16:31:41 +03:00
Lauri Ojansivu edc5ecd477 Merge remote-tracking branch 'origin/main' 2026-10-14 16:08:55 +03:00
Lauri Ojansivu d04b8444a6 All Boards: a tab per organization and team, where their admins manage members.
The left menu of All Boards gets a tab for each organization and team
the member is in. A tab shows the boards shared with that group (through
the facet filter of #4791) and who is in it; the group's admins -
orgs.$.isAdmin, and now teams.$.isAdmin for teams - and site admins add
members by user name and remove them there. A group whose membership
comes from the sign-in provider (LDAP and other directory sync) is shown
read-only, even to a site admin: WeKan reads directory groups and never
writes them back. A member sees only their own groups; organization and
team memberships stay out of what a client may write.

models/lib/groupMembers.js decides access and builds the membership
changes; tests/groupMembers.test.cjs covers admins, site admins, members,
strangers, a directory-synced group, double membership and the server's
checks. The new strings are English placeholders until the next
translation pass.

Thanks to xet7 !

Fixes #802.
2026-10-14 15:52:07 +03:00
Lauri Ojansivu c772769c0e Draw or write by hand on a card, saved as PNG and SVG and editable later.
Card menu / Drawing opens a drawing surface: pick a colour and a pen
width and draw with a mouse, pen or finger; undo the last stroke or clear.
Saving keeps the strokes on the card (drawings), so the drawing opens
again for editing from the same popup, and uploads a PNG of it as a
card attachment through the normal upload - so it shows wherever a
picture does. "Download as SVG" builds the SVG from the strokes. The
drawing code is the dependency overlay's ("Red Strings", #3392): strokes
are SVG paths.

Only numbers, the allowed colours and pen widths ever reach the SVG -
a point or colour carrying text is dropped - so nothing typed becomes
markup, and the SVG is not uploaded as a file at all (uploads treat SVG
as dangerous content). models/lib/cardDrawing.js cleans strokes, builds
the SVG and replaces an edited drawing; tests/cardDrawing.test.cjs
covers paths, dots, edits, injected colours, widths and points, clamping
and the bound. A Meteor run started with the new card field. The new
strings are English placeholders until the next translation pass.

Thanks to aleroot and xet7 !

Fixes #1178.
2026-10-14 15:51:47 +03:00
Lauri Ojansivu 03d0144538 Column sets: the same cards in several named sets of columns.
A new board view, Column sets. A board keeps named column sets - say
Completion, Priority, Milestone - each with its own coloured columns, and
every card is in one column of each set, or "Not placed". Picking a set
shows its columns; dragging a card to another column changes only where
it is in that set - its list, swimlane, labels and members stay - and
each card shows, as coloured labels, its column in the other sets, as the
issue asked. Board admins make and delete sets and columns; moving cards
needs the right to edit a card. Clicking a title opens the card.

Registered like every view: the Board View menu (appended after Version
Control), the default order and VIEWS_ADDED_LATER, the user's boardView
values and the API description, and an instance feature group so the
Admin Panel can turn it off. models/lib/columnSets.js holds the logic;
tests/columnSets.test.cjs covers places, removed columns, the other
sets' badges, foreign columns, dotted keys and the bounds, and
boardViewMenu / boardViewSettings now expect the view last. A Meteor run
started with the new fields. Documented in
docs/Features/Board/Column-Sets.md. The new strings are English
placeholders until the next translation pass.

Thanks to kentonv and xet7 !

Fixes #211.
2026-10-14 15:48:42 +03:00
Lauri Ojansivu 2fd3993538 A linked whole list: a live mirror of another board's list.
The add-list form gets "or link a list from another board": pick a board
you can see and one of its lists, and this board gets a list that shows
that list's cards as they are, live. They stay the source board's cards,
under its rules - opened, edited, moved by whoever may write the source
board - and a card typed into the mirror is added to the source list by
addCardToLinkedList, which checks the right to write in that list. In
the Swimlanes view the mirror shows its cards once, in the first
swimlane. The source cards come through linkedListCards, published only
to a subscriber who may see both boards, so the board publication is not
widened. A list cannot mirror its own board, an archived or foreign list,
or another mirror.

models/lib/linkedList.js decides; tests/linkedList.test.cjs covers the
selector, the swimlane rule, the refused links, both visibility checks
and the source board's write rule. A Meteor runtime run started with the
new list fields. The new strings are English placeholders until the next
translation pass.

Thanks to javen9881 and xet7 !

Fixes #2809.
2026-10-14 15:44:38 +03:00
Lauri Ojansivu 0a5ae3e205 Browser push notifications, per device.
Member Settings / Notification Settings / "Browser notifications on this
device": the browser asks for permission and gives a push subscription,
and from then on every notification the member gets in WeKan - the same
recipients, watching and muted kinds as the tray and e-mail - also pops
up from that browser even with WeKan closed; a click opens the card. Turn
it on per browser and device, up to ten per account.

web-push 3.6.7 (pinned) sends it with this WeKan's own VAPID key pair,
made once and kept in a server-only collection; only the public key
leaves the server. A subscription's endpoint is chosen by the browser and
POSTed to by the server, so it is accepted only over https and only at
the push services browsers use - an internal address or another site is
refused when saved and again before every send. Gone subscriptions are
forgotten. The service worker opens only paths of this WeKan. Not
offered on Sandstorm.

models/lib/webPush.js decides; tests/webPush.test.cjs covers the push
services, internal and look-alike hosts, foreign click targets and the
bound; server/lib/tests/webPush.tests.js runs in Meteor (passing): one
key pair, only the public key out, an internal endpoint refused. With
the webhook activity picker (c3321926ab) and the all-boards activity
feed (656af74b2c), this completes both issues. The new strings are
English placeholders until the next translation pass.

Thanks to saurabharch, hingerl and xet7 !

Fixes #2026,
Fixes #1377.
2026-10-14 15:44:01 +03:00
Lauri Ojansivu d3966fa6b9 Fix the custom field cardKinds schema: WeKan stopped at startup.
The Story and Task commit (0d77882c3b) declared cardKinds as
`type: [String]`, which the schema library rejects at startup ("Invalid
definition for cardKinds field: type may not be an array"), so WeKan
stopped. It is `type: Array` with a 'cardKinds.$' entry of the two kinds
now. Found by a Meteor runtime test run; tests/cardKinds.test.cjs now
pins the form and checks that no array shorthand type is left in the
schemas these changes touched.

Thanks to xet7 !
2026-10-14 15:41:26 +03:00
Lauri Ojansivu 656af74b2c An activity feed across all of a member's boards.
Member menu / "Activity on all my boards" (/activity) lists what
happened on every board the member sees in All Boards, newest first, a
page of 50 at a time: who, what - in the same sentence the notifications
use - on which card and board, and when, with the card linked. It never
carries a field's value, so an admin-only custom field cannot leak
through it; on a board where the member sees only cards assigned to
them, only those cards' activity is listed; and it is empty when the
Admin Panel hides activities or turns them off.

models/lib/activityFeed.js shapes and filters the entries;
tests/activityFeed.test.cjs covers the entry, an unseen board, the
assigned-only rule, the bound and the server's switches. Part of #2026
and #1377, with the webhook activity picker and browser push. The new
strings are English placeholders until the next translation pass.

Thanks to xet7 !
2026-10-14 15:37:31 +03:00
Lauri Ojansivu c3321926ab Webhooks: pick which activities a webhook is sent.
An outgoing webhook was sent every activity, or whatever its activities
list said with no way to change it in the interface. Its form now has
"Send this webhook:" with "Every activity" and a tick per kind of
activity - the same groups a member mutes notifications by (#572):
labels, members, assignees, comments, moves, dates, checklists,
attachments, custom field values, archive and created. Ticks are saved as
the act-<type> descriptions the activity hook matches webhooks by;
nothing ticked, or Every activity, stays "all", the default.

models/lib/webhookActivityPicker.js maps both ways;
tests/webhookActivityPicker.test.cjs checks that a picked group selects
exactly its activities through webhookActivitySelection, the form state,
and that a webhook never silently gets nothing. Part of #2026 and #1377,
with the activity feed and browser push. The new strings are English
placeholders until the next translation pass.

Thanks to xet7 !
2026-10-14 15:37:16 +03:00
Lauri Ojansivu d8690a26f5 Cards changed since a member last opened them are marked, as a Member Settings switch.
Member Settings / "Mark cards changed since I last opened them", off by
default. With it on, a minicard gets a blue edge when anything of the
card changed after the member last opened it - its own fields (title,
description, dates, labels, members), its checklists and their items,
attachments and comments - until they open it. Turning it on records
when, so the cards a member never opened are not all marked at once, and
closing a card counts as having seen it, so their own edits made while
it was open do not mark it.

models/lib/unreadChanges.js takes the latest change across those
documents and compares it; tests/unreadChanges.test.cjs covers each kind
of change, seen since, off, older than the switch and the wiring. The
new strings are English placeholders until the next translation pass.

Thanks to Lacour20, martinpospech and xet7 !

Fixes #2111,
Fixes #1604.
2026-10-14 15:37:02 +03:00
Lauri Ojansivu 946f032883 Translate the cross-board subtask, checklist owner, shared header, facet and card kind strings.
The 16 strings added as English placeholders by the #1931, #1618, #2805,
#4791 and #2012 commits are translated in all 234 locales, each with that
locale's own words for board, card, list, swimlane and label, and its
usual agile terms for Story and Task (where that is the English word, a
still-correct variant such as "User-Story").

Low confidence, for a native speaker to check: ace, ak, am, as, ay, ba,
bi, bm, bo, bua, chr, co, csb, cy-GB, dz, ee, ff, fj, fo, fur, ga, gd, gn,
gv, ha, haw, hsb, ht, ig, iu, kl, km, km-KH, km_KH, kok, ks, kw, la, lg,
lld, mai, mg, mi, nah, nap, nd, nso, ny, oc, om, or_IN, ps, qu, rm, rn,
rup, rw, sah, sc, scn, sd, se, sm, sn, so, ss, st, szl, tg, ti, tig,
tk_TM, tlh, tn, to, tpi, ts, tt, uz-AR, ve, ve-CC, ve-PP, vo, wa, wa-RR,
wal, wo, wuu-Hans, xh, yi, yo, zgh.

Thanks to xet7 !
2026-10-14 15:36:32 +03:00
Lauri Ojansivu 1647ed5a78 Record Story and Task cards, cross-board subtasks, checklist owners and the rest in Upcoming.
The commits since: story and task cards, an existing card from another
board as a subtask, a person responsible for each checklist item with
dated items in the calendar and Gantt, adding and dragging cards in the
Table view, one shared list header row, All Boards facets, and a board
admin adding only their own organizations and teams. #5141, #864, #1931,
#1618, #2805, #4791 and #2012 leave TODO Later.

Thanks to xet7 !
2026-10-14 15:27:12 +03:00
Lauri Ojansivu 0d77882c3b Story and Task cards, with custom fields of their own.
A card can be a Story or a Task: card menu / Card kind (or an ordinary
card again), with a book or a checked-box badge on the minicard. A custom
field can be limited to card kinds - "Only on these card kinds", none
ticked being every card as before. A kind's own fields are added to a
card when it gets that kind, and shown only on cards of it; a field of
no kind shows on every card. The one choke point every render uses,
customFieldsWD, leaves out fields of other kinds, so the card details and
minicard agree.

models/lib/cardKinds.js decides; tests/cardKinds.test.cjs covers the
fields a kind brings, not twice, other kinds' fields staying off, unknown
kinds, and the card menu, badge and the field's ticks. The new strings
are English placeholders until the next translation pass.

Thanks to nightwind27 and xet7 !

Fixes #2012.
2026-10-14 15:26:00 +03:00
Lauri Ojansivu 000bc1c490 All Boards groups boards by first character, organization, team and board label.
A facet bar above All Boards shows the first characters of the board
names (one emoji or letter, any case), the organizations and teams the
boards are shared with, and the member's own board labels - each with
its count - and a click shows only that group, in the server-paged
sorted views and the drag-ordered one alike; again, or "Show all boards",
clears it. Board labels are the member's own (profile.boardLabels):
"apply label X to the boards whose name includes Y" files many at once,
and a label can be removed. Counts are over the boards the member sees
in All Boards - the same visibility selector as getAllBoardsPage, hidden
boards left out - never more.

models/lib/allBoardsFacets.js counts, builds each group's selector and
applies labels; tests/allBoardsFacets.test.cjs covers characters, an
inactive organization link, labels of boards no longer visible, escaped
and injected facet values, empty labels and the server's visibility. The
new strings are English placeholders until the next translation pass.

Thanks to xet7 !

Fixes #4791.
2026-10-14 15:25:36 +03:00
Lauri Ojansivu 9d95bda245 One shared, sticky list header row for all swimlanes, as a board setting.
Board Settings / Swimlane gets "One shared list header row", off by
default. With it on, the Swimlanes view shows the list headers once - the
first swimlane's - and the other swimlanes no longer repeat them; that
row stays at the top while the board scrolls, through the existing
sticky list header rule of #3847, which the setting turns on, rather than
a second sticky mechanism (tests/stickyListHeadersSwimlanesView keeps
that so). The only new CSS rule is scoped to boards with the setting.

tests/sharedListHeaders.test.cjs checks the board field, the toggle, the
first-swimlane class, the reuse of the sticky rule and the scoping. The
new strings are English placeholders until the next translation pass.

Thanks to mikesutton and xet7 !

Fixes #2805.
2026-10-14 15:25:17 +03:00
Lauri Ojansivu 7ca1489279 Checklist items get a person responsible, and dated items show in the calendar and Gantt.
Each checklist item gets a "who is responsible" button: the board's
active members to pick from, or nobody; the avatar then shows on the
item. A deny rule keeps the responsible person an active member of the
item's board. Checklist items with a due date (#4755) now appear in the
calendar and in the Frappe and dhtmlx Gantt views as "card: item", with
their own `ci:` ids: opening one opens its card, a calendar drag of one is
refused, and moving one in a Gantt moves only the item's own due date -
never the card's dates.

models/lib/checklistItemSchedule.js collects dated items (not of archived
or missing cards) and decides who may be responsible;
tests/checklistItemSchedule.test.cjs covers both and the wiring of the
three views. tests/checklistItemDueDate.test.cjs, which pinned #4755 as
"due dates only", now says the assignee is #1618's own field. The new
strings are English placeholders until the next translation pass.

Thanks to martinbruno90 and xet7 !

Fixes #1618.
2026-10-14 15:24:57 +03:00
Lauri Ojansivu e9af50ff07 An existing card from another board as a subtask.
The "add existing card as subtask" picker offered only cards of the same
board. It now has a board picker: the card's own board first, then the
other boards the member may write. Another board's cards come from the
server (crossBoardSubtaskCandidates), and picking one attaches it through
addCrossBoardSubtask, which checks on the server, with server data, that
the member may write both the parent's and the child's board, that the
child is an ordinary, not archived card, and that no loop forms through
any parent. The card keeps the parents it has. Same-board picks work as
before.

tests/crossBoardSubtasks.test.cjs runs the loop check across boards and
checks both write rights, the card types, the escaped search and the
popup. The new string is an English placeholder until the next
translation pass.

Thanks to TiibCD and xet7 !

Fixes #1931.
2026-10-14 15:24:23 +03:00
Lauri Ojansivu 5409e059ae Translate the Mattermost, Share to WeKan, passkey and hidden board strings.
The 27 strings added as English placeholders by the #1297, #1293, #5847
and #4265 commits are translated in all 234 locales, each with that
locale's own words for board, card, members and its Member Settings menu
name; WeKan, Mattermost and https:// are kept as they are. Where a
language's word for passkey is the English one, the popup title is
"Your passkeys", so it is not left equal to the English.

Low confidence, for a native speaker to check: ace, ak, am, as, ay, ba,
bi, bm, bo, bua, chr, csb, cv, dz, ee, ff, fj, fo, gd, gn, gv, ha, haw,
hsb, ig, iu, kl, km_KH, kok, ks, kw, la, lg, lld, mai, mg, mi, nah, nap,
nd, nso, ny, om, qu, rn, rup, sah, sc, se, si, sm, sn, ss, st, szl, ti,
tig, tk_TM, tlh, tn, to, tpi, ts, tt, ug, uz-AR, ve, ve-CC, ve-PP, vo, wa,
wal, wo, wuu-Hans, xh, yi, zgh.

Thanks to xet7 !
2026-10-14 15:23:58 +03:00
Lauri Ojansivu 0a566dc9ac Table view: add cards, and drag a row onto another to move its card.
The Table view only listed cards. Now a member who may write gets an add
row under the table - a title, a list and a swimlane - and can drag a row
onto another: the dragged card goes just before the target card, in the
target's list and swimlane, through the same card.move the board uses.
Adding goes through tableViewAddCard, which checks that list and swimlane
are on that board and not archived and that the member may write in that
list, as for any card added. Rows are not draggable without the right.

models/lib/tableViewDrop.js places the card; tests/tableViewDrop.test.cjs
covers between, first and same-list drops, a drop onto itself, the
server's checks and that only writers get the controls.

Thanks to guakamole and xet7 !

Fixes #864.
2026-10-14 15:13:09 +03:00
Lauri Ojansivu ed2649f702 A board admin may add only the organizations and teams they belong to.
setBoardOrgs and setBoardTeams refused nothing but non-admins: a board
admin could link any organization or team of the instance to a board,
and with it give every one of its members the board. Now an organization
or team that is new to the board must be one the board admin belongs to;
a site administrator may add any. Links already on the board stay -
somebody else may have added them - and removing or switching one off is
always allowed. The pickers offer only the admin's own groups, and the
org and team publications, which gave non-admins nothing (so the pickers
were empty for them), now give a signed-in user their own groups' names.

models/lib/boardGroupLinks.js decides; tests/boardGroupLinks.test.cjs
covers own groups, existing links, removals, site admins, a foreign group
refused and not offered, and that both methods check before writing.

Thanks to chrisi51 and xet7 !

Fixes #5141.
2026-10-14 15:04:41 +03:00
Lauri Ojansivu d9e2d7bedd Record passkeys, ntfy, Share to WeKan and the other decided features in a new Upcoming.
v12.43 was cut before these commits, so they go into a new Upcoming above
it: passkey login and Log out everywhere, the login webhook, ntfy, the
clear HTML e-mail default, Mattermost direct messages, read-only Rules
for members, the rule e-mail member picker, the subtask landing swimlane,
hidden team boards, Share to WeKan on Android, Sandstorm card sharing and
the LibreJS labels page. #1953, #2217, #2290, #2435, #4434, #5148, #1040,
#1188, #1297, #1293, #5847 and #4265 leave TODO Later.

Thanks to xet7 !
2026-10-14 15:02:41 +03:00
Lauri Ojansivu 1acc9f2b9f Leaving a board that came through a team hides it, restorably.
A board a member reaches through one of their teams or organizations, or
their e-mail domain, could not be left: without a member entry quitBoard
refused it, and with one the board stayed in All Boards through the team.
Now leaving such a board puts it in the member's own hidden list
(profile.hiddenBoards): All Boards leaves it out, in the paged sorted
views and the drag-ordered one, and Member Settings / Hidden boards
brings it back with Restore. The team or organization membership is not
touched, and access stays the board's own. An All Boards tile of a board
the member is not on gets an eye-slash "leave" button; a member who also
has the board through a team is both removed and hidden. A board reached
only by membership leaves as before.

models/lib/boardInvites.js decides (planQuitBoard's hide,
reachesBoardOtherwise, hiddenBoardsAfter); tests/hiddenBoards.test.cjs
covers team, organization and domain reach, an inactive link, the bound,
that the team is not touched and the All Boards filters.
tests/boardInvites.test.cjs expects the plan's hide: false where nothing
else reaches the board. The new strings are English placeholders until
the next translation pass.

Thanks to LaureHades and xet7 !

Fixes #4265.
2026-10-14 15:01:14 +03:00
Lauri Ojansivu 63ef900d56 Passkey (WebAuthn) login, and Log out everywhere.
Member Settings gets Passkeys: add a passkey on this device under a name,
see the account's passkeys and remove one. The sign-in page gets "Sign in
with a passkey": the browser offers this WeKan's passkeys, no user name
needed. Verification is @simplewebauthn/server 14.0.3 (MIT, pinned)
against ROOT_URL's host and origin, so a passkey cannot sign in to another
site or through another address; each sign-in signs a fresh challenge
that is used once and expires in five minutes, and the signature counter
must move forward, so a copied passkey is refused. A disabled account
cannot sign in with one (the existing validateLoginAttempt hooks apply).
Passkeys are a top-level user field, never published and not writable
from the client; up to 20 per account. Not offered on Sandstorm.

Member Settings also gets Log out everywhere: after a confirmation every
login token of the account goes, so Meteor ends every session - other
browsers, phones, the desktop app - and this one. Nothing else changes.

models/lib/passkeys.js holds the relying party, base64url and the
browser conversions; client/lib/passkeys.js uses navigator.credentials
directly. tests/passkeys.test.cjs signs assertions with a software ES256
authenticator: a real signature verifies, and another key, another
origin, another challenge or a counter that went back are refused; it
also checks single-use challenges, the unpublished field and the pin.
server/lib/tests/passkeys.tests.js runs in Meteor (2 passing): the module
bundles, login options name no account, and registration needs a user.
Documented in docs/Features/Login/Passkeys.md. The new strings are
English placeholders until the next translation pass.

Thanks to xet7 !

Fixes #5847.
2026-10-14 14:59:25 +03:00
Lauri Ojansivu a4727e6764 Allow the Sandstorm share helper's two addresses in the risk baseline.
models/lib/sandstormShare.js (#1188) parses a page path against
http://grain.invalid, a reserved name that is never contacted, and cites
Sandstorm's documentation of startSharing.

Thanks to xet7 !
2026-10-14 14:56:50 +03:00
Lauri Ojansivu 52b0ea0d81 Android: share to WeKan from other apps.
The web app manifest declares a share_target, so on a phone where WeKan
is installed as an app it appears in Android's Share menu. Sharing a web
page, a note or a message opens /share-target, "Add to WeKan": pick a
board and a list, and what was shared becomes a card there - the page's
title or the text's first line as the title, the text and the address in
its description, the address once - and the card opens. Only boards the
member may add cards to are offered, and the server checks the right to
write in that list, on that board, as for any card added.

models/lib/shareTarget.js turns a share into a card and holds the
manifest's declaration; tests/shareTarget.test.cjs covers what browsers,
notes and chat apps share, an empty share, javascript: addresses, the
manifest, the route and the server's checks. Documented in
docs/Platforms/FOSS/OS/Android.md. The four new strings are English
placeholders until the next translation pass.

Thanks to memeplex and xet7 !

Fixes #1293.
2026-10-14 14:55:04 +03:00
Lauri Ojansivu 349d6dc5e6 Webhooks: also send a Mattermost direct message to the card's people.
A saved outgoing webhook gets "Also send a Mattermost direct message to
the card's members and assignees". With it ticked, every message about a
card also goes to each of its members and assignees as the webhook's own
message with channel "@username" - Mattermost's direct-message channel -
so they hear about their cards without following the channel. The person
who made the change is not messaged, nobody twice, a name that is not a
valid Mattermost user name is skipped, and a card stops at 50. A board
webhook does this only for its own board's cards. The direct messages go
at once, best effort, through webhookFetch; the channel message keeps its
grouping and schedule. Two-way webhooks are left as they are.

models/lib/mattermostDirectMessages.js picks the people and builds the
messages; tests/mattermostDirectMessages.test.cjs covers them, the actor,
bad names, the bound, and that it is off unless ticked and sent through
the SSRF guard. docs/Features/Webhooks/Chat/Mattermost.md says what
Mattermost needs. The two new strings are English placeholders until the
next translation pass.

Thanks to xet7 !

Fixes #1297.
2026-10-14 14:54:38 +03:00
Lauri Ojansivu fdc52787e1 Translate the subtask swimlane, read-only Rules, login webhook, rule email member and ntfy strings.
The 24 strings added as English placeholders by the #1953, #2217, #2290,
#2435 and #5148 commits are translated in all 234 locales, with each
locale's own words for rules, members, board, swimlane and subtasks;
@username and the ntfy.sh example addresses are kept as they are, and the
quoted label in the Rules option's description is that locale's own
translation of the board admin's tick.

The Acehnese "keep #label" strings from the earlier pass used Malay
"simpan"; they say "keubah", the file's own word, now
(tests/wrongLanguageLocales). The Maori, Northern Sotho, Somali and Tok
Pisin recovery suites count 50 recovery keys since the four bulk actions
of #6768.

Low confidence, for a native speaker to check: ace, ak, am, ay, ba, bi,
bm, bo, bua, chr, co, csb, cv, dz, ee, ff, fj, fo, fur, fy-NL, gd, gn, gv,
ha, haw, hsb, ig, iu, kl, kok, ks, kw, la, lb, lg, lld, mai, mg, mi, my,
nah, nap, nd, nso, ny, oc, om, or_IN, pap, ps, qu, rn, rup, rw, sah, sc,
scn, sd, se, sm, sn, so, ss, st, szl, tg, ti, tig, tk_TM, tlh, tn, to,
tpi, ts, tt, ug, uz-AR, ve, ve-CC, ve-PP, vo, wa, wa-RR, wal, wo,
wuu-Hans, xh, yi, yo, zgh.

Thanks to xet7 !
2026-10-14 14:54:17 +03:00
Lauri Ojansivu d1d806de2c Sandstorm: Copy card URL shares the card through Sandstorm.
Inside a grain WeKan's own address reaches nobody, so the copied card link
was useless to anybody else. On Sandstorm, Copy card URL - in the card
menu and in the card's link popup - now sends the shell
{ startSharing: { pathname } }, which opens Sandstorm's Share Access
dialog; its sharing link opens the grain at that card. The shell accepts
only a path starting with "/", so only the card's path is sent. Outside
Sandstorm both buttons copy as before.

models/lib/sandstormShare.js builds the message;
tests/sandstormShare.test.cjs checks it, that nothing else reaches the
shell, and that both buttons share only inside a grain.

Thanks to AlexanderZobkov and xet7 !

Fixes #1188.
2026-10-14 14:41:13 +03:00
Lauri Ojansivu 631a7fdfe2 LibreJS: a JS Web Labels page for the browser bundle.
LibreJS runs a page's JavaScript only when it can tell the code is free.
A Meteor bundle is generated, minified and named by hash, so it cannot
carry per-file @license comments; LibreJS's other way is a JS Web Labels
table, linked with rel="jslicense". WeKan now serves /jslicense.html,
built from the client bundle it is actually serving (web.browser and
web.browser.legacy, under ROOT_URL's path), labelling each script with
the free licenses the bundle combines - Expat (MIT), Apache-2.0, Modified
BSD, FreeBSD and ISC, under LibreJS's own names and addresses - and with
WeKan's source at the running version. Every page links it from <head>,
whether or not the custom head is on.

models/lib/jsWebLabels.js builds the table and the link;
tests/jsWebLabels.test.cjs checks the scripts chosen from a manifest, the
table LibreJS reads, escaping and the wiring. releases/risk-baseline.json
allows the license addresses, and ntfy.sh, which the ntfy channel
(d873af70e8) names in a placeholder and comments.

Thanks to alexgleason and xet7 !

Fixes #1040.
2026-10-14 14:39:40 +03:00
Lauri Ojansivu d873af70e8 ntfy as a notification channel, per member.
Member Settings / Notification Settings gets "ntfy phone notifications":
a member pastes their ntfy topic's address, and an access token if the
topic needs one, and each notification they get in WeKan - the same
recipients, watching and muted activity kinds - also arrives in the ntfy
app, with the board and card as the title, what happened as the message
and a tap opening the card.

It uses ntfy's JSON publishing, so a title in any language needs no
header encoding, and goes through webhookFetch: the SSRF guard, Internal
destinations for a self-hosted ntfy, the Powerbox on Sandstorm. An
admin-only custom field value still reaches only board admins. One read
per activity finds the recipients who turned it on; sending is deferred
and best effort, so ntfy being down never touches e-mail or the tray.
The address and token are a top-level user field, saved and read only
through the member's own methods, not client-writable and never
published.

models/lib/ntfy.js parses topic addresses and builds the request;
tests/ntfy.test.cjs covers the request, bad addresses, tokens with header
injection, javascript: links, off, and that the setting is unpublished
and sent only through webhookFetch. Documented in
docs/Features/Notifications/ntfy.md. The new strings are English
placeholders until the next translation pass.

Thanks to xet7 !

Fixes #5148.
2026-10-14 14:37:00 +03:00
Lauri Ojansivu 058677e078 E-mails in the clear HTML layout by default: notifications and account mail.
Notification e-mail now defaults to the clearly arranged layout of #5171 -
board first, board, actor and card in bold and linked, with a plain-text
alternative. One sentence (classic) is still one choice away at the
Admin Panel, board and member levels, and the tray keeps the classic line.

Account e-mails - reset password, verify e-mail, enroll account, and the
board and registration invitations - get an HTML part in the same style,
built from the same translated text, which is still sent as the text
part: no translation changes. Every piece of text is escaped first, the
link paragraph becomes a button, and only an http(s) address is linked. A
caller's own HTML and texts that are not WeKan's e-mail strings are left
as they were, and a failure to build the HTML leaves the text alone.

models/lib/accountEmailHtml.js renders it; tests/accountEmailHtml.test.cjs
lays out the real English texts and checks that markup in a name, a board
title or the site name stays text and only http(s) is linked.
notificationDelivery's default assertions now expect clear for e-mail,
and emailLocalizationPreparation's harness allows the new module, checks
the HTML part, and checks the absent-HTML case with literal text.

Thanks to Meeques and xet7 !

Fixes #4434.
2026-10-14 14:33:10 +03:00
Lauri Ojansivu 75e260966b Rule emails: pick board members for To from a list with autocomplete.
The send-email action gets a member picker under To: typing filters the
board's active members by user name or full name, and a pick adds
@username to To. Addresses are still typed into To as before. When the
rule runs, each @username becomes that member's current email address,
so the rule - and whoever edits it - holds a user name, never somebody's
address, and a member who changes address is still reached. A @name of
somebody who is not an active member of the board, or has no address, is
dropped rather than sent as text, and nobody is mailed twice.

models/lib/ruleEmailMembers.js holds the parsing, the choices and the
expansion; tests/ruleEmailMembers.test.cjs covers them, non-members,
duplicates, an address with @ inside, and that the picker never reads an
email address. rulesHelper imports it, so the ruleEmailFailure harness,
which runs rulesHelper without require, is given it as it is given
ruleParts. The new strings are English placeholders until the next
translation pass.

Thanks to chenghongxi and xet7 !

Fixes #2435.
2026-10-14 14:30:10 +03:00
Lauri Ojansivu ea256dd772 Admin Panel: a login and registration webhook, off by default.
Admin Panel / Settings / Global Webhooks gets one address WeKan tells
when somebody logs in, when a new account is made, or both - nothing is
sent until an event is ticked. Each message is a JSON POST with the
event, the user name and the time; the email, the login's IP address,
the full name and the sign-in method are added only when ticked. A token
goes in X-Wekan-Token, as for board webhooks.

It is sent after the login, through webhookFetch - the SSRF guard,
Internal destinations and, on Sandstorm, the Powerbox - and a receiver
that is down only leaves a log line. The setting is read and saved
through site-admin methods and never published.

models/lib/accountWebhook.js cleans the setting and builds the message;
tests/accountWebhook.test.cjs checks the default carries name and time
only, each extra field only when ticked, the events separately, bad
addresses refused, and that no hash, services or user id is sent.
Documented in docs/Features/Webhooks/Login-and-Registration-Webhook.md.
The new strings are English placeholders until the next translation pass.

Thanks to KrunalDholakiya and xet7 !

Fixes #2290.
2026-10-14 14:25:19 +03:00
Lauri Ojansivu f4a3690256 Rules can be shown to board members, read-only, when the Admin Panel allows it.
Admin Panel / Settings / Visibility / Rules gets "Boards may show their
Rules to members, read-only", off by default (Settings
rulesReadOnlyForMembers, site admin only). With it on, a board admin ticks
"Show these Rules to board members, read-only" on the board's Rules page
(rulesVisibleToMembers). Members of that board then get Rules in Board
Settings and see the rules list and each rule's details, with no control
that changes a rule; the methods that change rules keep their board-admin
checks, so nothing is granted. Without both ticks only board and site
admins see the Rules page, which until now drew its list for any member
who opened the address.

models/lib/rulesReadOnly.js decides 'edit', 'read' or 'none', and
client/lib/rulesAccess.js is the one place the Rules page, Board Settings
and rule details ask. tests/rulesReadOnly.test.cjs covers both switches,
each alone, a non-member, and that a reader's page has no edit control.
boardMenuOrder now leaves the reader's alternative branch out of the
admin's sequence and pins it alone; instanceFeatures counts the fourth
site-admin method; roleHelperRegressions follows the canAdmin check to
rulesAccess.js. The new strings are English placeholders until the next
translation pass.

Thanks to justinr1234 and xet7 !

Fixes #2217.
2026-10-14 14:23:31 +03:00
Lauri Ojansivu 740c3920eb Board Settings / Subtasks: pick the swimlane new subtasks land in.
Beside the landing list, Board Settings / Subtasks now has a landing
swimlane, stored as subtasksDefaultSwimlaneId. As with the list, the main
board's choice wins on a shared deposit board, then the deposit board's
own; a swimlane that is archived, gone or on another board is never used.
With none picked, nothing changes: the swimlane with the parent card's
swimlane title, else the default one (#2732). Changing the deposit board
clears the choice, and a copied board does not keep its template's.

models/lib/subtaskLandingSwimlane.js decides; tests/subtaskLandingSwimlane
covers it, and tests/subtaskSwimlaneInheritance2732 now runs the chooser
for the title match instead of reading the inline lookup it replaced. The
two new strings are English placeholders until the next translation pass.

Thanks to BjoernLudwig and xet7 !

Fixes #1953.
2026-10-14 14:19:52 +03:00
github-actions[bot] f6b97816c6 Bump versions for v12.43 2026-10-14 11:13:43 +00:00
Lauri Ojansivu 533c8c273c Prepare v12.43 release 2026-10-14 14:12:36 +03:00
Lauri Ojansivu e7ceb217ea Move the unreleased entries out of v12.42 into a new Upcoming; add #1273 and #2662.
v12.42 was cut on origin while Bots, board search reveal, label order,
avatar order, internal webhook destinations, the how-tos and the
notification recovery fixes were being recorded locally, and the merge
left their entries inside the v12.42 section although that release does
not contain those commits (tests/changelogEntriesBelongToTheirRelease
caught all eleven). The v12.42 section is restored exactly as released,
and those entries move to a new Upcoming, with the keep #label / @member
setting and the FreeBSD rc.d script. Both leave TODO Later.

Thanks to biji and xet7 !
2026-10-14 14:10:56 +03:00
Lauri Ojansivu 9a29dbd8b7 FreeBSD bundle: an rc.d script, so WeKan runs as a service.
releases/bsd/rc.d/wekan runs the bundle's own start-wekan.sh (bundled
Node.js and FerretDB on SQLite) under daemon(8) as an unprivileged
wekan_user, configured from rc.conf: wekan_dir, wekan_port,
wekan_root_url, wekan_data (WRITABLE_PATH), wekan_logfile and wekan_env.
It has no -r, because start-wekan.sh already restarts WeKan, and stop
signals the whole process group so FerretDB stops with node.

releases/bsd/build-bundle.sh puts it in the FreeBSD bundle as
bundle/rc.d/wekan, and bsd.yml fails a FreeBSD zip without it. The
FreeBSD doc has the install steps, the rc.conf table and an outline of a
ports Makefile to propose to FreeBSD.

tests/freebsdRcScript.test.cjs checks the rc.subr contract with sh -n,
that it never runs as root or restarts twice, and that the bundle ships it.

Thanks to xet7 !

Fixes #2662.
2026-10-14 14:09:11 +03:00
Lauri Ojansivu 8fd04f9984 Translate the keep #label / @member title setting into every locale.
The two Member Settings strings added for #1273 were committed as English
placeholders; this fills them in all 234 locales, with each locale's own
words for label, member, card and title. "#" and "@" are kept as they are.

Low confidence, for a native speaker to check: ace, ak, am, ay, ba, bi,
bm, bo, bua, chr, csb, cv, dz, ee, ff, fj, fo, fur, gd, gn, gv, ha, haw,
ig, iu, kl, km-KH, kok, ks, kw, la, lg, lld, mai, mg, mi, nah, nap, nd,
nso, ny, om, pap, qu, rn, rup, rw, sah, sc, se, sm, so, ss, st, szl, ti,
tig, tk_TM, tlh, to, tpi, ts, tt, uz-AR, ve, ve-CC, ve-PP, vl-SS, vo, wa,
wa-RR, wal, wuu-Hans, yi, zgh.

Thanks to xet7 !
2026-10-14 14:09:11 +03:00
Lauri Ojansivu a6b2938e1c Updates 2026-10-14 14:04:07 +03:00
Lauri Ojansivu 331d1e347b Updated translations. 2026-10-14 14:03:00 +03:00
Lauri Ojansivu caa9a29f92 Merge remote-tracking branch 'origin/main' 2026-10-14 14:02:03 +03:00
Lauri Ojansivu cbbc9a7338 Record the notification and cron fixes, internal webhook destinations and the how-tos in Upcoming.
#632, #1324, #1126, #1100 and the internal webhook destinations leave TODO Later.

Thanks to Raymond-Voogt, thuanpq, killua-eu, Dreampie and xet7 !
2026-10-14 14:00:47 +03:00
Lauri Ojansivu e0317a9ff3 Drain the activity notification recovery list; index synced-cron's lookup.
#6768: the recovery scan retried every pending activity notification each
round, and one whose delivery fails for good - its activity deleted or
changed since, a recipient who lost access, a plan with nothing or too much
to send - failed the same way every round, forever, so the list only grew.
Such an error is terminal now: after a ten-minute grace (an activity being
written is never touched) the scan cancels that notification through the
same cancellation an administrator uses, as the system, which compacts it
to a cancelled receipt. A pending notification older than
ACTIVITY_NOTIFICATION_MAX_AGE_DAYS (7) is cancelled as expired instead of
being delivered a week late. Network and other transient failures are still
retried. Admin Panel -> Problems gets "Retry all" and "Cancel older..." for
the whole list at once, site administrators only.

#6769: before each run synced-cron asks whether a job of that name is still
running - { name, finishedAt: { $exists: false } } - every minute for every
job on every instance, and only { intendedAt, name } and the TTL on
startedAt were indexed, so it scanned the job history. { name, finishedAt }
is created at startup with WeKan's ensureIndex.

The new strings are translated in every locale.

Thanks to Raymond-Voogt and xet7 !

Fixes #6768,
Fixes #6769.
2026-10-14 14:00:21 +03:00
Lauri Ojansivu b4d2604e61 Outgoing webhooks may reach internal hosts a site admin lists.
Admin Panel -> Settings -> Global Webhooks -> Internal destinations: the
hosts on WeKan's own network - a Node-RED, a chat server, a Matrix hookshot
- that outgoing webhooks may reach, one per line, a name or an address,
with a port to allow only that port. Empty by default. Only site
administrators read and change it, through methods, and it is never
published, so a board member still can not point a webhook inward and the
list does not describe the network to anyone else. A listed host is called
directly without following redirects; every other address stays behind the
SSRF guard, and the REST routes that save a webhook accept a listed host as
well. The Node-RED how-to (#2017) says how.

The new strings are translated in every locale.

Thanks to xet7 !
2026-10-14 13:59:19 +03:00
Lauri Ojansivu dff0856585 How-to: Matrix notifications through an outgoing webhook, no bot.
matrix-hookshot's generic webhooks post a JSON body's top-level `text` into
a Matrix room, and WeKan's outgoing webhook already sends the activity as
a sentence in `text` - so a board's (or the instance's) outgoing webhook
pointed at a hookshot address is all it takes. WeKan needs no Matrix
account, token or bot.

Thanks to killua-eu and xet7 !

Fixes #1100.
2026-10-14 13:48:27 +03:00
Lauri Ojansivu c47fe98ab0 How-to: RFID tags moving cards through the REST API.
A USB or serial RFID reader and a short Python script, as a WeKan user with
write access to the board: the scanned tag finds its card through an RFID
custom field (GET .../cardsByCustomField) and moves it to another list, or
comments on it, or creates a card for an unknown tag - and a Rule can do
the rest.

Thanks to xet7 !

Fixes #1126.
2026-10-14 13:47:50 +03:00
Lauri Ojansivu f715e95b40 How-to: Bitbucket login through the generic OAuth2 settings.
Bitbucket Cloud needs no provider of its own: an OAuth consumer with the
callback /_oauth/oidc, and the OAuth2 settings pointing at bitbucket.org's
login pages and api.bitbucket.org's /2.0/user, mapping uuid, nickname and
display_name. Checked against the code: an endpoint given as a full URL is
used as it is, and the client secret goes in the token request as Bitbucket
accepts it. Bitbucket's user record has no email, which the how-to says,
with what that means for OAUTH2_ALLOWED_EMAIL_DOMAINS.

Thanks to thuanpq and xet7 !

Fixes #1324.
2026-10-14 13:47:50 +03:00
Lauri Ojansivu 28477b5a85 Rancher: install WeKan with its Helm chart on current Rancher.
The page described Rancher 1 with Rancher Active Proxy and the
wekanteam/wekan:meteor-1.4 image. It now describes Rancher v2: adding the
WeKan chart repository (wekan.github.io/charts), installing with ROOT_URL,
a release tag, a persistent volume for FerretDB's files under
WRITABLE_PATH and the cluster's ingress and certificate issuer, and
upgrading - in Rancher's own screens and with helm.

Thanks to Dreampie and xet7 !

Fixes #632.
2026-10-14 13:47:50 +03:00
Lauri Ojansivu f8f0f6c5ac Record Bots, board search reveal, label order sort and avatar order in Upcoming.
#2110, #2171 and #2040 leave TODO Later.

Thanks to HolgerGH, rlach, mnantel and xet7 !
2026-10-14 13:45:53 +03:00
Lauri Ojansivu 7dd484f8c2 Board search steps back too: Shift+Enter and the arrows beside the count.
The decision for #2171 was next AND previous; only next was there. Now
Shift+Enter in the search box steps to the previous card found, the arrows
beside "2 of 7" step either way for a mouse or a touch screen, and both
wrap round - back from the first is the last.

Thanks to rlach and xet7 !
2026-10-14 13:45:49 +03:00
Lauri Ojansivu cba618e2f9 Sort a list by the board's label order.
A board's labels have an order now - a board admin moves a label up or down
when editing it (the same write, and permission, as editing a label) - and
Sort cards has "Label order": each card comes where its first label comes
in that order, cards without a label last, and cards that rank the same
keep the order they were dragged into. Like "Sort by votes" it is a display
order only: no card's manual position changes, so turning it off gives the
dragged order back.

The new string is translated in every locale.

Thanks to mnantel and xet7 !

Fixes #2040.
2026-10-14 13:44:33 +03:00
Lauri Ojansivu c239177cd8 Board search shows each card found on the board, and Enter steps on.
A board search found cards and listed them in the sidebar, but finding the
card on the board was still the reader's job; a browser's own find did not
help either, as the issue shows. Now a result opened from the sidebar is
also revealed on the board - scrolled into the middle of the view and
highlighted for a moment - and pressing Enter again in the search box steps
to the next card found, wrapping round, with "2 of 7" under the box, the
way finding on a page works. A list found is revealed the same way. The
highlight does not pulse for people who ask for reduced motion.

The new string is translated in every locale.

Thanks to rlach and xet7 !

Fixes #2171.
2026-10-14 13:43:08 +03:00
Lauri Ojansivu c56c03d202 Minicard avatars in the order people were added, each with its own colour.
The minicard floated every avatar to the end of its row, which drew them
in reverse: the person added first, whom a team treats as the responsible
one, came last, while the opened card showed them first. The avatars are a
wrapping row aligned to the end now, in the order they were added - still
wrapping right-aligned as #5081 asked. Each person's initials avatar has a
colour of their own, the same everywhere and every time
(models/lib/avatarColor.js), every colour keeping the initials readable at
WCAG AA, and the avatar's tooltip names the person.

Thanks to HolgerGH and xet7 !

Fixes #2110.
2026-10-14 13:40:15 +03:00
Lauri Ojansivu aa48a2b71d Board Settings / Bots: hand cards to external runners, as kanbots does.
Board Settings -> Bots, below Workflow view, is the Rules page's view for
bots: runners on your own server - an AI agent, a CI job, a script - that
the board hands cards to. It is laid out like kanbots in the board's own
theme, Font Awesome and drag and drop: a rail of bots, live runs and
activity; the board's lists as columns, each card with its run's state
(Queued, Running, Waiting for you, Ready to review, Done, Failed, Stopped),
the step it is on and the answers to a question it asked; and a drawer with
the card's thread, Hand to bot, Stop and a reply box. Board admins only,
like the rest of the Rules page, and never a board or public view.

A bot has no rights of its own, like Rules and workflows. WeKan only sends
to it: a bot is an outgoing webhook marked as a bot, subscribed to no board
activity and no reminders, and a dispatch goes through the ordinary webhook
queue - the SSRF guard, its signing token, retries, Sandstorm's Powerbox.
The runner works on the board through WeKan's REST API with its own user's
token and board role, and reports its run at one REST route that needs
write access to the board. Handing over is a board admin's choice, in the
view or with the new Rule action "Hand the card to bot"; an answer or a
reply is the admin's own comment, under their right to comment.

The GitHub- and Gitea-compatible APIs' routes are regular expression
literals now, and an instance board is Trello's "org" level.

The new strings are translated in every locale.

Thanks to xet7 !
2026-10-14 13:39:18 +03:00
github-actions[bot] 39c3e64563 Bump versions for v12.42 2026-10-14 10:16:44 +00:00
Lauri Ojansivu 84e402638c Prepare v12.42 release 2026-10-14 13:15:39 +03:00
Lauri Ojansivu 09ecd5f66a Record the optional APIs and the board key backfill fix in Upcoming.
#4930 leaves TODO Later.

Thanks to Raymond-Voogt and xet7 !
2026-10-14 13:14:21 +03:00
Lauri Ojansivu d0378f33c2 Give old boards their keys in one pass, not one query per board.
v12.41 gave every board without a key one at startup by re-reading every
key of the instance for each board, and counting up from 2 again for every
board that shares a title: quadratic, and on 32,500 boards hours of slow
`{ key: { $exists: true } }` queries. Now the keys in use are read once,
the new keys are planned in memory with a counter per base
(models/lib/boardKey.js planBoardKeys, linear), and written in batches of
1000, each only where the board still has no key, so a key a board admin
set meanwhile is never replaced. A single new board reads only the keys
that could collide with its suggestion (an index range on the new `key`
index), not all of them. Planning 32,500 boards takes well under a second;
600 boards are written in under one on a running server.

Thanks to Raymond-Voogt and xet7 !

Fixes #6767.
2026-10-14 13:13:55 +03:00
Lauri Ojansivu 86ec72fade Optional APIs: Trello, Jira, GitHub and Gitea compatible, off by default.
Admin Panel / Settings / Visibility / Optional APIs ticks REST APIs in the
shape of Trello's, Jira's, GitHub's and Gitea's, so apps and scripts written
for them - Trellinator, Jira and GitHub clients - can use WeKan as their
backend, each described on the page with the address a client uses.

None has rights of its own. Each is a translator: a request in the other
tracker's shape becomes the matching request to WeKan's own REST API, made
with the caller's own WeKan token on the loopback address, and the answer
becomes the other tracker's JSON. Every WeKan permission therefore applies
unchanged - read-only and comment-only roles, assigned-only members, list
permissions - and an API that is not ticked answers 404. All of them sit
under /api, so WITH_API turns them off with the rest of the REST API.

Trello: members, boards, lists, cards, labels, comments and checklists; a
deleted card is archived. Jira: projects are boards (the board key), issues
are cards (DEV-7), statuses are lists and transitions move cards, with
simple JQL. GitHub and Gitea: repositories are boards and issues are cards,
open or closed as in Board View / Version Control, with labels, milestones,
comments and Link pagination.

The new strings are translated in every locale. A runtime test over real
HTTP shows a read-only member refused and a Trello-created card readable
through Jira and GitHub.

Thanks to xet7 !

Fixes #4930.
2026-10-14 13:11:23 +03:00
Lauri Ojansivu 0b22fb6b69 Record the GitHub organization import and the Rules changes in Upcoming.
Thanks to xet7 !
2026-10-14 12:44:00 +03:00
Lauri Ojansivu 5eb3d45655 Merge remote-tracking branch 'origin/main' 2026-10-14 12:43:43 +03:00
Lauri Ojansivu 935e68dfdd GitHub organization import: repositories, issues, releases, Actions, wiki.
Import -> GitHub -> Import a whole GitHub organization makes every
repository of an organization or user its own board, opening in Board View
/ Version Control. Issues go through the ordinary importBoard, as the person
who started the job, with their comments, labels and files. Releases are
cards in a Releases list, their files attached only when chosen. Each
.github/workflows file becomes Rules. The wiki is read from its git
repository with isomorphic-git (1.42.4, pinned), every request through the
SSRF guard and only to github.com: a page is a card in the Wiki list, its
[[links]] point at and are attached to the linked pages' cards, and each
commit that changed it is a dated edit in the card's history, so Restore
brings back an older version. The job shows progress, can be cancelled,
pauses on a restart and stops at a rate limit; the token is kept in memory
only.

Version Control gains Releases and Wiki tabs: the wiki as GitHub shows it,
with the page list, _Sidebar and _Footer, links that stay in the tab, and
each page's history with Restore.

GitHub Actions workflows map to Rules: issue events to triggers, label,
close, reopen, comment, assign and create steps to actions, actions/stale to
an aging rule. Code - builds, tests, scripts - is never run: such a workflow
is a disabled rule with its YAML as the description. WeKan's Rules gain what
they lacked for it: a "commented on" trigger, an "add comment" action written
as the person whose change ran the rule and only where they may comment,
cron schedules in UTC, and a rule description shown on the rule. A disabled
rule no longer fires on a schedule or from its button, which it did.

The new strings are translated in every locale.

Thanks to xet7 !
2026-10-14 12:43:36 +03:00
Lauri Ojansivu 842a5a8781 Record Version Control and the Android bundles in a new Upcoming.
v12.41 was already released when the Android bundles were committed, so
their entry was in the wrong section: it moves to a new Upcoming, and
v12.41's In short says again what was released. Upcoming records the
Version Control view, issue files, synced issue details, the public views
of a public board and the card QR code. #2015 leaves TODO Later.

Thanks to xet7 !
2026-10-14 12:10:21 +03:00
Lauri Ojansivu 326d9bbf61 Board View / Version Control, issue files and synced issue details.
Version Control shows the board as a GitHub or Gitea issue tracker in the
board's own theme: cards are issues (open, or closed when archived, in a
Closed or Done list, or closed upstream), the board's labels are labels,
labels with a due date and the importers' milestone: labels are
milestones, comments are the conversation and attachments are the files.
The issue list takes GitHub's search words (is:open, label:, milestone:,
author:, assignee:@me, no:label, sort:). New issue, Close and Reopen write
through the ordinary collections, so the History tab - the board's
history table - shows them and Restore undoes them. Import links open the
GitHub, GitLab, Gitea and Forgejo importers, and each synced list has a
Sync now button.

The GitHub, GitLab, Gitea and Forgejo importers bring each issue's files:
user-attachments and user-images links, GitLab uploads, and Gitea and
Forgejo assets, downloaded through the import's SSRF guard and stored as
card attachments in Admin Panel / Attachments / Default Storage.

List sync has a new option, Comments, labels, state and files, off by
default: comments come from one paged repository-wide list (GitHub,
Gitea) or each issue's notes (GitLab), keyed by the tracker's ids, so a
second run writes only what changed, and nothing written in WeKan is
changed. Gitea issues are fetched without pull requests, and a rate
limited answer is waited out once or ends the run with the time to retry.

A public board's Show on / Default on Public Board settings are for the
public: visitors and non-members get them, read-only, and the board's own
members get the private side, so any view - Version Control, Email, Chat
and the rest - can be the read-only public view.

The card menu has a QR code of the card's link, drawn in the browser by
WeKan's own encoder, which the tests check module for module against
qrcode-svg.

The new strings are translated in every locale.

Thanks to xet7 !

Fixes #2015.
2026-10-14 12:09:34 +03:00
Lauri Ojansivu b6bdae765d Record the maintainer's answers to every open design question in TODO Later.
TODO Later's design questions block is now the decisions, each to be built
as decided: organization and team tabs, team-board hiding, read-only Rules
behind an Admin Panel option, log out everywhere and passkeys, linked lists,
search scrolling, avatars, label ordering, QR codes, subtasks across boards,
checklist item owners, Table view editing, column sets, Yjs co-editing,
drawing (PNG and SVG, editable), board grouping, card types, unread marks
for every card field, push notifications and an activity feed, Mattermost,
Matrix, ntfy and ActivityPub, a Trellinator-compatible API, an Android share
target, Nextcloud, FreeBSD rc.d, LibreJS and internal webhook destinations.
Gogs beside WeKan is closed as WONTFIX.

Thanks to xet7 !

Closes #6554.
2026-10-14 11:30:09 +03:00
github-actions[bot] 5588931bf2 Bump versions for v12.41 2026-10-14 08:08:00 +00:00
Lauri Ojansivu ae0d3170ec Record the Android bundles; TODO Later's Planned block is done.
Everything requested on 2026-10-13 is now built. What stays in TODO Later is
the one thing that cannot work, Jira list sync from a Sandstorm grain, so it
is not asked again.

Thanks to xet7 !
2026-10-14 11:08:00 +03:00
Lauri Ojansivu b8d9cb2565 Android bundles for Termux, for all four Android CPUs.
Each release's Linux bundle of the matching CPU is rebuilt inside Termux's
own image, on a runner of that CPU, with wekan/node-patches'
node-android-<cpu> of the same version, checked against its SHA256: npm
reinstalls the server packages run by that Node.js, so every native addon is
built for it, and sharp from source against Termux's libvips. Each addon's
architecture is checked, sharp must load, and the bundle must start and
answer HTTP 200 before it is published. A CPU whose Android Node.js is not
published yet is skipped and builds by itself once it is; the single-file
workflow, which already makes Android single files from these bundles, now
waits for them.

Thanks to xet7 !
2026-10-14 11:07:36 +03:00
Lauri Ojansivu 9993d8e0db Prepare v12.41 release 2026-10-14 11:06:54 +03:00
Lauri Ojansivu bc79748232 Record the CodeQL #583 fix in Upcoming.
Thanks to GitHub CodeQL and xet7 !
2026-10-14 11:05:20 +03:00
Lauri Ojansivu 73fcfb8545 Answer a test's HTTPS request without echoing it back.
CodeQL alert #583 (js/reflected-xss): tests/httpsServer.test.cjs's server
answered `served ${req.url} ...`, sending the request back as the body. It
now names the page by comparison, as plain text.
tests/noReflectedRequest.test.cjs keeps the shape out of every file: no
res.end/res.write/res.send writes a request's url, query, params, headers or
body into the response. Test code only, so nothing for Admin Panel / Problems
to record.

Thanks to GitHub CodeQL and xet7 !
2026-10-14 11:05:15 +03:00
Lauri Ojansivu b8a6fc371b Record the #6766 fix in Upcoming.
Thanks to Raymond-Voogt and xet7 !
2026-10-14 11:04:06 +03:00
Lauri Ojansivu ebf2a019a6 "Lists missing their swimlane" no longer counts board-wide lists, and Restore never hides cards.
The check counted every board-wide list whose position history recorded
the swimlane it was created in, so lists made since the automatic
un-binding stopped (#6515, released in v10.31) were counted too, and the
count kept growing. Restore would have bound them back to that swimlane,
hiding the cards they hold in other swimlanes - 490 cards in 14 lists on
the reporter's instance.

Now a list created after v10.31's release is never offered, being
board-wide by design, and a list with any card (archived ones too) in a
swimlane other than the one it would be bound to is never restored. A list
that really lost its binding, with cards only in its own swimlane, is still
restored. tests/listSwimlaneRestore.test.cjs and a runtime test on a real
database cover both rules and that case.

Thanks to Raymond-Voogt and xet7 !

Fixes #6766.
2026-10-14 11:03:53 +03:00
Lauri Ojansivu 5466872bf1 Accept the musl AppImage's bundle check in the node-runtime guard.
AppImage.yml wraps the musl bundles too, whose Node.js needs no glibc
libraries; tests/nodeRuntimeDependency.test.cjs still requires
node-runtime/libatomic.so.1 for every glibc bundle.

Thanks to xet7 !
2026-10-14 10:56:56 +03:00
Lauri Ojansivu f4d2b3a7ef Record musl bundles, Sandstorm list sync and two locale fixes; Planned is down to Android.
Upcoming gains the musl bundles and AppImages, list sync through the
Sandstorm Powerbox, and Hong Kong Chinese and Flemish in their own
languages. TODO Later's Planned block now holds only the Android bundles,
which wait for wekan/node-patches, and records that Jira list sync cannot
work from a grain, so it is not asked again.

The Sandstorm REST API works with a grain API key and is documented in
docs/Platforms/FOSS/Container/Sandstorm/Powerbox-External-APIs.md since
commit 2cc0b59a51.

Thanks to xet7 !

Fixes #2167.
2026-10-14 10:45:00 +03:00
Lauri Ojansivu f3c92273ae Hong Kong Chinese in Traditional characters, and Flemish in Dutch.
zh-HK had 90 values in Simplified Chinese, found with OpenCC; they are now
Traditional with Hong Kong usage. vl-SS (Flemish) held 158 values in about a
dozen other languages - Venda, Vietnamese, Venetian, Walloon, Papiamento and
more - now in Flemish, and its 225 values still in English are filled.
Values a test pins or an audit corrected are kept, and
tests/wrongLanguageLocales.test.cjs now covers both files.

Thanks to xet7 !
2026-10-14 10:43:39 +03:00
Lauri Ojansivu a1d2994c1c musl bundles and AppImages for Alpine and the other musl systems.
Each release's Linux bundle of x64 and arm64 is rebuilt inside Alpine on a
runner of that CPU: the musl Node.js of exactly the bundle's version,
checked against its SHA256, replaces the glibc one and its node-runtime
libraries; npm reinstalls the server packages so every native addon is
compiled against musl and sharp's musl package is the one installed; then
every addon's architecture is checked, sharp must load, and the bundle must
start and answer HTTP 200 before it is published. FerretDB and the MongoDB
Database Tools are static and stay. The AppImage workflow wraps them too, as
x86_64-musl and aarch64-musl, and starts each inside Alpine to check it.

Thanks to xet7 !
2026-10-14 10:38:39 +03:00
Lauri Ojansivu d53b4da799 On Sandstorm, sync lists from GitHub, GitLab, Gitea and Forgejo through the Powerbox.
A grain's only way out is the bridge proxy, so list sync never reached a
tracker. A board member who may write saves the list's source and allows
the grain to reach that tracker's API root in the Powerbox; the grant
belongs to the tracker, found by the URL being fetched, so the periodic job
needs no signed-in user. GitLab's token goes as private_token, since the
bridge drops PRIVATE-TOKEN; GitHub, Gitea and Forgejo read public
repositories, since their token headers cannot pass; Jira, which signs in
only with an Authorization header, is refused with that reason. Off
Sandstorm list sync still goes through the SSRF guard, unchanged.

New strings translated in all languages.

Thanks to xet7 !
2026-10-14 10:35:13 +03:00
Lauri Ojansivu f22af91967 Count the rewritten locale files correctly in In short.
Fifteen held another language and six machine-fill prefixes.

Thanks to xet7 !
2026-10-14 10:29:38 +03:00
Lauri Ojansivu 2679f77f86 Record Bun single files, Sandstorm webhooks and API keys, and the locale rewrites.
Upcoming gains them; TODO Later's Planned block now holds only what is
left - Android bundles, Sandstorm sync and import (whose credentials the
bridge proxy cannot carry) and Node.js musl bundles, with the questions
that decide them - and the translation block names the languages that need
a native speaker.

Thanks to xet7 !
2026-10-14 10:29:25 +03:00
Lauri Ojansivu ba6d0014f7 Rewrite locale files that held another language in their own.
The locale tag decides the language. Found on 2026-10-14 and rewritten:
ro and ro-RO (Italian, French and Spanish blocks), rup, lld, rm and ve-CC
(Italian; ve-CC also Zulu and Venda), gl and gl-ES (Portuguese), ace
(Malay/Indonesian), ve (Zulu), sk (Czech and Polish), hsb (Czech), vo
(Esperanto), bi (Tok Pisin), uz-AR (half Latin script, Uyghur and Kazakh
forms; now Uzbek in Arabic script). Machine-fill prefixes and notes are
gone from to ("Faka-Tonga:"), qu ("Kay willaymi:"), wal ("Wolayttatto:"),
hsb ("WeKan:"), an, fur and lg, each value now in its own language. cmn
and yue_CN no longer call a board a wooden plank; List actions means the
actions of a list in pa, pt, pt-BR, ru_RU, vi-VN, hu and ko; "No" is the
word in pa, lt and te-IN; et-EE's board is a tahvel.

Values a person or an earlier audit had already reviewed are kept: every
value a regression test pins, every audited correction, and every label
another string quotes. Poker cards show their numbers everywhere.
tests/wrongLanguageLocales.test.cjs keeps the old languages and prefixes
out and checks every placeholder against English; two older tests that
expected Venda in the Venetian file, and a prefix in Quechua, now expect
the language of the tag.

Low confidence, for a native speaker: to, qu, wal, lg, bi, vo, lld, rup,
ve, ace, uz-AR (its use of ې is for a speaker to settle), hsb and rm.

Thanks to xet7 !
2026-10-14 10:28:43 +03:00
Lauri Ojansivu 35e216b846 Review the list-permission open-board check; no example URL in egress.mjs.
tests/boardPermission.test.cjs lists every public-board check in the server
and models, so none silently leaves instance boards out: #4527's "everyone
signed in" is one, and it covers public AND instance boards. The Powerbox
webhook comment in imports/sandstorm/egress.mjs gave an example address,
which the release risk audit reports as an outbound URL; it now describes
the shape without one.

Thanks to xet7 !
2026-10-14 09:51:41 +03:00
Lauri Ojansivu 2cc0b59a51 On Sandstorm, send webhooks through the Powerbox, and accept the REST API with a grain API key.
A grain has no network but the bridge proxy, so an outgoing webhook now
leaves only through an API a board admin approved in the Powerbox for that
webhook's address: each saved webhook offers "Allow this grain to reach this
address". The grant belongs to the destination, not to a person, is found
by the URL being sent to, and is approved as the address's parent path so
the bridge delivers to the exact address. Every sender - immediate, grouped
and stored - goes through webhookFetch, which off Sandstorm, or for an
address no grant covers, is exactly the SSRF-guarded fetchSafe.

The REST API called with a Sandstorm API key acts as the key's user: in a
grain the bridge sets X-Sandstorm-User-Id itself. Only in a grain, only
after a WeKan token, only for an existing enabled account, and a key without
participate reads only.

New strings translated in all languages.

Thanks to xet7 !
2026-10-14 09:49:42 +03:00
Lauri Ojansivu b1e58094f4 Bun single files: one executable per Bun platform, musl included.
Each is the platform's published Bun bundle ZIP, unchanged, inside that
bundle's own Bun: built on the platform itself (Alpine for musl), so the
runtime is exactly the one the bundle was tested with. The first start
extracts the bundle once into a per-version cache directory, without a
second copy of Bun, and every start runs index.mjs with the executable
itself as Bun. Like the Bun bundle it needs MONGO_URL and ROOT_URL.

The launcher reads the ZIP itself (stored, deflated, ZIP64, Unix modes,
symbolic links) and refuses any entry or link that would land outside the
cache directory. Before publishing, each file is checked: it names itself,
refuses to start without a database, extracts once and reuses that, and
loads every native addon of the bundle. Linux x64/arm64 (glibc and musl),
macOS and Windows; FreeBSD and Android have no runner to build on.

Thanks to xet7 !
2026-10-14 09:48:50 +03:00
Lauri Ojansivu f14274e3ca Record board editing for everyone, list permissions, board keys, shared labels and HTTPS.
Upcoming gains the features, the list archive fix, the CodeQL fix and the
Node-RED and Uberspace docs. TODO Later's decided block is done and removed;
the open requests are rewritten as the design questions that decide them,
and the locale files found in another language are listed.

Thanks to xet7 !
2026-10-14 09:28:57 +03:00
Lauri Ojansivu a0de507143 Document webhooks to Node-RED and WeKan on Uberspace 7.
Node-RED: the http in flow, and that an internal address is refused by the
SSRF guard, so Node-RED needs a public name. Uberspace 7: the UberLab guide,
the update script, and email settings on the one environment line.

Thanks to yeurplc, jeyca and xet7 !

Fixes #2017,
Fixes #2009.
2026-10-14 09:28:45 +03:00
Lauri Ojansivu 167ccf9e2d Turn an email's HTML into text in one pass, with no pattern to take apart.
CodeQL alerts #581 and #582: htmlToText still removed script elements and
tags with regular expressions, looped through a helper CodeQL does not
follow. A one-pass scanner drops tags whole, script/style/head elements with
their content, and unfinished tags. The tree-wide guard no longer accepts a
fixed-point helper and also catches a whole element removed by pattern.

Thanks to GitHub CodeQL and xet7 !
2026-10-14 09:28:45 +03:00
Lauri Ojansivu 0a3797dbf6 Archiving a list archives its cards, and restoring it brings them back.
Only template lists archived their cards, so a normal list's cards stayed
visible in the Lists, Table and Calendar views. A server hook on the list's
archived field now archives the cards of every list, whichever way it is
archived, and restore brings back exactly those - the rule #2292 gave
swimlanes. Cards archived with the list get the list's archivedAt, so a
browser clock that differs from the server's cannot keep them archived.

Thanks to Raymond-Voogt and xet7 !

Fixes #6765.
2026-10-14 09:28:45 +03:00
Lauri Ojansivu 7595727917 Boards every signed-in user may edit, per-list permissions, board keys and shared labels.
A board can let every signed-in user edit it, when Admin Panel / Settings /
Visibility allows it: a non-member then comments, edits and moves cards like
a Normal member, and their first edit makes them one. Board-wide settings
stay members-only.

Lists can allow or refuse editing and commenting per role (list menu, board
admins): built-in roles, custom roles and, on an open board, everyone signed
in. The server applies it to card, checklist, attachment and comment writes
and to moves into the list; the REST API honours a list's refusal on every
per-list card route. A direct client write of the setting is refused and
shown in Admin Panel / Problems.

Every board has a key, unique in the instance, suggested from its title and
set for existing boards at startup; card numbers show as KEY-7 and searching
for KEY-7 finds the card.

Shared labels in Admin Panel / Labels, for every board or an organization's
or team's boards; a board uses them beside its own, and a rename or colour
change reaches every board that uses it.

New strings translated in all languages. Low confidence: chr, tlh, wal, ay,
ace, nah, gn, iu, zgh, rup, wo, se, ff, tig, vo, bm, kl, bua, nap, mai, to,
ks, qu, bo, fj, haw, om, ss, ts, ve-PP, lld, scn.

Thanks to Meeques, justinr1234, tiFabGrinch, ejlkemper and xet7 !

Fixes #4575,
Fixes #4527,
Fixes #2449,
Fixes #2450,
Fixes #2761,
Fixes #4698.
2026-10-14 09:28:45 +03:00
Lauri Ojansivu 1bf7a62cab Serve HTTPS from WeKan itself, when a certificate is configured.
#916 (decided 2026-10-14): HTTPS_PORT with HTTPS_CERT_FILE and
HTTPS_KEY_FILE (a path or the PEM itself) makes WeKan serve HTTPS; without
them nothing changes, and a proxy stays the recommendation for
production. Meteor's HTTP server - pages, DDP, websockets - is not
replaced: a TLS listener hands each decrypted connection to it, so
everything works the same over HTTPS. A renewed certificate is loaded
within a minute without a restart (a broken one keeps the old).
HTTPS_REDIRECT=true redirects plain HTTP to ROOT_URL when that is https,
else the same host on the HTTPS port, keeping only the path. The snap
gets https-port, https-cert-file, https-key-file, https-bind-ip and
https-redirect.

tests/httpsServer.test.cjs makes a real certificate with openssl, serves
a page and a websocket upgrade through the bridge, swaps the certificate
without a restart, and refuses incomplete settings, an untrusted
certificate, an injected host and a scheme-relative redirect.

Thanks to 68040 and xet7 !

Fixes #916.
2026-10-14 08:25:25 +03:00
Lauri Ojansivu 1becf7a9c1 Record custom roles, group memberships, the webhook proxy and the role helper fixes.
Upcoming gets the three features and the bug fixes; TODO Later's
decided-and-waiting list drops #3022, #4178 and #1455, which are done.

Thanks to xet7 !
2026-10-14 08:22:52 +03:00
Lauri Ojansivu 548834c4ee Fix five role helpers that answered the same for everybody.
Found while mapping every role check for custom board roles:
- canImportIntoBoard tested the role helpers as references
  (`!user.isWorker`, always false), so Import was offered to nobody; it
  asks the write capability now, as the server does.
- the rules list's canAdmin used `user.isBoardAdmin` without calling it,
  so every member saw the rule controls.
- the role popup's isLastAdmin compared activeAdmins() - a list - with 1,
  so the last admin was never stopped from giving the role away.
- the members widget's isWorker called Boards.hasWorker, which is not a
  collection function.
- OIDC's DEFAULT_BOARD_ID used Array#contains, which does not exist, so
  the configured role flags threw; isCommentOnly works next to the
  documented isCommentsOnly spelling.

tests/roleHelperRegressions.test.cjs pins each, and fails for any role
helper tested as a bare reference anywhere in client/, models/ and
packages/, and for a list compared with a number.

Thanks to xet7 !
2026-10-14 08:22:12 +03:00
Lauri Ojansivu 50bb989eb8 Custom board roles, and team memberships that leave with the team.
#3022 (decided 2026-10-14): a site admin builds named roles in Admin
Panel / People / Roles from the capability matrix - sees all cards,
comments, edits, moves cards, invites - and a board admin gives one to a
member in the same popup as the built-in roles. Board admin stays
built-in. Every permission check asks boardRoleCapabilities, which
answers a custom role from the role; the member's built-in flags carry
the strongest built-in role that grants nothing more (fallbackRoleFor,
checked for every combination), so code reading a flag never grants
more, and a deleted role leaves exactly that. Where a role is asked by
name (hasWorker, isCommentOnly, ...) a custom role reads as the built-in
role that does what it does; commenting is asked as a capability
everywhere. Invites, REST whole-board reads, imports (a foreign custom
role is dropped) and member mapping (all eight flags now, not four)
follow it. The six new strings are translated in all 234 locales.

#4178 (decided 2026-10-14): a team or organization that gives board
memberships takes them away again when the user leaves it - only those:
each records where it came from (grantedBy), a direct membership is
never touched, a board admin is kept, and a board admin's own add or
removal is final. Teams through user edits, LDAP, OIDC and the board
sidebar; organizations where they propagate members.

Also: the Roles Status table gets a Move cards column; the comment form,
reactions and chat ask the comment capability (a No comments member was
offered reactions the server refused).

Tests: customBoardRoles and groupBoardMembership node suites, and the
runtime suites (9 passing) against a real database.

Thanks to xet7 and mfnalex !

Fixes #3022,
Fixes #4178.
2026-10-14 08:20:49 +03:00
Lauri Ojansivu 78957e14cb Send outgoing webhooks through an HTTP proxy, with the SSRF checks kept.
HTTP_PROXY, HTTPS_PROXY and NO_PROXY now work for every outgoing request
that goes through the SSRF guard: webhooks, imports, avatar downloads,
list sync. The destination is still resolved once and checked against
the blocked ranges first, and the proxy is handed that checked address,
never the name: an http:// request goes to the proxy in absolute form on
the pinned address with the real Host, an https:// one through a CONNECT
tunnel to the pinned address with TLS still verified against the real
name (and WEBHOOK_TLS_CA_CERT). A name resolving to an internal address
is refused before the proxy is asked anything. Proxy credentials in the
URL become Proxy-Authorization. The snap gets http-proxy, https-proxy and
no-proxy.

tests/outboundProxy.test.cjs runs a real proxy on loopback: the pinned
address, the Host header, the credentials, the CONNECT authority, and
that a refused destination never reaches the proxy.

Thanks to gbarre and xet7 !

Fixes #1455.
2026-10-14 08:20:30 +03:00
Lauri Ojansivu cf41e221ea Record the CodeQL #570-#580 fixes in Upcoming.
Thanks to GitHub CodeQL and xet7 !
2026-10-14 07:35:43 +03:00
Lauri Ojansivu 2352f1f44f Fix GitHub CodeQL alerts #570-#580.
#570, #571 (incomplete multi-character sanitization): email to card
turned an HTML-only email into text with one pass of each removal, so a
script element spliced together by removing an inner one survived and its
contents reached the card ("<scr<script></script>ipt>alert(1)</script>").
Both removals now run to a fixed point.

#574 (regular expression injection): the single file's wekan.env writer
spliced the setting name into a RegExp. A setting must be an environment
variable name, is compared literally, and its value must be one line. The
kept-versions reader built a half-escaped RegExp from the file extension;
it now reads wekan-<version><exe> literally.

#572, #573 (incomplete string escaping): two runtime tests escaped only
some metacharacters; they compare literally now. #575-#580 (URL substring
and hostname patterns): translation tests matched example hostnames with
unescaped dots or substring checks; the dots are escaped and the hosts
bounded.

tests/tagStrippingFixedPoint.test.cjs missed the <[^>]+> spelling; it now
catches both, accepts a fixed-point helper only when it really loops, and
records the search-index text of server/lib/documentGif.js as never
rendered. tests/emailToCard.test.cjs fails on the old htmlToText;
tests/singleFile.test.cjs refuses regex and newline input in setting
names and values, and reads only real version files.

Nothing here is an attempt a user can make at runtime that the server
refuses, so there is no Admin Panel / Problems key to add.

Thanks to GitHub CodeQL and xet7 !
2026-10-14 07:35:20 +03:00
Lauri Ojansivu e8eeaa4956 Record the 2026-10-14 design decisions in TODO Later.
Shared labels (#2761, #4698), board keys for card numbers (#2449,
#2450), membership removal on leaving a team or organization (#4178),
webhooks through an HTTP proxy (#1455), custom roles (#3022), editing by
every signed-in user (#4575), per-list permissions (#4527) and optional
HTTPS in WeKan's own server (#916) are decided; each moves from the open
requests to a list of decisions waiting to be implemented.

Thanks to xet7 !
2026-10-14 07:24:21 +03:00
Lauri Ojansivu f240a3d056 Record the OpenBSD 7.8 bundle build in Upcoming.
Thanks to xet7 !
2026-10-14 07:16:34 +03:00
Lauri Ojansivu 209e3b0c47 Build the OpenBSD bundles on OpenBSD 7.8.
node-patches now builds OpenBSD's Node.js on 7.8, the first release whose
base Clang (19.1.7) builds Node 26 - 7.7's Clang 16 has no
std::atomic_ref. The bundle is built and started where that binary runs.
The BSD page says OpenBSD 7.8 or newer is needed, and that NetBSD's
bundled OpenSSL gets FreeBSD's configuration; tests/bsdBundles.test.cjs
fails for an OpenBSD target older than 7.8.

Thanks to xet7 !
2026-10-14 07:16:24 +03:00
Lauri Ojansivu 52f0bc1271 Document Igbo interrupted import recovery translations
Record passing checks and outstanding fluent and browser verification.

Thanks to xet7 !
2026-10-14 07:14:51 +03:00
Lauri Ojansivu 8c25ce78b9 Translate Igbo interrupted import recovery messages
Preserve permanent deletion, retained partial boards, source-file limitations and untouched foreign boards. Add catalog and browser coverage. Recovery prose needs fluent review.

Thanks to xet7 !
2026-10-14 07:14:15 +03:00
Lauri Ojansivu 269a96aa7d Merge remote-tracking branch 'origin/main' 2026-10-14 07:12:30 +03:00
Lauri Ojansivu 6e3678552a Document Igbo stopped sync recovery translations
Record passing translation checks and unfinished fluent and browser verification.

Thanks to xet7 !
2026-10-14 07:11:47 +03:00
Lauri Ojansivu 5c5391fb98 Translate Igbo stopped list sync recovery messages
Preserve applied changes, discarded pending writes, replay restrictions and display limits. Add catalog assertions and a browser case. Recovery prose needs fluent review.

Thanks to xet7 !
2026-10-14 07:10:22 +03:00
Lauri Ojansivu 30e3c65169 Bring TODO Later up to date with the open issues.
Remove what is no longer open: #3318 (closed as not planned), #6692 and
#6509 (closed as completed), and email to card and the Node.js single
files from Planned (shipped in v12.40). The September translation-repair
checkpoints are cut to what is still open.

Every one of the 62 open issues was read with its comments and checked
against the code on 2026-10-14. None other than #2141 is fully done; each
is now listed under its topic with what exists and what is missing.
Record the #2141 closure in Upcoming.

Thanks to xet7 !
2026-10-14 07:07:52 +03:00
Lauri Ojansivu 19aa5fd7dd Document Igbo workflow and permission translations
Record passing translation checks and remaining language and browser verification. Keep concurrent TODO Later edits separate.

Thanks to xet7 !
2026-10-14 07:07:29 +03:00
Lauri Ojansivu 3f88fb39a5 Pin the per-member notification frequency #2141 asked for.
The issue asked for Trello's Send Emails choice per member: Never,
Periodically (about once an hour) or Instantly. All three already exist:
email off is the member's Notification Settings override (bde66a5c7d,
2026-09-10), and the member's delivery schedule is immediate, every 5
minutes to 24 hours, or daily, with quiet hours (ab785acd1e, 2026-10-10).
A member's choice wins over the board's and the Admin Panel's.

tests/notificationFrequency2141.test.cjs pins each option at member
level, that an unknown member value is ignored rather than trusted, and
that the switch and the schedule picker are in the member's settings.

Thanks to saurabharch and xet7 !

Fixes #2141.
2026-10-14 07:06:15 +03:00
Lauri Ojansivu daac3ead0d Translate Igbo workflow and permission controls
Preserve Wrike status behavior, directory-sync identifiers and subtask permission distinctions. Add catalog and browser coverage. Technical prose is lower confidence and needs fluent review.

Thanks to xet7 !
2026-10-14 07:04:56 +03:00
Lauri Ojansivu cb7aa2bf6b Add required Upcoming summary and translation heading
Thanks to xet7 !
2026-10-14 06:59:04 +03:00
Lauri Ojansivu cfde3385b8 Document Igbo notification translations in Upcoming
Record passing checks and outstanding fluent and browser verification.

Thanks to xet7 !
2026-10-14 06:58:05 +03:00
Lauri Ojansivu 238354f6a5 Translate Igbo notification delivery and integration settings
Preserve grouping, scheduling, inherited webhook settings, identity omission and literal link syntax. Add catalog assertions and browser coverage. Technical wording needs fluent review.

Thanks to xet7 !
2026-10-14 06:57:02 +03:00
github-actions[bot] 265238550f Bump versions for v12.40 2026-10-14 03:56:18 +00:00
Lauri Ojansivu addf08f52c Prepare v12.40 release 2026-10-14 06:54:55 +03:00
Lauri Ojansivu daf8549cdd Document Igbo transfer-help translations
Record passing checks and outstanding fluent and browser verification.

Thanks to xet7 !
2026-10-14 06:54:31 +03:00
Lauri Ojansivu 93ef17b326 Translate Igbo Trello and Sandstorm transfer help
Preserve literal swimlane labels and distinguish access requirements, partial export warnings and selected-board exports. Add catalog and browser checks. Technical wording needs fluent review.

Thanks to xet7 !
2026-10-14 06:54:00 +03:00
Lauri Ojansivu 7306d06919 Document Igbo import mapping and bulk transfer translations
Record passing checks and outstanding fluent and browser verification.

Thanks to xet7 !
2026-10-14 06:51:09 +03:00
Lauri Ojansivu 1cea7b89f1 Translate Igbo import mapping and bulk transfer controls
Preserve user mapping choices, missing-column handling and single-board versus multi-board ZIP behavior. Add catalog and browser coverage. Technical prose needs fluent review.

Thanks to xet7 !
2026-10-14 06:50:36 +03:00
Lauri Ojansivu 8bc9d15be1 Document second Igbo import-guide translation batch
Record passing checks and outstanding fluent and browser verification.

Thanks to xet7 !
2026-10-14 06:47:33 +03:00
Lauri Ojansivu 7e8ebcd458 Translate twelve more Igbo import guides
Preserve exclusions, English-header requirements, subtask syntax and fallback mapping. Extend catalog and browser coverage. Technical prose is lower confidence and needs fluent review.

Thanks to xet7 !
2026-10-14 06:46:50 +03:00
Lauri Ojansivu 93ded63395 Document first Igbo import-guide translation batch
Record passing checks and outstanding fluent and browser verification.

Thanks to xet7 !
2026-10-14 06:43:23 +03:00
Lauri Ojansivu 29282c6ddf Translate ten Igbo import guides with format limits
Preserve mappings, first-board limits, completed items and literal Org mode identifiers. Add catalog assertions and browser cases. Longer technical prose is lower confidence and needs fluent review.

Thanks to xet7 !
2026-10-14 06:42:44 +03:00
Lauri Ojansivu e1b92a011e Document Igbo linked-field translation progress
Record passing checks, preserved Chinese values and remaining placeholders and review.

Thanks to xet7 !
2026-10-14 06:39:42 +03:00
Lauri Ojansivu 77218ed315 Translate Igbo linked fields and board controls
Distinguish link directions and permission limits, translate announcement controls, and repair the custom-fields label. Add regression assertions and browser coverage. Direct technical wording needs fluent review.

Thanks to xet7 !
2026-10-14 06:39:13 +03:00
Lauri Ojansivu 5416b7f672 Document Igbo attachments and refresh translation backlog counts
Record passing checks and distinguish the filtered inventory from pending keys and semantic review.

Thanks to xet7 !
2026-10-14 06:36:42 +03:00
Lauri Ojansivu 4e80dcf3ba Translate Igbo card attachment controls and storage identifier
Add positive and negative regression coverage for attachment visibility, limits, self-links and detachment. Direct technical wording needs fluent review.

Thanks to xet7 !
2026-10-14 06:35:51 +03:00
Lauri Ojansivu 9fb2559fd8 Name the regression tests of the BSD and FreeBSD Bun entries in Upcoming.
Thanks to xet7 !
2026-10-14 06:33:48 +03:00
Lauri Ojansivu f6d7aafc5f Document Scottish Gaelic import and tooltip corrections
Record passing checks and outstanding fluent and browser verification.

Thanks to xet7 !
2026-10-14 06:32:07 +03:00
Lauri Ojansivu 399e9a19d7 Restore Scottish Gaelic import instructions and header help
Preserve Jira pagination, required fields and automation rules, Excel mapping and per-sheet boards, and notification toggle behavior. Add catalog and browser assertions. Direct technical wording needs fluent review.

Thanks to xet7 !
2026-10-14 06:31:30 +03:00
Lauri Ojansivu 2834c752f4 Document Scottish Gaelic report control corrections
Record passing checks and outstanding fluent and browser verification.

Thanks to xet7 !
2026-10-14 06:29:11 +03:00
Lauri Ojansivu 999922d6a6 Updated translations. 2026-10-14 06:28:02 +03:00
Lauri Ojansivu 2dd80befcf Document Scottish Gaelic report translation review
Record passing checks and outstanding statistical terminology and browser verification.

Thanks to xet7 !
2026-10-14 06:26:34 +03:00
Lauri Ojansivu 8b46431477 Restore Scottish Gaelic report methodology and limits
Replace shorthand report notes with translated assumptions, minimum samples, missing-history handling and fallback dates. Clarify headings and error guidance. Statistical terminology is lower confidence and requires fluent review.

Thanks to xet7 !
2026-10-14 06:26:02 +03:00
Lauri Ojansivu 7b3b9f00e1 Document Scottish Gaelic date and linking corrections
Record verified checks, unchanged Chinese values and outstanding language and browser review.

Thanks to xet7 !
2026-10-14 06:23:20 +03:00
Lauri Ojansivu 19f5807e04 Clarify Scottish Gaelic date triggers and card linking
Restore initial date setting, Gregorian ISO-week details and destination versus card-derived board actions. Add positive and negative catalog assertions and browser coverage. Direct wording needs fluent review.

Thanks to xet7 !
2026-10-14 06:22:56 +03:00
Lauri Ojansivu add0493214 Document Scottish Gaelic meaning corrections and review limits
Record passing translation and changelog checks, preserved Chinese values, and pending browser and fluent verification.

Thanks to xet7 !
2026-10-14 06:20:44 +03:00
Lauri Ojansivu 1fda1c4dd0 Correct Scottish Gaelic action meanings and stale import guides
Translate Todoist and Planner instructions and distinguish recurring cards from resets and move reasons from descriptions. Add catalog and browser regression coverage. Direct technical translations still need fluent review.

Thanks to xet7 !
2026-10-14 06:19:59 +03:00
Lauri Ojansivu faed3b3048 Document Scottish Gaelic recovery translations and full placeholder check
Record passing checks and unfinished semantic and browser verification.

Thanks to xet7 !
2026-10-14 06:17:18 +03:00
Lauri Ojansivu 53be36536d Translate Scottish Gaelic recovery messages and close placeholder gap
Preserve retained sync changes, permanent partial-board deletion and server-only settings. Add full placeholder and browser checks; technical prose needs fluent review.

Thanks to xet7 !
2026-10-14 06:14:42 +03:00
Lauri Ojansivu 80bac12b8f Document Scottish Gaelic workflow translation progress
Record validation and unfinished fluent and browser review.

Thanks to xet7 !
2026-10-14 06:12:00 +03:00
Lauri Ojansivu 73bdd4061b Translate Scottish Gaelic workflow and permission messages
Retain status transitions, environment names and authorization restrictions. Technical prose needs fluent review; extend regression coverage.

Thanks to xet7 !
2026-10-14 06:11:29 +03:00
Lauri Ojansivu 154a3d6c71 Document Scottish Gaelic integration translation progress
Record passing checks and unfinished fluent and runtime review.

Thanks to xet7 !
2026-10-14 06:09:16 +03:00
Lauri Ojansivu 9724c40077 Translate Scottish Gaelic integration and notification controls
Fill 53 strings while preserving placeholders, Trello labels, inherited settings and delivery choices. Technical prose needs fluent review; extend regression coverage.

Thanks to xet7 !
2026-10-14 06:08:44 +03:00
Lauri Ojansivu 6c5d109754 Document Scottish Gaelic import-control translation progress
Record verification and unfinished fluent and browser review.

Thanks to xet7 !
2026-10-14 06:06:13 +03:00
Lauri Ojansivu 6a028b4327 Translate Scottish Gaelic import mapping and bulk export controls
Fill 27 English strings, preserving user choices, ZIP exceptions and workbook layout. Technical prose needs fluent review; extend regressions.

Thanks to xet7 !
2026-10-14 06:05:43 +03:00
Lauri Ojansivu 6543da6538 Document the second Scottish Gaelic import-guide batch
Record checks, lower-confidence prose and remaining verification work.

Thanks to xet7 !
2026-10-14 06:03:21 +03:00
Lauri Ojansivu c5f86df1a3 Translate twelve more Scottish Gaelic import guides
Retain export prerequisites, field mapping and omission warnings. Technical prose is lower confidence and needs fluent review; extend regression coverage.

Thanks to xet7 !
2026-10-14 06:02:41 +03:00
Lauri Ojansivu f69c05e75b Document Scottish Gaelic import-guide translation progress
Record lower-confidence prose, passing checks and unfinished review.

Thanks to xet7 !
2026-10-14 05:59:53 +03:00
Lauri Ojansivu 7f86d9f396 Translate ten Scottish Gaelic import guides
Preserve mapping, completion dates and first-board limitations. Technical prose is lower confidence and needs fluent review; add regression coverage.

Thanks to xet7 !
2026-10-14 05:59:20 +03:00
Lauri Ojansivu 4fc3e3b1e2 Document Scottish Gaelic field-link translation progress
Record checks and remaining fluent and browser verification work.

Thanks to xet7 !
2026-10-14 05:56:45 +03:00
Lauri Ojansivu 4f35ee5751 Translate Scottish Gaelic field links and board announcements
Fill 26 English placeholders with direction, visibility and permission conditions intact. Add regression coverage; fluent review remains needed.

Thanks to xet7 !
2026-10-14 05:56:12 +03:00
Lauri Ojansivu d2766886bf Document Scottish Gaelic attachment translation progress
Record validation, lower-confidence terminology and remaining review work.

Thanks to xet7 !
2026-10-14 05:54:00 +03:00
Lauri Ojansivu 4f5f5d0d33 Translate Scottish Gaelic attachment and legacy storage messages
Fill 41 English placeholders and add attachment and credential regressions. Technical storage wording is lower confidence and needs fluent review.

Thanks to xet7 !
2026-10-14 05:53:28 +03:00
Lauri Ojansivu 1238df0faa Document Irish rule grammar corrections in Upcoming
Record regression coverage and unfinished runtime and semantic verification.

Thanks to xet7 !
2026-10-14 05:50:16 +03:00
Lauri Ojansivu 503d439ddc Correct Irish rule subjects and omit duplicated linking verbs
Use assignee event wording and retain subjects in rule fragments. Omit the extra copula after Irish temporal clauses, with locale-boundary and assembled-rule regressions.

Thanks to xet7 !
2026-10-14 05:49:37 +03:00
Lauri Ojansivu b6d2a1dc5f Document Irish import and header help corrections
Record verification and remaining semantic review work.

Thanks to xet7 !
2026-10-14 05:46:38 +03:00
Lauri Ojansivu 32ae43650c Restore omitted Irish import and header help details
Explain Jira pagination and required fields, Excel sheet mapping and header toggle scope. Extend regression coverage.

Thanks to xet7 !
2026-10-14 05:46:06 +03:00
Lauri Ojansivu fc1f67cdff Document Irish date and card-action translation corrections
Record checks and remaining semantic review work.

Thanks to xet7 !
2026-10-14 05:43:52 +03:00
Lauri Ojansivu 301fd32e09 Correct Irish date triggers and card-link actions
Restore setting-or-changing date triggers, ISO weeks and explicit source/destination actions with regression checks.

Thanks to xet7 !
2026-10-14 05:43:15 +03:00
Lauri Ojansivu 825f93aec6 Document Irish report metric translation corrections
Record regression checks and remaining verification work.

Thanks to xet7 !
2026-10-14 05:40:54 +03:00
Lauri Ojansivu 36579f0e7c Clarify Irish report metrics and numeric summary labels
Replace shorthand with explicit quantities, forecast limits and moving-range descriptions. Cover report controls and numeric sums.

Thanks to xet7 !
2026-10-14 05:40:22 +03:00
Lauri Ojansivu 9af9c31670 Document Irish analytics translation corrections in Upcoming
Record coverage and remaining semantic and runtime verification work.

Thanks to xet7 !
2026-10-14 05:37:54 +03:00
Lauri Ojansivu 7676469d0e Restore complete Irish analytics explanations
Correct report titles and replace shorthand with methodology, uncertainty, history requirements and date fallbacks. Add regression coverage.

Thanks to xet7 !
2026-10-14 05:37:21 +03:00
Lauri Ojansivu b05ed07828 Document Irish semantic translation corrections in Upcoming
Record regression coverage and the limits of placeholder detection.

Thanks to xet7 !
2026-10-14 05:34:41 +03:00
Lauri Ojansivu e4d4b2125c Correct Irish action meanings and stale import guidance
Distinguish recurrence from reset, restore optional move reasons and permanent anonymization wording, and translate stale Todoist and Planner guides.

Thanks to xet7 !
2026-10-14 05:34:08 +03:00
Lauri Ojansivu 081cd3d683 Document Irish recovery translations and full placeholder check
Record passing checks and unfinished semantic and browser runtime verification.

Thanks to xet7 !
2026-10-14 05:31:36 +03:00
Lauri Ojansivu ee99da3ddf Translate Irish recovery messages and close the placeholder gap
Preserve retained sync changes, permanent partial-board deletion and server-only settings. Add full catalog placeholder and browser regression checks.

Thanks to xet7 !
2026-10-14 05:30:56 +03:00
Lauri Ojansivu 5bfa145f29 Document Irish workflow translation progress in Upcoming
Record verification and outstanding all-language work.

Thanks to xet7 !
2026-10-14 05:28:03 +03:00
Lauri Ojansivu 9ccf484c6d Translate Irish workflow and permission messages
Retain Wrike status transitions, LDAP setting names and subtask authorization restrictions. Extend regression coverage.

Thanks to xet7 !
2026-10-14 05:27:33 +03:00
Lauri Ojansivu c8cbdd9f16 Document Irish notification translation progress in Upcoming
Record checks and outstanding translation and browser verification work.

Thanks to xet7 !
2026-10-14 05:25:06 +03:00
Lauri Ojansivu b3a554e748 Translate Irish integration and notification controls
Preserve link-rule syntax, inherited webhook settings, grouping and quiet-hour behavior. Extend translation and browser coverage.

Thanks to xet7 !
2026-10-14 05:24:32 +03:00
Lauri Ojansivu a02c11a116 Document Irish import and export translation progress
Record passing checks and outstanding translation and browser verification work.

Thanks to xet7 !
2026-10-14 05:21:58 +03:00
Lauri Ojansivu 1c92a11f39 Translate Irish import and export controls
Preserve user matching choices, column mapping, bulk export layout and Trello access requirements. Add translation and browser regressions.

Thanks to xet7 !
2026-10-14 05:21:22 +03:00
Lauri Ojansivu b89f49c0fe Document the second Irish import-guide translation batch
Record checks, preserved Chinese values and remaining verification work.

Thanks to xet7 !
2026-10-14 05:18:50 +03:00
Lauri Ojansivu bc97a96117 Translate twelve more Irish import guides
Preserve export prerequisites, field mapping and omitted content warnings. Extend import help regression coverage.

Thanks to xet7 !
2026-10-14 05:18:12 +03:00
Lauri Ojansivu 9190084351 Document Irish import guidance translations in Upcoming
Record verified coverage and remaining browser runtime and translation work.

Thanks to xet7 !
2026-10-14 05:15:31 +03:00
Lauri Ojansivu 6be0ca89dc Translate ten Irish import guides
Preserve format mappings, completed items and first-board limitations. Extend translation checks and import help browser coverage.

Thanks to xet7 !
2026-10-14 05:14:52 +03:00
Lauri Ojansivu ddecce6236 Document Irish card-link and storage translations in Upcoming
Record regression coverage and remaining translation work.

Thanks to xet7 !
2026-10-14 05:12:03 +03:00
Lauri Ojansivu f71a482f02 Translate Irish card links and remaining legacy storage labels
Fill 67 English placeholders, preserving link directions, permission checks and storage key distinctions. Extend translation and browser regression coverage.

Thanks to xet7 !
2026-10-14 05:11:28 +03:00
Lauri Ojansivu 40f8826fba Document Malagasy action-label corrections and remaining inventory
Record verification, Chinese preservation and outstanding all-language work.

Thanks to xet7 !
2026-10-14 05:05:40 +03:00
Lauri Ojansivu aaaa3391bc Restore Malagasy action and status label meaning
Clarify optional move reasons, version-check failure and header toggle scope with regression coverage.

Thanks to xet7 !
2026-10-14 05:04:57 +03:00
Lauri Ojansivu b5974db90e Document Malagasy storage translation audit in Upcoming
Record verification and remaining review work.

Thanks to xet7 !
2026-10-14 05:02:48 +03:00
Lauri Ojansivu 7ec01cb14e Distinguish Malagasy storage credentials from permissions
Correct Google Cloud authentication labels and backup help, preserving optional credential fallback choices.

Thanks to xet7 !
2026-10-14 05:02:18 +03:00
Lauri Ojansivu 3593121b7f Document Malagasy scheduling translation corrections in Upcoming
Record checks and outstanding browser runtime and semantic review work.

Thanks to xet7 !
2026-10-14 04:59:56 +03:00
Lauri Ojansivu bd274fb312 Correct Malagasy recurrence and time-adjustment explanations
Restore set-or-change date triggers, calendar labels and time attribution limits. Cover recurrence and time report wording with regression checks.

Thanks to xet7 !
2026-10-14 04:59:18 +03:00
Lauri Ojansivu fb02fa3f1c Document Malagasy report-label translation audit in Upcoming
Record corrected terminology, regression checks and pending browser runtime verification.

Thanks to xet7 !
2026-10-14 04:56:46 +03:00
Lauri Ojansivu e6d688867c Correct Malagasy report labels and WIP group terminology
Clarify numeric value sums, report measures, forecasts and swimlane selection. Add translation regressions and browser coverage.

Thanks to xet7 !
2026-10-14 04:56:14 +03:00
Lauri Ojansivu fdeebd05c3 Document Malagasy account and report translation repairs
Record semantic corrections, preservation checks and remaining all-language review.

Thanks to xet7 !
2026-10-14 04:51:48 +03:00
Lauri Ojansivu 01204a94f0 Restore Malagasy account warning and report explanations
Replace unrelated export text in account anonymization and expand five report methodology notes. Preserve destructive consequences and analytical limits with regression coverage.

Thanks to xet7 !
2026-10-14 04:51:15 +03:00
Lauri Ojansivu 7e299300e0 Document Malagasy import guidance corrections
Record stale-English findings, semantic checks and remaining all-language review.

Thanks to xet7 !
2026-10-14 04:48:21 +03:00
Lauri Ojansivu 0e518992bb Repair stale Malagasy import guidance
Translate old English Todoist and Planner guides, restore Jira pagination and Excel worksheet behavior, and correct CSV delimiter terminology. Extend semantic and browser coverage.

Thanks to xet7 !
2026-10-14 04:47:49 +03:00
Lauri Ojansivu a996de4e17 Document Malagasy recovery translation milestone
Record placeholder coverage, semantic limits, preservation checks and remaining all-language work.

Thanks to xet7 !
2026-10-14 04:45:24 +03:00
Lauri Ojansivu ef2e446d98 Translate Malagasy import and Scrum recovery messages
Translate 29 entries preserving permanent deletion, unchanged records and rollback safeguards. Add a full-catalog placeholder gate and extend recovery browser coverage.

Thanks to xet7 !
2026-10-14 04:44:53 +03:00
Lauri Ojansivu 11895f38fa Document Malagasy sync recovery translations
Record preserved recovery semantics, verification limits and remaining all-language work.

Thanks to xet7 !
2026-10-14 04:42:22 +03:00
Lauri Ojansivu 8422cc349c Translate Malagasy stopped sync recovery messages
Translate 18 recovery entries preserving retained changes, abandoned writes, access restrictions and replayable-operation safeguards. Extend semantic and browser coverage.

Thanks to xet7 !
2026-10-14 04:41:50 +03:00
Lauri Ojansivu f43e3288fd Document Malagasy Scrum planning translations
Record semantic coverage, preservation checks and remaining all-language review.

Thanks to xet7 !
2026-10-14 04:39:41 +03:00
Lauri Ojansivu 36647667e6 Translate Malagasy Scrum planning transfer messages
Translate 21 entries preserving matching rules, preview outcomes, unchanged cards and synchronization safeguards. Extend semantic and browser coverage.

Thanks to xet7 !
2026-10-14 04:39:12 +03:00
Lauri Ojansivu a84e61caec Document Malagasy workflow translation coverage
Record preserved semantics, regression checks and remaining all-language review.

Thanks to xet7 !
2026-10-14 04:36:26 +03:00
Lauri Ojansivu 9c09c273d4 Translate Malagasy workflow and administration messages
Translate 25 entries for Wrike, LDAP, OAuth, subtask permissions and Blockly fragments. Preserve identifiers and placeholders and extend regression coverage.

Thanks to xet7 !
2026-10-14 04:35:56 +03:00
Lauri Ojansivu b7795367f6 Document Malagasy notification delivery translations
Record preserved semantics, regression checks and remaining all-language work.

Thanks to xet7 !
2026-10-14 04:33:38 +03:00
Lauri Ojansivu 519e99e4f3 Translate Malagasy integrations and notification delivery
Translate 45 messages while preserving inherited defaults, webhook privacy, grouping, quiet hours and link-rule syntax. Add semantic and browser coverage.

Thanks to xet7 !
2026-10-14 04:33:06 +03:00
Lauri Ojansivu b9c0e5739e Document Malagasy bulk transfer translations
Record translated mapping choices, preservation checks and remaining all-language review.

Thanks to xet7 !
2026-10-14 04:30:44 +03:00
Lauri Ojansivu ef60786045 Translate Malagasy bulk import and export choices
Translate 35 messages covering member and column mapping, bulk transfer layouts, Trello export and Sandstorm access. Preserve technical markers and add semantic and browser coverage.

Thanks to xet7 !
2026-10-14 04:30:03 +03:00
Lauri Ojansivu 27f29d2cd5 Document Malagasy project import translations
Record translated import limits, regression coverage and remaining all-language review.

Thanks to xet7 !
2026-10-14 04:27:30 +03:00
Lauri Ojansivu b3eaaf9f4e Translate Malagasy project import guidance
Translate twelve project import guides with explicit omission warnings, English column requirements and nested-task markers. Extend semantic and browser coverage.

Thanks to xet7 !
2026-10-14 04:26:56 +03:00
Lauri Ojansivu b09a406d9f Document Malagasy outline and task import translations
Record semantic coverage, preservation checks and remaining translation work.

Thanks to xet7 !
2026-10-14 04:24:20 +03:00
Lauri Ojansivu 211c67ad11 Translate Malagasy outline and task import guides
Translate ten import guides while preserving format names, data mappings, completion dates and first-board restrictions. Extend semantic and browser coverage.

Thanks to xet7 !
2026-10-14 04:23:43 +03:00
Lauri Ojansivu 239190a02d Document Malagasy linked-card and storage translations
Record placeholder coverage, regression checks and remaining all-language work.

Thanks to xet7 !
2026-10-14 04:21:21 +03:00
Lauri Ojansivu 7d10b098f8 Translate Malagasy linked-card and storage messages
Fill 67 English placeholders including field-link permissions and directions, announcements, storage settings and rule connectors. Extend catalog and browser regression coverage.

Thanks to xet7 !
2026-10-14 04:20:46 +03:00
Lauri Ojansivu 244331c775 Document Haitian action labels and translation inventory
Record semantic corrections, verification limits and the current remaining all-language inventory.

Thanks to xet7 !
2026-10-14 04:16:14 +03:00
Lauri Ojansivu 50955434aa Clarify Haitian card actions and bounce thresholds
Distinguish destination linking from creating a board from the current card, explain version lookup failure and configured bounce thresholds, and extend regression coverage.

Thanks to xet7 !
2026-10-14 04:15:35 +03:00
Lauri Ojansivu 4bfec2b6b4 Document Haitian import and sync guidance review
Record updated guidance, semantic review and regression checks with the remaining all-language work still open.

Thanks to xet7 !
2026-10-14 04:12:53 +03:00
Lauri Ojansivu 4063ccbbc8 Update Haitian import and synchronization guidance
Restore Jira pagination and Excel worksheet mapping explanations. Use Haitian preview terminology and clarify external tracking. Add semantic and browser coverage.

Thanks to xet7 !
2026-10-14 04:12:13 +03:00
Lauri Ojansivu 5d3b6f99fe Document Haitian analytics translation repairs
Record restored methodology, regression coverage and preservation checks while keeping all-language review open.

Thanks to xet7 !
2026-10-14 04:08:25 +03:00
Lauri Ojansivu 8a4cf08d65 Restore Haitian analytics explanations and report labels
Replace incomplete shorthand with translated methodology, missing-history rules, forecast limits and date fallbacks. Clarify movement reasons and never-synced state; add semantic and browser coverage.

Thanks to xet7 !
2026-10-14 04:07:50 +03:00
Lauri Ojansivu bf8d41363c Document Haitian WIP and numeric summary translation repairs
Record semantic regression coverage, preservation checks and remaining all-language review.

Thanks to xet7 !
2026-10-14 04:04:50 +03:00
Lauri Ojansivu dcb7064e49 Correct Haitian WIP groups and numeric summary labels
Distinguish groups from enabling limits, clarify swimlane selection and describe numeric value sums instead of field counts. Add catalog and browser regression coverage.

Thanks to xet7 !
2026-10-14 04:04:12 +03:00
Lauri Ojansivu 46fd33defc Document Haitian backup and credential translation repairs
Record technical-literal coverage, preservation checks and remaining all-language review.

Thanks to xet7 !
2026-10-14 03:59:43 +03:00
Lauri Ojansivu 9bc9a3fe7e Correct Haitian backup and credential terminology
Repair eight storage-help entries while preserving encryption-key requirements, restoration warnings, backup paths and provider permission labels. Add semantic and cloud-settings browser coverage.

Thanks to xet7 !
2026-10-14 03:59:09 +03:00
Lauri Ojansivu 13e861f476 Document Haitian account anonymization warning repair
Record the corrected consequences, regression evidence and remaining all-language review.

Thanks to xet7 !
2026-10-14 03:56:49 +03:00
Lauri Ojansivu cdcbe1e430 Repair Haitian account anonymization warning
Replace incorrectly duplicated export guidance with the actual permanent identity changes, avatar removal, disabled login and preserved history. Add semantic and confirmation-popup regression coverage.

Thanks to xet7 !
2026-10-14 03:56:19 +03:00
Lauri Ojansivu 98b66c25f8 Document Haitian recurrence translation corrections
Record semantic regression coverage, preservation checks and remaining all-language work.

Thanks to xet7 !
2026-10-14 03:54:14 +03:00
Lauri Ojansivu 95f0938b36 Distinguish Haitian card recurrence from checklist reset
Correct eight labels for recurring cards, automatic unchecking, checking all items and known accounts. Add semantic and popup regression coverage.

Thanks to xet7 !
2026-10-14 03:53:34 +03:00
Lauri Ojansivu 6a5f4a16c4 Document Haitian due-card and search translation repairs
Record production-parser coverage, preservation checks and remaining all-language review.

Thanks to xet7 !
2026-10-14 03:50:51 +03:00
Lauri Ojansivu 7c5571248e Correct Haitian due-card scope and attachment search terms
Repair nine labels and verify the production parser accepts attachment presence and absence queries while rejecting invalid predicates. Extend browser search coverage.

Thanks to xet7 !
2026-10-14 03:50:19 +03:00
Lauri Ojansivu 5a5787c517 Document Haitian administration translation corrections
Record tested semantics, preservation checks and remaining all-language review.

Thanks to xet7 !
2026-10-14 03:47:29 +03:00
Lauri Ojansivu 54c2af0993 Correct Haitian administration and notification precedence
Repair fourteen hostname, permission, override and interface labels. Preserve tenant prerequisites and administrator boundaries; extend semantic and notification UI coverage.

Thanks to xet7 !
2026-10-14 03:46:58 +03:00
Lauri Ojansivu d43e17624d Document Haitian settings translation repairs
Record validation, Chinese preservation and remaining all-language semantic review.

Thanks to xet7 !
2026-10-14 03:44:27 +03:00
Lauri Ojansivu a7346d6a58 Correct Haitian visibility and email settings wording
Repair fourteen settings strings, including SMTP host, clickable URL schemes, starred swimlanes and template labels. Preserve fallback behavior and add semantic and browser regression coverage.

Thanks to xet7 !
2026-10-14 03:43:55 +03:00
Lauri Ojansivu 3dcedf2a8e Document Haitian upload and archive wording corrections
Record regression coverage, preservation checks and remaining all-language review.

Thanks to xet7 !
2026-10-14 03:41:38 +03:00
Lauri Ojansivu 08b5bbf36f Correct Haitian upload direction and archive guidance
Correct ten upload and control labels. Preserve map administrator permissions and point archive restoration to All Boards; extend semantic and map-upload browser coverage.

Thanks to xet7 !
2026-10-14 03:40:58 +03:00
Lauri Ojansivu e0eb420fc2 Document Haitian calendar and core-interface corrections
Record wrong-language repairs, regression results and remaining all-language review.

Thanks to xet7 !
2026-10-14 03:38:34 +03:00
Lauri Ojansivu 6227e6b908 Replace French calendar labels in Haitian Creole
Correct 20 calendar and core-interface entries, preserving calendar variants and restoring account, snapshot and roadmap meanings. Add semantic and calendar-selector regression coverage.

Thanks to xet7 !
2026-10-14 03:38:02 +03:00
Lauri Ojansivu 276d173d01 Document Haitian subtask and checklist corrections
Record validation and remaining all-language review in Upcoming and the translation audit.

Thanks to xet7 !
2026-10-14 03:35:30 +03:00
Lauri Ojansivu 5dd13b7808 Unify Haitian subtask and checklist terminology
Correct 30 entries across card controls, activity text and settings. Restore matching and template-copy wording, preserve attachment retention, and extend subtask browser assertions.

Thanks to xet7 !
2026-10-14 03:34:49 +03:00
Lauri Ojansivu 70981a0812 Document Haitian semantic translation corrections
Record the 81 corrected entries, regression coverage and remaining all-language review.

Thanks to xet7 !
2026-10-14 03:32:36 +03:00
Lauri Ojansivu bcdf935521 Correct Haitian attachment and rule terminology
Correct 81 entries: attachment terminology throughout the catalog, checkbox actions, date initialization triggers and assignee grammar. Preserve placeholders and deletion distinctions, and add rule-builder and attachment-settings UI coverage.

Thanks to xet7 !
2026-10-14 03:32:06 +03:00
Lauri Ojansivu c60d616ccb Document Haitian placeholder milestone and remaining review
Record the stale-English corrections, current catalog gate and validation limits in Upcoming and the translation audit.

Thanks to xet7 !
2026-10-14 03:27:18 +03:00
Lauri Ojansivu eca6c80825 Finish Haitian English placeholders and repair stale import guides
Translate eight current placeholders and two stale-English guides. Preserve Scrum recovery permissions and data retention, add the current catalog gate and extend six browser cases. Semantic review remains separate.

Thanks to xet7 !
2026-10-14 03:26:43 +03:00
Lauri Ojansivu 048f4cc5fd Document Haitian planning sync and recovery translations
Record coverage and remaining all-language work in Upcoming and the translation audit.

Thanks to xet7 !
2026-10-14 03:23:58 +03:00
Lauri Ojansivu 940aee620b Translate Haitian planning sync and recovery messages
Translate 48 entries with retained-data, permanent-deletion and replay restrictions intact. Extend catalog and browser coverage for planning sync and interrupted operations.

Thanks to xet7 !
2026-10-14 03:23:28 +03:00
Lauri Ojansivu 5287a6800a Document Haitian workflow and planning translations
Record regression coverage and remaining translation work in Upcoming and the audit.

Thanks to xet7 !
2026-10-14 03:21:01 +03:00
Lauri Ojansivu 8c8c00546d Translate Haitian workflow and planning messages
Translate 45 administration, workflow, Blockly and Scrum entries. Preserve permissions, completion groups, source matching and placeholders; cover subtask editing and denied access in the browser suite.

Thanks to xet7 !
2026-10-14 03:20:34 +03:00
Lauri Ojansivu b8f2925f1b Document Haitian integration and notification translations
Record regression results, preserved Chinese values and the remaining all-language inventory in Upcoming and the audit.

Thanks to xet7 !
2026-10-14 03:17:56 +03:00
Lauri Ojansivu 38e2004f8e Translate Haitian integration and notification settings
Translate 49 remaining entries while preserving rule syntax, inherited settings, identity controls and scheduling distinctions. Register Haitian Creole in notification UI regression coverage.

Thanks to xet7 !
2026-10-14 03:17:25 +03:00
Lauri Ojansivu cbd2d0ec2c Document Haitian Creole bulk import and export translations
Record coverage and remaining all-language review in Upcoming and the translation audit.

Thanks to xet7 !
2026-10-14 03:15:16 +03:00
Lauri Ojansivu 7687970a9d Translate Haitian Creole bulk import and export controls
Preserve member mapping limits, project boundaries, export layouts and placeholders; add catalog and browser regression coverage.

Thanks to xet7 !
2026-10-14 03:14:42 +03:00
Lauri Ojansivu 4598f254a4 Document Haitian Creole import mapping translations
Thanks to xet7 !
2026-10-14 03:10:35 +03:00
Lauri Ojansivu a79388c894 Translate Haitian Creole import mapping controls
Thanks to xet7 !
2026-10-14 03:10:01 +03:00
Lauri Ojansivu 84cd1a7e17 Document Haitian Creole project import translations
Thanks to xet7 !
2026-10-14 03:07:51 +03:00
Lauri Ojansivu e26b9f041b Translate Haitian Creole project import guides
Thanks to xet7 !
2026-10-14 03:07:19 +03:00
Lauri Ojansivu 724ff27a10 Document Haitian Creole import guide translations
Thanks to xet7 !
2026-10-14 03:04:37 +03:00
Lauri Ojansivu 80e756b223 Translate Haitian Creole outline and task import guides
Thanks to xet7 !
2026-10-14 03:04:08 +03:00
Lauri Ojansivu 8f94222d92 Document Haitian Creole linked-card translation progress
Thanks to xet7 !
2026-10-14 03:01:55 +03:00
Lauri Ojansivu 4ac5621e64 Translate Haitian Creole linked cards and storage controls
Thanks to xet7 !
2026-10-14 03:01:22 +03:00
Lauri Ojansivu ffd8494ee1 Document shared-word translation review
Thanks to xet7 !
2026-10-14 02:56:30 +03:00
Lauri Ojansivu db56686e35 Preserve reviewed shared words in translation inventory
Thanks to xet7 !
2026-10-14 02:56:03 +03:00
Lauri Ojansivu d47eaf3b13 Document Latin import repairs and translation inventory
Thanks to xet7 !
2026-10-14 02:53:51 +03:00
Lauri Ojansivu 96e2c6f620 Translate stale Latin Todoist and Planner import guidance
Thanks to xet7 !
2026-10-14 02:53:21 +03:00
Lauri Ojansivu 849fcfdb94 Document Latin prefix cleanup and remaining review gaps
Thanks to xet7 !
2026-10-14 02:51:10 +03:00
Lauri Ojansivu d1e8c955e3 Repair remaining prefixed Latin report translations
Thanks to xet7 !
2026-10-14 02:50:41 +03:00
Lauri Ojansivu c0d8555395 Document Latin status and account translation review
Thanks to xet7 !
2026-10-14 02:48:05 +03:00
Lauri Ojansivu 6f0bbb9a34 Repair Latin status account and repair-result translations
Thanks to xet7 !
2026-10-14 02:47:36 +03:00
Lauri Ojansivu ddcd625c3c Document Latin monitoring translation review
Thanks to xet7 !
2026-10-14 02:45:07 +03:00
Lauri Ojansivu 2e4f49297e Repair Latin monitoring and conversion progress translations
Thanks to xet7 !
2026-10-14 02:44:38 +03:00
Lauri Ojansivu 9697d3ac00 Document Latin migration confirmation translation review
Thanks to xet7 !
2026-10-14 02:42:11 +03:00
Lauri Ojansivu 7991925d05 Repair Latin migration confirmations and progress labels
Thanks to xet7 !
2026-10-14 02:41:40 +03:00
Lauri Ojansivu de5c695347 Document Latin board migration translation review
Thanks to xet7 !
2026-10-14 02:39:03 +03:00
Lauri Ojansivu 5c32f58834 Repair Latin board migration translations
Thanks to xet7 !
2026-10-14 02:38:33 +03:00
Lauri Ojansivu ce05a0d6bc Document Latin migration status translation review
Thanks to xet7 !
2026-10-14 02:36:10 +03:00
Lauri Ojansivu d9f565bdb7 Repair Latin migration status and S3 translations
Thanks to xet7 !
2026-10-14 02:35:44 +03:00
Lauri Ojansivu 621ab16e82 Document Latin cloud guidance translation review
Thanks to xet7 !
2026-10-14 02:33:48 +03:00
Lauri Ojansivu efbff09543 Repair Latin cloud configuration guidance
Thanks to xet7 !
2026-10-14 02:33:20 +03:00
Lauri Ojansivu 46106f55d4 Document Latin backup translation review
Thanks to xet7 !
2026-10-14 02:31:10 +03:00
Lauri Ojansivu 6bfdbc9179 Repair Latin backup scope and credential translations
Thanks to xet7 !
2026-10-14 02:30:44 +03:00
Lauri Ojansivu 280b32db97 Document Latin security translation review
Thanks to xet7 !
2026-10-14 02:28:49 +03:00
Lauri Ojansivu 386fbba5ec Repair Latin security setting translations
Thanks to xet7 !
2026-10-14 02:28:21 +03:00
Lauri Ojansivu 37cbaeff48 Document Latin migration translation review
Thanks to xet7 !
2026-10-14 02:25:42 +03:00
Lauri Ojansivu e949142731 Repair Latin migration and card loading translations
Thanks to xet7 !
2026-10-14 02:25:14 +03:00
Lauri Ojansivu f329147f45 Document Latin scheduling and cloud translation review
Thanks to xet7 !
2026-10-14 02:23:09 +03:00
Lauri Ojansivu 8cf28c4464 Repair Latin scheduling and cloud storage translations
Thanks to xet7 !
2026-10-14 02:22:43 +03:00
Lauri Ojansivu c997c3ad89 Document Latin support and lockout translation review
Thanks to xet7 !
2026-10-14 02:20:29 +03:00
Lauri Ojansivu 08137b1d4a Repair Latin support and lockout translations
Thanks to xet7 !
2026-10-14 02:20:00 +03:00
Lauri Ojansivu f3d436febb Document Latin maintenance translation review
Thanks to xet7 !
2026-10-14 02:17:11 +03:00
Lauri Ojansivu 5002df4c9d Repair Latin maintenance and account translations
Thanks to xet7 !
2026-10-14 02:16:42 +03:00
Lauri Ojansivu d0efe6565a Document Latin storage translation review
Thanks to xet7 !
2026-10-14 02:14:26 +03:00
Lauri Ojansivu 104480b6cc Repair Latin storage and attachment repair translations
Thanks to xet7 !
2026-10-14 02:13:52 +03:00
Lauri Ojansivu c6c5b4b56c Document Latin checklist and attachment translation review
Thanks to xet7 !
2026-10-14 02:11:16 +03:00
Lauri Ojansivu a83a794b12 Repair Latin checklist and attachment action translations
Thanks to xet7 !
2026-10-14 02:10:44 +03:00
Lauri Ojansivu 681e7a4b07 Document Latin invitation and memory translation review
Thanks to xet7 !
2026-10-14 02:08:05 +03:00
Lauri Ojansivu 2dd8c45cab Repair Latin invitation and Node memory translations
Translate 35 mixed-language entries, preserving invitation conditions, organization controls, allocated/used/peak memory and diagnostic overwrite semantics. Verify V8 terminology and add regression and browser coverage.

Thanks to xet7 !
2026-10-14 02:05:19 +03:00
Lauri Ojansivu 82d73334e0 Document Latin waiting and ticket translation audit
Thanks to xet7 !
2026-10-14 02:01:56 +03:00
Lauri Ojansivu 5df2e188cc Repair Latin waiting indicators and ticket labels
Translate 30 mixed-language controls and messages, preserving deletion prerequisites, Cc syntax, window-action distinctions, ticket states and history actions. Add regression and browser coverage.

Thanks to xet7 !
2026-10-14 02:01:13 +03:00
Lauri Ojansivu 03029bf0a4 Document Latin office API and recovery translation audit
Thanks to xet7 !
2026-10-14 01:57:37 +03:00
Lauri Ojansivu 1c0204e23e Repair Latin office API and recovery reports
Translate 25 mixed-language entries, preserving API aggregation and configuration, recovery actions and automatic continuation. Add regression and office-report browser coverage.

Thanks to xet7 !
2026-10-14 01:56:48 +03:00
Lauri Ojansivu 779d5ffa7e Document Latin field and report translation audit
Thanks to xet7 !
2026-10-14 01:54:18 +03:00
Lauri Ojansivu bd9d654aae Repair Latin string template and report translations
Translate 30 mixed-language entries, preserving value substitution, space entities, invisible-character filtering and successful-login reporting scope. Extend custom-field browser coverage.

Thanks to xet7 !
2026-10-14 01:53:41 +03:00
Lauri Ojansivu 04351489ad Document Latin location and background translation audit
Thanks to xet7 !
2026-10-14 01:51:09 +03:00
Lauri Ojansivu 08c7d5314b Repair Latin location and background translations
Translate 30 mixed-language entries, preserving coordinate distinctions, size placeholders, sorting direction and literal troubleshooting commands. Add positive and negative location-detection browser coverage.

Thanks to xet7 !
2026-10-14 01:50:32 +03:00
Lauri Ojansivu 2e8e089819 Document Latin sorting and dependency translation audit
Thanks to xet7 !
2026-10-14 01:48:14 +03:00
Lauri Ojansivu 959bfbcedc Repair Latin sorting and dependency translations
Translate 37 mixed-language entries, preserving dependency direction, completion polarity, import formats and unmatched counts. Keep the exact shared Latin word Color and add regression and browser coverage.

Thanks to xet7 !
2026-10-14 01:47:33 +03:00
Lauri Ojansivu 7ea0448f23 Document Latin search help translation audit
Thanks to xet7 !
2026-10-14 01:43:28 +03:00
Lauri Ojansivu 30c12e510c Repair Latin search instructions and examples
Translate 32 mixed-language entries, preserving query placeholders while explaining OR/AND, negation, sorting, date ranges and archive defaults. Localize user, absence and card-number examples with regression and browser coverage.

Thanks to xet7 !
2026-10-14 01:42:49 +03:00
Lauri Ojansivu b791d8d054 Document Latin search predicates translation audit
Thanks to xet7 !
2026-10-14 01:39:28 +03:00
Lauri Ojansivu e3ea440a31 Repair Latin search predicates and introductory help
Translate 40 mixed-language search entries, preserve validation constraints and placeholders, and use a localized list example. Verify parser registration and shared selector wording; add overdue-search browser coverage.

Thanks to xet7 !
2026-10-14 01:38:48 +03:00
Lauri Ojansivu 90349ce081 Document Latin search translation audit
Thanks to xet7 !
2026-10-14 01:36:43 +03:00
Lauri Ojansivu cb3da6ac52 Repair Latin search messages and operator names
Translate 48 mixed-language entries, retaining search access boundaries and placeholders. Preserve shorthand aliases and verify distinct localized operators are registered in the parser, with browser coverage for title filtering.

Thanks to xet7 !
2026-10-14 01:36:05 +03:00
Lauri Ojansivu 976e0893f1 Document Latin template sharing and card view audit
Thanks to xet7 !
2026-10-14 01:33:49 +03:00
Lauri Ojansivu 13f06820f7 Repair Latin template sharing and card view translations
Translate 40 mixed-language entries, preserving template scope and nonempty filtering, domain validation examples, permission boundaries and incomplete due-card semantics.

Thanks to xet7 !
2026-10-14 01:33:12 +03:00
Lauri Ojansivu c062eb7b74 Document Latin roles and weekdays translation audit
Thanks to xet7 !
2026-10-14 01:31:06 +03:00
Lauri Ojansivu 61480096ba Repair Latin role explanations and weekday labels
Translate 35 mixed-language entries, preserving global-admin rights, unsaved role previews, assigned-only visibility and linked-card deletion prerequisites. Add regression and browser coverage.

Thanks to xet7 !
2026-10-14 01:30:31 +03:00
Lauri Ojansivu bc28b2f978 Document Latin reminder and preference translation audit
Thanks to xet7 !
2026-10-14 01:28:14 +03:00
Lauri Ojansivu ee4ce8a1eb Repair Latin reminders and preference descriptions
Translate 35 mixed-language entries, preserving reminder states, irreversible deletion, self-account scope, keyboard combinations and enabled/disabled preference behavior.

Thanks to xet7 !
2026-10-14 01:27:35 +03:00
Lauri Ojansivu f51ded9566 Document Latin authentication and date translation audit
Thanks to xet7 !
2026-10-14 01:25:23 +03:00
Lauri Ojansivu d0676f4a76 Repair Latin authentication and date activity labels
Translate 30 mixed-language entries, preserving irreversible swimlane deletion, date placeholders and relative card/list placement. Add regression and browser warning coverage.

Thanks to xet7 !
2026-10-14 01:24:47 +03:00
Lauri Ojansivu 408883859c Document Latin rule hints and branding audit
Thanks to xet7 !
2026-10-14 01:22:56 +03:00
Lauri Ojansivu c59113bebf Repair Latin rule hints and branding settings
Translate 40 mixed-language entries, preserving empty-field matching, comma-separated checklist input, distinct date fields, authentication names, JSON filenames and HTML tags.

Thanks to xet7 !
2026-10-14 01:22:23 +03:00
Lauri Ojansivu 1c2d02f1f2 Document Latin rule description translation audit
Thanks to xet7 !
2026-10-14 01:20:10 +03:00
Lauri Ojansivu fd921c8fef Repair Latin rule actions and saved descriptions
Translate 50 action fragments and descriptions with matching movement and checklist wording. Recognize the exact shared Latin imperative Remove without exempting longer English prose. Add regression and browser coverage.

Thanks to xet7 !
2026-10-14 01:19:34 +03:00
Lauri Ojansivu 637e7e0902 Document Latin trigger grammar translation audit
Thanks to xet7 !
2026-10-14 01:17:29 +03:00
Lauri Ojansivu 61a2f68df0 Repair Latin rule trigger sentences and linking verbs
Translate 33 prefixed trigger fragments and repair three unprefixed assignee/date entries. Omit the redundant linking verb for Latin finite predicates, preserving other locales and testing assembled and saved descriptions.

Thanks to xet7 !
2026-10-14 01:16:39 +03:00
Lauri Ojansivu 9114224715 Document Latin rule action translation audit
Thanks to xet7 !
2026-10-14 01:14:08 +03:00
Lauri Ojansivu 5e94c5e2a0 Repair Latin rule action and duration labels
Translate 24 mixed-language controls while preserving completion polarity, relative dates, all-card scope and distinct duration units. Add workflow toolbox browser coverage.

Thanks to xet7 !
2026-10-14 01:13:31 +03:00
Lauri Ojansivu e575b6d559 Document Latin rule import and scheduling audit
Thanks to xet7 !
2026-10-14 01:11:38 +03:00
Lauri Ojansivu 1f11631552 Repair Latin rule import and scheduling translations
Translate 30 mixed-language entries. Preserve partial workflow conversion, unmapped reporting, weekday boundaries and distinct due-date states, with regression coverage.

Thanks to xet7 !
2026-10-14 01:11:03 +03:00
Lauri Ojansivu 3c3a3a2cc1 Document Latin rule builder translation audit
Thanks to xet7 !
2026-10-14 01:09:10 +03:00
Lauri Ojansivu 6150377ef5 Repair Latin rule builder and activity translations
Translate 40 mixed-language rule controls and activity entries, preserving trigger and action distinctions, import and export direction, archive restoration and placeholders. Extend workflow builder browser coverage to Latin.

Thanks to xet7 !
2026-10-14 01:08:23 +03:00
Lauri Ojansivu 9af473f39c Document Latin deletion and subtask translation audit
Thanks to xet7 !
2026-10-14 01:06:20 +03:00
Lauri Ojansivu 0da6274347 Repair Latin deletion warnings and subtask settings
Translate 40 mixed-language deletion, hierarchy, minicard and activity entries. Preserve irreversible deletion warnings, empty duplicate-list scope, full ancestry versus immediate parent, and activity placeholders.

Thanks to xet7 !
2026-10-14 01:05:39 +03:00
Lauri Ojansivu 5ffbfa083f Document Latin organization translation audit
Thanks to xet7 !
2026-10-14 01:03:23 +03:00
Lauri Ojansivu 7729efb8e0 Repair Latin organization permissions and tenant labels
Translate 25 mixed-language organization, membership, date and color labels. Preserve tenant configuration requirements and the limits of delegated administration, with regression and browser coverage.

Thanks to xet7 !
2026-10-14 01:02:48 +03:00
Lauri Ojansivu 70e696edca Document Latin field policy translation audit
Thanks to xet7 !
2026-10-14 00:58:10 +03:00
Lauri Ojansivu f8a09add9c Repair Latin field policies and system metrics
Correct 30 prefixed labels, preserve new/all-card scope, memory distinctions and private-only visibility, and extend custom-field browser coverage.

Thanks to xet7 !
2026-10-14 00:57:47 +03:00
Lauri Ojansivu fdec6bb6ba Document Latin system information translation audit
Thanks to xet7 !
2026-10-14 00:56:09 +03:00
Lauri Ojansivu 0dda250d79 Repair Latin webhook and system information labels
Correct 30 prefixed labels, preserve product names and runtime configuration identifiers, and distinguish outgoing from bidirectional webhooks.

Thanks to xet7 !
2026-10-14 00:55:49 +03:00
Lauri Ojansivu c86732f73c Document Latin transfer limit and SMTP translation audit
Thanks to xet7 !
2026-10-14 00:54:08 +03:00
Lauri Ojansivu 49dc81ea2b Repair Latin transfer limits and SMTP messages
Correct 40 prefixed strings, restore literal size units and invitation variables, preserve separate server safety caps and extend attachment-limit browser coverage.

Thanks to xet7 !
2026-10-14 00:53:46 +03:00
Lauri Ojansivu 0db0ad3d12 Document Latin upload and WIP translation audit
Thanks to xet7 !
2026-10-14 00:51:48 +03:00
Lauri Ojansivu 7842c44d16 Repair Latin upload and WIP messages
Correct 30 prefixed strings, distinguish upload states and logo fields, preserve WIP recovery choices, and add warning browser coverage.

Thanks to xet7 !
2026-10-14 00:51:28 +03:00
Lauri Ojansivu 58dd1771f6 Document Latin sidebar and tracking translation audit
Thanks to xet7 !
2026-10-14 00:49:47 +03:00
Lauri Ojansivu 42ab802edd Repair Latin sidebar and tracking controls
Correct 30 prefixed strings, preserve default-board behavior, shortcut ranges and add/remove distinctions, and extend shortcut-page coverage.

Thanks to xet7 !
2026-10-14 00:49:27 +03:00
Lauri Ojansivu dbe9712bd9 Document Latin member removal and shortcut audit
Thanks to xet7 !
2026-10-14 00:47:48 +03:00
Lauri Ojansivu 7f256bce30 Repair Latin member removal and shortcut messages
Correct 30 prefixed strings, retain Sandstorm access revocation warning and all-card removal notification, and distinguish self-membership from assignment in shortcut coverage.

Thanks to xet7 !
2026-10-14 00:47:27 +03:00
Lauri Ojansivu bcacf5d58b Document Latin visibility translation audit
Thanks to xet7 !
2026-10-14 00:45:42 +03:00
Lauri Ojansivu 48a62a615e Repair Latin visibility and notification descriptions
Correct 30 prefixed strings, preserve public-read/member-edit and assigned-card boundaries, and extend archive-tab browser coverage to Latin.

Thanks to xet7 !
2026-10-14 00:45:18 +03:00
Lauri Ojansivu 9673e6e0c9 Document Latin list action translation audit
Thanks to xet7 !
2026-10-14 00:43:20 +03:00
Lauri Ojansivu 5cb027f4c4 Repair Latin list actions and board departure messages
Correct 30 prefixed strings, preserve all-card scope and permanent deletion versus archiving, and add list warning browser coverage.

Thanks to xet7 !
2026-10-14 00:42:59 +03:00
Lauri Ojansivu 7892d7dac8 Document Latin mapping and validation translation audit
Thanks to xet7 !
2026-10-14 00:41:07 +03:00
Lauri Ojansivu 0225b90529 Repair Latin member mapping and validation messages
Correct 25 prefixed strings, preserve current-user fallback, four-digit years and the last-administrator restriction, and extend keyboard-shortcut browser coverage.

Thanks to xet7 !
2026-10-14 00:40:48 +03:00
Lauri Ojansivu dfc88ff47f Document Latin Trello import translation audit
Thanks to xet7 !
2026-10-14 00:39:06 +03:00
Lauri Ojansivu 2e641f3fb7 Repair Latin Trello import messages
Correct 40 prefixed strings, preserve ZIP limits, credential requirements and destructive cancellation scope, and add file and credential-label browser coverage.

Thanks to xet7 !
2026-10-14 00:38:44 +03:00
Lauri Ojansivu b28afc5f6d Document Latin advanced filter and import translation audit
Thanks to xet7 !
2026-10-14 00:36:38 +03:00
Lauri Ojansivu 7f26a7e7a9 Repair Latin advanced filter and import instructions
Correct 30 prefixed messages, update stale Jira pagination and Excel sheet guidance, preserve literal filter examples, and extend Latin import browser coverage to nine more formats.

Thanks to xet7 !
2026-10-14 00:36:11 +03:00
Lauri Ojansivu beb06e9944 Document Latin filter translation audit
Thanks to xet7 !
2026-10-14 00:33:18 +03:00
Lauri Ojansivu 9860c8a248 Repair Latin sorting and filter controls
Correct 30 prefixed strings, retain distinct due periods and missing-value filters, and distinguish members, assignees and creators. Add date-filter browser coverage.

Thanks to xet7 !
2026-10-14 00:32:57 +03:00
Lauri Ojansivu ff6dc11156 Document Latin export translation audit
Thanks to xet7 !
2026-10-14 00:31:01 +03:00
Lauri Ojansivu 855b54d55a Repair Latin export labels and name collision errors
Correct 30 prefixed strings, preserve export permissions, uploader/time distinctions and disk-space requirements, and add export-popup browser coverage.

Thanks to xet7 !
2026-10-14 00:30:40 +03:00
Lauri Ojansivu f9161d0096 Document Latin account email and import error audit
Thanks to xet7 !
2026-10-14 00:29:02 +03:00
Lauri Ojansivu 737e8cd4d0 Repair Latin account emails and import errors
Correct 25 prefixed messages, retain invitation variables and paragraph breaks, and clarify permission and linked-card restrictions. Add malformed-JSON browser coverage.

Thanks to xet7 !
2026-10-14 00:28:42 +03:00
Lauri Ojansivu 843c1b2b8b Document Latin custom field translation audit
Thanks to xet7 !
2026-10-14 00:26:51 +03:00
Lauri Ojansivu 9c5f1c6ad4 Repair Latin custom fields and account email messages
Correct 50 prefixed strings, restore date-format tokens and enrollment email paragraphs, and preserve field deletion and global administrator warnings. Add form browser coverage.

Thanks to xet7 !
2026-10-14 00:26:32 +03:00
Lauri Ojansivu 592537acc0 Document Latin role and comment translation audit
Thanks to xet7 !
2026-10-14 00:24:32 +03:00
Lauri Ojansivu 9b890bbe16 Repair Latin role and comment controls
Correct 30 prefixed strings, preserve assigned-card and read-only restrictions, clarify worker actions and restore the empty editor placeholder. Extend semantic and browser coverage.

Thanks to xet7 !
2026-10-14 00:24:13 +03:00
Lauri Ojansivu 20b654a113 Document Latin color and navigation translation audit
Thanks to xet7 !
2026-10-14 00:22:24 +03:00
Lauri Ojansivu 30024f1e66 Repair Latin colors and navigation labels
Translate 32 prefixed strings, including all 25 color names, and update the stale archive restoration location. Add color distinctions and skip-link coverage. Less common shade descriptions remain lower-confidence pending linguistic review.

Thanks to xet7 !
2026-10-14 00:22:04 +03:00
Lauri Ojansivu 1a0c035cde Document Latin font and card aging translation audit
Thanks to xet7 !
2026-10-14 00:20:06 +03:00
Lauri Ojansivu 35d2caec77 Repair Latin font and card aging controls
Correct 35 prefixed strings, preserve font size distinctions, idle-day fading levels and movement directions, and extend font preview browser coverage.

Thanks to xet7 !
2026-10-14 00:19:47 +03:00
Lauri Ojansivu dd0800dce5 Document Latin member mapping translation audit
Thanks to xet7 !
2026-10-14 00:17:55 +03:00
Lauri Ojansivu 8e336c5a13 Repair Latin member mapping and appearance labels
Correct 40 mixed-language strings, preserve imported-role limits and mapping scope, and distinguish theme categories from commands. Add semantic checks and font-popup browser coverage.

Thanks to xet7 !
2026-10-14 00:17:26 +03:00
Lauri Ojansivu 9ec1afce50 Document Latin voting and Planning Poker translation audit
Thanks to xet7 !
2026-10-14 00:15:38 +03:00
Lauri Ojansivu 9c55d0f589 Repair Latin voting and Planning Poker labels
Correct 45 prefixed strings, restore numeric poker choices, distinguish account anonymization from import, and cover positive and negative voting labels.

Thanks to xet7 !
2026-10-14 00:15:17 +03:00
Lauri Ojansivu 374334e63c Document Latin card archive translation audit
Thanks to xet7 !
2026-10-14 00:13:26 +03:00
Lauri Ojansivu d3ea074216 Repair Latin card archive and voting messages
Correct 40 prefixed strings and preserve archive restoration, irreversible deletion, member scope and voting roles. Add card archive confirmation browser coverage.

Thanks to xet7 !
2026-10-14 00:13:06 +03:00
Lauri Ojansivu a7d577b9fc Document Latin board display translation audit
Thanks to xet7 !
2026-10-14 00:11:10 +03:00
Lauri Ojansivu 57534c06e9 Repair Latin board appearance and view labels
Correct 44 prefixed mixed-language strings, preserve visibility and assignee scope, and add semantic checks and calendar menu browser coverage.

Thanks to xet7 !
2026-10-14 00:10:41 +03:00
Lauri Ojansivu b770cba5d1 Document Latin sizing and archive translation audit
Thanks to xet7 !
2026-10-14 00:08:47 +03:00
Lauri Ojansivu f6f05040ae Repair Latin sizing and archive controls
Correct 55 mixed-language strings and test personal/shared scope, toggle direction, data-loss warnings and permanent attachment deletion. Add a browser toggle regression.

Thanks to xet7 !
2026-10-14 00:08:26 +03:00
Lauri Ojansivu a27c4b84fb Document Latin checklist and workspace translation audit
Thanks to xet7 !
2026-10-14 00:06:01 +03:00
Lauri Ojansivu 51421f5384 Repair Latin checklist and workspace translations
Correct 60 mixed-language strings, retain the valid shared Latin word Color, and test argument order, opposite states and non-destructive Home removal. Add workspace navigation browser coverage.

Thanks to xet7 !
2026-10-14 00:05:38 +03:00
Lauri Ojansivu c4f575324b Document Latin activity translation audit
Thanks to xet7 !
2026-10-14 00:02:12 +03:00
Lauri Ojansivu 1e254be892 Repair mixed-language Latin activity messages
Replace 65 prefixed strings with Latin, preserving positional title arguments, move direction, checklist state and comment permissions. Add semantic regression checks and card-history browser coverage.

Thanks to xet7 !
2026-10-14 00:01:48 +03:00
Lauri Ojansivu 5a763705ca Document Latin recovery translation coverage
Thanks to xet7 !
2026-10-13 23:59:17 +03:00
Lauri Ojansivu dc4ea4984b Translate Latin synchronization and recovery messages
Fill 51 pending strings and test matching precedence, retained changes, permanent deletion and recovery restrictions. Register Latin recovery browser scenarios.

Thanks to xet7 !
2026-10-13 23:58:54 +03:00
Lauri Ojansivu bf595d117b Document Latin workflow and Scrum translation coverage
Thanks to xet7 !
2026-10-13 23:57:13 +03:00
Lauri Ojansivu 1212eb97c7 Translate Latin workflow and Scrum import messages
Fill 35 pending translations and repair preview and history labels. Preserve import restrictions, configuration names and placeholders, and extend regression and browser coverage.

Thanks to xet7 !
2026-10-13 23:56:46 +03:00
Lauri Ojansivu ab677e9ace Document Latin notification and link settings translations
Record 46 fills, preservation and regression coverage, and 86 remaining Latin placeholders.

Thanks to xet7 !
2026-10-13 23:52:51 +03:00
Lauri Ojansivu 08eb3aabba Translate Latin notification webhook and link settings
Fill 46 pending strings while preserving link-rule variables, webhook configuration names, quiet-hour behavior and identity choices. Extend semantic and notification settings coverage.

Thanks to xet7 !
2026-10-13 23:52:20 +03:00
Lauri Ojansivu 2e142f7d8b Document Latin import mapping and export translations
Record 35 fills, validation results and 132 remaining Latin placeholders.

Thanks to xet7 !
2026-10-13 23:50:33 +03:00
Lauri Ojansivu 6f71d68317 Translate Latin import mapping and export controls
Fill 35 pending labels and instructions while preserving member mapping, ZIP boundaries, field placeholders and Trello round-trip behavior. Extend semantic and browser checks.

Thanks to xet7 !
2026-10-13 23:50:00 +03:00
Lauri Ojansivu 15d42c6dcf Document the next Latin import-guide batch
Record ten translated guides, coverage and 167 remaining Latin placeholders.

Thanks to xet7 !
2026-10-13 23:48:17 +03:00
Lauri Ojansivu 8d76a58ff8 Translate ten more Latin import guides
Preserve attachment exclusions, source menu and column names, hierarchy markers, archive state and import format choices. Extend semantic and browser instruction checks.

Thanks to xet7 !
2026-10-13 23:47:42 +03:00
Lauri Ojansivu c6791fd51f Document Latin import-guide translation coverage
Record twelve fills, the Trello repair, validation results and 177 remaining Latin placeholders.

Thanks to xet7 !
2026-10-13 23:46:06 +03:00
Lauri Ojansivu ec0e77480e Translate Latin import guides and repair Trello instructions
Translate twelve pending import guides while preserving source menus, format markers, hierarchy mappings and limits. Replace mixed English in the Trello guide and add thirteen browser cases plus semantic checks.

Thanks to xet7 !
2026-10-13 23:45:32 +03:00
Lauri Ojansivu 354ca7aa83 Document Latin card-link translations and pending work
Record 34 fills, two guarded repairs, test coverage and 189 remaining Latin placeholders.

Thanks to xet7 !
2026-10-13 23:42:56 +03:00
Lauri Ojansivu 90ed19f207 Translate Latin card attachments and field links
Fill 34 pending English strings and repair two malformed card-link labels. Cover link direction, permissions and field matching in semantic tests and add Latin browser cases.

Thanks to xet7 !
2026-10-13 23:42:14 +03:00
Lauri Ojansivu 626605259a Document short-rule translations in four more locales
Record 40 fills, preservation and regression checks, and the remaining translation queue.

Thanks to xet7 !
2026-10-13 23:39:12 +03:00
Lauri Ojansivu abf6ab4137 Translate remaining short rules in Javanese Kazakh Tajik and Somali
Fill 40 English placeholders for sorting, actors, recipients, self-selection, counts and Somali Blockly controls. Preserve existing translations and extend alias and token checks.

Thanks to xet7 !
2026-10-13 23:38:31 +03:00
Lauri Ojansivu a42f9e6bef Document short Blockly translations and Khmer alias coverage
Record 126 file-value fills, ten locale names, validation results and the remaining translation inventory.

Thanks to xet7 !
2026-10-13 23:37:00 +03:00
Lauri Ojansivu 0e554efcaf Translate remaining short Blockly controls in ten locales
Fill 126 English file values, covering 140 locale entries through the Khmer alias. Keep control aliases and placeholders consistent and extend regression and browser coverage.

Thanks to xet7 !
2026-10-13 23:36:24 +03:00
Lauri Ojansivu fc8858bc13 Document short rule translations and remaining review
Record 40 fills, the guarded Tagalog endpoint repair and test coverage in Upcoming and the translation audit.

Thanks to xet7 !
2026-10-13 23:33:30 +03:00
Lauri Ojansivu 8685cab6a7 Translate short rule labels in six languages
Fill 40 English placeholders in Bengali, Nepali, Hungarian, Icelandic, Albanian and Tagalog. Use numeric separators where inflection cannot fit the count template, and correct the Tagalog sublist endpoint. Extend semantic, token and Blockly coverage.

Thanks to xet7 !
2026-10-13 23:32:58 +03:00
Lauri Ojansivu fff86bafba Document short-label translations across 22 locales
Record 52 fills, preservation checks, browser discovery and the remaining all-language inventory.

Thanks to xet7 !
2026-10-13 23:30:13 +03:00
Lauri Ojansivu 6b97949c4a Translate short labels and S3 fields in 22 locales
Fill 52 remaining English placeholders while preserving correct shared words and existing Chinese translations. Add token and meaning checks and extend S3 and Blockly browser coverage.

Thanks to xet7 !
2026-10-13 23:29:33 +03:00
Lauri Ojansivu 5493914bd6 Document Southern Sotho catalog review and remaining inventory
Record email corrections, validation limits and the refreshed all-language translation queue.

Thanks to xet7 !
2026-10-13 23:26:17 +03:00
Lauri Ojansivu 94f4cf2409 Correct Southern Sotho email instructions and limits
Clarify message content, labels, recipient maximum and SES sender direction. Remove mixed English in email help and add semantic and settings UI regressions.

Thanks to xet7 !
2026-10-13 23:25:37 +03:00
Lauri Ojansivu 74ace78ef7 Document Southern Sotho Scrum and recovery review
Record review through key 4069 and regression results in Upcoming and the translation audit.

Thanks to xet7 !
2026-10-13 23:23:21 +03:00
Lauri Ojansivu 6d74dfa66f Correct Southern Sotho Scrum estimate and progress labels
Distinguish estimates from size and translate the sprint progress term. Extend backlog UI and semantic coverage for Scrum and recovery boundaries.

Thanks to xet7 !
2026-10-13 23:22:51 +03:00
Lauri Ojansivu 81124c23c2 Document Southern Sotho Blockly semantic review
Record review through key 3729, six guarded corrections and validation limits in Upcoming and the translation audit.

Thanks to xet7 !
2026-10-13 23:20:38 +03:00
Lauri Ojansivu 0056f57d79 Clarify Southern Sotho Blockly angles and movement
Translate inverse tangent, use angular-degree terminology and repair vertical movement shortcuts. Extend semantic checks while preserving valid loop, list and text instructions.

Thanks to xet7 !
2026-10-13 23:20:04 +03:00
Lauri Ojansivu 6948989dac Document Southern Sotho analytics translation review
Record 54 guarded repairs, semantic and browser coverage, and remaining review limitations in Upcoming and the translation audit.

Thanks to xet7 !
2026-10-13 23:17:31 +03:00
Lauri Ojansivu 2bc93bdfde Restore Southern Sotho analytics explanations and sync labels
Replace abbreviated chart notes with methodology, missing-data rules and uncertainty caveats. Clarify time corrections, synchronization and credentials, with semantic regressions and seven browser cases. Statistical terminology remains subject to native-speaker review.

Thanks to xet7 !
2026-10-13 23:16:54 +03:00
Lauri Ojansivu e941257634 Document Southern Sotho WIP translation review
Record historical review through key 2999 and its regression coverage in Upcoming and the translation audit.

Thanks to xet7 !
2026-10-13 23:13:30 +03:00
Lauri Ojansivu 77f401cdee Correct Southern Sotho WIP groups and field summaries
Restore group selection, severity and API endpoint meanings. Add semantic regressions and a read-only WIP dialog browser case.

Thanks to xet7 !
2026-10-13 23:12:58 +03:00
Lauri Ojansivu 95626f68c1 Document Southern Sotho storage and migration review
Record seven guarded repairs, regression coverage and remaining translation work in Upcoming and the translation audit.

Thanks to xet7 !
2026-10-13 23:11:10 +03:00
Lauri Ojansivu a14536c444 Clarify Southern Sotho storage and migration translations
Distinguish archived records from saved data and orphaned cards from ownership. Translate remaining S3 prose and extend semantic and browser coverage.

Thanks to xet7 !
2026-10-13 23:10:39 +03:00
Lauri Ojansivu 1bbee3cab7 Document Southern Sotho anonymization translation repair
Record four corrections, confirmation coverage and ongoing all-language review.

Thanks to xet7 !
2026-10-13 23:06:21 +03:00
Lauri Ojansivu 77c1437808 Restore Southern Sotho account anonymization warning
Review source keys 2480–2599. Replace the unrelated export description with the account effects and irreversible-action warning. Clarify whole-instance backup scope and add semantic and browser checks.

Thanks to xet7 !
2026-10-13 23:05:42 +03:00
Lauri Ojansivu 801176fe2b Document Southern Sotho recurrence translation review
Record eight corrections, regression coverage and ongoing all-language work.

Thanks to xet7 !
2026-10-13 23:03:47 +03:00
Lauri Ojansivu e9479f0b07 Distinguish Southern Sotho reset and recurrence controls
Review source keys 2350–2479. Correct automatic checklist reset, card recurrence, last activity and migration severity labels. Add semantic and browser regression coverage.

Thanks to xet7 !
2026-10-13 23:03:08 +03:00
Lauri Ojansivu b8ebd2d842 Document Southern Sotho report translation review
Record five corrections, validation and continued all-language work.

Thanks to xet7 !
2026-10-13 23:00:54 +03:00
Lauri Ojansivu 26090b467a Clarify Southern Sotho report metrics and checklist templates
Review source keys 2135–2349. Correct recovery severity, template-copy labels, translation counts and time until due. Add semantic and browser regression coverage.

Thanks to xet7 !
2026-10-13 23:00:12 +03:00
Lauri Ojansivu cb31f8a042 Document Southern Sotho search translation review
Record three numeric clarification repairs and validation under Upcoming and the translation audit.

Thanks to xet7 !
2026-10-13 22:58:03 +03:00
Lauri Ojansivu d50db7b0bc Clarify Southern Sotho numeric search constraints
Review source keys 1990–2134. Express positive and negative integer signs explicitly and identify the maximum allowed background image size. Preserve query syntax and add semantic and browser checks.

Thanks to xet7 !
2026-10-13 22:57:23 +03:00
Lauri Ojansivu 53957ac74c Document Southern Sotho template translation review
Record five corrections, regression coverage and the ongoing all-language review.

Thanks to xet7 !
2026-10-13 22:55:13 +03:00
Lauri Ojansivu 19cc07f1ed Correct Southern Sotho template and table guidance
Review source keys 1845–1989. Clarify nonempty shared-template eligibility, personal attachments, swimlane grouping and positive integer limits. Add semantic and browser regression coverage.

Thanks to xet7 !
2026-10-13 22:54:35 +03:00
Lauri Ojansivu 390574a643 Document Southern Sotho configuration translation review
Record seven corrections, final validation and remaining all-language work.

Thanks to xet7 !
2026-10-13 22:52:24 +03:00
Lauri Ojansivu 08871f2aa6 Clarify Southern Sotho configuration and visibility guidance
Review source keys 1590–1844. Correct rule-field terminology, OAuth precedence and server storage, board visibility labels and header-toggle guidance; add semantic and browser checks.

Thanks to xet7 !
2026-10-13 22:51:23 +03:00
Lauri Ojansivu e0704813b3 Document short-string inventory repair and Southern Sotho fill
Record 20 translations, exact symbol exemptions, validation and the remaining candidate inventory.

Thanks to xet7 !
2026-10-13 22:47:15 +03:00
Lauri Ojansivu 15643364b0 Count short translation phrases and fill Southern Sotho words
Replace the blanket short-letter exemption with exact key/value symbols. Translate 20 Southern Sotho rule, Blockly and personal-filter strings. Add inventory and browser regression coverage while preserving existing Chinese values.

Thanks to xet7 !
2026-10-13 22:46:14 +03:00
Lauri Ojansivu 740a85b7f5 Document Southern Sotho automation translation review
Record 13 corrections, regression checks and the remaining short-string inventory gap.

Thanks to xet7 !
2026-10-13 22:43:29 +03:00
Lauri Ojansivu 9dc53b66f9 Correct Southern Sotho automation conditions and labels
Review source keys 1417–1589. Restore date-set triggers, auto-detection, rule states, assignee conditions and removal of labels. Translate short English fragments and add semantic and browser checks.

Thanks to xet7 !
2026-10-13 22:42:27 +03:00
Lauri Ojansivu d38dc5021e Document corrected translation inventory and storage fill
Record exact technical-name exemptions, 22 Southern Sotho translations, validation and the newly visible remaining backlog.

Thanks to xet7 !
2026-10-13 22:39:55 +03:00
Lauri Ojansivu d223f4fd53 Expose untranslated storage prose and fill Southern Sotho labels
Replace broad MongoDB, S3 and Azure Blob prefix exemptions with exact technical names. Translate 22 Southern Sotho storage strings, preserve product names and add positive/negative inventory tests plus S3 settings browser coverage.

Thanks to xet7 !
2026-10-13 22:39:08 +03:00
Lauri Ojansivu ba380bfc2b Document Southern Sotho status translations and inventory gap
Record six repairs and identify broad technical-prefix exemptions hiding untranslated prose across locale files.

Thanks to xet7 !
2026-10-13 22:36:19 +03:00
Lauri Ojansivu 12b380b74d Translate Southern Sotho database status and field ordering
Review source keys 1280–1416. Translate MongoDB status descriptions and correct card-field ordering terminology and movement labels. Add semantic and browser regression coverage.

Thanks to xet7 !
2026-10-13 22:35:13 +03:00
Lauri Ojansivu e587c5ac5c Document Southern Sotho link and email translation review
Record ten corrections and validation in Upcoming and the translation audit.

Thanks to xet7 !
2026-10-13 22:32:47 +03:00
Lauri Ojansivu 8cb58645cb Clarify Southern Sotho link and email settings
Review source keys 1130–1279. Correct automatic-link behavior, tracker URL templates, positive numeric limits and invitation/activity email template labels. Add semantic and browser regression coverage.

Thanks to xet7 !
2026-10-13 22:32:01 +03:00
Lauri Ojansivu ec3661b22f Document Southern Sotho visibility translation review
Record nine corrections, regression checks and the remaining semantic review under Upcoming and the translation audit.

Thanks to xet7 !
2026-10-13 22:29:29 +03:00
Lauri Ojansivu 9b7942b661 Correct Southern Sotho labels and visibility controls
Review source keys 930–1129. Repair label terminology, board linking, public/private description controls and the empty starred-items message. Add semantic and browser regression coverage.

Thanks to xet7 !
2026-10-13 22:28:27 +03:00
Lauri Ojansivu 27407f2b14 Document Southern Sotho stale import translation repairs
Record six corrections, browser coverage and the limits of exact-match English placeholder counts.

Thanks to xet7 !
2026-10-13 22:26:18 +03:00
Lauri Ojansivu 126aaebb91 Translate stale Southern Sotho import instructions
Review source keys 814–929. Translate older English Todoist and Planner instructions, restore Jira pagination and Excel sheet guidance, and correct Taskwarrior and Focalboard label terminology. Add semantic checks and six browser cases.

Thanks to xet7 !
2026-10-13 22:24:42 +03:00
Lauri Ojansivu 1ec26bd3a7 Document Southern Sotho filter and import translation corrections
Record reviewed source keys, five guarded repairs and regression checks under Upcoming and the translation audit.

Thanks to xet7 !
2026-10-13 22:22:25 +03:00
Lauri Ojansivu 44cc4c30ee Repair Southern Sotho filter syntax and import guidance
Review source keys 590–813. Restore literal escape examples and plain-bullet Markdown import guidance; distinguish unused disk space from price and data fields from agricultural fields. Add semantic and browser regression coverage.

Thanks to xet7 !
2026-10-13 22:21:35 +03:00
Lauri Ojansivu 6066341684 Document Southern Sotho archive and template translation review
Record the three repairs, regression coverage and remaining all-language work under Upcoming and the translation audit.

Thanks to xet7 !
2026-10-13 22:18:39 +03:00
Lauri Ojansivu e69d2c7311 Correct Southern Sotho archive help and template terminology
Review source keys 455–589, preserving valid translations. Repair the archive restore location, worker role and save-template action; add semantic and browser regression coverage.

Thanks to xet7 !
2026-10-13 22:17:53 +03:00
Lauri Ojansivu b8e3594acb Document Southern Sotho board and card control review
Record guarded meaning corrections and validation. Archive the oldest retained release by size and repartition October archives without changing released entries.

Thanks to xet7 !
2026-10-13 22:13:15 +03:00
Lauri Ojansivu 397ea42870 Repair Southern Sotho board and card control meanings
Review source keys 220 through 454 and fix nine misleading labels covering avatar limits, historical time, grouping, pulse, unread comments, due dates, account anonymization and card labels. Add guarded corrections and a read-only popup-title browser case.

Thanks to xet7 !
2026-10-13 22:12:22 +03:00
Lauri Ojansivu a0f42a284a Document Southern Sotho activity and control review
Record guarded meaning corrections, argument-order evidence, validation and remaining historical review.

Thanks to xet7 !
2026-10-13 22:09:25 +03:00
Lauri Ojansivu ef05274b3c Repair Southern Sotho activity and control meanings
Review the first 220 source strings and correct four semantic errors: reversed title arguments, automatic template application, the positive-integer height constraint and matching-card search scope. Record guarded repairs and extend the activity browser case.

Thanks to xet7 !
2026-10-13 22:08:47 +03:00
Lauri Ojansivu b22ca6b710 Document Southern Sotho full placeholder coverage
Record remaining recovery translations, full-inventory checks, pending fluency and UI validation, and the all-language backlog.

Thanks to xet7 !
2026-10-13 22:06:14 +03:00
Lauri Ojansivu 80df25497d Fill remaining Southern Sotho recovery translations
Translate the final 72 English placeholders and promote the full-inventory no-placeholder check. Preserve cancellation, discard and rollback constraints, already-applied changes and foreign-board protection. Register six existing recovery browser cases for Southern Sotho; fluency and runtime UI review remain pending.

Thanks to xet7 !
2026-10-13 22:05:37 +03:00
Lauri Ojansivu b6133bd072 Document Southern Sotho sync diagnostics translations
Record retention and estimate checks, browser registration and remaining translation work.

Thanks to xet7 !
2026-10-13 22:02:12 +03:00
Lauri Ojansivu b7fb228486 Translate Southern Sotho sync diagnostics and planning
Fill 45 source-report, retention, estimate-mapping, email-failure and planning placeholders. Preserve omitted-versus-null handling, access requirements and no-resume/no-undo warnings; extend semantic checks and the sync-planning browser case.

Thanks to xet7 !
2026-10-13 22:01:34 +03:00
Lauri Ojansivu 8bd53731c6 Document Southern Sotho Scrum import and sync translations
Record semantic checks, browser registration and remaining language work.

Thanks to xet7 !
2026-10-13 21:59:10 +03:00
Lauri Ojansivu 1128608ba5 Translate Southern Sotho Scrum import and sync guidance
Fill 50 placeholders, retaining no-duplicate import matching, unchanged unmatched cards, source read-only behavior, assigned-card review scope, local-copy preservation and retry reuse. Extend semantic checks and the existing Scrum import browser case.

Thanks to xet7 !
2026-10-13 21:58:32 +03:00
Lauri Ojansivu 296b41a478 Document Southern Sotho Scrum planning translations
Record report semantics, browser coverage registration and remaining language review.

Thanks to xet7 !
2026-10-13 21:56:12 +03:00
Lauri Ojansivu 59b761f444 Translate Southern Sotho Scrum planning and report guidance
Fill 75 planning, role, sprint, release and report placeholders. Preserve cancellation and rollover behavior, partial-report limits, unknown estimates and first-observation caveats. Extend semantic checks and the backlog paging browser case; Scrum terminology needs native review.

Thanks to xet7 !
2026-10-13 21:55:33 +03:00
Lauri Ojansivu 42776e5b1f Document Southern Sotho Blockly text translations
Record the Blockly placeholder gate, semantic checks and remaining language work.

Thanks to xet7 !
2026-10-13 21:52:59 +03:00
Lauri Ojansivu b6740c17a0 Translate Southern Sotho Blockly text and rule editor messages
Fill 115 text, shortcut, workspace and rule-editor placeholders. Preserve search keys, replacement scope, text indexing, concatenated whitespace and rule constraints. Add a full-inventory Blockly no-placeholder gate; descriptive programming terminology still needs native review.

Thanks to xet7 !
2026-10-13 21:52:26 +03:00
Lauri Ojansivu 66a67154d0 Document Southern Sotho Blockly procedure translations
Record mathematical and procedure checks, remaining review limits and translation inventory.

Thanks to xet7 !
2026-10-13 21:49:01 +03:00
Lauri Ojansivu b54fe95288 Translate Southern Sotho Blockly procedures and math controls
Fill 95 math, procedure, accessibility and shortcut placeholders. Verify random bounds, degrees rather than radians, return values, disabled definitions and screen-reader toggles. Mathematical and programming terminology remains lower confidence pending native review.

Thanks to xet7 !
2026-10-13 21:48:28 +03:00
Lauri Ojansivu d6e7a87532 Document Southern Sotho Blockly mathematics translations
Record comparison and numeric checks, native-review uncertainty and remaining language work.

Thanks to xet7 !
2026-10-13 21:45:55 +03:00
Lauri Ojansivu fb193ebb80 Translate Southern Sotho Blockly logic and mathematics
Fill 90 placeholders for list sorting, Boolean logic, arithmetic and statistics. Verify strict and inclusive comparisons, logical negation, copy behavior, constants and numeric bounds. Statistical and trigonometric terminology remains lower confidence pending native review.

Thanks to xet7 !
2026-10-13 21:45:13 +03:00
Lauri Ojansivu 37f1a98aae Document Southern Sotho Blockly list translations
Record semantic and placeholder validation, pending native and browser review, and remaining language inventory.

Thanks to xet7 !
2026-10-13 21:42:38 +03:00
Lauri Ojansivu c9fdb2f34c Translate Southern Sotho Blockly accessibility and list operations
Fill 110 placeholders for input labels, keyboard navigation and list operations. Check numbered tokens, dividend/divisor, get/remove distinctions, copy semantics, index direction and not-found behavior. Programming terminology remains lower confidence pending native review.

Thanks to xet7 !
2026-10-13 21:42:02 +03:00
Lauri Ojansivu 1774dcf0af Document Southern Sotho Blockly control translations
Record keyboard exemptions, token and semantic checks, native-review uncertainty and remaining translation backlog.

Thanks to xet7 !
2026-10-13 21:39:27 +03:00
Lauri Ojansivu 73e233a13b Translate Southern Sotho Blockly editor and control flow
Fill 75 editor, accessibility, colour and control-flow placeholders. Preserve 18 physical keyboard legends with exact-source exemptions and check numbered placeholders, loop conditions and bounds. Variable and programming terminology uses descriptive wording with lower confidence and needs native review.

Thanks to xet7 !
2026-10-13 21:38:51 +03:00
Lauri Ojansivu 84d85ecb21 Document Southern Sotho notification settings translations
Record semantic checks, browser registration and remaining translation work.

Thanks to xet7 !
2026-10-13 21:35:45 +03:00
Lauri Ojansivu 7c10778dac Translate Southern Sotho notification and automation settings
Fill 46 placeholders for delivery timing and grouping, workflow states, LDAP sync, card field visibility and subtask completion. Preserve token inventories and positive and negative permission semantics; extend notification and subtask browser cases.

Thanks to xet7 !
2026-10-13 21:35:07 +03:00
Lauri Ojansivu 04172e20f6 Document Southern Sotho import control translations
Record semantic validation, browser coverage registration and remaining translation inventory.

Thanks to xet7 !
2026-10-13 21:32:45 +03:00
Lauri Ojansivu d33245d26b Translate Southern Sotho import and webhook controls
Fill 55 placeholders for member mapping, CSV columns, batch import and export, Trello access, link rules and webhook payloads. Preserve substitution tokens, literal interchange markers and permission limits, and extend the existing CSV and member-mode browser cases.

Thanks to xet7 !
2026-10-13 21:32:06 +03:00
Lauri Ojansivu 6274990a6b Document Southern Sotho import instruction translations
Record format and exclusion checks, browser registration, protected Chinese values and remaining translation work.

Thanks to xet7 !
2026-10-13 21:29:31 +03:00
Lauri Ojansivu 42d5187bf6 Translate Southern Sotho import format instructions
Fill 22 instructions, retaining file formats, literal menu and column names, field mappings, first-board selection and excluded content. Extend semantic checks and register the existing import-help browser cases for Southern Sotho.

Thanks to xet7 !
2026-10-13 21:28:47 +03:00
Lauri Ojansivu 6ceef2e227 Document Southern Sotho card-link translations
Record validation and distinguish the pending-feature fills from the unchanged filtered backlog.

Thanks to xet7 !
2026-10-13 21:25:49 +03:00
Lauri Ojansivu c6a5484608 Translate Southern Sotho card attachment and field-link controls
Fill 34 English placeholders, preserving link directions, shared-field matching, unavailable-card privacy and edit permissions. Extend translation checks and both existing card interaction browser cases.

Thanks to xet7 !
2026-10-13 21:25:10 +03:00
Lauri Ojansivu eed374c9f9 Document completed Corsican source-ordered review
Record the final recovery and email label corrections, validation, protected Chinese values and remaining all-language backlog.

Thanks to xet7 !
2026-10-13 21:22:37 +03:00
Lauri Ojansivu e88d2ebc11 Complete Corsican catalog review with recovery label fixes
Replace five remaining Italian labels in recovery and email controls. Preserve existing translated recovery warnings and email permissions, and cover the refresh labels in the recovery browser case.

Thanks to xet7 !
2026-10-13 21:21:48 +03:00
Lauri Ojansivu 95692438c3 Document remaining Corsican Blockly review
Record reviewed operations, mathematical regression checks and pending Corsican Blockly browser execution in Upcoming and the translation audit.

Thanks to xet7 !
2026-10-13 21:16:13 +03:00
Lauri Ojansivu 6d22052060 Review Corsican Blockly math and text operations
Align remaining physical keyboard names and workspace search shortcuts, repair sorting wording, and verify mathematical distinctions. Add Corsican to existing Blockly interaction coverage.

Thanks to xet7 !
2026-10-13 21:15:32 +03:00
Lauri Ojansivu cde9fa96d9 Document Corsican Blockly review and placeholder checks
Record preserved Blockly wording, corrected keyboard legends and map example, passing checks and pending browser execution.

Thanks to xet7 !
2026-10-13 21:12:47 +03:00
Lauri Ojansivu 0b753e316d Review Corsican Blockly strings and preserve keyboard legends
Preserve recognizable physical key names and numbered placeholder inventories, including multiplicity. Correct the map floor-plan example and add Corsican to the existing map browser suite.

Thanks to xet7 !
2026-10-13 21:11:59 +03:00
Lauri Ojansivu aa6ec76dcf Document Corsican flow-analysis translation coverage
Record full methodology translations, lower-confidence statistical terminology, passing checks and pending browser execution in Upcoming and the translation audit.

Thanks to xet7 !
2026-10-13 21:09:15 +03:00
Lauri Ojansivu 0034964d9a Record single-file WeKan in Upcoming.
Thanks to xet7 !
2026-10-13 21:08:44 +03:00
Lauri Ojansivu 1b73b3a0b9 Restore full Corsican flow-analysis explanations
Replace abbreviated substitutes with forecast assumptions, missing-history behavior and date fallbacks. Repair WIP group and authentication wording and register five chart-help browser cases. Corsican statistical terminology is lower confidence and needs native review.

Thanks to xet7 !
2026-10-13 21:08:26 +03:00
Lauri Ojansivu 31e569a18e Single-file WeKan for every platform with a Node.js bundle.
One executable per platform: that platform's published bundle repackaged
as a Node.js single executable application whose ZIP Node 26.11 mounts as
a virtual file system (useVfs + vfsArchive). Only FerretDB is copied out,
once and verified, to wekan-files/bin; native addons load from the VFS.

Everything WeKan writes is in wekan-files beside the executable, which
WRITABLE_PATH points at: attachments, avatars, db, bin, backup, logs,
versions. WeKan now treats a WRITABLE_PATH named wekan-files as the files
root (models/lib/filesRoot.js, shared by every place that derived it).

Started in a terminal it shows a menu: start, stop, status, version info
as Admin Panel / Version shows it, update to the newest release or change
to any other (checksum verified, backed up first, the old executable
kept), backup to wekan-files/backup/YYYY-MM-DD_HH-MM-SS, run at boot
(systemd, launchd, Task Scheduler, cron, Termux:Boot), log, settings.
Every entry is also a command. start runs the start-wekan.sh loop:
recovery, requested restore, FerretDB, recovery page, WeKan, restart.

Meteor's main.js chdir()s into the bundle, which a VFS cannot be, so the
server role keeps a virtual working directory. Node 26.11.1 breaks
fs.writev/readv while any VFS is mounted; they are rebuilt on fs.write
and fs.read.

single-file.yml builds every target in releases/single-file/targets.json
from its verified bundle (Macs on a Mac, to re-sign), starts it where a
runner exists with browser tests on amd64, and attaches what started.
Release All and Release All Missing call it.

Thanks to xet7 !
2026-10-13 21:08:09 +03:00
Lauri Ojansivu 7073878380 Document Corsican account and import translation coverage
Record passing parser and translation regressions, conditional browser coverage and preserved Chinese values in Upcoming and the translation audit.

Thanks to xet7 !
2026-10-13 21:04:12 +03:00
Lauri Ojansivu 105eb49d8c Correct Corsican account, report and import wording
Preserve repair limitations, acknowledgement scope and scoped-import choices. Test the native card-number search keyword and register conditional report-help browser coverage.

Thanks to xet7 !
2026-10-13 21:03:31 +03:00
Lauri Ojansivu bbecce3c50 Document Corsican monitoring translation coverage
Record passing regressions, preserved Chinese values and the current filtered translation backlog in Upcoming and the translation audit.

Thanks to xet7 !
2026-10-13 20:59:51 +03:00
Lauri Ojansivu c1cc3c49cd Correct Corsican monitoring and job-control translations
Preserve migration thresholds, background behavior and literal storage names. Extend positive and negative translation regression coverage.

Thanks to xet7 !
2026-10-13 20:59:06 +03:00
Lauri Ojansivu e2320d1e4b Document Corsican board-migration translation coverage
Record repair conditions, passing regression checks and pending rendered validation in Upcoming and the translation audit.

Thanks to xet7 !
2026-10-13 20:56:06 +03:00
Lauri Ojansivu f4d12f7b0f Correct Corsican board-migration and progress translations
Preserve duplicate-empty-list conditions, shared-list conversion ordering and independent repair actions. Extend positive and negative translation regression coverage.

Thanks to xet7 !
2026-10-13 20:55:16 +03:00
Lauri Ojansivu 494e7f5da2 Document Corsican cloud translation coverage
Record restore and cloud configuration repairs, passing regressions and pending browser execution in Upcoming and the translation audit.

Thanks to xet7 !
2026-10-13 20:52:19 +03:00
Lauri Ojansivu 56dd1cbf08 Correct Corsican restore and cloud-storage translations
Preserve restore modes, credential alternatives, provider menu names and migration states. Extend language regressions and register cloud-help browser coverage.

Thanks to xet7 !
2026-10-13 20:51:35 +03:00
Lauri Ojansivu ce9e1e05df Document Corsican feature and backup translation coverage
Record corrected meanings, passing regressions and unavailable browser runtime in Upcoming and the translation audit.

Thanks to xet7 !
2026-10-13 20:48:58 +03:00
Lauri Ojansivu cf03f236b5 Correct Corsican feature and backup guidance
Restore automatic loading instructions and the actual irreversible account-anonymization confirmation. Preserve feature scopes, backup boundaries and literal markup, with regression checks and browser help coverage.

Thanks to xet7 !
2026-10-13 20:48:13 +03:00
Lauri Ojansivu e422bbad24 Document Corsican migration translation coverage
Record guarded scheduling and storage repairs, passing regressions and pending browser execution in Upcoming and the translation audit.

Thanks to xet7 !
2026-10-13 20:42:26 +03:00
Lauri Ojansivu f8c5783f41 Correct Corsican scheduling and migration translations
Preserve cloud product names, migration prerequisites and literal configuration commands. Extend regression checks and register migration-help browser coverage.

Thanks to xet7 !
2026-10-13 20:41:42 +03:00
Lauri Ojansivu 8e78cee2af Document Corsican recurrence and account translation checks
Record regression coverage, pending lockout browser execution and preservation of existing Chinese values in Upcoming and the translation audit.

Thanks to xet7 !
2026-10-13 20:39:11 +03:00
Lauri Ojansivu 9cb558de5f Correct Corsican recurrence and account settings translations
Distinguish card recurrence from reset and known-user password failures from nonexistent usernames. Repair upload and support messages and register lockout-settings browser coverage.

Thanks to xet7 !
2026-10-13 20:38:31 +03:00
Lauri Ojansivu be7f8d5916 Document Corsican checklist and storage translation coverage
Record guarded repairs, passing checks and pending default-storage browser execution in Upcoming and the translation audit.

Thanks to xet7 !
2026-10-13 20:35:44 +03:00
Lauri Ojansivu 588e39a31e Correct Corsican checklist and storage translations
Preserve item ordering, backend names, compaction warning and overtime meaning. Extend translation regressions and register default-storage browser coverage.

Thanks to xet7 !
2026-10-13 20:34:57 +03:00
Lauri Ojansivu c4bd61b171 Document Corsican report translation coverage
Record guarded repairs, regression checks and pending recovery-report browser execution in Upcoming and the translation audit.

Thanks to xet7 !
2026-10-13 20:32:33 +03:00
Lauri Ojansivu 5c96e8b59e Correct Corsican report and ticket translations
Preserve reporting scope, recovery operations and technical literals. Add language regression checks and register recovery-report browser coverage.

Thanks to xet7 !
2026-10-13 20:31:40 +03:00
Lauri Ojansivu 785599fdb9 Document Corsican search and control translation checks
Record parser regression coverage, pending browser execution and preservation of existing Chinese wording in Upcoming and the translation audit.

Thanks to xet7 !
2026-10-13 20:28:49 +03:00
Lauri Ojansivu 5fce837d4e Correct Corsican search help and dependency controls
Preserve query logic, proper names and map controls. Exercise translated ascending and descending sort predicates and register search-help browser coverage.

Thanks to xet7 !
2026-10-13 20:28:02 +03:00
Lauri Ojansivu 6cd487d4b7 Document Corsican search translation coverage
Record parser checks, pending browser execution and preservation of existing Chinese translations in Upcoming and the translation audit.

Thanks to xet7 !
2026-10-13 20:25:19 +03:00
Lauri Ojansivu d017d184dc Correct Corsican search keywords and instructions
Preserve executable search syntax and the direction of recent-date filters. Verify translated predicates with the search parser and register overdue-search browser coverage.

Thanks to xet7 !
2026-10-13 20:24:35 +03:00
Lauri Ojansivu a2a1a84a1a Record Corsican template and search translation audit in Upcoming 2026-10-13 20:18:41 +03:00
Lauri Ojansivu 930fd9fe2b Correct Corsican templates and search result wording
Preserve authorized-board and member-or-assignee scopes, result counts and parser abbreviations. Add guarded repairs and shared-template guidance coverage.
2026-10-13 20:17:51 +03:00
Lauri Ojansivu 6eead7c780 Record Corsican preferences and weekday translation audit in Upcoming 2026-10-13 20:14:39 +03:00
Lauri Ojansivu 0343ff48a0 Correct Corsican preferences, roles and weekday names
Preserve editor shortcuts, global-admin rights, linked-card deletion ordering and notification placeholders. Add guarded repairs and member-preference UI coverage.
2026-10-13 20:13:51 +03:00
Lauri Ojansivu 889da2fdb4 Record Corsican login and date translation audit in Upcoming 2026-10-13 20:10:28 +03:00
Lauri Ojansivu f2b4ea8984 Correct Corsican rule summaries, login settings and date activities
Preserve authentication prerequisite identifiers, old and new date values, deletion warnings and directional placement. Add guarded repairs and passwordless-settings UI coverage.
2026-10-13 20:09:22 +03:00
Lauri Ojansivu b098519df6 Record Corsican date-trigger translation audit in Upcoming 2026-10-13 20:06:22 +03:00
Lauri Ojansivu ed0c2c3ffc Correct Corsican date triggers and action fragments
Restore set-or-changed date conditions and assignee trigger wording. Preserve checklist negations and destination direction, with guarded repairs and date-trigger UI coverage.
2026-10-13 20:05:41 +03:00
Lauri Ojansivu fbf8b30069 Record Corsican workflow translation audit in Upcoming 2026-10-13 20:03:18 +03:00
Lauri Ojansivu 2c787355da Correct Corsican automation workflow and scheduling labels
Preserve import limitations and unmapped-node reporting, trigger/action distinctions, scheduling direction and completion negation. Extend guarded repairs and workflow UI coverage.
2026-10-13 20:02:30 +03:00
Lauri Ojansivu 24bd386ad9 Record Corsican hierarchy and rule translation audit in Upcoming 2026-10-13 19:59:31 +03:00
Lauri Ojansivu 6ad1329613 Correct Corsican card hierarchy, activity and rule labels
Preserve card-parent paths, movement direction, checklist counts and activity placeholders. Add guarded repairs and an empty-rules-page browser case.
2026-10-13 19:58:37 +03:00
Lauri Ojansivu d175d4e463 Record Corsican organization translation audit in Upcoming 2026-10-13 19:56:05 +03:00
Lauri Ojansivu 1eb90ef43f Correct Corsican invitation and organization administration wording
Preserve invitation tokens, tenant routing configuration, organization-admin permission boundaries and permanent board deletion warnings. Extend guarded repairs and custom-field UI coverage.
2026-10-13 19:55:25 +03:00
Lauri Ojansivu 5da4c323b5 Record Corsican upload and attachment translation audit in Upcoming 2026-10-13 19:52:12 +03:00
Lauri Ojansivu 84e25df892 Correct Corsican uploads and attachment limit wording
Preserve distinct upload and download limits, separate API buffering safety caps, WIP recovery choices and notification scope. Restore invariant unit labels and add attachment-limit UI coverage.
2026-10-13 19:51:25 +03:00
Lauri Ojansivu e756dc369c Record Corsican settings and shortcut translation audit in Upcoming 2026-10-13 19:48:25 +03:00
Lauri Ojansivu 6f2e4740ab Correct Corsican settings, shortcuts and membership wording
Preserve member-removal notifications, unsaved-description recovery and overtime semantics. Keep the Pomodoro technique name unchanged and add guarded repairs and shortcut UI coverage.
2026-10-13 19:47:33 +03:00
Lauri Ojansivu d8ab7af7e9 Record Corsican board and member translation audit in Upcoming 2026-10-13 19:43:21 +03:00
Lauri Ojansivu 01e3a9542e Correct Corsican board, archive and member controls
Distinguish personal overrides from cancellation and retain assigned-only access, notification conditions and deletion warnings. Add guarded repairs and an archive-tab browser case.
2026-10-13 19:42:31 +03:00
Lauri Ojansivu 87918c29fb Record Corsican Trello translation audit in Upcoming 2026-10-13 19:39:48 +03:00
Lauri Ojansivu b03a4afca6 Correct Corsican Trello import and cancellation wording
Preserve archive limits, optional workspace placement, member mapping and irreversible deletion scope. Add guarded repairs and Trello import UI coverage.
2026-10-13 19:39:05 +03:00
Lauri Ojansivu 9725a4d617 Record Corsican filter and importer audit in Upcoming 2026-10-13 19:35:43 +03:00
Lauri Ojansivu 8b9c88ba25 Correct Corsican filtering and import guidance
Translate stale English Todoist and Planner instructions against current source. Preserve importer mappings, field names and attachment limits, and add six importer browser cases.
2026-10-13 19:34:45 +03:00
Lauri Ojansivu b1e4363759 Record Corsican field and account translation audit in Upcoming 2026-10-13 19:31:21 +03:00
Lauri Ojansivu 4f89cd92e3 Correct Corsican field, account and export wording
Keep email placeholders and account actions distinct, preserve role restrictions and explain that enabling permanent deletion does not itself delete content. Add guarded repairs and custom-field UI coverage.
2026-10-13 19:30:26 +03:00
Lauri Ojansivu cc41a7af66 Record the BSD bundles in Upcoming.
Thanks to xet7 !
2026-10-13 19:26:05 +03:00
Lauri Ojansivu 58f8b67904 BSD: existing Node.js patch sets per BSD, and OpenBSD/NetBSD bundle builds ready.
docs/Platforms/FOSS/OS/BSD.md now lists the patches each BSD's packagers
keep for Node.js - FreeBSD ports www/node26, OpenBSD ports lang/node,
pkgsrc lang/nodejs (already 26.11.1), DragonFly DeltaPorts - and what WeKan
does with each, plus Bun, sharp/libvips, bcrypt and FerretDB on each BSD, and
how to install on OpenBSD (under /usr/local, wxallowed) and NetBSD (paxctl).

bsd.yml: the OpenBSD job mounts its workspace wxallowed before starting the
bundle there, and the NetBSD job compiles the native modules with pkgsrc's
GCC 14, as Node 26 needs C++20. Both build by themselves once wekan/node-
patches publishes node-openbsd-* and node-netbsd-x64.

Thanks to xet7 !
2026-10-13 19:25:44 +03:00
Lauri Ojansivu c337bb3896 Record Corsican navigation translation audit in Upcoming 2026-10-13 19:22:33 +03:00
Lauri Ojansivu dbaa663280 Correct Corsican navigation, color and role translations
Restore directional controls, comment and worker permission limits, and the deliberately empty comment placeholder. Preserve newer translations through guarded repairs and extend the color-rule browser case.

Specialized color shade paraphrases are lower confidence and remain open to native-speaker refinement.
2026-10-13 19:21:30 +03:00
Lauri Ojansivu 79a82b4a2f Document Corsican profile translation audit in Upcoming 2026-10-13 19:18:00 +03:00
Lauri Ojansivu 18278cce84 Correct Corsican profile and appearance wording
Preserve imported roles and permission limits, favorite toggles, automatic width and card inactivity thresholds. Add guarded repairs and regression coverage for profile and font controls.
2026-10-13 19:17:09 +03:00
Lauri Ojansivu 1cbd7df31f WeKan bundles for the BSDs: FreeBSD Node.js bundle, and the plan for each BSD.
docs/Platforms/FOSS/OS/BSD.md says where FreeBSD, OpenBSD, NetBSD and
DragonFly BSD stand - the Bun bundles FreeBSD already has, which FerretDB
and Node.js binaries exist for each, and the plan, easiest first.

.github/workflows/bsd.yml, called by Release All, builds WeKan's Node.js
bundle for a BSD: it takes the release's amd64 bundle, verifies it and adds
that BSD's Node.js (wekan/node-patches) and FerretDB, then inside a VM of
that BSD compiles the native modules, runs the same bundle steps as every
other platform, and starts the bundle with its own start-wekan.sh, which
must answer HTTP 200 before the zip is attached. FreeBSD x64 builds now,
from node-freebsd-x64 and ferretdb-freebsd-amd64. FreeBSD arm64, OpenBSD
amd64/arm64 and NetBSD amd64 are skipped, not failed, until node-patches
publishes their Node.js. The Node.js resolver, the native addon ELF check
and the platform packages step know the BSD names.

Thanks to xet7 !
2026-10-13 19:16:40 +03:00
Lauri Ojansivu a566e2cd0d Record Corsican card and Planning Poker review 2026-10-13 19:10:02 +03:00
Lauri Ojansivu a63e83ec5e Correct Corsican card controls and Planning Poker scores
Repair 47 mixed-language or misleading values, including literal Planning Poker scores and destructive-action warnings. Regional prose remains provisional pending fluent review.
2026-10-13 19:09:21 +03:00
Lauri Ojansivu fb6391d0e3 Record Corsican board view and timeline review 2026-10-13 19:06:24 +03:00
Lauri Ojansivu b61c0df17f Correct Corsican board view and timeline labels
Repair 26 values, retain literal Gantt naming and restore Gregorian ISO week and timeline meanings. Regional phrasing remains provisional pending fluent review.
2026-10-13 19:05:41 +03:00
Lauri Ojansivu 7b7269bd14 Record Corsican board display wording review 2026-10-13 19:03:11 +03:00
Lauri Ojansivu b1c1c68040 Correct Corsican board display and attachment warnings
Repair 27 mixed-language values, retaining offline data-loss warnings and archive versus deletion semantics. Regional phrasing remains provisional pending fluent review.
2026-10-13 19:02:15 +03:00
Lauri Ojansivu 49fb7219d3 Record Corsican workspace and card control review 2026-10-13 18:59:25 +03:00
Lauri Ojansivu 9869b00d25 Correct Corsican workspace and card controls
Repair 37 mixed-language values while preserving selection limits, Home-board retention and numeric validation. Regional phrasing remains provisional pending fluent review.
2026-10-13 18:58:35 +03:00
Lauri Ojansivu 30f7c19d3f Record Corsican repair and recovery wording review 2026-10-13 18:55:25 +03:00
Lauri Ojansivu d7c43eeb4b Correct Corsican repair and recovery guidance
Repair 20 mixed-language values, including account anonymization and recovery scope. Retain literal identifiers and reversibility limits. Regional phrasing remains provisional pending fluent review.
2026-10-13 18:54:34 +03:00
Lauri Ojansivu 7937697628 Record bounces from the other sending services in Upcoming.
Thanks to xet7 !
2026-10-13 18:54:28 +03:00
Lauri Ojansivu 57e05c4304 Email bounces from Mailgun, SendGrid, Postmark, Brevo, Mailjet, SparkPost, Mandrill and Resend.
Besides Amazon SES, WeKan now takes the bounce and spam-complaint
webhooks of the other common sending services, at
/api/inbound-email/bounces/<service>, each proven the way that service
signs: Mailgun's HMAC-SHA256, SendGrid's ECDSA signature over the raw body,
Mandrill's HMAC-SHA1 over the URL and fields, Resend's Svix signature; and
for Postmark, Brevo, Mailjet and SparkPost, which do not sign, a password
in the webhook address (Basic auth or ?secret=). Each service's events
become WeKan's hard, soft, automatic-reply and complaint categories, so the
same Admin Panel rules act on them. Bounces -> Other sending services
turns each on with its key or password, written only when typed and never
shown back; a forged or unsigned request is refused and logged in Problems
-> Security. These endpoints, like the SES one, work without WITH_API.

Mail systems with no webhook keep working as before: their bounce emails
reach WeKan by IMAP and are recognised as delivery status notifications.

Thanks to xet7 !
2026-10-13 18:54:06 +03:00
Lauri Ojansivu a7da9876df Record Corsican storage and migration wording review 2026-10-13 18:52:32 +03:00
Lauri Ojansivu 78d684e364 Correct Corsican storage migration and avatar guidance
Repair 25 mixed-language values while preserving migration preconditions, retained data and irreversible deletion warnings. Regional phrasing remains provisional pending fluent review.
2026-10-13 18:51:50 +03:00
Lauri Ojansivu 3a9725ca42 Record Corsican administrative wording review 2026-10-13 18:49:26 +03:00
Lauri Ojansivu 1e10d606b5 Correct Corsican administrative and storage wording
Repair 25 mixed-language values while preserving roles, shared-template eligibility, search conjunction and storage scope. Regional phrasing remains provisional pending fluent review.
2026-10-13 18:48:41 +03:00
Lauri Ojansivu 1b7dd9eb8c Record Corsican settings and confirmation review 2026-10-13 18:45:43 +03:00
Lauri Ojansivu f4e5519c87 Correct Corsican settings rules and deletion confirmations
Repair 25 mixed-language values with exact token and semantic checks. Regional phrasing remains provisional pending fluent review.
2026-10-13 18:44:53 +03:00
Lauri Ojansivu ff160f28b7 Record Corsican filter and access wording corrections 2026-10-13 18:42:23 +03:00
Lauri Ojansivu e7a4eb0ffd Correct Corsican filter help and access guidance
Repair 20 mixed-language values, preserve exact filter examples and distinguish public visibility, editing rights and Sandstorm access revocation. Regional phrasing remains provisional pending fluent review.
2026-10-13 18:41:41 +03:00
Lauri Ojansivu dfa02cd8c9 Record Corsican board wording and importer corrections 2026-10-13 18:38:58 +03:00
Lauri Ojansivu d07120761b Correct Corsican board warnings and import guidance
Repair 33 mixed-language values and restore Jira pagination and Excel mapping instructions. Regional wording remains provisional pending fluent review.
2026-10-13 18:38:10 +03:00
Lauri Ojansivu b7bbbdbdf7 Record Corsican permission wording corrections 2026-10-13 18:35:12 +03:00
Lauri Ojansivu 724fd0730d Correct Corsican membership permissions and board controls
Repair 37 mixed-language labels and descriptions, including account anonymization and logged-in user access. Preserve role restrictions and archive semantics. Regional phrasing remains provisional pending fluent review.
2026-10-13 18:34:25 +03:00
Lauri Ojansivu 2453838acd Record Corsican notification wording review 2026-10-13 18:31:32 +03:00
Lauri Ojansivu f7e256b997 Correct Corsican activity notification wording
Replace 39 mixed-language values with guarded repairs, preserving direction and placeholder case. Regional phrasing remains provisional pending fluent review.
2026-10-13 18:30:32 +03:00
Lauri Ojansivu 15eb5b7910 Record email bounces, Problems -> Email and the runtime tests in Upcoming.
Thanks to xet7 !
2026-10-13 18:27:44 +03:00
Lauri Ojansivu 79ac301675 Email bounces with Amazon SES, Problems -> Email, and runtime tests for sending and receiving.
Email that comes back is handled in WeKan instead of landing in the
administrator's own inbox. Admin Panel -> E-mail -> Bounces: hard bounces
(a mistyped invited address), soft bounces, automatic replies and spam
complaints, each with actions done after a count - list it, stop emailing
the address, tell who invited it, delete the invitation, ignore - as
listmonk and Discourse do it, and the list of addresses that bounced, which
an admin clears to email them again. Amazon SES Bounce and Complaint
notifications and mail SES receives arrive through SNS at
/api/inbound-email/ses, signature-checked against Amazon's certificate and
accepted only from the topics the Admin Panel lists. Delivery status
notifications and out-of-office replies arriving by IMAP, a provider
webhook or a Sandstorm grain are bounces too, never cards.

Admin Panel -> Problems -> Email shows what went wrong sending or
receiving: a send the mail server refused or could not reach, an IMAP
mailbox that cannot be read (never with its password), an unreadable
email, an email for a board that no longer takes email. Attempts stay in
Problems -> Security. The inbound email endpoints no longer need the whole
REST API turned on.

Runtime tests run WeKan's own transports against local SMTP and IMAP
servers over TCP, a real Sandstorm Maildir, the webhook over HTTP and
signed SNS messages; UI tests drive the Email and Chat views, the
email-to-card settings and the Bounces page. They found and this fixes:
every whole-message parse failed in the server bundle (nodemailer's
addressparser resolved to its ESM build), the Chat view's drag and drop
never started (dragscroll took the mouse) and dropped a card into the
target's previous list, and the Chat panes did not follow a change to a
right-to-left language.

Thanks to xet7 !

Fixes #794,
Fixes #3948.
2026-10-13 18:27:25 +03:00
Lauri Ojansivu b5250c6fae Record Corsican activity wording corrections 2026-10-13 18:26:04 +03:00
Lauri Ojansivu e3c22d9e92 Correct mixed Italian wording in Corsican activity messages
Record 22 exact before/after repairs, preserve placeholder inventories and distinguish checked and unchecked actions. Regional phrasing remains provisional pending fluent review.
2026-10-13 18:25:21 +03:00
Lauri Ojansivu cc416e5ab0 Record Corsican importer translation inventory 2026-10-13 18:22:09 +03:00
Lauri Ojansivu 9198e84b39 Complete Corsican import instructions and filtered fill inventory
Preserve format literals, import mappings and unsupported-data warnings. Regional phrasing remains provisional pending fluent review; older mixed-language values remain to be corrected.
2026-10-13 18:21:31 +03:00
Lauri Ojansivu 4c7660d2de Record Corsican import export and controls translation progress 2026-10-13 18:18:23 +03:00
Lauri Ojansivu 342e479ed9 Translate Corsican import export and board settings controls
Fill 65 English values with token and semantic checks and extend seven browser workflows. Regional phrasing remains provisional pending fluent review.
2026-10-13 18:17:29 +03:00
Lauri Ojansivu 28e9e9d61f Record Corsican notification translation progress 2026-10-13 18:14:17 +03:00
Lauri Ojansivu 877a3a7e81 Translate Corsican notification delivery and webhook settings
Preserve inherited defaults, identity omission, grouping and quiet-hour waiting. Regional phrasing remains provisional pending fluent review.
2026-10-13 18:13:00 +03:00
Lauri Ojansivu 26a6176270 Record Corsican recovery translation progress 2026-10-13 18:10:58 +03:00
Lauri Ojansivu ef77f5a3f3 Translate Corsican synchronization and import recovery messages
Preserve partial-data retention, irreversible discard warnings and recovery refusal reasons. Regional phrasing remains provisional pending fluent review.
2026-10-13 18:10:10 +03:00
Lauri Ojansivu d7c606c29e Record Corsican card-link translation progress 2026-10-13 18:07:10 +03:00
Lauri Ojansivu cdb2405e93 Translate Corsican card attachments and linked fields
Fill 30 English placeholders and cover link direction, edit permission, paused links and inaccessible cards. Regional phrasing remains provisional pending fluent review.
2026-10-13 18:06:13 +03:00
Lauri Ojansivu eb1b966225 Record completed Swahili importer translation inventory 2026-10-13 18:02:57 +03:00
Lauri Ojansivu d963a78bf4 Complete Swahili import instructions and current fill inventory 2026-10-13 18:01:44 +03:00
Lauri Ojansivu 6d88440dd3 Record Swahili export and settings translation progress 2026-10-13 17:54:25 +03:00
Lauri Ojansivu e02fd383b5 Translate Swahili export guidance and settings controls 2026-10-13 17:53:39 +03:00
Lauri Ojansivu 7d8939271e Record Swahili control translation progress 2026-10-13 17:50:28 +03:00
Lauri Ojansivu 387b88c959 Translate Swahili notification, mapping and Scrum controls 2026-10-13 17:49:44 +03:00
Lauri Ojansivu 9a6f00e927 Record Swahili card-link and recovery translation progress 2026-10-13 17:46:13 +03:00
Lauri Ojansivu c4f4e465f2 Translate Swahili card links and recovery decisions 2026-10-13 17:45:29 +03:00
Lauri Ojansivu 3a459697f2 Record Maltese exempt-prose audit and repairs 2026-10-13 17:40:28 +03:00
Lauri Ojansivu 6d5ae79420 Translate Maltese storage prose and short labels excluded from fill reports 2026-10-13 17:39:31 +03:00
Lauri Ojansivu a26aa63978 Record Maltese filtered inventory completion and remaining review 2026-10-13 17:32:50 +03:00
Lauri Ojansivu f26aaac01e Complete Maltese import-format instructions and current fill inventory 2026-10-13 17:31:44 +03:00
Lauri Ojansivu 54dfa466df Record Maltese settings translation progress 2026-10-13 17:27:50 +03:00
Lauri Ojansivu ce113b5a50 Translate Maltese settings and workflow rules 2026-10-13 17:26:56 +03:00
Lauri Ojansivu 208f43bab3 Record Maltese export translation progress 2026-10-13 17:24:13 +03:00
Lauri Ojansivu bbe5b139d2 Translate Maltese batch import and export guidance 2026-10-13 17:23:27 +03:00
Lauri Ojansivu 2892fdcd3d Record Maltese Scrum translation progress 2026-10-13 17:20:42 +03:00
Lauri Ojansivu c87fcb37ec Translate Maltese Scrum import and subtask controls 2026-10-13 17:20:02 +03:00
Lauri Ojansivu 95c1ad0304 Record Maltese import mapping translation progress 2026-10-13 17:17:21 +03:00
Lauri Ojansivu f0c5c03cb1 Translate Maltese import people and column mapping controls 2026-10-13 17:16:25 +03:00
Lauri Ojansivu 5a4cc61ba5 Record Maltese notification translation progress 2026-10-13 17:14:04 +03:00
Lauri Ojansivu 1e6b6340df Translate Maltese notification delivery and planning synchronization 2026-10-13 17:13:18 +03:00
Lauri Ojansivu d1e226e867 Record Maltese recovery translation progress 2026-10-13 17:09:09 +03:00
Lauri Ojansivu 57a10d1ac9 Translate Maltese recovery decisions and data-retention warnings 2026-10-13 17:08:15 +03:00
Lauri Ojansivu af8342d859 Record Maltese card-link translation progress 2026-10-13 17:04:13 +03:00
Lauri Ojansivu 60528034c1 Translate Maltese attached-card and linked-field messages
Fill 30 English placeholders while retaining existing translations. Verify matching fields, propagation direction, edit permission, unavailable cards and self-link refusal. Extend the existing attachment and field-link browser flows to Maltese.
2026-10-13 17:03:12 +03:00
Lauri Ojansivu 50f00961c5 Record Khmer and regional time-history translation review 2026-10-13 16:58:01 +03:00
Lauri Ojansivu 1a7fa67d94 Restore time-history meanings in nine more catalog paths
Complete Khmer and its aliases, Burmese, Amharic, Yiddish, Bhojpuri, Maithili and Konkani notes. Preserve the Khmer symlink, map its browser tag, and check Yiddish RTL layout.

Technical prose in these languages is provisional pending fluent review. Preserve existing Chinese values and unrelated new feature translations.
2026-10-13 16:56:59 +03:00
Lauri Ojansivu 767d469750 Record the Email and Chat board views in Upcoming.
Thanks to xet7 !
2026-10-13 16:56:52 +03:00
Lauri Ojansivu ddb33d21d0 Board views Email and Chat.
Email shows the board as a Gmail-like mailbox in the board's own theme:
the Inbox is the list email to card puts new cards in, Sent and All mail
follow the cards' email, the board's lists are folders and its labels are
Gmail labels, coloured on every message. The messages are the shared
table page, newest first, paged on the server; a message opens as the
card popup, its comments the conversation. Compose and Reply send From
the member's own board address, so the answer returns to this board, and
by In-Reply-To to the same card as a comment.

Chat shows the board as a Slack / Rocket.Chat workspace in the current
theme: a tree of Workspaces, boards, swimlanes, lists and cards as
channels; the selected card in the middle; its comments as chat on the
other side. Tree | card | chat left to right, mirrored right to left.
Each pane folds, the two borders drag (and move with the arrow keys),
and widths and folds are saved on the member's profile. Swimlanes, lists,
cards, workspaces and boards reorder by drag and drop through the same
paths the board and All Boards use.

The new strings are translated in every locale.

Thanks to xet7 !
2026-10-13 16:56:29 +03:00
Lauri Ojansivu 56eefe8a76 Record eight further time-history translation repairs 2026-10-13 16:51:09 +03:00
Lauri Ojansivu 021d016f5d Restore time-history explanations in eight more catalogs
Use Hiragana for the Japanese Hiragana catalog and its registered browser language tag. Extend direction checks to Sorani, Pashto, Sindhi and Uyghur. Record exact before/after repairs and all four source distinctions.

Kurmanji, Sorani, Pashto, Sindhi, Uyghur, Hausa and Somali technical phrasing is provisional pending fluent review. Existing Chinese values remain unchanged.
2026-10-13 16:50:05 +03:00
Lauri Ojansivu 5e90f13750 Record email to card in Upcoming.
Thanks to xet7 !
2026-10-13 16:47:45 +03:00
Lauri Ojansivu 988b177e59 Email to card: board addresses, IMAP mailboxes, provider webhooks and Sandstorm grain email.
An email to a member's personal board address, board+<token>@<domain>,
becomes a card on that board, as Trello's email-to-board does (#794,
#3948). The subject is the title; the body (quoted replies stripped, HTML
as text) is the description, with a From line; attachments are attached
(no SVG, as for uploads). The author is the address's member, never the
From address, and they must still have write access when the email
arrives. A reply whose In-Reply-To/References matches a card's thread
becomes a comment on it.

Notification Settings, at three levels: the Admin Panel turns it on and
holds the domain, the provider webhook secret and the IMAP mailboxes with
their passwords (never published, never sent back to a browser); a board
admin turns the board on and picks the list and swimlane; board and
member choose which parts of an email are used. The member sees and
replaces their own address.

It arrives by POST /api/inbound-email (Mailgun, SendGrid, Postmark, JSON,
forms, multipart with attachments, raw MIME), by IMAP polling with
imapflow and mailparser under a database lease so one replica polls, or
on Sandstorm through the grain's own address: the bridge's Maildir is
read, a member's board token in the subject routes it, and WeKan's
outgoing email goes through HackSessionContext.send, since a grain has no
network. The email and hack-session capnp schemas are generated with the
other Sandstorm bindings.

Unknown addresses, wrong webhook secrets and over-limit addresses are
logged under authn.inbound-email. Rate limit per address, size limits,
Message-ID deduplication. The new strings are translated in every locale.

Thanks to xet7 !
2026-10-13 16:47:16 +03:00
Lauri Ojansivu f08367b5cf Record Chinese email translation review and refreshed backlog 2026-10-13 16:43:57 +03:00
Lauri Ojansivu f84a8e3a83 Verify Chinese email-to-card translation meanings and controls 2026-10-13 16:42:41 +03:00
Lauri Ojansivu 706c9f34c1 Record regional time-history translation review 2026-10-13 16:27:26 +03:00
Lauri Ojansivu c6aa8490d0 Complete time-history explanations in 23 more catalogs
Regional technical prose in Faroese, Frisian, Scottish Gaelic, Aragonese, Corsican, Sardinian and Sicilian remains provisional pending fluent review.
2026-10-13 16:25:29 +03:00
Lauri Ojansivu f1505aebd0 Record further time-history language repairs 2026-10-13 16:14:29 +03:00
Lauri Ojansivu 44d8210a69 Restore time-history meaning in 48 more catalogs 2026-10-13 16:12:52 +03:00
Lauri Ojansivu 2c9bfdb500 Record time-history language repairs and refreshed translation backlog 2026-10-13 16:04:38 +03:00
Lauri Ojansivu 3fe68ad266 Restore complete time-history explanations in 54 catalogs 2026-10-13 16:02:05 +03:00
Lauri Ojansivu 16926454f7 Record Uzbek terminology repairs and archive the oldest retained release 2026-10-13 15:54:56 +03:00
Lauri Ojansivu 6c10d035c2 Correct Uzbek assigned-person labels and time-history explanation 2026-10-13 15:53:32 +03:00
Lauri Ojansivu 7b312dd563 Record Latin Uzbek fills and remaining language review 2026-10-13 15:49:13 +03:00
Lauri Ojansivu 6825f00ac7 Fill Latin Uzbek feature and recovery translations 2026-10-13 15:47:42 +03:00
Lauri Ojansivu 9be8b3b087 Record the planned email, Sandstorm, Android and single-file work in TODO Later.
Thanks to xet7 !
2026-10-13 15:39:42 +03:00
Lauri Ojansivu 8c7973e0a9 Record Azerbaijani feature fills and remaining translation review 2026-10-13 15:37:33 +03:00
Lauri Ojansivu 224b4cac07 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-10-13 15:37:23 +03:00
Lauri Ojansivu 9d8a48c4d8 Fill Azerbaijani feature, recovery and accessibility strings 2026-10-13 15:35:40 +03:00
Lauri Ojansivu 31932e08ab Record Azerbaijani terminology repairs in Upcoming 2026-10-13 15:24:21 +03:00
Lauri Ojansivu 30a92983f5 Correct Azerbaijani board and assigned-person terminology
Repair 38 values in each of the az, az-AZ and az-LA catalogs. Use lövhə for a Kanban board rather than şura (council), distinguish the assigned person, and correct the card-to-board command. Preserve query literals, invitation tokens and visibility emphasis; keep activity predicates compatible with the actor prefix.

Record all 114 exact corrections. Terminology and correction-ledger checks pass, including newer-translation preservation. Three browser cases register but have not been executed.

Thanks to xet7 !
2026-10-13 15:19:53 +03:00
Lauri Ojansivu 1376c99ca2 Record Malayalam fills and Scrum recovery review in Upcoming 2026-10-13 15:06:52 +03:00
Lauri Ojansivu 0ae7f04e55 Fill Malayalam feature strings and correct rule linking-verb insertion
Translate 223 current placeholders and thirteen exempt labels, preserving existing non-English values. Cover Scrum import matching, first-sync preservation, conflicts and recovery consequences. Omit the redundant linking verb before already inflected Malayalam predicates and share the Kannada/Malayalam browser check.

Six focused suites and exact source-token checks pass. Nine Malayalam browser cases register; execution and broader native wording review remain open.

Thanks to xet7 !
2026-10-13 15:05:33 +03:00
Lauri Ojansivu 9c8d74c955 Record Urdu fills and RTL verification limits in Upcoming 2026-10-13 14:51:09 +03:00
Lauri Ojansivu 762bb1df85 Fill Urdu feature strings and extend RTL browser coverage
Translate 193 current English placeholders while preserving existing non-English text. Verify permissions, import exclusions, recovery consequences, technical identifiers and mixed-direction interpolation. Register Urdu feature flows and five existing RTL layout cases.

Longer technical wording remains provisional pending speaker review; browser execution is still outstanding.

Thanks to xet7 !
2026-10-13 14:50:09 +03:00
Lauri Ojansivu 5d839ea1a5 Record Kannada fills and rule grammar review in Upcoming 2026-10-13 14:42:43 +03:00
Lauri Ojansivu 10c4cc3bfa Fill Kannada feature strings and omit redundant rule linking verb
Translate 193 current placeholders and nine labels hidden by fill exemptions. Preserve existing non-English values. Omit the separate linking verb before already inflected Kannada action predicates; retain the helper behavior for other languages. Use numerator/denominator notation for fixed-order progress counts.

Check permissions, import exclusions, recovery consequences, source literals, locale boundaries and runtime interpolation. Register six Kannada browser cases. Longer technical wording and full rule clause grammar remain open to speaker review.

Thanks to xet7 !
2026-10-13 14:41:25 +03:00
Lauri Ojansivu 02cfa64e83 Record Thai translation progress and remaining audit scope 2026-10-13 14:27:38 +03:00
Lauri Ojansivu 0a7eba3e4e Fill Thai feature strings and untranslated byte unit
Translate 193 current feature placeholders and the exempt Bytes label, preserving all existing non-English values. Check field directions, permissions, import exclusions, recovery consequences, source literals and runtime interpolation. Register five Thai browser cases for the affected feature flows.

Longer technical wording remains provisional pending speaker review.

Thanks to xet7 !
2026-10-13 14:26:00 +03:00
Lauri Ojansivu 7e4a409db2 Add Trello export, Sandstorm Powerbox and release build fixes to ChangeLog.
Thanks to xet7 !
2026-10-13 14:21:32 +03:00
Lauri Ojansivu ccff887734 Find release assets reliably before wrapping or building them.
Both v12.39 Mac apps were skipped although their bundles were attached:
`gh release view ... | grep -q` let grep exit at the first match while gh
was still writing, gh got SIGPIPE, pipefail failed the pipeline, and the
job said the bundle had not been published. The Windows EXE, Docker image
and snap-launchpad checks had the same shape. Each now reads the asset
list whole before matching it.

Thanks to xet7 !
2026-10-13 14:20:57 +03:00
Lauri Ojansivu 121bf673a5 Keep waiting for a Launchpad snap build through network blips.
The armhf and s390x builds succeeded, then the runner's connection to the
Launchpad API dropped or a poll hung, and snapcraft ended each attempt as
"Network is unreachable" or "Timed out waiting for build". A failed
build-state poll is now retried for up to 30 minutes; other errors and the
workflow's own deadline are unchanged.

Thanks to xet7 !
2026-10-13 14:20:57 +03:00
Lauri Ojansivu 13c9bd27d5 Build Sharp natively for the FreeBSD Bun bundles.
Both FreeBSD jobs stopped at "native check: sharp": Sharp's only FreeBSD
prebuilt is WebAssembly with Worker threads, and under Bun on FreeBSD the
first image operation never returned, while the same build works under
Bun on Linux. Sharp is now compiled against the system libvips there, as
on Android, and the FreeBSD VM installs vips.

Thanks to xet7 !
2026-10-13 14:20:57 +03:00
Lauri Ojansivu 88d00e02f1 Export boards to Trello, keep swimlanes as labels, and reach Trello from Sandstorm.
Export to Trello: Board Settings -> Export -> Trello creates the board in
Trello through its API with the saved key and token: labels, lists, cards,
checklists, comments, attachment files, and archived lists and cards. It
runs as a server job from the board export, with the same access check.

Swimlanes: Trello has none, so each swimlane is exported as a label named
"Swimlane: <title>". The Swimlanes by Blue Cat Power-Up groups Trello cards
into rows by label, and every Trello import turns those labels back into
swimlanes.

Sandstorm: a grain has no network of its own. Allow access to Trello opens
the Sandstorm Powerbox for https://api.trello.com/1, like the Sandstorm
WordPress app reaches wordpress.org; the access token is kept per user on
the server, and Trello import and export go through the bridge proxy.

Thanks to xet7 !
2026-10-13 14:20:56 +03:00
Lauri Ojansivu 5e82284450 Record Tamil translation fills and calendar review in Upcoming 2026-10-13 14:19:34 +03:00
Lauri Ojansivu 5de70e0399 Fill Tamil feature strings and correct calendar spelling
Translate 193 current feature placeholders and the exempt Bytes label. Correct four existing calendar labels using the long vowel already used elsewhere in the catalog, with exact ledger entries. Cover permission direction, import exclusions, recovery consequences, Trello markers and runtime interpolation; register Tamil feature and calendar browser cases.

Longer technical wording remains provisional pending speaker review.

Thanks to xet7 !
2026-10-13 14:18:20 +03:00
Lauri Ojansivu e9b0de11df Record Nepali and new Trello translation progress in Upcoming 2026-10-13 14:06:44 +03:00
Lauri Ojansivu 66bd077c36 Translate Marathi Trello API labels and cover new permission wording
Fill eleven new Marathi strings without changing existing values. Extend Bengali checks for the concurrently supplied translations and register Bengali and Marathi export-dialog browser cases.

Thanks to xet7 !
2026-10-13 14:04:50 +03:00
Lauri Ojansivu 9384c350fa Fill Nepali feature strings and missing Chinese Trello labels
Translate 193 Nepali placeholders and 11 new Trello/Sandstorm strings in each of nine Chinese catalog paths. Preserve all existing non-English values, including every earlier zh translation. Check import exclusions, permissions, recovery consequences, literal swimlane markers, exact tokens and runtime interpolation. Register browser coverage without contacting Trello.

Thanks to xet7 !
2026-10-13 14:01:22 +03:00
Lauri Ojansivu db282dd372 Record eight-language storage translation progress
Update Upcoming, audit evidence and the supplemental review count while keeping broader language review and browser execution open.

Thanks to xet7 !
2026-10-13 13:43:49 +03:00
Lauri Ojansivu 49b1e07a48 Translate storage settings in eight more languages
Fill 200 English MongoDB and S3 values in Nepali, Kannada, Malayalam, Javanese, Kazakh, Kyrgyz, Tajik and Somali, using the existing catalogs for terminology. Preserve existing translations and verified zh wording.

Thirteen locale and supplemental-audit suites pass, including token, state and runtime checks. Fourteen storage-settings browser cases register; a running rebuilt app is still needed. Complete software compounds remain provisional pending speaker review.

Thanks to xet7 !
2026-10-13 13:42:27 +03:00
Lauri Ojansivu 7004356e1f Record four-language storage translation review in Upcoming
Update the supplemental candidate count and document preserved wording, Tagalog corrections, validation and remaining review.

Thanks to xet7 !
2026-10-13 13:34:59 +03:00
Lauri Ojansivu 3e10d0aa1d Translate four storage settings catalogs and repair Tagalog instructions
Fill 100 English MongoDB and S3 values in Bengali, Icelandic, Tagalog and Albanian. Repair 15 mixed English/Tagalog clauses, retaining literal AWS console labels and the one-time secret display warning. Preserve all other translations, including existing zh wording.

Locale, correction-ledger, supplemental audit and runtime checks pass. Six storage-settings browser cases register; execution needs a running rebuilt app. Complete software wording remains provisional pending speaker review.

Thanks to xet7 !
2026-10-13 13:33:29 +03:00
Lauri Ojansivu f502638236 Record Marathi fills and the supplemental translation audit
Update Upcoming and TODO Later with the filtered backlog and the additional review queue. Keep native-language review and browser execution explicitly open.

Thanks to xet7 !
2026-10-13 13:22:50 +03:00
Lauri Ojansivu 9ec7e44a1d Merge remote-tracking branch 'origin/main' 2026-10-13 13:19:53 +03:00
Lauri Ojansivu f263dd0b8d Translate Marathi feature and settings prose and expose hidden review candidates
Fill 212 current placeholders and 55 English labels hidden by technical-name and short-word exemptions. Keep existing Marathi translations and preserve source tokens, product names and physical keyboard legends.

Add a read-only audit for product-prefixed prose, generic labels and keyboard names. Candidates require language-specific review; existing human-verified zh wording must not be overwritten.

Marathi, supplemental audit, shared-label and Scrum recovery checks pass. Register Marathi linked-field, notification, recovery and storage-settings browser coverage. Browser execution and broader native language review remain unfinished.

Thanks to xet7 !
2026-10-13 13:19:35 +03:00
Lauri Ojansivu 22ebf0a9b4 Record Bengali translation completion and remaining audit work
Update Upcoming and TODO Later after the current Bengali fill list became empty. Preserve the distinction between filled strings and completed language-quality review.

Thanks to xet7 !
2026-10-13 13:00:22 +03:00
Lauri Ojansivu 012ee1eb0c Translate remaining Bengali feature strings
Fill 182 English placeholders for linked fields, import formats and mapping, notifications, webhooks, LDAP, Wrike and recovery. Preserve existing Bengali wording. The full current fill inventory is empty; older wording and complex technical phrases still need native review.

Verify exact placeholders, permissions, import exclusions, recovery consequences and i18next interpolation. Three focused suites pass. Four Bengali browser cases register; execution needs a running application.

Thanks to xet7 !
2026-10-13 12:59:20 +03:00
Lauri Ojansivu 12e44340b2 Record shared-label translations in Upcoming and refresh audit counts
Track remaining fills and language-quality review separately. Document preservation of human-verified Chinese wording and the unavailable browser app.

Thanks to xet7 !
2026-10-13 12:49:46 +03:00
Lauri Ojansivu 67d1ad324f Translate shared UI labels and repair seeded terminology
Fill 3,129 English values and repair 167 seeded values across 130 locale paths. Preserve existing translations and human-verified zh catalogs. Retire 15 completed pending keys.

Record provisional terminology in Luganda, Tswana, Tongan, Tsonga, Aymara, Quechua, Wolaytta, Venda, Acehnese, Walloon, Wolof and Tamazight as low confidence pending native review.

All 307 translation/Blockly suites and 21 human-preference checks pass. Source tokens match across all 246 catalogs. Expanded browser coverage registers 125 tests; execution requires a running app.

Thanks to xet7 !
2026-10-13 12:47:44 +03:00
github-actions[bot] d2134fe15d Bump versions for v12.39 2026-10-13 09:16:21 +00:00
Lauri Ojansivu f968e5b466 Prepare v12.39 release 2026-10-13 12:15:14 +03:00
Lauri Ojansivu 6aa2ad2c91 Add the SAML qualifier fix to ChangeLog.
Thanks to xet7 !
2026-10-13 12:12:19 +03:00
Lauri Ojansivu 9949bca0b9 Log in a SAML binding that lacks the optional qualifiers.
A binding stored with issuer, nameID and nameIDFormat but no nameQualifier
or spNameQualifier keys was not found by identity, because a query for ''
does not match a missing field. The user was then found by username and
refused as account-already-bound-to-saml. An empty assertion qualifier now
also matches a missing one, and the next login writes them as empty
strings. A non-empty qualifier and a missing issuer still fail closed.

Thanks to xet7 !
2026-10-13 12:12:12 +03:00
Lauri Ojansivu 8cfb5ce71e Add the Bun build and release check fixes to ChangeLog.
Thanks to xet7 !
2026-10-13 01:07:14 +03:00
Lauri Ojansivu 68c9f2bcd0 Record the Bun and UCS internals in the build script parity guard.
tests/buildScriptParity.test.cjs failed because the Bun target-build
scripts, which bun.yml runs on each target runner, and the UCS migration's
docker-exec.mjs are not build.sh menu entries. They are listed as internals
with the reason, like the other release-workflow helpers.

Thanks to xet7 !
2026-10-13 01:06:51 +03:00
Lauri Ojansivu fb12ea42c8 Fix the Android and FreeBSD Bun builds and the false windows-arm64 error.
Android: Termux's user does not own the bind-mounted bundle, which the
workflow makes writable for everyone, so chmod failed with EPERM before an
allowed write. Files are now chmodded only when not writable already.

FreeBSD: the final Bun native check was killed at its deadline with no
output, its buffered console lost with it. It now writes each step
unbuffered and exits explicitly, and the supervisor tells a hung operation
(failure, naming the step) from every check passing and Bun then hanging on
exit (oven-sh/bun#44595), which passes with a warning.

windows-arm64 built and attached its bundle but showed an error: the
expected no-prebuilt-addon probe printed Bun's raw "error:" line, which
GitHub turns into an annotation. It is now reported as one plain line.

Thanks to xet7 !
2026-10-13 01:06:51 +03:00
Lauri Ojansivu 163ebf260a Check the bundle version in an rspack bundle, and word a cancelled snap job right.
The Docker job's bundle-version check looked for "name":"wekan" in app.js,
but rspack keeps only the version field the server reads, so the check was
skipped with a warning on every release. It now also reads the one
v-prefixed version-only module, and still only warns when that is absent
or ambiguous. A cancelled snap-launchpad job no longer claims it ran out of
time: the v12.38 jobs were cancelled at 1h49m of a 360-minute cap.

Thanks to xet7 !
2026-10-13 01:06:51 +03:00
Lauri Ojansivu fbd0267226 Add the image scan fixes to ChangeLog.
Thanks to xet7 !
2026-10-13 00:27:06 +03:00
Lauri Ojansivu 169670abd5 Raise eight npm packages inside Meteor's packages in the bundle.
The Artifact Hub scan of the v12.38 image found advisories in browserslist,
baseline-browser-mapping, colord, svgo, source-map-js (minifier-css),
compression and qs (webapp, oauth) and lodash.template (simple-schema), all
fixed in the same major. bump-bundle-npm-deps.mjs now raises them, and
gives a bumped package its own copy of a dependency whose sibling is a
different version, so compression 1.8.2 runs with the bytes, negotiator and
safe-buffer it declares. nodemailer and postcss-selector-parser fixes need a
new major and are recorded as not fixable.

Thanks to xet7 !
2026-10-13 00:26:22 +03:00
Lauri Ojansivu be2a4053be Remove docker-cli from the Docker image.
Debian's docker-cli carries Go 1.24.4, whose standard library advisories
were most of the Quay and Artifact Hub scans of the image, and only the UCS
migration used it, for docker exec. The migration's mongo and mongoexport
wrappers now run releases/ucs-migration/docker-exec.mjs, which speaks the
Docker Engine API over the mounted socket with the same stdout, stderr and
exit status, and the image installs no Docker CLI.

Thanks to xet7 !
2026-10-13 00:26:22 +03:00
github-actions[bot] bd5a374182 Bump versions for v12.38 2026-10-12 19:33:00 +00:00
Lauri Ojansivu cfc34c125a Prepare v12.38 release 2026-10-12 22:31:56 +03:00
Lauri Ojansivu 460e10f848 Merge remote-tracking branch 'origin/main' 2026-10-12 22:31:31 +03:00
Lauri Ojansivu 0930e9b314 Add CodeQL alert 569, SAML linking, favorites and list drag fixes to ChangeLog.
Thanks to GitHub CodeQL, Raymond-Voogt, mimZD and xet7 !
2026-10-12 22:22:04 +03:00
Lauri Ojansivu a9c75efe60 Explain why SAML could not link an existing account, and find it by email.
Every refusal to link a SAML login to an existing (e.g. LDAP) account threw
the same "SAML identity does not match this account" and logged nothing.
Each condition now has its own reason in the server log, and a missing email
or email_verified=true attribute in the assertion is named on the sign-in
page. The refusal still fails closed. With merging on, a login name that is
the assertion's email also finds the account by a verified email, instead of
trying to create a new user. email_verified, the matching rules and the
unused SAML_ATTRIBUTES are documented.

Thanks to xet7 !
2026-10-12 22:21:30 +03:00
Lauri Ojansivu 715eb2b826 Match HTML end tags that carry attributes (CodeQL alert 569).
Browsers end a script at </script foo="bar"> too, so an end-tag pattern
written as <\/script\s*> or <\/script> misses it. The reported test, the
server input sanitizer's style pattern and one more test now use
<\/tag\b[^>]*>, and tests/securityAlerts20261012.test.cjs fails on the
loose shape for script, noscript, style, iframe, textarea and title
anywhere in the tree.

Thanks to GitHub CodeQL and xet7 !
2026-10-12 22:21:30 +03:00
Lauri Ojansivu bcf2305448 Show private starred boards in the header Favorites popup.
The starredPublicBoards publication only sent public and instance boards,
so a user whose favorites are all private boards saw an empty popup. It now
sends every starred board the user may see - public/instance, or an active
membership, organization, team or domain share - through the shared
visibility builder, so a revoked share or stale star still never brings a
private board back.

Thanks to Raymond-Voogt and xet7 !

Fixes #6764.
2026-10-12 22:21:30 +03:00
Lauri Ojansivu 205110e3a5 Show clearly where a dragged list will land.
The list drop placeholder had no --list-width, so none of the list width
rules matched it and it kept flex-shrink: 1 with no content; in a swimlane
wider than the window flexbox shrank it to a thin shadow. It now never
shrinks, every list sortable pins its min-width to the dragged list's width,
and it is drawn as a tinted, dashed drop area.

Thanks to mimZD and xet7 !

Fixes #6763.
2026-10-12 22:21:30 +03:00
Lauri Ojansivu 05adb51be2 Clarify Upcoming build fixes and regression coverage.
Thanks to xet7 !
2026-10-12 22:18:28 +03:00
Lauri Ojansivu 6c04af3bcf Document target build and release packaging fixes.
Record regression coverage and platform validation limits for the v12.37 build-log fixes.

Thanks to xet7 !
2026-10-12 22:06:48 +03:00
Lauri Ojansivu 929e3051d4 Fix Bun target builds and release asset checks.
Preserve Android build metadata through Termux's environment reset and build Sharp against libvips. Bound native addon probes and final checks, read complete AppImage asset lists, and follow authenticated chart pagination. Keep expected negative smoke diagnostics out of CI error annotations.

Thanks to xet7 !
2026-10-12 22:05:36 +03:00
github-actions[bot] 7dd26551ed Bump versions for v12.37 2026-10-12 10:57:07 +00:00
Lauri Ojansivu bd54d98963 Prepare v12.37 release 2026-10-12 13:56:03 +03:00
Lauri Ojansivu e7db57832c Merge remote-tracking branch 'origin/main' 2026-10-12 13:55:39 +03:00
Lauri Ojansivu dd04694e52 Document the Bun Rspack dependency fix and successful ESM build 2026-10-12 13:50:16 +03:00
Lauri Ojansivu 3d20a9e4a6 Install Rspack build dependencies explicitly in Bun CI
Include development and optional dependencies in the locked npm install and verify the Meteor Rspack config before compilation. Exercise both omitted and included dependency installs with an offline fixture.
2026-10-12 13:47:41 +03:00
github-actions[bot] 299087d912 Bump versions for v12.36 2026-10-12 10:05:20 +00:00
Lauri Ojansivu 59d823ea66 Prepare v12.36 release 2026-10-12 13:04:17 +03:00
Lauri Ojansivu ac59abff4c Clarify Upcoming Bun fix summary and regression coverage 2026-10-12 13:03:33 +03:00
Lauri Ojansivu bb9b3f0af3 Document the Bun installer version fix 2026-10-12 12:53:00 +03:00
Lauri Ojansivu f24448ebd1 Pass a semantic version to the Bun setup action 2026-10-12 12:52:43 +03:00
Lauri Ojansivu 5985117db6 Updated Sandstorm WeKan install docs. 2026-10-12 12:42:12 +03:00
github-actions[bot] 9f0644fa02 Bump versions for v12.35 2026-10-12 09:06:29 +00:00
Lauri Ojansivu 02fd967566 Prepare v12.35 release 2026-10-12 12:05:23 +03:00
Lauri Ojansivu 06ee936e84 Align Upcoming changelog with release note conventions 2026-10-12 12:03:51 +03:00
Lauri Ojansivu 7d1d7b45f5 Document ZIP format for Bun release downloads 2026-10-12 12:02:29 +03:00
Lauri Ojansivu 05d45e219f Package Bun release bundles as ZIP archives
Preserve executable modes and dereference dependency links. Update release uploads, expected assets and recovery checks for ZIP filenames.
2026-10-12 12:02:03 +03:00
Lauri Ojansivu 870259d285 Document the Bun release workflow and its validation 2026-10-12 11:55:19 +03:00
Lauri Ojansivu 1d1db1c234 Build and attach Bun release bundles for every supported OS and CPU
Build one Meteor ESM application, rebuild native addons on each target, and attach verified archives and checksums. Use native hosted runners, Alpine and Termux containers, and FreeBSD VMs. Cover release planning, failed-build gates, native modules, HTTP/DDP, and Chromium signup and board creation.

Thanks to dupontbertrand and xet7 !
2026-10-12 11:53:26 +03:00
Lauri Ojansivu 907b85711d Merge remote-tracking branch 'origin/main' 2026-10-12 11:50:10 +03:00
Lauri Ojansivu cd0e7d19b4 Document the CodeQL 565 to 568 fixes and the Windows node-gyp fix.
Thanks to GitHub CodeQL and xet7 !
2026-10-12 11:43:57 +03:00
Lauri Ojansivu b34d36e8f5 Windows win32 and ARM64 bundles: find node-gyp from inside each package.
The jobs run compile-native-addons.mjs from programs/server with the bundle
as `../..`, and node-gyp runs with each package as its working directory.
The relative node-gyp path was resolved from there, so v12.34's build-win32
and build-win-arm64 failed with "Cannot find module ...accounts-password/
programs/server/node_modules/node-gyp/bin/node-gyp.js". The bundle path is
made absolute first. The new test calls it the way the jobs do, with a fake
node-gyp that resolves paths the way the child process does, which the old
fake did not.

Thanks to xet7 !
2026-10-12 11:43:02 +03:00
Lauri Ojansivu 900267451c Fix CodeQL alerts 565 to 568, and the same shapes elsewhere.
Alert 565 (ReDoS): the package-specifier pattern in
releases/prune-unreachable-npm.mjs allowed `/` both between and inside path
segments, so a quoted `-/-/-/...` could be split in exponentially many ways;
es-module-lexer's minified lexer alone took over two seconds. A `/` now only
separates segments. Over the 23,812 files in node_modules it finds every
package it found before, and also `require('querystring-es3/')`, the
trailing-slash form the old pattern missed.

Alert 566 (bad tag filter): the Sandstorm readiness test matched <script>
case-sensitively. It and three negative checks that said "no <script" in
sanitized output now match any case; one of those also collected tag names
with a case-sensitive pattern that an <SCRIPT> tag would have escaped.

Alerts 567 and 568 (code sanitization): a test wrote dictionary paths into
generated JavaScript with JSON.stringify. It copies the dictionaries beside
its entry so the source is constant, and four more test probes take module
paths as arguments instead. The i18n loader the test drives, which writes an
asset name into generated server code, refuses a name outside a locale
tag's characters and escapes the literal.

tests/securityAlerts20261012.test.cjs fails on any case-sensitive script,
noscript or iframe pattern and on JSON.stringify spliced into an import,
require or asset path anywhere in the tree.

Thanks to GitHub CodeQL and xet7 !
2026-10-12 11:43:02 +03:00
github-actions[bot] 833e52b5da Bump versions for v12.34 2026-10-12 08:21:13 +00:00
Lauri Ojansivu adbd19b1ba Prepare v12.34 release 2026-10-12 11:20:11 +03:00
Lauri Ojansivu a1f09eb58b Document the LDAPS company-CA fix, and repair Upcoming's structure.
Merging #6761 left a stray "This release fixes the following bugs:" above
the security header and the Sandstorm entries ahead of the security fixes.
Security comes first again, the Sandstorm entries follow the bug fixes under
one topic line with their contributor credited, two one-word wrapped lines
are reflowed, and In short is back to four sentences.

Thanks to fvb-sb, mnutt and xet7 !
2026-10-12 11:14:55 +03:00
Lauri Ojansivu 8ab7d46714 Trust the operating system's certificate authorities for TLS.
Since v12.15 the LDAP server's certificate is verified by default, and
Node.js verified it only against the Mozilla list it is built with. A
company root CA installed on the server with update-ca-certificates was
unknown to WeKan, so after upgrading from v12.14 the snap refused LDAPS
logins with UNABLE_TO_VERIFY_LEAF_SIGNATURE.

server/main.js now adds the operating system's CAs to Node's own before any
application code runs: tls.getCACertificates('system'), and the distribution
CA bundle files Go's crypto/x509 reads. The bundle file matters in the snap:
on Debian and Ubuntu snapd keeps the host's /etc/ssl, but the per-CA links
update-ca-certificates makes point into /usr/local/share/ca-certificates,
which the snap sees from its base. Verification is not relaxed - a CA the
system does not have is still refused - and --use-bundled-ca keeps Node's
list only. It covers LDAPS, mail, webhooks and OAuth2 in every launcher.

A refused LDAP certificate now says what to do, in the log and in Admin
Panel's Test connection: install the CA on the server, or set LDAP_CA_CERT.
No hint suggests turning verification off.

tests/systemCaCertificates6762.test.cjs runs a TLS server with a certificate
from a test private CA (its key is not kept) and checks the reported error
without the CA, verification with it in the system bundle, refusal without
it, --use-bundled-ca, and the hints. Two LDAP guards follow the extended
error handling; prune-unreachable-npm.mjs leaves the regex guard's reviewed
list, as it no longer builds a RegExp.

Thanks to fvb-sb and xet7 !

Fixes #6762.
2026-10-12 11:14:34 +03:00
Lauri Ojansivu 8a947993f3 Merge pull request #6761 from mnutt/sandstorm-startup-timing
Sandstorm startup timing improvements
2026-10-12 10:52:52 +03:00
Lauri Ojansivu e3ed0ef801 Merge branch 'main' into sandstorm-startup-timing 2026-10-12 10:52:05 +03:00
Michael Nutt 9647c07554 Fix startup test assumptions and release documentation checks 2026-10-12 00:07:58 -04:00
Michael Nutt 11db7365a5 Record Sandstorm startup and packaging improvements
Link the six implementation commits from Upcoming and keep released
changelog sections unchanged.
2026-10-11 23:30:56 -04:00
Michael Nutt bab0c13988 Reduce server bundle file count for Sandstorm extraction
Strengthen dependency analysis and expose retention reasons in an audit
report. Prune verified browser inputs, unused client polyfills and server
type declarations. Include safety regressions and packaged-runtime results.
2026-10-11 23:30:56 -04:00
Michael Nutt 2b8a4a234f Defer SAML and LDAP dependencies until first use
Load SAML, LDAP and transliteration implementations when their operations
need them. Preserve registration and authentication checks, with tests
and documentation accompanying the change.
2026-10-11 23:30:56 -04:00
Michael Nutt 8c26d75e31 Load server source maps lazily from sidecar files
Extract inline maps during bundle trimming so startup avoids scanning
large base64 comments. Preserve mapped stacks and include packaging
regressions and measured production results.
2026-10-11 23:30:56 -04:00
Michael Nutt df1c240edd Load server translations from private assets on demand
Keep English embedded and load other server dictionaries on first use,
without changing client loading or translation contents. Include loader
regressions, build integration checks and profiling documentation.
2026-10-11 23:30:56 -04:00
Michael Nutt 3df146c164 Defer ZIP and Excel initialization until first use
Load archive libraries only when an import, export or restore needs them.
Keep lightweight timing enabled and make detailed Meteor profiling opt-in.
Include first-use regression checks and production measurements.
2026-10-11 23:30:56 -04:00
Michael Nutt 22d7d23118 Measure Sandstorm startup and signal database and HTTP readiness
Add launcher timings and a local production-path profiling runner. Replace
the fixed database delay with listener readiness and notify the waiting
page through a held request. Include tests and profiling documentation.
2026-10-11 23:30:56 -04:00
Lauri Ojansivu 17a551ac80 Document the mongo-tools-only change.
Thanks to xet7 !
2026-10-12 02:25:46 +03:00
Lauri Ojansivu 41bc82713e Use only the one-program mongo-tools-<platform> from mongo-tools-patches.
fetch-mongo-tools.sh, the local build-release-bundle.sh and the
check-arch-binaries.sh preflight fell back to the eight separate
<tool>-<platform> binaries when a release had no mongo-tools-<platform>.
wekan/mongo-tools-patches now publishes only the one program, so every
bundle, image and snap gets that or no tools, and says so. The Windows and
macOS jobs also remove the inherited amd64 mongo-tools with the launchers.

tests/fetchMongoTools.test.cjs serves a release that has only the per-tool
binaries and checks that none is installed or even requested, and fails on
any script or workflow that builds a per-tool download address.

Thanks to xet7 !
2026-10-12 02:25:32 +03:00
Lauri Ojansivu 147c83d953 Document the CodeQL 562, 563 and 564 fixes.
Thanks to GitHub CodeQL and xet7 !
2026-10-12 02:18:53 +03:00
Lauri Ojansivu 4caa929cfe Fix CodeQL alerts 562, 563 and 564, and the same shapes elsewhere.
Alert 564 (regex injection): releases/platform-native-packages.mjs compiled
its command-line platform and arch into a RegExp. The canvas package names
are now compared as strings. The upload naming of attachments and avatars,
the attachment rename popup and tools/mirror-menu.mjs built a RegExp from a
file extension or a name the same way; they use literal suffix and prefix
checks now. tests/noUnescapedDynamicRegExp.test.cjs parses every source file
and fails on a RegExp built from a value that is not escaped, unless the
site is listed with the reason its values are constants.

Alert 563 (incomplete escaping): name.replace('/', '%2F') replaced only the
first slash. registryPackagePath() replaces every one and refuses anything
that is not an npm package name. Seven tests escaped a regex the same
first-only way; 'g++'.replace('+', '\\+') let "- g+" pass a guard. They use
the complete escapeRegExp, and tests/noIncompleteRegExpEscaping.test.cjs
fails on any string-pattern replace that performs an escape.

Alert 562 (ReDoS): the CasaOS reverse-domain id pattern in
tests/platformTemplates.test.cjs let '.' be matched by both groups, so
'0.0.0...!' backtracked exponentially. Each separator now belongs to one
group, and the test runs CodeQL's attack string against it.

Thanks to GitHub CodeQL and xet7 !
2026-10-12 02:18:33 +03:00
Lauri Ojansivu 534d411aca Document the UCS avatar fix and the GitHub CLI 2.48 fix.
Thanks to xet7 !
2026-10-12 01:30:56 +03:00
Lauri Ojansivu 0516826f75 Release scripts work with a GitHub CLI older than 2.48.
They read every page of a GitHub list with gh api --paginate --slurp,
and --slurp exists only from gh 2.48 on: an older gh stopped the release
with "unknown flag: --slurp". --paginate alone prints the pages back to
back, and gh_pages() now reads them one after another. A truncated page
is still an error, never a shorter list.

The tests feed the scripts pages the way gh prints them, check that no
gh call asks for --slurp, and (negative) that a truncated page fails.

Thanks to xet7 !
2026-10-12 01:30:07 +03:00
Lauri Ojansivu 7514d6e175 UCS: avatars show again after an upgrade, and lazily loaded code is fetched from the page's own address.
On Univention WeKan is served at /wekan. profile.avatarUrl holds
/cdn/storage/avatars/<id> - what Change Avatar and the server's own
migrations store - and it was rendered as stored, so the browser asked
the UCS web server for a path outside WeKan, got 404, and every avatar
was an empty circle, while Change Avatar, which builds its own link,
showed the picture. models/lib/avatarDisplayUrl.js treats the stored
value as the avatar's name: whatever host or old path prefix stood
before /cdn/storage/avatars/ or /cfs/files/avatars/ is replaced with the
path WeKan is served at now, relative to the page's origin. The avatar,
the People page and the report tables draw through it; the default
avatar gets the prefix too. External pictures and data: URLs are left
alone.

The same session, opened through 127.0.0.1:8443/wekan, fetched lazily
loaded code from ROOT_URL's host ucs-2275.wekan.intranet, which the
browser could not resolve (NS_ERROR_UNKNOWN_HOST, "Cross-Origin Request
Blocked"). Meteor's dynamic-import setting useLocationOrigin is now on
by default, so that code comes from the page's own origin and ROOT_URL
path; an explicit value in METEOR_SETTINGS still wins. Links keep using
ROOT_URL as before.

tests/avatarDisplayUrl.test.cjs covers every stored shape, (negative)
leaves external, data: and query-string URLs alone, checks that no
template puts a stored avatar URL in src without avatarSrc, and pins the
dynamic-import default and its override. The Playwright spec
avatar-sub-path.e2e.js checks the drawn src and the setting in a browser;
it was not run here.

Thanks to xet7 !
2026-10-12 01:30:07 +03:00
Lauri Ojansivu 1eb9eb43ed Merge remote-tracking branch 'origin/main'
# Conflicts:
#	Dockerfile
2026-10-12 01:13:26 +03:00
Lauri Ojansivu d1a2c39cf2 Document the stripped Node.js and the one-program MongoDB Database Tools.
Thanks to xet7 !
2026-10-12 01:09:05 +03:00
Lauri Ojansivu 44af4f3e72 Bundles carry the MongoDB Database Tools as one 15 MB program instead of eight.
The eight tools were eight Go programs, each with its own copy of the Go
runtime, the MongoDB driver and the tools' shared packages: about 105 MB
in every bundle, image and snap. wekan/mongo-tools-patches now also
publishes mongo-tools-<platform>, all eight in one program of about 15 MB.

releases/fetch-mongo-tools.sh puts it in a bundle with a launcher per
tool - a three-line shell script on Linux and macOS, a .cmd on Windows -
so mongodump keeps working from $SNAP/bin, the bundle and PATH. Launchers
rather than symlinks, because a symlink has to survive zip -r, Docker
COPY, snapcraft and every user's unzip. Until the newest
mongo-tools-patches release has mongo-tools-<platform>, it fetches the
eight per-tool assets as before. It first removes the tools a repacked
bundle inherited from amd64, so a platform without tools ships none
rather than the wrong CPU's.

Every bundle job in release-all.yml, repack-bundle-for-arch.sh and
build-release-bundle.sh use it; the snaps move mongo-tools into bin/
with its launchers; the telemetry audit scans mongo-tools as a tool; the
Windows single EXE unpacks the .cmd launchers; the preflight and the URL
check accept the one program.

tests/fetchMongoTools.test.cjs serves the release from a local HTTP
server and checks the launchers run the program with their arguments,
the Windows .cmd files, the per-tool fallback, and (negative) that
inherited amd64 tools are removed, that a checksum mismatch is refused,
and that no workflow or snap recipe downloads the tools itself.
releaseBinaryFreshness now finds the newest-release URL in the helper;
workflowFailureDiagnostics required an ::error:: beside each new exit.

Thanks to xet7 !
2026-10-12 01:08:48 +03:00
Lauri Ojansivu 8a78749061 boardPermission guard: record the commented-out Sandstorm migration it now finds.
ed5502edfa brought server/sandstorm.js back, and with it a commented-out
v0.8 migration that sets permission: 'public'. It is a write, not a read
gate, so it cannot leave instance boards out; it is listed as reviewed.

Thanks to xet7 !
2026-10-12 01:08:38 +03:00
Lauri Ojansivu 3eb549058c Strip the symbol table and debug data from every bundled node.
nodejs.org's node executables carry their full symbol table and DWARF debug
data, which only a debugger reads. Node 26.11.1 for linux-x64 is 150.0 MB
and 116.7 MB without them; linux-arm64 goes from 150.2 to 114.6 MB. Every
bundle, Docker image, snap, AppImage, Flatpak and the Sandstorm package
ships one.

releases/strip-node.sh runs after the download has been verified against its
source's checksum, so the file verified and recorded is the original:
- ELF: eu-strip (elfutils). GNU strip only reads the host's own CPU and
  fails on an arm64 node on an x64 runner. Inside an emulated target
  container the native strip is the fallback.
- Mach-O: strip -x keeps the 39006 exported symbols addons bind to and
  drops the 901035 local ones. It then re-signs ad hoc, because stripping
  breaks the signature and an arm64 Mac runs no unsigned binary.
- PE: nothing. node.exe has no COFF symbols, and its debug data is a
  separate .pdb that is not shipped.
It refuses a result that lost .dynsym, the table native addons resolve
N-API against. The stripped linux-x64 node was run with bcrypt, argon2,
sharp and @napi-rs/canvas, and each hashed, verified or rendered correctly.

embed-verified-node.sh calls it for amd64, arm64, Windows, macOS, Sandstorm
and the local build, and install-node-for-arch.sh for the emulated
architectures. build-amd64, build-arm64 and the extra-arch containers
install elfutils.

tests/stripNode.test.cjs strips real gcc -g binaries (it checks that
.symtab and .debug_* are gone, .dynsym stays, and the binary still runs),
refuses an ELF without .dynsym and a non-binary, leaves a .exe untouched,
and pins every call site after its checksum check.

tests/buildScriptParity.test.cjs lists every releases/ script: it named
compile-native-addons.mjs, added by 34a33f7b14 and not registered, and now
strip-node.sh. Both are registered as release-workflow helpers, and the
suite passes again. Its earlier failure was reported as pre-existing; it was
not.

Thanks to xet7 !
2026-10-12 00:40:41 +03:00
Lauri Ojansivu c9dad1ecbe Document the release build speed-ups and the 32-bit ARM sharp fix
Adds the Docker, Launchpad and history, and native-module entries to Upcoming, and notes in the released v12.33 sharp entry that its 32-bit ARM zips can crash on ARMv6 and some ARMv7 boards, and where the fix is.

Thanks to xet7 !
2026-10-12 00:11:16 +03:00
Lauri Ojansivu 628bb95619 32-bit ARM: use the WebAssembly sharp, not a native build that can SIGILL; add canvas elsewhere.
Inspecting the bundles to find what could be removed found a crash risk
added in v12.33 by 943bfd9197. That commit gave the armv6, armhf and armv7
bundles sharp's @img/sharp-linux-arm package. readelf shows its .node is
built for ARMv7 (Tag_CPU_arch: v7) and its libvips for ARMv8. The armv6
bundle runs on Raspberry Pi 1 and Zero, and the armhf/armv7 bundles on
ARMv7 boards. Executing an instruction the CPU lacks raises SIGILL, which
kills the whole WeKan process the first time a thumbnail is made. Before
that commit the bundles had only the linux-x64 package, which fails to load
and is handled. 32-bit ARM now downloads no native package and keeps the
WebAssembly sharp, which runs on any CPU (thumbnails and GIFs were made
with it under Node 26).

The same inspection found that @napi-rs/canvas is not dead weight. pdfjs-dist
6 builds a DOMMatrix while loading, so without canvas the import throws and
PDF attachment text is never indexed for search. This was checked: a PDF
made with pdfkit gives its text back with canvas and fails without it.
Every bundle except Linux x64 had only canvas-linux-x64-gnu, which
platform-native-packages then removed. The script now adds the glibc/MSVC
canvas package for the target as it does for sharp, verified by sha512 and
by its binary header. That covers linux x64/arm64/riscv64, darwin x64/arm64
and win32 x64/arm64. It never installs a musl package, nor anything on
32-bit ARM, where canvas is ARMv7 too. Where none is published (ppc64,
s390x, ia32, loong64), that is logged.

Checked against the npm registry: linux-arm64 and win32-x64 got sharp and
canvas, and both passed the ELF/PE check. linux-arm downloaded nothing.
tests/platformNativePackages.test.cjs covers 32-bit ARM, the canvas choice
(no musl, no ARM, none where unpublished) and the canvas download.

Thanks to xet7 !
2026-10-12 00:09:03 +03:00
Lauri Ojansivu 5f60152d08 Release workflows: upload 1 MB instead of 237 MB to Launchpad, and stop fetching unused history.
Launchpad snap legs (95-104 minutes in v12.32): prepare-launchpad-source.sh
archived the whole tree, 237 MB of docs, tests, translations and client
code, for every architecture. The wekan part (source: .) then copied it
again on Launchpad's slowest builders, and the recipe uses none of it,
because the app comes from the release .zip. The snapshot now holds only
what snapcraft.yaml reads: snapcraft.yaml, snap/, snap-src/, and
releases/fetch.sh, check-telemetry.py, risk-baseline.json (its deny-hash
list) and migrate-mongodb-to-ferretdb.mjs. That is 1.1 MB.
tests/releaseSnapLaunchpadFlatten.test.cjs checks the snapshot holds the
listed files and nothing else, and checks against the real snapcraft.yaml
that every project path it reads is in the list.

Git history that was fetched and never read:
- snap-launchpad cloned the full history (fetch-depth: 0, 447 MB) because
  remote-build rejects shallow repositories. What it uploads is the fresh
  one-commit repository made from git archive, so a shallow checkout is
  enough.
- bump only reads the tree and pushes one commit on top: shallow.
- prepare fetched history to see whether the release tag already existed.
  It now asks GitHub with git ls-remote, which also works on a re-run.
- update-ondra-gantt-repos cloned ~160 MB of history per variant repository
  to add one commit: shallow.
- release.yml (chart-releaser) ran on every push to main with a full clone,
  but this repository has no charts/; the chart is published to
  wekan/charts by release-all.yml. It now runs only when charts/ changes.

The wekan/charts checkouts keep fetch-depth: 0 because they need the
gh-pages branch, and that repository is 15 MB.

Thanks to xet7 !
2026-10-12 00:03:32 +03:00
Lauri Ojansivu 3c34e65403 Docker image: build from the published .zip on the build machine, not under QEMU.
In v12.32 "Build and push multi-arch image" took 27 of the docker job's 29
minutes. For each of seven platforms, six of them emulated, the Dockerfile
installed a compiler toolchain, resolved and downloaded Node.js, reinstalled
programs/server with npm, re-ran the prune, bump, trim and prune passes, and
purged the toolchain again. That platform's .zip leg in release-all.yml had
already done all of it, with the same scripts.

Stage 1 (FROM --platform=$BUILDPLATFORM) now runs natively on the runner for
each target. It downloads that target's .zip, checks it against the
release's own .sha256sum, re-runs the telemetry check, reads bundle/node's
ELF header to confirm the target CPU, and lays out /build and
/usr/local/bin/node. The image's node is the .zip's own node: the file the
leg resolved and verified, rather than a second download that had to match
it. Stage 2 is the only part that runs as the target CPU. It adds the WeKan
user, applies an OS update, installs file, libatomic1 and docker-cli, and
copies stage 1's result. No npm and no compiler ever enter the image.

The docker job read the base image from the first FROM; it now reads the
last one, since the first is the build-machine stage.

Stage 1 was run outside Docker against the real v12.32 s390x zip: the
checksum, the telemetry check (47979 files) and the ELF check (machine 22)
passed, and the layout was right. The ELF check rejects the same node when
expected as ppc64le, and rejects a non-ELF file. No Docker build ran here.

tests/dockerImageFromBundle.test.cjs pins the two stages, the verification,
the per-architecture ELF numbers (shared with check-addon-arch.mjs), and
that no reinstall, Node.js download or toolchain comes back. It fails on
the previous Dockerfile. Nine tests pinned the old in-image steps; each now
pins the new behaviour, with the reason recorded beside it.

Thanks to xet7 !
2026-10-12 00:01:07 +03:00
Lauri Ojansivu 403d485af9 Document the Windows win32 and ARM64 addon compile fix
Adds an Upcoming section for it, and notes in the released v12.33 entries that their win32 and win-arm64 zips were not published, and where the fix is.

Thanks to xet7 !
2026-10-11 23:35:08 +03:00
Lauri Ojansivu 34a33f7b14 Windows win32 and ARM64 bundles: compile bcrypt and argon2 with node-gyp itself.
v12.33's build-win32 and build-win-arm64 both failed their addon check:

  ...accounts-password/node_modules/argon2 has no arm64 Windows addon to load

The jobs relied on npm to compile the addons that have no prebuild for their
CPU (argon2 0.44 for win32-arm64; bcrypt and argon2 for win32-ia32) during
the programs/server install, through Meteor's npm-rebuild.js. The release
jobs use npm 12, which ignores npm_config_arch, npm_config_target_arch and
npm_config_nodedir ("Unknown env config", which it says will become an
error) and blocks install scripts outside allowScripts ("install scripts
blocked"). So nothing was compiled.

releases/compile-native-addons.mjs now runs node-gyp itself, using the
node-gyp that the install brings (pinned to 13.x, which finds Visual
Studio 2026), with --arch for the target CPU. win32 also passes the nodedir
that holds the fork's ia32 node.lib. It does this for every copy of each
named package that has a binding.gyp and no win32-<arch> prebuild, before
prune-build-only-modules removes node-gyp. It fails if node-gyp fails or
leaves no .node. Both jobs no longer set the npm_config_ variables.

Checked with the real node-gyp 13.0.2, argon2 0.44.0 and bcrypt 6.0.0
(with their win32 prebuilds removed, compiled for this machine): both
compiled, loaded, and hashed and verified a password.
tests/compileNativeAddons.test.cjs covers the plan, the node-gyp arguments
and every way the compile could quietly not happen.
tests/win32BundleBuild.test.cjs pins the step in both jobs and refuses
npm_config_arch/_target_arch/_nodedir in any workflow.
tests/releaseBundleCoverage.test.cjs used to require npm_config_arch: ia32,
and now requires the node-gyp cross-build, with the reason recorded in the
test.

Thanks to xet7 !
2026-10-11 23:34:51 +03:00
github-actions[bot] 798e314150 Bump versions for v12.33 2026-10-11 20:10:57 +00:00
Lauri Ojansivu 34924a5f3d Prepare v12.33 release 2026-10-11 23:09:17 +03:00
Lauri Ojansivu be09a819c3 Merge remote-tracking branch 'origin/main' 2026-10-11 23:08:50 +03:00
Lauri Ojansivu a21c5ad2ef Tidy the Upcoming CHANGELOG section
Shorten In short to three sentences covering every topic, and name the renamed platformNativePackages test in the Windows entry.

Thanks to xet7 !
2026-10-11 23:03:48 +03:00
Lauri Ojansivu 322270701f Document the UCS LDAP login fix
Thanks to xet7 !
2026-10-11 22:59:32 +03:00
Lauri Ojansivu 5f0716fc74 UCS: refuse to publish an app whose LDAP group settings refuse every login.
After the 12.32 UCS upgrade every LDAP login failed with "LDAP authentication
failed". The UCS compose (wekan/univention) kept the 6.09 setting
LDAP_GROUP_FILTER_ENABLE=true with no group id attribute and no group name.
6.09 read that as "in any group". WeKan now refuses an incomplete group
filter, as intended since #3700, so isUserInGroup refused everyone. The
compose is fixed in wekan/univention: the filter is off, because UCS grants
access through (wekanActivated=TRUE), and the group id attribute is cn, so
Domain Admins become admins again.

releases/ucs-release.py now applies WeKan's own group-setting rule to the
compose before it prepares a portal upload. With the group filter or admin
sync on, it needs the three group lookup settings, and an enabled filter
needs a group name. A compose that would refuse every LDAP login is not
published. It refuses the 12.32 compose and passes the fixed one.
tests/ucsRelease.test.py covers both, plus the rule.
tests/ldapGroupFilterConfig.test.cjs shows that WeKan refuses the 6.09 UCS
settings and needs nothing missing with the fixed ones.

The UCS README describes the failure and gives a workaround for installs that
already upgraded: two appcenter/env/wekan UCR values and univention-app
reinitialize. It also explains the cross-origin error seen when WeKan is
opened through a host other than ROOT_URL.

Thanks to xet7 !
2026-10-11 22:59:15 +03:00
Lauri Ojansivu 2fb604bc25 Document verified Nextcloud acceptance coverage and deployment limits
Record the 40 passing real browser checks across Nextcloud 32-35 and the mutual-TLS FRP subpath run, companion and release regressions, and the remaining Docker and ARM64 validation.

Thanks to xet7.
2026-10-11 22:49:33 +03:00
Lauri Ojansivu 91bfd08097 Test real Nextcloud deployments and preserve signed SockJS identities
Keep verified gateway identities bound to their actual upgrade sockets before SockJS filters custom headers. Add repository-local Nextcloud 32-35 acceptance runs for authentication, isolation, uploads, lifecycle, source-built upgrades, restored volumes and mutual-TLS FRP. Harden release archive checks and exercise signing and workflow failures.

Thanks to xet7.
2026-10-11 22:46:45 +03:00
Lauri Ojansivu 0b44d78d4a Document sharp on every platform
Thanks to xet7 !
2026-10-11 22:41:03 +03:00
Lauri Ojansivu 943bfd9197 Make sharp load on every WeKan platform: add each bundle's own sharp package.
sharp makes the attachment thumbnails. Every bundle except amd64 is made
from the amd64 Linux bundle, and npm installed only @img/sharp-linux-x64
there. Reading the published v12.32 zips showed that every other bundle
carried that one package and nothing it could load: arm64, ppc64le, s390x,
riscv64, i386, armhf, armv7, armv6, mac-arm64 and mac-x64, as well as the
Docker images and snaps built from them. Thumbnails fell back to the
full-size image, and nothing failed.

releases/windows-native-packages.mjs becomes platform-native-packages.mjs,
<bundle> <win32|linux|darwin> <arch>. It removes the packages npm would not
install on that target (os, cpu, and on Linux libc), downloads
@img/sharp-<platform>-<arch> and its own libvips at the versions pinned,
checks each against the registry's sha512, and checks every native addon's
header for the target. sharp publishes nothing native for Linux i386 or
loong64, so there it uses @img/sharp-wasm32. That package is now a root
optional dependency in package.json, at sharp's own range. It was run
under Node 26 with no native package: it made a 512 px WebP thumbnail and
a GIF.

check-addon-arch.mjs now reads ELF (with byte order) and Mach-O (including
universal binaries) as well as PE, and follows npm's libc rule on Linux.

The script now runs in build-amd64, build-arm64 (in its arm64 container),
both macOS jobs, all three Windows jobs, and build-extra-arches through
install-node-for-arch.sh. There it runs with the target node's own
process.arch, so armv6/armv7/armhf are arm, ppc64le is ppc64 and i386 is
ia32. build-release-bundle.sh runs it for the local machine.

Against the published v12.32 zips, given their native addons, it added the
right sharp and libvips for all eleven Linux and macOS bundles, kept wasm32
for i386, and passed every bcrypt, argon2 and sharp binary. Checked the
other way, the s390x bundle was refused as ppc64.
tests/platformNativePackages.test.cjs covers the script with a mocked
registry (libvips, wasm fallback, wrong CPU, tampered tarball, path
traversal). tests/sharpEveryPlatform.test.cjs requires every job that makes
a bundle zip to run it for its own target, after the prune and before the
zip, and the wasm32 dependency to stay at sharp's version.

Thanks to xet7 !
2026-10-11 22:40:47 +03:00
Lauri Ojansivu 0e9e62cfa1 Document the Windows sharp and ARM64 argon2 fix
Thanks to xet7 !
2026-10-11 21:59:20 +03:00
Lauri Ojansivu b5e96ac682 Windows bundles: add sharp for their own CPU, and build argon2 for ARM64.
Every Windows bundle is made from the amd64 Linux bundle, so sharp, which
makes the attachment thumbnails, had only @img/sharp-linux-x64 there. It
could never load on Windows, and every thumbnail fell back to the full-size
image. sharp publishes @img/sharp-win32-ia32, -x64 and -arm64 with the
libvips DLLs included, so nothing needs compiling.
releases/windows-native-packages.mjs puts in the package at the version
sharp pins, checks it against the registry's sha512, and removes the
packages whose os/cpu name another platform (sharp-linux-x64, its libvips,
@napi-rs/canvas-linux-x64-gnu). canvas is only pdfjs-dist's optional
dependency for rendering pages; WeKan only extracts text with pdfjs, and
there is no win32-ia32 canvas.

Running the addon check on the published v12.32 zips found a worse fault:
the win-arm64 zip has no arm64 argon2. argon2 0.44 publishes no win32-arm64
prebuild, and the job set no npm_config_arch, so node-gyp-build's install
test passed in the x64 runner node with the win32-x64 prebuild and compiled
nothing. accounts-password imports argon2 with no fallback. The rebuild
step now sets npm_config_arch=arm64, so argon2 is cross-compiled with
Visual Studio's ARM64 tools, which the runner image includes.

All three Windows jobs now run windows-native-packages.mjs and then
check-addon-arch.mjs for bcrypt, argon2 and @img/sharp-win32-<arch>, before
the zip is made. check-addon-arch accepts scoped package names. Against the
published v12.32 zips, win64 passes after the script, and win-arm64 still
fails on argon2, which the npm_config_arch fix addresses at build time.
tests/windowsNativePackages.test.cjs covers the script with a mocked
registry, including a tampered tarball and a path-traversal entry.
tests/win32BundleBuild.test.cjs pins the steps in all three jobs and the
target CPU of the cross-built ones.

Thanks to xet7 !
2026-10-11 21:58:43 +03:00
Lauri Ojansivu f08acbea8a Document the win32 bundle build fix
Adds an Upcoming section for it, and notes in the released v12.32 entry that its win32 bundle was not published after all, and where the fix is.

Thanks to xet7 !
2026-10-11 21:45:19 +03:00
Lauri Ojansivu df48ef2aeb Fix the win32 bundle build: no drive-letter path for tar, and check the ia32 addons.
v12.32 was the first release whose build-win32 got past the Node.js lookup,
and it died at the first step it had never run:

  tar: D\:\a\\_temp/node-ia32: Cannot open: No such file or directory

RUNNER_TEMP is a Windows path, and GNU tar in Git Bash reads anything before
a colon as a remote host. The step now builds the nodedir on the POSIX form
of the path (cygpath -u), unpacks the headers by relative name inside it,
checks that include/node/node.h arrived, and gives node-gyp the Windows form
(cygpath -w).

The step after that had never run either. bcrypt and argon2 publish no
win32-ia32 prebuild, so they must be compiled for ia32, and accounts-password
imports both with no fallback. A cross-build that silently did nothing would
still zip and pass 7z t, and fail only at the first sign-in.
releases/check-addon-arch.mjs reads each shipped addon's PE header and fails
the job unless bcrypt and argon2 each have one for ia32. It skips packages
whose os/cpu name another platform, as npm does. On the published v12.31
win64 zip it passes as x64 and refuses the same zip as ia32.

tests/win32BundleBuild.test.cjs pins the new step, checks that no Windows
job in any workflow gives tar a RUNNER_TEMP or GITHUB_WORKSPACE path, and
tests the checker on synthetic PE and ELF files, positive and negative.

Thanks to xet7 !
2026-10-11 21:44:57 +03:00
github-actions[bot] a3ad3b8673 Bump versions for v12.32 2026-10-11 18:11:05 +00:00
Lauri Ojansivu eb457235fb Prepare v12.32 release 2026-10-11 21:08:51 +03:00
Lauri Ojansivu 0b38901ea8 Wrap the two over-long lines in the v12.31 CHANGELOG entry
Whitespace only: the Nextcloud entry's two paragraphs are rewrapped at 80 columns, with the same words in the same order, so tests/changelogFormat.test.cjs passes again.

Thanks to xet7 !
2026-10-11 21:08:06 +03:00
Lauri Ojansivu 807abaf1b3 Tidy the Upcoming CHANGELOG section
Shorten In short to three sentences covering all four topics, remove a UCS sentence pasted into the docs reorganization entry, describe the UCS topic as a whole, and wrap its long lines at 80 columns.

Thanks to xet7 !
2026-10-11 21:06:39 +03:00
Lauri Ojansivu 7175f2b9ff Document the card drop flicker fix
Thanks to mimZD and xet7 !
2026-10-11 21:04:51 +03:00
Lauri Ojansivu a8323cc39c Fix card flicker on drop: never show a dropped card in both lists.
The drop handler must call sortable('cancel'), which puts the dragged card
back at its source until Blaze re-renders the move. The drop preview from
b687f37272 fills the target slot during that gap, but the real card stayed
visible at the source, so the card was drawn in both lists. The reporter's
newest video shows that frame: card "2" already in the target list and still
in the source list with its old count, then the source catches up.

retireCardDropPreview() now also hides the node that sortable('cancel') put
back, with an inline style, which Blaze does not manage there:
- a move to another list keeps it hidden until Blaze removes it, even when
  the new card renders a flush earlier;
- a reorder within one list shows only the preview until Blaze moves the
  node beside it, instead of jumping the card back to its old place first;
- a rejected move, or one that never arrives within 10 seconds, shows the
  card again where it is, so no card is left invisible.

tests/cardDropPreviewSource.test.cjs drives the helper on a minimal DOM: the
video's cross-list drop, a reorder, a drop in place, a rejected move, a
timeout, and a source copy that is never removed. It also checks that every
caller passes the node to hide. It fails on the video's frame without this
fix. 37-card-drag-sort.e2e.js now checks the source while the preview is
shown, and adds a reorder within one list.

Thanks to mimZD and xet7 !

Fixes #6430,
2026-10-11 21:04:30 +03:00
Lauri Ojansivu 76129475a1 Document bundled UCS migration and registry fallback
Thanks to xet7 !
2026-10-11 21:01:43 +03:00
Lauri Ojansivu 7f4a8fa6c1 Bundle UCS migration tools in the WeKan image and use available registry mirrors
Run the temporary migration worker from the ordinary versioned application image. Prefer the target App Center compose images, then try the same upstream version on GHCR, Docker Hub and Quay before downtime. Reject inaccessible or incomplete images before portal upload. Cover UCS-only access, fallback, failure reporting, cleanup and migration integrity with regression tests.

Thanks to xet7 !
2026-10-11 21:01:12 +03:00
Lauri Ojansivu 23ff4fb200 Document the docs reorganization
Moves the oldest release that no longer fits CHANGELOG.md's size budget to old-CHANGELOG/2026/10.md.

Thanks to xet7 !
2026-10-11 19:54:00 +03:00
Lauri Ojansivu 5b04b4b47c Reorganize docs: platform pages under docs/Platforms, and update their links.
The autoupdate design pages move from docs/Design/Autoupdate to where their
platform lives: the per-OS pages to docs/Platforms/FOSS/OS/OS-future-alternatives,
Snap-Core and Snap-Ondra-Gantt to Container/Snap, UCS and Nextcloud to
Container/Docker, and Platforms.md to docs/Platforms. docs/Platforms/FOSS/Cloud
is split into Container/Docker (Helm, OpenShift, CloudFoundry) and
FOSS/CloudFlare; Friend and UCS move under Container/Docker; Leo moves to
Features/ImportExport/Leo.

Every link to a moved page is updated: relative links in the docs, including
the moved pages' own links to the rest of the tree, full docs/ paths in the
workflows, tests and comments, and the Upcoming CHANGELOG entry. Released and
archived CHANGELOG sections are records and keep their original links.
tests/docsLinksResolve.test.cjs passes.

Thanks to xet7 !
2026-10-11 19:53:18 +03:00
Lauri Ojansivu 95386b0f7b Document the win32 bundle fix
Thanks to xet7 !
2026-10-11 19:38:26 +03:00
Lauri Ojansivu 24e5cc4300 Build the win32 bundle again: find node-patches' Windows checksum sidecar.
wekan/node-patches publishes node-win32.exe with its checksum in
node-win32.sha256sum (without .exe), as the line `<hash> *node-win32.exe`.
releases/resolve-node-source.sh looked only for node-win32.exe.sha256sum and an
unmarked name, so it answered node_found=false and build-win32 skipped itself
every release, although both binaries were published. The resolver now also
tries the sidecar without .exe and accepts sha256sum's `*` binary-mode marker,
and strips CR. Verified against v26.11.1: win32 resolves, and the downloaded
node-win32.exe matches the published SHA256. tests/releaseNodeSources.test.cjs
runs the matcher on real sidecar lines, both positive and negative.

Thanks to xet7 !
2026-10-11 19:38:11 +03:00
Lauri Ojansivu 926845d97b Merge remote-tracking branch 'origin/main' 2026-10-11 18:58:03 +03:00
Lauri Ojansivu a742976538 Document the CPU platform table update
Thanks to xet7 !
2026-10-11 18:57:20 +03:00
Lauri Ojansivu 4ad51ec8eb Update the CPU platform table from the node-patches and FerretDB releases.
docs/Design/Autoupdate/Platforms.md now lists every binary that the newest
wekan/node-patches (v26.11.1, 16 targets) and wekan/FerretDB (v1.92.0, 25
targets) releases publish. Its "what is built" table matches what v12.30 and
v12.31 shipped and ghcr.io/wekan/wekan:v12.30 contains: armv7, armv6, win64,
win-arm64, mac-x64 and mac-arm64 bundles are added, riscv64's Node.js comes from
node-patches for v26.11.1, and loong64 is skipped. The doc also explains why
win32 is not built: the resolver looks for node-win32.exe.sha256sum, but
node-patches publishes node-win32.sha256sum. FreeBSD amd64 is listed as having
both binaries now.

Thanks to xet7 !
2026-10-11 18:56:55 +03:00
github-actions[bot] 482c7f05e8 Bump versions for v12.31 2026-10-11 15:25:46 +00:00
Lauri Ojansivu f7e925e42f Prepare v12.31 release 2026-10-11 18:23:51 +03:00
Lauri Ojansivu 81d2f85f59 Document the possible additional platforms
Thanks to xet7 !
2026-10-11 18:12:44 +03:00
Lauri Ojansivu ece7830a8e Document Sandstorm migration port handoff fix 2026-10-11 18:11:57 +03:00
Lauri Ojansivu 2c4c89e794 Preserve replacement Sandstorm bridge until Meteor takes its port 2026-10-11 18:11:42 +03:00
Lauri Ojansivu bb9c4c7044 Document the possible additional platforms, with ready files for five of them.
docs/Design/Autoupdate/Platforms.md opens with the open source platforms that
WeKan is not on yet, checked in each catalog on 2026-10-11, and what is still
missing for each. ZimaOS/CasaOS, TrueNAS, Coolify, Dokploy and the AUR get
the files their catalogs take and a README with the pull request each
expects, under docs/Platforms/. Each was tested with that catalog's own tools
in .tools/ where they run without the platform: CasaOS's validator and store
builder, the TrueNAS library render, Dokploy's three validators, and Arch's
makepkg, whose package started WeKan. tests/platformTemplates.test.cjs keeps
the image tag and settings the same across them.

Thanks to xet7 !
2026-10-11 18:11:35 +03:00
Lauri Ojansivu 65918a9570 Document Mac smoke cleanup and UCS credential storage fixes 2026-10-11 17:49:53 +03:00
Lauri Ojansivu a1e97a18c4 Pass UCS provider credentials through stdin instead of a plaintext file 2026-10-11 17:49:25 +03:00
Lauri Ojansivu 3c085a2d71 Fix Mac app smoke cleanup for exited process groups 2026-10-11 17:48:18 +03:00
Lauri Ojansivu 465aaa219e Merge remote-tracking branch 'origin/main' 2026-10-11 17:43:29 +03:00
Lauri Ojansivu c3a13373c0 Document Nextcloud ExApp support in the upcoming release 2026-10-11 17:41:50 +03:00
Lauri Ojansivu a325cfe991 Support Nextcloud ExApp identities and publish WeKan with embedded SQLite 2026-10-11 17:41:28 +03:00
github-actions[bot] 9592d6d820 Bump versions for v12.30 2026-10-11 14:17:51 +00:00
Lauri Ojansivu d76a4f4489 Prepare v12.30 release 2026-10-11 17:15:35 +03:00
Lauri Ojansivu 3042cf06f2 Document the Sandstorm client half and Powerbox button
Thanks to xet7 !
2026-10-11 17:02:52 +03:00
Lauri Ojansivu ed5502edfa Sandstorm: the client half runs again, and the Powerbox button adds people to the board.
sandstorm.js was imported only by server/imports.js since mainModule turned
eager loading off, so its browser code never ran in a grain: the grain URL and
title did not follow the board, the Powerbox picker could not open, and the
isSandstorm helper was undefined, so email, password, logout and avatar
settings that Sandstorm handles showed anyway. The server half - which loads
the Cap'n Proto bridge client and node:net - is now server/sandstorm.js, and
the client imports the shared sandstorm.js. Its shell listener ignores
messages that are not its replies instead of throwing on them.

The Powerbox button is back in the members widget, for people who may invite.
It passes the open board, and the claimed person is invited to it through
inviteUserToBoard, with the member picker's own permission checks; #2405
removed the old button because the identity it claimed was never attached to
a board.

Thanks to xet7 !
2026-10-11 17:02:20 +03:00
Lauri Ojansivu 2e927f59f3 Resolve latest published WeKan and FerretDB versions for manual UCS releases 2026-10-11 16:46:49 +03:00
Lauri Ojansivu 6b121be9da Run UCS and Nextcloud publishing only as standalone manual workflows 2026-10-11 16:37:58 +03:00
Lauri Ojansivu 3ad350b786 Document the Sandstorm port handoff fix
Thanks to xet7 !
2026-10-11 16:34:22 +03:00
Lauri Ojansivu 6e6b5d2e30 Fix Sandstorm grain showing an empty response while WeKan starts.
start.js killed the please-wait bridge and then loaded main.js, but
Meteor 3 awaits every Meteor.startup hook before webapp calls listen, so
nothing answered on port 4000 for all of WeKan's startup. The
sandstorm-http-bridge got connection-refused and the browser showed
NS_ERROR_NET_EMPTY_RESPONSE. The bridge now keeps the port until Meteor
binds it: the first listen on the app port waits for the bridge process
to exit and then binds, and logs "WeKan is listening on port 4000".

Thanks to xet7 !
2026-10-11 16:34:21 +03:00
Lauri Ojansivu b0d1397b16 Document the Import page sources in columns
Thanks to xet7 !
2026-10-11 16:15:49 +03:00
Lauri Ojansivu f44e52242c Import page: show every source under "Import from:" at once, in columns.
When there is horizontal room, the source list and the parts list flow
into as many columns as fit (a column width, not a count), so WeKan,
Trello, Jira and the rest are all on screen at once instead of below
the fold. A phone keeps one column.

Thanks to xet7 !
2026-10-11 16:15:07 +03:00
Lauri Ojansivu de6dcebffa Document UCS writable storage for attachments avatars and SQLite 2026-10-11 16:10:41 +03:00
Lauri Ojansivu 64002ea6e5 Prepare UCS releases with FerretDB version tags 2026-10-11 16:08:49 +03:00
Lauri Ojansivu 546d0e759f Document the Windows 7-Zip fix
Thanks to xet7 !
2026-10-11 16:06:00 +03:00
Lauri Ojansivu 6cb7e45b92 Windows bundles use the runner's 7-Zip instead of installing it from Chocolatey.
v12.29's build-win64 failed before building anything: community.chocolatey.org
answered 504 and `choco install -y 7zip` could not resolve 7zip.install.
windows-latest already has 7-Zip, so build-win64, build-win-arm64 and
build-win32 use it, and fall back to Chocolatey with five retries only on an
image without it.

Thanks to xet7 !
2026-10-11 16:05:42 +03:00
github-actions[bot] 6061337628 Bump versions for v12.29 2026-10-11 12:44:19 +00:00
Lauri Ojansivu 209957a811 Prepare v12.29 release 2026-10-11 15:42:32 +03:00
Lauri Ojansivu 16f1eb5606 Merge remote-tracking branch 'origin/main' 2026-10-11 15:41:51 +03:00
Lauri Ojansivu 8bb445604e Share UCS and Nextcloud publishing workflows across release and recovery 2026-10-11 15:35:33 +03:00
Lauri Ojansivu 6a695cdda8 Document Sandstorm Cap'n Proto in JavaScript and the capnp-es fork
Thanks to xet7 !
2026-10-11 15:30:52 +03:00
Lauri Ojansivu ee63d478e3 Sandstorm activity events and Powerbox identity claims work again on Node 26.
Both are Cap'n Proto calls on sandstorm-http-bridge's /tmp/sandstorm-api;
the bridge's HTTP proxy has no route for either. They went through
node-capnp (capnp.node), built for Node 14, so a grain ran without them.
imports/sandstorm/bridgeClient.mjs now makes the same calls in plain
JavaScript with @wekanteam/capnp-es, using schema classes generated from
Sandstorm's .capnp files by releases/sandstorm-capnp-schemas.sh. The
activity hook reads the board from the activity instead of the removed
hard-coded 'sandstorm' board, and a notification that cannot be sent no
longer fails the change. The .spk drops the 33.6 MB capnp.node.

tests/sandstormCapnpRpc.test.cjs drives every RPC message from a
hand-built peer, and runs the same cases plus WeKan's bridge calls against
the reference C++ RPC implementation with Sandstorm's schemas.

Thanks to xet7 !
2026-10-11 15:29:45 +03:00
Lauri Ojansivu 3eec76b366 Fork capnp-es as @wekanteam/capnp-es with complete RPC Resolve and Release.
npm capnp-es 0.0.16 (upstream commit 439f564), vendored like the other
npm-packages forks with its tarball SHA256 and npm integrity recorded. Its
experimental RPC Conn could not talk to a C++ peer such as
sandstorm-http-bridge: it ignored Resolve, Release and Disembargo, never
released an import, left tail-call answers (takeFromOtherQuestion) pending
forever, released question ids instead of export ids, answered bootstrap
with a null capability, and its DeferredTransport lost a message whenever
two arrived together. The fork handles all of them, and its compiler no
longer regenerates structs declared in another file, which made generated
Sandstorm schema modules a SyntaxError on load. Every change is in
npm-packages/capnp-es/patches/wekan.patch.

Thanks to xet7 !
2026-10-11 15:29:36 +03:00
Lauri Ojansivu 847106ea03 Document UCS 12.28 migration and remaining release steps 2026-10-11 14:55:02 +03:00
Lauri Ojansivu 97df539519 Document the Sandstorm grain log warnings fix
Thanks to xet7 !
2026-10-11 14:20:16 +03:00
Lauri Ojansivu 5e25090e8f Sandstorm grain logs no longer show false warnings.
start-memory.js turns Node's Web Storage off with
--no-experimental-webstorage, so Node 26 no longer prints "localStorage is
not available because --localstorage-file was not provided" into every
grain log, and it keeps an administrator's heap size or Web Storage choice
while still adding what is missing. The ROOT_URL email warning is skipped
in a grain, whose ROOT_URL is always its loopback address behind the bridge
and which has no SMTP of its own.

Thanks to xet7 !
2026-10-11 14:19:00 +03:00
github-actions[bot] b4f6618376 Bump versions for v12.28 2026-10-11 10:45:48 +00:00
Lauri Ojansivu b077ee722e Prepare v12.28 release 2026-10-11 13:43:42 +03:00
Lauri Ojansivu 9bec7602b9 Document the node suite fixes
Thanks to xet7 !
2026-10-11 13:38:02 +03:00
Lauri Ojansivu d6bee1bdaf Fix the six failing node suites.
Validation.md links models/eventLog.js by its real name, the README counts
the 166 catalogs now over 90% translated, and CHANGELOG.md is wrapped at 80
columns (whitespace only). The guards follow deliberate changes: Supported.md
belongs directly in docs/Databases, build.sh's macOS branch writes
export PATH="$HOME/.meteor:$PATH" since the npx meteor change, and the
wasmagic pin is checked against the lockfile instead of a copied number.

Thanks to xet7 !
2026-10-11 13:36:32 +03:00
Lauri Ojansivu a8a264a23b Document the Sandstorm runtime versions fix
Thanks to xet7 !
2026-10-11 13:12:37 +03:00
Lauri Ojansivu 242c4b097c Sandstorm uses the same Node.js, npm and FerretDB as other platforms.
The .spk now carries the exact Node.js the Dockerfile pins (26.11.1), which
is what https://wekan.fi/version.txt advertises, downloaded and verified
with releases/embed-verified-node.sh like the zip bundles, instead of
Meteor's dev-bundle node or the newest Node 24. FerretDB is fetched by the
release tag version.txt names, and the Sandstorm and Windows workflows
install the Dockerfile's npm with releases/use-release-npm.sh instead of a
hard-coded npm 12.0.2. Comments and docs describing the current runtime
say Node 26.

Thanks to xet7 !
2026-10-11 13:12:21 +03:00
Lauri Ojansivu 866d44da54 Document the Sandstorm start-memory.js fix
Thanks to xet7 !
2026-10-11 12:44:42 +03:00
Lauri Ojansivu d6427a5afb Fix Sandstorm grains crash-looping on ./start-memory.sh.
A grain has no shell: the meteor-spk 0.6.0 base ships only mongod, niscud
and node in bin/, so the pkgdef's #!/bin/sh start-memory.sh failed with
"execvp(argvp[0], argvp): No such file or directory" and no grain booted.
The memory-derived heap launcher is now start-memory.js, run by node.
start.js also no longer spawns the missing /bin/sleep, which had made every
wait return at once.

Thanks to xet7 !
2026-10-11 12:44:23 +03:00
Lauri Ojansivu d86a0556a8 Document installing Meteor with npx meteor
Thanks to xet7 !
2026-10-11 03:17:07 +03:00
Lauri Ojansivu 0dbf9d8502 Install Meteor with npx meteor, as the Meteor documentation says.
https://docs.meteor.com/about/install.html installs Meteor on Linux, macOS
and Windows alike with `npx meteor`, as the user and never with sudo; the
global npm install is only its fallback. npx runs the meteor package's bin,
which installs into ~/.meteor (%LOCALAPPDATA%\.meteor on Windows) and puts
it on PATH, so npm 12's refusal to run install scripts does not apply, and
the documented npm fallback flags that npm 12 rejects are not needed.

build.sh's Linux branch and build.bat now run npx -y meteor unless Meteor
is installed already, as the macOS branch did. build.sh removes the global
installer package an earlier setup left, and gives ~/.npm back to the user
if an old sudo npm made it root's. releases/rebuild-release.sh does the
same instead of sudo npm on Linux and brew install meteor on macOS.

Checked with npm 12.2.0: npx -y meteor installed Meteor 3.5.2 into a
scratch HOME and added it to .bashrc and .bash_profile.

Thanks to xet7 !
2026-10-11 03:17:01 +03:00
Lauri Ojansivu 9cf0b7b398 Document the npm 12 Meteor install fixes as one entry
Thanks to xet7 !
2026-10-11 03:12:50 +03:00
Lauri Ojansivu 9c3692f832 Let npm 12 run the Meteor installer's install script.
npm 12 runs no package's install script unless allow-scripts names it, and
only warns when it blocks one. The meteor npm package is just an installer:
its install script is what downloads Meteor, so Setup -> Install dependencies
finished with no Meteor. build.sh, build.bat and releases/rebuild-release.sh
now pass --allow-scripts=meteor, which npm 12.2.0 matches against the
registry URL's package name, so it allows that one script and nothing else.
Checked with npm 12.2.0 on a registry package: blocked without the flag, run
with it. npx -y meteor (macOS) runs the installer as the package's bin and
needs no change.

tests/npmNoUnsafePerm.test.cjs becomes tests/npmMeteorInstall.test.cjs and
also fails on a global npm install of meteor without --allow-scripts=meteor.

Thanks to xet7 !
2026-10-11 03:12:41 +03:00
Lauri Ojansivu 3286f05bb2 Document the npm 12 --foreground-scripts fix
Thanks to xet7 !
2026-10-11 03:07:50 +03:00
Lauri Ojansivu 3c755c1855 Install Meteor with --foreground-scripts, the full flag name npm 12 requires.
5c9334b76c passed --foreground-script, as Meteor's install documentation
spells it. npm 11 read that as an abbreviation; npm 12.2.0, which build.sh
installs from the Dockerfile's NPM_VERSION before installing Meteor, stops
with 'Invalid abbreviated flag'. Checked against npm 12.2.0: it accepts
--foreground-scripts and refuses both --foreground-script and --unsafe-perm.
The test now also fails on the abbreviated flag, in the scripts and in the
.agents notes it was copied from.

Thanks to xet7 !
2026-10-11 03:07:38 +03:00
Lauri Ojansivu c65bf12f05 Updated vscodium sandbox 2026-10-11 03:05:28 +03:00
Lauri Ojansivu 163149a94b Document the npm 11 Meteor install fix
Thanks to xet7 !
2026-10-11 03:04:38 +03:00
Lauri Ojansivu 5c9334b76c Install Meteor with --foreground-script, which npm 11 accepts.
Setup -> Install dependencies stopped with EUNKNOWNCONFIG right after n
installed the pinned Node and npm 11: npm 7 removed --unsafe-perm and npm 11
refuses an unknown cli flag. build.sh and releases/rebuild-release.sh now
pass --foreground-script, as Meteor's own install documentation does, and
tests/npmNoUnsafePerm.test.cjs fails when any script passes the old flag.

Thanks to xet7 !
2026-10-11 03:03:53 +03:00
Lauri Ojansivu da9c08db64 Merge pull request #6760 from wekan/dependabot/npm_and_yarn/aws-sdk/lib-storage-3.1147.0
Bump @aws-sdk/lib-storage from 3.1137.0 to 3.1147.0
2026-10-10 23:45:31 +03:00
dependabot[bot] f83b2555ec Bump @aws-sdk/lib-storage from 3.1137.0 to 3.1147.0
Bumps [@aws-sdk/lib-storage](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/lib/lib-storage) from 3.1137.0 to 3.1147.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.1147.0/lib/lib-storage)

---
updated-dependencies:
- dependency-name: "@aws-sdk/lib-storage"
  dependency-version: 3.1147.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-10 20:44:35 +00:00
Lauri Ojansivu a93b97ff97 Merge pull request #6759 from wekan/dependabot/npm_and_yarn/sanitize-html-2.18.0
Bump sanitize-html from 2.17.7 to 2.18.0
2026-10-10 23:42:33 +03:00
Lauri Ojansivu 946a2cc4ba Merge pull request #6758 from wekan/dependabot/npm_and_yarn/enhanced-resolve-5.26.0
Bump enhanced-resolve from 5.25.1 to 5.26.0
2026-10-10 23:37:31 +03:00
Lauri Ojansivu 5820a04e68 Merge pull request #6757 from wekan/dependabot/npm_and_yarn/aws-sdk/client-s3-3.1147.0
Bump @aws-sdk/client-s3 from 3.1137.0 to 3.1147.0
2026-10-10 23:37:10 +03:00
Lauri Ojansivu ec8d448944 Merge pull request #6756 from wekan/dependabot/npm_and_yarn/wasmagic-1.0.11
Bump wasmagic from 1.0.10 to 1.0.11
2026-10-10 23:36:21 +03:00
dependabot[bot] 77b53613d2 Bump sanitize-html from 2.17.7 to 2.18.0
Bumps [sanitize-html](https://github.com/apostrophecms/apostrophe/tree/HEAD/packages/sanitize-html) from 2.17.7 to 2.18.0.
- [Changelog](https://github.com/apostrophecms/apostrophe/blob/main/packages/sanitize-html/CHANGELOG.md)
- [Commits](https://github.com/apostrophecms/apostrophe/commits/sanitize-html@2.18.0/packages/sanitize-html)

---
updated-dependencies:
- dependency-name: sanitize-html
  dependency-version: 2.18.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-10 20:33:58 +00:00
dependabot[bot] a08793d82c Bump enhanced-resolve from 5.25.1 to 5.26.0
Bumps [enhanced-resolve](https://github.com/webpack/enhanced-resolve) from 5.25.1 to 5.26.0.
- [Release notes](https://github.com/webpack/enhanced-resolve/releases)
- [Changelog](https://github.com/webpack/enhanced-resolve/blob/main/CHANGELOG.md)
- [Commits](https://github.com/webpack/enhanced-resolve/compare/v5.25.1...v5.26.0)

---
updated-dependencies:
- dependency-name: enhanced-resolve
  dependency-version: 5.26.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-10 20:33:43 +00:00
dependabot[bot] 89da3af177 Bump @aws-sdk/client-s3 from 3.1137.0 to 3.1147.0
Bumps [@aws-sdk/client-s3](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-s3) from 3.1137.0 to 3.1147.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.1147.0/clients/client-s3)

---
updated-dependencies:
- dependency-name: "@aws-sdk/client-s3"
  dependency-version: 3.1147.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-10 20:33:31 +00:00
dependabot[bot] 3d713c0205 Bump wasmagic from 1.0.10 to 1.0.11
Bumps [wasmagic](https://github.com/moshen/wasmagic) from 1.0.10 to 1.0.11.
- [Commits](https://github.com/moshen/wasmagic/compare/v1.0.10...v1.0.11)

---
updated-dependencies:
- dependency-name: wasmagic
  dependency-version: 1.0.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-10 20:33:00 +00:00
Lauri Ojansivu 0ad5089742 Merge remote-tracking branch 'origin/main' 2026-10-10 21:21:21 +03:00
Lauri Ojansivu d09ade4419 Document Gujarati translation progress
Record the completed current catalog, validation and remaining all-language inventory.

Thanks to xet7 !
2026-10-10 21:15:26 +03:00
Lauri Ojansivu 91ebd76c18 Translate remaining current Gujarati strings
Fill 196 English placeholders while preserving existing translations and exact source tokens. Verify script, import exclusions, field permissions, notification delivery and destructive versus resumable recovery actions. Longer Gujarati technical wording remains low-confidence pending speaker review.

All 306 translation/Blockly suites, 246 catalog inventories and 21 preservation checks pass. Five browser cases pass syntax and discovery; runtime remains unverified on this host.

Thanks to xet7 !
2026-10-10 21:15:00 +03:00
Lauri Ojansivu d68ced18dc Document Hindi translation progress
Record both completed current catalogs, validation and the remaining all-language inventory.

Thanks to xet7 !
2026-10-10 21:09:13 +03:00
Lauri Ojansivu 08f1aa2cfd Translate remaining current Hindi strings
Fill 392 English placeholders across both Hindi catalogs while preserving existing translations and exact source tokens. Verify import exclusions, field permissions, notification delivery and destructive versus resumable recovery actions. Longer Hindi technical wording remains low-confidence pending speaker review.

All 306 translation/Blockly suites, 246 catalog inventories and 21 preservation checks pass. Ten browser cases pass syntax and discovery; runtime remains unverified on this host.

Thanks to xet7 !
2026-10-10 21:08:44 +03:00
Lauri Ojansivu 02df92478b Document Traditional Chinese translation progress
Record three completed current catalogs, validation and the remaining all-language inventory.

Thanks to xet7 !
2026-10-10 21:03:38 +03:00
Lauri Ojansivu 9c4477ef32 Translate remaining current Traditional Chinese strings
Fill 678 English placeholders across three catalogs while preserving existing translations and exact tokens. Verify script, import exclusions, field permissions, notifications, Scrum and recovery meanings. Keep specialist wording provisional pending speaker review and broader language auditing open.

All 306 translation/Blockly suites, 246 catalog inventories and 21 preservation checks pass. Fifteen browser cases pass syntax and discovery; runtime remains unverified on this host.

Thanks to xet7 !
2026-10-10 21:03:08 +03:00
Lauri Ojansivu 348e35574b Document Simplified Chinese translation progress
Record six completed current catalogs, validation and the remaining all-language inventory.

Thanks to xet7 !
2026-10-10 20:57:58 +03:00
Lauri Ojansivu ee2626fe2e Translate remaining current Simplified Chinese strings
Fill 1356 English placeholders across six catalogs while preserving existing translations and exact tokens. Verify import exclusions, field permissions, notifications, Scrum and recovery meanings. Keep specialist wording provisional pending speaker review and broader language auditing open.

All 306 translation/Blockly suites, 246 catalog inventories and 21 preservation checks pass. Thirty browser cases pass syntax and discovery, including the zh-SG runtime tag; runtime remains unverified on this host.

Thanks to xet7 !
2026-10-10 20:57:27 +03:00
Lauri Ojansivu ad646fc444 Document Arabic legacy-language corrections
Record the reviewed candidate queue, retained Arabic values, regression coverage and remaining broader language audit.

Thanks to xet7 !
2026-10-10 20:52:10 +03:00
Lauri Ojansivu 3481a310cb Replace legacy Persian text in Arabic catalogs
Review all 456 remaining seed-language candidates. Correct 358 values per catalog and retain 98 valid/shared Arabic values, for 1074 corrections across ar, ar-DZ and ar-EG. Restore activity, email, rule, import and analytics meanings while preserving exact tokens and unchanged translations. Longer rule fragments, color shades and analytics wording remain low-confidence pending speaker review.

All 306 translation/Blockly suites, 246 catalog inventories and 21 preservation checks pass. Three Arabic rule-color browser cases pass syntax and discovery; runtime remains unverified. Broader unflagged language review remains open.

Thanks to xet7 !
2026-10-10 20:51:41 +03:00
Lauri Ojansivu 118dceb7e2 Document Arabic translation progress and remaining language audit
Record current placeholder fills, Persian core-label repairs, validation and the still-open older wrong-language review.

Thanks to xet7 !
2026-10-10 20:45:42 +03:00
Lauri Ojansivu 2204c5693d Translate current Arabic strings and repair Persian core labels
Fill 678 English placeholders across three Arabic catalogs and replace 18 wrong-language core values. Preserve other translations and exact source tokens. Add permission, import, notification, Scrum and recovery regressions. Longer technical wording remains low-confidence pending speaker review; the broader Persian-seed audit remains open.

All 306 translation/Blockly suites, 246 catalog inventories and 21 preservation checks pass. Fifteen browser cases pass syntax and discovery; runtime remains unverified on this host.

Thanks to xet7 !
2026-10-10 20:45:09 +03:00
Lauri Ojansivu 35090748e5 Document Hebrew translation progress
Record both completed current catalogs, regression coverage and the remaining all-language inventory.

Thanks to xet7 !
2026-10-10 20:39:31 +03:00
Lauri Ojansivu 6f11e15f7d Translate remaining current Hebrew strings
Fill 396 English placeholders across both Hebrew catalogs while preserving existing translations and exact source tokens. Verify import exclusions, field permissions, notification delivery and destructive versus resumable recovery actions. Longer Hebrew technical wording remains low-confidence pending speaker review.

All 306 translation/Blockly suites, 246 catalog inventories and 21 preservation checks pass. Ten browser cases pass syntax and discovery; runtime remains unverified on this host.

Thanks to xet7 !
2026-10-10 20:39:05 +03:00
Lauri Ojansivu 7647ed2ec0 Document Persian translation progress
Record both completed current catalogs, regression coverage and the remaining all-language inventory.

Thanks to xet7 !
2026-10-10 20:34:31 +03:00
Lauri Ojansivu 973215e72e Translate remaining current Persian strings
Fill 398 English placeholders across both Persian catalogs while preserving existing translations and exact source tokens. Check import exclusions, field permissions, notification delivery and destructive versus resumable recovery actions. Longer Persian technical wording remains low-confidence pending speaker review.

All 306 translation/Blockly suites, 246 catalog inventories and 21 preservation checks pass. Ten browser cases pass syntax and discovery; runtime remains unverified on this host.

Thanks to xet7 !
2026-10-10 20:34:03 +03:00
Lauri Ojansivu 21525ad916 Document Estonian translation progress
Record the completed current catalog, regression coverage and remaining all-language inventory.

Thanks to xet7 !
2026-10-10 20:29:02 +03:00
Lauri Ojansivu 2a19a286ab Translate remaining current Estonian strings
Fill 110 English placeholders while preserving existing translations and exact source tokens. Verify permissions, synchronization directions, import semantics, notification timing and workflow completion states. Longer technical wording remains low-confidence pending speaker review.

All 306 translation/Blockly suites, 246 catalog inventories and 21 preservation checks pass. Three browser cases pass syntax and discovery; runtime remains unverified on this host.

Thanks to xet7 !
2026-10-10 20:28:33 +03:00
Lauri Ojansivu 8e9236776a Document Afrikaans translation progress
Record both completed current catalogs, regression coverage and the remaining all-language inventory.

Thanks to xet7 !
2026-10-10 20:25:01 +03:00
Lauri Ojansivu 4c5bf48b53 Translate remaining current Afrikaans strings
Fill 240 English placeholders across both Afrikaans catalogs while preserving existing translations and source tokens. Check field permissions, import exclusions, notification delivery and workflow completion states. Longer technical wording remains low-confidence pending speaker review.

All 306 translation/Blockly suites, 246 catalog inventories and 21 preservation checks pass. Six browser cases pass syntax and discovery; runtime remains unverified on this host.

Thanks to xet7 !
2026-10-10 20:24:32 +03:00
Lauri Ojansivu 284b8a96d4 Document Belarusian translation progress
Record the translated current catalog, regression coverage and remaining all-language inventory.

Thanks to xet7 !
2026-10-10 20:20:17 +03:00
Lauri Ojansivu 7c187e7318 Translate remaining current Belarusian strings
Fill 130 English placeholders while preserving existing translations and exact source tokens. Cover permissions, import exclusions, notification delivery and workflow states. Longer Belarusian technical wording remains low-confidence pending speaker review.

All 306 translation/Blockly suites, 246 catalog inventories and 21 preservation checks pass. Three browser cases pass syntax and discovery; runtime remains unverified on this host.

Thanks to xet7 !
2026-10-10 20:19:27 +03:00
Lauri Ojansivu aee55ee2d7 Document Icelandic translation fills and validation
Record the 130-value batch and refresh the full remaining placeholder count.

Thanks to xet7.
2026-10-10 20:11:51 +03:00
Lauri Ojansivu 4aa0bd17b3 Translate remaining current Icelandic strings
Fill 130 English placeholders, including 20 import-format instructions, while preserving existing translations and exact tokens. Replace the historical completion gate with the full current catalog. Longer Icelandic technical wording remains provisional pending native-speaker review.

All 306 translation/Blockly suites, 246 catalog inventories and 21 preservation checks pass. Three browser cases pass syntax and discovery; runtime execution remains unverified without the app and browser system dependencies.

Thanks to xet7.
2026-10-10 20:11:09 +03:00
Lauri Ojansivu bb7c69e417 Document Bosnian translation fills and validation
Record the 110-value batch and refresh the full remaining placeholder count.

Thanks to xet7.
2026-10-10 20:05:34 +03:00
Lauri Ojansivu 7f0ee4463c Translate remaining current Bosnian strings
Fill 110 English placeholders while preserving existing translations, established Bosnian terminology and exact tokens. Longer technical wording remains provisional pending native-speaker review.

All 306 translation/Blockly suites, 246 catalog inventories and 21 preservation checks pass. Three browser cases pass syntax and discovery; runtime execution remains unverified without the app and browser system dependencies.

Thanks to xet7.
2026-10-10 20:05:04 +03:00
Lauri Ojansivu c215db4f68 Document Albanian translation fills and validation
Record the 110-value batch and refresh the full remaining placeholder count.

Thanks to xet7.
2026-10-10 20:01:32 +03:00
Lauri Ojansivu 9cd88a43c4 Translate remaining current Albanian strings
Fill 110 English placeholders while preserving existing translations and exact tokens. Replace the historical completion gate with the full current catalog and verify action meanings. Longer Albanian technical wording remains provisional pending native-speaker review.

All 306 translation/Blockly suites, 246 catalog inventories and 21 preservation checks pass. Three browser cases pass syntax and discovery; runtime execution remains unverified without the app and browser system dependencies.

Thanks to xet7.
2026-10-10 20:00:58 +03:00
Lauri Ojansivu 19b001e2de Document Latvian translation fills and validation
Record the 110-value batch and refresh the full remaining placeholder count.

Thanks to xet7.
2026-10-10 19:56:56 +03:00
Lauri Ojansivu e4908540ef Translate remaining current Latvian strings
Fill 110 English placeholders while preserving existing translations and exact tokens. Check link directions, permissions, import semantics, notification settings and completion states. Longer Latvian technical wording remains provisional pending native-speaker review.

All 306 translation/Blockly suites, 246 catalog inventories and 21 preservation checks pass. Three browser cases pass syntax and discovery; runtime execution remains unverified without the app and browser system dependencies.

Thanks to xet7.
2026-10-10 19:56:25 +03:00
Lauri Ojansivu b5a3042c1a Document Macedonian vocabulary review results
Record 262 corrections, 55 retained shared values and validation after updating the historical JSON example record.

Thanks to xet7.
2026-10-10 19:52:09 +03:00
Lauri Ojansivu 5f005ecfe1 Correct remaining reviewed Macedonian vocabulary matches
Review 317 long values matching Bulgarian or Serbian: correct 262 wrong-language or meaning-drifted values and retain 55 valid shared terms. Preserve all other values and exact tokens. Update the existing JSON example correction record after translating its Serbian sample prose into Macedonian. Specialized Macedonian wording remains lower confidence pending native-speaker review.

The 306-suite run passed 305 suites and exposed the stale audit record in one. After correcting the record, that suite and the focused audit and Macedonian checks pass on rerun. All 246 catalog inventories and 21 preservation checks pass. Existing browser cases pass syntax and discovery; runtime remains unverified.

Thanks to xet7.
2026-10-10 19:51:36 +03:00
Lauri Ojansivu 0b08c66446 Document Macedonian fills and language corrections
Record the 130 fills, 154 reviewed corrections, validation and remaining semantic audit.

Thanks to xet7.
2026-10-10 19:46:17 +03:00
Lauri Ojansivu 496acb14e7 Fill Macedonian strings and correct mixed-language values
Translate 130 English placeholders and replace 154 reviewed wrong-language or mixed-language values with Macedonian. Preserve all other translations, exact placeholders, parsed constants and markup. Longer Macedonian technical descriptions and legacy corrections have lower confidence pending native-speaker review.

All 306 translation/Blockly suites, 246 catalog inventories and 21 preservation checks pass. Three browser cases pass syntax and discovery; runtime execution remains unverified without the app and browser system dependencies. Record 317 additional cross-locale matches for individual language review rather than treating placeholder completion as fluency.

Thanks to xet7.
2026-10-10 19:45:44 +03:00
Lauri Ojansivu 206057b690 Document Bulgarian translation fills and validation
Record the 110-value batch and current full placeholder backlog.

Thanks to xet7.
2026-10-10 19:39:40 +03:00
Lauri Ojansivu bcd043e676 Translate remaining current Bulgarian strings
Fill 110 English placeholders while preserving existing translations, established Bulgarian terminology and exact tokens. Longer technical wording remains provisional pending speaker review.

All 306 translation/Blockly suites, 246 catalog inventories and 21 preservation checks pass. Three browser cases pass syntax and discovery; runtime execution remains unverified without the app and browser system dependencies.

Thanks to xet7.
2026-10-10 19:39:10 +03:00
Lauri Ojansivu e046eac309 Document Croatian Serbian and Slovenian translation fills
Record the 440-value batch and validation, and refresh the full remaining placeholder count.

Thanks to xet7.
2026-10-10 19:35:51 +03:00
Lauri Ojansivu 5a68457ad6 Translate current Croatian Serbian and Slovenian strings
Fill 440 English placeholders across four catalogs, preserving existing translations, established terminology and exact placeholders. Longer technical wording remains provisional pending speaker review.

All 306 translation/Blockly suites, 246 catalog inventories and 21 preservation checks pass. Twelve browser cases pass syntax and discovery; runtime execution remains unverified without the app and browser system dependencies.

Thanks to xet7.
2026-10-10 19:35:22 +03:00
Lauri Ojansivu 044288ad5b Document Vietnamese fills and full translation backlog
Record the 414-value batch and validation. Include pending Transifex keys in the reported full remaining scope.

Thanks to xet7.
2026-10-10 19:30:07 +03:00
Lauri Ojansivu 5a35e57aaf Translate remaining current Vietnamese strings
Fill 414 English placeholders across vi and vi-VN, preserving existing translations and exact tokens. Cover imports, linked fields, notifications, Scrum planning and recovery choices. Longer technical wording remains provisional pending speaker review.

All 305 translation/Blockly suites, 246 catalog inventories and 21 preservation checks pass. Six browser cases pass syntax and discovery; runtime execution remains unverified without the app and browser system dependencies. Record the full remaining placeholder inventory, including pending Transifex keys.

Thanks to xet7.
2026-10-10 19:29:36 +03:00
github-actions[bot] 7e707e8c63 Bump versions for v12.27 2026-10-10 16:26:16 +00:00
Lauri Ojansivu 9498840c55 Document Basque translation review and validation
Record the current fill completion and follow-up corrections without editing the released section.

Thanks to xet7.
2026-10-10 19:25:31 +03:00
Lauri Ojansivu 480e3639f6 Refine and validate current Basque translations
Review the 196-placeholder batch included in e595f9a83b and correct noun forms in 23 newly filled values. Preserve all pre-existing translations. Longer technical wording remains provisional pending speaker review.

All 305 translation/Blockly suites, 246 catalog inventories and 21 preservation checks pass; the focused Basque regression passes after corrections. Three browser cases pass syntax and discovery, with runtime execution unverified without the app and browser system dependencies.

Thanks to xet7.
2026-10-10 19:24:59 +03:00
Lauri Ojansivu f3a231fe38 Prepare v12.27 release 2026-10-10 19:23:05 +03:00
Lauri Ojansivu e595f9a83b Updated translations. 2026-10-10 19:22:45 +03:00
Lauri Ojansivu 0cc6997440 Merge remote-tracking branch 'origin/main' 2026-10-10 19:22:22 +03:00
Lauri Ojansivu f208bf1b29 Document current Galician translation fills
Record the 220-value batch, validation and remaining all-language audit work.

Thanks to xet7.
2026-10-10 19:19:18 +03:00
Lauri Ojansivu c5f1ea975c Translate remaining current Galician strings
Fill 220 English placeholders across both Galician catalogs while preserving existing translations, placeholders and parsed constants. Longer technical wording is provisional pending speaker review.

All 305 translation/Blockly suites, 246 catalog inventories and 21 human-preference checks pass. Six browser cases pass syntax and discovery; runtime execution remains unverified without the local app and browser dependencies.

Thanks to xet7.
2026-10-10 19:18:47 +03:00
Lauri Ojansivu 36ad88b5f7 Document current Valencian translation completion
Record regional fills, shared native label handling, regression coverage and remaining review limits in Upcoming.

Thanks to xet7.
2026-10-10 19:15:08 +03:00
Lauri Ojansivu b6261f8a46 Translate remaining current Valencian strings
Fill 109 English placeholders using reviewed regional forms, preserving existing translations and exact tokens. Retain the native shared Dates label with locale- and source-specific guards. Longer technical and regional wording remains provisional pending speaker review.

Verify 305 translation/Blockly suites, 246 catalog inventories and 21 human-preference checks. Register three browser cases using ca-valencia; syntax and discovery pass, but execution remains unverified without the local app and browser dependencies.

Thanks to xet7.
2026-10-10 19:14:36 +03:00
Lauri Ojansivu b7fa26f929 Document current Catalan translation completion
Record Catalan fills, shared native label handling, regression coverage and remaining review limits in Upcoming.

Thanks to xet7.
2026-10-10 19:11:08 +03:00
Lauri Ojansivu 9485d19719 Translate remaining current Catalan strings
Fill 218 English placeholders across ca and ca_ES, preserving existing translations and exact tokens. Retain the native shared Dates label with locale- and source-specific guards. Longer technical wording remains provisional pending speaker review.

Verify 305 translation/Blockly suites, 246 catalog inventories and 21 human-preference checks. Register six browser cases; syntax and discovery pass, but execution remains unverified without the local app and browser dependencies.

Thanks to xet7.
2026-10-10 19:10:35 +03:00
Lauri Ojansivu fea58e1312 Document which databases WeKan supports, and how each is tested.
docs/Databases/Supported.md is the one list: FerretDB v1 with SQLite (the
default everywhere), PostgreSQL (works), MySQL, MariaDB and SAP HANA
(experimental), FerretDB 2 (not tested), and MongoDB 6, 7, 8 and 9 - with
what checks each: the 110-query conformance catalogue answers the same on
all of them, and WeKan's own tests run on Meteor's bundled MongoDB 8 and on
FerretDB. It also says what to know about MongoDB versions: the Linux
kernel 6.19-7.0.13 refusal and its fixes, AVX, the driver, and upgrades.

The README and the Status section of CHANGELOG.md gain a Databases list,
docs/Databases/README.md links the page, the Meteor 2.14 compatibility guide
is marked historical, and the conformance page counts 110 cases and the
MongoDB backends. CLAUDE.md and AGENTS.md describe the new Status block.

Thanks to xet7 !
2026-10-10 19:07:14 +03:00
Lauri Ojansivu 61586e3da5 Document current Greek translation completion
Record Greek fills, regression coverage and remaining review limits in Upcoming.

Thanks to xet7.
2026-10-10 19:05:58 +03:00
Lauri Ojansivu 7f26c8ee12 Translate remaining current Greek strings
Fill 220 English placeholders across el and el-GR while preserving existing translations and exact tokens. Longer technical wording remains provisional pending speaker review.

Verify 305 translation/Blockly suites, 246 catalog inventories and 21 human-preference checks. Register six browser cases; syntax and discovery pass, but execution remains unverified without the local app and browser dependencies.

Thanks to xet7.
2026-10-10 19:05:30 +03:00
Lauri Ojansivu e7affef910 Updated ChangeLog: MongoDB 6 and 7 conformance.
Thanks to xet7 !
2026-10-10 19:02:50 +03:00
Lauri Ojansivu debd1d876d Database conformance: compare MongoDB 6 and 7 too.
Beside MongoDB 8 and 9, the conformance run that EVERYTHING runs now also
asks the real MongoDB 6 (mongo:6.0) and MongoDB 7 - the mongo:7 image read
from docker-compose-mongodb-v7.yml, the one WeKan's own MongoDB install runs.
All eight agree on all 110 cases: MongoDB 6.0.28, 7.0.43, 8.0.32 and 9.0.2,
and FerretDB on SQLite, PostgreSQL, MySQL and MariaDB.

Thanks to xet7 !
2026-10-10 19:02:36 +03:00
Lauri Ojansivu 0b5e938db4 Document current Malay translation completion
Record Malay fills, regression coverage and remaining review limits in Upcoming.

Thanks to xet7.
2026-10-10 19:02:02 +03:00
Lauri Ojansivu a0c7a0a40a Translate remaining current Malay strings
Fill 352 English placeholders across ms and ms-MY while preserving existing translations and exact tokens. Cover current linking, import and notification features plus LDAP and recovery. Longer technical wording remains provisional pending speaker review.

Verify 305 translation/Blockly suites, 246 catalog inventories and 21 human-preference checks. Register six browser cases; syntax and discovery pass, but execution remains unverified without the local app and browser dependencies.

Thanks to xet7.
2026-10-10 19:01:33 +03:00
Lauri Ojansivu 937cc77218 Document current Indonesian translation completion
Record Indonesian fills, regression coverage and remaining review limits in Upcoming.

Thanks to xet7.
2026-10-10 18:57:54 +03:00
Lauri Ojansivu ef6a8178d1 Translate remaining current Indonesian strings
Fill 176 English placeholders while preserving existing translations and exact tokens. Cover current linking, import and notification features plus LDAP and recovery. Longer technical wording remains provisional pending speaker review.

Verify 305 translation/Blockly suites, 246 catalog inventories and 21 human-preference checks. Register three browser cases; syntax and discovery pass, but execution remains unverified without the local app and browser dependencies.

Thanks to xet7.
2026-10-10 18:57:18 +03:00
Lauri Ojansivu 7bd1042417 Updated ChangeLog: FerretDB matches MongoDB, MongoDB 8 and 9 conformance, build.bat parity.
The Upcoming section after v12.26 gets its bug fix and developer-facing
subsections ahead of the translations, a translations subsection header, the
closing line thanking translators, and an In short covering all of it. Long
TODO Later lines are wrapped at 80 columns (whitespace only).

Thanks to xet7 !
2026-10-10 18:56:09 +03:00
Lauri Ojansivu c454f8567d build.bat: do what build.sh does, not only offer the same menu.
The menu checks already matched every entry; these labels existed but did
less than their build.sh twins:

- Mocha runs with --full-app, EMAIL_RECEIPT_SWEEP_INTERVAL_MS, WRITABLE_PATH
  and its own METEOR_LOCAL_DIR, and returns its exit status instead of 0.
- `build.bat --run-everything [mode]` works, as `./build.sh --run-everything`.
- The floating-promises guard fails instead of only warning.
- Playwright browsers live in .tools\ms-playwright and a single-browser run
  sets WEKAN_PLAYWRIGHT_PROJECT; .tools\.meteor is on PATH.
- CUSTOM PORT + SUBDOMAIN: a bare name is NAME.localhost, the port is
  checked, a trailing slash is removed, and the WEKAN_DEV_* overrides work.
- The trace dev server uses the computed heap, not a fixed 8192 MB.
- The Docker image tag is read from the compose file; "Kill all dev
  servers" also stops `rspack build --watch`.
- The build sets the same diagnostics (npm verbose, METEOR_PROFILE, module
  resolution, the command-output tracer), supports
  WEKAN_BUILD_HEAP_SNAPSHOT, and explains an out-of-heap failure.
- Setup installs Node.js and npm at the Dockerfile's NODE_VERSION and
  NPM_VERSION. build.sh pinned npm@12.0.2 by hand while the Dockerfile says
  12.2.0; it now reads NPM_VERSION too.

tests/buildScriptParity.test.cjs checks each of these in both scripts.

Thanks to xet7 !
2026-10-10 18:55:04 +03:00
Lauri Ojansivu a7ed9ac57f Document current Korean translation completion
Record Korean fills, regression coverage and remaining review limits in Upcoming.

Thanks to xet7.
2026-10-10 18:53:43 +03:00
Lauri Ojansivu a56a3abb9e Translate remaining current Korean strings
Fill 412 English placeholders across ko and ko-KR, preserving existing translations and exact tokens. Cover current linking, import and notification features plus planning and recovery. Longer technical wording remains provisional pending speaker review.

Verify 305 translation/Blockly suites, 246 catalog inventories and 21 human-preference checks. Register six browser cases; syntax and discovery pass, but execution remains unverified without the local app and browser dependencies.

Thanks to xet7.
2026-10-10 18:53:23 +03:00
Lauri Ojansivu 277445e6f2 Document current Hiragana fills and remaining script audit
Record completed English placeholder fills, script regressions and older review candidates in Upcoming.

Thanks to xet7.
2026-10-10 18:49:30 +03:00
Lauri Ojansivu fb336423fd Translate current Japanese Hiragana placeholders
Fill 206 English entries with hiragana prose and preserve technical literals and tokens. Longer technical wording and spacing remain provisional pending reader review. Record 3641 older kanji or katakana values as script-review candidates, not completed conversion.

Verify 305 translation/Blockly suites, 246 catalog inventories and 21 human-preference checks. Register three browser cases using the ja-Hira runtime tag; syntax and discovery pass, but execution remains unverified without the local app and browser dependencies.

Thanks to xet7.
2026-10-10 18:49:04 +03:00
Lauri Ojansivu 14b01778b4 Document standard Japanese translation completion
Record Japanese fills, regression coverage and the remaining Hiragana script review in Upcoming.

Thanks to xet7.
2026-10-10 18:43:52 +03:00
Lauri Ojansivu 08cba19eac Database conformance: compare MongoDB 8 and 9 too, in EVERYTHING.
releases/db-conformance.sh, which EVERYTHING runs from build.sh and, through
releases/run-everything.sh, from build.bat, now also runs the query catalogue
against the real MongoDB 8 and MongoDB 9 servers (mongo:8.0 and mongo:9.0),
queried directly with no FerretDB in between. All FerretDB backends could
agree with each other and still all be wrong; beside MongoDB they did not:
$group gave every accumulator after the first an empty group, $min/$max took
null as a value, and capped max was not enforced at once - all fixed in
wekan/FerretDB. WeKan's e-mail outbox report, one $group with several
accumulators, was affected on FerretDB.

The $text case now creates the text index MongoDB requires, and records the
score by type only, since FerretDB's score is its own by design. On a Linux
kernel MongoDB refuses (6.19 to 7.0.13), its backend is a SKIP with the
reason, not an error. The run: SQLite, MongoDB 8.0.32, MongoDB 9.0.2,
PostgreSQL, MySQL and MariaDB answer all 110 cases the same.

buildScriptParity: releases/translations/audit-short-prose.mjs is a
read-only review queue, run directly, like audit-progress.mjs.

Thanks to xet7 !
2026-10-10 18:43:44 +03:00
Lauri Ojansivu 9b4d16b1ce Translate remaining current standard Japanese strings
Fill 412 English placeholders across ja and ja-JP, preserving existing translations and exact tokens. Cover current linking, import and notification features plus planning and recovery. Longer technical wording remains provisional pending speaker review. The Hiragana catalog remains a separate script and translation follow-up.

Verify 304 translation/Blockly suites, 246 catalog inventories and 21 human-preference checks. Add five browser cases alongside existing Japanese notification coverage; six cases pass syntax and discovery, but execution remains unverified without the local app and browser dependencies.

Thanks to xet7.
2026-10-10 18:43:31 +03:00
Lauri Ojansivu b338c8e171 Document current Russian translation completion
Record Russian fills, regression coverage and remaining review limits in Upcoming.

Thanks to xet7.
2026-10-10 18:38:41 +03:00
Lauri Ojansivu e1774f913c Translate remaining current Russian strings
Fill 330 English placeholders across three Russian catalogs while preserving existing translations and exact source tokens. Longer technical wording remains provisional pending speaker review.

Verify 304 translation/Blockly suites, 246 catalog inventories and 21 human-preference checks. Add eight browser cases alongside existing Russian notification coverage; nine cases pass syntax and discovery, but execution remains unverified without the local app and browser dependencies.

Thanks to xet7.
2026-10-10 18:38:19 +03:00
Lauri Ojansivu cb8594f682 Document current Ukrainian translation completion
Record Ukrainian fills, regression coverage and remaining review limits in Upcoming.

Thanks to xet7.
2026-10-10 18:35:09 +03:00
Lauri Ojansivu 782a778f28 Translate remaining current Ukrainian strings
Fill 220 English placeholders across both Ukrainian catalogs while preserving existing translations and exact source tokens. Longer technical wording remains provisional pending speaker review.

Verify 304 translation/Blockly suites, 246 catalog inventories and 21 human-preference checks. Add five browser cases alongside existing Ukrainian notification coverage; six cases pass syntax and discovery, but execution remains unverified without the local app and browser dependencies.

Thanks to xet7.
2026-10-10 18:34:49 +03:00
Lauri Ojansivu 6a34711770 Document current Turkish translation completion
Record Turkish fills, regression coverage and remaining review limits in Upcoming.

Thanks to xet7.
2026-10-10 18:31:17 +03:00
Lauri Ojansivu 05a077cbbe Translate remaining current Turkish strings
Fill 110 English placeholders while preserving existing translations and exact source tokens. Longer technical wording remains provisional pending speaker review.

Verify 304 translation/Blockly suites, 246 catalog inventories and 21 human-preference checks. Add two browser cases alongside existing Turkish notification coverage; three cases pass syntax and discovery, but execution remains unverified without the local app and browser dependencies.

Thanks to xet7.
2026-10-10 18:30:57 +03:00
Lauri Ojansivu 2b823a9125 Document Romanian fills and field label corrections
Record Romanian translation scope, regression coverage and remaining review limits in Upcoming.

Thanks to xet7.
2026-10-10 18:27:46 +03:00
Lauri Ojansivu ab22344f91 Fill current Romanian strings and correct Italian field labels
Translate 220 English placeholders across Romanian catalogs and correct two Italian custom-field labels. Preserve other existing translations and exact source tokens. Longer technical wording remains provisional pending speaker review.

Verify 304 translation/Blockly suites, 246 catalog inventories and 21 human-preference checks. Register six browser cases, checked by syntax and discovery; execution remains unavailable without the local app and browser dependencies.

Thanks to xet7.
2026-10-10 18:27:25 +03:00
Lauri Ojansivu da36723bc0 Document current Hungarian translation completion
Record Hungarian fills, regression coverage and remaining review limits in Upcoming.

Thanks to xet7.
2026-10-10 18:24:06 +03:00
Lauri Ojansivu 0ec3ab7731 Translate remaining current Hungarian strings
Fill 110 English placeholders while preserving existing translations and exact source tokens. Longer technical wording remains provisional pending speaker review.

Verify 304 translation/Blockly suites, 246 catalog inventories and 21 human-preference checks. Register three browser cases, checked by syntax and discovery; execution remains unavailable without the local app and browser dependencies.

Thanks to xet7.
2026-10-10 18:23:47 +03:00
Lauri Ojansivu 361ae847ea Document Slovak fills and Czech wording corrections
Record translation scope, regression coverage and remaining review limits in Upcoming.

Thanks to xet7.
2026-10-10 18:20:27 +03:00
Lauri Ojansivu 1d76d08c7f Fill current Slovak strings and correct Czech seed wording
Translate 110 English placeholders and replace 198 Czech or mixed-language values with Slovak. Preserve other existing values and every source token. Longer technical phrasing remains provisional pending speaker review; this is not a full audit of older Slovak text.

Verify 304 translation/Blockly suites, 246 catalog inventories and 21 human-preference checks. Add three browser cases, verified by syntax and discovery only; execution remains unavailable without the local app and browser dependencies.

Thanks to xet7.
2026-10-10 18:20:05 +03:00
Lauri Ojansivu 6a07089fdd Document current Czech translation completion
Record Czech catalog fills, reuse provenance, regression coverage and remaining review limits in Upcoming.

Thanks to xet7.
2026-10-10 18:15:27 +03:00
Lauri Ojansivu 0ac133b2f4 Translate remaining current Czech strings
Fill 316 English placeholders across Czech catalogs, including 96 regional values reused from existing base Czech translations. Preserve existing translated values and source tokens. Longer technical wording remains provisional pending speaker review.

Verify 304 translation/Blockly suites, 246 catalog inventories and 21 human-preference checks. Register and syntax-check six browser cases; browser execution remains unverified without the local app and system dependencies.

Thanks to xet7.
2026-10-10 18:15:07 +03:00
Lauri Ojansivu 0d80fb4d7e Document current Polish translation completion
Record Polish catalog fills, reuse provenance, regression coverage and remaining review limits in Upcoming.

Thanks to xet7.
2026-10-10 18:11:31 +03:00
Lauri Ojansivu ff097539bf Translate remaining current Polish strings
Fill 316 English placeholders across Polish catalogs, including 96 regional values reused from existing base Polish translations. Preserve all existing translated values and source tokens. Longer technical wording remains provisional pending speaker review.

Verify 304 translation/Blockly suites, 246 catalog inventories and 21 human-preference checks. Register and syntax-check six browser cases; browser execution remains unverified without the local app and system dependencies.

Thanks to xet7.
2026-10-10 18:11:06 +03:00
Lauri Ojansivu 35c26ae472 Document current Dutch translation completion
Record the 220-string batch, validation and remaining all-language work in Upcoming.

Thanks to xet7.
2026-10-10 18:07:29 +03:00
Lauri Ojansivu 6f18ec01d9 Translate remaining current Dutch strings
Fill 220 English values across nl and nl-NL. Preserve existing translations, exact source tokens and workflow literals. Extend full-current-fill checks and representative UI coverage for linked cards, imports and notifications.

All 304 translation/Blockly suites, 246 catalog inventories and 21 preservation checks pass. Six browser cases pass syntax and discovery; execution remains unverified because the local app and browser dependencies are unavailable. Longer technical wording remains provisional pending speaker review.

Thanks to xet7.
2026-10-10 18:07:04 +03:00
Lauri Ojansivu 911278d7ad Document current Portuguese translation completion
Record the 440-string regional batch, validation and remaining all-language work in Upcoming.

Thanks to xet7.
2026-10-10 18:03:54 +03:00
Lauri Ojansivu 465b62605d Translate current strings across Portuguese locale variants
Fill 440 English values across pt, pt-BR, pt-PT and pt_PT. Preserve existing translations, exact source tokens and workflow literals. Use European and Brazilian terminology in the corresponding catalogs and extend full-current-fill and regional meaning checks.

All 304 translation/Blockly suites, 246 catalog inventories and 21 preservation checks pass. Twelve browser cases pass syntax and discovery; execution remains unverified because the local app and browser dependencies are unavailable. Longer technical and regional wording remains provisional pending speaker review.

Thanks to xet7.
2026-10-10 18:03:27 +03:00
Lauri Ojansivu 11a8aa948e Document current Spanish translation completion
Record the 990-string batch, validation and remaining all-language work in Upcoming.

Thanks to xet7.
2026-10-10 17:59:54 +03:00
Lauri Ojansivu 5146f9365c Translate current strings across Spanish locale variants
Fill 990 English values across nine Spanish catalogs. Preserve existing regional translations, source tokens and parsed constants. Extend full-current-fill and meaning checks for linked cards, imports, notifications and workflows.

All 304 translation/Blockly suites, 246 catalog inventories and 21 preservation checks pass. Twenty-seven browser cases pass syntax and discovery; execution remains unverified because the local app and browser dependencies are unavailable. New wording uses neutral Spanish; regional style and longer technical wording remain provisional pending speaker review.

Thanks to xet7.
2026-10-10 17:59:31 +03:00
Lauri Ojansivu a700ebab7e Document current French translation completion
Record the 540-string batch, exact shared French terms and validation in Upcoming.

Thanks to xet7.
2026-10-10 17:56:16 +03:00
Lauri Ojansivu 78bceed7d7 Translate current strings across French locale variants
Fill 540 English values across five French catalogs. Preserve existing translations, tokens and parsed constants. Retain Dates and __count__ minutes as correct French with locale-specific exact-source exceptions, and test source changes and locale isolation.

All 304 translation/Blockly suites, 246 catalog inventories and 21 preservation checks pass. Fifteen representative browser cases pass syntax and discovery; execution remains unverified because the local app and browser dependencies are unavailable. Longer technical and regional wording remains provisional pending speaker review.

Thanks to xet7.
2026-10-10 17:55:50 +03:00
Lauri Ojansivu 4b1d675096 Document current German translations in Upcoming release
Record the 440-string fill and 86 Swiss spelling corrections separately from the prepared v12.26 release.

Thanks to xet7.
2026-10-10 17:49:29 +03:00
Lauri Ojansivu ee9fbe1cfd Translate current strings across German locale variants
Fill 440 English values across de, de-AT, de-CH and de_DE. Normalize 86 older Swiss German values from ß to ss without otherwise changing their wording. Preserve all other existing translations and exact source placeholders.

All 304 translation/Blockly suites pass; 16 German/Blockly suites pass after spelling corrections. All 246 catalog inventories and 21 preservation checks pass. Twelve browser cases pass syntax and discovery; execution remains unverified because the local app and browser dependencies are unavailable. Longer technical wording remains provisional pending speaker review.

Thanks to xet7.
2026-10-10 17:48:37 +03:00
github-actions[bot] 5b165c1102 Bump versions for v12.26 2026-10-10 14:48:19 +00:00
Lauri Ojansivu 9463090ce7 Prepare v12.26 release 2026-10-10 17:45:58 +03:00
Lauri Ojansivu 18e7270b5c Document current Italian translation completion
Record the 110-string batch, validation and remaining all-language work.

Thanks to xet7.
2026-10-10 17:45:09 +03:00
Lauri Ojansivu c5fc636117 Translate remaining current Italian strings
Fill 110 English placeholders for linked cards, imports, webhooks, notifications and workflows. Preserve existing translations, source tokens and parsed constants. Extend the full-current-fill gate and representative browser coverage.

All 304 translation/Blockly suites, 246 catalog inventories and 21 preservation checks pass. Three browser scenarios pass syntax and discovery; execution remains unverified because the local app and browser dependencies are unavailable. Longer technical wording remains provisional pending speaker review.

Thanks to xet7.
2026-10-10 17:44:46 +03:00
Lauri Ojansivu ca76283671 Document Norwegian Bokmål and Danish current translation completion
Record the 220-string batch, its validation and remaining all-language work.

Thanks to xet7.
2026-10-10 17:41:40 +03:00
Lauri Ojansivu bda8d389e8 Translate remaining current Norwegian Bokmål and Danish strings
Fill 110 English placeholders per catalog for linked cards, imports, webhooks, notifications and workflows. Preserve existing translations, exact source tokens and workflow literals; extend full-current-fill and representative UI regression coverage.

All 304 translation/Blockly suites, 246 catalog inventories and 21 preservation checks pass. Six browser cases pass syntax and discovery; execution remains unverified because the local app and browser dependencies are unavailable. Longer technical wording remains provisional pending speaker review.

Thanks to xet7.
2026-10-10 17:41:15 +03:00
Lauri Ojansivu 636bafefd8 Document Finnish and Swedish current translation completion
Record the 220-string batch, validation and remaining all-language work.

Thanks to xet7.
2026-10-10 17:37:35 +03:00
Lauri Ojansivu b71d83ce7e Translate remaining current Finnish and Swedish strings
Fill 110 English placeholders per language for linked cards, imports, webhooks, notifications and Wrike workflows. Preserve existing translations, exact source tokens and workflow literals; extend full-current-fill and UI regression coverage.

All 304 translation/Blockly suites, 246 catalog inventories and 21 preservation checks pass. Six representative browser cases pass syntax and discovery; execution remains unverified because the local app and browser dependencies are unavailable. Longer technical wording remains provisional pending speaker review.

Thanks to xet7.
2026-10-10 17:37:10 +03:00
Lauri Ojansivu 43ce8e4de1 Document current Esperanto translation completion
Record the 227-string batch, its validation and the remaining all-language work.

Thanks to xet7.
2026-10-10 17:32:41 +03:00
Lauri Ojansivu a5db842c0e Translate remaining current Esperanto strings
Fill 227 English placeholders for imports, linked fields, notifications, Scrum planning and recovery. Preserve existing translations, source tokens, external menu names and parsed literals. Extend the full-current-fill gate and representative UI cases.

Longer technical phrasing remains provisional pending speaker review. All 304 translation/Blockly suites, 246 catalog inventories and 21 preservation checks pass. Three browser cases pass syntax and discovery; execution is unverified because the local app and browser dependencies are unavailable.

Thanks to xet7.
2026-10-10 17:32:13 +03:00
Lauri Ojansivu dfaa4ed048 Document completion of notification time labels across locales
Link the verified translation batch and refresh short-prose review counts while keeping the wider translation work open.

Thanks to xet7.
2026-10-10 17:26:12 +03:00
Lauri Ojansivu 306b5dc927 Translate remaining notification time labels in every locale
Fill 150 English values in 75 catalogs and guard both time labels in all 234 non-English locale paths. Preserve existing translations and exact source tokens. Extend representative notification UI coverage.

Minority-language wording is provisional, especially Acehnese, Aymara, Bambara, Buryat, Cherokee, Chuvash, Dzongkha, Ewe, Fulfulde, Inuktitut, Greenlandic, Konkani, Kashmiri, Ladin, Nahuatl, Aromanian, Sakha, Tigre, Klingon, Tongan, Arabic-script Uzbek, Veps, Volapük, Wolaytta and Tamazight. Record lexical sources and low-confidence wording in the audit.

Validation: 304 translation/Blockly suites, 246 catalog inventories and 21 preservation checks pass. Browser tests pass syntax/discovery only; local app unavailable.

Thanks to xet7.
2026-10-10 17:25:42 +03:00
Lauri Ojansivu 7973afe32d Record notification time-label coverage and remaining locales
Thanks to xet7 !
2026-10-10 17:18:00 +03:00
Lauri Ojansivu 065b0e0ec3 Translate notification time labels across 151 locale paths
Fill daily send-time and quiet-hours end labels in 149 JSON files, also covering two existing aliases. Preserve every prior translation and all English variants. Validate the labels against their time-input context and add representative localized UI coverage.

All 304 translation/Blockly suites, 246 catalog key/token inventories and 21 preservation checks pass. Eight browser cases pass syntax/discovery checks; execution remains unverified. Regional phrasing remains provisional, especially Amharic, Khmer, Burmese, Malagasy and Uyghur. Sources and the remaining 75 locale paths are recorded in the translation audit.

Thanks to xet7 !
2026-10-10 17:17:17 +03:00
Lauri Ojansivu 77912c8833 Record Papiamento coverage and passing translation checks
Thanks to xet7 !
2026-10-10 17:12:55 +03:00
Lauri Ojansivu b25cadef3e Translate current Papiamento strings and repair mixed-language guidance
Fill 112 English values and correct five mixed-Spanish permission and search descriptions. Preserve role limits, source placeholders and all other prior translations. Extend linking, identity mapping and delivery regression coverage.

All 303 translation/Blockly suites pass. After final wording corrections, the three targeted Papiamento/audit suites pass; all 246 catalog inventories and 21 preservation checks pass. Three browser cases pass syntax/discovery checks but remain unexecuted. Longer technical wording remains low confidence pending speaker review; sources are recorded in the audit.

Thanks to xet7 !
2026-10-10 17:12:35 +03:00
Lauri Ojansivu e3ee4ce9ef Record Zulu translation coverage and remaining backlog
Thanks to xet7 !
2026-10-10 17:08:32 +03:00
Lauri Ojansivu c87837d19d Translate current Zulu strings and clarify import identities
Fill 112 English values in each Zulu catalog and clarify one Xhosa import label. Keep existing Zulu translations, source placeholders and Wrike API literals intact. Add link, permission, privacy and identity-substitution regression coverage.

Long Zulu technical sentences remain low confidence; terminology references are recorded in the audit. Broad suites pass 302/303 with Papiamento still incomplete. All 246 catalog inventories and 21 preservation checks pass; targeted Zulu and Xhosa checks pass. Seven localized browser cases are discovered and syntax-checked; execution remains unverified.

Thanks to xet7 !
2026-10-10 17:08:08 +03:00
Lauri Ojansivu 6eef205c87 Record Wu and Xhosa translation coverage
Thanks to xet7 !
2026-10-10 17:03:49 +03:00
Lauri Ojansivu d91dfdfe87 Translate current Wu and Xhosa strings
Fill 224 English values, including short delivery labels, and correct four Xhosa field and swimlane labels. Preserve all other existing values and source placeholders. Extend current-fill, meaning and localized UI coverage.

Specialist vocabulary and longer sentences in both languages remain low confidence; research sources and older terminology work are recorded in the audit. Broad suites pass 301/303 with two remaining untranslated-catalog failures. All 246 catalog inventories and 21 preservation checks pass. Four browser scenarios pass syntax/discovery checks; execution remains unverified without a local app.

Thanks to xet7 !
2026-10-10 17:02:58 +03:00
Lauri Ojansivu c644635d66 Updated ChangeLog: v12.26 was not published, so its section is Upcoming again.
The v12.26 release stopped in its bump job before anything was published -
no GitHub Release and no tag - so per CLAUDE.md its heading is back to
"# Upcoming WeKan ® release" and the next run takes v12.26 again. The
translation entries written after it, under a second Upcoming section, move
into its Translations group, and the API docs fix is listed under Releases.

Thanks to xet7 !
2026-10-10 17:02:34 +03:00
Lauri Ojansivu ed4a3a86a1 API docs: a spread in a schema no longer stops the release.
The bump job of release-all.yml stopped in openapi/generate_openapi.py with
"AttributeError: 'NoneType' object has no attribute 'name'". Notification
delivery builds part of the Boards, Integrations, Settings and Users schemas
with `...deliverySchemaFields('notificationDelivery')`, and the generator
read the key of every schema property, which a spread does not have.

A spread is now documented as one optional object named by the helper's
first string argument - notificationDelivery on all four - and a spread with
no such name is skipped with a warning. The generated spec renders with
@redocly/cli. tests/openapiSchemaSpread.test.cjs runs the whole generator
when a Python with esprima is available, and fails on any schema spread in
models/ that would be left undocumented.

Thanks to xet7 !
2026-10-10 17:01:29 +03:00
Lauri Ojansivu b4212406db Record Lithuanian and Yiddish translation coverage
Thanks to xet7 !
2026-10-10 16:55:26 +03:00
Lauri Ojansivu 2d587d6c27 Translate current Lithuanian and Yiddish strings
Fill 224 English values and correct 12 malformed Lithuanian activity/card labels and 40 Yiddish customization mistranslations. Preserve source tokens, filter examples and correct existing translations. Include At/To in the short-prose audit.

Add current-catalog, semantic and placeholder checks and four localized UI scenarios. Broad suites pass 299/303; four existing untranslated-catalog failures remain. All 246 catalog inventories and 21 preservation checks pass. UI discovery passes; browser execution remains unverified. Yiddish technical compounds and long sentences are low confidence pending speaker review; sources are recorded in the translation audit.

Thanks to xet7 !
2026-10-10 16:55:03 +03:00
Lauri Ojansivu 320f7c15b0 Record Hawaiian placeholder coverage and remaining translation work
Thanks to xet7 !
2026-10-10 16:46:46 +03:00
Lauri Ojansivu fe1a734523 Complete the current Hawaiian translation fill
Translate 349 remaining values and retain 17 exact physical keyboard legends. Cover the full current catalog and short Blockly prose, and extend planning and recovery UI scenarios to Hawaiian.

Specialist programming, planning and recovery terminology is low confidence. The older malformed-text audit remains open. Translation suites pass 297/302 with five existing untranslated-catalog failures; 21 preservation checks pass. Browser scenarios were discovered and syntax-checked, not executed.

Thanks to xet7 !
2026-10-10 16:46:20 +03:00
Lauri Ojansivu 87322f6a83 Record Hawaiian math and procedure translation progress
Refresh the pending counts and describe validated math, function and navigation translations with remaining review explicitly open.

Thanks to xet7.
2026-10-10 16:36:30 +03:00
Lauri Ojansivu 18bb105f91 Translate Hawaiian Blockly math and procedure messages
Fill 171 English values for mathematics, functions, variables and accessibility controls. Retain source formulas and placeholders and distinguish bounds, return values and screen-reader states in regression coverage.

All 19 targeted suites, 21 preservation checks and 246 catalog token/order checks pass. Browser execution remains unverified. Hawaiian statistical, exponent and programming compounds remain low confidence pending fluent-speaker review.

Thanks to xet7.
2026-10-10 16:36:10 +03:00
Lauri Ojansivu d496bb85e9 Record Hawaiian Blockly list and logic translation progress
Refresh remaining translation counts and record the tested input, list and logic families without claiming all-language completion.

Thanks to xet7.
2026-10-10 16:31:26 +03:00
Lauri Ojansivu 714129c68f Translate Hawaiian Blockly inputs, lists and logic
Fill 186 English values, including short labels and list aliases omitted from ordinary reports. Preserve existing translations and numbered placeholders. Protect whole translated families and distinctions between getters, removals, copied lists and Boolean comparisons.

All 19 targeted suites, 21 preservation checks and 246 catalog token/order checks pass. Browser execution remains unverified. Hawaiian technical phrasing remains low confidence pending fluent-speaker review; remaining translation work stays open.

Thanks to xet7.
2026-10-10 16:31:06 +03:00
Lauri Ojansivu 2758243a60 Record Hawaiian import and Blockly translation progress
Update TODO counts and document the resumed all-language Blockly work, validation and remaining review.

Thanks to xet7.
2026-10-10 16:27:07 +03:00
Lauri Ojansivu e373cfb7a2 Translate Hawaiian imports, notifications and Blockly controls
Fill 169 English values while preserving existing translations, source tokens, external commands and import exclusions. Cover notification timing, webhook identity, LDAP, card visibility and Blockly color, loop and editor controls.

All 19 targeted suites and 21 preservation checks pass. Browser scenarios are registered but await a running application. Hawaiian programming compounds and longer technical wording remain low confidence pending speaker review; wider translation work remains open.

Thanks to xet7.
2026-10-10 16:26:43 +03:00
Lauri Ojansivu 79f7092c79 Record Hawaiian translation progress and remaining coverage
Document the local card-link and import mapping batch, updated pending queue and five existing current-fill test failures. Keep the all-language translation work open.

Thanks to xet7.
2026-10-10 16:20:03 +03:00
Lauri Ojansivu b2867896fb Translate Hawaiian card links and import mapping
Fill 61 English placeholders and repair eight malformed custom-field strings. Preserve source tokens and existing correct-language text. Extend positive, negative and browser regression coverage.

Hawaiian technical compounds and longer sentences remain low confidence pending fluent-speaker review. Targeted and preservation checks pass. The broad run passes 297 of 302 translation suites; five other catalog completion gates expose new untranslated keys. Browser execution awaits a running app.

Thanks to xet7.
2026-10-10 16:19:41 +03:00
Lauri Ojansivu 994ec6a0ae Prepare v12.26 release 2026-10-10 16:13:39 +03:00
Lauri Ojansivu 1184900407 Updated ChangeLog: CHANGELOG.md fits GitHub's Markdown limit.
Thanks to xet7 !
2026-10-10 16:12:02 +03:00
Lauri Ojansivu 5029b65382 Keep CHANGELOG.md under GitHub's 500 KiB Markdown limit, so GitHub shows it.
At 2.1 MB, GitHub showed "Sorry about that, but we can't show files that
are this big right now" for CHANGELOG.md. GitHub renders Markdown up to
500 KiB, shows only plain text above that, and nothing above about 2 MB;
the July to September archives, 683 KB to 2 MB, were shown as text only.

releases/changelog-archive.mjs now cuts by size instead of by month:
CHANGELOG.md keeps Status, Upcoming and the newest releases that fit in
450,000 bytes (345 KB now, two releases), and the rest move to
old-CHANGELOG/<year>/<MM>.md, a month over the same budget continuing in
<MM>-part2.md and on, whose parts link each other. Every archive part now
renders, except v12.13, one release of 537 KB in a part of its own. No
release section is changed: all 1,225 are where they were, byte for byte.

release-all.sh runs the archive after renaming Upcoming, keeping the newest
release, which release-notes.sh reads. tests/changelogArchive.test.cjs fails
when CHANGELOG.md or a part is over the budget. CLAUDE.md and AGENTS.md
describe the size rule instead of the current month.

Thanks to xet7 !
2026-10-10 16:11:51 +03:00
Lauri Ojansivu 55107a9214 Updated ChangeLog: translations in 172 languages, and the release changes.
Thanks to xet7 !
2026-10-10 16:06:02 +03:00
Lauri Ojansivu 3ec85c82d6 Release notes: a Translations group may give the count of its languages.
When too many languages were updated to list - more than 20 - the
Translations group gives "**Languages updated:** 172 languages." and the
release notes say "Updated translations at 172 languages." instead of a list
of one "172 languages" item. A listed language name loses a trailing full
stop. CLAUDE.md and AGENTS.md describe the count, and that a part of the
Upcoming section with nothing to say is left out.

Thanks to xet7 !
2026-10-10 16:05:47 +03:00
Lauri Ojansivu 4c0942ff56 Release: everything under the Upcoming heading is optional.
releases/release-all.sh stopped when the Upcoming section had no entries, no
**In short:** summary, or a Translations group without its
**Languages updated:** line, and releases/release-notes.sh failed on the
same in the workflow. Now only the "# Upcoming WeKan ® release" heading is
required - it is what becomes the new version. A missing part is a warning,
and the release notes leave it out: no In short section without a summary,
no language list without one (the translators are still thanked), and an
empty section still gets the thanks and the ChangeLog link.

Thanks to xet7 !
2026-10-10 16:02:33 +03:00
Lauri Ojansivu 93e83ec536 README: 164 locale catalogs have non-English text for over 90% of source keys.
The new interface strings of attached cards, linked custom fields, subtask
checkboxes, Calendar Mode and notification delivery are in English in every
locale until they are translated, which lowered the count from 191.

Thanks to xet7 !
2026-10-10 15:30:16 +03:00
Lauri Ojansivu cd4875b91a Updated ChangeLog: attached cards, linked custom fields, subtask checkboxes, Calendar Mode, notification delivery, and the #6751 and #6755 fixes.
Long lines are wrapped at 80 columns (whitespace only), so the format check
passes again.

Thanks to xet7 !
2026-10-10 15:30:04 +03:00
Lauri Ojansivu ab785acd1e Choose what notifications contain, and group and schedule them, for e-mail, the tray and webhooks.
One delivery model (models/lib/notificationDelivery.js) for every channel -
e-mail, the notification tray and outgoing webhooks - set in the
Notifications popup at Admin Panel, board and Member Settings scope, and in
each one-way webhook's form:

- Content. Webhooks: the text on or off, and which property groups are sent
  (standard, ids, names, links, actor, people, details), with checklist,
  list, swimlane and board names, before/after values, the card description
  and links added as opt-in groups. E-mail and tray: the classic sentence or
  a clear layout - the board as a link first, the actor and card in bold -
  and which parts are shown.
- Grouping: one message per event, per card, per board, or all together.
- Scheduling: immediately, every 5 minutes to 24 hours, or once a day, with
  optional quiet hours, in the time zone of whoever set it.

The most specific explicit setting wins: member, then board, then Admin
Panel for e-mail and tray; the webhook, then its board, then Admin Panel for
webhooks. A member can only leave their own name out of outgoing webhooks.
The built-in defaults keep today's behaviour. Grouped and scheduled webhooks
and e-mail go through durable queues that survive restarts; queued webhooks
use the SSRF guard and log a blocked address in Admin Panel - Problems.
Values of admin-only custom fields now reach only board admins in e-mail and
tray, and never a webhook.

The new strings are in English in every locale, pending Transifex.

Thanks to HaKePlan, Meeques and xet7 !

Fixes #3695,
Fixes #5171.
2026-10-10 15:28:26 +03:00
Lauri Ojansivu 746c097c9a Link the custom fields of two cards, so a change on one is carried to the other.
From a card's Custom Fields menu, "Link fields to another card" links it to
another card by a pasted card link or a title search, both ways or one way
(this card sends to a main card). Fields match by name and type; a dropdown
value is carried by its item name, a multi-select only when the other field
has every item, and fields on one card only (such as Remarks) stay there.
Linking fills only the other card's empty matching fields once.

A link is held on both cards and counts only when both sides match. Linking
needs read and edit rights on both cards; a card the user cannot read is "not
found". A carried value is written as the link's creator, recorded in
Activities and History, and only while that user may still read and edit both
cards; admin-only fields never take part. A loop guard stops echoes and rings,
with a depth limit and a write budget per change. A copied, imported or
restored card starts unlinked, a deleted card's links are removed from the
others, and a direct client write of the links is refused and trips a canary.

The new strings are in English in every locale, pending Transifex.

Thanks to MarcusDger and xet7 !

Fixes #5681.
2026-10-10 15:26:46 +03:00
Lauri Ojansivu a713154d70 Tick subtasks done from the parent card's Subtasks list, with done/total in its heading.
Each subtask row has a checkbox that marks the subtask card complete
(dueComplete, the card's own "Mark as complete"), and the Subtasks heading
shows "Subtasks (1/3)". A subtask is done when it is marked complete or
archived, which the minicard's subtask badge and "Hide completed subtasks"
now also count.

The setSubtaskDone method checks the edit right on the subtask itself, which
may be on another board; comment-only, read-only, worker and non-members are
refused, an assigned-only member may tick only a subtask assigned to them,
and an archived subtask is refused. The change is recorded in the subtask's
History like the card details toggle.

The new strings are in English in every locale, pending Transifex.

Thanks to zhoujunhaohao and xet7 !

Fixes #4693.
2026-10-10 15:25:16 +03:00
Lauri Ojansivu 8215191197 Give the Attach card popup its title, and audit attached cards in the inventories.
The attach-card work left four node suites failing: the Attach card popup had
no attachCardPopup-title, so it rendered with no header and no close button;
its submit button was missing from the menu action inventory; the new
attachedCardIds card field had no email content disposition; and a comment
carried a full Trello URL, which the release URL audit reports as a new
origin. The title is added in English to every locale, pending Transifex.

Thanks to xet7 !
2026-10-10 15:05:21 +03:00
Lauri Ojansivu 04a4f628e1 Turn keyboard shortcuts on for new users, and let d open a hovered card's due date.
The users schema stored profile.keyboardShortcuts: false for every new user,
while isKeyboardShortcuts() treats a missing value as on (keyboard shortcuts
are on by default). So a new account had shortcuts off until it turned them
off and on again. The schema default is now true, and a test pins every
profile boolean's schema default to the default its helper reads.

With no card opened, d on a hovered or selected minicard - the card the other
card shortcuts act on - now opens that card and its Due date editor, instead
of doing nothing until the card was opened. A card the user may not edit is
not opened, and nothing happens with shortcuts off.

Thanks to mimZD and xet7 !

Fixes #6755.
2026-10-10 15:03:31 +03:00
Lauri Ojansivu 4758908725 Added a design report on organizations, teams and their hierarchy.
What the open issues ask for, what the code does today (and the defects
found reading it), how other software manages groups and syncs them from
LDAP, SAML, OIDC and SCIM, and options for WeKan, with the decisions left
open for the maintainer.

Thanks to xet7 !
2026-10-10 15:02:37 +03:00
Lauri Ojansivu b7ba1e2075 Fill today green in the date popup, so it shows on every board theme.
The ring drawn in the day button's own text colour was hard to see on themes
whose day buttons are already a strong colour, such as wisteria's purple.
Today now has its own fill, green with white text, forced over every theme
(the custom theme colour sets the buttons' background with !important), with
a white inset ring and the underline kept for colour-blind users. A day that
is both today and selected keeps a visible green outline.

Thanks to mimZD and xet7 !

Fixes #6751.
2026-10-10 14:52:42 +03:00
Lauri Ojansivu f133ddbba1 Add Calendar Mode to the Board View menu, below Calendar: Trello's calendar.
Calendar Mode shows each card on the day it is due - the date only, no
time - with its labels in their colours and its whole title over as many
lines as it needs, and the cards of a day in the board's own order: by
their list's place, then their place in the list. A month or a week,
starting on the user's first day of the week, with Today, Previous and
Next; the board Filter applies, as on Calendar, and a card opens beside
the calendar. The grid and the day's order are models/lib/calendarMode.js.

A board that saved its Board View order before this view existed still
has the default order, with Calendar Mode in its place and the menu's
separators: a saved order that lacks only views added later
(VIEWS_ADDED_LATER) is the default order of its time. Any other saved
order is the user's own, and the new view goes at its end, as before.

Tests: tests/calendarMode.test.cjs (the month and week grid, the first
day of the week, the day's order, cards without a due date, the wiring),
tests/boardViewSettings.test.cjs (an order saved before Calendar Mode, and
a custom one), tests/boardViewMenu.test.cjs and
tests/playwright/specs/calendar-mode.e2e.js (the menu entry below
Calendar, labels and order on today's cell, Week, opening a card).

Thanks to tripledee and xet7 !

Fixes #3194.
2026-10-10 14:31:28 +03:00
Lauri Ojansivu 32fa89fdc3 Attach a card to a card, as Trello's card attachments, and import them from Trello.
A card can now have other cards attached to it, beside its files. In the
card's Attachments, + offers "Attach card": type part of a card title on
this board and pick it, or paste a card link (Copy card link gives one) to a
card on any board you can read. An attached card shows its title, board and
list, opens that card, and can be removed again.

- Data: cards.attachedCardIds, the attached cards' ids, at most 100
  (models/lib/attachedCards.js).
- Methods (server/models/attachedCards.js): attachCardToCard and
  detachCardFromCard need the right to edit the card, and only a card the
  user can read can be attached - its board's read rule and, for an
  assigned-only member, a card assigned to them. Any other is refused as
  "not found", the same as a card that does not exist. attachedCardsInfo
  shows an attached card's title only to a viewer who can read it; anybody
  else sees that one is there.
- Trello import: an attachment that links to a Trello card of the same
  export becomes an attached card; a link to a card of another board stays
  in the description under Links, as attached links always did.
- WeKan import: attached cards follow the cards that came in the same
  export to their new ids; the rest are dropped.

Tests: tests/attachedCards.test.cjs (the module, the permission rules and
the wiring), server/lib/tests/attachedCards.tests.js (attaching across
boards, refusing unreadable and assigned-only cards, a non-editor, and
hiding what a viewer cannot read) and tests/playwright/specs/attached-cards.e2e.js
(attach by title, refuse a hidden card, open, remove; and a Trello import).
Docs: docs/Features/Cards/Attachments/Attached-Cards.md.

Thanks to ricardoboss and xet7 !

Fixes #3257.
2026-10-10 14:24:44 +03:00
Lauri Ojansivu 8da410b74c Updated ChangeLog.
Thanks to GitHub CodeQL and xet7 !
2026-10-10 13:50:11 +03:00
Lauri Ojansivu eefc66185b Escape a Scrum helper name completely before matching it, and find incomplete escaping anywhere.
CodeQL alert 560 (incomplete string escaping) was
tests/scrumViewHelpers.test.cjs putting a helper name into a regular
expression with only "$" escaped, leaving the backslash and every other
special character as syntax. It now escapes all of them.

tests/incompleteEscaping.test.cjs finds the shape in every WeKan source and
test file: a backslash escape of some character in an expression that does
not also escape the backslash itself, including inside a template literal's
interpolation, where this one was. It catches the old line and the earlier
Plane link title of alert 555, and passes the complete escapes of the iCal,
PDF, file-name and card-link code.

Thanks to GitHub CodeQL and xet7 !
2026-10-10 13:49:27 +03:00
Lauri Ojansivu 3435b3208b Updated ChangeLog: the Excel exports stream, so TODO Later's in-memory list is empty.
@wekanteam/exceljs 4.7.4, with the streaming writer fix, is carried in
npm-packages/exceljs, so the card and board Excel exports stream and the
last place of #6745's list of big data held in memory is done. The list is
removed from TODO Later, and the Upcoming entry of the Excel change says the
release is installed.

Thanks to markusst1982 and xet7 !
2026-10-10 12:35:15 +03:00
Lauri Ojansivu 778b533b44 Updated ChangeLog.
Thanks to xet7 !
2026-10-10 12:22:29 +03:00
Lauri Ojansivu 207072476b Ask for the SAML popup in the SAML popup browser test.
SAML logs in by full-page redirect by default since the popup was found to
be blocked in iframes and on some phones; SAML_LOGIN_FLOW=popup still
chooses the popup. The test of the popup flow never asked for it, so
loginWithSaml navigated its own page away and the test lost its context.
It now saves loginFlow popup and waits for the client to have it;
tests/samlPopupErrors checks every spec driving the real popup does so.

Thanks to xet7 !
2026-10-10 12:21:44 +03:00
Lauri Ojansivu 6ec8773937 Scrum: release names on cards, the import-into-board preview and the import report show again.
- A card put into a release created after the card fields had cached the
  board's sprint and release names showed "Release:" with no name: saving a
  sprint or release does not change the board's revision the cache is keyed
  by. A shown record that names an ID the cache does not have now asks
  again, at most once per 30 s per set of missing IDs (models/lib/scrum.js
  unknownScrumNameIds).
- Importing Scrum planning into an existing board never showed its preview,
  and the Scrum import report showed every loss line empty: their helpers
  were registered on scrumReportTable instead of scrumView, which draws
  them. The preview also read its counts through scrumView's own helpers of
  the same names, and the import's refresh closed the panel before its
  result was seen; both are fixed.
tests/scrumViewHelpers.test.cjs checks every Scrum template registers the
helpers its own markup uses.

Thanks to xet7 !
2026-10-10 12:21:44 +03:00
Lauri Ojansivu 9cee923dbe Import placeholders take a person's name where the file gives one; import browser tests follow the people choice.
- A person seen first without a name - Taiga's story owner, given as an
  e-mail address - kept the address as the placeholder's name although a
  comment of theirs named them; a later name now replaces a name that is
  only the key, never a real one (tests/importMembersMode).
- The import browser tests still expected a comment by a file's person to
  read "name: text". Since every import asks who the people become, the
  default makes a placeholder user for them, and the comment is theirs with
  the text as written: the tests now check the text and that its author is
  a placeholder with that name who cannot log in. The placeholder test no
  longer asserts an account isActive the users schema does not store; its
  board membership is the inactive part, as before.
- The ClickUp test's CSV left a cell holding a comma unquoted, which ClickUp
  and WeKan's own export quote; it moved every later column. The Planner
  test now matches "Task 2" exactly, not Task 20 to Task 29 too.

Thanks to xet7 !
2026-10-10 12:21:36 +03:00
Lauri Ojansivu 62eb9c5c29 Text exports such as OPML are made from checked values, not stripped as HTML.
The export boundary passed every finished export through the HTML
sanitizer. A text document of a format's own - OPML, Leo's XML, Markdown,
CSV - is that format's syntax, not HTML: OPML keeps a card's title in
<outline text="...">, so its export held only the board's name, every such
export logged its own tags as unsafe markup, and a whole board was held to
the size of one string. A JSON export is checked as before; a text one is
made again from the cards and the board's, lists' and swimlanes' titles
passed through the boundary.

Thanks to xet7 !
2026-10-10 12:21:36 +03:00
Lauri Ojansivu e7be1ed082 Streamed attachments are stored again, and a Trello .zip brings its files.
- Meteor-Files 3's addFile is async - addFile(path, opts, proceedAfterUpload)
  returning the file - and takes no callback. addAttachmentFromStream passed
  one as its third argument, which was never called, so every streamed
  attachment waited forever: a WeKan or Trello .zip import, a board clone, a
  REST raw upload and a storage move stopped at their first file. It now
  awaits the promise, as copyFile does; tests/importAttachmentStream checks
  that no code hands addFile a callback.
- TrelloCreator rebuilt each attachment from a fixed list of fields that
  left out the zip entry the .zip route had matched it to, so every
  attachment fell back to downloading its Trello URL, which needs Trello's
  own login, and none arrived. The entry and size are kept now
  (tests/zipBombBleed); a board that fails in a .zip import is also logged.
- A file record without versions made the file route throw "reading
  'original'" and answer 500; it is a missing file, answered 404.

Thanks to xet7 !
2026-10-10 12:21:27 +03:00
Lauri Ojansivu 8834cecea9 Excel-based tool exports work again, and All Boards offers Export all boards in its bar.
- The Planner, Wrike, monday.com, Teamwork.com and Businessmap exports
  answered 500. The shared renderer required each workbook writer through a
  variable, require(path), which the bundler cannot resolve; each is now a
  literal require. tests/exportAllBoards checks that no server or model file
  requires a module by an expression, and the format tests pin the new form.
- Export all boards was a row of the All Boards sidebar's home view only,
  which nothing on the page opens since All Boards lost its hamburger: it was
  reachable only through Search and the back arrow. It is now a button in
  the page's own header bar, beside Search, opening the same popup.

Thanks to xet7 !
2026-10-10 12:21:27 +03:00
Lauri Ojansivu c23891dfff A board announcement can be dismissed: the board no longer lies over it.
The board canvas fills its wrapper from the top, so it lay over the
announcement banner above it, and its swimlane header took the clicks
meant for the banner's close button - nobody could dismiss an
announcement. While a banner shows, the wrapper is now a column: the
banner, then the canvas taking the rest; without one nothing changes.

The browser test checks that the close button is the element at its own
place and that the board starts below the banner, then dismisses it.

Thanks to xet7 !
2026-10-10 12:21:17 +03:00
Lauri Ojansivu 5e9c313f03 Right-click a Blocks block beside its label in the localized Blocks browser test.
At x=30 the label of a script with tall or wide glyphs - Bhojpuri, Tibetan,
Cherokee, Inuktitut, Kashmiri and eight more - covered the click point, and
Firefox gave the right-click to the text, so the context menu test timed out
in those 13 locales in Firefox only. The click is now inside the block, left
of where Blockly draws its label; all 13 pass in Firefox.

Thanks to xet7 !
2026-10-10 12:21:17 +03:00
Lauri Ojansivu adb46489a7 Read the board's own view in the view and upgraded-session browser tests.
Since the board view is remembered per board (#4906, #4256), a view chosen
on a board is saved in profile.boardViews[boardId], not in the shared
profile.boardView. The two Playwright specs still waited for the shared
value, so both timed out in every browser although the view was saved. They
now read the board's own entry.

Thanks to xet7 !
2026-10-10 12:21:17 +03:00
Lauri Ojansivu 5b723c111e A Scrum transfer's card that is only on its source board is "not matched", not "on another board".
Importing Scrum planning into an existing board reported every card of the
file's own source board that had no match on the target as belonging to
another board, so "card-not-matched" was all but unreachable for the usual
board-to-board transfer and the server test of the import failed. The
lookup for cards of other boards now leaves out the file's own board; a
card of a third board named in the file is still reported as another
board's and never written.

Thanks to xet7 !
2026-10-10 11:44:19 +03:00
Lauri Ojansivu d1de4a1fe6 Stored Sync rules bring a card's labels to another board as Card.copy and Card.move do.
Since #1759 a card copied or moved to another board brings its labels: a
label that board lacks is created there when the actor is its admin. The
stored (durable) rule copyCard, move to another board and move-all onto
another board still matched by name only and dropped the rest, so the
durable copy or move was no longer the ordinary one - the parity three
server tests check - and the card lost a label a direct copy would keep.

The durable plans now use the same planCrossBoardLabels with the rule's
actor. The labels to create are saved in the command (copy) or move
(single move, and each move-all unit), so a replay creates nothing twice:
each is pushed only when no label with its id is on that board yet, before
the card that names it is written. In a move-all, a label created for one
card is that board's label for the next, as when Card.move moves them one
by one. Commands saved before this, without the labels, still validate.

The server tests now expect the created label, once across a replay, and
the ordinary copy or move finding it; the card-move test gains the negative
of a mover who is not the new board's admin, where the label is dropped and
its activity removed. The command tests cover the new field, its validation
and an older command without it.

Thanks to xet7 !
2026-10-10 11:44:19 +03:00
Lauri Ojansivu 650849f98d Keep the release metadata, source audit and title-link guard green after Meteor 3.6-rc.0.
- Dockerfile METEOR_RELEASE and the snapcraft description named Meteor
  3.6-beta.3 and 3.6-beta.1 after .meteor/release moved to 3.6-rc.0, which
  tests/releaseVersionConsistency and tests/websiteVersionInfo refuse.
- package-lock.json gained eight npm funding and deprecation links with the
  @wekanteam packages now in npm-packages; they are metadata nothing fetches,
  allowed for that file only and exactly in releases/risk-baseline.json, as
  the earlier funding links are.
- tests/titleClickToEdit read the viewer's link handler in fixed 1,000 and
  1,200 character windows; the #6753 branches at its top pushed the lines
  past them. It now reads the handler's own body: a card link is routed
  before window.open, and the click is stopped after it.

Thanks to xet7 !
2026-10-10 11:44:10 +03:00
Lauri Ojansivu 2ad41b9ee9 Updated ChangeLog.
Thanks to xet7 !
2026-10-10 02:20:49 +03:00
Lauri Ojansivu d5c59211c0 Updated dependencies.
Thanks to developers of dependencies !
2026-10-10 02:01:58 +03:00
Lauri Ojansivu 7f8627590b Updated meteor-reactive-cache 2026-10-10 01:51:54 +03:00
Lauri Ojansivu aad1edffc2 Updated ChangeLog.
Thanks to GitHub CodeQL and xet7 !
2026-10-10 01:33:22 +03:00
Lauri Ojansivu bb04a832ba Show the old link escaping as its output, not as code, in the security alerts test.
CodeQL alert 559 (incomplete string escaping) was
tests/securityAlerts20261010.test.cjs re-running the escaping fixed for
alert 555, to show it let a URL end a link early. The test now holds the
Markdown the old code wrote, written out, and still checks that it renders
as two links. The check that no code escapes links that way now reads
tests/ as well as models, server, client and imports, and finds the old
line.

Thanks to GitHub CodeQL and xet7 !
2026-10-10 01:33:10 +03:00
Lauri Ojansivu 1bdc171f0f Updated meteor-reactive-cache 2026-10-10 01:23:44 +03:00
Lauri Ojansivu fa12f7dcad Updated ChangeLog.
Thanks to carl-unique and xet7 !
2026-10-10 01:21:32 +03:00
Lauri Ojansivu 0495b8729d Copying a code block in a card description no longer opens a new tab.
The copy button of a code block is an <a href="#"> inside the card viewer.
Its own listener copied the text and prevented the navigation, but the click
went on to the viewer's 'click a' handler, which opens every link it sees in
a new tab - here this page plus "#". The button now stops the click, and the
viewer handler leaves the copy button alone and no longer opens a link to a
place on the page ("#", "#section") in a new tab; it moves to the target in
place when there is one.

tests/copyCodeBlock.test.cjs pins both and their order before window.open;
tests/playwright/specs/viewer-copy-code.e2e.js clicks the button and checks
that no tab opens, the page stays and the block's text is copied.

Thanks to carl-unique and xet7 !

Fixes #6753.
2026-10-10 01:20:53 +03:00
Lauri Ojansivu 51023baa0c Upgrade to Meteor 3.6-rc.0 .
Thanks to Meteor developers !
2026-10-10 01:16:36 +03:00
Lauri Ojansivu 9f29a41740 Added npmjs.com @wekanteam npm packages directly to wekan repo npm-packages directory.
Thanks to xet7 !
2026-10-10 01:11:32 +03:00
Lauri Ojansivu 42a4c0babb Updated ChangeLog.
Thanks to GitHub CodeQL, dependabot and xet7 !
2026-10-10 00:46:08 +03:00
Lauri Ojansivu 5ec6d9af18 Fix the code scanning and Dependabot alerts of 2026-10-10.
- Markdown links from imported files (CodeQL #555): Plane escaped [ and ]
  in a link title but not backslash, so a title ending in "\" ran on into
  the URL, and its URL went through encodeURIComponent, which leaves ( and )
  as they are, so a ")" ended the link early and the rest became a second
  link. ClickUp, Super Productivity and Vikunja wrote links the same way.
  All four now use models/lib/markdownLink.js, which escapes the title and
  percent-encodes what ends or breaks a destination.
- A replacement of a space with itself in a test (CodeQL #554), where a
  regular-expression escape was meant. tests/noIdentityReplacement.test.cjs
  now also finds the regular-expression form of an identity replacement.
- The vendored @wekanteam/exceljs (CodeQL #556-#558, Dependabot #137-#142):
  sheet names are read without a backtracking regular expression, the
  manual test pages pin their CDN script with Subresource Integrity, and
  brace-expansion is 5.0.12. Same change as the fork, .tools/exceljs
  25db789. The vendored jade's dev tools also had brace-expansion 5.0.6.

tests/securityAlerts20261010.test.cjs pins each, and the shape of each
across the tree.

Thanks to GitHub CodeQL, dependabot and xet7 !
2026-10-10 00:45:30 +03:00
Lauri Ojansivu dd6fe3ef1e Added @wekanteam/exceljs@4.7.4 to WeKan repo directly, because after publishing to npmjs.com it still is not available to be installed with npm install.
Thanks to xet7 !
2026-10-10 00:14:05 +03:00
Lauri Ojansivu 70a4da3b3c Updated ChangeLog.
Thanks to markusst1982 and xet7 !
2026-10-09 23:40:00 +03:00
Lauri Ojansivu b1841b7525 Write the card and board Excel exports as they are drawn, reading each picture only when it is written.
The card Excel export, and the board export drawn in the card layout, read
every picture of the card or board into memory while drawing the sheet and
wrote the whole workbook at the end. They now write through
createWorkbookWriter to the response, and the board export writes each card's
rows out once the card is drawn. With the @wekanteam/exceljs fork's streaming
writer (.tools/exceljs: archiver 8 support and pictures on a streaming sheet)
each picture is a function the writer calls when it reaches it, so one is open
at a time and a picture that cannot be read is written empty rather than
ending the download. The fork is recognised by WorkbookWriter._addDrawing;
until a release with it is installed, these exports take the buffered writer
as before. A failure after the first byte now ends the download instead of
leaving it waiting.

Thanks to markusst1982 and xet7 !
2026-10-09 23:39:18 +03:00
Lauri Ojansivu 52b86ef753 Read a PDF export's pictures one at a time as they are drawn, and write it to disk, not memory.
The card and board PDF exports read every picture of the card or board into
memory before making the document, then built the whole PDF in memory too.
Each picture is now a loader: the Unicode PDF writer reads it when it draws
its row and hands it to PDFKit, which embeds it and lets the bytes go, so
one picture is in memory at a time. The document is written to a temporary
file as it is made and sent from there with its length, so it is not held
in memory either, and the base-font fallback still takes over when the
Unicode PDF fails - reading the pictures only then. Removed from TODO Later;
the Excel half stays there with what blocks it.

Thanks to markusst1982 and xet7 !
2026-10-09 23:13:44 +03:00
Lauri Ojansivu 85a226433c Stream attachment and background uploads and downloads over the REST API.
The attachment REST API carried every file as base64 inside JSON: an upload
gathered the whole body into a string, decoded it and stored it, and a
download read the whole file and encoded it - the file and its base64 copy
in memory at once, up to the API limits. Beside that form, each route now
takes the file itself: an upload whose body is the file (any Content-Type
but JSON, its card or board in the query) streams into storage, and
?raw=1 on a download streams the file out, served as a download that
cannot run in WeKan's origin. Both make the same checks first - one helper
now holds the card upload checks for both forms - and the API limits are
checked against Content-Length before reading and counted as the bytes flow.
api.py uses the raw form for its four attachment and background commands,
streaming from and to disk. The DDP methods stay base64 by their nature;
docs/API/Attachments.md says to use the raw routes for large files.
Removed from TODO Later.

Thanks to markusst1982 and xet7 !
2026-10-09 23:10:43 +03:00
Lauri Ojansivu cd054f9964 Stream files moved to or from the old CollectionFS storage.
Admin Panel > Attachments > Move read each file whole into memory: out of
CollectionFS it gathered the GridFS download into one Buffer and wrote it
with writeFileSync, into CollectionFS it read the source storage whole and
then uploaded it. Both now pipe: the GridFS download stream into the new
file, and the storage's read stream into the GridFS upload, chunk by chunk.
A copy that fails leaves no half file, and a missing binary still names the
attachment. Removed from TODO Later.

Thanks to markusst1982 and xet7 !
2026-10-09 23:06:49 +03:00
Lauri Ojansivu ddc025c24d Stream live Trello import attachments from Trello into storage as each card is made.
The live Trello import, and a Trello .json import with saved credentials,
downloaded every uploaded attachment of the board first - each read whole,
up to 256 MB - and kept them all on the board as base64 until it was made.
TrelloCreator now gets a streamer: each attachment is downloaded when the
creator reaches it and streamed into storage, one at a time. The download is
the same - the URL check, the credential only for Trello's own hosts, every
redirect validated and pinned - through fetchSafe's new stream mode, which
hands the body on as a stream with the same byte limit counted as it flows.
Removed from TODO Later.

Thanks to markusst1982 and xet7 !
2026-10-09 23:05:47 +03:00
Lauri Ojansivu dd14753712 Updated translations. 2026-10-09 23:04:13 +03:00
Lauri Ojansivu 719194741a Stream a cloned board's attachments from the source storage into the copy.
Cloning built the board's whole export with every attachment as base64 and
imported that. The export is now built without file data, and the importer
streams each attachment from the source board's own storage as it reaches
it, held to the upload limit like every imported attachment. Removed from
TODO Later.

Thanks to markusst1982 and xet7 !
2026-10-09 23:03:55 +03:00
Lauri Ojansivu 627592cd7f Stream each board of Export all boards as WeKan JSON into its zip entry.
Every board was built whole - every attachment as base64 inside it - then
serialized and added to the .zip. Each board is now written into its entry
piece by piece by the streaming exporter the single board export uses, with
attachments as base64 in aligned chunks, paced by the response's
backpressure, so memory holds a chunk at a time instead of a board. Removed
from TODO Later.

Thanks to markusst1982 and xet7 !
2026-10-09 23:03:04 +03:00
Lauri Ojansivu 87ef8144f2 Merge remote-tracking branch 'origin/main' 2026-10-09 23:02:48 +03:00
Lauri Ojansivu cd75f8c476 Stream a Trello .zip import from a temporary file, its attachments into storage.
The route read the whole upload into memory, up to its 200 MB cap, opened the
zip from that buffer, and read each attachment whole, up to 100 MB, to put
it on the board JSON as base64. The upload is now written to a temporary
file as it arrives, under the same cap, the zip is opened from the file, and
each attachment streams from its entry into storage through TrelloCreator,
counted against the same per-file limit and the same 1 GB budget of
everything inflated (server/lib/boundedZipEntry.js boundedZipEntryStream).
Only the board JSON files are held in memory. The .zip import takes the
people choice too. Removed from TODO Later.

Thanks to markusst1982 and xet7 !
2026-10-09 23:02:22 +03:00
Lauri Ojansivu 521ddf85dd List in TODO Later where big data is still held in memory.
Thanks to markusst1982 and xet7 !
2026-10-09 23:01:01 +03:00
Lauri Ojansivu 734f362d44 Record the large-board speed and memory fixes in the changelog.
Thanks to markusst1982 and xet7 !
2026-10-09 13:28:34 +03:00
Lauri Ojansivu d56bb34490 Make large boards fast on 8 GB: load only what is visible, stop re-running every minicard, and stream big attachments.
On a seeded board of 4,000 cards, 12,000 comments, 20,000 checklist items
and 32,000 activities, served from FerretDB with SQLite as the Snap is:
closing a card took about 4 s and now takes 0.1 s, the board finished
loading in 17.6 s and now in 6.2 s, a card open re-ran about 7,700
computations and now about 1,000, and FerretDB used 13 s of CPU and 724 MB
while the board opened, now 3 s and 280 MB.

- The client data cache queued one more stop callback each time a helper
  re-ran, and every check asked Tracker for dependents, which on close was
  710 walks of 3 ms; there is now one callback per computation and key and
  one timer per key (imports/lib/dataCache.js).
- Opening a card writes the user's last-viewed card, and avatars, @mentions,
  list and swimlane headers read the whole user, so everything re-ran; they
  read only the fields they show. The comment, checklist and subtask indexes
  were one object every minicard depended on and are now per card
  (imports/lib/keyedGroupIndex.js).
- The lazy card window kept a full copy of every card of a list per window;
  it keeps the id and the sort keys, runs its id query once instead of five
  times, and sends comment text only when minicards show comments
  (server/publications/cardsWindow.js, models/lib/cardWindowFields.js).
  Comment reactions and legacy attachment lookups come with the opened card
  only.
- Attachment downloads honour Range for filesystem, GridFS and cloud
  storage (206, 416, If-Range); uploads to cloud storage are spooled to a
  temporary file and streamed instead of gathered into one Buffer; the API
  copy checks the size limit before reading; the legacy attachment
  migration streams, and no longer stores "[object Promise]" as a file; the
  board export no longer concatenates buffers quadratically.

The FerretDB fork pushes dotted-path filters such as meta.cardId down to
SQLite (they were each a full scan) and decodes only the fields a count
filters on. docs/Features/Admin-Panel/Problems/Large-Boards.md describes how
a large board loads and how to size a server with 8 GB of RAM.

Thanks to markusst1982 and xet7 !

Fixes #6745.
2026-10-09 13:28:08 +03:00
Lauri Ojansivu 026e7034e9 Record people choice, CSV and Excel mapping, one import path, Kanboard links and GitHub comments in the changelog.
Thanks to xet7 !
2026-10-09 13:14:17 +03:00
Lauri Ojansivu c80a656901 Read every import source through one path and one file chooser, from one description of the sources.
models/lib/importSources.js describes every import source once: its name,
which files its chooser accepts, whether its export can be pasted, what a
file is sent as, whether a .zip is one export of the tool, and which
importer creates the board. The import page's list of sources, its file
chooser and its text box, "Import many boards" and the test of api.py's
lists all read it.

The page had a branch per source - one each for Excel and the four other
workbook tools, Vikunja, Notion, Plane, the text formats, CSV, Trello's and
WeKan's files - each reading its file its own way, and four file choosers.
It now has one: the chosen file is read by the reader "Import many boards"
uses, or the pasted text is read, then only the steps a source really has of
its own run (the column mapping of CSV and Excel, the empty-export check of
WeKan, Jira's estimate field, Trello's workspace, Taiga's slimming), then the
people step every source shares. Kanboard's people come from the server's
parser like every generalized source's, so its own client mapper is gone.
Every source can now be chosen as a file, not only pasted.

Thanks to xet7 !
2026-10-09 13:13:37 +03:00
Lauri Ojansivu 86652c8718 Ask for the columns of a CSV or Excel import, apply Owner, and import WeKan's own Excel export.
A CSV, TSV or Excel import now shows which column is which card field
before importing, pre-filled from the header names - English names and
WeKan's own export column names in every language - and asks for what is
missing: a file with no list column gets one list, named on the page,
instead of one list per row. Unused columns can be kept as text custom
fields. The choice goes to the server as csvMapping, checked before
anything is written, and the REST import and api.py take one too.

The Owner column is applied, as are swimlane, received date, parent card,
spent time, overtime and archived. A TSV value containing a comma stays one
value. A dropdown custom field value that is not one of its items is skipped
instead of failing the import.

An Excel workbook is read sheet by sheet, its header found in row 1 or where
WeKan's export puts it, in any language; formula cells are read as their
results and rich text as text. WeKan's own Excel export imports, and a
workbook with many boards - each on its own sheet, as Export all boards
writes it - imports as many boards; the Activity sheet is skipped.

Thanks to xet7 !
2026-10-09 12:52:21 +03:00
Lauri Ojansivu 594867bab7 Import Kanboard task links as parent cards and dependencies, and GitHub comments as card comments.
Kanboard task links were only reported as losses. They now become WeKan
data: "is a child of" and "is a parent of" make the parent card, and blocks,
is blocked by, duplicates, is duplicated by, fixes, is fixed by and relates
to make dependencies of the matching type; the milestone pair becomes
related-to and is reported. Kanboard stores each link from both tasks, so
the two rows of one link become one parent link or one dependency. Rows from
getAllTaskLinks, getTaskLinkById and links grouped by label are read; an
unknown label, a link to a task not in the file and a second parent are
reported. The Kanboard export writes parents and dependencies back as task
links from both ends, so the file round-trips. The docs no longer claim typed
custom fields for Kanboard, which has none.

GitHub, Gitea and Forgejo issues put their embedded comments into the card
description; each now becomes a card comment with its author and date, and
the description is left as the issue wrote it. A comment without text, and
comments counted on the issue but not embedded, are reported.

The Jira instruction's endpoint literal is the current search
(GET /rest/api/3/search/jql) in every locale too, as code literals are copied
from English; the translation suites that pinned the old one follow.

Thanks to xet7 !
2026-10-09 12:27:59 +03:00
Lauri Ojansivu 7f163a8000 Test the import page's choice of who a file's people become.
Thanks to xet7 !
2026-10-09 12:21:16 +03:00
Lauri Ojansivu 73db241bd4 Choose who an import's people become, for every import, and stream imported attachments.
Every import page now asks once what the people named in the file become
(models/lib/importMembersMode.js): existing users chosen in the map-members
step, placeholder users that keep the original username and name and cannot
log in, to be mapped to real users later, or all the person importing. All
five creators read the same mapping. The generalized importer, which used to
drop people nobody mapped, now makes placeholders for them, as Trello and
WeKan imports do, and offers its people for mapping by reading the file with
the same parser (importBoard with previewPeople, which creates nothing).
Import many boards offers placeholders or the importer. The REST import, the
WeKan .zip route and the Trello route take the mode too.

A WeKan .zip imported as a new board no longer puts its attachment files
inline in memory within a 512 MB budget: the full-board importer streams each
from the archive into storage, as the import into an existing board does.
Both, and the Trello importer when given a stream, go through one helper
(server/lib/importAttachmentStream.js) that holds every imported attachment
to the Admin Panel's upload limit and to none when none is set, skipping a
file whose stated size is over it and stopping one that grows past it.

The import page instructions name Jira's current search (GET
/rest/api/3/search/jql with fields=*all, paged by nextPageToken), Todoist's
Manage data > Export as CSV and Planner's Export as Excel.

Thanks to xet7 !
2026-10-09 12:20:36 +03:00
Lauri Ojansivu da9a1c4d28 Record many-board import and export, its fixes and the per-format docs in the changelog.
Thanks to xet7 !
2026-10-09 10:34:15 +03:00
Lauri Ojansivu 7858d84e12 Import and export many boards at once for every format, and document each format in its own directory.
Import many boards: on the import page of every source, several export
files, or one .zip holding them, each become their own board through the
same path a single import of that file takes, and the page lists what each
file became (models/lib/importManyFiles.js). A .zip that is itself one
export (Vikunja, Notion, Plane, WeKan with attachments) stays one board.
One board per project: for the generalized importer, each swimlane the
import would make - a project, folder or list of the tool - becomes its own
board (models/lib/importSplit.js), so an export of a whole app imports as
many boards, as a Trello .zip does. Kanri's all-data export now reads every
board, not only the first, so it splits into one board per Kanri board.

Export all boards: All Boards sidebar, or Multi-Selection for the selected
boards, downloads every board the user may export in one format, each
checked as its own export is. Excel is one workbook with a sheet per board,
named after the board; every other format is a .zip with one file per board
(server/routes/exportAllBoards.js). The per-format bytes are made in one
place, server/lib/renderExternalExport.js, for the board route and this.

A WeKan .zip export imports as a new board from All Boards, unpacked on the
server with its attachments (it was refused in the browser). api.py gains
importboardsfrom and exportallboards, sends every source in the shape the
import page sends, and fetches Excel, PDF, CSV and TSV exports from their
own routes and writes them as bytes.

Fixed while documenting: a Trello Butler rule's list name kept its case,
so the rule fires; the HTML export fetches the board JSON by URL instead of
a link the export popup no longer has; Tasks.org exports time spent;
Nextcloud Deck's done flag marks the due date done.

docs/Features/ImportExport has a directory per format with the steps to
import and export it, one board and many, and its details, moved from
Format-Coverage.md, which is now the index and the shared contract.
tests/importExportDocsCoverage.test.cjs fails when a format has no page.

Thanks to xet7 !
2026-10-09 10:33:37 +03:00
Lauri Ojansivu 4790328ea7 Record the Wrike workflow and the import and export format page in the changelog.
The Wrike entry no longer says its export writes a WeKan list column, and two
entries describe Wrike's custom workflow statuses and workflows, and the page
that lists every supported format.

Thanks to xet7 !
2026-10-09 09:49:31 +03:00
Lauri Ojansivu 2ea4d09678 Document every import and export format WeKan supports.
docs/Features/ImportExport/External-Tools.md listed ten tools while WeKan
reads and writes more than fifty. It now lists every tool and file, grouped
into project and task tools, issue trackers, plain text and outlines, and
WeKan's own formats: where in each tool to get the file, what WeKan writes
back, and which tools are not supported yet and why. The README's import and
export lists link to it and name the Wrike workflow, the Board Settings
export page points to it and to the Wrike workflow, and the historical
wishes in Sync.md say which have been done since.

tests/importExportDocsCoverage.test.cjs fails when a source on the import
page or a format in the export menu is missing from the page, so it cannot
go stale again.

Thanks to xet7 !
2026-10-09 09:48:54 +03:00
Lauri Ojansivu a78b4aa744 Export Wrike Excel with Wrike's custom workflow statuses, and import and export Wrike workflows as lists and rules.
The Wrike Excel export wrote the WeKan list as a custom column named WeKan
list, which Wrike reads as a custom field rather than a status. It now
writes the five workflow columns of Wrike's import help article: Default
task workflow on folder rows, and on task rows Workflow (named after the
board), Status (the list's status group) and Custom Status (the list).
Wrike applies a custom status only when that workflow exists, matching
case, so the board's lists are also exported as that workflow: the JSON of
Wrike's GET /workflows, one custom status per list with the nearest Wrike
color. That JSON is the only exchange format, since Wrike has no file
import or export of workflows.

A list's status group comes from the board's rules. A "moved to this list,
mark complete" rule makes it Completed (Cancelled when its name says so), a
"mark incomplete" rule makes it Active (or Deferred), otherwise the name
decides. In Rules > Import / Export, a pasted Wrike workflow adds a list for
each status the board lacks, after the previous status's list, and adds the
rules that do what Wrike's status groups do. Those rules show in the list,
Workflow and Blocks views. A server method does this as a board admin and
adds nothing twice. Wrike's own automation rules have no export, import or
API, so they cannot be carried over.

The Wrike import reads Custom Status as the list and a single Workflow as
the board title. It reports a status group the status name does not imply,
since only the workflow import's rules keep it.

tests/wrikeWorkflow.test.cjs and tests/wrikeFormat.test.cjs cover the
workflow, rules, colors, refusals and round trips. Two new Playwright cases
import a workbook with the workflow columns and apply a Wrike workflow
through the Rules popup twice, and the export audit fetches the workflow.
New text is in English, pending Transifex.

Thanks to xet7 !
2026-10-09 09:48:47 +03:00
Lauri Ojansivu b46a0f8091 Refresh translation backlog and verification status 2026-10-09 04:05:59 +03:00
Lauri Ojansivu 6b936b7dde Refresh translation coverage after Xhosa fill
Update measured README coverage to 191 and protect the full current Xhosa fill. All 333 translation-related suites pass; the new completion gate also passes separately. Remaining language-quality and translation work stays open.
2026-10-09 04:05:30 +03:00
Lauri Ojansivu a3b3fc1da5 Document current Xhosa fill completion 2026-10-09 04:00:41 +03:00
Lauri Ojansivu ce7d915850 Complete current Xhosa translation fill
Fill the final seven import instructions, preserving header requirements, hierarchy markers and exclusions. The current fill list is empty. Specialized wording remains low confidence pending speaker review. Three targeted suites and all 21 preservation checks pass.
2026-10-09 04:00:09 +03:00
Lauri Ojansivu 8910f9d991 Document Xhosa task import translations 2026-10-09 03:58:17 +03:00
Lauri Ojansivu be4c6e4b7c Translate Xhosa task and board import instructions
Fill nine instructions preserving commands, first-board limits, completion dates and attachment exclusions. Specialized wording remains low confidence pending speaker review. Three targeted suites and all 21 preservation checks pass.
2026-10-09 03:57:50 +03:00
Lauri Ojansivu 001f3bd9eb Document Xhosa planning import translations 2026-10-09 03:56:10 +03:00
Lauri Ojansivu de41ca91d4 Translate Xhosa outline and planning import instructions
Fill eight instructions preserving export commands, field names, tokens and mappings. Specialized wording remains low confidence pending speaker review. Three targeted suites and all 21 preservation checks pass.
2026-10-09 03:55:41 +03:00
Lauri Ojansivu feb42b433a Document Xhosa Blockly completion 2026-10-09 03:54:08 +03:00
Lauri Ojansivu 597a6a5ff4 Complete Xhosa Blockly text and workspace translations
Fill 70 messages preserving variables, composed-message whitespace and keyboard labels. Specialized wording remains low confidence pending speaker review. Both targeted suites and all 21 preservation checks pass; import instructions remain.
2026-10-09 03:53:40 +03:00
Lauri Ojansivu 69d213ba44 Document Xhosa text control translations 2026-10-09 03:52:02 +03:00
Lauri Ojansivu 200611eaac Translate Xhosa Blockly shortcuts and text controls
Fill 40 messages preserving variables, keyboard labels, copy semantics and indexing direction. Specialized wording remains low confidence pending speaker review. Both targeted suites and all 21 preservation checks pass.
2026-10-09 03:51:33 +03:00
Lauri Ojansivu 9a0cd96a92 Document Xhosa accessibility translations 2026-10-09 03:50:12 +03:00
Lauri Ojansivu e34dbda38a Translate Xhosa Blockly accessibility and shortcuts
Fill 40 messages preserving variables, keyboard labels, screen-reader states and navigation distinctions. Specialized wording remains low confidence pending speaker review. Both targeted suites and all 21 preservation checks pass.
2026-10-09 03:49:45 +03:00
Lauri Ojansivu 949dfa5868 Document Xhosa procedure translations 2026-10-09 03:48:21 +03:00
Lauri Ojansivu a31bdc1bfe Translate Xhosa Blockly editor and procedures
Fill 35 messages preserving function variables, keyboard labels, output distinctions and placement warnings. Specialized wording remains low confidence pending speaker review. Both targeted suites and all 21 preservation checks pass.
2026-10-09 03:47:50 +03:00
Lauri Ojansivu 326209ce9f Document Xhosa mathematical function translations 2026-10-09 03:46:31 +03:00
Lauri Ojansivu abbcd6f77d Translate Xhosa Blockly mathematical functions
Fill 30 messages with regression checks for notation, rounding directions and degrees-versus-radians instructions. Specialized wording remains low confidence pending speaker review. Both targeted suites and all 21 preservation checks pass.
2026-10-09 03:46:06 +03:00
Lauri Ojansivu d8e23a7222 Document Xhosa Blockly statistics translations 2026-10-09 03:44:39 +03:00
Lauri Ojansivu 94d5407ccd Translate Xhosa Blockly numeric tests and statistics
Fill 35 messages preserving variables, sign distinctions and random bounds. Specialized wording remains low confidence pending speaker review. Both targeted suites and all 21 preservation checks pass.
2026-10-09 03:44:12 +03:00
Lauri Ojansivu 695cf4410d Document Xhosa Blockly arithmetic translations 2026-10-09 03:41:52 +03:00
Lauri Ojansivu 5a2c397755 Translate Xhosa Blockly logic and arithmetic
Fill 30 messages with regression checks for variables, mathematical constants, bounds and logical distinctions. Specialized wording remains low confidence pending speaker review. Both targeted suites and all 21 preservation checks pass.
2026-10-09 03:41:20 +03:00
Lauri Ojansivu a97a017f34 Document Xhosa Blockly comparison translations 2026-10-09 03:39:46 +03:00
Lauri Ojansivu f9ad418fd9 Translate Xhosa Blockly list editing and comparisons
Fill 35 messages with regression checks for sort variables, copy semantics, insertion and comparison distinctions. Specialized wording remains low confidence pending speaker review. Both targeted suites and all 21 preservation checks pass.
2026-10-09 03:39:08 +03:00
Lauri Ojansivu b255281450 Document Xhosa Blockly list translations 2026-10-09 03:37:46 +03:00
Lauri Ojansivu b67c91bde5 Translate Xhosa Blockly list retrieval and removal
Fill 40 messages with regression checks for variables, return/remove distinctions and copy semantics. Specialized wording remains low confidence pending speaker review. Both targeted suites and all 21 preservation checks pass.
2026-10-09 03:37:12 +03:00
Lauri Ojansivu 6d8c120746 Document Xhosa Blockly navigation translations 2026-10-09 03:35:47 +03:00
Lauri Ojansivu 86026ab08e Translate Xhosa Blockly navigation and list creation
Fill 40 messages with regression checks for variables, coordinate names, list semantics and consistent positions. Specialized wording remains low confidence pending speaker review. Both targeted suites and all 21 preservation checks pass.
2026-10-09 03:35:25 +03:00
Lauri Ojansivu 8e598c9131 Document Xhosa Blockly input translations 2026-10-09 03:34:14 +03:00
Lauri Ojansivu 3a759d0927 Translate Xhosa Blockly input and accessibility labels
Fill 40 messages with regression checks for variables, keyboard help and distinct actions and positions. Specialized wording remains low confidence pending speaker review. Both targeted suites and all 21 preservation checks pass.
2026-10-09 03:33:47 +03:00
Lauri Ojansivu a2c58dc353 Document Xhosa Blockly editor translations 2026-10-09 03:32:25 +03:00
Lauri Ojansivu 1210db0bbc Translate Xhosa Blockly conditions and editor actions
Fill 45 messages with regression checks for variables, conditions, keyboard labels and rendered coordinates. Specialized wording remains low confidence pending speaker review. Both targeted suites and all 21 preservation checks pass.
2026-10-09 03:31:47 +03:00
Lauri Ojansivu aca549b56b Document Xhosa Blockly control translations 2026-10-09 03:30:15 +03:00
Lauri Ojansivu 59fb4bb980 Translate Xhosa Blockly colors and loop controls
Fill 36 messages and preserve percent variables, keyboard labels, numeric limits and read-only restrictions. Specialized wording remains low confidence pending speaker review. Both targeted suites and all 21 preservation checks pass.
2026-10-09 03:29:46 +03:00
Lauri Ojansivu 267ec1b6db Document Xhosa interrupted import translations 2026-10-09 03:27:52 +03:00
Lauri Ojansivu 0c7fad659b Translate Xhosa interrupted imports and history recovery
Fill 35 messages, preserving variables and deletion and rollback warnings. Specialized wording remains low confidence pending speaker review. Both targeted suites and all 21 preservation checks pass.
2026-10-09 03:27:22 +03:00
Lauri Ojansivu 7ada198cc4 Document Xhosa recovery control translations 2026-10-09 03:25:39 +03:00
Lauri Ojansivu 84951459e6 Translate Xhosa recovery controls and stuck sync operations
Fill 40 messages with regression checks for variables, cancellation, discard consequences and record limits. Specialized wording remains low confidence pending speaker review. Both targeted suites and all 21 preservation checks pass.
2026-10-09 03:25:07 +03:00
Lauri Ojansivu 970d1ee06f Document Xhosa diagnostics translations 2026-10-09 03:23:24 +03:00
Lauri Ojansivu 5f893a2dce Translate Xhosa diagnostics and notification failures
Fill 35 messages with regression checks for estimate clearing, planning restrictions, report access and retry warnings. Specialized wording remains low confidence pending speaker review. Both targeted suites and all 21 preservation checks pass.
2026-10-09 03:22:56 +03:00
Lauri Ojansivu 8e01dfd438 Document Xhosa sync report translations 2026-10-09 03:21:14 +03:00
Lauri Ojansivu e925fba96c Translate Xhosa sync reports and omissions
Fill 24 messages and retain report limits, hidden-value scope and partial-failure warnings in regression checks. Specialized wording remains low confidence pending speaker review. Both targeted suites and all 21 preservation checks pass.
2026-10-09 03:20:36 +03:00
Lauri Ojansivu f21a19fa53 Document Xhosa sync-conflict translations 2026-10-09 03:16:34 +03:00
Lauri Ojansivu d3f641754f Translate Xhosa sync conflicts and preview actions
Fill 20 messages and verify preservation guarantees, replacement reuse and preview prerequisites. Specialized wording remains low confidence pending speaker review. Targeted suites and 21 preservation checks pass.
2026-10-09 03:16:05 +03:00
Lauri Ojansivu 3ab29d2044 Document Xhosa import-preview translations 2026-10-09 03:14:40 +03:00
Lauri Ojansivu 116d2f58b7 Translate Xhosa planning imports and previews
Fill 22 messages and preserve reference and count variables. Verify preview rendering and preservation warnings. Specialized wording remains low confidence pending speaker review. Targeted suites and 21 preservation checks pass.
2026-10-09 03:14:08 +03:00
Lauri Ojansivu 8e1fdbff8e Document Xhosa observation translations 2026-10-09 03:12:05 +03:00
Lauri Ojansivu 7612460d73 Translate Xhosa sprint states and observations
Fill 24 messages and preserve references, UTC and report limits. Verify close/cancel semantics and observation caveats. Specialized wording remains low confidence pending speaker review. Targeted suites and 21 preservation checks pass.
2026-10-09 03:11:34 +03:00
Lauri Ojansivu b7ab193374 Document Xhosa report translations 2026-10-09 03:09:55 +03:00
Lauri Ojansivu 400fb9e276 Translate Xhosa sprint reports and events
Fill 30 messages and preserve summary variables and keyboard names. Verify unknown estimates and report comparison restrictions. Specialized wording remains low confidence pending speaker review. Targeted suites and 21 preservation checks pass.
2026-10-09 03:09:27 +03:00
Lauri Ojansivu c4e6e75d7a Document Xhosa planning translations 2026-10-09 03:08:18 +03:00
Lauri Ojansivu c5ac9ced64 Translate Xhosa planning settings
Fill 30 labels and verify sprint action distinctions and consistent view wording. Specialized terminology remains low confidence pending speaker review. Targeted suites and 21 preservation checks pass.
2026-10-09 03:07:57 +03:00
Lauri Ojansivu a7822fd5f8 Document Xhosa settings translations 2026-10-09 03:06:49 +03:00
Lauri Ojansivu 7258355abf Translate Xhosa board and rule settings
Fill 29 placeholders and preserve variables, URL examples and LDAP identifiers. Verify permissions, conflicts and rendered errors. Specialized wording remains low confidence pending speaker review. Targeted suites and 21 preservation checks pass.
2026-10-09 03:06:24 +03:00
Lauri Ojansivu 2e569324ac Document Zulu fill and refresh translation backlog 2026-10-09 03:04:27 +03:00
Lauri Ojansivu f2f8c08caa Complete current Zulu fill and refresh coverage count
Localize keyboard labels while retaining printed legends, correct the monday.com possessive and verify both fill lists are empty. The broad run found a stale README count; update it to 190 and verify the failing suite passes. Language review and broader translation work remain outstanding.
2026-10-09 03:03:54 +03:00
Lauri Ojansivu a22f732cda Document final Zulu import translations 2026-10-09 02:59:15 +03:00
Lauri Ojansivu 956932e01a Translate final Zulu import instruction group
Fill seven instructions per catalog and preserve commands, columns and hierarchy markers. Test header requirements and excluded data. Specialized terminology remains low confidence pending speaker review. Three targeted suites and 21 preservation checks pass.
2026-10-09 02:58:54 +03:00
Lauri Ojansivu 76971b42e0 Document middle Zulu import translations 2026-10-09 02:57:35 +03:00
Lauri Ojansivu 2cf65610d4 Translate middle Zulu import instruction group
Fill nine instructions per catalog with exact commands and formats. Test first-board selection, archives and attachment exclusions. Specialized terminology remains low confidence pending speaker review. Three targeted suites and 21 preservation checks pass.
2026-10-09 02:57:11 +03:00
Lauri Ojansivu 70ef56d712 Document first Zulu import translations 2026-10-09 02:55:58 +03:00
Lauri Ojansivu 3305076c3b Translate first Zulu import instruction group
Fill eight import instructions per catalog, preserving commands and formats. Test completion, archive and swimlane behavior. Specialized terminology remains low confidence pending speaker review. Three targeted suites and 21 preservation checks pass.
2026-10-09 02:55:36 +03:00
Lauri Ojansivu 520ff3f322 Document Zulu recovery-decision translations 2026-10-09 02:54:27 +03:00
Lauri Ojansivu dbb9c5e5b1 Translate Zulu recovery decisions and history checkpoints
Fill 22 values per catalog and preserve checkpoint variables. Verify deletion warnings, rollback restrictions and read-only semantics. Specialized terminology remains low confidence pending speaker review. Targeted suites and 21 preservation checks pass.
2026-10-09 02:54:07 +03:00
Lauri Ojansivu 8664941fdc Document Zulu interrupted-import translations 2026-10-09 02:53:01 +03:00
Lauri Ojansivu 6e019d9299 Translate Zulu interrupted-import messages
Fill 25 messages per catalog and preserve counts and stage variables. Test replayability, applied-change preservation and deletion warnings. Specialized terminology remains low confidence pending speaker review. Targeted suites and 21 preservation checks pass.
2026-10-09 02:52:33 +03:00
Lauri Ojansivu 5f4451d6a5 Document Zulu recovery-control translations 2026-10-09 02:51:07 +03:00
Lauri Ojansivu 6dc4df5e0a Translate Zulu notification controls and stuck sync messages
Fill 25 messages per catalog and preserve progress variables. Test cancellation, pending work and already-applied-change guarantees. Specialized terminology remains low confidence pending speaker review. Targeted suites and 21 preservation checks pass.
2026-10-09 02:50:44 +03:00
Lauri Ojansivu 0749598402 Document Zulu notification-recovery translations 2026-10-09 02:49:19 +03:00
Lauri Ojansivu 578a802281 Translate Zulu mail and notification recovery messages
Fill 25 values per catalog and verify retry warnings, null semantics and activity preservation. Specialized terminology remains low confidence pending speaker review. Targeted suites and 21 preservation checks pass.
2026-10-09 02:48:55 +03:00
Lauri Ojansivu a7c310750e Document Zulu sync-report translations 2026-10-09 02:47:33 +03:00
Lauri Ojansivu e31f01a785 Translate Zulu sync reports and diagnostics
Fill 30 values per catalog and test retention limits, incomplete runs, permissions and null semantics. Specialized terminology remains low confidence pending speaker review. Targeted suites and 21 preservation checks pass.
2026-10-09 02:47:08 +03:00
Lauri Ojansivu 3f39a0e161 Document Zulu sync-preview translations 2026-10-09 02:45:49 +03:00
Lauri Ojansivu cd36346f35 Translate Zulu sync recovery and previews
Fill 25 messages per catalog and check preservation guarantees, replacement reuse and preview limits. Specialized terminology remains low confidence pending speaker review. Both targeted suites and 21 preservation checks pass.
2026-10-09 02:45:22 +03:00
Lauri Ojansivu fdd89c113e Document Zulu planning-import translations 2026-10-09 02:44:05 +03:00
Lauri Ojansivu a294e929c6 Translate Zulu planning imports and observations
Fill 35 messages per catalog, preserving variables and identifiers. Verify report caveats, preview counts, unchanged cards and no-source-write instructions. Specialized terminology remains low confidence pending speaker review. Targeted suites and 21 preservation checks pass.
2026-10-09 02:43:41 +03:00
Lauri Ojansivu a5d86b628a Document Zulu sprint translations 2026-10-09 02:41:51 +03:00
Lauri Ojansivu 191406347f Translate Zulu sprint planning and reports
Fill 50 messages per catalog and preserve summary variables. Test unknown estimates, partial reports, rollover and cancellation behavior. Specialized terminology remains low confidence pending speaker review. Both targeted suites and 21 preservation checks pass.
2026-10-09 02:41:23 +03:00
Lauri Ojansivu f71e86e1cb Document Zulu settings translations 2026-10-09 02:39:39 +03:00
Lauri Ojansivu 7a5dfe1e3d Translate Zulu link rules and planning settings
Fill 38 application messages per catalog. Preserve brace variables and technical examples; test permissions, conflicts, hidden fields and sprint actions. Specialized terminology remains low confidence pending speaker review. Targeted suites and 21 preservation checks pass.
2026-10-09 02:39:11 +03:00
Lauri Ojansivu b9c14962ce Document Zulu statistics and trigonometry translations 2026-10-09 02:37:46 +03:00
Lauri Ojansivu 45bc8254f0 Translate Zulu statistics and trigonometry messages
Fill 27 values per catalog with exact variables and function symbols. Test statistical distinctions, coordinates, logarithm base and angle units. Specialized terminology remains low confidence pending speaker review. Targeted suites and 21 preservation checks pass.
2026-10-09 02:37:16 +03:00
Lauri Ojansivu ecda7a8c74 Document Zulu number-property translations 2026-10-09 02:36:06 +03:00
Lauri Ojansivu 1fcaf906cf Translate Zulu number properties and powers
Fill 26 values per catalog, preserving mathematical constants and formulas. Test parity, rounding and sign distinctions. Specialized terms remain low confidence pending speaker review; root and prime vocabulary follows the government mathematics dictionary. Targeted suites and 21 preservation checks pass.
2026-10-09 02:35:44 +03:00
Lauri Ojansivu 9b4a3f6d6f Document Zulu navigation translations 2026-10-09 02:34:12 +03:00
Lauri Ojansivu e471744e18 Translate Zulu keyboard navigation and screen-reader messages
Fill 50 values per catalog. Preserve shortcut variables and test rendered movement, directions and screen-reader toggles. Specialized terminology remains low confidence pending speaker review. Targeted suites and 21 preservation checks pass.
2026-10-09 02:33:49 +03:00
Lauri Ojansivu 9d588aeb6e Document Zulu input-label translations 2026-10-09 02:32:40 +03:00
Lauri Ojansivu 02850b72df Translate Zulu Blockly input accessibility labels
Fill 51 labels per catalog and preserve numbered variables. Test paired input distinctions, coordinates and rendered input numbers. Specialized terminology remains low confidence pending speaker review. Both targeted suites and 21 preservation checks pass.
2026-10-09 02:32:14 +03:00
Lauri Ojansivu a90f88bfbd Document Zulu variable and workspace translations 2026-10-09 02:31:11 +03:00
Lauri Ojansivu abe868f48b Translate Zulu variable and workspace messages
Fill 58 strings per catalog and preserve variables, shortcuts and joined phrase spacing. Verify rendered name conflicts, workspace counts and conditional labels. Specialized terminology remains low confidence pending speaker review. Targeted suites and 21 preservation checks pass.
2026-10-09 02:30:42 +03:00
Lauri Ojansivu 6e16ccacde Document Zulu text-operation translations 2026-10-09 02:29:34 +03:00
Lauri Ojansivu bc86e817ef Translate Zulu Blockly text operations
Fill 55 strings per catalog, preserving variables and empty grammar suffixes. Check replacement order, trimming direction, text length and missing matches. Specialized terminology remains low confidence pending speaker review. Both targeted suites and 21 preservation checks pass.
2026-10-09 02:29:14 +03:00
Lauri Ojansivu 0ca4f8c990 Refresh translation backlog and document Zulu procedures 2026-10-09 02:27:44 +03:00
Lauri Ojansivu ca55854277 Translate Zulu Blockly procedure controls
Fill 24 strings per catalog and verify function output, disabled definitions and variable substitutions. Specialized terms remain low confidence pending speaker review. The preceding broad run passed 333 suites; targeted suites and 21 preservation checks pass after this batch.
2026-10-09 02:27:22 +03:00
Lauri Ojansivu 6256e33d36 Document Zulu logic and arithmetic translations 2026-10-09 02:23:12 +03:00
Lauri Ojansivu 71d5e27802 Translate Zulu Blockly logic and basic arithmetic
Fill 59 strings per Zulu catalog and preserve variables and mathematical symbols. Verify logic distinctions, bounds and rendered substitutions. Specialized terminology remains low confidence pending speaker review. Both targeted suites and 21 preservation checks pass.
2026-10-09 02:22:43 +03:00
Lauri Ojansivu e0f72e699c Document Zulu list-operation translations 2026-10-09 02:20:59 +03:00
Lauri Ojansivu ab1a7a8171 Translate Zulu Blockly list operations
Fill 75 English values per catalog and preserve numbered variables. Verify retrieval and removal distinctions, sort direction, substitutions and non-prose configuration. Specialized programming terms remain low confidence pending speaker review. Targeted suites and 21 preservation checks pass.
2026-10-09 02:20:27 +03:00
Lauri Ojansivu 2ee180a75a Document Zulu editor translations 2026-10-09 02:19:07 +03:00
Lauri Ojansivu ccd6277314 Translate Zulu Blockly editor and accessibility labels
Fill 50 English values per Zulu catalog. Preserve variables and test rendered deletion and pixel labels plus true/false loop distinctions. Specialized programming terms remain low confidence pending speaker review. Targeted suites and 21 preservation checks pass.
2026-10-09 02:18:44 +03:00
Lauri Ojansivu 5461cd3d71 Document Zulu settings and control translations 2026-10-09 02:17:32 +03:00
Lauri Ojansivu 0774342c12 Translate Zulu board settings and Blockly controls
Fill 52 English strings in each Zulu catalog and test variables, rendered substitutions and restrictions. Specialized programming terms are low confidence and need speaker review. Both targeted suites and 21 preservation checks pass.
2026-10-09 02:17:00 +03:00
Lauri Ojansivu 1545424da2 Document Albanian import translations 2026-10-09 02:14:52 +03:00
Lauri Ojansivu 4bf0f430d2 Translate Albanian import instructions
Translate the 21 newer import instructions while preserving commands, column names and variables. Verify excluded data, first-board selection and task hierarchy. Import and placeholder suites and 21 preservation checks pass.
2026-10-09 02:14:22 +03:00
Lauri Ojansivu 9cc225c427 Document remaining Afrikaans import translations 2026-10-09 02:10:33 +03:00
Lauri Ojansivu 62f3021f5a Translate remaining Afrikaans import instructions
Preserve commands, columns and variables while translating seven instructions. Cover excluded data, English headers and task hierarchy. Import and placeholder suites and all 21 preservation checks pass; the current fill list is empty.
2026-10-09 02:10:05 +03:00
Lauri Ojansivu 20aba286e6 Document further Afrikaans import translations 2026-10-09 02:09:03 +03:00
Lauri Ojansivu 13b1df58cc Translate six further Afrikaans import instructions
Preserve commands, extensions and variables while translating six instructions. Cover excluded attachments, completion dates, archives and comments. Import and placeholder suites and all 21 preservation checks pass.
2026-10-09 02:08:38 +03:00
Lauri Ojansivu edb1d87403 Document first Afrikaans import translations 2026-10-09 02:07:41 +03:00
Lauri Ojansivu 1c1e84323f Translate first Afrikaans import instruction group
Translate eight instructions while preserving commands and variables. Cover completion, archives, first-board selection and swimlanes. Import and placeholder checks pass after a successful broad run of 333 translation-related suites.
2026-10-09 02:07:17 +03:00
Lauri Ojansivu 9e9da6721f Document Galician interrupted-import and history recovery 2026-10-09 02:04:10 +03:00
Lauri Ojansivu 35a643cb6e Translate Galician interrupted-import and history recovery
Translate 68 values across two catalogs and preserve exact variables. Cover permanent deletion, non-destructive keep, conflict blocking and rollback restrictions. Both relevant suites and all 21 preservation checks pass; current fill lists are empty.
2026-10-09 02:03:46 +03:00
Lauri Ojansivu df4022776a Document Galician synchronization recovery 2026-10-09 02:02:37 +03:00
Lauri Ojansivu 19b1c2d879 Translate Galician synchronization recovery
Translate 42 values across two catalogs and preserve exact variables. Cover retained applied changes, unwritten pending changes and discard restrictions. Both relevant suites and all 21 preservation checks pass.
2026-10-09 02:02:11 +03:00
Lauri Ojansivu 2339eaed28 Document Galician planning results 2026-10-09 02:00:59 +03:00
Lauri Ojansivu 2e922f7828 Translate Galician planning results
Translate 34 values across two catalogs, preserving counters and reference variables. Cover invalid JSON, unchanged cards, source matching and first-sync behavior. Both relevant suites and all 21 preservation checks pass.
2026-10-09 02:00:26 +03:00
Lauri Ojansivu 3d3312ba42 Document Galician settings and planning translations 2026-10-09 01:59:15 +03:00
Lauri Ojansivu 1643efb120 Translate Galician settings and planning controls
Translate 40 values across two catalogs, preserving variables, link syntax and configuration names. Cover access restrictions, import behavior and empty domain settings. Both relevant suites and all 21 preservation checks pass.
2026-10-09 01:58:47 +03:00
Lauri Ojansivu 72a5bd6d38 Document remaining Galician import translations 2026-10-09 01:57:34 +03:00
Lauri Ojansivu f28e0ee74d Translate remaining Galician import instructions
Translate 14 values across two catalogs, preserving commands, columns, extensions and variables. Cover excluded data, English headers and task hierarchy. Three relevant suites and all 21 preservation checks pass.
2026-10-09 01:57:03 +03:00
Lauri Ojansivu f3b93df3bc Document further Galician import translations 2026-10-09 01:55:50 +03:00
Lauri Ojansivu 37efcb8f8b Translate six further Galician import instructions
Translate 12 values across two catalogs, preserving commands, extensions and variables. Cover excluded attachments, completion dates, archives and comments. Three relevant suites and all 21 preservation checks pass.
2026-10-09 01:55:20 +03:00
Lauri Ojansivu 5a43a4df5e Document shared Galician terminology corrections 2026-10-09 01:53:44 +03:00
Lauri Ojansivu 877eca8186 Correct shared mixed-language Galician terminology
Correct 106 values across both Galician catalogs and record audited changes. Preserve placeholders, restore the single-board Home limit and remove an unsupported loading-default claim. Three relevant suites and all 21 preservation checks pass.
2026-10-09 01:53:13 +03:00
Lauri Ojansivu 8a17561c5b Document further Galician regional corrections 2026-10-09 01:51:38 +03:00
Lauri Ojansivu 3e3209d27f Correct further Portuguese terminology in Galician regional strings
Replace 93 values with reviewed Galician wording and record audited corrections. Verify native terminology, exact placeholders and action distinctions. Three relevant suites and all 21 preservation checks pass.
2026-10-09 01:51:10 +03:00
Lauri Ojansivu b2e6c422d6 Document Galician regional label corrections 2026-10-09 01:50:04 +03:00
Lauri Ojansivu dd6bd0df11 Correct Portuguese labels in the Galician regional catalog
Replace 33 labels and messages with reviewed Galician wording and record audited corrections. Verify native terminology and exact placeholder inventories. Three relevant suites and all 21 preservation checks pass.
2026-10-09 01:49:38 +03:00
Lauri Ojansivu 14991b98d5 Document Galician announcements and import translations 2026-10-09 01:48:32 +03:00
Lauri Ojansivu 521cb051f5 Translate Galician announcements and initial imports
Translate 28 values across two catalogs, preserving commands, format keywords and variables. Cover hierarchy, first-board selection, archives and completion. Three relevant suites and all 21 preservation checks pass.
2026-10-09 01:48:05 +03:00
Lauri Ojansivu 3b56d67277 Document Catalan and Valencian recovery results 2026-10-09 01:46:30 +03:00
Lauri Ojansivu e65c495bc9 Translate Catalan and Valencian recovery results
Translate 84 values across three catalogs, preserving counters and reference variables. Check keep and discard warnings, foreign-board preservation and rollback restrictions. Four relevant suites and all 21 preservation checks pass; current fill lists are empty.
2026-10-09 01:46:03 +03:00
Lauri Ojansivu 00dd96a42a Document Catalan and Valencian recovery controls 2026-10-09 01:44:50 +03:00
Lauri Ojansivu 2c491f18bb Translate Catalan and Valencian recovery controls
Translate 66 values across three catalogs. Check discard restrictions, retained applied changes and full deletion warnings for partial imports. Four relevant suites and all 21 preservation checks pass.
2026-10-09 01:44:24 +03:00
Lauri Ojansivu dd2e712f88 Document Catalan and Valencian planning results 2026-10-09 01:43:23 +03:00
Lauri Ojansivu 47b7094306 Translate Catalan and Valencian planning results
Translate 60 values across three catalogs, preserving counters and reference variables. Cover planning matching, unchanged cards and recovery semantics. Four relevant suites and all 21 preservation checks pass.
2026-10-09 01:42:57 +03:00
Lauri Ojansivu 9fbe39938c Document Catalan and Valencian settings translations 2026-10-09 01:41:56 +03:00
Lauri Ojansivu dca5008ba9 Translate Catalan and Valencian settings and planning
Translate 75 values across three catalogs, preserving variables, link syntax and LDAP identifiers. Cover access restrictions, import behavior and empty domain settings. Four relevant suites and all 21 preservation checks pass.
2026-10-09 01:41:29 +03:00
Lauri Ojansivu 18de867522 Document Catalan and Valencian outline translations 2026-10-09 01:40:15 +03:00
Lauri Ojansivu 72f614759f Translate Catalan and Valencian outline imports
Translate OPML, Org mode and Todoist instructions in three catalogs. Preserve format keywords, commands and variables, and check hierarchy, completion and comment mappings. Import and placeholder suites and all 21 preservation checks pass.
2026-10-09 01:39:49 +03:00
Lauri Ojansivu 4b17556839 Document remaining Catalan and Valencian import translations 2026-10-09 01:38:52 +03:00
Lauri Ojansivu 7a54cfdb01 Translate remaining Catalan and Valencian import instructions
Translate 13 instructions in three catalogs while preserving commands, placeholders and regional wording. Cover excluded data, English headers, hierarchy and archives. Import and placeholder suites and all 21 preservation checks pass.
2026-10-09 01:38:26 +03:00
Lauri Ojansivu b76b4fcd70 Document first Catalan and Valencian import translations 2026-10-09 01:36:59 +03:00
Lauri Ojansivu 22c4f03f82 Translate first Catalan and Valencian import instructions
Translate eight instructions in three catalogs while preserving commands, placeholders and regional wording. Cover first-board selection, completion dates, archives and swimlane mappings. Import and placeholder suites and all 21 preservation checks pass.
2026-10-09 01:36:31 +03:00
Lauri Ojansivu cf0c244d14 Document remaining Russian import translations 2026-10-09 01:35:18 +03:00
Lauri Ojansivu 260875da30 Translate remaining Russian import instructions
Translate 13 instructions across four Russian locale paths, preserving the shared catalog alias. Cover commands, placeholders, excluded data, English headers, hierarchy and archives. Import and placeholder suites and all 21 preservation checks pass.
2026-10-09 01:34:51 +03:00
Lauri Ojansivu e9e7488cb9 Document first Russian import translations 2026-10-09 01:33:37 +03:00
Lauri Ojansivu dd9084b716 Translate first Russian import instruction group
Translate eight instructions across four Russian locale paths, preserving the shared catalog alias. Cover commands, placeholders, first-board selection, completion dates and archives. Import and placeholder suites and all 21 preservation checks pass.
2026-10-09 01:33:10 +03:00
Lauri Ojansivu 7f668ea97d Document Ukrainian regional planning translations 2026-10-09 01:31:42 +03:00
Lauri Ojansivu dee430763a Fill Ukrainian regional planning and recovery translations
Reuse reviewed Ukrainian wording for 94 English regional values while preserving existing translations and placeholders. Extend checks for recovery warnings, planning restrictions, LDAP identifiers and link examples. Four relevant suites and all 21 preservation checks pass.
2026-10-09 01:31:16 +03:00
Lauri Ojansivu c26c7d5292 Document Ukrainian regional outline translations 2026-10-09 01:30:17 +03:00
Lauri Ojansivu d2e49f8871 Translate Ukrainian regional outline import instructions
Fill OPML, Org mode and Todoist instructions from reviewed Ukrainian translations. Verify literals, hierarchy, completion and comment mappings. All 333 translation-related suites and 21 preservation checks pass.
2026-10-09 01:29:47 +03:00
Lauri Ojansivu 4d02c81f78 Document remaining Ukrainian import translations 2026-10-09 01:26:58 +03:00
Lauri Ojansivu b0f492ce1e Translate remaining Ukrainian import instructions
Translate 13 instructions in both Ukrainian catalogs while preserving commands, columns and placeholders. Cover excluded data, English headers, task hierarchy and archives. Import and placeholder suites and all 21 preservation checks pass.
2026-10-09 01:26:29 +03:00
Lauri Ojansivu 97fa12ff55 Document first Ukrainian import translations 2026-10-09 01:25:09 +03:00
Lauri Ojansivu d3d66f4688 Translate first Ukrainian import instruction group
Translate eight instructions in both Ukrainian catalogs while preserving commands and placeholders. Cover first-board selection, completion dates, archives and swimlane mappings. Import and placeholder suites and all 21 preservation checks pass.
2026-10-09 01:24:40 +03:00
Lauri Ojansivu 2473728a14 Document Lithuanian import translations 2026-10-09 01:23:28 +03:00
Lauri Ojansivu 4d82b3b812 Translate Lithuanian import instructions
Translate 21 instructions while preserving commands, column names and placeholders. Cover first-board selection, archive behavior, import limitations and task hierarchy. Import and placeholder suites and all 21 preservation checks pass.
2026-10-09 01:22:59 +03:00
Lauri Ojansivu 8a4f46b34e Document Latvian import translations 2026-10-09 01:21:16 +03:00
Lauri Ojansivu 18b30dea23 Translate Latvian import instructions
Translate 21 instructions while preserving commands, column names and placeholders. Cover first-board selection, archive behavior, import limitations and task hierarchy. Import and placeholder suites and all 21 preservation checks pass.
2026-10-09 01:20:48 +03:00
Lauri Ojansivu 0bebcf3f50 Document Estonian import translations 2026-10-09 01:19:01 +03:00
Lauri Ojansivu b6f202a2b8 Translate Estonian import instructions
Translate 21 instructions while preserving commands, column names and placeholders. Cover first-board selection, archive behavior, import limitations and task hierarchy. Import and placeholder suites and all 21 preservation checks pass.
2026-10-09 01:18:30 +03:00
Lauri Ojansivu 1a2ab61141 Document remaining Bosnian import translations 2026-10-09 01:16:33 +03:00
Lauri Ojansivu 848345e329 Translate remaining Bosnian import instructions
Translate 13 import instructions while preserving commands, columns and placeholders. Cover import limitations, English headers and task hierarchy with regression checks. Both translation suites and all 21 preservation checks pass.
2026-10-09 01:16:03 +03:00
Lauri Ojansivu b248b8bb09 Document first Bosnian import translations 2026-10-09 01:12:36 +03:00
Lauri Ojansivu 515b7d85ed Translate first Bosnian import instructions
Translate eight instructions while preserving commands, extensions and variables. Cover first-board selection, completion dates, archived cards and swimlane mappings. Translation, placeholder and preservation checks pass.
2026-10-09 01:12:01 +03:00
Lauri Ojansivu 1636a201ac Document remaining Serbian import translations 2026-10-09 01:10:59 +03:00
Lauri Ojansivu 2fda0df675 Translate remaining Serbian import instructions
Translate 13 instructions in Cyrillic while preserving commands, columns, variables and import limitations. Cover excluded data, English headers, hierarchy and archived tasks. Translation, placeholder and preservation checks pass.
2026-10-09 01:10:28 +03:00
Lauri Ojansivu 05e3069162 Document first Serbian import translations 2026-10-09 01:09:00 +03:00
Lauri Ojansivu d5307ed5d7 Translate first Serbian import instructions
Translate eight instructions in Cyrillic while preserving commands, extensions and variables. Cover first-board selection, completion dates, archived cards and swimlane mappings. Translation, placeholder and preservation checks pass.
2026-10-09 01:08:31 +03:00
Lauri Ojansivu dc23047295 Document remaining Slovenian import translations 2026-10-09 01:07:17 +03:00
Lauri Ojansivu 5402befdc6 Translate remaining Slovenian import instructions
Translate 13 instructions in both Slovenian catalogs while preserving commands, columns, variables and import limitations. Cover excluded data, English headers, hierarchy and archived tasks. Translation, placeholder and preservation checks pass.
2026-10-09 01:06:44 +03:00
Lauri Ojansivu d0d0bef5c4 Document first Slovenian import translations 2026-10-09 01:05:19 +03:00
Lauri Ojansivu bafb8ec764 Translate first Slovenian import instructions
Translate eight instructions in both Slovenian catalogs while preserving commands, extensions and variables. Cover first-board selection, completion dates, archived cards and swimlane mappings. Translation, placeholder and preservation checks pass.
2026-10-09 01:04:46 +03:00
Lauri Ojansivu 217af2bba9 Document Croatian import translations 2026-10-09 01:03:40 +03:00
Lauri Ojansivu 8484038c8c Translate Croatian import instructions
Translate 21 instructions while preserving commands, columns, variables and import limitations. Extend regression coverage and refresh the broader translation test selection: 333 suites pass. Record the remaining untranslated backlog without treating deferred keys as complete.
2026-10-09 01:03:11 +03:00
Lauri Ojansivu 789c1b7739 Document remaining Bulgarian import translations 2026-10-09 01:00:22 +03:00
Lauri Ojansivu f92c673b11 Translate remaining Bulgarian import instructions
Translate 13 instructions while preserving commands, columns, variables and import limitations. Cover excluded data, English headers, hierarchy and archived tasks. Translation, placeholder, completion and preservation checks pass.
2026-10-09 00:59:50 +03:00
Lauri Ojansivu a9daee4a37 Document first Bulgarian import translations 2026-10-09 00:58:07 +03:00
Lauri Ojansivu 28c73fbedd Translate first Bulgarian import instructions
Translate eight instructions while preserving commands, extensions and variables. Cover first-board selection, completion dates, archived cards and swimlane mappings. Translation, placeholder, completion and preservation checks pass.
2026-10-09 00:57:32 +03:00
Lauri Ojansivu 77b09aaa70 Document remaining Vietnamese import translations 2026-10-09 00:56:05 +03:00
Lauri Ojansivu a0310aaa4b Translate remaining Vietnamese import instructions
Translate 13 instructions in both Vietnamese catalogs while preserving commands, columns, variables and import limitations. Cover excluded data, English headers, hierarchy and archived tasks. Translation, placeholder, completion and preservation checks pass.
2026-10-09 00:55:26 +03:00
Lauri Ojansivu 174a6c9adf Document first Vietnamese import translations 2026-10-09 00:53:43 +03:00
Lauri Ojansivu 9331c78da8 Translate first Vietnamese import instructions
Translate eight instructions in both Vietnamese catalogs while preserving commands, extensions and variables. Cover first-board selection, completion dates, archived cards and swimlane mappings. Translation, placeholder, completion and preservation checks pass.
2026-10-09 00:53:13 +03:00
Lauri Ojansivu 9ce62f2966 Document remaining Malay import translations 2026-10-09 00:51:47 +03:00
Lauri Ojansivu be9c5be485 Translate remaining Malay import instructions
Translate 13 instructions in both Malay catalogs while preserving commands, columns, variables and import limitations. Cover excluded data, English headers, hierarchy and archived tasks. Translation, placeholder and preservation checks pass.
2026-10-09 00:51:19 +03:00
Lauri Ojansivu 682b73fa3a Document first Malay import translations 2026-10-09 00:49:51 +03:00
Lauri Ojansivu bebeec9b39 Translate first Malay import instructions
Translate eight instructions in both Malay catalogs while preserving commands, extensions and variables. Cover first-board selection, completion dates, archived cards and swimlane mappings. Translation, placeholder and preservation checks pass.
2026-10-09 00:49:23 +03:00
Lauri Ojansivu 0633af3652 Document remaining Indonesian import translations 2026-10-09 00:48:16 +03:00
Lauri Ojansivu 42d5d92611 Translate remaining Indonesian import instructions
Translate 13 instructions while preserving commands, columns, variables and import limitations. Cover excluded data, English headers, hierarchy and archived tasks. Translation, placeholder and preservation checks pass.
2026-10-09 00:47:47 +03:00
Lauri Ojansivu 843218613f Document first Indonesian import translations 2026-10-09 00:46:27 +03:00
Lauri Ojansivu e23797cecf Translate first Indonesian import instructions
Translate eight instructions while preserving commands, extensions and variables. Cover first-board selection, completion dates, archived cards and swimlane mappings. Translation, placeholder and preservation checks pass.
2026-10-09 00:45:58 +03:00
Lauri Ojansivu 442f5eff29 Document remaining Korean import translations 2026-10-09 00:44:52 +03:00
Lauri Ojansivu 712b45a103 Translate remaining Korean import instructions
Translate 13 instructions in both Korean catalogs while preserving commands, columns, variables and import limitations. Cover excluded data, English headers, hierarchy and archived tasks. Translation, placeholder and preservation checks pass.
2026-10-09 00:44:24 +03:00
Lauri Ojansivu deb1ff5562 Document first Korean import translations 2026-10-09 00:42:58 +03:00
Lauri Ojansivu 8673bef810 Translate first Korean import instructions
Translate eight instructions in both Korean catalogs while preserving commands, extensions and variables. Cover first-board selection, completion dates, archived cards and swimlane mappings. Translation, placeholder and preservation checks pass.
2026-10-09 00:42:28 +03:00
Lauri Ojansivu f5648f5040 Document remaining Hiragana import translations 2026-10-09 00:41:09 +03:00
Lauri Ojansivu 7ca8c0f12d Translate remaining Hiragana import instructions
Translate 13 instructions in ja-HI while preserving commands, columns, variables and import limitations. Check Hiragana script, excluded data, English headers, hierarchy and archived tasks. Translation, placeholder and preservation checks pass.
2026-10-09 00:40:39 +03:00
Lauri Ojansivu 7f242752ca Document first Hiragana import translations 2026-10-09 00:38:46 +03:00
Lauri Ojansivu d1e2652cf8 Translate first Hiragana import instructions
Translate eight instructions in ja-HI while preserving commands, extensions and variables. Check Hiragana script, first-board selection, completion dates and archived cards. Translation, placeholder and preservation checks pass.
2026-10-09 00:38:15 +03:00
Lauri Ojansivu a580dcad0d Document remaining Japanese import translations 2026-10-09 00:36:58 +03:00
Lauri Ojansivu 80b30b3b13 Translate remaining Japanese import instructions
Translate 13 instructions in ja and ja-JP while preserving commands, columns, variables and import limitations. Cover excluded data, English headers, hierarchy and archived tasks. Translation, placeholder and preservation checks pass.
2026-10-09 00:36:30 +03:00
Lauri Ojansivu aac9e083f2 Document first Japanese import translations 2026-10-09 00:34:58 +03:00
Lauri Ojansivu 1c2bbe874c Translate first Japanese import instructions
Translate eight instructions in ja and ja-JP while preserving commands, extensions and variables. Cover first-board selection, completion dates, archived cards and swimlane mappings. Translation, placeholder and preservation checks pass.
2026-10-09 00:34:29 +03:00
Lauri Ojansivu a0120ed1fe Document remaining Greek import translations 2026-10-09 00:33:17 +03:00
Lauri Ojansivu c09588c385 Translate remaining Greek import instructions
Translate 13 instructions in both Greek catalogs while preserving commands, columns, variables and import limitations. Cover excluded data, English headers, hierarchy and archived tasks. Translation, placeholder and preservation checks pass.
2026-10-09 00:32:49 +03:00
Lauri Ojansivu 246b81d2a2 Document first Greek import translations 2026-10-09 00:31:06 +03:00
Lauri Ojansivu 49e95666ee Translate first Greek import instructions
Translate eight instructions in both Greek catalogs while preserving commands, extensions and variables. Cover first-board selection, completion dates, archived cards and swimlane mappings. Translation, placeholder and preservation checks pass.
2026-10-09 00:30:35 +03:00
Lauri Ojansivu f19ccf409d Document Turkish import translations 2026-10-09 00:29:20 +03:00
Lauri Ojansivu 4399632332 Translate Turkish import instructions
Translate 21 instructions while preserving commands, columns, variables and import limitations. Cover first-board selection, excluded data, English headers, completion dates and archived tasks. Translation, placeholder and preservation checks pass.
2026-10-09 00:28:46 +03:00
Lauri Ojansivu 3299d27041 Document Hungarian import translations 2026-10-09 00:26:52 +03:00
Lauri Ojansivu f76a63197c Translate Hungarian import instructions
Translate 21 instructions while preserving commands, columns, variables and import limitations. Cover first-board selection, excluded data, English headers, completion dates and archived tasks. Translation, placeholder and preservation checks pass.
2026-10-09 00:26:21 +03:00
Lauri Ojansivu 99fb2437d0 Document Romanian import translations 2026-10-09 00:24:08 +03:00
Lauri Ojansivu 617e347ebe Translate Romanian import instructions
Translate 21 instructions in both Romanian catalogs while preserving commands, columns, variables and import limitations. Cover first-board selection, excluded data, English headers, completion dates and archived tasks. Translation, placeholder and preservation checks pass.
2026-10-09 00:23:39 +03:00
Lauri Ojansivu aacab378c1 Document Slovak import translations 2026-10-09 00:21:47 +03:00
Lauri Ojansivu 2bc47e2d02 Translate Slovak import instructions
Translate 21 instructions while preserving commands, columns, variables and import limitations. Cover first-board selection, excluded data, English headers, completion dates and archived tasks. Translation, placeholder and preservation checks pass.
2026-10-09 00:21:19 +03:00
Lauri Ojansivu e59ee88286 Document Czech import translations 2026-10-09 00:19:32 +03:00
Lauri Ojansivu e71fa762a1 Translate Czech import instructions
Translate 21 instructions in both Czech catalogs while preserving commands, columns, variables and import limitations. Cover first-board selection, excluded data, English headers, completion dates and archived tasks. Translation, placeholder and preservation checks pass.
2026-10-09 00:19:03 +03:00
Lauri Ojansivu 63a4548d51 Document Polish import translations 2026-10-09 00:16:54 +03:00
Lauri Ojansivu 5256aa752d Translate Polish import instructions
Translate 21 instructions in both Polish catalogs while preserving commands, columns, variables and import limitations. Cover first-board selection, excluded data, English headers, completion dates and archived tasks. Translation, placeholder and preservation checks pass.
2026-10-09 00:16:24 +03:00
Lauri Ojansivu bcc54148ce Document Norwegian import translations 2026-10-09 00:14:29 +03:00
Lauri Ojansivu f308b39b22 Translate Norwegian Bokmål import instructions
Translate 21 instructions while preserving commands, columns, variables and import limitations. Add regression checks for first-board selection, excluded data, English headers, completion dates and archived tasks. Translation, placeholder and preservation checks pass.
2026-10-09 00:13:59 +03:00
Lauri Ojansivu 514f000e08 Document Danish import translations 2026-10-09 00:12:12 +03:00
Lauri Ojansivu 62761d0b72 Translate Danish import instructions
Translate 21 instructions while preserving literal commands, columns, variables and import limitations. Add regression checks for first-board selection, excluded data, English headers, completion dates and archived tasks. Translation, placeholder and preservation checks pass.
2026-10-09 00:11:40 +03:00
Lauri Ojansivu dd768f7dd7 Document Swedish import translations 2026-10-09 00:09:55 +03:00
Lauri Ojansivu c701c44abf Translate Swedish import instructions
Translate 21 instructions while preserving commands, columns, variables and import limitations. Cover first-board selection, excluded data, English headers, completion dates and archived tasks. Translation, placeholder and preservation checks pass.
2026-10-09 00:09:26 +03:00
Lauri Ojansivu 7ccba64ca8 Document Dutch import translations 2026-10-09 00:07:41 +03:00
Lauri Ojansivu f73ae5a179 Translate Dutch import instructions
Translate 21 instructions in both Dutch catalogs while retaining literal commands, variables and import limitations. Extend regression coverage for excluded data, first-board selection, English headers and archived tasks. Translation, placeholder and preservation checks pass.
2026-10-09 00:07:09 +03:00
Lauri Ojansivu 64bb352902 Document Italian import translations 2026-10-09 00:05:18 +03:00
Lauri Ojansivu 4b2c47268a Translate Italian import instructions
Translate all 21 import instructions while preserving commands, columns, extensions and variables. Cover first-board selection, excluded data, English headers and archived tasks with regression checks. Translation and placeholder suites pass.
2026-10-09 00:04:48 +03:00
Lauri Ojansivu 17fb31b207 Document remaining Portuguese import instruction translations 2026-10-09 00:01:02 +03:00
Lauri Ojansivu a846a7961f Finish import instructions in four Portuguese catalogs
Translate 52 values with regional terms and preserved commands, columns, extensions and hierarchy markers. Verify excluded data, English headers and archived tasks. Import-instruction and placeholder suites and all 21 preservation checks pass.
2026-10-09 00:00:31 +03:00
Lauri Ojansivu a8921cfdd7 Document Portuguese import instruction translations 2026-10-08 23:59:09 +03:00
Lauri Ojansivu 69f5721790 Translate eight import instructions in four Portuguese catalogs
Fill 32 values with regional terminology and preserved commands, extensions and variables. Check first-board selection, completion dates and archived cards. Import-instruction and placeholder suites and all 21 preservation checks pass.
2026-10-08 23:58:40 +03:00
Lauri Ojansivu 5e2b26060c Document remaining Spanish import instruction translations 2026-10-08 23:57:30 +03:00
Lauri Ojansivu 771127e70b Finish import instructions in nine Spanish catalogs
Translate 117 values with preserved commands, columns, extensions and hierarchy markers. Verify excluded data, English header requirements and archived tasks. Import-instruction and placeholder suites and all 21 preservation checks pass.
2026-10-08 23:56:59 +03:00
Lauri Ojansivu d4f8f6d4e2 Document Spanish import instruction translations 2026-10-08 23:55:41 +03:00
Lauri Ojansivu 6de5319b8b Translate eight import instructions in nine Spanish catalogs
Fill 72 values with preserved commands, extensions and variables. Check first-board selection, completion dates and archived cards. Import-instruction and placeholder suites and all 21 preservation checks pass.
2026-10-08 23:55:08 +03:00
Lauri Ojansivu bab55fea87 Document remaining French import instruction translations 2026-10-08 23:53:57 +03:00
Lauri Ojansivu 7d52e278f3 Finish import instructions in five French catalogs
Translate 65 values with preserved commands, columns, extensions and hierarchy markers. Verify excluded data, English header requirements and archived tasks. Import-instruction and placeholder suites and all 21 preservation checks pass.
2026-10-08 23:53:28 +03:00
Lauri Ojansivu a879845b9b Document French import instruction translations 2026-10-08 23:52:02 +03:00
Lauri Ojansivu 034eea1a80 Translate eight import instructions in five French catalogs
Fill 40 values while preserving source commands, extensions and variables. Check first-board selection, archived cards and completion dates. Import-instruction and placeholder suites and all 21 preservation checks pass.
2026-10-08 23:51:34 +03:00
Lauri Ojansivu 5e3ae06471 Document German import instruction translations 2026-10-08 23:50:24 +03:00
Lauri Ojansivu cafca6bf33 Translate 21 import instructions in four German catalogs
Fill 84 values while preserving source commands, extensions, columns and hierarchy markers. Test exact variables, first-board selection, archived and completed tasks, and excluded data. Import-instruction and placeholder suites and all 21 preservation checks pass.
2026-10-08 23:49:53 +03:00
Lauri Ojansivu d034cdfea5 Document complete sign-in warning inventory coverage 2026-10-08 23:47:52 +03:00
Lauri Ojansivu 260b4946e6 Translate Cherokee warning and cover every non-English catalog
Discover all 234 non-English catalog paths for warning regression checks, including missing values, exact tokens and repeated address roles. Warning and placeholder suites and 21 preservation checks pass. Cherokee prose has low confidence and needs native review; the wider translation backlog remains unfinished.
2026-10-08 23:47:23 +03:00
Lauri Ojansivu 83471df7ad Document Tamazight and Inuktitut warning translations 2026-10-08 23:45:55 +03:00
Lauri Ojansivu 204bb32c1d Translate Tamazight and Inuktitut sign-in warnings
Preserve repeated address variables and ROOT_URL and check prose scripts. Warning and placeholder suites and 21 preservation checks pass. These translations have lower confidence and need native review.
2026-10-08 23:45:26 +03:00
Lauri Ojansivu 08cac513bd Document Tigre and Wolaytta translations and label corrections 2026-10-08 23:44:01 +03:00
Lauri Ojansivu 8fc5a2b440 Translate Tigre and Wolaytta warnings and correct three labels
Preserve repeated address variables and ROOT_URL. Three translation suites and 21 preservation checks pass. These direct translations have low confidence and need native review.
2026-10-08 23:43:32 +03:00
Lauri Ojansivu c6924a9c0e Document two warning translations and broad regression results 2026-10-08 23:41:50 +03:00
Lauri Ojansivu 2d4e5fd2dc Translate Greenlandic and Nahuatl sign-in warnings
Preserve repeated address variables and ROOT_URL. All 333 selected translation-related suites and 21 preservation checks pass. These translations have lower confidence and need native review.
2026-10-08 23:41:20 +03:00
Lauri Ojansivu fdbe1d0ee5 Document Veps, Volapuk and Klingon warning translations 2026-10-08 23:38:16 +03:00
Lauri Ojansivu 3faee05c50 Translate Veps, Volapuk and Klingon sign-in warnings
Preserve repeated address variables and ROOT_URL. Warning and placeholder suites and 21 preservation checks pass. These translations have lower confidence and need native review.
2026-10-08 23:37:44 +03:00
Lauri Ojansivu 906ef78b4a Document Uzbek and Kashmiri warnings and script corrections 2026-10-08 23:36:16 +03:00
Lauri Ojansivu 9df171db32 Translate Arabic-script Uzbek and Kashmiri sign-in warnings
Preserve repeated address variables and ROOT_URL. Correct three Uzbek labels to the registered script. Three translation suites and 21 preservation checks pass. These translations have lower confidence and need native review.
2026-10-08 23:35:43 +03:00
Lauri Ojansivu 77b78a367b Document Tibetan, Dzongkha and Tigrinya warning translations 2026-10-08 23:34:19 +03:00
Lauri Ojansivu 7acb9ad77f Translate Tibetan, Dzongkha and Tigrinya sign-in warnings
Preserve repeated address variables and ROOT_URL, with separate Tibetan and Dzongkha prose. Warning and placeholder suites and 21 preservation checks pass. These translations have lower confidence and need native review.
2026-10-08 23:33:49 +03:00
Lauri Ojansivu 82171885c6 Document Bambara, Ewe and Fulah warning translations 2026-10-08 23:32:22 +03:00
Lauri Ojansivu efb1c9a7e4 Translate Bambara, Ewe and Fulah sign-in warnings
Preserve repeated address variables and ROOT_URL. Warning and placeholder suites and 21 preservation checks pass. These translations have lower confidence and need native review.
2026-10-08 23:31:51 +03:00
Lauri Ojansivu be1b5a216c Document four warning translations and five label corrections 2026-10-08 23:30:46 +03:00
Lauri Ojansivu 1dbf7a2f2e Translate four sign-in warnings and correct Andean labels
Add Acehnese, Aymara, Guarani and Quechua warnings with exact variables. Correct five prefixed or untranslated labels. Three translation suites and 21 preservation checks pass. These direct translations have lower confidence and need native review.
2026-10-08 23:30:16 +03:00
Lauri Ojansivu 8737841667 Document Buryat, Chuvash and Sakha warning translations 2026-10-08 23:28:23 +03:00
Lauri Ojansivu 4fd868561d Translate Buryat, Chuvash and Sakha sign-in warnings
Preserve repeated address variables and ROOT_URL. Warning and placeholder suites and 21 preservation checks pass. These translations have lower confidence and need native review.
2026-10-08 23:27:54 +03:00
Lauri Ojansivu 5a666ae521 Document Oromo, Luganda, Wolof and Akan warning translations 2026-10-08 23:26:43 +03:00
Lauri Ojansivu 1ecb67d1a0 Translate Oromo, Luganda, Wolof and Akan sign-in warnings
Preserve repeated address variables and ROOT_URL. Warning and placeholder suites and 21 preservation checks pass. These translations have lower confidence and need native review.
2026-10-08 23:26:14 +03:00
Lauri Ojansivu beafd43f75 Document four more sign-in warning translations 2026-10-08 23:24:47 +03:00
Lauri Ojansivu 7b76269efa Translate Flemish, Northern Sami, Manx and Cornish sign-in warnings
Preserve configured and opened address variables and ROOT_URL. Warning and placeholder suites and 21 preservation checks pass. Northern Sami, Manx and Cornish prose has lower confidence and needs native review.
2026-10-08 23:24:18 +03:00
Lauri Ojansivu e2b0f2b047 Document five more sign-in warning translations 2026-10-08 23:22:40 +03:00
Lauri Ojansivu 2f47eebcc4 Translate sign-in warnings in five registered languages
Add Moroccan Arabic, Hiragana, Walloon, Waray and Venetian warnings with exact address variables. Verify legacy locale identities and Hiragana script. These direct translations have lower confidence and need native review. Warning and placeholder suites and 21 preservation checks pass.
2026-10-08 23:22:08 +03:00
Lauri Ojansivu 2f9f1b1e7a Document southern African warning translations 2026-10-08 23:18:26 +03:00
Lauri Ojansivu efcdceb0b2 Translate southern African warnings and correct Venda sign-in label
Preserve exact repeated variables and the separate Venetian and Veps catalogs. Extend warning coverage to 169 paths. The prose has lower confidence and needs native review.
2026-10-08 23:17:47 +03:00
Lauri Ojansivu 4b9d0bfc2d Document Pacific warning translations 2026-10-08 23:15:30 +03:00
Lauri Ojansivu ab218dc5ce Translate five Pacific sign-in warnings
Preserve exact repeated variables and extend coverage to 164 paths. Hawaiian Samoan Tongan and Fijian prose has lower confidence and needs native review.
2026-10-08 23:14:59 +03:00
Lauri Ojansivu 42359d9daf Document Romance warnings and native sign-in labels 2026-10-08 23:13:30 +03:00
Lauri Ojansivu 5cfd2010f8 Correct Aromanian and Latin sign-in labels 2026-10-08 23:12:47 +03:00
Lauri Ojansivu e2c52f5efd Translate five Romance and Latin sign-in warnings
Preserve exact repeated variables and extend coverage to 159 paths. These translations have lower confidence and need native review.
2026-10-08 23:11:51 +03:00
Lauri Ojansivu 28500d1843 Document additional Indic warning translations 2026-10-08 23:09:52 +03:00
Lauri Ojansivu 50d9ba91ad Translate five additional Indic sign-in warnings
Preserve exact repeated variables and locale scripts. Extend coverage to 154 paths. These translations have lower confidence and need native review.
2026-10-08 23:09:17 +03:00
Lauri Ojansivu 595f6ffd96 Document regional European warning translations 2026-10-08 23:08:02 +03:00
Lauri Ojansivu 9ae7e40076 Translate five regional European sign-in warnings
Preserve exact repeated variables and extend coverage to 149 paths. These translations have lower confidence and need native review.
2026-10-08 23:07:32 +03:00
Lauri Ojansivu bce8e40076 Document regional Romance warning translations 2026-10-08 23:06:13 +03:00
Lauri Ojansivu 28c61baefc Translate regional Romance sign-in warnings
Preserve exact repeated variables and extend coverage to 144 paths. These translations have lower confidence and need native review.
2026-10-08 23:05:36 +03:00
Lauri Ojansivu f6b5af4b14 Document five additional African warning translations 2026-10-08 23:04:19 +03:00
Lauri Ojansivu c0e538a582 Translate five additional African sign-in warnings
Preserve repeated variables and extend warning coverage to 139 paths. Kinyarwanda Kirundi Chichewa Sesotho and Setswana prose has lower confidence and needs native review.
2026-10-08 23:03:49 +03:00
Lauri Ojansivu 041d617bfc Document additional African sign-in warning translations 2026-10-08 23:02:35 +03:00
Lauri Ojansivu 45f9929be3 Translate additional African sign-in warnings
Add six values with exact repeated variables. Extend coverage to 134 paths. These translations have lower confidence and need native review.
2026-10-08 23:01:59 +03:00
Lauri Ojansivu f4e6c04a04 Document Tatar Bashkir Tajik and Turkmen warning translations 2026-10-08 23:00:26 +03:00
Lauri Ojansivu 533c3b9f56 Translate Tatar Bashkir Tajik and Turkmen sign-in warnings
Preserve exact repeated variables and locale scripts. Extend coverage to 128 paths. These translations have lower confidence and need native review.
2026-10-08 22:59:42 +03:00
Lauri Ojansivu b0dad5abf9 Document Sindhi Pashto Kurdish and Uyghur warning translations 2026-10-08 22:58:34 +03:00
Lauri Ojansivu 9008e2674d Translate Sindhi Pashto Kurdish and Uyghur sign-in warnings
Preserve exact repeated variables and locale scripts. Extend coverage to 124 paths. These translations have lower confidence and need native review.
2026-10-08 22:57:59 +03:00
Lauri Ojansivu 97ea0bd3d6 Document additional script and Pacific warning translations 2026-10-08 22:56:41 +03:00
Lauri Ojansivu b5d08b94b2 Translate Amharic Burmese Khmer and Pacific sign-in warnings
Preserve repeated variables and the Khmer regional symlink. Extend warning coverage to 119 paths. Amharic and Pacific prose has lower confidence and needs native review.
2026-10-08 22:56:11 +03:00
Lauri Ojansivu eea1b93bdc Document five further sign-in warning translations 2026-10-08 22:54:07 +03:00
Lauri Ojansivu 1b4347dbac Translate sign-in address warning in five more languages
Preserve repeated variables and extend coverage to 112 catalogs. Malagasy Somali and Hausa prose has lower confidence and needs native review.
2026-10-08 22:53:32 +03:00
Lauri Ojansivu 6436f35401 Document Celtic and western European warning translations 2026-10-08 22:52:15 +03:00
Lauri Ojansivu 1c0e2b0138 Translate Celtic and western European sign-in warnings
Preserve repeated address variables and extend warning coverage to 107 catalogs. Regional prose has lower confidence and needs native review.
2026-10-08 22:51:39 +03:00
Lauri Ojansivu aab6056007 Document Central Asian and Caucasian warning translations 2026-10-08 22:50:10 +03:00
Lauri Ojansivu 09991df86c Translate Central Asian and Caucasian sign-in warnings
Add 11 values with repeated address variables preserved. Extend warning regression coverage to 97 catalogs.
2026-10-08 22:49:33 +03:00
Lauri Ojansivu 5333ae39db Document additional sign-in warning translations 2026-10-08 22:48:19 +03:00
Lauri Ojansivu 91ca8cdcb8 Translate sign-in address warning in 15 more catalogs
Preserve repeated address variables and regional wording. Extend warning regression coverage to 86 catalogs.
2026-10-08 22:47:46 +03:00
Lauri Ojansivu 030171b557 Document South Asian sign-in warning translations 2026-10-08 22:46:13 +03:00
Lauri Ojansivu 39626be9b0 Translate sign-in address warning in South Asian languages
Add 13 catalog values with exact repeated address variables and the ROOT_URL configuration key. Extend warning regression coverage to 71 catalogs.
2026-10-08 22:45:41 +03:00
Lauri Ojansivu e8fb15ff3b Document expanded sign-in warning translations 2026-10-08 22:44:09 +03:00
Lauri Ojansivu 94ab6a2168 Translate sign-in address warning in 32 more catalogs
Extend regional coverage and add Arabic Persian Hebrew and Ukrainian wording. Preserve repeated address variables and the ROOT_URL configuration key.
2026-10-08 22:43:37 +03:00
Lauri Ojansivu 52237542de Document sign-in address translation checks 2026-10-08 22:41:58 +03:00
Lauri Ojansivu 928efb3d3e Translate sign-in address warning in 26 locales
Preserve repeated expected and actual address variables, their rendering order and the literal ROOT_URL configuration key.
2026-10-08 22:41:26 +03:00
Lauri Ojansivu c738148ed4 Document Finnish import and sign-in translation checks 2026-10-08 22:39:30 +03:00
Lauri Ojansivu e9f5e08aae Translate remaining Finnish import and sign-in prose
Preserve command names, hierarchy markers, omitted-data warnings and repeated origin variables. The fill tool now reports no Finnish placeholders within its scope.
2026-10-08 22:38:56 +03:00
Lauri Ojansivu a5ff353332 Document additional import translation checks 2026-10-08 22:37:05 +03:00
Lauri Ojansivu a79b9f811a Translate seven import formats in Wu Papiamento and Yiddish
Preserve source commands, column names, hierarchy markers and omitted-data warnings. Repair the three translation completion failures without weakening coverage.
2026-10-08 22:36:29 +03:00
Lauri Ojansivu ae381f82f3 Document Finnish import translation checks 2026-10-08 22:31:30 +03:00
Lauri Ojansivu 73622c9539 Translate eight Finnish import instructions
Preserve source commands, file extensions, first-board limits and archive mappings. Record the full placeholder inventory check.
2026-10-08 22:30:53 +03:00
Lauri Ojansivu 7435ce3392 Document Kashubian planning translation checks 2026-10-08 22:28:46 +03:00
Lauri Ojansivu 6b08c3d3dd Translate Kashubian Scrum planning and recovery messages
Preserve exact placeholders and distinguish rollback from keeping records. Recovery prose has lower confidence and needs native review.
2026-10-08 22:28:06 +03:00
Lauri Ojansivu 63c62b8037 Document Scottish Gaelic planning translation checks 2026-10-08 22:25:42 +03:00
Lauri Ojansivu 10a3a73023 Translate Scottish Gaelic Scrum planning and recovery messages
Preserve exact placeholders and distinguish rollback from keeping records. Recovery prose has lower confidence and needs native review.
2026-10-08 22:25:12 +03:00
Lauri Ojansivu 2c0bee8bb1 Document Welsh planning translation checks 2026-10-08 22:23:00 +03:00
Lauri Ojansivu 4611b60670 Translate Welsh Scrum planning and recovery messages
Preserve exact placeholders in both Welsh catalogs and distinguish rollback from keeping records. Recovery prose needs native review.
2026-10-08 22:22:21 +03:00
Lauri Ojansivu 44a64afa26 Document Basque planning translation checks 2026-10-08 22:19:47 +03:00
Lauri Ojansivu 4fee733285 Translate Basque Scrum planning and recovery messages
Preserve exact placeholders and distinguish rollback from keeping records. Recovery prose needs native review.
2026-10-08 22:19:04 +03:00
Lauri Ojansivu ed782347ad Document Breton planning translation checks 2026-10-08 22:17:17 +03:00
Lauri Ojansivu 4e54dda91c Translate Breton Scrum planning and recovery messages
Recovery prose has lower confidence and needs native review. Preserve exact placeholders and distinguish rollback from keeping records.
2026-10-08 22:16:43 +03:00
Lauri Ojansivu d332544812 Document Occitan planning translation checks 2026-10-08 22:14:20 +03:00
Lauri Ojansivu cd0795e818 Record Quire, Wrike, Teamwork.com, Businessmap, Redmine, Notion and Plane in the changelog.
The Upcoming import and export group now lists twenty-one tools, the In
short paragraph names the larger set without listing every one, and the
TODO Later formats bullet counts the twenty-one added on 2026-10-08.

Thanks to xet7 !
2026-10-08 22:13:54 +03:00
Lauri Ojansivu d5d4735e5b Translate Occitan Scrum planning and recovery messages
Recovery prose has lower confidence and needs native review. Preserve exact placeholders and distinguish rollback from keeping records.
2026-10-08 22:13:25 +03:00
Lauri Ojansivu d21a263393 Import boards from Plane issue exports.
Plane's Workspace Settings > Exports .zip is read with its CSV, JSON or XLSX
files (one per project, as Plane's export task and IssueExportSerializer
write them), or one of those files chosen or pasted: states become lists,
projects swimlanes, and parents, labels, dates, links and comments keep
their places, with priority, estimate and cycle as custom fields. Plane has
no import of its own to export to, so this is import only.

Thanks to xet7 !
2026-10-08 22:12:50 +03:00
Lauri Ojansivu 9a93cdf57f Document Asturian Scrum planning translations 2026-10-08 22:09:18 +03:00
Lauri Ojansivu a1aea15a2c Translate Asturian Scrum planning and recovery messages
Recovery prose has lower confidence and needs native review. Preserve exact placeholders and distinguish rollback from keeping records.
2026-10-08 22:08:51 +03:00
Lauri Ojansivu 5c82bdd16e Document Aragonese Scrum planning translations 2026-10-08 22:06:56 +03:00
Lauri Ojansivu b1156bcefa Translate Aragonese Scrum planning and recovery messages
Recovery prose has lower confidence and needs native review. Preserve exact placeholders and distinguish rollback from keeping records.
2026-10-08 22:06:27 +03:00
Lauri Ojansivu 3688b645c0 Document Neapolitan Scrum planning translations 2026-10-08 22:04:34 +03:00
Lauri Ojansivu 07341319f3 Translate Neapolitan Scrum planning and recovery messages
Recovery prose has lower confidence and needs native review. Preserve exact placeholders and distinguish rollback from keeping records.
2026-10-08 22:04:01 +03:00
Lauri Ojansivu f2b838a882 Document Sicilian Scrum planning translations 2026-10-08 22:01:55 +03:00
Lauri Ojansivu 85621c368c Translate Sicilian Scrum planning and recovery messages
Recovery prose has lower confidence and needs native review. Preserve exact placeholders and distinguish rollback from keeping records.
2026-10-08 22:01:26 +03:00
Lauri Ojansivu b7d3274780 Import and export boards as Notion Markdown & CSV exports.
Notion's Markdown & CSV export is read as the downloaded .zip (opened on the
server under size limits) or as one database CSV: database properties map to
lists, people, dates, tags and custom fields, and page Markdown becomes the
card description. Export writes a database CSV that Notion's CSV import
reads, and which imports back.

Thanks to xet7 !
2026-10-08 22:01:22 +03:00
Lauri Ojansivu ab79498ff5 Document Sardinian Scrum planning translations 2026-10-08 21:59:20 +03:00
Lauri Ojansivu 157995c7d5 Translate Sardinian Scrum planning and recovery messages
Recovery prose has lower confidence and needs native review. Preserve exact placeholders and distinguish rollback from keeping records.
2026-10-08 21:58:53 +03:00
Lauri Ojansivu 0eb8598607 Document Corsican Scrum planning translations 2026-10-08 21:56:40 +03:00
Lauri Ojansivu ae77f3f3b0 Translate Corsican Scrum planning and recovery messages
Recovery prose has lower confidence and needs native review. Preserve exact placeholders and distinguish rollback from keeping records.
2026-10-08 21:56:08 +03:00
Lauri Ojansivu 6c9ffd63d4 Import and export boards as Redmine issue CSV.
Redmine's issue list "Also available in: CSV" export is read by its English
column names: each status becomes a list, issues become cards with the
tracker as a label, assignee, author, dates, parent task and related issues,
and priority, category, target version, estimated time, % Done and custom
fields become custom fields. Export writes the same CSV, which imports back.

Thanks to xet7 !
2026-10-08 21:55:27 +03:00
Lauri Ojansivu 22abc50226 Document Irish Scrum planning translations 2026-10-08 21:53:59 +03:00
Lauri Ojansivu b629778b0a Translate Irish Scrum planning and recovery messages
Recovery wording has lower confidence and needs native review. Preserve placeholders and distinguish rollback from keeping records.
2026-10-08 21:53:29 +03:00
Lauri Ojansivu 019133076f Document Kannada Scrum planning translations 2026-10-08 21:50:52 +03:00
Lauri Ojansivu 63b56e9a3b Translate Kannada Scrum planning and recovery messages 2026-10-08 21:50:05 +03:00
Lauri Ojansivu edb808e266 Import and export boards as Businessmap (Kanbanize) Excel workbooks.
Businessmap's Advanced Search "Configure results" Excel download is read by
its English column names: Column becomes lists and Lane swimlanes, owners,
tags, color, dates, comments and parent links keep their places, and
Priority, Size, Type and other columns become custom fields. Export writes
the same workbook, which imports back.

Thanks to xet7 !
2026-10-08 21:49:11 +03:00
Lauri Ojansivu 25ed2d88de Document Gujarati Scrum planning translations 2026-10-08 21:47:40 +03:00
Lauri Ojansivu cc864f1520 Translate Gujarati Scrum planning and recovery messages 2026-10-08 21:47:10 +03:00
Lauri Ojansivu 0f240dac0d Document Thai Scrum planning translations 2026-10-08 21:45:01 +03:00
Lauri Ojansivu 420d1aeabc Translate Thai Scrum planning and recovery messages 2026-10-08 21:44:34 +03:00
Lauri Ojansivu 76e985a300 Document Urdu Scrum planning translations 2026-10-08 21:42:52 +03:00
Lauri Ojansivu 20603cf77d Translate Urdu Scrum planning and recovery messages 2026-10-08 21:42:24 +03:00
Lauri Ojansivu 1547b289fc Document Nepali Scrum planning translations 2026-10-08 21:40:38 +03:00
Lauri Ojansivu 7aa90f7a00 Import and export boards as Teamwork.com Excel task import templates.
Teamwork.com imports tasks into a project from an Excel template (List or
Table view > ... > Import Tasks) whose help article names ten columns:
Tasklist, Task, Description, Assign to, Start date, Due date, Priority,
Estimated time, Tags and Status. Teamwork documents no export in that
layout, so the template is what is read and written. Task lists become
lists and tasks cards; a "-", "#" or ">" before a task name, doubled per
level, makes it a subtask card of the nearest shallower task above, in
its parent's list. Comma-separated Assign to e-mails become the owner and
assignees, Start and Due date the start and due dates (date cells, ISO,
or the site's numeric format with day/month order decided for the whole
file), Tags labels, Priority low/medium/high and Estimated time (25,
01:30, 1h 15m, 1h, 2 hours) the custom fields Priority and Estimated time
(hours), and Status Complete the checkbox field Complete with the due
date marked done. Other columns, unknown values, unreadable dates and
estimates, nameless rows and subtasks without or skipping a parent level
are reported; an .xls file is refused with a request to save it as .xlsx.
Export writes the template with date cells, HH:MM estimates and subtasks
after their parents.

tests/teamworkFormat.test.cjs builds the template with ExcelJS and covers
the subtask levels, every estimate form, the negatives and the round
trip; a Playwright case imports a workbook through the page. New
instruction text is in English, pending Transifex.

Thanks to xet7 !
2026-10-08 21:40:19 +03:00
Lauri Ojansivu 7c5dbdba52 Translate Nepali Scrum planning and recovery messages 2026-10-08 21:40:05 +03:00
Lauri Ojansivu ff8f82b40d Strip tags to a fixed point in the Vikunja export test.
The test's stand-in sanitizer removed tags in one pass, the shape
tests/tagStrippingFixedPoint.test.cjs keeps out of the tree (CodeQL
js/incomplete-multi-character-sanitization). It now loops until
nothing changes.

Thanks to xet7 !
2026-10-08 21:38:50 +03:00
Lauri Ojansivu 063365e8b9 Import and export boards as Wrike Excel workbooks.
Wrike documents the columns of its Excel import, not of its export: the
help article's key, parent task, title, status, priority, assigned to,
start date, duration, end date, depends on, start date constraint and
description, custom fields to the right of description, and the official
sample excel_import_sample.xls, whose folders and projects are rows with
a path as their Title (/Folder 2/Subfolder 1/) above their tasks. WeKan
reads and writes that template, headers matched case-insensitively.
Folder rows become swimlanes, Status the lists (a custom workflow's
Custom Status wins over its group), Key the reference, Parent Task the
parent card (a folder's Key puts the task in that folder), Assigned To
"Name <email>" the owner and assignees, Start and End Date the start and
due dates, Depends On "13FS" an is-blocked-by dependency, and Priority,
Duration and every other column custom fields. A start date constraint,
a folder or project's own status and dates, the workflow columns, the
SS/FF/SF kind of a dependency, bad keys and dates, and the repeated rows
of a task in several folders are reported. Export writes the template on
a Tasks sheet with a folder row per swimlane, sequential Keys, date cells,
the Wrike status group as Status and the list as the custom field WeKan
list, which the import reads back.

tests/wrikeFormat.test.cjs builds the sample's structure with ExcelJS
and covers the negatives and the round trip; a Playwright case imports a
template through the page. New instruction text is in English, pending
Transifex.

Thanks to xet7 !
2026-10-08 21:38:34 +03:00
Lauri Ojansivu 0c19856af5 Document Tamil Scrum planning translations 2026-10-08 21:38:14 +03:00
Lauri Ojansivu fb648cd0be Translate Tamil Scrum planning and recovery messages 2026-10-08 21:37:47 +03:00
Lauri Ojansivu b0c5dbcba7 Document Bengali Scrum planning translations 2026-10-08 21:35:44 +03:00
Lauri Ojansivu 979edf4c3b Import and export boards as Quire CSV.
Quire's Import CSV reads Name, Assignee, Tag, Start, Due, Priority,
Status, Description, Parent and ID, matched by header name in any order;
its Export CSV, as the sample file its guide links and its API's
export-csv example show, also writes Started, Completed, Duration,
Estimate, Time log, Variation, Successors, Created and Created by. A
header with several values is repeated, or with merge joined by ", ".
The hierarchy is read in all three ways Quire writes it: a Parent column,
repeated ID columns by depth, and an ID path such as "#6, #8". Statuses
become lists and tasks cards with the ID as reference and the parent
task as parent card, Assignee as owner and assignees, Tag as labels,
Start (or Started), Due, Completed and Created as dates, Created by as
Requested by, and Priority (Low, High, Urgent, or the API's -1 to 2) as
a custom field; Medium is Quire's default and is left out. Dates are
read as Quire writes them, "Jun 2, 2026", or ISO 8601, and a leading '
before formula-like text is removed. Duration, Estimate, Time log,
Variation, Successors, unknown priorities and unreadable dates are
reported. Export writes the import columns with Completed, Created and
Created by, cards numbered #1... with Parent, and repeated Assignee and
Tag columns.

tests/quireCsv.test.cjs covers the sample file, the API's example, the
import shape, the negatives and the round trip; a Playwright case
imports through the page and the export audit fetches the format. New
instruction text is in English, pending Transifex.

Thanks to xet7 !
2026-10-08 21:35:31 +03:00
Lauri Ojansivu 3b9e8bdcce Translate Bengali Scrum planning and recovery messages 2026-10-08 21:35:09 +03:00
Lauri Ojansivu ed6d0c49e8 Document Hindi Scrum planning translations 2026-10-08 21:33:24 +03:00
Lauri Ojansivu 473b565285 Translate Hindi Scrum planning and recovery messages 2026-10-08 21:32:46 +03:00
Lauri Ojansivu 9e349932d7 Document Malay and Indonesian Scrum translations 2026-10-08 21:30:52 +03:00
Lauri Ojansivu 8d68bd56cb Translate Malay and Indonesian Scrum planning and recovery 2026-10-08 21:30:23 +03:00
Lauri Ojansivu d49c78adc1 Document Persian Scrum import and recovery translations 2026-10-08 21:27:18 +03:00
Lauri Ojansivu f0c89ad318 Translate Persian Scrum import and recovery messages 2026-10-08 21:26:54 +03:00
Lauri Ojansivu fb5a7fb47e Document Hebrew Scrum translations and coverage correction 2026-10-08 21:25:05 +03:00
Lauri Ojansivu e39aa19360 Translate Hebrew Scrum imports and recovery; refresh coverage count 2026-10-08 21:24:35 +03:00
Lauri Ojansivu 9b40cb6f64 Record in TODO Later which import and export formats are left, and why.
Thanks to xet7 !
2026-10-08 21:23:46 +03:00
Lauri Ojansivu 8565174456 Record the fourteen new import and export formats in the changelog.
Planner, MeisterTask, Obsidian Kanban, Linear, TickTick, ClickUp,
Nullboard, Kanri, Pivotal Tracker, Tasks.org, monday.com, Super
Productivity, Taiga and Vikunja open the Upcoming section with an In
short paragraph, and the translation entries already there get their
own subsection header.

Thanks to xet7 !
2026-10-08 21:21:47 +03:00
Lauri Ojansivu c4a93d8377 Document Super Productivity, Taiga and Vikunja translations 2026-10-08 21:21:06 +03:00
Lauri Ojansivu 42797d6418 Translate Super Productivity, Taiga and Vikunja imports 2026-10-08 21:20:29 +03:00
Lauri Ojansivu a8e73adf48 Import and export boards as Vikunja data exports.
Vikunja's Settings > Data Export writes, and its Vikunja Export migrator
reads, a zip with data.json (an array of projects with their tasks, views,
buckets, task_buckets and positions), filters.json, VERSION and
files/<id>. Both layouts are read: project views with buckets per view
(0.24 and newer) and bucket_id / is_done_bucket (0.21-0.23); a VERSION
older than 0.20.1 is refused, as Vikunja refuses it. The first kanban
view's buckets become lists and tasks cards in that view's order, several
projects swimlanes under their parent's name, with labels, assignees as
owner and assignees, created_by as Requested by, the dates (Go's zero time
is no date), the HTML description and comments as text, TipTap task lists
as checklists, hex color as card color, priority, percent done and done as
custom fields, subtask/parenttask as parent cards and blocking, related
and duplicate links as dependencies. Attachments, reminders, repeats,
other relation kinds, label and project colors, WIP limits, backgrounds,
reactions and saved filters are reported. The zip is uploaded, or its
data.json chosen or pasted, and opened on the server under size, entry
and inflate limits. Export writes the same zip, with a list called Done
as the done bucket and several swimlanes as child projects.

tests/vikunjaFormat.test.cjs covers both layouts, the round trip through
the zip and the negatives; Playwright cases import a zip and a pasted
data.json through the page and download the export. New instruction text
is in English, pending Transifex.

Thanks to xet7 !
2026-10-08 21:20:06 +03:00
Lauri Ojansivu 97bfa13b6c Document Bengali, Kannada and Nepali short-label translations 2026-10-08 21:18:47 +03:00
Lauri Ojansivu 68601cb89f Translate Bengali, Kannada and Nepali short interface labels 2026-10-08 21:18:11 +03:00
Lauri Ojansivu bd6a8ce019 Import and export boards as Taiga project dumps.
Taiga's Admin > Project > Export writes, and its load_dump importer reads,
one JSON object: the whole project, with people as emails and statuses,
swimlanes, sprints and roles by name. User story statuses become lists,
Taiga swimlanes swimlanes, and user stories cards in kanban order with
their tags, assignees, owner as Requested by, watchers, dates and comments
from the history entries. Tasks become subtask cards of their story with
their own status, people, dates and comments; epics and issues get
swimlanes of their own, an epic is the parent of its first story and an
issue's type, priority and severity become labels. Custom attributes,
story points (the Scrum estimate), blocked notes and requirements become
custom fields, tag colors the nearest label colors, open milestones Scrum
sprints with the backlog order as rank. Attachments (the page leaves the
embedded files out before sending), the wiki, the timeline, votes, WIP
limits, memberships and closed sprints are reported. A .json.gz dump is
unpacked first. Export writes every key Taiga's exporter writes, with
subtasks as tasks, a story's checklist items as tasks and sprints with
both dates as milestones.

Imported labels take a board-level label_colors map as well as a tag's
own color.

tests/taigaFormat.test.cjs covers the mapping, the negatives, that every
name the dump refers to exists, and round trips both ways, on a fixture
written from taiga-back's export serializers. Playwright cases import a
dump through the page, reject a malformed one, and the export audit
fetches the new format. New instruction text is in English, pending
Transifex.

Thanks to xet7 !
2026-10-08 21:17:07 +03:00
Lauri Ojansivu 9cd988a4db Document Mongolian short-label translations 2026-10-08 21:16:25 +03:00
Lauri Ojansivu 2f31971c4b Translate short Mongolian Blockly labels 2026-10-08 21:15:59 +03:00
Lauri Ojansivu b7e515dba6 Import and export boards as Super Productivity backups.
Super Productivity's Export Data writes sp-backup_*.json: {timestamp,
lastUpdate, crossModelVersion, data}, with project, task, tag, note, boards
and archiveYoung/archiveOld as {ids, entities} entity states. Projects become
swimlanes, so none is dropped; the board takes the title of the only one.
Its boards are saved filters, not containers, so lists follow its default
Kanban board: done tasks in Done, the KANBAN_IN_PROGRESS tag in In Progress,
a project's backlog in Backlog, the rest in To Do. Tasks become cards and
sub-tasks sub-task cards, tags labels, notes and web link attachments the
description, the scheduled day or time the due date (the start date when a
deadline is the due date), doneOn the end date, timeSpent spent hours,
timeEstimate and priority custom fields, and archived tasks archived cards.
Repeat configurations, reminders, issue links, project notes, local
attachments, the per-day time breakdown and the saved boards are reported;
pre-v14 backups are refused.

Export writes a backup Import from File accepts at crossModelVersion 4.5:
project, task, tag, note, boards and both archives, leaving the slices the
app fills with defaults out. Swimlanes become projects, lists one board of
panels with other lists as tags, checklists markdown task lists in the
notes, and spent hours are booked on the card's end or creation day.

tests/superProductivityFormat.test.cjs checks the mapping, the negatives,
the round trip and the export against Super Productivity's own field and
cross-model validation; a Playwright case imports through the page and the
export audit fetches the format. New instruction text is in English,
pending Transifex.

Thanks to xet7 !
2026-10-08 21:14:58 +03:00
Lauri Ojansivu 88545c8087 Document import translations and refresh remaining-work snapshot 2026-10-08 21:14:47 +03:00
Lauri Ojansivu 464ae965ed Translate Tasks.org and monday.com imports in three languages 2026-10-08 21:14:15 +03:00
Lauri Ojansivu 1ceaad80f4 Import and export boards as monday.com Excel workbooks.
monday's "Export board to Excel" is documented only through its help
article's screenshots and cannot be changed: the board's name in row 1,
then per group its name, a header row starting Name with the board's
own column titles, the items and a summary row; with "+ updates" a
<board>-updates sheet of Item ID, Item Name, User, Created At and Update
Content. Column titles are the board owner's, so they are matched by
meaning: Name is the title, Status the list (groups become swimlanes;
without a Status column the groups are the lists), Person / People /
Owner / Assignee the owner and assignees, Date / Due date / Deadline the
due date, Timeline the start and due dates, Tags labels, Item ID the
reference, Long text / Description / Notes the description, and every
other column a custom field. Updates become comments on the item they
name. Subitems are undocumented; as community reports show them - a
Subitems header row after the parent, its rows indented one column -
they become subtasks. Bad dates, nameless items and updates for unknown
items are reported. Export writes the flat table monday's own Excel
import reads: the first sheet, a header row and ISO dates.

tests/mondayFormat.test.cjs builds the documented layout with ExcelJS,
including updates and subitems, and covers the negatives and the round
trip; a Playwright case imports a workbook through the page. New
instruction text is in English, pending Transifex.

Thanks to xet7 !
2026-10-08 21:13:39 +03:00
Lauri Ojansivu 97202f0423 Document seven-format import translations 2026-10-08 21:11:42 +03:00
Lauri Ojansivu 77002d3ef6 Translate seven import formats in Wu, Papiamento and Yiddish 2026-10-08 21:11:12 +03:00
Lauri Ojansivu 68b2cceb19 Import and export boards as Tasks.org backups.
Tasks.org's backup (Settings > Backups > Export tasks) is JSON:
{version, timestamp, data:{tasks, tags, caldavAccounts, caldavCalendars,
...}}, written with every default left out. A task's list is its
caldavTasks[].calendar, named by caldavCalendars[].uuid; lists become
lists and tasks cards, with notes as description, tags as labels,
priority 0-2 as custom field Priority (High, Medium, Low), due and start
dates in epoch ms (a time only when ms % 60000 > 0, otherwise all-day,
kept as that day at 12:00 UTC), completion as the end date, elapsed time
as time spent, the estimate as custom field Estimate (hours), comments,
and caldavTasks[].remoteParent as subtask cards linked to their parent -
Tasks.org subtasks are full tasks with their own dates and completion,
which a checklist item could not keep. Deleted tasks are skipped and
reported; repeat rules, reminders, location reminders, a running timer,
attachments (the backup has no file contents), comment pictures and
undefined lists are reported. Export writes a backup Tasks.org's Import
backup accepts: one local account named after the board, a list per
WeKan list, tag definitions and per task its caldavTasks row, with
stable ids so a second restore skips tasks it already has; checklist
items become subtasks.

tests/tasksorgFormat.test.cjs runs a backup shaped like Tasks.org's
exporter output, the negatives, the fields its importer requires and the
round trip; a Playwright case imports through the page and the export
audit fetches the format. New instruction text is in English, pending
Transifex.

Thanks to xet7 !
2026-10-08 21:10:58 +03:00
Lauri Ojansivu 2e5fd9530e Import and export boards as Pivotal Tracker stories CSV.
Pivotal Tracker shut down on 2025-04-30; its stories CSV exports remain.
Columns are matched by name and repeated ones (Owned By, Blocker/Blocker
Status, Comment, Task/Task Status, Pull Request, Git Branch) collected in
order, so exports from before about 2018, which have no Priority, read
too. Current State becomes lists in Tracker's workflow order (unscheduled
to rejected), Type and Labels labels, Id the source reference, Owned By
the owner and assignees, Requested By Requested by, Created at / Accepted
at / Deadline the created, end and due dates ("Nov 22, 2014" or
11/22/2014), Estimate the Story points field and Scrum estimate, Priority
a custom field, each Iteration with its dates a Scrum sprint (one holding
only accepted stories is finished and reported), comments keep the author
and date of their "(Author - Mon D, YYYY)" tail, Task pairs a Tasks
checklist with done items, and an unresolved "#id" blocker an
is-blocked-by dependency. Pull requests, git branches, other blockers and
unknown states, types, priorities and dates are reported. Export writes
the columns Tracker's import reads, without Id, Iteration and URL.

Todoist's RFC 4180 reader is shared. tests/pivotalCsv.test.cjs runs a
2023 and a pre-2018 export, the Scrum planning, the negatives and the
round trip; a Playwright case imports through the page and the export
audit fetches the format. New instruction text is in English, pending
Transifex.

Thanks to xet7 !
2026-10-08 21:09:53 +03:00
Lauri Ojansivu eec53eb322 Import and export boards as Kanri JSON.
Kanri's Import & Export page writes, and its Import from Kanri reads, one
board (Partial Export: id, title, columns of cards, background, globalTags)
or all data (Full Export: the app settings and every board). Columns
become lists, cards become cards with their description, due date and
due-date-done flag and the card id as source reference, a card's tasks a
Tasks checklist, tags labels with their colors, and Kanri's card colors
WeKan's (teal and custom colors as hex). One import makes one board, so
from all data the first board is imported and the others are reported, as
are the background image, the countdown due date display, tags no card
uses and colors that are not colors. Imported labels now keep a source's
label color, and the collected export carries the card color and due-date
done. Export writes a single-board file Kanri's Partial import accepts.

tests/kanriFormat.test.cjs covers the mapping, all-data files, the round
trip and the negatives; Playwright cases import a board and an all-data
file through the page, and the export audit fetches the new format. New
instruction text is in English, pending Transifex.

Thanks to xet7 !
2026-10-08 21:08:40 +03:00
Lauri Ojansivu ca6c0e81c1 Document Punjabi, Swahili and Uzbek short-label translations 2026-10-08 21:07:56 +03:00
Lauri Ojansivu 85fad588e2 Translate short Punjabi, Swahili and Uzbek Blockly labels 2026-10-08 21:07:26 +03:00
Lauri Ojansivu 2725f7a2aa Import and export boards as Nullboard .nbx files.
Nullboard's "Export this board..." writes plain JSON despite the .nbx
extension: one board {format, id, revision, title, lists: [{title,
notes: [{text, raw, min}]}]}, or an array of them for "Export all
boards". Every board is checked as Nullboard's own checkImport checks
it - the five keys, a non-empty id and revision, a lists array and
format 20190412 - and anything else is refused. Lists become lists and
notes cards; a note is free text, so its first non-blank line is the
card title and the rest the description. A raw note, drawn as a
heading in the list, gets the label raw; collapsed notes, empty notes,
repeated list titles and every board after the first in an array are
reported. Export writes one board Nullboard imports, with the title and
description as the note text, checklists after them as [ ] / [x] lines
and a card labelled raw as a raw note; Nullboard notes are text only,
so other labels, dates, people and comments are not written.

tests/nullboardFormat.test.cjs runs a welcome-board shaped sample, the
negatives and a round trip through Nullboard's checkImport; a
Playwright case imports through the page and the export audit fetches
the format. New instruction text is in English, pending Transifex.

Thanks to xet7 !
2026-10-08 21:07:01 +03:00
Lauri Ojansivu 6529d03231 Document Marathi, Malayalam and Telugu short-label translations 2026-10-08 21:05:50 +03:00
Lauri Ojansivu 8be1815560 Translate short Marathi, Malayalam and Telugu Blockly labels 2026-10-08 21:05:10 +03:00
Lauri Ojansivu 1e7911a2d0 Import and export boards as ClickUp task CSV.
ClickUp documents two CSV shapes, both read by header name: the
workspace export (Task ID, Task Link, Task Type, Task Custom ID, Task
Name, Task Content, Status, each date as Unix milliseconds and as text,
Parent ID, Subtask IDs, Attachments, Assignees, Tags, Priority, List
Name, Folder Name/Path, Space Name, the time columns, Checklists and
Comments) and the Spreadsheets importer's columns (Priority 1-4,
Description content, List, Task assignee, Checklist, Time Estimate).
Statuses become lists, ClickUp lists swimlanes, the Space the board
title. Tasks become cards with Parent ID or the parent's Subtask IDs as
subtasks, Task Content as description with its attachments as links,
the [A,B] assignees and tags, the created, due and start dates (the
millisecond column, or ISO 8601), Time Spent as spent hours, and
Priority, Task Type, Custom ID, Folder and Time Estimate as custom
fields; the importer's Checklist becomes a checklist. The export's
Checklists and Comments cells have no documented format and are
reported, as are other dates. Export writes the workspace export
columns, plus the importer's Checklist column, which ClickUp maps by
name.

tests/clickupCsv.test.cjs covers both shapes, ClickUp's own importer
example, the negatives and the round trip; a Playwright case imports
through the page and the export audit fetches the format. New
instruction text is in English, pending Transifex.

Thanks to xet7 !
2026-10-08 21:04:38 +03:00
Lauri Ojansivu 46f026ddb6 Import and export boards as TickTick backup CSV.
TickTick's Generate backup writes, and Import backup reads, a CSV that
starts with a quoted Date / Version / Status preamble, then Folder Name,
List Name, Title, Kind, Tags, Content, Is Check list, the start and due
dates, Reminder, Repeat, Priority, Status, the created and completed
times, Order, Timezone, Is All Day, Is Floating, Column Name, Column
Order, View Mode, taskId and parentId, and since version 7.2 projectKind.
TickTick publishes no specification; this follows real 7.1 and 7.2
backups, as Vikunja's TickTick migrator reads them. The header is found
by its first cell and columns are read by name.

Each TickTick list becomes a swimlane and its kanban columns lists.
Tasks become cards with Content as description and its ▫ / ▪ lines as
the checklist, tags as labels, and the start, due, created and completed
dates; an all-day start or due date is the calendar day in the task's
Timezone, so it does not move. Status 1 is completed, 2 archived and -1
the label won't do; Priority 1/3/5 and Folder Name are custom fields;
taskId and parentId make subtasks. Reminders, repeat rules and unknown
codes are reported. Export writes a 7.2 backup for one TickTick list.

tests/ticktickCsv.test.cjs covers 7.1 and 7.2 backups, time zones, the
negatives and the round trip; a Playwright case imports through the page
and the export audit fetches the format. New instruction text is in
English, pending Transifex.

Thanks to xet7 !
2026-10-08 21:02:01 +03:00
Lauri Ojansivu 5331f1b7b9 Document Czech, Japanese and Chinese short-label translations 2026-10-08 21:00:36 +03:00
Lauri Ojansivu 09c9ce2915 Translate short Czech, Japanese and Chinese labels 2026-10-08 21:00:04 +03:00
Lauri Ojansivu fb7cbc66e6 Import and export boards as Linear CSV.
Linear exports issues as CSV - the workspace export or a view's - with
ID, Team, Title, Description, Status, Estimate, Priority, Project,
Creator, Assignee, Labels, Cycle, the created, started, completed,
canceled and archived dates, Due Date and Parent issue, plus columns it
has added over time; they are matched by header name. Values are read as
Linear's own open-source CSV importer reads them: priority names, labels
joined by ", ", and a leading ' before text a spreadsheet could take for
a formula, which is removed. Statuses become lists and teams swimlanes;
issues become cards with the ID as reference and Parent issue as the
parent card, Assignee as owner, Creator as Requested by, the labels and
dates, Archived as an archived card, and Priority, Estimate, Project,
Cycle and Canceled as custom fields. Updated, Triaged, cycle dates,
Initiatives, milestones, SLA Status, non-ISO dates and unknown priorities
are reported. Export writes Linear's columns in its order, with the same
formula guard.

tests/linearCsv.test.cjs covers the mapping, the parent link, the
negatives and the round trip; a Playwright case imports through the page
and the export audit fetches the format. New instruction text is in
English, pending Transifex.

Thanks to xet7 !
2026-10-08 20:59:26 +03:00
Lauri Ojansivu 4fc3fc382b Document Yiddish and Papiamento short-label translations 2026-10-08 20:58:20 +03:00
Lauri Ojansivu c07736a824 Translate short Blockly labels in Yiddish and Papiamento 2026-10-08 20:57:50 +03:00
Lauri Ojansivu 435461f939 Import and export boards as Obsidian Kanban plugin files.
The plugin keeps a board in one Markdown file, and the generic Markdown
import read it only partly: the frontmatter and settings footer were
reported as unknown lines, #tags and @{dates} stayed in titles, and the
Complete lanes and the Archive were lost. This follows the plugin's own
parser and writer: kanban-plugin: board frontmatter (basic accepted),
## Lane (limit) headings, **Complete** lanes, - [ ] cards with 4-space or
tab continuation lines, *** and ## Archive, and the %% kanban:settings %%
JSON footer, whose date-format, date-trigger and time-trigger are used.
Lanes become lists with the card limit as WIP limit; #tags labels;
@{date} @@{time} the due date; the Tasks plugin's dates (due, start,
created, done, scheduled) and priority emoji the dates and a Priority
custom field; its id and depends-on the reference and blocked-by links;
Dataview key:: value fields custom fields; task lines in a card's body
its checklist; [x] and Complete lanes the done label, as the Markdown
import does; the Archive archived cards. Recurrence, cancelled dates and
other frontmatter keys are reported. Export writes the plugin's layout.

Imported columns now carry a WIP limit: KanboardCreator turns a column's
wip_limit, or Kanboard's own task_limit, which it dropped until now, into
the list's limit.

tests/obsidianKanban.test.cjs covers the plugin's sample layout, custom
date formats, the negatives and the round trip; a Playwright case imports
through the page and the export audit fetches the format. New instruction
text is in English, pending Transifex.

Thanks to xet7 !
2026-10-08 20:57:08 +03:00
Lauri Ojansivu c5a00d5b51 Document Finnish, German and French short-label translations 2026-10-08 20:56:25 +03:00
Lauri Ojansivu 7b7f419f00 Translate short Blockly labels in Finnish, German and French 2026-10-08 20:55:57 +03:00
Lauri Ojansivu 5bdd700bd5 Document MeisterTask instruction translations 2026-10-08 20:54:45 +03:00
Lauri Ojansivu f462841831 Translate MeisterTask import instructions in three languages 2026-10-08 20:54:15 +03:00
Lauri Ojansivu 87d33a5a7d Document three Planner instruction translations 2026-10-08 20:52:59 +03:00
Lauri Ojansivu 24e73687c9 Translate Planner import instructions in Wu, Papiamento and Yiddish 2026-10-08 20:52:30 +03:00
Lauri Ojansivu 804c7183a2 Import and export boards as MeisterTask project CSV.
MeisterTask reads one CSV shape and writes another; both are matched by
header name. Its import shape, from MeisterTask's own sample file, is
project,section,name,notes,due_date,status,tags; its export header is
id,token,name,notes,created_at,updated_at,status,due_date,
status_updated_at,assignee,section,tags. Sections become lists and tasks
cards, with notes as description, "; "-separated tags as labels, the
assignee as owner, due and created dates in ISO 8601, status 2 as
completed with status_updated_at (or completed_at) as the end date, and
status 8 or 16 (binned, archived) as archived cards. Columns MeisterTask
documents no shape for - checklists, comments, custom fields, tracked
time - unknown status codes and non-ISO dates are reported. Export writes
the import shape, which MeisterTask reads back.

Todoist's RFC 4180 reader is shared and names the format in its errors.
tests/meistertaskCsv.test.cjs runs MeisterTask's published sample, the
export header, the negatives and the round trip; a Playwright case
imports through the page and the export audit fetches the format. New
instruction text is in English, pending Transifex.

Thanks to xet7 !
2026-10-08 20:52:04 +03:00
Lauri Ojansivu 4c2b3f014a Document short translation audit coverage 2026-10-08 20:50:25 +03:00
Lauri Ojansivu e17d58820d Expose short untranslated prose for locale review 2026-10-08 20:49:42 +03:00
Lauri Ojansivu 7ef5b875fa Import and export boards as Microsoft Planner Excel workbooks.
Planner's "Export plan to Excel" writes one sheet, Tasks: plan name, plan
ID and export date on rows 1-3, then Task ID, Task Name, Bucket Name,
Progress, Priority, Assigned To, Created By, the created, start, due and
completed dates, Late, Completed By, Description, Completed Checklist
Items, Checklist Items and Labels. Columns are matched by name, as newer
exports add Is Recurring. Buckets become lists and tasks cards, with the
Task ID as source reference, the ;-separated assignees as owner and
assignees, Created By as Requested by, the checklist and labels, and
Progress, Priority and Completed By as custom fields. Dates are text in
the exporting user's locale: day/month order is decided for the whole
file, and reported when every date could be read either way; date cells
keep their day. Which checklist items were done (Planner exports only a
count), a recurring task's repeat and unknown values are reported.
Export writes the same workbook as .xlsx.

The layout follows a real Planner export, the MIT-licensed test data of
the plannr package, added under tests/fixtures/planner/ with its
licence. tests/plannerFormat.test.cjs imports it, round-trips an export
through ExcelJS and covers the negatives; Playwright cases import the
file through the page and download the export. New instruction text is
in English, pending Transifex.

Thanks to xet7 !
2026-10-08 20:48:39 +03:00
Lauri Ojansivu ed682ba7cb Record Wu review and current translation test results 2026-10-08 20:47:46 +03:00
Lauri Ojansivu 151d42286a Document Wu Blockly label translations 2026-10-08 20:43:37 +03:00
Lauri Ojansivu 07a09ca77d Translate remaining English Wu Blockly labels 2026-10-08 20:42:56 +03:00
Lauri Ojansivu cd4413bec4 Document Wu flow report corrections 2026-10-08 20:41:29 +03:00
Lauri Ojansivu 3e3a2a4f4c Restore complete Wu flow report explanations 2026-10-08 20:40:58 +03:00
Lauri Ojansivu ff59569619 Document Wu import and sync corrections 2026-10-08 20:39:29 +03:00
Lauri Ojansivu 72b11e7fe2 Correct Wu import, WIP group and sync instructions 2026-10-08 20:38:57 +03:00
Lauri Ojansivu 9103e94822 Document Wu repair result and account corrections 2026-10-08 20:37:45 +03:00
Lauri Ojansivu 736dac75d9 Correct Wu account messages and repair result counts 2026-10-08 20:37:13 +03:00
Lauri Ojansivu 246c12864b Record the History chain, Blockly and next import format decisions in TODO Later.
The coordinated History chain stays switched on offline only, Blockly
translations stay paused, and Microsoft Planner is the next import and
export format.

Thanks to xet7 !
2026-10-08 20:36:54 +03:00
Lauri Ojansivu e34fa76f05 Document Wu job control corrections 2026-10-08 20:36:10 +03:00
Lauri Ojansivu d8b8afd73f Correct Wu job controls and migration ranges 2026-10-08 20:35:43 +03:00
Lauri Ojansivu f3d20593d5 Document Wu monitoring label corrections 2026-10-08 20:34:39 +03:00
Lauri Ojansivu 92c494d492 Correct Wu monitoring and repeat interval labels 2026-10-08 20:34:08 +03:00
Lauri Ojansivu aa36ee0e5e Document Wu repair prompt corrections 2026-10-08 20:33:03 +03:00
Lauri Ojansivu 3d7d096229 Correct Wu repair prompts and migration field names 2026-10-08 20:32:36 +03:00
Lauri Ojansivu 2b85aca061 Document Wu storage and task corrections 2026-10-08 20:31:13 +03:00
Lauri Ojansivu d80d26740e Correct Wu storage and scheduled task meanings 2026-10-08 20:30:42 +03:00
Lauri Ojansivu 3a7b940c89 Document Wu cloud setup and migration corrections 2026-10-08 20:29:36 +03:00
Lauri Ojansivu 881899ee4a Correct Wu cloud setup and migration statuses 2026-10-08 20:29:08 +03:00
Lauri Ojansivu 4f98e29e6e Document Wu cloud storage help corrections 2026-10-08 20:28:03 +03:00
Lauri Ojansivu 7a0460e755 Correct Wu anonymization and cloud storage help 2026-10-08 20:27:35 +03:00
Lauri Ojansivu 8992c000b5 Document Wu settings help corrections 2026-10-08 20:26:13 +03:00
Lauri Ojansivu b37c643b17 Correct Wu settings help and anonymization warning 2026-10-08 20:25:48 +03:00
Lauri Ojansivu 05193fc32f Document Wu database migration help corrections 2026-10-08 20:24:15 +03:00
Lauri Ojansivu 202684210a Correct Wu storage and database migration help 2026-10-08 20:23:45 +03:00
Lauri Ojansivu 4203db7480 Document Wu account and scheduled-job corrections 2026-10-08 20:22:31 +03:00
Lauri Ojansivu c3c45288a7 Correct Wu account and scheduled-job wording 2026-10-08 20:22:04 +03:00
Lauri Ojansivu b9ef3d828a Document Wu recurrence and lockout corrections 2026-10-08 20:20:43 +03:00
Lauri Ojansivu bf69929f30 Correct Wu Markdown, recurrence and lockout wording 2026-10-08 20:20:14 +03:00
Lauri Ojansivu 03a9b9e950 Document Wu storage and progress corrections 2026-10-08 20:19:01 +03:00
Lauri Ojansivu 5bf865dd39 Correct Wu storage, progress and workspace wording 2026-10-08 20:18:34 +03:00
Lauri Ojansivu 269f5c32a0 Document Wu diagnostics and storage corrections 2026-10-08 20:17:26 +03:00
Lauri Ojansivu f136811382 Correct Wu diagnostics, checklist and storage meanings 2026-10-08 20:17:01 +03:00
Lauri Ojansivu 67560061b5 Document Wu animation and invitation corrections 2026-10-08 20:15:36 +03:00
Lauri Ojansivu bdff13b866 Correct Wu animation and invitation wording 2026-10-08 20:15:07 +03:00
Lauri Ojansivu 59f86821f5 Document Wu report and template corrections 2026-10-08 20:13:59 +03:00
Lauri Ojansivu 4b4fd5d9f6 Correct Wu report, template and creator wording 2026-10-08 20:13:32 +03:00
Lauri Ojansivu 7dd9c08188 Document Wu dependency and map translation corrections 2026-10-08 20:12:13 +03:00
Lauri Ojansivu b8d6041ca4 Correct Wu dependency, map and server instructions 2026-10-08 20:11:41 +03:00
Lauri Ojansivu ab7edffb75 Document Wu search logic help corrections 2026-10-08 20:08:17 +03:00
Lauri Ojansivu df966f107c Correct Wu search logic help translations 2026-10-08 20:07:47 +03:00
Lauri Ojansivu e76a1b2bfe Document Wu search help corrections 2026-10-08 20:06:33 +03:00
Lauri Ojansivu 0ea8f7c6fd Correct Wu search help translations 2026-10-08 20:06:04 +03:00
Lauri Ojansivu 56465b9d94 Document Wu search predicate corrections 2026-10-08 20:04:54 +03:00
Lauri Ojansivu 8ab081b960 Correct Wu search predicate translations 2026-10-08 20:04:28 +03:00
Lauri Ojansivu 30b9cf602b Document Wu search result and operator corrections 2026-10-08 20:03:11 +03:00
Lauri Ojansivu c890e36ab9 Correct Wu search result and operator translations 2026-10-08 20:02:40 +03:00
Lauri Ojansivu 64b12006d5 Document Wu shared template and card view corrections 2026-10-08 20:01:25 +03:00
Lauri Ojansivu 199e533493 Correct Wu shared template and card view translations 2026-10-08 20:01:01 +03:00
Lauri Ojansivu f29bf13a42 Document Wu calendar and domain corrections 2026-10-08 19:59:45 +03:00
Lauri Ojansivu e696cc805e Correct Wu calendar and domain translations 2026-10-08 19:59:20 +03:00
Lauri Ojansivu f0d84f6196 Document Wu notification control and role corrections 2026-10-08 19:58:21 +03:00
Lauri Ojansivu 19b36056dc Correct Wu notification control and role translations 2026-10-08 19:57:55 +03:00
Lauri Ojansivu c39265e56a Document Wu reminders and card control corrections 2026-10-08 19:56:38 +03:00
Lauri Ojansivu 76665c8a84 Correct Wu reminders and card control translations 2026-10-08 19:56:12 +03:00
Lauri Ojansivu 1e6e7fb5a1 Document Wu date and placement corrections 2026-10-08 19:55:07 +03:00
Lauri Ojansivu 7deea150f7 Correct Wu date and placement translations 2026-10-08 19:54:33 +03:00
Lauri Ojansivu d19db8aaff Document Wu login and configuration literal corrections 2026-10-08 19:53:27 +03:00
Lauri Ojansivu 76a68a3c8a Correct Wu login and configuration literals 2026-10-08 19:53:03 +03:00
Lauri Ojansivu 86a7bd5e7a Document Wu rule field action corrections 2026-10-08 19:51:55 +03:00
Lauri Ojansivu fa956fdbc8 Correct Wu rule field action translations 2026-10-08 19:51:27 +03:00
Lauri Ojansivu f0fc0b7fb5 Document Wu checklist and movement action corrections 2026-10-08 19:50:22 +03:00
Lauri Ojansivu 7563d81d24 Correct Wu checklist and movement action translations 2026-10-08 19:49:56 +03:00
Lauri Ojansivu 5974ca4e33 Document Wu rule condition corrections 2026-10-08 19:48:54 +03:00
Lauri Ojansivu 1795e650a1 Correct Wu rule condition translations 2026-10-08 19:48:27 +03:00
Lauri Ojansivu 33600ac255 Document Wu rule movement and completion corrections 2026-10-08 19:47:26 +03:00
Lauri Ojansivu 5bf8f7493b Correct Wu rule movement and completion translations 2026-10-08 19:46:59 +03:00
Lauri Ojansivu d7c913d70e Document Wu rule import and schedule corrections 2026-10-08 19:45:58 +03:00
Lauri Ojansivu 64513444e5 Correct Wu rule import and schedule translations 2026-10-08 19:45:34 +03:00
Lauri Ojansivu eb75d16bb3 Document Wu rule trigger corrections 2026-10-08 19:44:31 +03:00
Lauri Ojansivu 49af179c15 Correct Wu rule trigger translations 2026-10-08 19:44:04 +03:00
Lauri Ojansivu 3b82a5e68a Document Wu parent card and positional activity corrections 2026-10-08 19:42:56 +03:00
Lauri Ojansivu 84180373a0 Correct Wu parent card and positional activity translations 2026-10-08 19:42:29 +03:00
Lauri Ojansivu db6655d7d2 Document Wu organization and deletion corrections 2026-10-08 19:41:14 +03:00
Lauri Ojansivu 3a35c59529 Correct Wu organization and deletion translations 2026-10-08 19:40:49 +03:00
Lauri Ojansivu 5136a961ec Document Wu administration and field scope corrections 2026-10-08 19:39:41 +03:00
Lauri Ojansivu ff8985d1bd Correct Wu administration and field scope translations 2026-10-08 19:39:11 +03:00
Lauri Ojansivu 7fbb086a65 Document Wu limits and mail setting corrections 2026-10-08 19:38:03 +03:00
Lauri Ojansivu 3511aa9f18 Correct Wu limits and mail setting translations 2026-10-08 19:37:33 +03:00
Lauri Ojansivu fe2d0614f2 Document Wu tracking and link corrections 2026-10-08 19:36:15 +03:00
Lauri Ojansivu bcdd7f3f1c Correct Wu tracking and link translations 2026-10-08 19:35:42 +03:00
Lauri Ojansivu db97ba99d8 Document Wu shortcuts and starred item corrections 2026-10-08 19:34:33 +03:00
Lauri Ojansivu 8077105cbd Correct Wu shortcuts and starred item translations 2026-10-08 19:34:08 +03:00
Lauri Ojansivu c4ef4f5ecb Document Wu visibility and member removal corrections 2026-10-08 19:33:01 +03:00
Lauri Ojansivu 2ab39fc863 Correct Wu visibility and member removal translations 2026-10-08 19:32:33 +03:00
Lauri Ojansivu 881d92cb3a Document Wu selection and notification corrections 2026-10-08 19:31:07 +03:00
Lauri Ojansivu f3a7f4f6ed Correct Wu selection and notification translations 2026-10-08 19:30:41 +03:00
Lauri Ojansivu c1e2ee8645 Document Wu member and list operation corrections 2026-10-08 19:29:39 +03:00
Lauri Ojansivu 9aea43a6d9 Correct Wu member and list operation translations 2026-10-08 19:29:16 +03:00
Lauri Ojansivu d5b1e87c81 Document Wu Trello import corrections 2026-10-08 19:27:54 +03:00
Lauri Ojansivu efbc4911e2 Correct Wu Trello import translations 2026-10-08 19:27:30 +03:00
Lauri Ojansivu 61fc20f28b Document Wu import instruction corrections 2026-10-08 19:26:16 +03:00
Lauri Ojansivu 53273cb4d3 Correct Wu import instruction translations 2026-10-08 19:25:52 +03:00
Lauri Ojansivu 24a8972383 Document Wu sorting and filter corrections 2026-10-08 19:24:33 +03:00
Lauri Ojansivu 74ea292fc6 Correct Wu sorting and filter translations 2026-10-08 19:24:06 +03:00
Lauri Ojansivu 66d6f37176 Document Wu error and export corrections 2026-10-08 19:22:49 +03:00
Lauri Ojansivu e526e8b3d6 Correct Wu error and export translations 2026-10-08 19:22:21 +03:00
Lauri Ojansivu 4f11ddf29e Document Wu email and field message corrections 2026-10-08 19:21:13 +03:00
Lauri Ojansivu b6057034cd Correct Wu email and field message translations 2026-10-08 19:20:47 +03:00
Lauri Ojansivu 7162ffd21c Document Wu permissions and copying corrections 2026-10-08 19:19:24 +03:00
Lauri Ojansivu a0ac16de9d Correct Wu permissions and copying translations 2026-10-08 19:19:00 +03:00
Lauri Ojansivu 92262eeb79 Document Wu color and comment role corrections 2026-10-08 19:17:50 +03:00
Lauri Ojansivu 855127fa63 Correct Wu color and comment role translations 2026-10-08 19:17:26 +03:00
Lauri Ojansivu 0ab7588d42 Document Wu member search and toggle corrections 2026-10-08 19:16:20 +03:00
Lauri Ojansivu e0b7ac722b Correct Wu member search and toggle translations 2026-10-08 19:15:53 +03:00
Lauri Ojansivu 0b36fecc01 Document Wu planning and card dialog corrections 2026-10-08 19:14:38 +03:00
Lauri Ojansivu 034d1aa8d4 Correct Wu planning and card dialog translations 2026-10-08 19:14:14 +03:00
Lauri Ojansivu 9e2ad756f7 Document Wu voting and archive corrections 2026-10-08 19:13:02 +03:00
Lauri Ojansivu 2a2f48aff0 Correct Wu voting and archive translations 2026-10-08 19:12:32 +03:00
Lauri Ojansivu 4732a009c2 Document Wu timeline and card lifecycle corrections 2026-10-08 19:11:33 +03:00
Lauri Ojansivu 91be958efb Correct Wu timeline and card lifecycle translations 2026-10-08 19:11:05 +03:00
Lauri Ojansivu e5b7374699 Document Wu board visibility corrections 2026-10-08 19:09:42 +03:00
Lauri Ojansivu 0da6bfb61b Correct Wu board visibility and membership translations 2026-10-08 19:09:13 +03:00
Lauri Ojansivu d44a4157d5 Refresh translation backlog and verification snapshot 2026-10-08 19:08:09 +03:00
Lauri Ojansivu 4e09eb1821 Document Wu archive and attachment corrections 2026-10-08 19:06:32 +03:00
Lauri Ojansivu c5c1884f3c Correct Wu archive and attachment translations 2026-10-08 19:06:03 +03:00
Lauri Ojansivu 4f5cc2d52c Document Wu checklist and member corrections 2026-10-08 19:04:37 +03:00
Lauri Ojansivu 00c07df43e Correct Wu checklist and member translations 2026-10-08 19:04:10 +03:00
Lauri Ojansivu 1f59b9811c Document Wu width and shortcut corrections 2026-10-08 19:03:10 +03:00
Lauri Ojansivu ee28656601 Correct Wu list width and shortcut settings 2026-10-08 19:02:38 +03:00
Lauri Ojansivu bea6ed452b Document Wu workspace corrections 2026-10-08 18:59:06 +03:00
Lauri Ojansivu 865db11a14 Correct Wu workspace and home-board instructions
Repair 15 values, restore the single-board restriction and preserve removal warnings and positional date roles. Wu prose is lower confidence and needs native review.

Thanks to xet7.
2026-10-08 18:58:40 +03:00
Lauri Ojansivu 626502d663 Document Wu checklist and Markdown corrections 2026-10-08 18:57:28 +03:00
Lauri Ojansivu 51e3e8b375 Correct Wu checklist roles and Markdown label
Repair eight activity and workspace values, preserve placeholder roles and restore the Markdown format name. Wu prose is lower confidence and needs native review.

Thanks to xet7.
2026-10-08 18:56:58 +03:00
Lauri Ojansivu 06988d2574 Document Wu activity argument corrections 2026-10-08 18:55:54 +03:00
Lauri Ojansivu fc8a66d6ab Correct Wu activity summary argument roles
Replace 25 Mandarin-like summaries with Wu wording and restore positional percent arguments to their source roles. Update audit records. Wording is lower confidence and needs native review.

Thanks to xet7.
2026-10-08 18:55:23 +03:00
Lauri Ojansivu 024f5aa613 Document Wu board activity corrections 2026-10-08 18:54:03 +03:00
Lauri Ojansivu 5f41d4ca7a Correct Wu board activity wording
Replace 24 Mandarin-like or malformed activity values, preserve exact tokens and synchronize audit records. Wording is lower confidence and needs native review.

Thanks to xet7.
2026-10-08 18:53:31 +03:00
Lauri Ojansivu 0a7efed4b6 Document Wu activity language corrections 2026-10-08 18:52:03 +03:00
Lauri Ojansivu 5f5c1697d6 Correct Wu activity language and title argument order
Replace 26 Mandarin-like values with Wu wording, preserve source tokens and update exact-value audit records. Wording is lower confidence and needs native review.

Thanks to xet7.
2026-10-08 18:51:36 +03:00
Lauri Ojansivu d079ce4821 Document completion of current Wu fill list 2026-10-08 18:49:46 +03:00
Lauri Ojansivu 16ef038d8b Complete current Wu translation fill list
Translate 33 remaining recovery messages and verify placeholders, removal warnings and an empty full fill list. Wu wording is lower confidence and needs native review; older Mandarin-like passages still need correction.

Thanks to xet7.
2026-10-08 18:49:18 +03:00
github-actions[bot] 3858d4d5de Bump versions for v12.25 2026-10-08 15:48:37 +00:00
Lauri Ojansivu 9d7d1a1079 Document Wu Sync recovery translations in Upcoming 2026-10-08 18:47:49 +03:00
Lauri Ojansivu 7564f2a073 Translate Wu Sync planning and interrupted operations
Preserve placeholders and verify matching order, retained changes and replayable-operation warnings. Wu wording is lower confidence and needs native review.

Thanks to xet7.
2026-10-08 18:46:58 +03:00
Lauri Ojansivu aea5b5ad58 Prepare v12.25 release 2026-10-08 18:46:16 +03:00
Lauri Ojansivu 980ce3d794 Document Wu rule and import translations 2026-10-08 18:45:34 +03:00
Lauri Ojansivu ccf7625d38 Translate Wu rule editor and Scrum imports
Preserve placeholders and technical literals and verify permissions, import matching and unchanged foreign-board cards. Wu wording is lower confidence and needs native review.

Thanks to xet7.
2026-10-08 18:45:02 +03:00
Lauri Ojansivu 3fc11b03b6 Document completion of current Wu Blockly strings 2026-10-08 18:43:42 +03:00
Lauri Ojansivu 76a45e9ce5 Complete current Wu Blockly translation fill list
Translate 32 workspace and alias messages and verify placeholders, search directions and an empty Blockly fill list. Wu wording is lower confidence and needs native review.

Thanks to xet7.
2026-10-08 18:43:12 +03:00
Lauri Ojansivu 6c89fe0033 Document Wu text-value translations 2026-10-08 18:42:02 +03:00
Lauri Ojansivu ea7eaa769d Translate Wu Blockly text values and variables
Preserve variables and verify replacement order, whitespace handling and variable-type conflicts. Wu wording is lower confidence and needs native review.

Thanks to xet7.
2026-10-08 18:41:29 +03:00
Lauri Ojansivu 4b8b856944 Document Wu text-position translations 2026-10-08 18:40:17 +03:00
Lauri Ojansivu 889c5d84a9 Translate Wu Blockly text positions
Preserve variables and verify letter case, copied text and indexing from either end. Wu wording is lower confidence and needs native review.

Thanks to xet7.
2026-10-08 18:39:47 +03:00
Lauri Ojansivu f39aa3c247 Document Wu keyboard shortcut translations 2026-10-08 18:38:35 +03:00
Lauri Ojansivu 4226317591 Translate Wu Blockly keyboard shortcuts
Preserve key names and verify directions, navigation pairs and focus destinations. Wu wording is lower confidence and needs native review.

Thanks to xet7.
2026-10-08 18:38:02 +03:00
Lauri Ojansivu 5116a4d15f Document Wu procedure and accessibility translations 2026-10-08 18:36:49 +03:00
Lauri Ojansivu f6d83cebb3 Translate Wu Blockly procedures and accessibility modes
Preserve variables and verify function outputs, scope, disabled calls and screen-reader mode transitions. Wu wording is lower confidence and needs native review.

Thanks to xet7.
2026-10-08 18:36:14 +03:00
Lauri Ojansivu 4f74fc01a7 Document Wu trigonometry and workspace translations 2026-10-08 18:34:55 +03:00
Lauri Ojansivu bb5ad6de61 Translate Wu Blockly trigonometry and workspace
Preserve variables and key names and verify angle units, inverse functions and variable types. Wu wording is lower confidence and needs native review.

Thanks to xet7.
2026-10-08 18:34:25 +03:00
Lauri Ojansivu c3988cc861 Document Wu rounding translations 2026-10-08 18:33:14 +03:00
Lauri Ojansivu e25c024618 Translate Wu Blockly rounding and unary math
Preserve variables and mathematical literals and verify random bounds, rounding directions and logarithm bases. Wu wording is lower confidence and needs native review.

Thanks to xet7.
2026-10-08 18:32:35 +03:00
Lauri Ojansivu ea39af851f Document Wu statistics translations 2026-10-08 18:31:24 +03:00
Lauri Ojansivu 211c99607d Translate Wu Blockly number properties and statistics
Preserve variables and verify inclusive bounds, number properties and statistical terms. Wu wording is lower confidence and needs native review.

Thanks to xet7.
2026-10-08 18:30:51 +03:00
Lauri Ojansivu 8f3c8fd0b4 Document Wu arithmetic translations 2026-10-08 18:29:39 +03:00
Lauri Ojansivu 9e7b39e89a Translate Wu Blockly logic and arithmetic
Preserve variables and mathematical literals and verify negation, comparison bounds and arithmetic results. Wu wording is lower confidence and needs native review.

Thanks to xet7.
2026-10-08 18:29:09 +03:00
Lauri Ojansivu 87dc926fd7 Document Wu sorting and logic translations 2026-10-08 18:27:55 +03:00
Lauri Ojansivu 5fe338f83d Translate Wu Blockly sorting and logic
Preserve variables and verify sorting directions, case sensitivity and strict versus inclusive comparisons. Wu wording is lower confidence and needs native review.

Thanks to xet7.
2026-10-08 18:27:18 +03:00
Lauri Ojansivu 7b04e1936d Document Wu list-position translations 2026-10-08 18:26:07 +03:00
Lauri Ojansivu 2ecc9ea6fe Translate Wu Blockly list positions and removal
Preserve variables and verify removal return behavior, indexing, not-found results and copying semantics. Wu wording is lower confidence and needs native review.

Thanks to xet7.
2026-10-08 18:25:35 +03:00
Lauri Ojansivu aefcbd36ac Document Wu navigation and list translations 2026-10-08 18:24:23 +03:00
Lauri Ojansivu 5cae76ce91 Translate Wu Blockly navigation and list retrieval
Preserve variables and verify copy versus cut, first versus last and retrieval versus removal. Wu wording is lower confidence and needs native review.

Thanks to xet7.
2026-10-08 18:23:50 +03:00
Lauri Ojansivu cd9647bec4 Document Wu input-label translations 2026-10-08 18:22:24 +03:00
Lauri Ojansivu 1d7bf4d56b Translate Wu Blockly input roles
Preserve variables and verify input positions, arithmetic operands and count versus value. Wu wording is lower confidence and needs native review.

Thanks to xet7.
2026-10-08 18:21:45 +03:00
Lauri Ojansivu 15acdc30bd Document Wu field and accessibility translations 2026-10-08 18:20:31 +03:00
Lauri Ojansivu 31b86d4178 Translate Wu Blockly fields and accessibility
Preserve variables and key names and verify bitmap positions, opposite icon actions and condition ordering. Wu wording is lower confidence and needs native review.

Thanks to xet7.
2026-10-08 18:20:00 +03:00
Lauri Ojansivu 5e12ae3190 Document Wu loop and editing translations 2026-10-08 18:18:48 +03:00
Lauri Ojansivu e13249115e Translate Wu Blockly loops and editing
Preserve variables and verify true versus false conditions, fallback branches, deletion counts and opposite editing actions. Wu wording is lower confidence and needs native review.

Thanks to xet7.
2026-10-08 18:18:16 +03:00
Lauri Ojansivu 3642a38eae Document Wu import and Blockly translations 2026-10-08 18:16:53 +03:00
Lauri Ojansivu 7410bfc2db Translate Wu imports and Blockly controls
Preserve variables, import literals, key names and numeric limits. Verify loop restrictions and break versus continue. Wu wording is lower confidence and needs native review.

Thanks to xet7.
2026-10-08 18:16:14 +03:00
Lauri Ojansivu 1544c8af28 Document Wu settings translations 2026-10-08 18:14:05 +03:00
Lauri Ojansivu 1dfef78d90 Translate Wu board and LDAP settings
Preserve placeholders, link templates and configuration names. Check Wu phrasing and opposite actions. Wording is lower confidence and needs native review; Mandarin-like older passages remain to audit.

Thanks to xet7.
2026-10-08 18:13:31 +03:00
Lauri Ojansivu f58a0c3da3 Record broad translation verification after Papiamento fill 2026-10-08 18:12:37 +03:00
Lauri Ojansivu a6847dd92a Document completion of current Papiamento fill list 2026-10-08 18:09:49 +03:00
Lauri Ojansivu dc7ff85bfa Complete current Papiamento translation fill list
Translate 49 remaining recovery, import and history messages. Preserve variables and check destructive-action warnings and an empty full fill list. Specialized wording is lower confidence and needs native review.

Thanks to xet7.
2026-10-08 18:09:16 +03:00
Lauri Ojansivu c60c66175b Document Papiamento notification recovery translations 2026-10-08 18:07:16 +03:00
Lauri Ojansivu 5a53247e68 Translate Papiamento notification recovery
Preserve variables and verify recovery states, irreversible cancellation and retained changes. Specialized wording is lower confidence and needs native review.

Thanks to xet7.
2026-10-08 18:06:39 +03:00
Lauri Ojansivu b63dead3e1 Document Papiamento Sync and mail translations 2026-10-08 18:05:17 +03:00
Lauri Ojansivu 7b62fbbdd6 Translate Papiamento Sync outcomes and mail failures
Preserve variables and technical literals and verify missing versus null values, planning retention and retry behavior. Specialized wording is lower confidence and needs native review.

Thanks to xet7.
2026-10-08 18:04:45 +03:00
Lauri Ojansivu fede80c573 Document Papiamento Sync preview translations 2026-10-08 18:03:04 +03:00
Lauri Ojansivu bd576b6355 Translate Papiamento Sync previews and source reports
Preserve placeholders and verify replacement reuse, saved settings, omitted values and report limits. Specialized wording is lower confidence and needs native review.

Thanks to xet7.
2026-10-08 18:02:32 +03:00
Lauri Ojansivu dae71afc15 Document Papiamento import-preview and conflict translations 2026-10-08 18:01:01 +03:00
Lauri Ojansivu 487e0b16e1 Translate Papiamento import previews and Sync conflicts
Preserve placeholders and verify ambiguous matches, local versus source choices, retained content and unchanged subcards. Specialized wording is lower confidence and needs native review.

Thanks to xet7.
2026-10-08 18:00:24 +03:00
Lauri Ojansivu 60532b326a Document Papiamento Scrum report translations 2026-10-08 17:58:49 +03:00
Lauri Ojansivu eff57a7ea0 Translate Papiamento Scrum reports and observations
Preserve source variables, UTC and observation limits. Verify report scope, unknown estimates, sprint states and incomplete-import restrictions. Specialized wording is lower confidence and needs native review.

Thanks to xet7.
2026-10-08 17:58:10 +03:00
Lauri Ojansivu af71d2065a Document Papiamento import and planning translations 2026-10-08 17:56:25 +03:00
Lauri Ojansivu 5f64302d59 Translate Papiamento imports and Scrum planning
Preserve import keywords, application names and keyboard literals. Verify import mappings, completion policies and sprint actions. Specialized wording is lower confidence and needs native review.

Thanks to xet7.
2026-10-08 17:55:54 +03:00
Lauri Ojansivu 82532dfc0f Document Papiamento settings and rule translations 2026-10-08 17:54:14 +03:00
Lauri Ojansivu 9fffd1cb8b Translate Papiamento board settings and rules
Preserve source placeholders, link templates, URLs and configuration names. Check permissions, movement directions and saved and failure states. Specialized wording is lower confidence and needs native review.

Thanks to xet7.
2026-10-08 17:53:39 +03:00
Lauri Ojansivu 76c1f64872 Document completion of current Papiamento Blockly strings 2026-10-08 17:51:53 +03:00
Lauri Ojansivu ed5d5895b8 Complete current Papiamento Blockly translations
Translate 44 variable, workspace, search and alias messages. Verify exact placeholders, keyboard names, directional search and an empty Blockly fill list. Specialized wording is lower confidence and needs native review.

Thanks to xet7.
2026-10-08 17:51:13 +03:00
Lauri Ojansivu c3474f7030 Document Papiamento text-operation translations 2026-10-08 17:49:31 +03:00
Lauri Ojansivu 57b32d6d15 Translate Papiamento Blockly text operations
Preserve variables and verify text position, letter case, trimming direction and replacement semantics. Specialized wording is lower confidence and needs native review.

Thanks to xet7.
2026-10-08 17:48:57 +03:00
Lauri Ojansivu 11d4fe1e65 Document Papiamento keyboard shortcut translations 2026-10-08 17:47:25 +03:00
Lauri Ojansivu ef911475c0 Translate Papiamento Blockly keyboard shortcuts
Preserve variables and key names and verify opposite directions and navigation actions. Specialized terminology is lower confidence and needs native review.

Thanks to xet7.
2026-10-08 17:46:52 +03:00
Lauri Ojansivu 906872be3f Document Papiamento procedure translations 2026-10-08 17:43:04 +03:00
Lauri Ojansivu 0ae625d8a4 Translate Papiamento Blockly procedures and accessibility modes
Preserve variables and verify return values, function scope and opposite accessibility states. Specialized wording is lower confidence and needs native review. Thanks to xet7.
2026-10-08 17:42:29 +03:00
Lauri Ojansivu ddfab554bf Document Papiamento trigonometry translations 2026-10-08 17:41:17 +03:00
Lauri Ojansivu 5180e1cfb8 Translate Papiamento Blockly trigonometry and workspace
Preserve variables and key names with checks for angle units, bases and directions. Specialized wording is lower confidence and needs native review. Thanks to xet7.
2026-10-08 17:40:43 +03:00
Lauri Ojansivu 2649405307 Document Papiamento statistics translations 2026-10-08 17:38:48 +03:00
Lauri Ojansivu 665c3a17bc Translate Papiamento Blockly statistics and rounding
Preserve variables and verify numeric endpoints, rounding directions and statistical distinctions. Specialized wording is lower confidence and needs native review. Thanks to xet7.
2026-10-08 17:38:11 +03:00
Lauri Ojansivu d226a7643c Document Papiamento arithmetic translations 2026-10-08 17:36:40 +03:00
Lauri Ojansivu 09ca8d5909 Translate Papiamento Blockly arithmetic messages
Preserve variables and mathematical notation with boundary regression coverage. Specialized wording is lower confidence and needs native review. Thanks to xet7.
2026-10-08 17:36:09 +03:00
Lauri Ojansivu 8aae4dbc08 Document Papiamento logic translations 2026-10-08 17:34:56 +03:00
Lauri Ojansivu 9b83235800 Translate Papiamento Blockly logic messages
Preserve variables and null with comparison and condition regression coverage. Specialized wording is lower confidence and needs native review. Thanks to xet7.
2026-10-08 17:34:19 +03:00
Lauri Ojansivu 78eb73ad27 Document Papiamento list mutation translations 2026-10-08 17:33:11 +03:00
Lauri Ojansivu 684a5cdb00 Translate Papiamento Blockly list mutation and sorting
Preserve variables and distinguish insertion, replacement and copying operations. Specialized wording is lower confidence and needs native review. Thanks to xet7.
2026-10-08 17:32:33 +03:00
Lauri Ojansivu 3d60eba3d1 Document Papiamento retrieval translations 2026-10-08 17:31:04 +03:00
Lauri Ojansivu 30e950f79c Translate Papiamento Blockly navigation and retrieval
Preserve variables and distinguish retrieval, removal and combined operations. Specialized wording is lower confidence and needs native review. Thanks to xet7.
2026-10-08 17:30:32 +03:00
Lauri Ojansivu 1e711174f6 Document Papiamento input role translations 2026-10-08 17:29:14 +03:00
Lauri Ojansivu cae863f672 Translate Papiamento Blockly input roles
Preserve variables and coordinate letters with regression coverage for positions and division operands. Specialized wording is lower confidence and needs native review. Thanks to xet7.
2026-10-08 17:28:37 +03:00
Lauri Ojansivu 83149145cb Document Papiamento field translations 2026-10-08 17:27:08 +03:00
Lauri Ojansivu a0b3406e13 Translate Papiamento Blockly fields and icons
Preserve numbered variables and verify opposite actions, pixel states and input roles. Specialized wording is lower confidence and needs native review. Thanks to xet7.
2026-10-08 17:26:38 +03:00
Lauri Ojansivu a84d23b7e1 Document Papiamento loop translations 2026-10-08 17:25:23 +03:00
Lauri Ojansivu 04f20bc654 Translate Papiamento Blockly loops and deletion
Preserve numbered variables and verify loop conditions and deletion counts. Specialized wording is lower confidence and needs native review. Thanks to xet7.
2026-10-08 17:24:54 +03:00
Lauri Ojansivu 73cfc466ea Document Papiamento Blockly control translations 2026-10-08 17:23:27 +03:00
Lauri Ojansivu 46816ff9b5 Translate Papiamento Blockly editing and color messages
Use shared placeholder validation for numbered variables and clarify the historical test baseline. Specialized wording is lower confidence and needs native review. Thanks to xet7.
2026-10-08 17:22:55 +03:00
Lauri Ojansivu 57462061e3 Refresh translation backlog and broad verification status
Record 298 passing translation suites and the current 33800-value backlog across 48 languages, with 250 pending source keys tracked separately. Thanks to xet7.
2026-10-08 17:21:12 +03:00
Lauri Ojansivu 0f6c71c526 Document Yiddish fill-list completion 2026-10-08 17:18:33 +03:00
Lauri Ojansivu 3eb64a2c19 Translate remaining Yiddish import and history recovery messages
Preserve counters and verify recovery outcomes and an empty current fill list. Specialized wording is lower confidence and needs native review. Thanks to xet7.
2026-10-08 17:18:01 +03:00
Lauri Ojansivu ea434ef6ef Document Yiddish List Sync recovery translations 2026-10-08 17:16:03 +03:00
Lauri Ojansivu c18527c022 Translate Yiddish List Sync recovery messages
Preserve counters and verify retained changes, replayable operations and the oldest-50 limit. Specialized wording is lower confidence and needs native review. Thanks to xet7.
2026-10-08 17:15:22 +03:00
Lauri Ojansivu 3821250725 Document Yiddish activity recovery translations 2026-10-08 17:13:41 +03:00
Lauri Ojansivu 80fdbfff7a Translate Yiddish activity notification recovery
Preserve variables and verify retained work, delivery permissions and permanent cancellation. Specialized wording is lower confidence and needs native review. Thanks to xet7.
2026-10-08 17:13:04 +03:00
Lauri Ojansivu 4ac1e1b856 Document Yiddish Sync planning translations 2026-10-08 17:11:41 +03:00
Lauri Ojansivu 0d02ac276a Translate Yiddish Sync planning and notification diagnostics
Preserve variables and literals with regression checks for null handling and retained planning. Specialized wording is lower confidence and needs native review. Thanks to xet7.
2026-10-08 17:11:04 +03:00
Lauri Ojansivu e616efdbf6 Document Yiddish Sync report translations 2026-10-08 17:09:11 +03:00
Lauri Ojansivu 26a63bf7c5 Translate Yiddish Sync source and run reports
Preserve variables and verify retention limits, hidden values and partial-run semantics. Specialized wording is lower confidence and needs native review. Thanks to xet7.
2026-10-08 17:08:33 +03:00
Lauri Ojansivu 9d4f299ef3 Keep unshipped translation entries in Upcoming
Move translation entries absent from v12.24 back to Upcoming after the concurrent merge, and document Yiddish Sync conflict translations. Release ancestry and translation summary checks pass. Thanks to xet7.
2026-10-08 17:07:08 +03:00
Lauri Ojansivu a9527ce88c Translate Yiddish Sync conflicts and preview
Preserve variables and check retained data, limited review and replacement reuse. Specialized wording is lower confidence and needs native review. Thanks to xet7.
2026-10-08 17:05:32 +03:00
Lauri Ojansivu 3954197a93 Merge remote-tracking branch 'origin/main' 2026-10-08 17:05:25 +03:00
Lauri Ojansivu 48698b6c48 Document Yiddish Scrum import translations 2026-10-08 17:04:03 +03:00
Lauri Ojansivu fe18e6aaeb Translate Yiddish Scrum imports and observations
Preserve counters and references with import and observation regression coverage. Specialized wording is lower confidence and needs native review. Thanks to xet7.
2026-10-08 17:03:27 +03:00
Lauri Ojansivu a1d7f8961d Document Yiddish sprint planning translations 2026-10-08 17:01:32 +03:00
Lauri Ojansivu b51991d93b Translate Yiddish sprint planning and reports
Preserve counters and test estimate distinctions, sprint states and close/cancel outcomes. Specialized Scrum wording is lower confidence and needs native review. Thanks to xet7.
2026-10-08 17:00:59 +03:00
Lauri Ojansivu d550b1c1f1 Document Yiddish rule and Scrum translations 2026-10-08 16:59:22 +03:00
Lauri Ojansivu c6da6a7fec Translate Yiddish rule validation and Scrum settings
Preserve variables and check rule limits, conflict handling and completion policies. Specialized Scrum wording is lower confidence and needs native review. Thanks to xet7.
2026-10-08 16:58:53 +03:00
Lauri Ojansivu c911f4879b Document Yiddish import and settings translations 2026-10-08 16:57:14 +03:00
Lauri Ojansivu a1265408c6 Translate Yiddish imports and settings messages
Preserve variables, configuration names and import syntax. Specialized wording is lower confidence and needs native review. Thanks to xet7.
2026-10-08 16:56:39 +03:00
Lauri Ojansivu aa7e0bebec Document Yiddish Blockly fill-list completion 2026-10-08 16:54:41 +03:00
Lauri Ojansivu 80e6c6982e Finish Yiddish Blockly search and legacy label translations
Preserve search variables and shortcuts and verify no Blockly fill-list entries remain. Specialized wording is lower confidence and needs native review. Thanks to xet7.
2026-10-08 16:54:02 +03:00
Lauri Ojansivu 4b66a77a7b Document Yiddish workspace count translations 2026-10-08 16:52:48 +03:00
Lauri Ojansivu 1ad1f9bd95 Translate Yiddish Blockly text values and workspace counts
Preserve placeholders and joined announcement spacing with boundary regression coverage. Specialized wording is lower confidence and needs native review. Thanks to xet7.
2026-10-08 16:52:09 +03:00
Lauri Ojansivu fcda32fa93 Document Yiddish text position translations 2026-10-08 16:50:32 +03:00
Lauri Ojansivu 62e6109cab Translate Yiddish Blockly text position messages
Preserve variables and position markers with coverage for navigation and text boundaries. Specialized wording is lower confidence and needs native review. Thanks to xet7.
2026-10-08 16:49:58 +03:00
Lauri Ojansivu a39e5b5b9e Document Yiddish accessibility translations 2026-10-08 16:48:29 +03:00
Lauri Ojansivu 8408baacc1 Translate Yiddish Blockly shortcuts and accessibility
Preserve variables and verify opposite actions and accessibility state transitions. Specialized wording is lower confidence and needs native review. Thanks to xet7.
2026-10-08 16:47:57 +03:00
Lauri Ojansivu 8786ac39bb Document Yiddish function translations 2026-10-08 16:46:20 +03:00
Lauri Ojansivu 919c0f46ea Translate Yiddish Blockly functions and trigonometry
Preserve variables and key names with regression coverage for units and return values. Specialized wording is lower confidence and needs native review. Thanks to xet7.
2026-10-08 16:45:49 +03:00
Lauri Ojansivu 5de50b8ac4 Document Yiddish statistics translations 2026-10-08 16:42:59 +03:00
Lauri Ojansivu 89421b54b9 Translate Yiddish Blockly statistics and rounding
Preserve placeholders, numeric bounds and mathematical notation. Specialized mathematical wording is lower confidence and needs native review. Thanks to xet7.
2026-10-08 16:42:26 +03:00
Lauri Ojansivu 5c44145104 Document Yiddish arithmetic translations 2026-10-08 16:40:55 +03:00
Lauri Ojansivu bc67a1295e Translate Yiddish Blockly comparisons and arithmetic
Preserve placeholders and mathematical notation with boundary regression coverage. Specialized mathematical wording is lower confidence and needs native review. Thanks to xet7.
2026-10-08 16:40:26 +03:00
Lauri Ojansivu 4e29e9e243 Document Yiddish list mutation translations 2026-10-08 16:38:41 +03:00
Lauri Ojansivu 8e6ec95241 Translate Yiddish Blockly list mutation and logic
Preserve variables, index markers and operation distinctions. Specialized programming wording is lower confidence and needs native review. Thanks to xet7.
2026-10-08 16:38:12 +03:00
Lauri Ojansivu 0ba51442f3 Document Yiddish Blockly navigation translations 2026-10-08 16:36:28 +03:00
Lauri Ojansivu e4dfedb616 Translate Yiddish Blockly navigation and list messages
Preserve placeholders and distinguish retrieval, removal and combined operations. Specialized programming wording is lower confidence and needs native review. Thanks to xet7.
2026-10-08 16:35:53 +03:00
Lauri Ojansivu d8f5a29da5 Document Yiddish Blockly field translations 2026-10-08 16:34:06 +03:00
Lauri Ojansivu c1e46548b2 Translate Yiddish Blockly field and input labels
Preserve variables, coordinate letters, key names and opposing actions. Specialized programming wording is lower confidence and needs native review. Thanks to xet7.
2026-10-08 16:33:38 +03:00
Lauri Ojansivu 8442a6b75e Document Yiddish Blockly translations 2026-10-08 16:31:54 +03:00
Lauri Ojansivu c3e393860a Translate Yiddish Blockly control messages
Preserve numbered variables, numeric bounds and opposing loop conditions. Specialized programming wording is lower confidence and needs native review. Thanks to xet7.
2026-10-08 16:31:22 +03:00
Lauri Ojansivu 298c82e762 Document Afrikaans planning translations 2026-10-08 16:28:28 +03:00
Lauri Ojansivu 798aab05fd Translate Afrikaans planning and settings messages
Preserve source variables and technical examples in both catalogs, with regression coverage for planning retention and recovery. Thanks to xet7.
2026-10-08 16:27:57 +03:00
Lauri Ojansivu f0fc7bbfee Document Afrikaans import recovery translation checks 2026-10-08 16:23:43 +03:00
Lauri Ojansivu c2a42d0a4b Translate Afrikaans interrupted import recovery messages 2026-10-08 16:23:14 +03:00
Lauri Ojansivu 2b3d203bd4 Document Afrikaans recovery translation checks 2026-10-08 16:22:06 +03:00
Lauri Ojansivu f24679213d Translate Afrikaans List Sync recovery messages 2026-10-08 16:21:32 +03:00
Lauri Ojansivu 3d08020a79 Document Icelandic planning translation checks 2026-10-08 16:19:59 +03:00
Lauri Ojansivu dab22934dd Translate Icelandic planning and settings messages 2026-10-08 16:19:24 +03:00
Lauri Ojansivu 42dd8dc426 Document Icelandic import recovery translation checks 2026-10-08 16:17:18 +03:00
Lauri Ojansivu 9eb05f7b76 Translate Icelandic interrupted import recovery messages 2026-10-08 16:16:48 +03:00
Lauri Ojansivu a511ecb97c Document Icelandic recovery translation checks 2026-10-08 16:15:21 +03:00
Lauri Ojansivu 0811c1a54b Translate Icelandic List Sync recovery messages 2026-10-08 16:14:40 +03:00
Lauri Ojansivu 4f3b10ae0f Document Albanian planning translation checks 2026-10-08 16:13:09 +03:00
Lauri Ojansivu 64babf0506 Translate Albanian planning and settings messages 2026-10-08 16:12:38 +03:00
Lauri Ojansivu 1524996a03 Document Albanian import recovery translation checks 2026-10-08 16:10:33 +03:00
Lauri Ojansivu 465366cb58 Translate Albanian interrupted import recovery messages 2026-10-08 16:09:58 +03:00
Lauri Ojansivu 218a38082b Document Albanian recovery translation checks 2026-10-08 16:08:44 +03:00
Lauri Ojansivu efdce09e90 Translate Albanian List Sync recovery messages 2026-10-08 16:08:10 +03:00
Lauri Ojansivu 3fbad7dd46 Document Belarusian planning translation checks 2026-10-08 16:06:43 +03:00
Lauri Ojansivu c4cd73669d Translate Belarusian planning and settings messages 2026-10-08 16:06:05 +03:00
Lauri Ojansivu 287aa7e31f Document Belarusian import recovery translation checks 2026-10-08 16:03:55 +03:00
Lauri Ojansivu f5bb20772d Translate Belarusian interrupted import recovery messages 2026-10-08 16:03:15 +03:00
Lauri Ojansivu dbbee020a0 Document Belarusian recovery translation checks 2026-10-08 16:01:50 +03:00
Lauri Ojansivu da560a776e Translate Belarusian List Sync recovery messages 2026-10-08 16:01:16 +03:00
Lauri Ojansivu 573f6639d3 Document Upper Sorbian minimum verification 2026-10-08 15:59:40 +03:00
Lauri Ojansivu bda123a348 Recognize native Upper Sorbian minimum labels 2026-10-08 15:59:10 +03:00
Lauri Ojansivu b892e431ff Document shared mathematical label verification 2026-10-08 15:57:47 +03:00
Lauri Ojansivu e9a0ddb5d7 Recognize native Silesian and Sorbian math labels 2026-10-08 15:57:17 +03:00
Lauri Ojansivu fdf126d46f Document Serbian planning translation checks 2026-10-08 15:55:20 +03:00
Lauri Ojansivu 04ebc07f34 Translate Serbian planning and settings messages 2026-10-08 15:54:48 +03:00
Lauri Ojansivu b43cacafc5 Document Serbian import recovery translation checks 2026-10-08 15:52:58 +03:00
Lauri Ojansivu dbc4bdb2ce Translate Serbian interrupted import recovery messages 2026-10-08 15:52:26 +03:00
Lauri Ojansivu a583ef1b4a Document Serbian recovery and full translation checks 2026-10-08 15:51:20 +03:00
Lauri Ojansivu 29285219e6 Translate Serbian List Sync recovery messages 2026-10-08 15:49:35 +03:00
Lauri Ojansivu 6b630ed196 Document Macedonian planning translation checks 2026-10-08 15:48:10 +03:00
Lauri Ojansivu 26554c3975 Translate Macedonian planning and settings messages 2026-10-08 15:47:37 +03:00
Lauri Ojansivu 3043e65df7 Document Macedonian recovery translation checks 2026-10-08 15:45:36 +03:00
Lauri Ojansivu 32a1670b03 Translate Macedonian List Sync recovery messages 2026-10-08 15:45:05 +03:00
Lauri Ojansivu 105f57d846 Document Macedonian import recovery translation checks 2026-10-08 15:43:44 +03:00
Lauri Ojansivu 5ff83bf51d Translate Macedonian interrupted import recovery messages 2026-10-08 15:43:00 +03:00
Lauri Ojansivu 81ec7a1c16 Document Bosnian planning translation checks 2026-10-08 15:41:37 +03:00
Lauri Ojansivu a86e699ad8 Translate Bosnian planning and settings messages 2026-10-08 15:41:03 +03:00
Lauri Ojansivu 7110e93346 Document Bosnian import recovery translation checks 2026-10-08 15:39:23 +03:00
Lauri Ojansivu 220394061d Translate Bosnian interrupted import recovery messages 2026-10-08 15:38:50 +03:00
Lauri Ojansivu 2a5651afd7 Document Bosnian recovery translation checks 2026-10-08 15:37:34 +03:00
Lauri Ojansivu dcf55cecbe Translate Bosnian List Sync recovery messages 2026-10-08 15:37:03 +03:00
Lauri Ojansivu a705ff3339 Document Croatian planning translation checks 2026-10-08 15:35:38 +03:00
Lauri Ojansivu 41115d4633 Translate Croatian planning and settings messages 2026-10-08 15:35:07 +03:00
Lauri Ojansivu 156b524951 Document Croatian recovery translation checks 2026-10-08 15:33:13 +03:00
Lauri Ojansivu f4e758ede0 Translate Croatian List Sync recovery messages 2026-10-08 15:32:37 +03:00
Lauri Ojansivu c729033513 Document Croatian monitoring translation checks 2026-10-08 15:31:23 +03:00
Lauri Ojansivu fb9b6e442e Correct Croatian monitoring and flow explanations 2026-10-08 15:30:48 +03:00
Lauri Ojansivu b681db44c3 Document Croatian migration translation checks 2026-10-08 15:29:21 +03:00
Lauri Ojansivu 47fb6b7d5a Correct Croatian migration and storage translations 2026-10-08 15:28:50 +03:00
Lauri Ojansivu 853ebc6ea0 Document Croatian administration translation checks 2026-10-08 15:27:09 +03:00
Lauri Ojansivu 3292273b5d Correct Croatian administration and support translations 2026-10-08 15:26:33 +03:00
Lauri Ojansivu 0e69f4ee80 Document Croatian predicate translation checks 2026-10-08 15:25:21 +03:00
Lauri Ojansivu 83510c32db Correct Croatian search predicates and report translations 2026-10-08 15:24:49 +03:00
Lauri Ojansivu 4031440fc5 Document Croatian search translation checks 2026-10-08 15:23:34 +03:00
Lauri Ojansivu 97d0483ca8 Correct Croatian search and notification translations 2026-10-08 15:22:56 +03:00
Lauri Ojansivu b6fb98f054 Document Croatian rule translation checks 2026-10-08 15:21:03 +03:00
Lauri Ojansivu 29b6cdace3 Record the redirect login verification run in the login testing docs.
Thanks to xet7 !
2026-10-08 15:20:52 +03:00
Lauri Ojansivu 70833e7c54 Record the redirect login default in a new Upcoming section, and move the Caddy entry out of v12.24.
The Caddy proxy headers entry was added after v12.24 had been prepared,
under that release's heading, although v12.24 does not contain the
commit it links. It moves to a new Upcoming section with the redirect
login default.

Thanks to xet7 !
2026-10-08 15:20:36 +03:00
Lauri Ojansivu 7e629bbf57 Correct Croatian rule actions and triggers 2026-10-08 15:20:35 +03:00
Lauri Ojansivu f816249a28 Document Croatian card settings translation checks 2026-10-08 15:19:14 +03:00
Lauri Ojansivu ad51a03731 Log in by full-page redirect by default with OAuth2/OIDC, Google & co, SAML and CAS.
Every login that chooses between a popup and a redirect opened a popup by
default. Google's sign-in pages, and other identity providers', can send
Cross-Origin-Opener-Policy, which makes Meteor's popup look closed at
once: the login was tried before the provider answered, and the user
was back on the sign-in page with no error. Popups are also blocked in
iframes and on some phones. The full-page redirect has no popup to lose.

- OAUTH2_LOGIN_STYLE (OAuth2/OIDC): redirect unless it says popup, in the
  server, the Admin Panel catalog and the samples.
- OAUTH_PROVIDERS_LOGIN_STYLE (Google, GitHub, Facebook, X, Meteor
  Developer, Weibo, Meetup): redirect by default, in the code, Dockerfile,
  snap, Sandstorm package and samples.
- SAML: both identity-provider profiles log in by redirect;
  SAML_LOGIN_FLOW=popup still chooses the popup, and the client no longer
  falls back to a popup while the configuration loads.
- CAS: redirects unless "popup": true. Its redirect could never finish:
  nothing called Meteor.initCas, which the package now does at startup.
  The sign-in page also passed username and password as loginWithCas's
  options and callback, so its callback never ran, and the CAS redirect
  page echoed the request address into HTML unescaped.

A redirect login's error now reaches the reloaded sign-in page, from
accounts-oauth's page-load hook and the SAML and CAS events, instead of
the page coming back silently.

Verified against a freshly built and prepared bundle with the local
identity-provider suite and no login style set: OIDC, SAML, CAS and all
seven Meteor providers log in by redirect, and each refusal is shown on
the sign-in page (31 of 31 with Chromium). Its LDAP wrong-password case
expected wording that changed in 679a8b3499 and is corrected.

Thanks to xet7 !
2026-10-08 15:18:59 +03:00
Lauri Ojansivu 9dee3f53da Correct Croatian card settings and rule translations 2026-10-08 15:18:38 +03:00
Lauri Ojansivu 91a14544a0 Document Croatian sidebar corrections
Thanks to xet7.
2026-10-08 15:14:49 +03:00
Lauri Ojansivu a463241478 Correct Croatian sidebar and email settings translations
Thanks to xet7.
2026-10-08 15:14:08 +03:00
Lauri Ojansivu fc99fb5df3 Document Croatian filter corrections
Thanks to xet7.
2026-10-08 15:12:30 +03:00
Lauri Ojansivu 332f6524ea Correct Croatian filters and archive translations
Thanks to xet7.
2026-10-08 15:12:01 +03:00
Lauri Ojansivu 26b2db9fc5 Document Croatian permission corrections
Thanks to xet7.
2026-10-08 15:10:28 +03:00
Lauri Ojansivu 7ee6ee455e Correct Croatian permissions and email translations
Thanks to xet7.
2026-10-08 15:09:58 +03:00
Lauri Ojansivu 6b1b3bbc3a Document Croatian control corrections
Thanks to xet7.
2026-10-08 15:08:25 +03:00
Lauri Ojansivu 46c4274729 Correct Croatian workspace and board controls
Thanks to xet7.
2026-10-08 15:07:52 +03:00
Lauri Ojansivu 0530b269e2 Document Croatian import recovery translations
Thanks to xet7.
2026-10-08 15:06:22 +03:00
Lauri Ojansivu b42a35dc2c Translate Croatian import recovery and correct core labels
Thanks to xet7.
2026-10-08 15:05:49 +03:00
Lauri Ojansivu 4064301ac0 Document Slovenian planning translations
Thanks to xet7.
2026-10-08 15:04:12 +03:00
Lauri Ojansivu 2228f212d0 Translate Slovenian controls and planning
Thanks to xet7.
2026-10-08 15:03:34 +03:00
Lauri Ojansivu fdac6d9d19 Document Slovenian synchronization translations
Thanks to xet7.
2026-10-08 15:01:41 +03:00
Lauri Ojansivu 1603813d9d Translate Slovenian stalled synchronization recovery
Thanks to xet7.
2026-10-08 15:01:07 +03:00
Lauri Ojansivu cd65c35eef Document Slovenian monitoring corrections
Thanks to xet7.
2026-10-08 14:59:40 +03:00
Lauri Ojansivu 552c154307 Correct Slovenian monitoring and flow translations
Thanks to xet7.
2026-10-08 14:59:12 +03:00
Lauri Ojansivu 2eb694378b Record the Caddy proxy headers documentation in the changelog.
Thanks to xet7 !
2026-10-08 14:58:29 +03:00
Lauri Ojansivu 88d3285f29 Send correct proxy headers from Caddy to every WeKan container and Sandstorm.
The production Caddyfile in the Meteor 3 Docker docs left the headers to
Caddy's defaults, so behind CloudFlare WeKan saw a CloudFlare edge
address as every visitor, and with no HTTP_FORWARDED_COUNT the proxy's
own address: all users shared the login cookie refresh limit of 30 per
10 seconds, which signs users out at busy times and sends a Google login
back to the sign-in page. Examples that read CF-Connecting-IP trusted it
from anybody who connected.

The global options now trust CloudFlare's published ranges, and only
those, through client_ip_headers CF-Connecting-IP, so grey cloud sites
and direct connections use their own address. Shared snippets give every
WeKan site the same headers - X-Forwarded-Proto https and the visitor's
address as the only X-Forwarded-For and X-Real-IP - and the container
template sets HTTP_FORWARDED_COUNT=1. Sandstorm gets its own snippet that
sets X-Real-IP, which Sandstorm believes from a loopback address, and the
Sandstorm docs explain how its gateway routes by Host, takes https from
BASE_URL and finds published sites by their sandstorm-www TXT record.
Both Caddyfiles were validated with caddy adapt, and the headers checked
against a live Caddy.

Thanks to xet7 !
2026-10-08 14:58:05 +03:00
Lauri Ojansivu 5a87c13009 Document Slovenian administration corrections
Thanks to xet7.
2026-10-08 14:57:20 +03:00
Lauri Ojansivu 04e6c6f0b2 Correct Slovenian administration and migration translations
Thanks to xet7.
2026-10-08 14:56:49 +03:00
Lauri Ojansivu 62bd8fe7ef Document Slovenian card detail corrections
Thanks to xet7.
2026-10-08 14:55:17 +03:00
Lauri Ojansivu 1019dd7970 Correct Slovenian card details and upload translations
Thanks to xet7.
2026-10-08 14:54:44 +03:00
Lauri Ojansivu 7d352d849f Document Slovenian search corrections
Thanks to xet7.
2026-10-08 14:53:14 +03:00
Lauri Ojansivu 1750705857 Correct Slovenian search and card view translations
Thanks to xet7.
2026-10-08 14:52:47 +03:00
Lauri Ojansivu ff2a721b43 Document Slovenian settings corrections
Thanks to xet7.
2026-10-08 14:50:38 +03:00
Lauri Ojansivu eebff1cdfc Correct Slovenian settings and notification translations
Thanks to xet7.
2026-10-08 14:50:10 +03:00
Lauri Ojansivu 3db579f927 Document Slovenian control corrections
Thanks to xet7.
2026-10-08 14:48:44 +03:00
Lauri Ojansivu 2c55b838ac Correct Slovenian workspace and board controls
Thanks to xet7.
2026-10-08 14:48:13 +03:00
Lauri Ojansivu 292f4b75cb Document Slovenian import recovery translations
Thanks to xet7.
2026-10-08 14:46:37 +03:00
Lauri Ojansivu 841a5aaeda Translate Slovenian import recovery and correct core labels
Thanks to xet7.
2026-10-08 14:46:07 +03:00
Lauri Ojansivu 66c92ab371 Document Bulgarian planning translations
Thanks to xet7.
2026-10-08 14:44:39 +03:00
Lauri Ojansivu 4a9629d4f7 Translate Bulgarian controls and planning
Thanks to xet7.
2026-10-08 14:44:11 +03:00
Lauri Ojansivu 929d321db1 Document Bulgarian recovery translations
Thanks to xet7.
2026-10-08 14:42:20 +03:00
Lauri Ojansivu ae15d9657f Translate Bulgarian import and synchronization recovery
Thanks to xet7.
2026-10-08 14:41:45 +03:00
Lauri Ojansivu b00e6357c9 Document Lithuanian planning translations
Thanks to xet7.
2026-10-08 14:39:57 +03:00
github-actions[bot] a45087c438 Bump versions for v12.24 2026-10-08 11:39:50 +00:00
Lauri Ojansivu 03839e6225 Translate Lithuanian controls and planning
Thanks to xet7.
2026-10-08 14:39:29 +03:00
Lauri Ojansivu 602e248d3c Prepare v12.24 release 2026-10-08 14:37:35 +03:00
Lauri Ojansivu 9ba8757cdc 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-10-08 14:37:07 +03:00
Lauri Ojansivu faf6725f9a Translate Lithuanian import and synchronization recovery
Thanks to xet7.
2026-10-08 14:36:45 +03:00
Lauri Ojansivu 699e260824 Document Latvian planning translations
Thanks to xet7.
2026-10-08 14:35:08 +03:00
Lauri Ojansivu be43a70825 Translate Latvian controls and planning
Thanks to xet7.
2026-10-08 14:34:31 +03:00
Lauri Ojansivu e81188c2ad Document Latvian recovery translations
Thanks to xet7.
2026-10-08 14:32:36 +03:00
Lauri Ojansivu ef9af80034 Translate Latvian import and synchronization recovery
Thanks to xet7.
2026-10-08 14:31:48 +03:00
Lauri Ojansivu 3e4f743694 Document Estonian recovery translations
Thanks to xet7.
2026-10-08 14:29:56 +03:00
Lauri Ojansivu 27998c617a Translate Estonian controls and recovery
Thanks to xet7.
2026-10-08 14:29:20 +03:00
Lauri Ojansivu baa927a70b Document Russian recovery translations
Thanks to xet7.
2026-10-08 14:27:07 +03:00
Lauri Ojansivu 24912011f5 Translate Russian controls and recovery
Thanks to xet7.
2026-10-08 14:26:31 +03:00
Lauri Ojansivu 6df66121db Document Ukrainian recovery translations
Thanks to xet7.
2026-10-08 14:24:09 +03:00
Lauri Ojansivu 1b3ea17c28 Translate Ukrainian controls and import recovery
Thanks to xet7.
2026-10-08 14:23:26 +03:00
Lauri Ojansivu 5b5ba294f9 Document Greek translation completion batch
Thanks to xet7.
2026-10-08 14:21:20 +03:00
Lauri Ojansivu c458610f62 Translate Greek controls and planning recovery
Thanks to xet7.
2026-10-08 14:20:19 +03:00
Lauri Ojansivu 8700609daf Document Turkish planning translations
Thanks to xet7.
2026-10-08 14:16:33 +03:00
Lauri Ojansivu 02ee6a37c9 Translate Turkish planning imports and history recovery
Thanks to xet7.
2026-10-08 14:15:54 +03:00
Lauri Ojansivu fcecbd1b1d Document Turkish synchronization translations
Thanks to xet7.
2026-10-08 14:15:41 +03:00
Lauri Ojansivu a9b2601c30 Translate Turkish controls and synchronization recovery
Thanks to xet7.
2026-10-08 14:14:26 +03:00
Lauri Ojansivu ddde78f87a Document Turkish import recovery translations
Thanks to xet7.
2026-10-08 14:13:45 +03:00
Lauri Ojansivu 8fcbd51013 Record the copy-link address and sign-in address fixes in the changelog.
Thanks to n8willis and xet7 !
2026-10-08 14:10:30 +03:00
Lauri Ojansivu 16bfc9ebab Translate Turkish interrupted import recovery
Fill 25 messages while preserving source variables and existing translations. Check retention, permanent deletion, recovery checkpoints and unrelated-board protection.

Thanks to xet7.
2026-10-08 14:09:54 +03:00
Lauri Ojansivu 4fc4f19f02 Say on the sign-in page why a Google login returns to it.
Signing in with Google brought the user back to the sign-in page every
time, with no error. Meteor's OAuth builds the provider's redirect_uri
from ROOT_URL, so the provider returns the browser to ROOT_URL's origin
and the callback leaves the login secret in that origin's storage. When
WeKan was opened at any other address - http for https, with or without
www, an IP address, another port, or the snap's 127.0.0.1 default - the
sign-in page never sees the secret and the login never completes.

The provider only accepts its registered redirect URI, so this cannot be
repaired from the page. Instead, when a provider login (OAuth2/OIDC,
SAML, CAS or an accounts-* provider such as Google) is offered and the
page's origin differs from ROOT_URL's, the sign-in page now names both
addresses and says to open WeKan at ROOT_URL or set ROOT_URL to the
address in use. The OAuth providers documentation lists it under
Troubleshooting.

Thanks to xet7 !
2026-10-08 14:09:34 +03:00
Lauri Ojansivu b7e569e8d3 Document Romanian planning and history recovery translations
Thanks to xet7.
2026-10-08 14:07:19 +03:00
Lauri Ojansivu 7e3ae95014 Translate Romanian planning imports and history recovery
Fill 29 messages in each Romanian catalog while preserving existing translations and source variables. Extend checks for matching priority, unchanged cards and history recovery decisions.

Thanks to xet7.
2026-10-08 14:06:40 +03:00
Lauri Ojansivu a53a61ea08 Document Romanian controls and Sync recovery translations
Thanks to xet7.
2026-10-08 14:05:10 +03:00
Lauri Ojansivu 0f2e81e01b Copy links use the address the browser reached WeKan by when ROOT_URL is loopback.
The snap's default ROOT_URL is 127.0.0.1, and every link built in the
browser - Copy link of a card, list or swimlane, comment permalinks -
goes through Meteor.absoluteUrl(), which reads ROOT_URL, so a browser
that opened WeKan by the machine's name copied a 127.0.0.1 link nobody
else could follow. When ROOT_URL is loopback and the page is not, the
client now builds absolute links from the page's own origin, keeping
ROOT_URL's path. A real ROOT_URL still wins, and the server keeps
ROOT_URL for emails: `snap set wekan root-url=...` remains the setting
for those.

Thanks to n8willis and xet7 !

Fixes #6752.
2026-10-08 14:04:48 +03:00
Lauri Ojansivu b67435dd6d Translate Romanian controls and stuck Sync recovery
Fill 44 messages in each Romanian catalog while preserving existing translations, source variables and literal syntax. Extend checks for retained and discarded Sync changes and server-only login settings.

Thanks to xet7.
2026-10-08 14:04:34 +03:00
Lauri Ojansivu 4a49199791 Document Romanian import recovery translations
Thanks to xet7.
2026-10-08 14:02:55 +03:00
Lauri Ojansivu 09a5b44862 Translate Romanian interrupted import recovery
Fill 25 messages in each Romanian catalog while preserving source variables and existing translations. Check retention, permanent deletion, recovery checkpoints and unrelated-board protection.

Thanks to xet7.
2026-10-08 14:02:22 +03:00
Lauri Ojansivu 7ff3358766 Merge remote-tracking branch 'origin/main' 2026-10-08 14:01:06 +03:00
Lauri Ojansivu e9f7a9b496 Document Hungarian controls and Sync recovery translations
Thanks to xet7.
2026-10-08 14:00:56 +03:00
Lauri Ojansivu 2108d855ca Translate Hungarian controls and stuck Sync recovery
Fill 44 messages while preserving existing translations, source variables and literal syntax. Extend checks for retained and discarded Sync changes, movement directions and server-only login settings.

Thanks to xet7.
2026-10-08 14:00:22 +03:00
Lauri Ojansivu 391e8b2673 Document Hungarian import recovery translations
Thanks to xet7.
2026-10-08 13:58:34 +03:00
Lauri Ojansivu 56600a495a Translate Hungarian interrupted import recovery
Fill 25 messages while preserving source variables and existing translations. Check retention, permanent deletion, recovery checkpoints and unrelated-board protection.

Thanks to xet7.
2026-10-08 13:58:02 +03:00
Lauri Ojansivu ee528c114f Document Slovak controls and Sync recovery translations
Thanks to xet7.
2026-10-08 13:56:49 +03:00
Lauri Ojansivu 66b6c6cf7d Translate Slovak controls and stuck Sync recovery
Fill 43 messages while preserving existing translations, source variables and literal syntax. Extend checks for retained and discarded Sync changes and movement directions.

Thanks to xet7.
2026-10-08 13:56:22 +03:00
Lauri Ojansivu fb5a7a61be Document Slovak import recovery translations
Thanks to xet7.
2026-10-08 13:54:53 +03:00
Lauri Ojansivu 6079856b93 Translate Slovak interrupted import recovery
Fill 25 messages while preserving source variables and existing translations. Check retention, permanent deletion, recovery checkpoints and unrelated-board protection.

Thanks to xet7.
2026-10-08 13:54:29 +03:00
Lauri Ojansivu 6560ecb659 Document Czech controls and Sync recovery translations
Thanks to xet7.
2026-10-08 13:53:18 +03:00
Lauri Ojansivu cb9850aa9d Translate Czech controls and stuck Sync recovery
Fill 43 messages while preserving existing translations, source variables and literal syntax. Extend checks for retained and discarded Sync changes and movement directions.

Thanks to xet7.
2026-10-08 13:52:47 +03:00
Lauri Ojansivu c7f993dc89 Document Czech import recovery translations
Thanks to xet7.
2026-10-08 13:50:45 +03:00
Lauri Ojansivu 6cff7c86fc Translate Czech interrupted import recovery
Fill 25 messages while preserving source variables and existing translations. Check retention, permanent deletion, recovery checkpoints and unrelated-board protection.

Thanks to xet7.
2026-10-08 13:50:21 +03:00
Lauri Ojansivu 8fe9a9d902 Document Polish planning and recovery translations
Thanks to xet7.
2026-10-08 13:49:12 +03:00
Lauri Ojansivu 49e8be5cd1 Translate Polish planning imports and recovery messages
Fill 61 messages while preserving existing translations, source variables and literal examples. Extend checks for recovery choices, matching priority and unchanged cards.

Thanks to xet7.
2026-10-08 13:48:44 +03:00
Lauri Ojansivu e90935d0b5 Document Polish import and Sync recovery translations
Thanks to xet7.
2026-10-08 13:47:04 +03:00
Lauri Ojansivu 8cdd39385f Translate Polish import guidance and stuck Sync recovery
Fill 36 messages while preserving existing translations, source variables and literal syntax. Extend checks for retained and discarded Sync changes.

Thanks to xet7.
2026-10-08 13:46:38 +03:00
Lauri Ojansivu 598c5fee98 Document Finnish planning and recovery translations
Thanks to xet7.
2026-10-08 13:45:12 +03:00
Lauri Ojansivu e25bf4ec94 Translate Finnish planning imports and recovery messages
Fill 61 messages while preserving existing translations, source variables and literal examples. Extend checks for recovery choices, matching priority and unchanged cards.

Thanks to xet7.
2026-10-08 13:44:48 +03:00
Lauri Ojansivu c83fef157a Document Bokmal planning and recovery translations
Thanks to xet7.
2026-10-08 13:43:02 +03:00
Lauri Ojansivu 1918d37067 Translate Bokmal planning imports and recovery messages
Fill 61 messages while preserving existing translations, source variables and literal examples. Extend checks for recovery choices, matching priority and unchanged cards.

Thanks to xet7.
2026-10-08 13:42:35 +03:00
Lauri Ojansivu 064957044f Document Bokmal import and Sync recovery translations
Thanks to xet7.
2026-10-08 13:40:57 +03:00
Lauri Ojansivu 28d3785914 Translate Bokmal import guidance and stuck Sync recovery
Fill 36 messages while preserving existing translations, source variables and literal syntax. Extend checks for retained and discarded Sync changes.

Thanks to xet7.
2026-10-08 13:40:33 +03:00
Lauri Ojansivu 321bfba791 Document Danish planning and recovery translations
Thanks to xet7.
2026-10-08 13:39:11 +03:00
Lauri Ojansivu 9da06c16a4 Translate Danish planning imports and recovery messages
Fill 61 messages while preserving existing translations, source variables and literal examples. Extend checks for recovery choices, matching priority and unchanged cards.

Thanks to xet7.
2026-10-08 13:38:46 +03:00
Lauri Ojansivu dc137e13ae Document Danish import and Sync recovery translations
Thanks to xet7.
2026-10-08 13:37:07 +03:00
Lauri Ojansivu cd886e99fc Translate Danish import guidance and stuck Sync recovery
Fill 36 messages while preserving existing translations, source variables and literal syntax. Extend checks for retained and discarded Sync changes.

Thanks to xet7.
2026-10-08 13:36:41 +03:00
Lauri Ojansivu 1462c41d06 Document Swedish planning and recovery translations
Thanks to xet7.
2026-10-08 13:35:14 +03:00
Lauri Ojansivu e00407123e Translate Swedish planning imports and recovery messages
Fill 61 messages while preserving existing translations, source variables and literal examples. Extend checks for recovery choices, matching priority and unchanged cards.

Thanks to xet7.
2026-10-08 13:34:50 +03:00
Lauri Ojansivu df966787c9 Document Swedish import and Sync recovery translations
Thanks to xet7.
2026-10-08 13:33:00 +03:00
Lauri Ojansivu 36e11d85fd Translate Swedish import guidance and stuck Sync recovery
Fill 36 messages while preserving existing translations, source variables and literal syntax. Extend checks for retained and discarded Sync changes.

Thanks to xet7.
2026-10-08 13:32:25 +03:00
Lauri Ojansivu 0cdc8151be Document Dutch planning and recovery translations
Thanks to xet7.
2026-10-08 13:30:57 +03:00
Lauri Ojansivu d54be51d9c Translate Dutch planning imports and recovery messages
Fill 61 messages in each Dutch catalog while preserving existing translations, source variables and literal examples. Extend checks for recovery choices, matching order and unchanged cards.

Thanks to xet7.
2026-10-08 13:30:27 +03:00
Lauri Ojansivu 407d4684ee Document Dutch import and Sync recovery translations
Thanks to xet7.
2026-10-08 13:28:51 +03:00
Lauri Ojansivu 78a076e317 Translate Dutch import guidance and stuck Sync recovery
Fill 36 messages in each Dutch catalog, preserving existing translations, source variables and literal syntax. Extend checks for retained and discarded Sync changes.

Thanks to xet7.
2026-10-08 13:28:18 +03:00
Lauri Ojansivu 4b05d12498 Document Portuguese import and Sync recovery translations
Thanks to xet7.
2026-10-08 13:26:38 +03:00
Lauri Ojansivu 9c4066ce8f Translate Portuguese import guidance and stuck Sync recovery
Fill 36 messages in each of four Portuguese catalogs while preserving regional wording, source variables, format syntax and LDAP settings. Extend checks for retained and discarded Sync changes.

Thanks to xet7.
2026-10-08 13:26:10 +03:00
Lauri Ojansivu 5ae46a82af Document Portuguese planning and recovery translations
Thanks to xet7.
2026-10-08 13:24:38 +03:00
Lauri Ojansivu ea258e866d Translate Portuguese planning imports and recovery messages
Fill 61 messages in each of four Portuguese catalogs, retaining existing translations and regional terminology. Extend checks for recovery decisions, matching priority and literal syntax.

Thanks to xet7.
2026-10-08 13:24:09 +03:00
Lauri Ojansivu cdc657832f Document Spanish import and Sync recovery translations
Thanks to xet7.
2026-10-08 13:22:08 +03:00
Lauri Ojansivu f47f6b3c65 Translate Spanish import guidance and stuck Sync recovery
Fill 36 messages in each of nine Spanish catalogs. Preserve source variables, import syntax, LDAP names and the distinction between retained and discarded Sync changes.

Thanks to xet7.
2026-10-08 13:21:34 +03:00
Lauri Ojansivu 62e7f69712 Document Spanish planning and recovery translations
Thanks to xet7.
2026-10-08 13:20:06 +03:00
Lauri Ojansivu afafc47bf0 Translate Spanish planning imports and recovery messages
Fill 61 messages in each of nine Spanish catalogs while preserving existing localized values and source syntax. Extend checks for recovery decisions, import matching and permanent-deletion warnings.

Thanks to xet7.
2026-10-08 13:19:40 +03:00
Lauri Ojansivu 6290b5bf78 Document Italian planning and recovery translations
Thanks to xet7.
2026-10-08 13:17:28 +03:00
Lauri Ojansivu 9687307660 Translate Italian planning and recovery messages
Fill 97 current messages while preserving existing localized values, source variables and literal examples. Extend regression checks for import matching, recovery decisions, LDAP settings and retained Sync changes.

Thanks to xet7.
2026-10-08 13:16:59 +03:00
Lauri Ojansivu a274106d05 Document German planning and recovery translations
Thanks to xet7.
2026-10-08 13:14:09 +03:00
Lauri Ojansivu d9b585135d Translate German planning imports and recovery messages
Fill 61 messages in each of four German catalogs, preserving existing translations and Swiss spelling. Extend regression checks for recovery decisions, source matching and literal syntax.

Thanks to xet7.
2026-10-08 13:13:38 +03:00
Lauri Ojansivu fca75ec05e Document French planning and recovery translations
Thanks to xet7.
2026-10-08 13:11:14 +03:00
Lauri Ojansivu 93c380310f Translate French planning imports and recovery messages
Fill 61 messages in each of five French catalogs, preserving existing localized values and source syntax. Extend translation and semantic regression checks for import, Sync and history recovery decisions.

Thanks to xet7.
2026-10-08 13:10:10 +03:00
Lauri Ojansivu 2b135202c3 Document Aromanian import recovery translations
Thanks to xet7.
2026-10-08 13:07:40 +03:00
Lauri Ojansivu cc7ff1c856 Translate Aromanian import recovery and repair mixed-language controls
Translate 25 interrupted-import messages and correct 65 card, checklist, attachment and account labels. Preserve variables, recovery decisions and shared dialog wording. New phrasing remains low-confidence pending fluent review.

Thanks to xet7.
2026-10-08 13:07:12 +03:00
Lauri Ojansivu d7d6ed2821 Document Aromanian search and organization corrections
Thanks to xet7.
2026-10-08 13:04:27 +03:00
Lauri Ojansivu fd90691e00 Correct Italian-seeded Aromanian search and organization labels
Preserve search variables, shared labels, predicate syntax and opposite actions. New wording remains low-confidence pending fluent review.

Thanks to xet7.
2026-10-08 13:03:55 +03:00
Lauri Ojansivu 62bf4d204e Document Aromanian rule-builder language corrections
Thanks to xet7.
2026-10-08 13:00:15 +03:00
Lauri Ojansivu cca57e46b5 Correct Italian-seeded Aromanian rule-builder labels
Replace 35 wrong-language values and preserve action opposites and shared checklist wording. New phrasing remains low-confidence pending fluent review.

Thanks to xet7.
2026-10-08 12:59:48 +03:00
Lauri Ojansivu 436336956a Record restored translation suite coverage after planning imports
All 322 translation suites and 21 human-preference checks pass. Keep the outstanding language backlog and fluent-review limitations visible.

Thanks to xet7.
2026-10-08 12:58:40 +03:00
Lauri Ojansivu 6f4a96b8c0 Translate Northern Sotho planning imports and repair locale suites
Fill 20 messages and extend the Scrum inventory to 127 while retaining per-key checks. Preserve matching priority and existing planning. Full new wording remains low-confidence pending fluent review.

Thanks to xet7.
2026-10-08 12:57:16 +03:00
Lauri Ojansivu bdc76af800 Document Maori planning translation repairs
Thanks to xet7.
2026-10-08 12:55:40 +03:00
Lauri Ojansivu 38e8f1d4bc Translate Maori planning imports and repair locale suites
Fill 20 messages and extend the Scrum inventory to 127 while retaining per-key checks. Preserve matching priority and existing planning. Full new wording remains low-confidence pending fluent review.

Thanks to xet7.
2026-10-08 12:55:05 +03:00
Lauri Ojansivu 6f58347b56 Document Somali planning translation repairs
Thanks to xet7.
2026-10-08 12:52:54 +03:00
Lauri Ojansivu 50a2d2fce1 Translate Somali planning imports and repair locale suites
Fill 20 messages and extend the Scrum inventory to 127 without weakening per-key checks. Preserve matching priority and existing planning. New wording remains low-confidence pending fluent review.

Thanks to xet7.
2026-10-08 12:52:23 +03:00
Lauri Ojansivu 59c5149746 Document Tok Pisin planning translation repairs
Thanks to xet7.
2026-10-08 12:50:57 +03:00
Lauri Ojansivu eb846b6ca0 Translate Tok Pisin planning imports and repair locale suites
Fill 20 messages and retain per-key coverage while extending Scrum inventory to 127. Preserve matching, non-duplication and first-Sync planning protection. New wording remains low-confidence pending fluent review.

Thanks to xet7.
2026-10-08 12:50:29 +03:00
Lauri Ojansivu 0a664f94c7 Document Ukrainian planning translation repairs
Thanks to xet7.
2026-10-08 12:49:21 +03:00
Lauri Ojansivu f06dd84b40 Translate Ukrainian Scrum import and Sync planning messages
Preserve counters, matching priority, non-duplication and existing planning protections. Restore planning regression coverage.

Thanks to xet7.
2026-10-08 12:48:54 +03:00
Lauri Ojansivu 93d814305e Document Estonian planning translation repairs
Thanks to xet7.
2026-10-08 12:47:24 +03:00
Lauri Ojansivu 27fe2c86ed Translate Estonian Scrum import and Sync planning messages
Preserve counters, matching priority, non-duplication and existing planning protections. Restore the Estonian regression suite without weakening coverage.

Thanks to xet7.
2026-10-08 12:46:55 +03:00
Lauri Ojansivu 6375b12636 Record Planning Sync, Scrum import into a board and import recovery in the changelog.
The three remaining buildable TODO Later items are done and removed from it.

Thanks to xet7 !
2026-10-08 12:45:32 +03:00
Lauri Ojansivu 3ae3852099 Repair planning translations and Aromanian system labels
Translate 116 new planning messages across six catalogs and correct 34 Italian-seeded Aromanian labels. Preserve counters, source matching and existing planning protections. New Aromanian wording remains low-confidence pending fluent review. Seven targeted suites pass; ten failures from the latest broad run remain open.

Thanks to xet7.
2026-10-08 12:45:17 +03:00
Lauri Ojansivu e8fac5e85c Record every board import before its first write, and let an admin keep or discard an interrupted one.
An import that stopped halfway - a restart, a hung writer, a failure - left a
partial board nothing recorded. Each board import, copy, Trello zip and
Trello API import now writes an importRuns record first, with the id its new
board will have; every creator inserts the board with that id and the run's
id stamped on it. A heartbeat keeps the run alive; the method deadline now
stops the writer at its next step. A scan at startup and every 5 minutes
flags a stopped or failed run once and records it in Admin Panel ->
Problems -> Recovery, where an administrator keeps the partial board or
discards it: only the board carrying the run's stamp is removed, through the
board's own removal, then whatever else has its id. Discard is refused while
the import's Scrum stage is still written or recovered, and the Scrum resume
is refused while the import is discarded. Resume is deliberately not
offered: the source file is not kept, for its size, its personal data and
the anonymization it would undo; discard and import again instead.

Thanks to xet7 !
2026-10-08 12:44:46 +03:00
Lauri Ojansivu 68ff232e65 Document Aromanian account language corrections
Thanks to xet7.
2026-10-08 12:37:05 +03:00
Lauri Ojansivu ef54907836 Import Scrum planning into an existing board.
Every Scrum import used to create a new board. A board administrator can
now import a native wekan-scrum-2 transfer, or a WeKan board export, into
a board that already exists, from the Sprints view: Preview shows what
would change and writes nothing, Import writes it.

Sprints and releases are matched by ID on this board, then by
provenance, then by a name exactly one record has on each side; the rest
are created once with the source's provenance, so importing the same
file again changes nothing. Cards are matched by ID, or by card number
and title, and never created; unmatched, ambiguous and other boards'
cards are reported and left alone. Matched cards get the file's sprint,
releases, backlog rank, issue type and acceptance criteria; a move into a
finished sprint is refused. The writes go through the journaled Scrum
import stage, so an interrupted import is finished or discarded the same
way, and are recorded as one Scrum History change.

Thanks to xet7 !
2026-10-08 12:36:57 +03:00
Lauri Ojansivu 94209a2764 Correct Italian-seeded Aromanian account and webhook labels
Replace 35 wrong-language values and preserve overtime meaning, invitation variables and technical names. Full new wording remains low-confidence pending fluent review.

Thanks to xet7.
2026-10-08 12:36:33 +03:00
Lauri Ojansivu a8a78f54f9 Give the board announcement popup a title and audit its Save.
The popup opened with no title, so it rendered without a header and with
no close button; it now opens with the existing "Board announcement"
key as its title. Its Save is a submit button inside the form, handled
by the form's submit event, which the menu action inventory now records
and checks.

Thanks to xet7 !
2026-10-08 12:36:28 +03:00
Lauri Ojansivu c06bf8d0e9 Sync sprints and releases into Scrum planning.
List Sync from Jira or GitLab now carries an issue's sprint and fix
versions / milestones into the board's Scrum planning while Scrum is
enabled on the board. A missing sprint or release is created on this
board with the dates the source gives; an existing one is matched by its
source ID first, then by name. A local planning change stays until the
source changes that issue's planning, and a first Sync never removes
planning. The writes go through the durable Sync journal and steps, so
an interrupted Sync is finished or discarded like any other.

Thanks to xet7 !
2026-10-08 12:35:58 +03:00
Lauri Ojansivu 2213eba10c Document Aromanian board-control language corrections
Thanks to xet7.
2026-10-08 12:35:25 +03:00
Lauri Ojansivu cfc5edcbdb Correct Italian-seeded Aromanian board controls
Replace 35 wrong-language values, retain variables and import format names, and cover movement and removal scope. Full new wording remains low-confidence pending fluent review.

Thanks to xet7.
2026-10-08 12:34:56 +03:00
Lauri Ojansivu 9cbf1c0565 Document Aromanian wrong-language corrections
Thanks to xet7.
2026-10-08 12:33:09 +03:00
Lauri Ojansivu d7ff39eba6 Replace Italian-seeded Aromanian dialogs and filters
Correct 47 wrong-language values while retaining invitation variables and technical names. Add regressions against Italian and English fallback. New phrasing remains low-confidence pending fluent review.

Thanks to xet7.
2026-10-08 12:32:41 +03:00
Lauri Ojansivu aa047afbf0 Document Aromanian stuck Sync translation coverage
Thanks to xet7.
2026-10-08 12:31:06 +03:00
Lauri Ojansivu 76b35e70c6 Translate Aromanian stuck Sync recovery messages
Fill 23 messages while preserving applied changes, unwritten remainder and replay eligibility. New wording remains low-confidence pending fluent review; older linguistic defects remain open.

Thanks to xet7.
2026-10-08 12:30:32 +03:00
Lauri Ojansivu a5e1bbc8d7 Document Aromanian controls and import translation coverage
Thanks to xet7.
2026-10-08 12:29:12 +03:00
Lauri Ojansivu 5e251e53ac Translate Aromanian imports controls and Sync planning
Fill 47 values while preserving import syntax, link-rule variables, configuration names and Sync planning semantics. New terminology and grammar remain low-confidence pending fluent review.

Thanks to xet7.
2026-10-08 12:28:35 +03:00
Lauri Ojansivu 6d0289b12d Document Aromanian Sync translation coverage
Thanks to xet7.
2026-10-08 12:26:28 +03:00
Lauri Ojansivu 43a7b0246e Translate Aromanian Sync choices and diagnostics
Fill 63 messages and cover conflict scope, replacement reuse, report limits and missing-versus-null values. New wording remains low-confidence pending fluent review.

Thanks to xet7.
2026-10-08 12:25:57 +03:00
Lauri Ojansivu fedbbaa166 Record move-direction rule triggers in the changelog.
Thanks to xet7 !
2026-10-08 12:25:46 +03:00
Lauri Ojansivu eac524380f Add rule triggers for a card moved forward or back.
The reporter puts a card at the bottom of the next list when it moves on and
at the top when it moves back. With only "moved to" and "moved from" a list
triggers, that needs a rule for every pair of lists. "A card is moved forward
to a later list" and "moved back to an earlier list" now fire on any move
between two lists of the board, compared by the lists' order rather than by
the screen, so they also work in right-to-left languages. They are matched
on the rule's own board, like the other trigger kinds
(models/lib/ruleMoveDirection.js).

tests/ruleMoveDirection.test.cjs covers the directions and what has none; a
Playwright case moves cards both ways through the REST API. The RepointBleed
guard counts the new matching path. New text is in English, pending
Transifex.

Thanks to rlach and xet7 !

Fixes #2076.
2026-10-08 12:25:29 +03:00
Lauri Ojansivu 0d44eab64f Document Aromanian notification recovery coverage
Thanks to xet7.
2026-10-08 12:23:45 +03:00
Lauri Ojansivu a050566954 Translate Aromanian notification recovery messages
Fill 36 messages and cover permanent rejection, retained pending work, non-recreation of activities and irreversible cancellation. New wording remains low-confidence pending fluent review.

Thanks to xet7.
2026-10-08 12:23:16 +03:00
Lauri Ojansivu 9c3c8a368b Record identifier link rules in the changelog.
Thanks to xet7 !
2026-10-08 12:23:02 +03:00
Lauri Ojansivu ed993fef84 Autolink references with an identifier and abbreviations to other tools.
Admin Panel / Settings / Features could turn one "<prefix><number>" pattern
into links (#3069). The reporter wanted "[Task:3384]" and "[Incident:3384]"
to link to their tracker, and "[TK:1223]" with TK an abbreviation of Task.
"More link rules" now takes one "<token> = <url>" per line, with {number}
and {identifier} in both, and "Abbreviations" maps an identifier to the
word the address needs. All rules are matched in one pass, earliest first,
after the #3069 pattern, and text that is already a link is never rewritten,
so no rule relinks another's link. Only an http(s) rule with exactly one
{number} is used, and the values are URL-encoded into the address.

The markdown package carries a copy of models/lib/externalLinkRules.js;
tests/externalLinkRules.test.cjs keeps the two identical and covers the
reporter's examples and the refusals. The release risk baseline records the
example addresses in the new comments, and the default avatar's, which it
had not. New text is in English, pending Transifex.

Thanks to rzoss and xet7 !

Fixes #1463.
2026-10-08 12:22:43 +03:00
Lauri Ojansivu 2c8a4f1b26 Document Aromanian Scrum instruction coverage
Thanks to xet7.
2026-10-08 12:21:07 +03:00
Lauri Ojansivu f73ce87d54 Translate remaining Aromanian Scrum instructions
Preserve reporting caveats, cancellation membership and rollback eligibility. Cover all current Scrum keys and source variables. Full new wording remains low-confidence pending fluent review.

Thanks to xet7.
2026-10-08 12:20:34 +03:00
Lauri Ojansivu 76f2a600b6 Record board announcements in the changelog.
Thanks to xet7 !
2026-10-08 12:19:55 +03:00
Lauri Ojansivu 7ccd987eb1 Give each board an announcement of its own.
The site has one announcement for everybody; the reporter's teams needed a
reminder per board. A board admin writes it in the board menu, Board
announcement, and turns it on. Every member then sees it as a banner on the
board, as plain text, until they dismiss it; an edit gives it a new version,
so it shows again. Dismissing records the version the server reads, for a
board the user can see, so a client cannot dismiss a future edit in advance.
The text is trimmed and bounded, and an empty one cannot be turned on.

tests/boardAnnouncement.test.cjs covers showing, dismissing, editing and
the bounds; the board menu order guard includes the new item, admin only. A
Playwright case checks the banner and the dismissal. New text is in English,
pending Transifex.

Thanks to TiibCD and xet7 !

Fixes #1566.
2026-10-08 12:19:38 +03:00
Lauri Ojansivu 771abe1def Document Aromanian Scrum translation coverage
Thanks to xet7.
2026-10-08 12:18:30 +03:00
Lauri Ojansivu 4eec94b7c8 Translate Aromanian Scrum labels and reporting controls
Fill 73 values, preserve variables and shared labels, and use the shared token scanner in regression coverage. New terminology and phrasing remain low-confidence pending fluent review.

Thanks to xet7.
2026-10-08 12:18:01 +03:00
Lauri Ojansivu 51c06a306d Record list-coloured cards in the changelog.
Thanks to xet7 !
2026-10-08 12:16:04 +03:00
Lauri Ojansivu 998a9cc336 Let a list colour the cards that have no colour of their own.
The reporter coloured lists by stage and wanted cards to follow: "by merely
dragging cards around the colors would change". The only way was a script
that rewrote every card's colour through the API, again after every move.
A list's colour popup now has "Cards without a colour of their own use this
colour". Such cards are shown in the list's colour, and nothing is written
to them: a card dragged to another list takes that list's colour, and a
card's own colour always wins. models/lib/cardDisplayColor.js decides; the
card colour helpers use it from the list the card is shown in.

tests/cardDisplayColor.test.cjs covers the rule; the guards that pinned the
old colour code now pin it through the new helper. New text is in English,
pending Transifex.

Thanks to C0rn3j and xet7 !

Fixes #4756.
2026-10-08 12:15:49 +03:00
Lauri Ojansivu 461cdf9f22 Record restored translation suite coverage
All 322 translation suites and 21 human-preference checks pass. Preserve the outstanding translation backlog and fluent-review caveat.

Thanks to xet7.
2026-10-08 12:15:00 +03:00
Lauri Ojansivu 4b0fde93ef Translate new Scrum messages and repair four locale suites
Fill 36 release-selection and recovery messages in Maori, Northern Sotho, Somali and Tok Pisin. Extend the expected inventory to 111 while retaining per-key coverage and variable checks. Full new wording remains low-confidence pending fluent review.

Thanks to xet7.
2026-10-08 12:13:36 +03:00
Lauri Ojansivu 4dba3f6799 Record per-board views in the changelog.
Thanks to xet7 !
2026-10-08 12:12:40 +03:00
Lauri Ojansivu 80fb317fda Remember the board view per board.
The view menu sits on a board, but choosing a view there changed it for
every board, so one board could not stay on Calendar while another stayed
on Swimlanes. A view chosen on a board is now that board's own
(profile.boardViews, published to its owner beside the shared value). A
board without its own choice uses the view every board shared before, then
the board's default, so nothing changes for anybody until they choose a
view on a board. A choice made off any board still sets the shared view,
and the browser's stored view follows only that one. The server accepts only
a known view under a safe board key. Label text visibility per board, the
other half of #4256, was done earlier.

The guards that pinned the single shared view now pin both; a new test runs
the stored-view logic per board, and a Playwright case checks two boards.

Thanks to DimDz, Meeques and xet7 !

Fixes #4906,
Fixes #4256.
2026-10-08 12:12:24 +03:00
Lauri Ojansivu 9d1da86712 Document German translations and planning regression repairs
Thanks to xet7.
2026-10-08 12:10:29 +03:00
Lauri Ojansivu 362a43fa43 Translate new planning messages exposed by regression tests
Fill 63 release-selection and Scrum recovery values in seven catalogs. Restore six planning suites without weakening their coverage; four other Scrum translation suites remain to repair.

Thanks to xet7.
2026-10-08 12:09:59 +03:00
Lauri Ojansivu 623f7c5a11 Record Scrum planning from more importers in the changelog.
The item leaves TODO Later.

Thanks to xet7 !
2026-10-08 12:08:54 +03:00
Lauri Ojansivu 958e9e13f3 Import Scrum sprints and releases from GitLab, OpenProject and Asana.
Only the Jira importer created sprints and releases. GitLab iterations and
milestones became nothing but milestone: and iteration: labels, OpenProject
versions only a version: label, and Asana milestones and Trello produced
nothing Scrum-related, so a board moved from those tools lost its planning.

Imports now map what each export really carries, through the same journaled
Scrum import stage as Jira and WeKan JSON, so an interrupted import is
recovered the same way. GitLab iterations become sprints and milestones
releases, with their dates and state. OpenProject versions become releases
and its Sprint resources sprints; position sets the backlog rank and story
points become the board's estimate field. Asana milestone tasks become
releases for themselves and the tasks they wait for. Trello has no sprints or
releases; its Power-Up data is counted in the import loss report.

As for Jira, a started sprint is imported as planned and a finished one not
at all, because the exports have no commitment or close snapshot; both are
reported. Malformed dates, unknown states, duplicate names and cards in an
iteration that is not imported are reported, never invented. The milestone
and iteration labels stay for List Sync and existing boards. The import
page's Scrum settings part now also decides whether external imports bring
their planning in. GitLab and OpenProject exports write each card's sprint
and first release back so a board round-trips.

Thanks to xet7 !
2026-10-08 12:08:29 +03:00
Lauri Ojansivu 478fcc07f0 Translate pending imports and recovery messages in German locales
Fill 144 values across four catalogs, preserve Swiss spelling, source variables and technical syntax, and cover recovery consequences and release selection.

Thanks to xet7.
2026-10-08 12:07:14 +03:00
Lauri Ojansivu f22eb8740d Record read-only custom fields and Scrum checkpoint recovery in the changelog.
The Scrum checkpoint item leaves TODO Later.

Thanks to xet7 !
2026-10-08 12:07:11 +03:00
Lauri Ojansivu 9ad9813c09 Let a board admin resolve a Scrum undo or redo that stopped on a conflict.
A Scrum History undo, redo or restore keeps a checkpoint that blocks every
Scrum edit on its board until it finishes, and only its author could retry
it, with the same plan. When somebody else had changed one of its records,
its History row changed or its author lost write access, every retry failed
the same way, the checkpoint could not be discarded online or offline, and
the board's Scrum edits stayed blocked for good.

A retry that fails for a reason the next one would meet again now marks its
checkpoint. A board administrator sees such a checkpoint in the History
recovery notice and can roll it back, which restores every record it wrote
to the saved "before" value and revision and is offered only when nobody
changed those records since and none of its History was written, or keep the
board as it is, which removes the checkpoint and writes nothing while
reporting how much of the operation had been written. A checkpoint that has
not stopped on a conflict stays its author's to retry. The resolution names
the checkpoint it resolves, so a repeat is a no-op; it claims the checkpoint
by compare-and-set, fencing the author's workers, waits for an in-flight
write, and checks every record before and after its own writes. Each one is
recorded in Admin Panel / Problems / Recovery. The offline command
releases/recover-scrum-history.cjs does the same with every writer stopped.

Unit, integration (against MongoDB 7), Meteor and Playwright tests cover
rollback, discard, refusals, concurrency and the offline command. New text
is in English, pending Transifex.

Thanks to xet7 !
2026-10-08 12:06:49 +03:00
Lauri Ojansivu 87ae8895c3 Add read-only custom fields that every member sees and only admins set.
A custom field could be hidden from non-admins (adminOnly, #3141), but not
shown to everyone while editable only by admins, which a score or a value
computed elsewhere needs. A custom field can now be marked read-only in its
settings: every board member sees the value, and only a board admin - or the
REST API and rules acting as one - can set, change or clear it. The card
editor offers editing only to board admins, and the server refuses everyone
else through adminOnly's write guard, at the driver boundary, so no write
path is left out. Turning the flag on or off is itself an admin's change.

tests/readOnlyCustomField.test.cjs drives the rule, including setting an
empty field and clearing one. New text is in English, pending Transifex.

Thanks to CarloRampini and xet7 !

Fixes #3143.
2026-10-08 12:05:59 +03:00
Lauri Ojansivu 1fde120c04 Document French translation recovery coverage
Thanks to xet7.
2026-10-08 12:04:17 +03:00
Lauri Ojansivu 20cae59763 Translate pending imports and recovery messages in French locales
Fill 180 values across five catalogs while retaining source variables and technical syntax. Cover recovery consequences, replay eligibility and release selection.

Thanks to xet7.
2026-10-08 12:03:42 +03:00
Lauri Ojansivu 561b4c8d3c Close #5050 as already implemented: add all users to a board.
The maintainer suggested adding the users to an organization or team and
giving the board to it. Admin Panel / People adds a page of selected users
to a team in one step, organizations gain users by email domain, and
"propagate members to boards" makes the team's or organization's members
members of its boards, later members included.

Thanks to rjl9 and xet7 !

Fixes #5050.
2026-10-08 12:03:40 +03:00
Lauri Ojansivu d6d01489cd Remove closed issues from TODO Later.
#6744 and #6746 are closed with their fixes in Upcoming, and #6509's SAP
HANA test is already its own entry.

Thanks to xet7 !
2026-10-08 12:03:07 +03:00
Lauri Ojansivu 1125113dba Document Finnish translation recovery coverage
Thanks to xet7.
2026-10-08 12:02:00 +03:00
Lauri Ojansivu b7c525e8d9 Record cross-board labels in the changelog.
Thanks to xet7 !
2026-10-08 12:01:38 +03:00
Lauri Ojansivu bffc32f899 Translate pending Finnish import and Sync recovery messages
Preserve variables, import syntax and configuration names. Cover retained changes, discarded remainder, replay eligibility and multiple-release selection.

Thanks to xet7.
2026-10-08 12:01:26 +03:00
Lauri Ojansivu 125e0960eb Bring a card's labels along when it moves or is copied to another board.
Labels belong to a board, so a card moved or copied to another board has
its labels mapped by name, and a label the destination board did not have
was dropped. Now a missing named label is created on the destination board,
with its colour, when the person moving or copying the card is an admin of
that board. Creating labels is admin-only there, and a move must not do
what that user could not do directly, so for anybody else such a label is
still dropped. Unnamed labels are never created or matched, as they cannot
be told apart. Copy and move share the rule in
models/lib/crossBoardLabels.js.

tests/crossBoardLabels.test.cjs covers admins, members, unnamed and duplicate
labels; the cross-board move Playwright case checks a label that only the
source board had.

Thanks to d3dbit and xet7 !

Fixes #1759.
2026-10-08 12:01:22 +03:00
Lauri Ojansivu fc9b896c4e Record the default OAuth2 organization in the changelog.
Thanks to xet7 !
2026-10-08 11:58:33 +03:00
Lauri Ojansivu dfcd115ff3 Let accounts created by an OAuth2/OIDC login join a default organization.
Organizations could gain new users automatically by email domain, but users
who register through one identity provider - the reporter's Nextcloud -
often share no domain, so they could not be grouped. OAUTH2_DEFAULT_ORGANIZATION,
or its field under Admin Panel / People / OAuth2, names an existing
organization (by short name, display name or id) that every account created
by an OAuth2/OIDC login joins, beside the domain rule. A name that matches no
organization creates none: the login goes on and the server log says so.

Listed on every platform, with snap help lines for it and for
DEFAULT_AVATAR_URL and OAUTH_PROVIDERS_ALLOWED_EMAIL_DOMAINS, which lacked
them.

Thanks to vasyugan and xet7 !

Fixes #5339.
2026-10-08 11:58:16 +03:00
Lauri Ojansivu 21244dcc34 Record Manx pending-message translation progress
Thanks to xet7 !
2026-10-08 11:57:24 +03:00
Lauri Ojansivu ee0806b4c0 Translate Manx release and stuck Sync recovery messages
Fill 25 pending messages while preserving release selection, applied/total variables, discard consequences and oldest-first limits. Full wording remains low-confidence pending fluent review; zero reported placeholders does not certify older translations.

Thanks to xet7 !
2026-10-08 11:56:47 +03:00
Lauri Ojansivu 83fe34a309 Record the default avatar and REST list archiving in the changelog.
Thanks to xet7 !
2026-10-08 11:56:05 +03:00
Lauri Ojansivu b426f9cfa8 Show a default avatar from DEFAULT_AVATAR_URL for users without one.
A user who had not uploaded an avatar was shown with initials only. The
maintainer accepted a general avatar feature configured in environment
variables only, with a URL to an intranet server or a Gravatar-style service
and the username replaced. DEFAULT_AVATAR_URL is that template: {username},
{userId}, {emailMd5} and {emailSha256} are replaced, URL-encoded, and only
an http(s) template is used. /avatar-default/<userId> redirects the browser
there; the server fetches nothing. An uploaded avatar always wins, and when
the default image does not load the initials are shown again. There is no
Admin Panel field. The variable is listed on every platform and documented in
docs/Features/Members/Default-Avatar.md.

Thanks to jLouzado and xet7 !

Fixes #824.
2026-10-08 11:55:42 +03:00
Lauri Ojansivu a7b32edfce Record Manx control and guidance translation progress
Thanks to xet7 !
2026-10-08 11:55:11 +03:00
Lauri Ojansivu 8378a4c706 Translate remaining reported Manx controls and guidance
Fill 47 messages while preserving import syntax, environment identifiers, report limits and named placeholders. Ordinary Manx placeholders are cleared; newly added pending messages and older language quality remain under review. Complete grammar is low-confidence pending fluent review.

Thanks to xet7 !
2026-10-08 11:54:39 +03:00
Lauri Ojansivu b95d4c1bfb Record the stuck List Sync discard in the changelog and remove it from TODO Later.
Thanks to xet7 !
2026-10-08 11:53:27 +03:00
Lauri Ojansivu cb69750b17 Let administrators discard List Sync operations that cannot be replayed.
A saved List Sync operation whose list was removed, recreated or
reconfigured, or whose actor lost full-list write access, could never be
replayed. The replay job refused it every minute with only a console line,
and because a new Sync of the list resumes the pending operation first,
that list could not Sync at all.

Such an operation is now marked once, with one Recovery event, and a
blocked manual Sync says why. Admin Panel / Problems / Recovery lists these
operations with what they applied and their live replayability. An
instance administrator can discard one only while the same checks a replay
makes still fail. The discard holds the list's Sync lease, records one
immutable decision, then removes the saved steps and the operation marker,
reading each removal back. It writes no card: changes already applied stay
and the rest are never written. A retry or a second administrator only
finishes the removal, and a replay that finds a decided discard completes it
instead of resuming. The intent stays as evidence and the discard is
audited in Recovery.

Unit, source-wiring and negative tests cover recording once, refusing
non-administrators and replayable operations, and discarding twice; an app
test and a Playwright case cover the Recovery page. New interface text is
in English, pending Transifex.

Thanks to xet7 !
2026-10-08 11:53:06 +03:00
Lauri Ojansivu ddf5a38f43 Record several releases per card in the changelog and remove it from TODO Later.
Thanks to xet7 !
2026-10-08 11:52:08 +03:00
Lauri Ojansivu 727ceedd01 Let a card be in more than one release.
A card had exactly one release, scrum.releaseId. Jira import kept only the
first fix version of an issue and reported the rest as lost, Jira export
wrote no fix versions at all, and the card UI could not show or pick more
than one release.

A card's releases are now a list, scrum.releaseIds, with releaseId kept as
its first entry so older servers, importers and REST readers still see one.
Existing cards are not migrated: every reader goes through cardReleaseIds(),
which reads the list and the old single field the same way, so cards saved
either way work side by side, and a card moves to the new form on its next
Scrum save. A save with releaseIds replaces the list; a save with only
releaseId, from an older client, replaces the first release and keeps the
others, and null clears them. Every release must belong to the board,
checked in one query; duplicates collapse and a releaseId that contradicts
the list is refused.

Copies and moves to another board link each release by name. The native
transfer writes one release exactly as before and adds releaseIds only for
cards with several, and remaps each of them on import. Jira import maps every
fix version and Jira export writes them all as fixVersions. Board View /
Sprints shows each release's cards and how many are done, counting a card in
each of its releases. Scrum History checks and guards every release, and
rule e-mails name them all. The Product Backlog and card details use a
multiple select.

tests/scrumMultipleReleases.test.cjs covers the rules and fails if any code
reads the single field directly; a Playwright spec drives the UI. New text is
in English, pending Transifex.

Thanks to xet7 !
2026-10-08 11:51:44 +03:00
Lauri Ojansivu cbf041397d Record Manx Sync translation progress
Thanks to xet7 !
2026-10-08 11:51:07 +03:00
Lauri Ojansivu 30013762c3 Translate Manx Sync conflicts and diagnostic reports
Fill 63 messages while preserving source/local distinctions, missing/null behavior, retention limits and non-resumable report semantics. Sync terminology and full grammar remain low-confidence pending fluent review.

Thanks to xet7 !
2026-10-08 11:50:35 +03:00
Lauri Ojansivu 0a80496acf Archive and restore lists through the REST API.
#2906 asked how to add and archive lists by script. Lists could be added,
edited and deleted through REST, but not archived or restored: the PUT edits
only live lists and has no archived field. POST
/api/boards/:boardId/lists/:listId/archive and .../unarchive now do what the
list menu does, for a user with write access to the board, answering 404
when the list is not in the expected state. docs/API/Lists.md shows both.

Thanks to stevekiss and xet7 !

Fixes #2906.
2026-10-08 11:50:10 +03:00
Lauri Ojansivu b2e1a8a544 Record the webhook, OAuth domain, team board, subtask and meta tag work in the changelog.
Thanks to xet7 !
2026-10-08 11:48:57 +03:00
Lauri Ojansivu fe60911408 Restrict OAuth login providers to email domains.
OAuth2/OIDC logins could be limited to email domains with
OAUTH2_ALLOWED_EMAIL_DOMAINS, but the Meteor login providers (Google,
GitHub, Facebook, X, Meteor Developer, Weibo, Meetup) let anyone with an
account there sign in. OAUTH_PROVIDERS_ALLOWED_EMAIL_DOMAINS, or the new
field under Admin Panel / People / OAuth login providers, restricts them
with the same rule and the same policy code. It is checked before an account
is created or merged, and on every later login, so narrowing the list also
applies to existing accounts. A provider that sends no email is refused while
a restriction is set, and a list that is not domain names is refused when
saved instead of locking everyone out.

Listed on every platform. New text is in English, pending Transifex.

Thanks to jakubgs and xet7 !

Fixes #1904.
2026-10-08 11:48:01 +03:00
Lauri Ojansivu 4ec94e58c6 Record Manx recovery translation progress
Thanks to xet7 !
2026-10-08 11:47:36 +03:00
Lauri Ojansivu 0691f5589d Translate Manx notification delivery and recovery messages
Fill 36 messages while retaining rejection duration, missing/changed activity, pause/cancel distinctions and non-recall warnings. Delivery terminology and complete grammar remain low-confidence pending fluent review.

Thanks to xet7 !
2026-10-08 11:46:52 +03:00
Lauri Ojansivu 08767f5c03 Record Manx Scrum translation progress
Thanks to xet7 !
2026-10-08 11:44:45 +03:00
Lauri Ojansivu 5f413c12eb Send names, the card link and the assigned person in outgoing webhooks.
An outgoing webhook carried mostly ids: the card title and the acting user,
but not the list, board or swimlane names, not the link, and nothing about
the person a join or an assignment was about. For an assignment the payload
said who assigned, but not who was assigned, which is what a chat
integration needs in order to tell that person.

The default payload now also has list, board, swimlane and url, member and
memberUsername for joining or leaving a card, and assigneeId, assignee and
assigneeUsername for joinAssignee/unjoinAssignee, which no param carried
before. The fields are additive, and WEBHOOKS_ATTRIBUTES still replaces the
default list wholesale. docs/Features/Webhooks/Webhook-data.md shows an
assignment payload.

Thanks to chrisi51 and xet7 !

Fixes #3297.
2026-10-08 11:44:40 +03:00
Lauri Ojansivu e54ad20004 Translate Manx Scrum planning labels and states
Fill 71 labels and short messages while preserving named variables, distinct sprint states and consistent navigation labels. Use the shared token scanner in the Manx regression suite. Scrum compounds and grammar remain low-confidence pending fluent review.

Thanks to xet7 !
2026-10-08 11:44:10 +03:00
Lauri Ojansivu d560e2fcf8 Make LDAP- and OIDC-synced team members members of the team's boards.
A user added to a team becomes a normal member of the boards that team is
assigned to (#4593, addUserToTeamBoards). The Admin Panel paths did that,
but the login providers' group syncs - LDAP_SYNC_TEAMS and OIDC groups with
PROPAGATE_OIDC_DATA - pushed the team onto the user with a plain $push. Such
users could see the team's boards but not work on them. Both now run the same
board sync after adding a team: LDAP imports it, and the wekan-oidc package,
which cannot import app code, reaches it through a global as it already
reaches authEnv.

tests/teamBoardsFromLoginSync.test.cjs pins both paths and fails if any code
pushes a team onto a user without the board sync; the OIDC concurrency test
now checks each concurrent login syncs only its own team's boards.

Thanks to xet7 !
2026-10-08 11:43:32 +03:00
Lauri Ojansivu 6c3db838ac Render the Admin Panel custom head meta tags, as meta elements only.
Admin Panel / Settings / Layout / Custom head meta tags was saved and
published, but the page's <head> only ever received the custom link tags and
the manifest, so description, Open Graph and other meta tags never reached a
browser or a link preview. They are written now, when custom head tags are
enabled. Only <meta> elements are taken from the text: a script, a style, a
second title or plain text is dropped, and so are http-equiv refresh and
set-cookie, which would redirect every visitor or write their cookies.

tests/customHeadMetaTags.test.cjs covers the tags kept and the ones refused;
a Playwright case fetches a served page.

Thanks to xet7 !
2026-10-08 11:41:59 +03:00
Lauri Ojansivu ef65306a2d Record additional import literal repairs
Thanks to xet7 !
2026-10-08 11:40:55 +03:00
Lauri Ojansivu 42c9a396e2 Land subtasks in the list the main board chose on its deposit board.
Board Settings / Subtasks shows the deposit board's lists since #3414 and
stores the chosen landing list on the main board, but addSubtaskCard read
only the deposit board's own setting, so the choice was ignored. When the
deposit board itself sends its subtasks to another board, that setting
names a list of a third board, and the subtask was written with the deposit
board's id and that third board's list.

models/lib/subtaskLandingList.js now decides: the main board's choice when
it is a live list of the deposit board, then the deposit board's own, and
only when the deposit board has none is its Queue list created and stored
as before. A choice that is gone, archived or on another board falls back
to the deposit board's first live list, never a foreign one. So several
boards that share one deposit board can each land their subtasks in a list
of their own, which is what the reporter wanted.

Thanks to TiibCD and xet7 !

Fixes #1781.
2026-10-08 11:40:25 +03:00
Lauri Ojansivu c1d3de6691 Restore Asana and OpenProject import literals
Repair 16 messages in 11 locale files and extend exact-literal checks to ten import formats. Accept grammatical affixes around commands and filenames without changing valid translated prose. Three relevant suites and 21 human-preference checks pass.

Thanks to xet7 !
2026-10-08 11:40:18 +03:00
Lauri Ojansivu 41454159ca Bring the import/export and History-chain TODO Later entries up to date.
The import/export entry lists what is left after the three new formats, and
the online History-chain switch moves to the maintainer-decision category,
with the choice it needs.

Thanks to xet7 !
2026-10-08 11:38:10 +03:00
Lauri Ojansivu 31b9d6058e Record cross-locale import literal repairs
Thanks to xet7 !
2026-10-08 11:37:38 +03:00
Lauri Ojansivu 84af75a9b1 Remove the receipt retention item from TODO Later.
The receipts are already the minimal id and outcome the decision asks for;
the test that pins it is in Upcoming.

Thanks to xet7 !
2026-10-08 11:37:11 +03:00
Lauri Ojansivu 416aedc706 Restore import example identifiers across locales
Repair 73 messages in 25 locale files. Preserve case-sensitive JSON fields and Jira API paths while retaining surrounding prose and localized quotation styles. Add catalog-wide positive and negative literal checks for four import formats. All 322 translation/i18n suites pass; broader prose translation remains in progress.

Thanks to xet7 !
2026-10-08 11:37:10 +03:00
Lauri Ojansivu c0c424657e Pin the receipt collections to the minimal form the retention decision asks for.
The 2026-09-30 retention decision compacts receipts and request IDs after 90
days "to a permanent minimal id and outcome". Stored Sync plans got a
compaction sweep because a plan carries its whole work. The five receipt
collections the decision also names (Scrum History completions and
requests, List Sync intents and completions, tray delivery receipts) were
left on the TODO list, but checked against the code they are already that
form when written: an id, the identity it proves and its outcome, with no
payload. Compacting them would save nothing and would break the exact-shape
checks their readers make.

tests/receiptRetentionShape.test.cjs builds each receipt with the real code
where it is pure, requires it to stay small and free of growing lists, and
pins the reader checks that refuse any extra field.

Thanks to xet7 !
2026-10-08 11:37:01 +03:00
Lauri Ojansivu a5f3d6176f Cut TODO Later down to what is still open.
Four blocks were dated development logs kept as history: "Carried to a
future release", "Active implementation checkpoint", "Scrum and Sync
resumed" and the #6701 snap login note. Checked against the code on
2026-10-08, almost all of what they called unfinished has since been built:
durable rule actions, Sync activation and replay, History linked at write,
Scrum worker serialization and recovery notices, the Jira Scrum import and
everything in their maintainer-decision lists. #6701 was closed on
2026-09-17 and its FerretDB fix is released.

They are replaced by one short "Carried to a future release" that lists
only the open work: nine items that can be built here, the ones that need
infrastructure or credentials, the backend properties that are not steps
left undone, and filing the #4790 split. The logs remain in git history.

Thanks to xet7 !
2026-10-08 11:34:07 +03:00
Lauri Ojansivu c152ded0b0 Record Bislama import instruction repairs
Thanks to xet7 !
2026-10-08 11:32:35 +03:00
Lauri Ojansivu e33df4be4c Repair Bislama import instructions and literal examples
Correct mixed-language instructions and translate OPML and Org mode help. Restore JSON field identifiers and the Jira API path; preserve format syntax and source variables. Full prose remains provisional pending fluent review.

Thanks to xet7 !
2026-10-08 11:32:01 +03:00
Lauri Ojansivu a3c7c112f5 Record the rule assignee actions, the merged-login fix and two guard fixes in the changelog.
Thanks to xet7 !
2026-10-08 11:31:59 +03:00
Lauri Ojansivu f735eba194 Rules: assignee actions, an optional title, and remove-all members that removes them.
#4294 asked for four things. Several triggers and actions, rule variables,
renaming in place and rule import/export were built earlier; two parts were
still missing.

- "I want the assignee to be the creator of the card by default": rules had
  member actions only. Add and Remove assignee actions take a username, a
  {creator}/{assignees}/{members} token, the user who triggered the rule, or
  "*" for every assignee, and write card.assignees. They are durable through
  Sync like the member actions, with the joinAssignee/unjoinAssignee
  activities the assignees hook writes.
- "give a Rule a default name": Add Rule with an empty title showed an
  error, and rules.createRule stored the literal "Rule". The title is now
  optional; the rule is named after its trigger and action ("When ...,
  then ..."), and the rules list renames it in place.

Fixing this found that "Remove all members from the card" iterated the
card's assignees while removing from its members: an earlier fix believed
assignMember wrote assignees, but both definitions in models/cards.js write
members. It removed only members who were also assignees. Remove-all now
iterates card.members, in the ordinary and the durable path, and the guards
that pinned the wrong collection now read models/cards.js to decide.

New text is added in English, pending Transifex.

Thanks to xeruf and xet7 !

Fixes #4294.
2026-10-08 11:31:12 +03:00
Lauri Ojansivu 531e432d26 Record Bislama wrapper cleanup and remaining audit scope
Thanks to xet7 !
2026-10-08 11:29:32 +03:00
Lauri Ojansivu 3c082ec6e5 Replace remaining Bislama artificial English wrappers
Correct 236 storage, migration, backup and report values. Preserve provider identifiers, units, simulation limits and uncertainty. The explicit wrapper queue is empty; other mixed-language values still need review. Statistical terminology and full technical prose remain low-confidence pending fluent review.

Thanks to xet7 !
2026-10-08 11:28:53 +03:00
Lauri Ojansivu a9dc8faa98 Count only writes in the card-field visibility writer guard.
server/moveBoardObjects.js reads cardFieldStates with a { cardFieldStates: 1 }
projection to drop hidden fields when objects move between boards. The guard
counted that read as a writer and failed. Projections are now excluded; the
publication is still pinned separately.

Thanks to xet7 !
2026-10-08 11:24:50 +03:00
Lauri Ojansivu 49353db813 Record the format specification links in the release risk baseline.
The Todoist, OPML and Org mode modules cite their specifications
(todoist.com, opml.org, orgmode.org) in comments; the release preflight
reported them as new URL origins. Only these files' entries are added.

Thanks to xet7 !
2026-10-08 11:24:16 +03:00
Lauri Ojansivu 9e2801714a Record Bislama search and report translation progress
Thanks to xet7 !
2026-10-08 11:24:08 +03:00
Lauri Ojansivu 29bca6f757 Keep a merged account on its boards.
Merging a login into an existing account (OIDC with
OAUTH2_MERGE_EXISTING_USERS, and the Meteor OAuth providers with their
merge setting) happens in Accounts.onCreateUser: the existing user is
removed and returned, and Meteor inserts it again under the same _id. That
removal ran Users.after.remove, which is the account-deletion cleanup: it
pulled the user out of every board's members and watchers and every card's
members and assignees, and deleted their avatars. A merged user came back
signed in with no boards. Both paths now remove through collection-hooks'
.direct, which skips the hooks.

tests/mergedUserKeepsBoards.test.cjs pins both paths and fails if any code
removes an account through the hooks and then returns it; the OIDC merge
test's stub now throws on a hooked removal.

Thanks to xet7 !
2026-10-08 11:23:40 +03:00
Lauri Ojansivu d332e18edb Correct Bislama search syntax and report translations
Replace 76 English wrappers and repair 12 search labels and messages. Verify localized queries with the production parser, including quoted values, absent fields, invalid limits and collision-free operators. Technical compounds remain provisional pending fluent review.

Thanks to xet7 !
2026-10-08 11:23:34 +03:00
Lauri Ojansivu ed5a3fc78c Record the Todoist, OPML and Org mode formats in the changelog.
Upcoming now opens with its new features, groups the login entries, and
keeps the translation entries in the translations subsection.

Thanks to xet7 !
2026-10-08 11:22:17 +03:00
Lauri Ojansivu 711303af32 Import and export boards as Org mode outlines.
Org mode files from Emacs, Orgzly, Beorg or organice import as boards:
level-1 headings become lists and level-2 headings cards. TODO keywords,
including those a #+TODO line declares, decide the done tag; [#A]
priorities become priority:A labels and :tags: labels. SCHEDULED, DEADLINE
and CLOSED become the start, due and end dates, and a :CREATED: property the
creation date. Body text becomes the description, and its - [ ] checkboxes
and childless level-3 headings the card's checklist; a level-3 heading with
children becomes a checklist of its own. #+TITLE names the board.
Timestamps carry no zone in Org and are read and written as UTC.
Repeaters, warning delays and text outside cards are reported in the loss
report. Export writes the same outline.

tests/orgMode.test.cjs covers custom keywords, localized day names, the
round trip and the negatives. A Playwright case imports a file through the
page, and the export audit fetches the new format. New instruction text is
in English, pending Transifex.

Thanks to xet7 !
2026-10-08 11:21:05 +03:00
Lauri Ojansivu 2fa6337c75 Record Bislama display and report translation progress
Thanks to xet7 !
2026-10-08 11:20:43 +03:00
Lauri Ojansivu 5e00df9be7 Correct Bislama display, weekday and report translations
Replace 76 artificial English wrappers while preserving template variables, named import counts, separators and font names. Search operators still need parser-aware review. Complete software compounds remain provisional pending fluent review.

Thanks to xet7 !
2026-10-08 11:20:02 +03:00
Lauri Ojansivu e6c8f85518 Import and export boards as OPML outlines.
OPML is what Workflowy, Dynalist, OmniOutliner, Logseq and most other
outliners export and import. Top-level outlines become lists and their
children cards, with the outliners' _note as the description and a completed
item (Workflowy's _complete, Dynalist's complete) as the done tag. Deeper
outlines become checklists with their descendants as items, and the head
title becomes the board title. A list's note and url/htmlUrl/xmlUrl links are
reported in the loss report. Export writes the same with Workflowy's
attribute names; line breaks and tabs in notes are written as character
references so that they survive the XML parser.

Like the Leo outline, the XML is parsed on the server only, with htmlparser2
in XML mode: DTDs and external entities are never resolved, and size and node
counts are bounded. tests/opmlOutline.test.cjs covers a Workflowy/Dynalist
file, the round trip and the negatives (an external entity, a node bomb). A
Playwright case imports an outline through the page, and the export audit
fetches the new format. New instruction text is in English, pending
Transifex.

Thanks to xet7 !
2026-10-08 11:18:14 +03:00
Lauri Ojansivu 3f09bbad13 Record Bislama navigation and rule translation progress
Thanks to xet7 !
2026-10-08 11:17:59 +03:00
Lauri Ojansivu d4607ee49c Correct Bislama navigation, diagnostics and rule labels
Replace 145 artificial English wrappers and translate the new Todoist import guidance. Preserve runtime identifiers, protocol names, time directions, check states and source variables. Complete software compounds remain provisional pending fluent review.

Thanks to xet7 !
2026-10-08 11:17:24 +03:00
Lauri Ojansivu 1eabb367e7 Import and export boards as Todoist project templates (CSV).
Todoist's Export as a template writes, and Import from template reads, a
CSV with TYPE, CONTENT, DESCRIPTION, PRIORITY, INDENT, AUTHOR, RESPONSIBLE,
DATE ... DEADLINE_LANG columns. Sections become lists, tasks become cards,
indented sub-tasks a Sub-tasks checklist, and notes comments on the task
above. @label words become labels and leave the title, PRIORITY 1-3 become
labels p1-p3 (1 is Todoist's highest; 4 is its "no priority"), RESPONSIBLE
becomes the owner, and DATE becomes the due date, or the start date when a
DEADLINE is the due date. Dates in words such as "every monday", DURATION,
orphan notes and sub-tasks and unknown row types are reported in the loss
report. Export writes the same columns in Todoist's order, with its
view_style=board meta row. Todoist exports no completed tasks, so finished
checklist items are left out.

tests/todoistCsv.test.cjs covers quoted fields, the round trip and the
negatives. A Playwright case imports a template through the page, and the
export audit fetches the new format. New instruction text is in English,
pending Transifex.

Thanks to xet7 !
2026-10-08 11:15:34 +03:00
Lauri Ojansivu 7e14c974bc Record Bislama wrapper correction progress
Thanks to xet7 !
2026-10-08 11:14:01 +03:00
Lauri Ojansivu e882ed8901 Replace 120 Bislama English wrappers with translated controls
Translate board, font, color, voting and filter labels. Restore numeric poker choices, date formats and compact sort labels without artificial prose prefixes. Fine color shades and software compounds remain provisional pending fluent review.

Thanks to xet7 !
2026-10-08 11:13:23 +03:00
Lauri Ojansivu cc68aef62a Wrap nine over-long CHANGELOG lines.
Whitespace only, from releases/changelog-wrap.mjs; tests/changelogFormat
passes again.

Thanks to xet7 !
2026-10-08 11:11:06 +03:00
Lauri Ojansivu d0ae7c73c1 Remove finished items from TODO Later.
#2713 is closed as implemented, and nothing waits on a security decision any
more, so their TODO Later entries go.

Thanks to xet7 !
2026-10-08 11:10:56 +03:00
Lauri Ojansivu 054c6e091d Close #2713 as already implemented: edit rules, and send a card by email.
A rule's trigger and actions are edited in place (rules.updateRule), and the
send-email rule action includes the card's content and live attachments,
with SMTP delivery tested against a local server. Live external mail
providers were not tested.

Thanks to kabi178 and xet7 !

Fixes #2713.
2026-10-08 11:10:26 +03:00
Lauri Ojansivu c32c967205 Record Bislama reported backlog completion and remaining language audit
Thanks to xet7 !
2026-10-08 11:09:48 +03:00
Lauri Ojansivu 49ed0b4202 Record automatic logout, secrets from files and the closed issues in the changelog.
The TODO Later block for the login settings is removed: all five items are
built.

Thanks to xet7 !
2026-10-08 11:09:03 +03:00
Lauri Ojansivu d787085c30 Translate remaining reported Bislama messages and short controls
Fill 224 workspace, Scrum, Sync and notification-recovery messages and ten short Blockly labels hidden by the fill heuristic. Preserve variable tokens, report limits, missing/null distinctions and irreversible cancellation wording. Complete software phrasing remains provisional pending fluent review; older seeded translations still need auditing.

Thanks to xet7 !
2026-10-08 11:08:09 +03:00
Lauri Ojansivu 71cd2aed05 Read MONGO_URL_FILE, MAIL_URL_FILE and S3_SECRET_KEY_FILE instead of three dead secret files.
MAIL_SERVICE_PASSWORD_FILE, MONGO_PASSWORD_FILE and S3_SECRET_FILE were
listed on every platform, and nothing read them. Per the maintainer decision
of 2026-10-08:

- MONGO_URL_FILE holds the whole MongoDB URL. Meteor connects before any
  WeKan code runs, so the Docker entrypoint, the snap's wekan-control,
  start-wekan.sh and start-wekan.bat read it and start WeKan with MONGO_URL
  set. An unreadable or empty file stops the start instead of falling back
  to a default database. The snap log now masks a password in MONGO_URL.
- MAIL_URL_FILE and S3_SECRET_KEY_FILE fill MAIL_URL and S3_SECRET_KEY first
  thing in server/main.js (models/lib/envSecretFiles.js). As for the login
  secrets, the variable wins over its file.
- The three dead names are gone from the Dockerfile, every docker-compose
  file, the snap settings and help, the start scripts and the docs. The
  example secret files are renamed to match, and WeKan logs a warning naming
  the replacement when a retired name is still set.

The issue itself was closed earlier, with only part of this done.

Thanks to xet7 !
2026-10-08 11:07:58 +03:00
Lauri Ojansivu c1d1a04790 Implement automatic logout with LOGOUT_WITH_TIMER and the LOGOUT_* settings.
The snap, Docker, docker-compose and start scripts have offered
LOGOUT_WITH_TIMER, LOGOUT_IN, LOGOUT_ON_HOURS and LOGOUT_ON_MINUTES since
2018, but the code that read them was removed with the job queue it ran on,
so they did nothing. Per the maintainer decision of 2026-10-08 they work
again. A login ends LOGOUT_IN days after it was made, or at
LOGOUT_ON_HOURS:LOGOUT_ON_MINUTES server time (the next such time, or on the
day LOGOUT_IN days later). Once a minute the server removes the expired
login tokens in one query, and Meteor signs out the browsers that used them.
The four settings are overridable in Admin Panel / People / Login like the
other login variables, with hours and minutes bounded. An unusable
combination signs nobody out and the server log says why.

tests/logoutTimer.test.cjs checks the one-query cutoff against the
per-login deadline over two years of logins in three time zones, two of them
with daylight saving time.

Thanks to xet7 !
2026-10-08 11:03:50 +03:00
Lauri Ojansivu 2b6f4efb49 Record Bislama function and text translation progress
Thanks to xet7 !
2026-10-08 11:03:11 +03:00
Lauri Ojansivu 74e4048f32 Translate Bislama functions, navigation and text operations
Fill 175 messages while preserving function results, angle units, screen-reader states, text-operation scope and numbered substitutions. Mathematical loanwords and full software phrases remain provisional pending fluent technical review.

Thanks to xet7 !
2026-10-08 11:02:41 +03:00
Lauri Ojansivu 68bdd70a51 Close #4403 as already implemented: Restrict the WeKan port to loopback.
`snap set wekan bind-ip=127.0.0.1` makes WeKan listen on loopback only
(snap-src/bin/config, snap-src/bin/wekan-control export BIND_IP, which
Meteor honours); the bundle and Docker take the BIND_IP environment
variable.

Thanks to galletl and xet7 !

Fixes #4403.
2026-10-08 11:00:47 +03:00
Lauri Ojansivu 9158772a61 Close #4250 as already implemented: Move a checklist from one card to another card.
The checklist menu has Move Checklist and Copy Checklist with a board /
swimlane / list / card picker (client/components/cards/checklists.jade,
checklists.js).

Thanks to qiutian00 and xet7 !

Fixes #4250.
2026-10-08 11:00:47 +03:00
Lauri Ojansivu d10fe621be Close #4223 as already implemented: Master dashboard like Kanboard's Bigboard plugin.
The Bigboard board view shows several boards on one page
(client/components/boards/bigboardView.*,
docs/Features/Board/Bigboard.md), and cards can be dragged between
boards in it.

Thanks to Jieiku and xet7 !

Fixes #4223.
2026-10-08 11:00:47 +03:00
Lauri Ojansivu 0a8c38f395 Close #2796 as already implemented: All Boards: drag and drop, and colour.
Boards on All Boards are reordered by drag and drop into each user's
own order (client/components/boards/boardsList.js,
models/lib/boardSortReorder.js, sortBoardsForUser), boards have colours
(boardChangeColorPopup), and the All Boards table view lists boards as
rows.

Thanks to compumatter and xet7 !

Fixes #2796.
2026-10-08 11:00:47 +03:00
Lauri Ojansivu 61ad1272bb Close #2489 as already implemented: Common WIP limit for several columns.
Board WIP limit groups share one limit across several lists
(wipLimitGroups in models/boards.js,
models/lib/wipLimitGroupDecision.js), set in the sidebar popup, and the
list headers highlight a group over its limit
(client/components/lists/listHeader.js).

Thanks to aviertio and xet7 !

Fixes #2489.
2026-10-08 11:00:46 +03:00
Lauri Ojansivu 948020275b Close #2211 as already implemented: Auth0: redirect to the full-screen login page instead of a popup.
Auth0 signs in through the OAuth2/OIDC service, whose login style can
be `redirect` (OAUTH2_LOGIN_STYLE, also in Admin Panel / People /
OAuth2); with OIDC_REDIRECTION_ENABLED=true the login page goes
straight to the provider (client/components/main/layouts.js,
server/models/settings.js).

Thanks to xet7 !

Fixes #2211.
2026-10-08 11:00:46 +03:00
Lauri Ojansivu dd410a3a8b Close #2148 as already implemented: Notification mail template.
Admin Panel / Settings / Email Templates customizes the subject and
body of invitation and activity emails, with {board} {card} {list}
{username} {url} {comment} {action} variables escaped on the server
(client/components/settings/settingBody.jade,
server/notifications/email.js).

Thanks to hingerlanton and xet7 !

Fixes #2148.
2026-10-08 11:00:46 +03:00
Lauri Ojansivu fcbd268d43 Close #2145 as already implemented: Mini date field.
A custom field of type date is shown on the minicard as a compact date
badge, its label can be hidden (showLabelOnMiniCard), and the date
formats include date-only variants that drop the time
(client/components/cards/cardDate.jade, client/lib/dateDisplay.js).

Thanks to gerroon and xet7 !

Fixes #2145.
2026-10-08 11:00:46 +03:00
Lauri Ojansivu 939aa8a95e Close #2043 as already implemented: Progress charts and work statistics for each board.
Board reports have ten charts - dashboard, burndown, burnup, cumulative
flow, control chart, cycle time, flow efficiency, lead time, throughput
and WIP run (client/components/boards/charts/,
server/lib/boardChartData.js) - with PDF and Excel export
(models/server/ExporterChartPDF.js, ExporterChartExcel.js).

Thanks to xet7 !

Fixes #2043.
2026-10-08 11:00:46 +03:00
Lauri Ojansivu 78899b673f Close #1990 as already implemented: Hide subtask boards on All Boards.
Subtask helper boards (titles wrapped in ^...^) are filtered out of All
Boards, the board publications, Bigboard and the multi-board calendar
by notHelperBoardTitle() (models/lib/helperBoards.js), tested by
tests/helperBoards.test.cjs.

Thanks to nmd3 and xet7 !

Fixes #1990.
2026-10-08 11:00:46 +03:00
Lauri Ojansivu 0f85328f15 Close #1921 as already implemented: Resend verification or change the verified flag.
An administrator can mark an email address verified in Admin Panel /
People / edit user (client/components/settings/peopleBody.jade,
setEmailVerified in server/models/users.js), and the verify-email and
resend-verification routes exist (config/accounts.js).

Thanks to lucg71 and xet7 !

Fixes #1921.
2026-10-08 11:00:46 +03:00
Lauri Ojansivu f94cb41c00 Close #1844 as already implemented: Smart search of cards.
Global Search (/global-search, config/router.js) matches free text in
titles, descriptions, comments, custom fields, checklists and
attachments (server/publications/cards.js), and finds archived cards
with `status:archived` / `status:all` (config/search-const.js).

Thanks to usmcamp0811 and xet7 !

Fixes #1844.
2026-10-08 11:00:46 +03:00
Lauri Ojansivu 1fbb9f4011 Close #1758 as already implemented: Use the EXIF orientation of uploaded pictures.
Thumbnails and covers are rotated from EXIF on the server with sharp's
`.rotate()` (server/lib/imageThumbnail.js, server/lib/imageGif.js).
Full-size originals are shown by the browser, which applies EXIF
orientation by default, and no WeKan CSS overrides `image-orientation`.

Thanks to CWempe and xet7 !

Fixes #1758.
2026-10-08 11:00:46 +03:00
Lauri Ojansivu f7879e95e6 Close #1704 as already implemented: Move list(s) to a different board.
The list menu has Move and Copy with a board picker
(client/components/lists/listHeader.jade, listHeader.js), and the
server move checks the right to change both boards
(server/models/lists.js, requireBoardMutation for source and
destination). Multi-Selection can move or copy several lists to another
board (client/components/sidebar/sidebarFilters.jade).

Thanks to h0jeZvgoxFepBQ2C and xet7 !

Fixes #1704.
2026-10-08 11:00:46 +03:00
Lauri Ojansivu 595f6aaed6 Close #1631 as already implemented: Receive notifications from other users only.
The person who made a change is never notified of it, except when they
@mention themselves: server/models/activities.js filters `user._id !==
userId` from the recipients. The requested behaviour is the fixed
default.

Thanks to gpelouze and xet7 !

Fixes #1631.
2026-10-08 11:00:46 +03:00
Lauri Ojansivu e27ca445d5 Close #793 as already implemented: Auto add user name to a moved card.
A rule does it: trigger "card moved to" a list
(client/components/rules/triggers/boardTriggers.jade) with the action
"add acting user as member"
(client/components/rules/actions/cardActions.jade,
models/lib/ruleActingUser.js, server/rulesHelper.js). The mover is
added to the card's members, shown on the minicard, and can be removed.

Thanks to xet7 !

Fixes #793.
2026-10-08 11:00:46 +03:00
Lauri Ojansivu 85c80d40fd Record the LDAP Sync now button in the changelog.
Thanks to xet7 !
2026-10-08 10:59:40 +03:00
Lauri Ojansivu 95147fc999 Add an LDAP Sync now button to Admin Panel / People / LDAP.
ldap_sync_now existed in packages/wekan-ldap/server/syncUser.js, but nothing
imported that file, so the method was never defined. It also imported every
directory user regardless of the sync settings. Per the maintainer decision of
2026-10-08 it is now loaded and wired to a Sync LDAP users now button. It runs
the background sync once with the settings in effect: import new users and/or
keep existing users updated, without needing the schedule to be on. Only an
active site administrator can run it, and with neither setting on it says
there is nothing to sync. The button and the scheduled job share one run at a
time, and a failed run reports the directory's error instead of success.

New text is added in English, pending Transifex.

Thanks to xet7 !
2026-10-08 10:59:21 +03:00
Lauri Ojansivu 0e060bb64c Record Bislama mathematics translation progress
Thanks to xet7 !
2026-10-08 10:58:46 +03:00
Lauri Ojansivu 5501cf17ae Translate Bislama list operations, logic and mathematics
Preserve numbered placeholders, LDAP identifiers, destructive-operation distinctions and random-number bounds. Statistics terminology and complete software phrases remain provisional pending fluent review.

Thanks to xet7 !
2026-10-08 10:58:01 +03:00
Lauri Ojansivu a68c9f8ccf Record translation progress under the actual Upcoming heading
Move misplaced translation entries out of Status, record the Bislama and login-notice batch, and guard against matching prose mentions of Upcoming. Changelog and release-summary tests pass.

Thanks to xet7 !
2026-10-08 10:49:39 +03:00
Lauri Ojansivu 59e67383ec Translate Bislama block controls and environment-only login guidance
Translate 116 Bislama Blockly labels and 62 read-only login notices while preserving source tokens and existing human prose. Extend state, operand, argument-order and read-only semantics checks. All 321 translation/i18n suites and 21 human-preference checks pass.

Bislama software compounds remain provisional. Browser and screen-reader sessions and the broader translation audit remain open.

Thanks to xet7 !
2026-10-08 10:47:13 +03:00
Lauri Ojansivu c149ed2d95 Record the LDAP Test connection fix in the changelog.
Thanks to xet7 !
2026-10-08 10:43:55 +03:00
Lauri Ojansivu 9d27f4be4d Make LDAP Test connection prove the directory answered without a service account.
ldapts opens its socket lazily. Test connection connected and then bound only
when LDAP_AUTHENTIFICATION was set, so without a service account no operation
ran and it reported success even for a host that does not resolve. Per the
maintainer decision of 2026-10-08 a test without credentials now does an
anonymous base-scope search of LDAP_BASEDN (no attributes, one entry at most)
and succeeds only when the directory answered; otherwise the directory's own
error is shown. Without a base DN it says nothing could be tested.

Thanks to xet7 !
2026-10-08 10:43:35 +03:00
Lauri Ojansivu 41bdab69de Record environment-only header login in the changelog.
Thanks to xet7 !
2026-10-08 10:42:25 +03:00
Lauri Ojansivu a7628504f1 Make header login settings environment-only.
With HEADER_LOGIN_ID and HEADER_LOGIN_TRUSTED_IPS set, a proxy at a trusted
address signs in as anyone it names. Since the 2026-10-05 Admin Panel work a
site administrator could switch that on from People / Header login, so a
stolen administrator session was enough to open every account. Per the
maintainer decision of 2026-10-08 the six HEADER_LOGIN_* settings now come
from the environment only: a value stored in the Admin Panel is ignored, the
pane shows the values in effect read-only with no Save button, and a save sent
by hand is refused and recorded in Admin Panel / Problems as ProxyBleed
(authn.header-login-env-only). Every other login section stays overridable.

New text is added in English only, pending Transifex.

Thanks to xet7 !
2026-10-08 10:41:59 +03:00
Lauri Ojansivu f7737da051 Record card-field and Bislama translation progress
Thanks to xet7 !
2026-10-08 10:40:07 +03:00
Lauri Ojansivu 58ce223329 Translate more card-field help and Bislama Blockly messages
Add 74 translations, repair three Bislama color labels and preserve exact numbered placeholders. Verify card-field scripts and rendering, Blockly ranges and control flow, and source-scoped native color terms. Relevant suites and human-preference checks pass.

Minority-language software wording remains provisional, especially Bislama and Wu Chinese; fluent-speaker review and browser checks remain open.

Thanks to xet7 !
2026-10-08 10:39:28 +03:00
Lauri Ojansivu cd87a0b465 Record maintainer decisions on the open login settings.
Automatic logout is implemented, header login stays environment-only, LDAP
Test connection without a service account does an anonymous base search,
ldap_sync_now gets a Sync now button, and the #5724 secret files become
MONGO_URL_FILE, MAIL_URL_FILE and S3_SECRET_KEY_FILE.

Thanks to xet7 !
2026-10-08 10:36:14 +03:00
Lauri Ojansivu 48a38103b7 Record six-locale keyboard translation progress
Thanks to xet7 !
2026-10-08 10:32:16 +03:00
Lauri Ojansivu 3ec8138130 Translate remaining keyboard labels in six locales
Fill 119 keyboard and card-field strings in Akan, Maori, Northern Sotho, Somali, Tok Pisin and Waray. Preserve physical key identities, navigation direction and source variables. All 31 relevant suites and 21 human-preference checks pass.

Assembled software wording remains provisional, especially Akan, Northern Sotho and Waray; fluent-speaker review and browser checks remain open.

Thanks to xet7 !
2026-10-08 10:31:38 +03:00
Lauri Ojansivu 9fc6b0accd Record accurate locale coverage reporting
Thanks to xet7 !
2026-10-08 10:26:36 +03:00
Lauri Ojansivu e503945ef1 Report locale coverage without claiming translation quality
Update the stale README metric to 182 of 234 locale catalogs above 90 percent non-English text. Keep the source-derived regression check and state that coverage does not establish fluency.

Thanks to xet7 !
2026-10-08 10:26:03 +03:00
Lauri Ojansivu e785873d14 Record additional card-field translation coverage
Thanks to xet7 !
2026-10-08 10:25:13 +03:00
Lauri Ojansivu 5ce7549bcf Translate card-field visibility in 22 more locale paths
Fill 44 values while retaining hiding scope, unchanged data and settings, restoration and board-specific field order. All 30 relevant suites pass, including full catalog token and rendering checks.

Breton, Faroese, Romansh, Sardinian, Scottish Gaelic and Sicilian wording is provisional and needs fluent-speaker review. Browser checks were not run.

Thanks to xet7 !
2026-10-08 10:24:19 +03:00
Lauri Ojansivu 0dcbdfb4d4 Record translation validation and remaining locale work
Thanks to xet7 !
2026-10-08 10:19:30 +03:00
Lauri Ojansivu 07a33f5d5f Validate translation placeholders and restore current locale catalogs
Reject damaged fill batches atomically, recognize non-Latin token names, and distinguish exact Blockly symbols from prose. Restore missing card-field keys, add 240 translations, and reconcile four superseded audit findings.

Validation: 320 translation/i18n suites, card-field rendering checks and 21 human-preference checks pass. Full translation completion and fluent-speaker review remain open.

Thanks to xet7 !
2026-10-08 10:18:09 +03:00
github-actions[bot] 4ca1544fa9 Bump versions for v12.23 2026-10-08 06:40:47 +00:00
Lauri Ojansivu dc75d10c3c Prepare v12.23 release 2026-10-08 09:38:46 +03:00
Lauri Ojansivu 31eee8fc2a Updates 2026-10-08 09:38:15 +03:00
Lauri Ojansivu 8bfd9f0a29 Document card-field visibility translations
Thanks to xet7 !
2026-10-08 09:35:27 +03:00
Lauri Ojansivu a30eaaf22f Translate card-field visibility in eight languages
Add 16 missing translations in Finnish, Swedish, German, French, Spanish, Portuguese, Italian and Dutch. Preserve source order and verify unchanged-data and per-board-order semantics.

Thanks to xet7 !
2026-10-08 09:34:49 +03:00
Lauri Ojansivu c87f3309b4 Document Moroccan Arabic short-label audit
Thanks to xet7 !
2026-10-08 09:32:54 +03:00
Lauri Ojansivu 486d622947 Translate Moroccan Arabic recovery and short labels
Fill 44 English placeholders including keyboard names and short Blockly labels. Check cancellation scope and retained key names. Technical wording remains low confidence pending speaker review; the broader dialect audit remains open.

Thanks to xet7 !
2026-10-08 09:32:15 +03:00
Lauri Ojansivu ccd80c2ddc Document Moroccan Arabic recovery translations
Thanks to xet7 !
2026-10-08 09:30:44 +03:00
Lauri Ojansivu 77a1829ce1 Translate Moroccan Arabic estimates and notification recovery
Fill 31 placeholders and check null handling, estimate fields, delivery uncertainty and recovery limits. Technical Darija wording remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 09:30:11 +03:00
Lauri Ojansivu 3f1b6753f5 Document Moroccan Arabic Sync report translations
Thanks to xet7 !
2026-10-08 09:28:42 +03:00
Lauri Ojansivu 86ac83f244 Translate Moroccan Arabic Sync previews and reports
Fill 31 placeholders and check retention, partial changes, access requirements and report limitations. Technical Darija wording remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 09:28:06 +03:00
Lauri Ojansivu 3015f97298 Document Moroccan Arabic Sync conflict translations
Thanks to xet7 !
2026-10-08 09:26:47 +03:00
Lauri Ojansivu 2e9243f3c8 Translate Moroccan Arabic observations and Sync conflicts
Fill 36 placeholders and check observation limits, no source writes, mapping-only removal and replacement reuse. Technical Darija wording remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 09:26:12 +03:00
Lauri Ojansivu c5385e3d01 multiselection-custom-color: reopen the color popup until it opens.
In the verification run the second click on the Multi-Selection color
button failed: the sidebar re-renders while the cards' new colors arrive,
and the button was "not stable" and then detached for 15 s. The spec now
clicks again, up to three times, until the popup's color wheel is visible.
It passed 3 of 3 alone.

Thanks to xet7 !
2026-10-08 09:26:01 +03:00
Lauri Ojansivu 7ab8026328 Document Moroccan Arabic sprint report translations
Thanks to xet7 !
2026-10-08 09:24:48 +03:00
Lauri Ojansivu 950d84771f Minicard date badges show their translated title, not "{{_ 'card-due'}}".
Since the date templates were merged (9c288c3d4d), the minicard received,
start, due, end and custom-field date badges passed their title as
showTitle="{{_ 'card-due'}}". Spacebars does not evaluate {{...}} inside an
inclusion argument, so the badge's title - its tooltip and accessible name -
read "{{_ 'card-due'}} week 41"; the #6750 browser spec's page snapshot
showed it. The badges now pass titleKey="card-due" and dateBadgeBody
translates it; the card-details badges keep their showTitle helpers.
tests/jadeInclusionArgsNoMustache.test.cjs fails on any +template(...)
argument string containing {{...}} in client/ or packages/, pins the five
badges, and recognises the broken form.

Thanks to xet7 !
2026-10-08 09:24:25 +03:00
Lauri Ojansivu 263d3ff9e6 Translate Moroccan Arabic sprint reports and events
Fill 36 placeholders and check estimate semantics, cancellation membership, partial reports and event distinctions. Technical Darija and Scrum terminology remain low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 09:23:51 +03:00
Lauri Ojansivu e7e2bed80f Shift shortcuts run once: re-dispatch only keys typed on a non-Latin layout.
The #6750 browser spec failed on Shift+D: the due date editor opened and
closed again. client/lib/keyboard.js re-dispatches a key typed on a
non-Latin layout as its Latin key code, and decided "another language" by
comparing the key with the key code's lowercase letter - which also matched
every SHIFTED Latin key ('D' is not 'd'), symbols such as '!' and the
Shift key itself. So each Shift shortcut fired twice; for Shift+D the copy
clicked the same due date control again and closed the popup. The Shift+1-9
filters were affected the same way.

It now re-dispatches only a single non-ASCII character whose key code is a
Latin letter or digit, and copies the Shift, Ctrl, Alt and Meta state, so
Shift shortcuts keep working on non-Latin layouts too.
tests/keyboardNonLatinRedispatch.test.cjs runs the real handler: shifted
Latin keys, symbols and modifiers are not duplicated; Cyrillic letters map
with and without Shift; it fails on the previous code.

Thanks to mimZD and xet7 !
2026-10-08 09:23:19 +03:00
Lauri Ojansivu 87b8c514d6 Document Moroccan Arabic sprint control translations
Thanks to xet7 !
2026-10-08 09:22:38 +03:00
Lauri Ojansivu b18b2531aa Translate Moroccan Arabic Scrum settings and sprint controls
Fill 31 placeholders and check completion policies, sprint actions, rollover scope and estimate sources. Technical Darija and Scrum terminology remain low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 09:22:00 +03:00
Lauri Ojansivu 96cbb2d581 Document Moroccan Arabic rule editor translations
Thanks to xet7 !
2026-10-08 09:20:46 +03:00
Lauri Ojansivu 21ccc81f98 Translations containing a literal %{...} show their text, not their key.
The browser suite failed "String Template help preserves literal examples"
in all 67 locales of every browser: the Custom Field String Template help
showed the raw key custom-field-stringtemplate-context-hint. Its text has
literal "%{card.title}" and "%{value|urlencode}", which the sprintf
post-processor cannot parse. __() caught that and retried with
`postProcess: false` - but i18next treats false as unset and runs the
global sprintf again, and the `sprintf` option the Blaze `_` helper always
passes triggers it as well, so the retry threw too and __() returned the
key. The retry now drops the positional sprintf option and passes an
explicit empty postProcess list, which really turns it off; real %s
formatting is unchanged.

tests/i18nLiteralPercentBraces.test.cjs runs the real __() with the
installed i18next: the help renders in English and all 246 locales when
called the Blaze way, %s still formats, and the old retry is shown to
return the raw key.

Thanks to xet7 !
2026-10-08 09:20:19 +03:00
Lauri Ojansivu 9dcb964c1a Translate Moroccan Arabic rule editor and Scrum labels
Fill 31 placeholders and check rule validation, permissions, conflict handling and Blockly aliases. Technical Darija and Scrum terminology remain low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 09:20:09 +03:00
Lauri Ojansivu 1a48978056 Document Moroccan Arabic workspace search translations
Thanks to xet7 !
2026-10-08 09:18:39 +03:00
Lauri Ojansivu e1368ff32f Translate Moroccan Arabic variables and workspace search
Fill 36 placeholders and check trimming, variable conflicts, composed counts and search shortcuts. Technical Darija wording remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 09:18:02 +03:00
Lauri Ojansivu 099755f8a2 Document Moroccan Arabic text translations
Thanks to xet7 !
2026-10-08 09:16:38 +03:00
Lauri Ojansivu 14c765311b Translate Moroccan Arabic text processing
Fill 42 placeholders and check positions, search arguments, replacement scope and prompt types. Technical Darija wording remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 09:16:00 +03:00
Lauri Ojansivu 3064f0767e Document Moroccan Arabic navigation translations
Thanks to xet7 !
2026-10-08 09:14:41 +03:00
Lauri Ojansivu 0c7b3f8d1a Translation tests check links by their parsed host, not as substrings.
GitHub CodeQL code scanning alerts #551, #552 and #553
(js/incomplete-url-substring-sanitization): tests/tatarBoardLayoutCorrections
and tests/tokPisinInterfaceTranslations checked translated text with
includes('https://trello.com/app-key') and includes('example.com'), which
also pass for https://evil.example/?x=https://trello.com/app-key or a host
such as example.com.evil.example. The Trello links are now parsed and must
have the host trello.com and the path /app-key, as a7ae58ea9f did for
#547; the example domain must be a whole word of the text.
tests/urlSubstringSanitization.test.cjs, which keeps this shape out of the
tree, flagged exactly these three lines and passes now. The other failing
tests in both files fail on the committed versions too: they list locale
keys that the new English strings are not yet translated into.

Thanks to GitHub CodeQL and xet7 !
2026-10-08 09:14:22 +03:00
Lauri Ojansivu 1c7ace6c4d Translate Moroccan Arabic navigation and letter case
Fill 37 placeholders and check navigation directions, text append roles and letter case. Technical Darija wording remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 09:14:09 +03:00
Lauri Ojansivu 9ba56f8ef9 Document Moroccan Arabic procedure translations
Thanks to xet7 !
2026-10-08 09:12:53 +03:00
Lauri Ojansivu 5b7f2d18d8 Translate Moroccan Arabic procedures and accessibility controls
Fill 39 placeholders and check return values, disabled functions, duplicate parameters and screen-reader toggle directions. Technical Darija wording remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 09:12:18 +03:00
Lauri Ojansivu dac88bb563 Document Moroccan Arabic function translations
Thanks to xet7 !
2026-10-08 09:10:59 +03:00
Lauri Ojansivu a788bb5a71 Translate Moroccan Arabic functions and workspace controls
Fill 43 placeholders and check rounding, logarithm bases, trigonometric units and variable types. Technical Darija wording remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 09:10:28 +03:00
Lauri Ojansivu 4035facebe Document Moroccan Arabic math translations
Thanks to xet7 !
2026-10-08 09:09:10 +03:00
Lauri Ojansivu 10be25a37d Translate Moroccan Arabic math and statistics
Fill 50 placeholders and check random bounds, number properties, operand roles, statistics and constant formulas. Technical Darija wording remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 09:08:40 +03:00
Lauri Ojansivu 62f52ae0f7 Document Moroccan Arabic logic translations
Thanks to xet7 !
2026-10-08 09:06:47 +03:00
Lauri Ojansivu f215e0c16b Translate Moroccan Arabic sorting and logic
Fill 46 placeholders and check comparisons, negation, branch labels and list operations. Technical Darija wording remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 09:06:14 +03:00
Lauri Ojansivu bb8a8ec8d9 Document Moroccan Arabic list translations
Thanks to xet7 !
2026-10-08 09:04:53 +03:00
Lauri Ojansivu f64e7e8e95 Translate Moroccan Arabic list retrieval and removal
Fill 42 placeholders and check item operations, copying, indexing and insertion bounds. Technical Darija wording remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 09:04:25 +03:00
Lauri Ojansivu ee2147862d Document Moroccan Arabic input translations
Thanks to xet7 !
2026-10-08 09:03:00 +03:00
Lauri Ojansivu 1a902b438d Translate Moroccan Arabic numeric inputs and keyboard hints
Fill 52 placeholders and check operands, bounds, coordinates, keyboard arguments and list indexing. Technical Darija wording remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 09:02:31 +03:00
Lauri Ojansivu e79f3d1577 Document Moroccan Arabic workspace translations
Thanks to xet7 !
2026-10-08 09:01:08 +03:00
Lauri Ojansivu b742f218f0 Translate Moroccan Arabic workspace and input labels
Fill 55 placeholders, including the short bitmap-on label. Check opposing actions, bitmap coordinates, deletion arguments and list positions. Technical Darija wording remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 09:00:34 +03:00
Lauri Ojansivu 91613bb2aa Document Moroccan Arabic loop translations
Thanks to xet7 !
2026-10-08 08:58:51 +03:00
Lauri Ojansivu 1c75103101 Translate Moroccan Arabic Blockly loops and conditions
Fill 24 placeholders and check loop actions, truth conditions, source tokens and catalog ordering. Technical Darija wording remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 08:58:20 +03:00
Lauri Ojansivu 0dc93dc873 Move unreleased translation entries to Upcoming
Place translation commits outside v12.22 when that release does not contain them. Preserve the entries and their validation records.

Thanks to xet7 !
2026-10-08 08:57:02 +03:00
Lauri Ojansivu f9221e73c3 Add Admin Panel card field visibility to the changelog.
Thanks to xet7 !
2026-10-08 08:54:29 +03:00
Lauri Ojansivu 03975e7d31 Admin Panel / Settings / Visibility / Features: choose which card fields every board shows.
Above the Features Save there is now one tick per field type of Board
Settings / Card - the same icons and names, all ticked by default. An
unticked field, for example Pomodoro, is not offered in any board's Board
Settings / Card and is not shown on any opened card or minicard, whatever
the board chose. Visibility only: no card data and no board setting is
written, so ticking it again shows every board as it was set. There is no
ordering here; the order stays each board's own.

The catalog and the decisions are models/lib/cardFieldVisibility.js. It is
stored as cardFieldStates on the settings document (missing means shown,
so existing installs are unchanged), published with the other Features
settings and saved by the site-admin-only saveInstanceFeatures method,
which refuses unknown field keys. The hiding happens at the client's one
board read (ReactiveCache.getBoard), which returns the board with the
hidden fields' flags reading false - the same object when nothing is
hidden - so every card and minicard template that reads a board flag agrees
without being edited; the per-card List title and the user's Labels text
override check the same mark. The server's reads, publications and the REST
API are unchanged. A whole-column tick in Board Settings / Card leaves the
hidden fields' board values alone.

Thanks to xet7 !
2026-10-08 08:54:04 +03:00
Lauri Ojansivu 3517ea0d20 Document Moroccan Arabic Blockly translations
Thanks to xet7 !
2026-10-08 08:52:53 +03:00
Lauri Ojansivu ec526a4d37 Translate Moroccan Arabic Blockly colours and controls
Technical wording remains low confidence pending speaker review. Repository-wide checks still require the new card-field-visibility keys in other locales.

Thanks to xet7 !
2026-10-08 08:52:11 +03:00
Lauri Ojansivu af7dd75cce 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-10-08 08:51:39 +03:00
Lauri Ojansivu b699cf6469 Merge remote-tracking branch 'origin/main' 2026-10-08 08:51:38 +03:00
Lauri Ojansivu 0b83fd4259 Document Turkmen short-label translations
Thanks to xet7 !
2026-10-08 08:50:30 +03:00
Lauri Ojansivu a2bec96008 Translate Turkmen short labels and keyboard names
Technical wording remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 08:49:55 +03:00
Lauri Ojansivu d8c40078e3 Document Turkmen notification recovery translations
Thanks to xet7 !
2026-10-08 08:48:40 +03:00
Lauri Ojansivu da438435d6 Translate Turkmen notification recovery controls
Remove the obsolete custom-colors-in-use translation after its source key was removed. Technical wording remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 08:48:08 +03:00
Lauri Ojansivu e16377014b Document Multi-Selection custom card colors.
Thanks to xet7 !
2026-10-08 08:47:56 +03:00
Lauri Ojansivu fdfa943212 Multi-Selection: set a custom card color on all selected cards.
One card's color popup already had a color wheel (#5514) that stores a
'#rrggbb' hex in card.color; Multi-Selection offered only the ready-made
palette, so a lighter shade that keeps card text readable had to be set card
by card. The Multi-Selection color popup now has the same color wheel and
writes the same field through the same Card.setColor and server allow rule.

Both color popups also show the custom colors already used on the board's
cards beside the palette, so a color picked once is one click away for the
next cards. No new storage and no new interface text.

Cards the user may not edit (Utils.canModifyCard, e.g. a linked card whose
source board is comment-only for them) are skipped, and one refused card no
longer stops the rest of the selection. The Cards schema validates color with
one shared rule (isAllowedCardColor: empty, a palette name or a strict
'#rrggbb'), so crafted values such as 'red;background:url(...)', '</style>'
or 'javascript:' are refused on the server and never offered as swatches.

Thanks to xet7 !
2026-10-08 08:47:18 +03:00
Lauri Ojansivu 92a09d85fc Document Turkmen diagnostic translations
Thanks to xet7 !
2026-10-08 08:46:24 +03:00
Lauri Ojansivu 2b5a9d95bf Translate Turkmen Sync diagnostics and mail failures
Technical wording remains low confidence pending speaker review. Repository-wide checks still require custom-colors-in-use in other locales.

Thanks to xet7 !
2026-10-08 08:45:28 +03:00
Lauri Ojansivu c2ad25d3bf Minicard comment preview gets its own line instead of being cut beside the assignees.
With Board Settings / Card / Comments "Show on Minicard" on, a card with
seven assignees showed its comment as "Någr…" next to the row of avatars.
The avatar rows are float: inline-end and the badges float: inline-start;
each .minicard-comment was overflow: hidden, a block formatting context,
which a float narrows to the width it leaves, and white-space: nowrap cut
the text there. The comments now clear both floats and start at full
width under them, and the text wraps, clamped to three lines with the
ellipsis at the end. Same in RTL, since clear: both is side-agnostic.

Tests: tests/minicardCommentsOnMinicard.test.cjs pins the clear, the wrap
and the clamp, and fails on any minicard comment rule that goes back to
nowrap, absolute positioning or a one-sided clear, or on avatar rows laid
out absolutely. tests/playwright/specs/minicard-comment-beside-assignees.e2e.js
reproduces the report with one comment and seven assignees and asserts no
avatar intersects the comment and the text is not clipped.

Thanks to xet7 !
2026-10-08 08:45:24 +03:00
Lauri Ojansivu 3fd886aee2 Document Turkmen Sync report translations
Thanks to xet7 !
2026-10-08 08:44:15 +03:00
Lauri Ojansivu dc45288dfd Translate Turkmen Sync previews and run reports
Technical wording remains low confidence pending speaker review. Repository-wide checks still require custom-colors-in-use in other locales.

Thanks to xet7 !
2026-10-08 08:43:49 +03:00
Lauri Ojansivu 58342dbc6f Document Turkmen Sync conflict translations
Thanks to xet7 !
2026-10-08 08:42:36 +03:00
Lauri Ojansivu e2ee5d94b7 Translate Turkmen Sync conflicts
Technical wording remains low confidence pending speaker review. Repository-wide checks still require custom-colors-in-use in other locales.

Thanks to xet7 !
2026-10-08 08:42:10 +03:00
Lauri Ojansivu 348467acde Document Turkmen daily observation translations
Thanks to xet7 !
2026-10-08 08:40:57 +03:00
Lauri Ojansivu 86d5255294 Translate Turkmen daily observations and import status
Technical wording remains low confidence pending speaker review. Repository-wide checks still require custom-colors-in-use in other locales.

Thanks to xet7 !
2026-10-08 08:40:32 +03:00
Lauri Ojansivu 86802bc8cf Document Turkmen sprint report translations
Thanks to xet7 !
2026-10-08 08:39:09 +03:00
Lauri Ojansivu d7de79a25f Translate Turkmen sprint events and reports
Technical wording remains low confidence pending speaker review. Repository-wide checks still require custom-colors-in-use in other locales.

Thanks to xet7 !
2026-10-08 08:38:41 +03:00
Lauri Ojansivu 2eb0d71f8d Document Turkmen Scrum planning translations
Thanks to xet7 !
2026-10-08 08:37:31 +03:00
Lauri Ojansivu c58ae5f9d5 Translate Turkmen Scrum planning
Technical wording remains low confidence pending speaker review. Repository-wide checks still require custom-colors-in-use in other locales.

Thanks to xet7 !
2026-10-08 08:37:08 +03:00
Lauri Ojansivu 6e89faa705 Document Turkmen workspace translations
Thanks to xet7 !
2026-10-08 08:35:59 +03:00
Lauri Ojansivu 9df0ea9aa9 Translate Turkmen workspace announcements and rule editor
Technical wording remains low confidence pending speaker review. Repository-wide checks still require custom-colors-in-use in other locales.

Thanks to xet7 !
2026-10-08 08:35:26 +03:00
Lauri Ojansivu ec1d9c2873 Document Turkmen text value translations
Thanks to xet7 !
2026-10-08 08:34:02 +03:00
Lauri Ojansivu c8ad1c5c33 Translate Turkmen text values and variable assignments
Technical wording remains low confidence pending speaker review. Repository-wide catalog checks still require the new custom-colors-in-use key in other locales.

Thanks to xet7 !
2026-10-08 08:33:37 +03:00
Lauri Ojansivu 93a4695a2a Document Turkmen text operation translations
Thanks to xet7 !
2026-10-08 08:32:23 +03:00
Lauri Ojansivu 2ccb33ae8c Translate Turkmen text operations and search
Technical wording remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 08:31:57 +03:00
Lauri Ojansivu 2f8b88ce45 Document Turkmen accessibility translations
Thanks to xet7 !
2026-10-08 08:30:50 +03:00
Lauri Ojansivu 2f718b75cf Translate Turkmen accessibility shortcuts
Technical wording remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 08:30:26 +03:00
Lauri Ojansivu e208f9db47 Document Turkmen function control translations
Thanks to xet7 !
2026-10-08 08:29:19 +03:00
Lauri Ojansivu cbaf5b65fe Translate Turkmen variables and function controls
Technical wording remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 08:28:57 +03:00
Lauri Ojansivu 6d3b96d9c2 Document Turkmen mathematical function translations
Thanks to xet7 !
2026-10-08 08:27:46 +03:00
Lauri Ojansivu be9ba700ef Translate Turkmen mathematical functions and workspace controls
Mathematical terminology remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 08:27:20 +03:00
Lauri Ojansivu cc555556ae Document Turkmen statistics translations
Thanks to xet7 !
2026-10-08 08:26:21 +03:00
Lauri Ojansivu 2cf0ad7e2e Translate Turkmen statistics and rounding
Mathematical terminology remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 08:25:59 +03:00
Lauri Ojansivu eabbc90ded Document Turkmen number property translations
Thanks to xet7 !
2026-10-08 08:25:00 +03:00
Lauri Ojansivu 31138d20fd Translate Turkmen arithmetic and number properties
Mathematical terminology remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 08:24:35 +03:00
Lauri Ojansivu 485b728401 Document Turkmen logic translations
Thanks to xet7 !
2026-10-08 08:23:23 +03:00
Lauri Ojansivu 45ae3addd6 Translate Turkmen comparisons and Boolean logic
Technical wording remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 08:23:02 +03:00
Lauri Ojansivu 12f155bad5 Document Turkmen list search translations
Thanks to xet7 !
2026-10-08 08:22:00 +03:00
Lauri Ojansivu 2c5cb11dc6 Translate Turkmen list search updates and sorting
Technical wording remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 08:21:30 +03:00
Lauri Ojansivu 3064981901 Document Turkmen list translations
Thanks to xet7 !
2026-10-08 08:20:22 +03:00
Lauri Ojansivu 599bc52e66 Translate Turkmen list retrieval and removal
Technical wording remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 08:20:01 +03:00
Lauri Ojansivu 056503389d Document Turkmen math input translations
Thanks to xet7 !
2026-10-08 08:18:50 +03:00
Lauri Ojansivu 24f9ccac4e Translate Turkmen math inputs and keyboard navigation
Technical wording remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 08:18:27 +03:00
Lauri Ojansivu 1fd6be311e Document Turkmen input label translations
Thanks to xet7 !
2026-10-08 08:17:25 +03:00
Lauri Ojansivu 8bd132753a Translate Turkmen editing and input labels
Technical wording remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 08:17:04 +03:00
Lauri Ojansivu fb750ccba7 Document Turkmen loop translations
Thanks to xet7 !
2026-10-08 08:15:58 +03:00
Lauri Ojansivu 4926db9432 Translate Turkmen loops conditions and editing
Technical wording remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 08:15:32 +03:00
Lauri Ojansivu 0430eb5329 Document Turkmen Blockly translations
Thanks to xet7 !
2026-10-08 08:14:16 +03:00
Lauri Ojansivu 21248c01f1 Translate Turkmen Blockly colours and controls
Technical wording remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 08:13:51 +03:00
Lauri Ojansivu 96931e9658 Document Konkani short-label translations
Thanks to xet7 !
2026-10-08 08:12:39 +03:00
Lauri Ojansivu f0282acf1e Translate Konkani short rule and board labels
Assembled sentence order remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 08:12:16 +03:00
Lauri Ojansivu 358f4cd9ba Document Konkani keyboard translation audit
Thanks to xet7 !
2026-10-08 08:11:15 +03:00
Lauri Ojansivu 9ae6dc8b6a Translate Konkani keyboard names and short Blockly labels
Technical wording remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 08:10:53 +03:00
Lauri Ojansivu a54c79e661 Document Konkani storage translation audit
Thanks to xet7 !
2026-10-08 08:09:53 +03:00
Lauri Ojansivu 71691a17bd Translate remaining Konkani storage and system labels
Technical wording remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 08:09:32 +03:00
Lauri Ojansivu 001b1f2291 Document Konkani notification recovery translations
Thanks to xet7 !
2026-10-08 08:08:24 +03:00
Lauri Ojansivu 510e137fad Translate Konkani notification recovery controls
Technical wording remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 08:08:02 +03:00
Lauri Ojansivu 75ef43e282 Document Konkani Sync report translations
Thanks to xet7 !
2026-10-08 08:06:52 +03:00
Lauri Ojansivu e1eea3f44d Translate Konkani Sync reports and mail diagnostics
Technical wording remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 08:06:28 +03:00
Lauri Ojansivu ede6382460 Document Konkani Sync conflict translations
Thanks to xet7 !
2026-10-08 08:05:14 +03:00
Lauri Ojansivu 19ec736e3c Translate Konkani Sync conflicts and previews
Technical wording remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 08:04:55 +03:00
Lauri Ojansivu 42a0cd6d19 Document Konkani sprint report translations
Thanks to xet7 !
2026-10-08 08:03:38 +03:00
Lauri Ojansivu c7cfd79d12 Translate Konkani sprint reports and daily observations
Technical wording remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 08:03:17 +03:00
Lauri Ojansivu d89aa81d09 Document Konkani Scrum planning translations
Thanks to xet7 !
2026-10-08 08:01:57 +03:00
Lauri Ojansivu a76cdf9ffd Translate Konkani Scrum planning and roles
Technical wording remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 08:01:35 +03:00
Lauri Ojansivu 7004382ed1 Document Konkani workspace and rule translations
Thanks to xet7 !
2026-10-08 08:00:26 +03:00
Lauri Ojansivu 4cb8d596b8 Translate Konkani workspace announcements and rule editor
Technical wording remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 08:00:03 +03:00
Lauri Ojansivu f00fb08672 Document Konkani text search translations
Thanks to xet7 !
2026-10-08 07:58:40 +03:00
Lauri Ojansivu 4d73ddf7f3 Translate Konkani text search and variable assignments
Technical wording remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 07:58:19 +03:00
Lauri Ojansivu 705ba46e8e Document Konkani accessibility and text translations
Thanks to xet7 !
2026-10-08 07:56:56 +03:00
Lauri Ojansivu 094a925dd4 Translate Konkani accessibility shortcuts and text controls
Technical wording remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 07:56:31 +03:00
Lauri Ojansivu 02c64ca9ba Document Konkani navigation and function translations
Thanks to xet7 !
2026-10-08 07:54:48 +03:00
Lauri Ojansivu fd29791d08 Translate Konkani workspace navigation and functions
Technical terminology remains low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 07:54:26 +03:00
Lauri Ojansivu e3e9000a47 Document Konkani mathematical function translations
Thanks to xet7 !
2026-10-08 07:51:34 +03:00
Lauri Ojansivu 294faaff6a Translate Konkani powers logarithms and trigonometry
Fill 29 placeholders, preserving bases, sign reversal and angle units. All 53 focused and 21 human-preference checks pass. Mathematical terminology remains low confidence pending speaker review; browser checks were not run.

Thanks to xet7 !
2026-10-08 07:51:13 +03:00
Lauri Ojansivu 3ecf9ba967 Document Konkani statistical translations
Thanks to xet7 !
2026-10-08 07:50:13 +03:00
Lauri Ojansivu 302f99524e Translate Konkani statistics and random-number bounds
Fill 24 placeholders, preserving aggregate distinctions, random bounds and rounding directions. All 52 focused and 21 human-preference checks pass. Statistical terminology remains low confidence pending speaker review; browser checks were not run.

Thanks to xet7 !
2026-10-08 07:49:48 +03:00
Lauri Ojansivu 298c05b17d Document Konkani number-property translations
Thanks to xet7 !
2026-10-08 07:48:48 +03:00
Lauri Ojansivu b7e59a922d Translate Konkani constants and number properties
Fill 21 English values, preserving constants, inclusive limits and remainder operands. All 51 focused and 21 human-preference checks pass. Mathematical terminology remains low confidence pending speaker review; browser checks were not run.

Thanks to xet7 !
2026-10-08 07:48:26 +03:00
Lauri Ojansivu 91a3daa150 Document Konkani arithmetic translations
Thanks to xet7 !
2026-10-08 07:47:29 +03:00
Lauri Ojansivu f1a4f2d7a1 Translate Konkani logic and arithmetic controls
Fill 22 English values, preserving Boolean conditions, coordinates and angle bounds. All 50 focused and 21 human-preference checks pass. Technical wording remains low confidence pending speaker review; browser checks were not run.

Thanks to xet7 !
2026-10-08 07:47:08 +03:00
Lauri Ojansivu 428effd994 Document Konkani comparison translations
Thanks to xet7 !
2026-10-08 07:46:08 +03:00
Lauri Ojansivu 7cdc7754dd Translate Konkani list conversion and comparisons
Fill 24 placeholders, preserving delimiter behavior, comparison boundaries and negation. All 49 focused and 21 human-preference checks pass. Technical wording remains low confidence pending speaker review; browser checks were not run.

Thanks to xet7 !
2026-10-08 07:45:46 +03:00
Lauri Ojansivu cc3614a4e2 Document Konkani list-search translations
Thanks to xet7 !
2026-10-08 07:44:44 +03:00
Lauri Ojansivu 92a962fbeb Translate Konkani list search editing and sorting
Fill 27 placeholders, preserving return tokens, repetition roles and copy behavior. All 48 focused and 21 human-preference checks pass. Technical wording remains low confidence pending speaker review; browser checks were not run.

Thanks to xet7 !
2026-10-08 07:44:24 +03:00
Lauri Ojansivu d93065f6b6 Document Konkani list retrieval translations
Thanks to xet7 !
2026-10-08 07:43:20 +03:00
Lauri Ojansivu 91240780fa Translate Konkani list retrieval and removal
Fill 23 English values, preserving removal semantics, index markers and copies. All 47 focused and 21 human-preference checks pass. Technical wording remains low confidence pending speaker review; browser checks were not run.

Thanks to xet7 !
2026-10-08 07:42:57 +03:00
Lauri Ojansivu 7b2bb4eed8 Document Konkani navigation translations
Thanks to xet7 !
2026-10-08 07:41:38 +03:00
Lauri Ojansivu ca54f57cbc Translate Konkani keyboard navigation and list creation
Fill 24 placeholders, preserving key roles, empty-list length and retrieval actions. All 46 focused and 21 human-preference checks pass. Technical wording remains low confidence pending speaker review; browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 07:41:18 +03:00
Lauri Ojansivu d3ba155cb1 Document Konkani mathematical input translations
Thanks to xet7 !
2026-10-08 07:40:17 +03:00
Lauri Ojansivu 46356156dd Translate Konkani mathematical and text input labels
Fill 32 placeholders, preserving axes, operand roles and indexed values. All 45 focused and 21 human-preference checks pass. Technical wording remains low confidence pending speaker review; browser checks were not run.

Thanks to xet7 !
2026-10-08 07:39:57 +03:00
Lauri Ojansivu 48feab4233 Document Konkani input translations
Thanks to xet7 !
2026-10-08 07:39:05 +03:00
Lauri Ojansivu c3f1c873f9 Translate Konkani input and accessibility labels
Fill 29 placeholders, preserving indexed fields, opposite actions and list positions. All 44 focused and 21 human-preference checks pass. Technical wording remains low confidence pending speaker review; browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 07:38:41 +03:00
Lauri Ojansivu b7804be1a0 Document Konkani editing translations
Thanks to xet7 !
2026-10-08 07:37:38 +03:00
Lauri Ojansivu 48aa6dca0a Translate Konkani block editing and bitmap controls
Fill 25 English values, preserving deletion roles, dimensions and opposite actions. All 43 focused and 21 human-preference checks pass. Technical wording remains low confidence pending speaker review; browser checks were not run.

Thanks to xet7 !
2026-10-08 07:37:18 +03:00
Lauri Ojansivu 0d2b412885 Document Konkani loop translations
Thanks to xet7 !
2026-10-08 07:36:18 +03:00
Lauri Ojansivu 938baddde2 Translate Konkani loops and conditional controls
Fill 27 English values, preserving loop roles, conditions and fallback branches. All 42 focused and 21 human-preference checks pass. Technical terminology remains low confidence pending speaker review; browser checks were not run.

Thanks to xet7 !
2026-10-08 07:35:57 +03:00
Lauri Ojansivu fe549fe586 Document Konkani Blockly translations
Thanks to xet7 !
2026-10-08 07:34:39 +03:00
Lauri Ojansivu a4f122cd67 Translate Konkani Blockly colours and block controls
Fill 20 placeholders, preserving colour bounds and variable/function roles. All 41 focused and 21 human-preference checks pass. Technical terminology remains low confidence pending speaker review; browser checks were not run.

Thanks to xet7 !
2026-10-08 07:34:18 +03:00
Lauri Ojansivu 34e5f5922a Document Maithili rule-fragment translations
Thanks to xet7 !
2026-10-08 07:33:20 +03:00
Lauri Ojansivu 3e9d44d729 Translate Maithili short rule and count fragments
Fill seven English-identical labels after inspecting rule and count contexts. All 73 focused and 21 human-preference checks pass. Assembled fragment wording remains low confidence pending speaker review; browser checks were not run.

Thanks to xet7 !
2026-10-08 07:32:56 +03:00
Lauri Ojansivu d447691e33 Document Maithili keyboard translations
Thanks to xet7 !
2026-10-08 07:32:00 +03:00
Lauri Ojansivu b634313d8d Translate Maithili keyboard announcements and short labels
Fill 21 English values, preserving indexed roles and menu symbols. All 72 focused and 21 human-preference checks pass. Keyboard transliterations remain low confidence pending speaker review; browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 07:31:39 +03:00
Lauri Ojansivu 55ae01783f Document Maithili storage translations
Thanks to xet7 !
2026-10-08 07:30:42 +03:00
Lauri Ojansivu 4e5f657126 Translate Maithili storage settings omitted by fill filter
Fill 29 English-identical values after verifying equality with English. Preserve providers, identifiers and endpoint examples. All 71 focused and 21 human-preference checks pass. Technical wording remains low confidence pending speaker review; browser checks were not run.

Thanks to xet7 !
2026-10-08 07:30:21 +03:00
Lauri Ojansivu 37d06181f8 Document Maithili notification recovery translations
Thanks to xet7 !
2026-10-08 07:29:25 +03:00
Lauri Ojansivu cda08470b8 Translate Maithili email failures and notification recovery
Fill 38 placeholders, preserving retry limitations, cancellation caveats and estimate rules. All 69 focused and 21 human-preference checks pass. Technical wording remains low confidence pending speaker review; browser checks were not run.

Thanks to xet7 !
2026-10-08 07:29:02 +03:00
Lauri Ojansivu 50a0ff01e8 Document Maithili sync-report translations
Thanks to xet7 !
2026-10-08 07:27:50 +03:00
Lauri Ojansivu 14b973de00 Translate Maithili sync reports and diagnostics
Fill 21 placeholders, preserving retention, permissions and partial-change caveats. All 67 focused and 21 human-preference checks pass. Technical wording remains low confidence pending speaker review; browser checks were not run.

Thanks to xet7 !
2026-10-08 07:27:30 +03:00
Lauri Ojansivu eade4e90c2 Document Maithili sync-preview translations
Thanks to xet7 !
2026-10-08 07:26:32 +03:00
Lauri Ojansivu ded9b5029f Translate Maithili sync preview and source fields
Fill 20 placeholders, preserving preview limits and omitted-data caveats. All 66 focused and 21 human-preference checks pass. Technical wording remains low confidence pending speaker review; browser checks were not run.

Thanks to xet7 !
2026-10-08 07:26:11 +03:00
Lauri Ojansivu a179fa4121 Document Maithili sync-conflict translations
Thanks to xet7 !
2026-10-08 07:25:23 +03:00
Lauri Ojansivu 581e6e773e Translate Maithili sync conflicts and import caveats
Fill 23 placeholders, preserving local data, subcard behavior and review scope. All 65 focused and 21 human-preference checks pass. Technical wording remains low confidence pending speaker review; browser checks were not run.

Thanks to xet7 !
2026-10-08 07:25:03 +03:00
Lauri Ojansivu 317700cb79 Document Maithili sprint-observation translations
Thanks to xet7 !
2026-10-08 07:23:54 +03:00
Lauri Ojansivu 9e8efbbc5f Translate Maithili sprint observations and partial reports
Fill 21 placeholders, preserving observation limits, UTC and report caveats. All 63 focused and 21 human-preference checks pass. Technical wording remains low confidence pending speaker review; browser checks were not run.

Thanks to xet7 !
2026-10-08 07:23:35 +03:00
Lauri Ojansivu fe5b15693b Document Maithili sprint-report translations
Thanks to xet7 !
2026-10-08 07:22:31 +03:00
Lauri Ojansivu 6efc6ccc1e Translate Maithili sprint reports and planning messages
Fill 30 placeholders, preserving units, report tokens and unknown-estimate distinctions. All 62 focused and 21 human-preference checks pass. Scrum terminology remains low confidence pending speaker review; browser checks were not run.

Thanks to xet7 !
2026-10-08 07:22:11 +03:00
Lauri Ojansivu 7fb89469d1 Document Maithili Scrum translations
Thanks to xet7 !
2026-10-08 07:21:10 +03:00
Lauri Ojansivu e52c9e9e0d Translate Maithili Scrum settings and sprint labels
Fill 30 placeholders, preserving policy and sprint-action distinctions. All 61 focused and 21 human-preference checks pass. Scrum terminology remains low confidence pending speaker review; browser checks were not run.

Thanks to xet7 !
2026-10-08 07:20:50 +03:00
Lauri Ojansivu 2be5205f12 Document Maithili rule-editor translations
Thanks to xet7 !
2026-10-08 07:19:50 +03:00
Lauri Ojansivu dc9b4d7211 Translate Maithili Blockly aliases and rule editor
Fill 25 English values, preserving validation, permissions and alias consistency. All 59 focused and 21 human-preference checks pass. Technical wording remains low confidence pending speaker review; browser checks were not run.

Thanks to xet7 !
2026-10-08 07:19:26 +03:00
Lauri Ojansivu 0a1b627212 Document Maithili workspace translations
Thanks to xet7 !
2026-10-08 07:18:18 +03:00
Lauri Ojansivu e68355df82 Translate Maithili variable warnings and workspace search
Fill 28 English values, preserving variable roles, counts and search keys. All 58 focused and 21 human-preference checks pass. Technical wording remains low confidence pending speaker review; browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 07:17:54 +03:00
Lauri Ojansivu 32408d17cb Document Maithili text-processing translations
Thanks to xet7 !
2026-10-08 07:16:52 +03:00
Lauri Ojansivu 2b01fd9903 Translate Maithili text processing and input prompts
Fill 25 placeholders, preserving replacement roles and whitespace handling. All 57 focused and 21 human-preference checks pass. Technical wording remains low confidence pending speaker review; browser checks were not run.

Thanks to xet7 !
2026-10-08 07:16:32 +03:00
Lauri Ojansivu d531adf3a9 Document Maithili text-search translations
Thanks to xet7 !
2026-10-08 07:15:38 +03:00
Lauri Ojansivu 462e4eaf19 Translate Maithili substrings and text search
Fill 24 placeholders, preserving search/count roles and missing-match tokens. All 56 focused and 21 human-preference checks pass. Technical wording remains low confidence pending speaker review; browser checks were not run.

Thanks to xet7 !
2026-10-08 07:15:20 +03:00
Lauri Ojansivu b55350e4bb Document Maithili text-case translations
Thanks to xet7 !
2026-10-08 07:14:26 +03:00
Lauri Ojansivu 03f4c03a10 Translate Maithili shortcuts and text case
Fill 24 placeholders, preserving append roles, character indices and letter-case distinctions. All 55 focused and 21 human-preference checks pass. Technical wording remains low confidence pending speaker review; browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 07:14:03 +03:00
Lauri Ojansivu 244fa888bf Document Maithili screen-reader translations
Thanks to xet7 !
2026-10-08 07:13:13 +03:00
Lauri Ojansivu cbed22e4e8 Translate Maithili screen-reader and shortcut labels
Fill 29 placeholders, preserving toggle tokens and navigation distinctions. All 54 focused and 21 human-preference checks pass. Technical wording remains low confidence pending speaker review; browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 07:12:52 +03:00
Lauri Ojansivu 478c72ba59 Document Maithili procedure translations
Thanks to xet7 !
2026-10-08 07:11:59 +03:00
Lauri Ojansivu cfa8ed7ba7 Translate Maithili procedure and rename controls
Fill 25 English values, preserving function tokens, output distinctions and warnings. All 53 focused and 21 human-preference checks pass. Technical wording remains low confidence pending speaker review; browser checks were not run.

Thanks to xet7 !
2026-10-08 07:11:34 +03:00
Lauri Ojansivu 1634943b36 Document Maithili trigonometry translations
Thanks to xet7 !
2026-10-08 07:10:26 +03:00
Lauri Ojansivu d0883d9463 Translate Maithili trigonometry and variable controls
Fill 26 placeholders, preserving angle units, variable types and parent tokens. All 52 focused and 21 human-preference checks pass. Technical terminology remains low confidence pending speaker review; browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 07:10:05 +03:00
Lauri Ojansivu a66e054a8d Document Maithili rounding translations
Thanks to xet7 !
2026-10-08 07:09:10 +03:00
Lauri Ojansivu 63c0ad6116 Translate Maithili rounding and random numbers
Fill 24 placeholders, preserving random bounds, bases and rounding directions. All 51 focused and 21 human-preference checks pass. Mathematical terminology remains low confidence pending speaker review; browser checks were not run.

Thanks to xet7 !
2026-10-08 07:08:50 +03:00
Lauri Ojansivu 7cc7f126a5 Document Maithili statistical translations
Thanks to xet7 !
2026-10-08 07:08:00 +03:00
Lauri Ojansivu 5147884534 Translate Maithili statistical functions
Fill 24 placeholders, preserving remainder operands and statistical distinctions. All 50 focused and 21 human-preference checks pass. Statistical terminology remains low confidence pending speaker review; browser checks were not run.

Thanks to xet7 !
2026-10-08 07:07:38 +03:00
Lauri Ojansivu 75cc7395af Document Maithili arithmetic translations
Thanks to xet7 !
2026-10-08 07:06:48 +03:00
Lauri Ojansivu 3f3d5e7bb7 Translate Maithili arithmetic and number properties
Fill 28 English values, preserving constants, coordinates and inclusive limits. All 49 focused and 21 human-preference checks pass. Technical wording remains low confidence pending speaker review; browser checks were not run.

Thanks to xet7 !
2026-10-08 07:06:22 +03:00
Lauri Ojansivu 016f670a94 Document Maithili logic translations
Thanks to xet7 !
2026-10-08 07:05:21 +03:00
Lauri Ojansivu ed7d6dd234 Translate Maithili logic controls and comparisons
Fill 25 English values, preserving inclusive comparisons, Boolean distinctions and tokens. All 48 focused and 21 human-preference checks pass. Technical wording remains low confidence pending speaker review; browser checks were not run.

Thanks to xet7 !
2026-10-08 07:05:02 +03:00
Lauri Ojansivu 3bcbbe2dae Document Maithili list sorting translations
Thanks to xet7 !
2026-10-08 07:04:05 +03:00
Lauri Ojansivu d02a70b511 Translate Maithili list editing and sorting
Fill 28 English values, preserving item/count tokens, copy semantics and sorting distinctions. All 47 focused and 21 human-preference checks pass. Technical wording remains low confidence pending speaker review; browser checks were not run.

Thanks to xet7 !
2026-10-08 07:03:47 +03:00
Lauri Ojansivu 0eeeaec173 Document Maithili list operation translations
Thanks to xet7 !
2026-10-08 07:02:48 +03:00
Lauri Ojansivu a3ceff24c2 Translate Maithili list removal and indexing
Fill 26 English values, preserving index markers, return tokens and removal semantics. All 46 focused and 21 human-preference checks pass. Technical wording remains low confidence pending speaker review; browser checks were not run.

Thanks to xet7 !
2026-10-08 07:02:30 +03:00
Lauri Ojansivu bfe3565033 Document Maithili list navigation translations
Thanks to xet7 !
2026-10-08 07:01:28 +03:00
Lauri Ojansivu 16e62e04bb Translate Maithili list creation and keyboard navigation
Fill 29 placeholders, preserving key roles, empty-list length and list actions. All 45 focused and 21 human-preference checks pass. Technical wording remains low confidence pending speaker review; browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 07:01:08 +03:00
Lauri Ojansivu 7f361e3c00 Document Maithili mathematical input translations
Thanks to xet7 !
2026-10-08 07:00:11 +03:00
Lauri Ojansivu bd3133c6fd Translate Maithili mathematical and text input labels
Fill 32 placeholders while preserving axes, operand roles and indexed tokens. All 44 focused and 21 human-preference checks pass. Technical wording remains low confidence pending speaker review; browser checks were not run.

Thanks to xet7 !
2026-10-08 06:59:52 +03:00
Lauri Ojansivu 43ae0c6ec6 Document Maithili input and accessibility translations
Thanks to xet7 !
2026-10-08 06:59:03 +03:00
Lauri Ojansivu 32c0635c52 Translate Maithili input and accessibility labels
Fill 34 English values, preserving coordinate roles and opposite actions. All 43 focused and 21 human-preference checks pass. Technical wording remains low confidence pending speaker review; browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 06:58:43 +03:00
Lauri Ojansivu 60d6cdda8d Document Maithili condition and editing translations
Thanks to xet7 !
2026-10-08 06:57:51 +03:00
Lauri Ojansivu 46ea73e57b Translate Maithili conditions and block editing
Fill 36 English values, preserving branch meanings, loop conditions and deletion tokens. All 42 focused and 21 human-preference checks pass. Technical wording remains low confidence pending speaker review; browser checks were not run.

Thanks to xet7 !
2026-10-08 06:57:32 +03:00
Lauri Ojansivu 761e2c4857 Document Maithili Blockly translations
Thanks to xet7 !
2026-10-08 06:56:24 +03:00
Lauri Ojansivu fe596305d1 Translate Maithili Blockly colours and loop controls
Fill 31 placeholders, preserving limits and indexed tokens. All 41 focused and 21 human-preference checks pass. Technical terminology remains low confidence pending speaker review; browser checks were not run.

Thanks to xet7 !
2026-10-08 06:56:03 +03:00
Lauri Ojansivu c4dda6cb4d Document Odia short-label translations
Thanks to xet7 !
2026-10-08 06:54:46 +03:00
Lauri Ojansivu 28b48d42d2 Translate Odia short labels and keyboard announcements
Fill 37 English values, including 19 short labels excluded by the missing-string filter. Preserve existing translations and technical identifiers. All 74 focused and 21 human-preference checks pass. Technical wording remains low confidence pending speaker review; browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 06:54:27 +03:00
Lauri Ojansivu a596dab2e5 Document Odia notification recovery translations
Thanks to xet7 !
2026-10-08 06:53:16 +03:00
Lauri Ojansivu 863ccf49f3 Translate Odia email failures and notification recovery
Fill 36 placeholders with regression coverage for tokens, script, cancellation and retry caveats. All 73 focused and 21 human-preference checks pass. Technical wording remains low confidence pending speaker review; browser checks were not run.

Thanks to xet7 !
2026-10-08 06:52:58 +03:00
Lauri Ojansivu cf734dc582 Document Odia sync diagnostic translations
Thanks to xet7 !
2026-10-08 06:51:00 +03:00
Lauri Ojansivu 13e2c96b79 Translate Odia sync diagnostics and estimate mapping
Fill 12 placeholders while preserving access requirements, retention, units and missing-versus-null behavior. Technical wording remains low confidence pending speaker review. All 71 focused and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 06:50:38 +03:00
Lauri Ojansivu 9877ebdd22 Document Odia sync report translation progress
Thanks to xet7 !
2026-10-08 06:47:10 +03:00
Lauri Ojansivu 630cd35b4b Translate Odia source omissions and sync reports
Fill 18 English placeholders while preserving report limits, hidden values and partial-change caveats. Technical wording remains low confidence pending speaker review. All 70 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 06:46:47 +03:00
Lauri Ojansivu f2a5dffa0d Document Odia sync preview translation progress
Thanks to xet7 !
2026-10-08 06:45:34 +03:00
Lauri Ojansivu cd9c4a3e3b Translate Odia sync preview guidance
Fill 18 English placeholders while preserving replacement reuse, preview limits and parser omission caveats. Technical wording remains low confidence pending speaker review. All 69 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 06:45:14 +03:00
Lauri Ojansivu 6ca70c7bc3 Document Odia sync conflict translation progress
Thanks to xet7 !
2026-10-08 06:44:07 +03:00
Lauri Ojansivu 1039789a4b Translate Odia sync conflict guidance
Fill 16 English placeholders while preserving source-write restrictions, review scope and local data retention. Technical wording remains low confidence pending speaker review. All 68 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 06:43:46 +03:00
Lauri Ojansivu efd96e4ca7 Document Odia sprint observation translation progress
Thanks to xet7 !
2026-10-08 06:42:24 +03:00
Lauri Ojansivu c1fc0c0fd2 Translate Odia sprint lifecycle and observations
Fill 25 English placeholders while preserving partial-report restrictions, UTC days, observation limits and unknown estimates. Technical wording remains low confidence pending speaker review. All 66 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 06:42:05 +03:00
Lauri Ojansivu 19363ad3f4 Document Odia sprint report translation progress
Thanks to xet7 !
2026-10-08 06:40:36 +03:00
Lauri Ojansivu 843339b916 Translate Odia sprint reports and events
Fill 30 English placeholders while preserving report counts, minute units and unknown-estimate caveats. Technical wording remains low confidence pending speaker review. All 65 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 06:40:13 +03:00
Lauri Ojansivu d3978e437d Document Odia Scrum planning translation progress
Thanks to xet7 !
2026-10-08 06:39:05 +03:00
Lauri Ojansivu 5c5bf6c141 Translate Odia Scrum planning settings
Fill 28 English placeholders while preserving role, policy and sprint action distinctions. Technical wording remains low confidence pending speaker review. All 64 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 06:38:41 +03:00
Lauri Ojansivu f6cd86021a Document Odia rule editor translation progress
Thanks to xet7 !
2026-10-08 06:37:30 +03:00
Lauri Ojansivu 5b693f3276 Translate Odia rule editor guidance
Fill nine English placeholders while preserving validation, administrator permissions and conflict recovery guidance. Technical wording remains low confidence pending speaker review. All 62 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 06:37:08 +03:00
Lauri Ojansivu fdb7b83321 Document Odia workspace search translation progress
Thanks to xet7 !
2026-10-08 06:36:04 +03:00
Lauri Ojansivu 10a806b11a Translate Odia workspace search and shared block labels
Fill 21 English placeholders while preserving keyboard shortcuts, match counts and shared label consistency. Technical wording remains low confidence pending speaker review. All 61 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 06:35:42 +03:00
Lauri Ojansivu b6fd766740 Document Odia workspace count translation progress
Thanks to xet7 !
2026-10-08 06:34:38 +03:00
Lauri Ojansivu 49412e659e Translate Odia variable warnings and workspace counts
Fill 19 English placeholders while preserving variable references, count distinctions and comment suffix spacing. Technical wording remains low confidence pending speaker review. All 60 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 06:34:17 +03:00
Lauri Ojansivu dfb080ce9a Document Odia text processing translation progress
Thanks to xet7 !
2026-10-08 06:33:12 +03:00
Lauri Ojansivu bfd3deea1f Translate Odia text replacement and whitespace messages
Fill 21 English placeholders while preserving replacement operand roles, all-occurrence semantics and whitespace behavior. Technical wording remains low confidence pending speaker review. All 59 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 06:32:48 +03:00
Lauri Ojansivu 0ac1b82895 Document Odia substring translation progress
Thanks to xet7 !
2026-10-08 06:31:41 +03:00
Lauri Ojansivu cce628eb04 Translate Odia text joining and substring messages
Fill 22 English placeholders while preserving position markers, search operand roles and not-found return values. Technical wording remains low confidence pending speaker review. All 58 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 06:31:21 +03:00
Lauri Ojansivu 1bdadd1291 Document Odia text case translation progress
Thanks to xet7 !
2026-10-08 06:30:16 +03:00
Lauri Ojansivu cf51fdde3b Translate Odia text case and character selection
Fill 20 English placeholders while preserving letter case, copy semantics and indexed operand roles. Technical wording remains low confidence pending speaker review. All 57 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 06:29:54 +03:00
Lauri Ojansivu 157fa6d301 Document Odia directional shortcut translation progress
Thanks to xet7 !
2026-10-08 06:28:54 +03:00
Lauri Ojansivu a1c1b3c713 Translate Odia directional keyboard shortcuts
Fill 22 English placeholders while preserving directional consistency and distinct movement and scrolling actions. Technical wording remains low confidence pending speaker review. All 56 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 06:28:32 +03:00
Lauri Ojansivu f08712ff94 Document Odia screen-reader translation progress
Thanks to xet7 !
2026-10-08 06:27:31 +03:00
Lauri Ojansivu 0e8feb9aab Translate Odia screen-reader and shortcut commands
Fill 21 English placeholders while preserving variable references, screen-reader states and opposite toggle actions. Technical wording remains low confidence pending speaker review. All 55 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 06:27:06 +03:00
Lauri Ojansivu 9a4dc00959 Document Odia procedure translation progress
Thanks to xet7 !
2026-10-08 06:26:01 +03:00
Lauri Ojansivu d3f1bb708b Translate Odia procedure definitions and calls
Fill 22 English placeholders while preserving function names, output distinctions and procedure restrictions. Technical wording remains low confidence pending speaker review. All 54 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 06:25:38 +03:00
Lauri Ojansivu 4955c34198 Document Odia trigonometry translation progress
Thanks to xet7 !
2026-10-08 06:24:30 +03:00
Lauri Ojansivu bbe0210801 Translate Odia trigonometry and variable creation
Fill 22 English placeholders while preserving angle units, function abbreviations and variable types. Technical wording remains low confidence pending speaker review. All 53 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 06:24:09 +03:00
Lauri Ojansivu e25ffd55c3 Document Odia rounding translation progress
Thanks to xet7 !
2026-10-08 06:23:04 +03:00
Lauri Ojansivu 6c99169451 Translate Odia rounding and mathematical functions
Fill 23 English placeholders while preserving inclusive bounds, rounding directions and function bases. Technical wording remains low confidence pending speaker review. All 52 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 06:22:39 +03:00
Lauri Ojansivu 02023df9ee Document Odia statistics translation progress
Thanks to xet7 !
2026-10-08 06:21:27 +03:00
Lauri Ojansivu 23319c3f96 Translate Odia statistics and random fraction messages
Fill 21 English placeholders while preserving distinct statistical operations, list-valued modes and random bounds. Statistical wording remains low confidence pending speaker review. All 51 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 06:21:05 +03:00
Lauri Ojansivu 6a1322842f Document Odia number property translation progress
Thanks to xet7 !
2026-10-08 06:20:02 +03:00
Lauri Ojansivu 760a3877c1 Translate Odia constants and number properties
Fill 22 English placeholders while preserving constant examples, numeric symbols and inclusive bounds. Technical wording remains low confidence pending speaker review. All 50 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 06:19:42 +03:00
Lauri Ojansivu f25f5b8330 Document Odia logic translation progress
Thanks to xet7 !
2026-10-08 06:18:33 +03:00
Lauri Ojansivu b87568cd43 Translate Odia Boolean logic and arithmetic messages
Fill 21 English placeholders while preserving Boolean distinctions, conditional labels and numeric domains. Technical wording remains low confidence pending speaker review. All 49 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 06:18:11 +03:00
Lauri Ojansivu bfab913aed Document Odia sorting translation progress
Thanks to xet7 !
2026-10-08 06:17:07 +03:00
Lauri Ojansivu 2c12e8cb45 Translate Odia sorting and comparison messages
Fill 25 English placeholders while preserving sort direction, copy semantics and inclusive comparisons. Technical wording remains low confidence pending speaker review. All 48 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 06:16:48 +03:00
Lauri Ojansivu 7cf7e32a46 Document Odia list search translation progress
Thanks to xet7 !
2026-10-08 06:15:37 +03:00
Lauri Ojansivu 9309fddbf6 Translate Odia list search and insertion messages
Fill 22 English placeholders while preserving not-found values, copy semantics and distinct insertion and replacement actions. Technical wording remains low confidence pending speaker review. All 47 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 06:15:16 +03:00
Lauri Ojansivu 4adc01cf37 Document Odia list item translation progress
Thanks to xet7 !
2026-10-08 06:14:10 +03:00
Lauri Ojansivu 9fd3f94aba Translate Odia list retrieval and removal tooltips
Fill 20 English placeholders while preserving index markers and distinct return and removal effects. Technical wording remains low confidence pending speaker review. All 46 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 06:13:47 +03:00
Lauri Ojansivu ec02e6212a Document Odia list navigation translation progress
Thanks to xet7 !
2026-10-08 06:12:46 +03:00
Lauri Ojansivu acd7db180d Translate Odia list creation and keyboard navigation
Fill 22 English placeholders while preserving shortcut tokens, index markers and distinct selection actions. Technical wording remains low confidence pending speaker review. All 45 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 06:12:27 +03:00
Lauri Ojansivu 89704e1dfe Document Odia mathematical input translation progress
Thanks to xet7 !
2026-10-08 06:11:21 +03:00
Lauri Ojansivu a5817dc264 Translate Odia mathematical and text input labels
Fill 29 English placeholders while preserving coordinate names, indexed tokens and operand roles. Technical wording remains low confidence pending speaker review. All 44 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 06:11:01 +03:00
Lauri Ojansivu 68f1661a71 Document Odia input translation progress
Thanks to xet7 !
2026-10-08 06:10:06 +03:00
Lauri Ojansivu e39de8d099 Translate Odia Blockly help and input labels
Fill 29 English placeholders while preserving indexed tokens and distinct icon and list input actions. Technical wording remains low confidence pending speaker review. All 43 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 06:09:44 +03:00
Lauri Ojansivu 66ae55b074 Document Odia editing translation progress
Thanks to xet7 !
2026-10-08 06:08:44 +03:00
Lauri Ojansivu 8c362db0e4 Translate Odia Blockly editing and bitmap labels
Fill 30 English placeholders while preserving indexed references and distinct editing actions. Technical wording remains low confidence pending speaker review. All 42 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 06:08:25 +03:00
Lauri Ojansivu 79792c8f22 Document Odia loop translation progress
Thanks to xet7 !
2026-10-08 06:07:28 +03:00
Lauri Ojansivu d0c0d238f1 Translate Odia Blockly loops and conditionals
Fill 20 English placeholders while preserving indexed tokens and condition polarity. Technical wording remains low confidence pending speaker review. All 41 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 06:07:09 +03:00
Lauri Ojansivu 15617dcb9c Document Odia Blockly translation progress
Thanks to xet7 !
2026-10-08 06:02:41 +03:00
Lauri Ojansivu 17ba351257 Translate Odia Blockly colours and controls
Fill 24 English placeholders while preserving existing translations, format tokens and colour bounds. Technical wording remains low confidence pending speaker review. All 40 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 06:02:14 +03:00
Lauri Ojansivu d7f76febcf Document Tatar ZenKit instruction correction
Thanks to xet7 !
2026-10-08 06:00:50 +03:00
Lauri Ojansivu b781aadd36 Correct Tatar ZenKit import instruction
Restore literal JSON field names and correct the surrounding wrong-language prose. Technical wording remains low confidence pending speaker review. All 151 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 06:00:24 +03:00
Lauri Ojansivu 423a36709f Document Tatar flow report explanation corrections
Thanks to xet7 !
2026-10-08 05:58:51 +03:00
Lauri Ojansivu 8368b6d1eb Correct Tatar flow report explanations
Replace nine incomplete or wrong-language values with full explanations preserving forecast limits, date fallbacks and time adjustment caveats. Statistical wording remains low confidence pending speaker review. All 150 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 05:58:28 +03:00
Lauri Ojansivu b67a6f0b50 Document Tatar WIP and flow label corrections
Thanks to xet7 !
2026-10-08 05:57:02 +03:00
Lauri Ojansivu 11bd47aead Correct Tatar WIP and flow report labels
Correct 37 wrong-language or incomplete values while preserving statistical distinctions, day units and WIP group meaning. Statistical terminology remains low confidence pending speaker review. All 149 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 05:56:41 +03:00
Lauri Ojansivu 08f12a3fdd Document Tatar event and import translation corrections
Thanks to xet7 !
2026-10-08 05:55:22 +03:00
Lauri Ojansivu b6eaa54879 Correct Tatar event and import translations
Correct 21 wrong-language values while preserving address labels, file formats, search syntax and import selection restrictions. Technical wording remains low confidence pending speaker review. All 148 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 05:55:02 +03:00
Lauri Ojansivu 6a8bee70d9 Document Tatar repair status translation corrections
Thanks to xet7 !
2026-10-08 05:54:07 +03:00
Lauri Ojansivu b667365d2c Correct Tatar repair status translations
Correct 25 wrong-language values while preserving repair placeholders, failure conditions and the acknowledgement label. Technical wording remains low confidence pending speaker review. All 147 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 05:53:47 +03:00
Lauri Ojansivu 05678ba52c Document Tatar repository access translation corrections
Thanks to xet7 !
2026-10-08 05:52:44 +03:00
Lauri Ojansivu bc343fd773 Correct Tatar repository access translations
Correct 31 wrong-language values while preserving technical identifiers, byte units and temporary account lockout. Technical wording remains low confidence pending speaker review. All 146 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 05:52:23 +03:00
Lauri Ojansivu eb906cfd4c Document Tatar migration threshold translation corrections
Thanks to xet7 !
2026-10-08 05:51:29 +03:00
Lauri Ojansivu b547ef6b26 Correct Tatar migration threshold translations
Correct 35 wrong-language values while preserving numeric ranges, resource identifiers, time units and background processing notices. Technical wording remains low confidence pending speaker review. All 145 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 05:51:10 +03:00
Lauri Ojansivu ae223d8aa5 Document Tatar job monitoring translation corrections
Thanks to xet7 !
2026-10-08 05:50:10 +03:00
Lauri Ojansivu b2a4999678 Correct Tatar job monitoring translations
Correct 39 wrong-language values while preserving storage and resource identifiers, numeric intervals and batch limits. Technical wording remains low confidence pending speaker review. All 144 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 05:49:50 +03:00
Lauri Ojansivu e2ab7f5146 Document Tatar migration progress translation corrections
Thanks to xet7 !
2026-10-08 05:48:44 +03:00
Lauri Ojansivu 86ae33675f Correct Tatar migration progress translations
Correct 34 wrong-language values while preserving URL and ID labels, distinct recovery targets and the continued board use notice. Technical wording remains low confidence pending speaker review. All 143 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 05:48:25 +03:00
Lauri Ojansivu 087d4d117b Document Tatar board migration translation corrections
Thanks to xet7 !
2026-10-08 05:47:24 +03:00
Lauri Ojansivu 6b8beeca6a Correct Tatar board migration and recovery translations
Correct 45 wrong-language values while preserving storage identifiers, administrator restrictions, duplicate deletion conditions and recovery warnings. Technical wording remains low confidence pending speaker review. All 142 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 05:47:04 +03:00
Lauri Ojansivu c59e571cf6 Document Tatar S3 connection translation corrections
Thanks to xet7 !
2026-10-08 05:45:30 +03:00
Lauri Ojansivu 53803532fa Correct Tatar S3 connection and scheduling translations
Correct 22 wrong-language values while preserving connection examples, storage identifiers and distinct migration and scheduling actions. Technical wording remains low confidence pending speaker review. All 141 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 05:45:11 +03:00
Lauri Ojansivu 7945b67ea2 Document Tatar migration and S3 corrections
Thanks to xet7 !
2026-10-08 05:42:34 +03:00
Lauri Ojansivu c70cb5d491 Correct Tatar migration outcomes and S3 controls
Correct 22 values while preserving service identifiers and distinct migration and connection outcomes. Technical wording remains low confidence pending speaker review. All 140 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 05:42:15 +03:00
Lauri Ojansivu 674dc6990c Document Tatar cloud status corrections
Thanks to xet7 !
2026-10-08 05:41:18 +03:00
Lauri Ojansivu 48ce6a9d50 Correct Tatar cloud status and migration controls
Correct 23 values while preserving provider names, credential states and pause versus stop distinctions. Technical wording remains low confidence pending speaker review. All 139 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 05:40:58 +03:00
Lauri Ojansivu ead3da35f3 Document Tatar cloud credential corrections
Thanks to xet7 !
2026-10-08 05:40:08 +03:00
Lauri Ojansivu 0cf1c99b06 Correct Tatar cloud credentials and console paths
Correct 22 values and restore literal console labels, credential fields and file extensions from English. Technical wording remains low confidence pending speaker review. All 138 focused checks and 21 human-preference checks pass; browser checks and external console revalidation were not run.

Thanks to xet7 !
2026-10-08 05:39:48 +03:00
Lauri Ojansivu f24d673c31 Document Tatar backup schedule and restore corrections
Thanks to xet7 !
2026-10-08 05:38:34 +03:00
Lauri Ojansivu 787e7f3031 Correct Tatar backup scheduling and restore modes
Correct 15 values while preserving time formats, monthly range and distinct restore modes. Retain existing continuous-backup translations. Technical wording remains low confidence pending speaker review. All 137 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 05:38:15 +03:00
Lauri Ojansivu 6663ae2bd3 Document Tatar notification and backup corrections
Thanks to xet7 !
2026-10-08 05:37:17 +03:00
Lauri Ojansivu 696bd0eade Correct Tatar notification controls and backup scope
Correct 18 values while preserving activity-recording distinctions and organization backup boundaries. Technical wording remains low confidence pending speaker review. All 136 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 05:36:57 +03:00
Lauri Ojansivu fe8f3885e2 Document Tatar anonymization and import corrections
Thanks to xet7 !
2026-10-08 05:35:51 +03:00
Lauri Ojansivu b66f0cbc40 Correct Tatar import export and anonymization guidance
Correct 14 values and replace incorrect account-anonymization export text with its actual irreversible consequences. Preserve service and field identifiers. Technical wording remains low confidence pending speaker review. All 135 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 05:35:23 +03:00
Lauri Ojansivu 5dfd799a9e Document Tatar loading and rendering corrections
Thanks to xet7 !
2026-10-08 05:34:03 +03:00
Lauri Ojansivu df2ed8af6e Correct Tatar loading and text rendering guidance
Correct 12 values, align automatic-loading guidance with English and restore configuration names and code examples. Technical wording remains low confidence pending speaker review. All 134 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 05:33:42 +03:00
Lauri Ojansivu 1cdf9a7fb9 Document Tatar migration cleanup corrections
Thanks to xet7 !
2026-10-08 05:32:32 +03:00
Lauri Ojansivu d2e92c0d40 Correct Tatar Sandstorm migration cleanup
Correct 11 values while preserving successful-migration prerequisites and irreversible cleanup warnings. Technical wording remains low confidence pending speaker review. All 133 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 05:32:13 +03:00
Lauri Ojansivu 20522ae4d5 Document Tatar cloud and migration corrections
Thanks to xet7 !
2026-10-08 05:31:14 +03:00
Lauri Ojansivu 48b4163afc Correct Tatar cloud storage and migration guidance
Correct 22 values and restore literal service names, database URLs, environment variables, commands and directory paths. Technical wording remains low confidence pending speaker review. All 132 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 05:30:48 +03:00
Lauri Ojansivu 6ec0df714a Document Tatar migration and filesystem corrections
Thanks to xet7 !
2026-10-08 05:29:38 +03:00
Lauri Ojansivu 9fad8d8e3f Correct Tatar migration errors and filesystem states
Correct 23 values while preserving warning, retry, resume and filesystem-state distinctions. Technical wording remains low confidence pending speaker review. All 131 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 05:29:17 +03:00
Lauri Ojansivu d3f68a672f Document Tatar scheduled-job and path corrections
Thanks to xet7 !
2026-10-08 05:28:23 +03:00
Lauri Ojansivu 43e83e3d81 Correct Tatar scheduled jobs and storage paths
Correct 24 values while preserving scheduling outcomes, job actions and distinct storage paths. Technical wording remains low confidence pending speaker review. All 130 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 05:28:04 +03:00
Lauri Ojansivu dd7e16198e Document Tatar unlock and activation corrections
Thanks to xet7 !
2026-10-08 05:27:02 +03:00
Lauri Ojansivu 8536bd6188 Correct Tatar unlock and user activation controls
Correct 20 values while preserving unlock scope and opposing activation actions. Technical wording remains low confidence pending speaker review. All 129 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 05:26:42 +03:00
Lauri Ojansivu bc049499b4 Document Tatar support and lockout corrections
Thanks to xet7 !
2026-10-08 05:25:51 +03:00
Lauri Ojansivu d3fed5749c Correct Tatar support and login lockout labels
Correct 22 values while preserving access restrictions, username distinctions and lockout units. Technical wording remains low confidence pending speaker review. All 128 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 05:25:28 +03:00
Lauri Ojansivu 1c19d50dc5 Document Tatar recurrence and visibility corrections
Thanks to xet7 !
2026-10-08 05:24:30 +03:00
Lauri Ojansivu 9f47dd11ed Correct Tatar recurrence and checklist visibility
Correct 11 values while preserving archive identifiers and distinguishing card recurrence from checklist reset. Technical wording remains low confidence pending speaker review. All 127 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 05:24:09 +03:00
Lauri Ojansivu 05bba1e1e4 Document Tatar upload and translation-control corrections
Thanks to xet7 !
2026-10-08 05:23:11 +03:00
Lauri Ojansivu e492230be3 Correct Tatar upload validation and custom translations
Correct 21 values while preserving byte units, placeholders, technical identifiers and deletion warnings. Technical wording remains low confidence pending speaker review. All 126 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 05:22:46 +03:00
Lauri Ojansivu abbf5d4eb8 Document Tatar board status and transfer corrections
Thanks to xet7 !
2026-10-08 05:21:43 +03:00
Lauri Ojansivu d7bb8c95ea Correct Tatar board status and transfer progress
Correct 18 values while preserving time categories, transfer measurements and the compaction error prefix. Technical wording remains low confidence pending speaker review. All 125 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 05:21:25 +03:00
Lauri Ojansivu 3ce27c4539 Document Tatar storage statistic and compaction corrections
Thanks to xet7 !
2026-10-08 05:20:36 +03:00
Lauri Ojansivu abdd2ff10a Correct Tatar storage statistics and compaction
Correct 19 values while preserving storage identifiers, compaction prerequisites and source warnings. Technical wording remains low confidence pending speaker review. All 124 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 05:20:17 +03:00
Lauri Ojansivu 036b0f4684 Document Tatar storage repair corrections
Thanks to xet7 !
2026-10-08 05:19:14 +03:00
Lauri Ojansivu 9b0069f353 Correct Tatar storage repair and default destination
Correct 19 values while preserving storage identifiers, read eligibility, repair scope and distinct progress states. Technical wording remains low confidence pending speaker review. All 123 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 05:18:53 +03:00
Lauri Ojansivu 2fc0ded649 Document Tatar checklist and storage corrections
Thanks to xet7 !
2026-10-08 05:17:54 +03:00
Lauri Ojansivu d754305780 Correct Tatar checklist copying and attachment storage
Correct 22 values while preserving storage names, copy modes and single, global and board-scoped attachment moves. Technical wording remains low confidence pending speaker review. All 122 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 05:17:32 +03:00
Lauri Ojansivu 4a1ac55ded Document Tatar memory and checklist corrections
Thanks to xet7 !
2026-10-08 05:16:38 +03:00
Lauri Ojansivu 37561e9ff9 Correct Tatar memory and checklist controls
Correct 24 values while preserving technical identifiers, distinct memory metrics and checklist line mapping. Technical wording remains low confidence pending speaker review. All 121 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 05:16:19 +03:00
Lauri Ojansivu e386bfc3e6 Document Tatar invitation and heap-metric corrections
Thanks to xet7 !
2026-10-08 05:15:20 +03:00
Lauri Ojansivu b5ce34018f Correct Tatar team invitations and heap metrics
Correct 16 values while preserving registration conditions, board-scoped removal and distinct Node metrics. Technical wording remains low confidence pending speaker review. All 120 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 05:14:58 +03:00
Lauri Ojansivu d4b7305e43 Document Tatar ticket and deletion constraint corrections
Thanks to xet7 !
2026-10-08 05:14:02 +03:00
Lauri Ojansivu 633ec0348d Correct Tatar ticket states and deletion constraints
Correct 18 values while preserving member-related deletion restrictions, distinct ticket states and Cc notation. Technical wording remains low confidence pending speaker review. All 119 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 05:13:42 +03:00
Lauri Ojansivu 35993e2c72 Document Tatar recovery and animation corrections
Thanks to xet7 !
2026-10-08 05:12:54 +03:00
Lauri Ojansivu f105370d32 Correct Tatar API recovery and waiting animations
Correct 25 values while preserving runtime identifiers, call ordering and recovery continuation. Technical wording remains low confidence pending speaker review. All 118 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 05:12:34 +03:00
Lauri Ojansivu 3205dfd2c5 Document Tatar report and office corrections
Thanks to xet7 !
2026-10-08 05:11:21 +03:00
Lauri Ojansivu 63c592bd94 Correct Tatar report and office activity labels
Correct 28 values while preserving protocol identifiers, login counts and API aggregation semantics. Technical wording remains low confidence pending speaker review. All 117 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 05:10:54 +03:00
Lauri Ojansivu ae4aab3af6 Document Tatar map and diagnostic corrections
Thanks to xet7 !
2026-10-08 05:09:56 +03:00
Lauri Ojansivu 30aa9e2706 Correct Tatar map detection and diagnostics
Correct 21 values and restore executable log commands while preserving detection states and sorting distinctions. Technical wording remains low confidence pending speaker review. All 116 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 05:09:35 +03:00
Lauri Ojansivu ba85615fe5 Document Tatar dependency and location corrections
Thanks to xet7 !
2026-10-08 05:08:38 +03:00
Lauri Ojansivu b938fafaba Correct Tatar dependency imports and location labels
Correct 26 values while preserving relationship directions, JSON/SVG identifiers, import counters and image-size tokens. Technical wording remains low confidence pending speaker review. All 115 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 05:08:17 +03:00
Lauri Ojansivu 9fadcf0ed9 Document Tatar card sorting and dependency corrections
Thanks to xet7 !
2026-10-08 05:07:24 +03:00
Lauri Ojansivu 80f851eaac Correct Tatar card sorting and dependency labels
Correct 18 values while preserving sort direction and opposing completion, visibility, addition and removal actions. Technical wording remains low confidence pending speaker review. All 114 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 05:07:03 +03:00
Lauri Ojansivu be06e22873 Document Tatar search logic corrections
Thanks to xet7 !
2026-10-08 05:06:15 +03:00
Lauri Ojansivu b1007d158f Correct Tatar search logic and export labels
Correct 19 values while preserving query examples, OR/AND distinctions, negation, sort syntax and the Arial font name. Technical wording remains low confidence pending speaker review. All 113 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 05:05:54 +03:00
Lauri Ojansivu dbb1006351 Document Tatar search instruction corrections
Thanks to xet7 !
2026-10-08 05:04:45 +03:00
Lauri Ojansivu 1d52b40e87 Correct Tatar search instructions and validation
Correct 32 values while preserving executable examples, operator placeholders and syntax metavariables. Technical wording remains low confidence pending speaker review. All 112 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 05:04:22 +03:00
Lauri Ojansivu fd229b05be Document Tatar search operator corrections
Thanks to xet7 !
2026-10-08 05:03:08 +03:00
Lauri Ojansivu fa722a2cc9 Correct Tatar search operators and predicates
Correct 38 values, restore shorthand symbols and use distinct parser-compatible search aliases. Technical wording remains low confidence pending speaker review. All 111 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 05:02:48 +03:00
Lauri Ojansivu 763eae4731 Document Tatar search-view and result corrections
Thanks to xet7 !
2026-10-08 05:01:44 +03:00
Lauri Ojansivu d802535c04 Correct Tatar search views and result messages
Correct 34 values while preserving permission scope, member-or-assignee filtering, incomplete-card criteria and result placeholders. Technical wording remains low confidence pending speaker review. All 110 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 05:01:22 +03:00
Lauri Ojansivu 6286b6d7ae Document Tatar shared-template and domain corrections
Thanks to xet7 !
2026-10-08 05:00:14 +03:00
Lauri Ojansivu a6ee59a032 Correct Tatar shared templates and domain labels
Correct 19 values while preserving domain syntax, validation restrictions and shared-template scope. Technical wording remains low confidence pending speaker review. All 109 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 04:59:50 +03:00
Lauri Ojansivu f0c4117fdf Document Tatar role and weekday corrections
Thanks to xet7 !
2026-10-08 04:58:50 +03:00
Lauri Ojansivu 5df7256c99 Correct Tatar role settings and weekdays
Correct 41 values while preserving administrator rights, read-only permission previews, read states and deletion prerequisites. Technical wording remains low confidence pending speaker review. All 108 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 04:58:27 +03:00
Lauri Ojansivu 3fa0a9698e Document Tatar editor and administration corrections
Thanks to xet7 !
2026-10-08 04:57:22 +03:00
Lauri Ojansivu c6d2d45ee6 Correct Tatar editor preferences and administration popups
Correct 17 values while preserving literal keyboard shortcuts, editor modes and multiple-card window behavior. Technical wording remains low confidence pending speaker review. All 107 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 04:57:00 +03:00
Lauri Ojansivu b03f8cd3e6 Document Tatar reminder and deletion corrections
Thanks to xet7 !
2026-10-08 04:56:03 +03:00
Lauri Ojansivu b688f5acf1 Correct Tatar reminders and account deletion messages
Correct 33 values while preserving date and mention placeholders, reminder distinctions and irreversible deletion warnings. Technical wording remains low confidence pending speaker review. All 106 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 04:55:43 +03:00
Lauri Ojansivu a1906c32f7 Document Tatar branding and authentication corrections
Thanks to xet7 !
2026-10-08 04:54:38 +03:00
Lauri Ojansivu a520d411f0 Correct Tatar branding and authentication labels
Correct 32 values while preserving protocol names, assetlinks.json, HTML boundaries and JSON markers. Technical wording remains low confidence pending speaker review. All 105 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 04:54:18 +03:00
Lauri Ojansivu 3c2c8d0381 Document Tatar minicard and activity corrections
Thanks to xet7 !
2026-10-08 04:53:13 +03:00
Lauri Ojansivu 26c37ae023 Correct Tatar minicards and label activity
Correct 23 values while preserving checklist counters and sequential activity argument roles verified from source. Technical wording remains low confidence pending speaker review. All 104 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 04:52:53 +03:00
Lauri Ojansivu ad692922dc Document Tatar deletion and subtask corrections
Thanks to xet7 !
2026-10-08 04:51:49 +03:00
Lauri Ojansivu 96c33af893 Correct Tatar deletion and subtask settings
Correct 33 date, color, deletion and subtask values while preserving irreversible warnings, duplicate-list conditions and placeholders. Technical wording remains low confidence pending speaker review. All 103 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 04:51:29 +03:00
Lauri Ojansivu 654a334fb3 Document Tatar custom-field and organization corrections
Thanks to xet7 !
2026-10-08 04:50:27 +03:00
Lauri Ojansivu 34e568ba07 Correct Tatar custom fields and organization settings
Correct 33 wrong-language values, restore literal domain examples and MULTITENANCY=true, and preserve administrator restrictions and field-sum semantics. Technical wording remains low confidence pending speaker review. All 102 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 04:50:07 +03:00
Lauri Ojansivu b744ae5cfc Document Tatar webhook and system-information corrections
Thanks to xet7 !
2026-10-08 04:48:59 +03:00
Lauri Ojansivu f61ff81382 Correct Tatar webhooks and system information
Correct 35 values, restore literal product and runtime identifiers, and preserve webhook directions and memory/version distinctions. Technical wording remains low confidence pending speaker review. All 101 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 04:48:38 +03:00
Lauri Ojansivu dbd8205fba Document Tatar email and registration corrections
Thanks to xet7 !
2026-10-08 04:47:46 +03:00
Lauri Ojansivu 6142935a1b Correct Tatar email and registration settings
Correct 31 wrong-language values while preserving protocol names, invitation placeholders and paragraph structure, template distinctions and optional webhook authentication. Technical wording remains low confidence pending speaker review. All 100 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 04:47:26 +03:00
Lauri Ojansivu e058ee5873 Document Tatar settings and transfer-limit corrections
Thanks to xet7 !
2026-10-08 04:46:20 +03:00
Lauri Ojansivu 7287a1e98b Correct Tatar settings and transfer limits
Correct 33 wrong-language or incomplete values while preserving defaults, WIP identifiers, transfer directions and autolink disabling behavior. Technical wording remains low confidence pending speaker review. All 99 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 04:45:58 +03:00
Lauri Ojansivu bcf44241b5 Document Tatar tracking and upload corrections
Thanks to xet7 !
2026-10-08 04:44:41 +03:00
Lauri Ojansivu 7e86baf78f Correct Tatar tracking and upload controls
Correct 18 tracking, upload, shortcut and logo settings values while preserving shortcut ranges and distinct upload states. Technical wording remains low confidence pending speaker review. All 98 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 04:44:21 +03:00
Lauri Ojansivu ac2231cfbc Document Tatar sidebar shortcut corrections
Thanks to xet7 !
2026-10-08 04:41:29 +03:00
Lauri Ojansivu 90a32cfb93 Correct Tatar sidebar shortcuts and board opening text
Correct 20 values preserving source tokens, sidebar targets, automatic-opening states and hour units. Technical wording remains low confidence pending speaker review. All 97 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 04:41:09 +03:00
Lauri Ojansivu a59bab23e5 Document Tatar removal and search corrections
Thanks to xet7 !
2026-10-08 04:40:16 +03:00
Lauri Ojansivu cd64ce968b Correct Tatar member removal and search controls
Correct 22 values preserving source tokens, removal consequences and membership/assignment distinctions. Technical wording remains low confidence pending speaker review. All 96 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 04:39:56 +03:00
Lauri Ojansivu b2edef8b97 Document Tatar visibility corrections
Thanks to xet7 !
2026-10-08 04:39:00 +03:00
Lauri Ojansivu 48909fe48a Correct Tatar visibility and preview guidance
Correct 17 values preserving source tokens, login markup and membership restrictions. Technical wording remains low confidence pending speaker review. All 95 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 04:38:40 +03:00
Lauri Ojansivu a74595a7f2 Document Tatar selection and role corrections
Thanks to xet7 !
2026-10-08 04:37:43 +03:00
Lauri Ojansivu de8392bd1f Correct Tatar selection notification and role text
Correct 25 values preserving source tokens, action aliases and role restrictions. Technical wording remains low confidence pending speaker review. All 94 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 04:37:19 +03:00
Lauri Ojansivu 776caa5133 Document Tatar list and settings corrections
Thanks to xet7 !
2026-10-08 04:36:12 +03:00
Lauri Ojansivu 626802e5fa Correct Tatar list actions and settings text
Correct 21 values preserving source tokens, bulk-action scope and list deletion/archive distinctions. Technical wording remains low confidence pending speaker review. All 93 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 04:35:53 +03:00
Lauri Ojansivu 8698a295fb Document Tatar validation corrections
Thanks to xet7 !
2026-10-08 04:35:02 +03:00
Lauri Ojansivu 8aab17e35a Correct Tatar validation and board membership text
Correct 22 values preserving source tokens, year/admin requirements and deletion consequences. Technical wording remains low confidence pending speaker review. All 92 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 04:34:43 +03:00
Lauri Ojansivu 04444a9a20 Document Tatar import progress corrections
Thanks to xet7 !
2026-10-08 04:33:49 +03:00
Lauri Ojansivu bd8a1df6d6 Correct Tatar import progress and member mapping
Correct 23 values preserving source tokens, cancellation distinctions and mapping fallback. Technical wording remains low confidence pending speaker review. All 91 focused checks and 21 human-preference checks pass; browser and import-runtime checks were not run.

Thanks to xet7 !
2026-10-08 04:33:30 +03:00
Lauri Ojansivu ce26c64d08 Document Tatar Trello import corrections
Thanks to xet7 !
2026-10-08 04:32:32 +03:00
Lauri Ojansivu d017b1cbe4 Correct Tatar Trello import options and errors
Correct 28 values preserving source tokens, literal extensions, downloader name and API-key URL. Technical wording remains low confidence pending speaker review. All 90 focused checks and 21 human-preference checks pass; browser and import-runtime checks were not run.

Thanks to xet7 !
2026-10-08 04:32:10 +03:00
Lauri Ojansivu 0fc2abc240 Document Tatar import guidance corrections
Thanks to xet7 !
2026-10-08 04:31:01 +03:00
Lauri Ojansivu 5349f6e0b0 Correct Tatar import guidance and technical examples
Correct 25 values restoring API paths, JSON keys, file extensions and filter syntax. Include missing Markdown bullet-list guidance. Technical wording remains low confidence pending speaker review. All 89 focused checks and 21 human-preference checks pass; browser and import-runtime checks were not run.

Thanks to xet7 !
2026-10-08 04:30:41 +03:00
Lauri Ojansivu a818093f15 Document Tatar filtering corrections
Thanks to xet7 !
2026-10-08 04:29:15 +03:00
Lauri Ojansivu f761b206f4 Correct Tatar sorting and filtering controls
Correct 34 values preserving source tokens, distinct sorting abbreviations and filter meanings. Technical wording remains low confidence pending speaker review. All 88 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 04:28:56 +03:00
Lauri Ojansivu a0f998fb1a Document Tatar export corrections
Thanks to xet7 !
2026-10-08 04:27:57 +03:00
Lauri Ojansivu 9821da6b21 Correct Tatar export and account conflict messages
Correct 29 values preserving source tokens, format names and export failure conditions. Technical wording remains low confidence pending speaker review. All 87 focused checks and 21 human-preference checks pass; browser and export-runtime checks were not run.

Thanks to xet7 !
2026-10-08 04:27:37 +03:00
Lauri Ojansivu 3069c116de Document Tatar email and error corrections
Thanks to xet7 !
2026-10-08 04:26:42 +03:00
Lauri Ojansivu f22b4c3f94 Correct Tatar email templates and error messages
Correct 34 values preserving source tokens, email paragraphs and permission distinctions. Technical wording remains low confidence pending speaker review. All 86 focused checks and 21 human-preference checks pass; browser and email-delivery checks were not run.

Thanks to xet7 !
2026-10-08 04:26:17 +03:00
Lauri Ojansivu 52b72fdc5c Document Tatar field control corrections
Thanks to xet7 !
2026-10-08 04:25:09 +03:00
Lauri Ojansivu 539f75d921 Correct Tatar field controls and date notation
Correct 22 prose values and restore three date-format labels to source notation. Preserve tokens and distinct field options. Technical wording remains low confidence pending speaker review. All 85 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 04:24:48 +03:00
Lauri Ojansivu 465b0e78e0 Document Tatar copying corrections
Thanks to xet7 !
2026-10-08 04:23:56 +03:00
Lauri Ojansivu 67f7566e81 Correct Tatar copying and restricted role controls
Correct 30 values preserving tokens, embedded JSON syntax and role restrictions. Technical wording remains low confidence pending speaker review. All 84 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 04:23:35 +03:00
Lauri Ojansivu 6b790cdcfe Document Tatar color and permission corrections
Thanks to xet7 !
2026-10-08 04:22:30 +03:00
Lauri Ojansivu 015aa9a3d9 Correct Tatar colors and comment permission text
Correct 36 text values and restore the blank comment placeholder. Preserve source tokens, shade distinctions and permission limits. Specialized color wording remains low confidence pending speaker review. All 83 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 04:22:07 +03:00
Lauri Ojansivu 3e851648d2 Document Tatar navigation corrections
Thanks to xet7 !
2026-10-08 04:21:07 +03:00
Lauri Ojansivu 2a862f7364 Correct Tatar navigation and card aging text
Correct 31 values preserving source tokens, movement directions, toggle states and aging tiers. Technical wording remains low confidence pending speaker review. All 82 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 04:20:47 +03:00
Lauri Ojansivu b6b173b393 Document Tatar user mapping corrections
Thanks to xet7 !
2026-10-08 04:19:44 +03:00
Lauri Ojansivu eb43ced15b Correct Tatar user mapping and appearance text
Correct 32 values preserving placeholders, mapping permission limits and font-size distinctions. Technical wording remains low confidence pending speaker review. All 81 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 04:19:22 +03:00
Lauri Ojansivu cfe7e6d368 Document Tatar popup label corrections
Thanks to xet7 !
2026-10-08 04:18:24 +03:00
Lauri Ojansivu 14cbd1404f Correct Tatar popup and account action labels
Correct 30 values preserving source tokens, distinct account actions and restoration targets. Technical wording remains low confidence pending speaker review. All 80 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 04:18:04 +03:00
Lauri Ojansivu 5677871ba5 Document Tatar voting and poker corrections
Thanks to xet7 !
2026-10-08 04:17:10 +03:00
Lauri Ojansivu a9a2e710ab Correct Tatar voting controls and poker symbols
Correct 27 prose values and restore ten numeric/question-mark options to source symbols. Preserve placeholders and support/opposition distinctions. Technical wording remains low confidence pending speaker review. All 79 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 04:16:46 +03:00
Lauri Ojansivu 5bf36236b3 Document Tatar card archival corrections
Thanks to xet7 !
2026-10-08 04:15:44 +03:00
Lauri Ojansivu 66c6211e8a Correct Tatar card archival and editing guidance
Correct 29 values preserving source tokens, restoration/deletion distinctions and overdue meaning. Technical wording remains low confidence pending speaker review. All 78 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 04:15:12 +03:00
Lauri Ojansivu 674e27806c Document Tatar board view corrections
Thanks to xet7 !
2026-10-08 04:14:05 +03:00
Lauri Ojansivu e628b9bb03 Correct Tatar board views and appearance controls
Correct 26 values preserving tokens, zoom limits, product names and equivalent action labels. Technical wording remains low confidence pending speaker review. All 77 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 04:13:43 +03:00
Lauri Ojansivu 9ccc0597da Document Tatar archive and board corrections
Thanks to xet7 !
2026-10-08 04:12:49 +03:00
Lauri Ojansivu 97ecc408e7 Correct Tatar archive attachment and board controls
Correct 32 values preserving source tokens, visibility markup and deletion distinctions. Technical wording remains low confidence pending speaker review. All 76 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 04:12:27 +03:00
Lauri Ojansivu 2e18e0d6cc Document Tatar common control corrections
Thanks to xet7 !
2026-10-08 04:11:30 +03:00
Lauri Ojansivu afc09ff58d Correct Tatar common controls and loading guidance
Correct 23 wrong-language values while preserving correct adjacent translations and source tokens. Technical wording remains low confidence pending speaker review. All 75 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 04:11:09 +03:00
Lauri Ojansivu bc36c11b75 Document Tatar board layout corrections
Thanks to xet7 !
2026-10-08 04:10:12 +03:00
Lauri Ojansivu 2dcf2f2596 Correct Tatar board selection and layout controls
Correct 33 values preserving source tokens, personal/shared scope and width/height distinctions. Technical wording remains low confidence pending speaker review. All 74 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 04:09:51 +03:00
Lauri Ojansivu 053d9fba6c Document Tatar checklist and workspace corrections
Thanks to xet7 !
2026-10-08 04:08:40 +03:00
Lauri Ojansivu 1aedd9578e Correct Tatar checklist activities and workspace controls
Correct 35 values preserving source tokens and template-confirmed argument roles. Technical wording remains low confidence pending speaker review. All 72 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 04:08:09 +03:00
Lauri Ojansivu a931c5986e Document Tatar movement activity corrections
Thanks to xet7 !
2026-10-08 04:07:03 +03:00
Lauri Ojansivu 06adc11de7 Correct Tatar movement import and membership activities
Correct 27 values preserving source/destination placeholders and sequential argument roles. Technical wording remains low confidence pending speaker review. All 71 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 04:06:44 +03:00
Lauri Ojansivu 6b5185991c Document Tatar activity corrections
Thanks to xet7 !
2026-10-08 04:05:40 +03:00
Lauri Ojansivu 01b12e1f4c Correct Tatar activity messages and title argument order
Correct 31 values while preserving source placeholders, action polarity and the title-before-card argument order used by the activity template. Technical wording remains low confidence pending speaker review. All 70 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 04:05:21 +03:00
Lauri Ojansivu ea1e10df21 Document Tatar rule description corrections
Thanks to xet7 !
2026-10-08 04:04:06 +03:00
Lauri Ojansivu 1a236cea3e Correct Tatar rule descriptions and checklist guidance
Correct 31 values preserving source tokens, comma-separated examples and correct existing translations. Technical wording and composed fragments remain low confidence pending speaker review. All 68 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 04:03:39 +03:00
Lauri Ojansivu 33dd2616e4 Document Tatar rule action corrections
Thanks to xet7 !
2026-10-08 04:02:45 +03:00
Lauri Ojansivu ef43db6b58 Correct Tatar checklist movement and email actions
Correct 31 wrong-language values while preserving correct translations, source tokens and variable tags. Technical wording and composed fragments remain low confidence pending speaker review. All 67 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 04:02:26 +03:00
Lauri Ojansivu 8b313dde1c Document Tatar trigger event corrections
Thanks to xet7 !
2026-10-08 04:01:37 +03:00
Lauri Ojansivu 7904a3b4dc Correct Tatar trigger events and date-change wording
Correct 37 values preserving tokens and distinct date types, set-or-change semantics and movement directions. Technical wording and composed fragments remain low confidence pending speaker review. All 66 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 04:01:16 +03:00
Lauri Ojansivu 5985c8e030 Document Tatar scheduled rule corrections
Thanks to xet7 !
2026-10-08 04:00:12 +03:00
Lauri Ojansivu 7b778d93e4 Correct Tatar scheduled rules and workflow controls
Replace 43 wrong-language values while preserving correct translations, source tokens and product names. Technical wording remains low confidence pending speaker review. All 65 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 03:59:53 +03:00
Lauri Ojansivu c7acdd37a3 Document Tatar rule editor corrections
Thanks to xet7 !
2026-10-08 03:58:55 +03:00
Lauri Ojansivu caf9ebdfe4 Correct wrong-language Tatar rule editor strings
Replace 46 incorrect values with Tatar rule, trigger and import/export wording while preserving correct existing translations and source tokens. Technical wording remains low confidence pending speaker review. All 64 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 03:58:34 +03:00
Lauri Ojansivu 561681f9a7 Document Tatar keyboard and recovery translations
Thanks to xet7 !
2026-10-08 03:57:26 +03:00
Lauri Ojansivu e18f43bd5e Translate Tatar keyboard labels and recovery controls
Fill 28 English placeholders preserving key-cap names and permanent cancellation semantics. Default inventory retains only 11 product names and mathematical identifiers; older wrong-language text still needs correction. Technical wording remains low confidence pending speaker review. All 62 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 03:57:05 +03:00
Lauri Ojansivu 8f161af0e2 Document Tatar diagnostic and recovery translations
Thanks to xet7 !
2026-10-08 03:56:07 +03:00
Lauri Ojansivu bdb0a7938f Translate Tatar synchronization diagnostics and recovery
Fill 56 English placeholders preserving tokens, limits, null semantics and recovery distinctions. Technical terminology remains low confidence pending speaker review. All 61 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 03:55:46 +03:00
Lauri Ojansivu 6ed32e30ec Document Tatar observation and synchronization translations
Thanks to xet7 !
2026-10-08 03:54:27 +03:00
Lauri Ojansivu c1fda30916 Translate Tatar observations and synchronization controls
Fill 56 English placeholders preserving tokens, observation limits and conflict-choice distinctions. Technical terminology remains low confidence pending speaker review. All 60 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 03:54:06 +03:00
Lauri Ojansivu 79a2f892e9 Document Tatar Scrum translations
Thanks to xet7 !
2026-10-08 03:52:37 +03:00
Lauri Ojansivu 6eb52f9385 Translate Tatar Scrum planning and sprint reports
Fill 61 English placeholders with preserved tokens and distinct sprint states, completion policies and unknown estimates. Technical terminology remains low confidence pending speaker review. All 58 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 03:52:17 +03:00
Lauri Ojansivu 11b6811bcb Document Tatar workspace and rule translations
Thanks to xet7 !
2026-10-08 03:51:10 +03:00
Lauri Ojansivu b991dcc9e9 Translate Tatar workspace search and block rule controls
Fill 41 English placeholders while preserving tokens, keyboard shortcuts and fragment spacing. Technical wording remains low confidence pending speaker review. All 56 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 03:50:51 +03:00
Lauri Ojansivu 9c8425aff6 Document Tatar text and variable translations
Thanks to xet7 !
2026-10-08 03:49:46 +03:00
Lauri Ojansivu 57f6d9917d Translate Tatar text formatting and variable controls
Fill 34 English placeholders with preserved source tokens and distinct text and variable operations. Technical wording remains low confidence pending speaker review. All 55 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 03:49:25 +03:00
Lauri Ojansivu b09d18754b Document Tatar text search translations
Thanks to xet7 !
2026-10-08 03:47:08 +03:00
Lauri Ojansivu 68a5e2a844 Translate Tatar text positions and search
Fill 32 English placeholders and cover tokens, character positions, case labels and not-found results. Technical wording remains low confidence pending speaker review. All 54 focused checks and 21 human-preference checks pass. Browser checks were not run.

Thanks to xet7 !
2026-10-08 03:46:47 +03:00
Lauri Ojansivu 2e5bc14386 Document Tatar shortcut translations
Thanks to xet7 !
2026-10-08 03:45:59 +03:00
Lauri Ojansivu a6516246e8 Translate Tatar navigation shortcuts
Fill 40 English placeholders and cover tokens, directions, navigation targets and screen-reader states. Technical wording remains low confidence pending speaker review. All 53 focused checks and 21 human-preference checks pass. Browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 03:45:38 +03:00
Lauri Ojansivu f972047b9e Document Tatar function translations
Thanks to xet7 !
2026-10-08 03:44:45 +03:00
Lauri Ojansivu 065fd8de29 Translate Tatar function controls
Fill 27 English placeholders and cover tokens, return distinctions and invocation references. Technical wording remains low confidence pending speaker review. All 52 focused checks and 21 human-preference checks pass. Browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 03:44:28 +03:00
Lauri Ojansivu eda0179c80 Document Tatar trigonometry translations
Thanks to xet7 !
2026-10-08 03:43:35 +03:00
Lauri Ojansivu c0acf256e6 Translate Tatar trigonometry and workspace controls
Fill 24 English placeholders and cover tokens, angle units, inverse functions and variable types. Technical wording remains low confidence pending speaker review. All 51 focused checks and 21 human-preference checks pass. Browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 03:43:17 +03:00
Lauri Ojansivu 195f149d8b Document Tatar mathematical function translations
Thanks to xet7 !
2026-10-08 03:42:27 +03:00
Lauri Ojansivu dd67fc6a21 Translate Tatar random numbers and mathematical functions
Fill 28 English placeholders and cover tokens, random bounds, logarithm bases and rounding distinctions. Technical wording remains low confidence pending speaker review. All 50 focused checks and 21 human-preference checks pass. Browser checks were not run.

Thanks to xet7 !
2026-10-08 03:42:07 +03:00
Lauri Ojansivu 70629a9962 Document Tatar statistics translations
Thanks to xet7 !
2026-10-08 03:41:18 +03:00
Lauri Ojansivu efc0acc280 Translate Tatar number tests and statistics
Fill 30 English placeholders and cover tokens, statistical distinctions, number polarity and remainder notation. Technical wording remains low confidence pending speaker review. All 49 focused checks and 21 human-preference checks pass. Browser checks were not run.

Thanks to xet7 !
2026-10-08 03:41:00 +03:00
Lauri Ojansivu 0eeabb128e Document Tatar arithmetic translations
Thanks to xet7 !
2026-10-08 03:40:05 +03:00
Lauri Ojansivu d4c35a997e Translate Tatar logic and arithmetic explanations
Fill 25 English placeholders and cover tokens, constants, coordinates and conditional labels. Technical wording remains low confidence pending speaker review. All 48 focused checks and 21 human-preference checks pass. Browser checks were not run.

Thanks to xet7 !
2026-10-08 03:39:45 +03:00
Lauri Ojansivu 83fa49f30b Document Tatar sorting and logic translations
Thanks to xet7 !
2026-10-08 03:38:56 +03:00
Lauri Ojansivu 58dcd3a7a9 Translate Tatar sorting and logic comparisons
Fill 32 English placeholders and cover tokens, sorting direction, inclusive comparisons and copy semantics. Technical wording remains low confidence pending speaker review. All 47 focused checks and 21 human-preference checks pass. Browser checks were not run.

Thanks to xet7 !
2026-10-08 03:38:37 +03:00
Lauri Ojansivu db67ef4c8f Document Tatar list editing translations
Thanks to xet7 !
2026-10-08 03:37:44 +03:00
Lauri Ojansivu 10e4c15eb0 Translate Tatar list removal and editing
Fill 32 English placeholders and cover tokens, removal, copy semantics and list positions. Technical wording remains low confidence pending speaker review. All 46 focused checks and 21 human-preference checks pass. Browser checks were not run.

Thanks to xet7 !
2026-10-08 03:37:23 +03:00
Lauri Ojansivu 452a23f5f8 Document Tatar navigation translations
Thanks to xet7 !
2026-10-08 03:36:27 +03:00
Lauri Ojansivu aa47a81037 Translate Tatar navigation and list construction
Fill 29 English placeholders and cover tokens, move-key references and retrieval versus removal. Technical wording remains low confidence pending speaker review. All 45 focused checks and 21 human-preference checks pass. Browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 03:36:06 +03:00
Lauri Ojansivu 8ede77001f Document Tatar input translations
Thanks to xet7 !
2026-10-08 03:35:10 +03:00
Lauri Ojansivu 2cbea286e7 Translate Tatar input labels
Fill 39 English placeholders and cover tokens, arithmetic operands, coordinates and position labels. Technical wording remains low confidence pending speaker review. All 44 focused checks and 21 human-preference checks pass. Browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 03:34:50 +03:00
Lauri Ojansivu 729d612c03 Document Tatar editing translations
Thanks to xet7 !
2026-10-08 03:34:04 +03:00
Lauri Ojansivu 5fc7eb82cb Translate Tatar editing and accessibility labels
Fill 37 English placeholders and cover tokens, opposite actions, bitmap coordinates and variable references. Technical wording remains low confidence pending speaker review. All 43 focused checks and 21 human-preference checks pass. Browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 03:33:44 +03:00
Lauri Ojansivu 16280bd7f7 Document Tatar loop translations
Thanks to xet7 !
2026-10-08 03:32:59 +03:00
Lauri Ojansivu 6e6b7b60c2 Translate Tatar loop and editing controls
Fill 33 English placeholders and cover tokens, loop polarity, variable references and deletion counts. Technical wording remains low confidence pending speaker review. All 42 focused checks and 21 human-preference checks pass. Browser checks were not run.

Thanks to xet7 !
2026-10-08 03:32:35 +03:00
Lauri Ojansivu d7d6dbb507 Document Tatar block and activity translations
Thanks to xet7 !
2026-10-08 03:31:36 +03:00
Lauri Ojansivu 5383509a30 Translate Tatar block controls and correct activity wording
Fill 24 English placeholders and correct nine mixed-language activity and comment strings. Preserve tokens and cover Tatar vocabulary, colour bounds and loop distinctions. Technical wording remains low confidence pending speaker review. All 41 focused checks and 21 human-preference checks pass. Browser checks were not run.

Thanks to xet7 !
2026-10-08 03:31:14 +03:00
Lauri Ojansivu 25965b789d Document Central Kurdish recovery translation coverage
Thanks to xet7 !
2026-10-08 03:30:13 +03:00
Lauri Ojansivu dd19d28474 Translate Central Kurdish recovery controls
Fill 25 English placeholders and cover tokens, delivery states and cancellation warnings. Default inventory retains 11 product names and mathematical symbols; none of 149 pending Transifex keys remain English. Technical wording remains low confidence pending speaker review. All 59 focused checks and 21 human-preference checks pass. Browser and right-to-left layout checks were not run.

Thanks to xet7 !
2026-10-08 03:29:51 +03:00
Lauri Ojansivu d603313a20 Document Central Kurdish diagnostic translations
Thanks to xet7 !
2026-10-08 03:28:51 +03:00
Lauri Ojansivu 2535250fc6 Translate Central Kurdish diagnostics and mail failures
Fill 37 English placeholders and cover tokens, retention limits, null handling and delivery distinctions. Technical wording remains low confidence pending speaker review. All 42 focused checks and 21 human-preference checks pass. Browser and right-to-left layout checks were not run.

Thanks to xet7 !
2026-10-08 03:28:31 +03:00
Lauri Ojansivu 583d3e78ae Document Central Kurdish Sync translations
Thanks to xet7 !
2026-10-08 03:27:24 +03:00
Lauri Ojansivu 36a39e467e Translate Central Kurdish Sync conflicts and preview
Fill 37 English placeholders and cover tokens, source protection, preview limits and shared labels. Technical wording remains low confidence pending speaker review. All 41 focused checks and 21 human-preference checks pass. Browser and right-to-left layout checks were not run.

Thanks to xet7 !
2026-10-08 03:27:05 +03:00
Lauri Ojansivu b9fd435b75 Document Central Kurdish report translations
Thanks to xet7 !
2026-10-08 03:26:02 +03:00
Lauri Ojansivu 7f4b2b1421 Translate Central Kurdish sprint reports and observations
Fill 44 English placeholders and cover tokens, UTC references, observation limits and unknown estimates. Technical wording remains low confidence pending speaker review. All 42 focused checks and 21 human-preference checks pass. Browser and right-to-left layout checks were not run.

Thanks to xet7 !
2026-10-08 03:25:43 +03:00
Lauri Ojansivu 2d2c409610 Document Central Kurdish Scrum planning translations
Thanks to xet7 !
2026-10-08 03:24:31 +03:00
Lauri Ojansivu cb4b1d1681 Translate Central Kurdish rule editor and Scrum planning
Fill 49 English placeholders and cover tokens, shared labels, sprint actions and rule restrictions. Technical wording remains low confidence pending speaker review. All 41 focused checks and 21 human-preference checks pass. Browser and right-to-left layout checks were not run.

Thanks to xet7 !
2026-10-08 03:24:11 +03:00
Lauri Ojansivu faa8177dda Document Central Kurdish workspace translations
Thanks to xet7 !
2026-10-08 03:23:16 +03:00
Lauri Ojansivu 2c2523222f Translate Central Kurdish workspace and variable messages
Fill 69 English placeholders and cover tokens, keyboard shortcuts, count fragments and trim directions. Technical wording remains low confidence pending speaker review. All 52 focused checks and 21 human-preference checks pass. Browser, screen-reader and right-to-left layout checks were not run.

Thanks to xet7 !
2026-10-08 03:22:56 +03:00
Lauri Ojansivu 67d247f5d8 Document Central Kurdish text operation translations
Thanks to xet7 !
2026-10-08 03:21:59 +03:00
Lauri Ojansivu a454a4511a Translate Central Kurdish text operations
Fill 47 English placeholders and cover tokens, positions, whitespace and replacement arguments. Technical wording remains low confidence pending speaker review. All 51 focused checks and 21 human-preference checks pass. Browser, screen-reader and right-to-left layout checks were not run.

Thanks to xet7 !
2026-10-08 03:21:37 +03:00
Lauri Ojansivu a608191d55 Document Central Kurdish shortcut translations
Thanks to xet7 !
2026-10-08 03:20:48 +03:00
Lauri Ojansivu eb39ca6b75 Translate Central Kurdish keyboard shortcuts
Fill 39 English placeholders and cover tokens, physical directions, navigation targets and action distinctions. Technical wording remains low confidence pending speaker review. All 50 focused checks and 21 human-preference checks pass. Browser, screen-reader and right-to-left layout checks were not run.

Thanks to xet7 !
2026-10-08 03:20:27 +03:00
Lauri Ojansivu 9fa1aba11c Document Central Kurdish function translations
Thanks to xet7 !
2026-10-08 03:19:40 +03:00
Lauri Ojansivu ae802feaa2 Translate Central Kurdish function and variable controls
Fill 37 English placeholders and cover tokens, return distinctions and invocation restrictions. Technical wording remains low confidence pending speaker review. All 49 focused checks and 21 human-preference checks pass. Browser, screen-reader and right-to-left layout checks were not run.

Thanks to xet7 !
2026-10-08 03:19:17 +03:00
Lauri Ojansivu 0c21b2735a Document Central Kurdish mathematical function translations
Thanks to xet7 !
2026-10-08 03:18:26 +03:00
Lauri Ojansivu 9eac080769 Translate Central Kurdish logarithms and trigonometry
Fill 34 English placeholders and cover tokens, logarithm bases, inverse functions and degree units. Technical wording remains low confidence pending speaker review. All 48 focused checks and 21 human-preference checks pass. Browser, screen-reader and right-to-left layout checks were not run.

Thanks to xet7 !
2026-10-08 03:18:07 +03:00
Lauri Ojansivu c29c9abe93 Document Central Kurdish statistics translations
Thanks to xet7 !
2026-10-08 03:17:22 +03:00
Lauri Ojansivu 2e5879ed5d Translate Central Kurdish statistics and number tests
Fill 35 English placeholders and cover tokens, statistical operations, number polarity, rounding and random bounds. Technical wording remains low confidence pending speaker review. All 47 focused checks and 21 human-preference checks pass. Browser, screen-reader and right-to-left layout checks were not run.

Thanks to xet7 !
2026-10-08 03:17:04 +03:00
Lauri Ojansivu 6f2100d9f6 Document Central Kurdish logic translations
Thanks to xet7 !
2026-10-08 03:16:04 +03:00
Lauri Ojansivu 6d08bbe431 Translate Central Kurdish logic and arithmetic
Fill 35 English placeholders and cover tokens, conditional labels, mathematical notation and operator distinctions. Technical wording remains low confidence pending speaker review. All 46 focused checks and 21 human-preference checks pass. Browser, screen-reader and right-to-left layout checks were not run.

Thanks to xet7 !
2026-10-08 03:15:46 +03:00
Lauri Ojansivu 76f4ffe499 Document Central Kurdish list editing translations
Thanks to xet7 !
2026-10-08 03:14:51 +03:00
Lauri Ojansivu 55b2db55e3 Translate Central Kurdish list editing and comparisons
Fill 37 English placeholders and cover tokens, insertion versus replacement, copy semantics and comparison labels. Technical wording remains low confidence pending speaker review. All 45 focused checks and 21 human-preference checks pass. Browser, screen-reader and right-to-left layout checks were not run.

Thanks to xet7 !
2026-10-08 03:14:28 +03:00
Lauri Ojansivu 5648dc6419 Document Central Kurdish list translations
Thanks to xet7 !
2026-10-08 03:13:38 +03:00
Lauri Ojansivu 83e4037699 Translate Central Kurdish list retrieval and removal
Fill 42 English placeholders and cover token inventories, retrieval versus removal and list positions. Technical wording remains low confidence pending speaker review. All 44 focused checks and 21 human-preference checks pass. Browser, screen-reader and right-to-left layout checks were not run.

Thanks to xet7 !
2026-10-08 03:13:21 +03:00
Lauri Ojansivu 1be86808c4 Document Central Kurdish input translations
Thanks to xet7 !
2026-10-08 03:12:24 +03:00
Lauri Ojansivu f150ca438c Translate Central Kurdish input labels and keyboard navigation
Fill 48 English placeholders and cover token inventories, arithmetic operands, positions and navigation distinctions. Technical wording remains low confidence pending speaker review. All 43 focused checks and 21 human-preference checks pass. Browser, screen-reader and right-to-left layout checks were not run.

Thanks to xet7 !
2026-10-08 03:12:02 +03:00
Lauri Ojansivu de50541203 Document Central Kurdish editing translations
Thanks to xet7 !
2026-10-08 03:11:14 +03:00
Lauri Ojansivu e1f627d11e Translate Central Kurdish Blockly editing and accessibility
Fill 54 English placeholders and cover token inventories, condition polarity and editing states. Technical wording remains low confidence pending speaker review. All 42 focused checks and 21 human-preference checks pass. Browser, screen-reader and right-to-left layout checks were not run.

Thanks to xet7 !
2026-10-08 03:10:57 +03:00
Lauri Ojansivu 52bd102d3d Document Central Kurdish Blockly translations
Thanks to xet7 !
2026-10-08 03:10:08 +03:00
Lauri Ojansivu 12002e195d Translate Central Kurdish Blockly colours and flow controls
Fill 39 English placeholders and cover tokens, colour bounds and loop-control distinctions. Technical wording remains low confidence pending speaker review. All 41 focused checks and 21 human-preference checks pass. Browser and right-to-left layout checks were not run.

Thanks to xet7 !
2026-10-08 03:09:50 +03:00
Lauri Ojansivu ef198b3faa Document Bhojpuri recovery translation coverage
Thanks to xet7 !
2026-10-08 03:08:42 +03:00
Lauri Ojansivu f033222baf Translate Bhojpuri final recovery controls
Fill six remaining ordinary English placeholders and verify cancellation safeguards. Default inventory retains only 11 product names and mathematical symbols; none of 149 pending Transifex keys remain English. Technical wording remains low confidence pending speaker review. All 61 focused checks and 21 human-preference checks pass. Browser checks were not run.

Thanks to xet7 !
2026-10-08 03:08:19 +03:00
Lauri Ojansivu 13c15fd9ef Document Bhojpuri diagnostic and recovery translations
Thanks to xet7 !
2026-10-08 03:07:31 +03:00
Lauri Ojansivu 3a09261d22 Translate Bhojpuri Sync diagnostics and notification recovery
Fill 49 English placeholders and cover tokens, retention limits, null handling and distinct delivery states. Technical wording remains low confidence pending speaker review. All 42 focused checks and 21 human-preference checks pass. Browser checks were not run.

Thanks to xet7 !
2026-10-08 03:07:14 +03:00
Lauri Ojansivu be71942a1f Document Bhojpuri observation and Sync translations
Thanks to xet7 !
2026-10-08 03:06:08 +03:00
Lauri Ojansivu 6758bca715 Translate Bhojpuri observations and Sync conflicts
Fill 54 English placeholders and cover tokens, UTC references, numeric limits and source protection. Technical wording remains low confidence pending speaker review. All 42 focused checks and 21 human-preference checks pass. Browser checks were not run.

Thanks to xet7 !
2026-10-08 03:05:51 +03:00
Lauri Ojansivu ba96100999 Document Bhojpuri Scrum planning translations
Thanks to xet7 !
2026-10-08 03:04:47 +03:00
Lauri Ojansivu 8044a82c3c Translate Bhojpuri Scrum planning and sprint controls
Fill 74 English placeholders and cover token preservation, sprint states, shared labels and unknown estimates. Scrum terminology remains low confidence pending speaker review. All 40 focused checks and 21 human-preference checks pass. Browser checks were not run.

Thanks to xet7 !
2026-10-08 03:04:30 +03:00
Lauri Ojansivu 509a9139b8 Document Bhojpuri workspace search translations
Thanks to xet7 !
2026-10-08 03:03:29 +03:00
Lauri Ojansivu 4246f112b0 Translate Bhojpuri workspace search and block rule editor
Fill 59 English placeholders while preserving existing translations, physical key inscriptions and source tokens. Cover search shortcuts, workspace counts and rule restrictions. Technical wording remains low confidence pending speaker review. All 54 focused checks and 21 human-preference checks pass; browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 03:03:10 +03:00
Lauri Ojansivu 04a9b84ff7 Document Bhojpuri text operation translations
Thanks to xet7 !
2026-10-08 03:02:07 +03:00
Lauri Ojansivu c8bce21f5f Translate Bhojpuri text operations and variable controls
Fill 71 English placeholders while preserving existing translations and tokens. Cover text positions, whitespace, replacement argument order and input prompts. Technical wording remains low confidence pending speaker review. All 53 focused checks and 21 human-preference checks pass. Browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 03:01:48 +03:00
Lauri Ojansivu be3cee2411 Document Bhojpuri navigation translations
Thanks to xet7 !
2026-10-08 03:00:44 +03:00
Lauri Ojansivu d98f3aff31 Translate Bhojpuri navigation and screen-reader controls
Fill 39 English placeholders while preserving existing translations and source tokens. Cover navigation directions and screen-reader toggle instructions. Accessibility wording remains low confidence pending speaker review. All 52 focused checks and 21 human-preference checks pass; browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 03:00:27 +03:00
Lauri Ojansivu e7132bcb74 Document Bhojpuri workspace and function translations
Thanks to xet7 !
2026-10-08 02:59:42 +03:00
Lauri Ojansivu f3fd1f108d Translate Bhojpuri workspace and function controls
Fill 37 English placeholders and cover token preservation, function return distinctions and invocation restrictions. Technical wording remains low confidence pending speaker review. All 51 focused checks and 21 human-preference checks pass. Browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 02:59:24 +03:00
Lauri Ojansivu 44a912a801 Document Bhojpuri trigonometry translations
Thanks to xet7 !
2026-10-08 02:57:20 +03:00
Lauri Ojansivu d69c4224ef Translate Bhojpuri logarithm and trigonometry messages
Fill 27 English placeholders preserving existing text, notation and tokens. Trigonometry terminology remains low confidence. All 50 focused checks and 21 human-preference checks pass. Browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 02:57:05 +03:00
Lauri Ojansivu 8ee666f99d Document Bhojpuri statistics translations
Thanks to xet7 !
2026-10-08 02:56:29 +03:00
Lauri Ojansivu 9937a38008 Translate Bhojpuri statistics and random-number instructions
Fill 29 English placeholders preserving existing text and tokens. Statistical and rounding terminology remains low confidence. All 49 focused checks and 21 human-preference checks pass. Browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 02:56:12 +03:00
Lauri Ojansivu f486ed5bfa Document Bhojpuri arithmetic translations
Thanks to xet7 !
2026-10-08 02:55:36 +03:00
Lauri Ojansivu 3c5a19f3a0 Translate Bhojpuri arithmetic and number tests
Fill 28 English placeholders preserving existing text, formulas and tokens. Inverse trigonometry wording remains low confidence. All 48 focused checks and 21 human-preference checks pass. Browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 02:55:20 +03:00
Lauri Ojansivu 67955d97cd Document Bhojpuri logic translations
Thanks to xet7 !
2026-10-08 02:54:29 +03:00
Lauri Ojansivu 989dfa33a6 Translate Bhojpuri Blockly logic and comparisons
Fill 29 English placeholders preserving existing text and tokens. Delimiter and Boolean terminology remains low confidence. All 47 focused checks and 21 human-preference checks pass. Browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 02:54:13 +03:00
Lauri Ojansivu 374aa3112f Document Bhojpuri list editing translations
Thanks to xet7 !
2026-10-08 02:53:29 +03:00
Lauri Ojansivu 6191084788 Translate Bhojpuri list indexing and editing instructions
Fill 32 English placeholders preserving existing text and tokens. Index and case-insensitive sorting terminology remains low confidence. All 46 focused checks and 21 human-preference checks pass. Browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 02:53:12 +03:00
Lauri Ojansivu 6143d7b4eb Document Bhojpuri list retrieval translations
Thanks to xet7 !
2026-10-08 02:52:35 +03:00
Lauri Ojansivu 770c24071f Translate Bhojpuri Blockly list retrieval and removal
Fill 30 English placeholders preserving existing text and tokens. Sublist terminology remains low confidence. All 45 focused checks and 21 human-preference checks pass. Browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 02:52:19 +03:00
Lauri Ojansivu 118bfdde68 Document Bhojpuri navigation translations
Thanks to xet7 !
2026-10-08 02:51:42 +03:00
Lauri Ojansivu 27c404777f Translate Bhojpuri text inputs and keyboard navigation
Fill 30 English placeholders preserving existing text and tokens. Statement-position wording remains low confidence. All 44 focused checks and 21 human-preference checks pass. Browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 02:51:26 +03:00
Lauri Ojansivu b8844838cd Document Bhojpuri input-label translations
Thanks to xet7 !
2026-10-08 02:50:18 +03:00
Lauri Ojansivu 5aea181bf2 Translate Bhojpuri Blockly input labels
Fill 31 English placeholders preserving existing text and tokens. Delimiter and operand wording remains low confidence. All 43 focused checks and 21 human-preference checks pass. Browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 02:50:00 +03:00
Lauri Ojansivu 16b0e5741f Document Bhojpuri editing translations
Thanks to xet7 !
2026-10-08 02:49:21 +03:00
Lauri Ojansivu 1fdaa042cc Translate Bhojpuri Blockly editing and accessibility labels
Fill 39 English placeholders preserving existing text and tokens. Bitmap-column and inline-input wording remains low confidence. All 42 focused checks and 21 human-preference checks pass. Browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 02:49:05 +03:00
Lauri Ojansivu 3445d922e7 Document Bhojpuri loop translations
Thanks to xet7 !
2026-10-08 02:48:25 +03:00
Lauri Ojansivu dc9706282e Translate Bhojpuri Blockly loops and conditions
Fill 24 English placeholders preserving existing text and tokens. Loop and iteration terminology remains low confidence. All 41 focused checks and 21 human-preference checks pass. Browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 02:48:05 +03:00
Lauri Ojansivu 52085d8acc Document Bhojpuri Blockly translations
Thanks to xet7 !
2026-10-08 02:47:18 +03:00
Lauri Ojansivu 6db36bf895 Translate Bhojpuri Blockly controls and colours
Fill 20 English placeholders preserving existing translations and tokens. Variable and function terminology remains low confidence. All 40 focused checks and 21 human-preference checks pass. Browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 02:47:03 +03:00
Lauri Ojansivu c307d0a7e7 Document Kurdish recovery and keyboard translations
Thanks to xet7 !
2026-10-08 02:46:02 +03:00
Lauri Ojansivu 0b7a3ad75d Translate Kurdish recovery cancellation and keyboard labels
Fill 21 placeholders preserving cancellation caveats and physical key names. The remaining eleven default-inventory entries are product names, null and mathematical notation. Pending-Transifex content and wording review remain. All 41 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 02:45:46 +03:00
Lauri Ojansivu fd554cd4f2 Document Kurdish activity recovery translations
Thanks to xet7 !
2026-10-08 02:45:07 +03:00
Lauri Ojansivu c11366158b Translate Kurdish activity recovery and time estimates
Fill 26 English placeholders preserving existing text and tokens. Reservation and recovery-metadata terminology remains low confidence. All 40 focused checks and 21 human-preference checks pass. Browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 02:44:50 +03:00
Lauri Ojansivu 8e5cff7bf7 Document Kurdish diagnostics translations
Thanks to xet7 !
2026-10-08 02:43:56 +03:00
Lauri Ojansivu a5fad090ce Translate Kurdish Sync diagnostics and mail failure labels
Fill 21 English placeholders preserving existing text and tokens. Diagnostic and receipt terminology remains low confidence. All 42 focused checks and 21 human-preference checks pass. Browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 02:43:39 +03:00
Lauri Ojansivu ae65132bea Document Kurdish Sync report translations
Thanks to xet7 !
2026-10-08 02:42:54 +03:00
Lauri Ojansivu 19c3926deb Translate Kurdish Sync omissions and run reports
Fill 23 English placeholders preserving existing text and tokens. Parser, path and representation terminology remains low confidence. All 41 focused checks and 21 human-preference checks pass. Browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 02:42:38 +03:00
Lauri Ojansivu 174a5413d3 Document Kurdish Sync conflict translations
Thanks to xet7 !
2026-10-08 02:41:54 +03:00
Lauri Ojansivu 9f9ac3578a Translate Kurdish Sync conflicts and preview labels
Fill 26 English placeholders preserving existing translations and tokens. Mapping and baseline terminology remains low confidence. All 40 focused checks and 21 human-preference checks pass. Browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 02:41:37 +03:00
Lauri Ojansivu e69cb55ce0 Document Kurdish sprint report caveats
Thanks to xet7 !
2026-10-08 02:40:44 +03:00
Lauri Ojansivu 805768b315 Translate Kurdish sprint report caveats and confirmations
Fill 13 English placeholders preserving existing translations and tokens. Snapshot and observation terminology remains low confidence. All 42 focused checks and 21 human-preference checks pass. Browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 02:40:21 +03:00
Lauri Ojansivu 2191aa7c5b Document Kurdish sprint report translations
Thanks to xet7 !
2026-10-08 02:39:34 +03:00
Lauri Ojansivu 5279638c38 Translate Kurdish sprint report and event labels
Fill 35 English placeholders with tokens preserved. Retrospective and swimlane terminology remains low confidence. All 41 focused checks pass. Browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 02:39:13 +03:00
Lauri Ojansivu b593677af6 Document Kurdish Scrum planning translations
Thanks to xet7 !
2026-10-08 02:37:56 +03:00
Lauri Ojansivu 70af858ee7 Translate Kurdish Scrum settings and planning labels
Fill 36 English placeholders preserving existing text and tokens. Scrum-role and increment terminology remains low confidence. All 40 focused checks and 21 human-preference checks pass. Browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 02:37:40 +03:00
Lauri Ojansivu 991e305bd0 Document Kurdish rule-editor translations
Thanks to xet7 !
2026-10-08 02:36:58 +03:00
Lauri Ojansivu 0866c90b97 Translate Kurdish Blockly aliases and rule-editor messages
Fill 25 English placeholders preserving existing translations and tokens. Trigger and zoom terminology remains low confidence. All 51 focused checks and 21 human-preference checks pass. Browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 02:36:41 +03:00
Lauri Ojansivu b64a80d044 Document Kurdish replacement and workspace translations
Thanks to xet7 !
2026-10-08 02:35:58 +03:00
Lauri Ojansivu 4ecc063c0a Translate Kurdish Blockly replacement and workspace messages
Fill 36 English placeholders preserving existing text and tokens. Workspace-stack and search-focus terminology remains low confidence. All 50 focused checks and 21 human-preference checks pass. Browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 02:35:42 +03:00
Lauri Ojansivu 2b1782ab72 Document Kurdish text-operation translations
Thanks to xet7 !
2026-10-08 02:34:52 +03:00
Lauri Ojansivu 1b40c8c204 Translate Kurdish Blockly text operations
Fill 41 English placeholders preserving existing translations and tokens. Case-conversion and substring terminology remains low confidence. All 49 focused checks and 21 human-preference checks pass. Browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 02:34:35 +03:00
Lauri Ojansivu e5d4c8e980 Document Kurdish keyboard shortcut translations
Thanks to xet7 !
2026-10-08 02:33:50 +03:00
Lauri Ojansivu 438f317739 Translate Kurdish Blockly keyboard shortcuts and announcements
Fill 44 English placeholders preserving existing translations and tokens. Keyboard-focus and block-stack terminology remains low confidence. All 48 focused checks and 21 human-preference checks pass. Browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 02:33:33 +03:00
Lauri Ojansivu ffb2cfaea1 Document Kurdish function and variable translations
Thanks to xet7 !
2026-10-08 02:32:40 +03:00
Lauri Ojansivu 90b11c488d Translate Kurdish Blockly function and variable controls
Fill 40 English placeholders preserving tokens and existing translations. Parent-block and procedure-output terminology remains low confidence. All 47 focused checks and 21 human-preference checks pass. Browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 02:32:24 +03:00
Lauri Ojansivu c331be3b1b Document Kurdish statistical math translations
Thanks to xet7 !
2026-10-08 02:31:38 +03:00
Lauri Ojansivu 70a6a5f115 Translate Kurdish statistical and unary math instructions
Fill 39 English placeholders preserving tokens and symbolic function names. Statistical and trigonometric terminology remains low confidence. All 46 focused checks and 21 human-preference checks pass. Browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 02:31:21 +03:00
Lauri Ojansivu 6c1099ef3d Document Kurdish arithmetic and statistics translations
Thanks to xet7 !
2026-10-08 02:30:33 +03:00
Lauri Ojansivu b83566a83c Translate Kurdish Blockly arithmetic and statistics labels
Fill 40 English placeholders preserving formulas and tokens. Root, prime-number and standard-deviation terminology remains low confidence. All 45 focused checks and 21 human-preference checks pass. Browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 02:30:16 +03:00
Lauri Ojansivu 2aab957954 Document Kurdish sorting and logic translations
Thanks to xet7 !
2026-10-08 02:29:25 +03:00
Lauri Ojansivu a5ed81ad37 Translate Kurdish Blockly sorting and logic instructions
Fill 29 English placeholders, preserving existing translations and tokens. Case-insensitive sorting terminology remains low confidence. All 44 focused checks and 21 human-preference checks pass. Browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 02:29:09 +03:00
Lauri Ojansivu b0e88c5c63 Document Kurdish list-operation translations
Thanks to xet7 !
2026-10-08 02:28:22 +03:00
Lauri Ojansivu 62b71bc22d Translate Kurdish Blockly list operations
Fill 52 English placeholders while preserving existing translations and tokens. Index and sublist terminology remains low confidence. All 43 focused checks and 21 human-preference checks pass; browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 02:28:05 +03:00
Lauri Ojansivu a661e2d879 Document Kurdish input and navigation translations
Thanks to xet7 !
2026-10-08 02:27:16 +03:00
Lauri Ojansivu 880a9c5aee Translate Kurdish Blockly input and navigation labels
Fill 57 English placeholders with source tokens preserved. Statement-position, iteration and replacement terminology remains low confidence. All 42 focused checks and 21 human-preference checks pass. Browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 02:26:59 +03:00
Lauri Ojansivu dd6a664e78 Document Kurdish Blockly editing translations
Thanks to xet7 !
2026-10-08 02:26:07 +03:00
Lauri Ojansivu e50a3f5b87 Translate Kurdish Blockly editing and accessibility instructions
Fill 54 English placeholders while preserving existing translations and tokens. Inline input and bitmap terminology remains low confidence. All 41 focused checks and 21 human-preference checks pass. Browser and screen-reader checks were not run.

Thanks to xet7 !
2026-10-08 02:25:42 +03:00
Lauri Ojansivu 31ae4e3c34 Document Kurdish Blockly translations
Thanks to xet7 !
2026-10-08 02:24:44 +03:00
Lauri Ojansivu 89af4691e6 Translate Kurdish Blockly control and colour instructions
Fill 26 English placeholders while preserving existing translations and tokens. Loop and collapsed-block technical wording remains low confidence. All 40 focused checks and 21 human-preference checks pass; browser checks were not run.

Thanks to xet7 !
2026-10-08 02:24:27 +03:00
Lauri Ojansivu c2093f6f92 Document completed shortcut key coverage and remaining translation work
Thanks to xet7 !
2026-10-08 02:23:06 +03:00
Lauri Ojansivu 2ae4b4737c Fill the final eight missing due-date shortcut entries
Add Cherokee, Inuktitut, Ladin, Aromanian, Tigre, Arabic-script Uzbek, Wolaytta and Tamazight entries. All eight are low confidence pending fluent review. Focused catalog checks: 53 pass, zero failures. Human preference: 21 pass. Broader untranslated and wrong-language content remains.

Thanks to xet7 !
2026-10-08 02:22:41 +03:00
Lauri Ojansivu 5afb44bddf Document eight further shortcut translations
Thanks to xet7 !
2026-10-08 02:21:43 +03:00
Lauri Ojansivu 294759f773 Translate due-date shortcut in eight further languages
Add Acehnese, Aymara, Fulah, Greenlandic, Nahuatl, Neapolitan, Volapuk and Klingon text. These additions remain low confidence pending fluent review. Preserve existing translations, source order and tokens. Focused checks: 50 pass, two catalog failures remain. Human preference: 21 pass.

Thanks to xet7 !
2026-10-08 02:21:26 +03:00
Lauri Ojansivu 9a619dce4e Document eleven further shortcut translations
Thanks to xet7 !
2026-10-08 02:20:38 +03:00
Lauri Ojansivu 565dbccc50 Translate due-date shortcut in eleven further locales
Respect declared languages of legacy Venetian, Veps, Flemish and Waray tags. Preserve existing translations and source order. All additions except Flemish remain low confidence pending fluent review. Focused checks: 49 pass, two existing catalog failures. Human preference: 21 pass.

Thanks to xet7 !
2026-10-08 02:20:19 +03:00
Lauri Ojansivu b032b36a0a Document thirteen additional shortcut translations
Thanks to xet7 !
2026-10-08 02:19:28 +03:00
Lauri Ojansivu a4a575973a Translate due-date shortcut in thirteen further languages
Preserve existing translations and source key order. Chuvash, Ewe, Sakha, Silesian, Kashubian and Walloon wording remains low confidence. Focused checks: 48 pass, two catalog checks still fail on missing shortcuts elsewhere. Human preference: 21 pass.

Thanks to xet7 !
2026-10-08 02:19:09 +03:00
Lauri Ojansivu 211fc78223 Document twenty-three further shortcut translations
Thanks to xet7 !
2026-10-08 02:18:24 +03:00
Lauri Ojansivu cd96952a90 Translate due-date shortcuts in twenty-three further locales
Preserve existing translations and source key order. Buryat, Fijian, Konkani, Cornish, Kirundi, Swati and Tigrinya wording is low confidence pending fluent review. Focused checks: 47 pass and two existing catalog failures. Human preference: 21 pass.

Thanks to xet7 !
2026-10-08 02:18:03 +03:00
Lauri Ojansivu 3fa66966d0 Document twenty additional due-date shortcut translations
Thanks to xet7 !
2026-10-08 02:17:09 +03:00
Lauri Ojansivu 9a1d04e3ae Translate missing due-date shortcuts in twenty languages
Preserve existing translations and verify source key order and placeholders. Aragonese, Breton, Faroese, Friulian, Manx, Romansh, Sardinian and Igbo wording remains low confidence pending fluent review. Focused checks: 46 pass, two existing catalog failures for missing shortcuts elsewhere. Human preference: 21 pass.

Thanks to xet7 !
2026-10-08 02:16:49 +03:00
Lauri Ojansivu abb71bd031 Document five additional shortcut translations
Thanks to xet7 !
2026-10-08 02:15:50 +03:00
Lauri Ojansivu 58497dab4f Translate due-date shortcut in five further languages
Add Assamese, Odia, Bhojpuri, Maithili and Cantonese shortcut text. Preserve existing translations and source key order. Odia, Bhojpuri and Maithili wording is low confidence pending fluent review. Focused checks: 45 pass; two existing catalog checks still fail on missing shortcuts elsewhere. Human preference: 21 pass.

Thanks to xet7 !
2026-10-08 02:15:30 +03:00
Lauri Ojansivu 7c1e0e98a1 Document seven further due-date shortcut translations
Thanks to xet7 !
2026-10-08 02:13:19 +03:00
Lauri Ojansivu 5138f059cf Translate due-date shortcut in seven additional catalogs
Preserve existing values and source order; verify tokens and opened-card wording. Occitan and Moroccan Arabic phrasing is low confidence pending speaker review.

Thanks to xet7 !
2026-10-08 02:13:01 +03:00
Lauri Ojansivu e0777ffdd1 Document additional due-date shortcut translations
Thanks to xet7 !
2026-10-08 02:12:16 +03:00
Lauri Ojansivu af6c9a2dba Translate due-date shortcut in 14 additional languages
Restore locale inventories without changing existing values. Verify source tokens and open-card scope. Translations remain provisional pending speaker review.

Thanks to xet7 !
2026-10-08 02:11:54 +03:00
Lauri Ojansivu 1b47a734a9 Document due-date shortcuts for additional variants
Thanks to xet7 !
2026-10-08 02:11:08 +03:00
Lauri Ojansivu 03419aaca1 Add due-date shortcut translations to 16 variant catalogs
Restore key inventories and source order without changing existing values. Handle English variants with legacy locale identifiers and verify language correspondence.

Thanks to xet7 !
2026-10-08 02:10:49 +03:00
Lauri Ojansivu b5a3afdfa0 Document regional due-date shortcut translations
Thanks to xet7 !
2026-10-08 02:10:07 +03:00
Lauri Ojansivu bd52ecab15 Add due-date shortcut translations to 47 regional variants
Match corresponding language and script, preserve existing values and restore source key order. Verify locale inventories, tokens and base-language correspondence.

Thanks to xet7 !
2026-10-08 02:09:46 +03:00
Lauri Ojansivu c9ba36dd87 Document due-date shortcut additions to 14 locales
Thanks to xet7 !
2026-10-08 02:08:50 +03:00
Lauri Ojansivu 1734c4d295 Add due-date shortcut translations to 14 more locales
Restore locale inventories without changing existing values. Preserve British English wording and verify tokens and open-card scope. Wording remains subject to speaker review.

Thanks to xet7 !
2026-10-08 02:08:25 +03:00
Lauri Ojansivu 00a11ae5b6 Document due-date shortcuts for 26 more languages
Thanks to xet7 !
2026-10-08 02:07:39 +03:00
Lauri Ojansivu 2c25817c3a Translate the missing due-date shortcut in 26 more languages
Restore locale key inventories and source order without changing existing values. Verify tokens and opened-card scope in representative scripts; wording remains subject to speaker review.

Thanks to xet7 !
2026-10-08 02:07:20 +03:00
Lauri Ojansivu 9bc5294b18 Document due-date shortcut translations for 30 languages
Thanks to xet7 !
2026-10-08 02:06:21 +03:00
Lauri Ojansivu 52a7d0a330 Translate the missing due-date shortcut in 30 languages
Restore locale key inventories and source order without changing existing translation values. Verify tokens and opened-card scope in representative scripts.

Thanks to xet7 !
2026-10-08 02:06:04 +03:00
Lauri Ojansivu 49e34b2c74 Document Tok Pisin shortcut corrections
Thanks to xet7 !
2026-10-08 02:05:06 +03:00
Lauri Ojansivu 2df5d7efe3 Complete Tok Pisin shortcut key inventory and wording
Correct five shortcut labels and translate the missing due-date shortcut. Verify full locale key order, membership versus assignment and source tokens. Wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 02:04:48 +03:00
Lauri Ojansivu 011e87053b Document Tok Pisin history and notification corrections
Thanks to xet7 !
2026-10-08 02:04:01 +03:00
Lauri Ojansivu c4c409a9d9 Correct Tok Pisin history and notification descriptions
Translate six mixed-language values and verify recording versus notification behavior, export exclusions and administrator requirements. Wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 02:03:42 +03:00
Lauri Ojansivu 2b9d9cb432 Document Tok Pisin organization label corrections
Thanks to xet7 !
2026-10-08 02:02:49 +03:00
Lauri Ojansivu e4e1464942 Correct Tok Pisin organization and background labels
Translate eight mixed-language values and verify action scope, consistency and source tokens. Wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 02:02:30 +03:00
Lauri Ojansivu 7a0a4eec91 Document Tok Pisin avatar and repository corrections
Thanks to xet7 !
2026-10-08 02:01:36 +03:00
Lauri Ojansivu f7a353ba6b Correct Tok Pisin avatar and repository labels
Translate 15 mixed-language or misleading values and verify consistent avatar actions and WIP group meaning. Technical wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 02:01:18 +03:00
Lauri Ojansivu 45b4518654 Document remaining Tok Pisin rule corrections
Thanks to xet7 !
2026-10-08 02:00:40 +03:00
Lauri Ojansivu 54512789cf Correct remaining Tok Pisin rule controls
Translate 16 English or mixed-language values and verify completion actions, schedule periods and source tokens. Wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 02:00:22 +03:00
Lauri Ojansivu fb0fd3c2d8 Document Tok Pisin privacy control corrections
Thanks to xet7 !
2026-10-08 01:59:18 +03:00
Lauri Ojansivu 5cb9ae7277 Correct Tok Pisin privacy and support controls
Translate 15 mixed-language values and verify import/export directions, favorite actions and setting scope. Wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:58:58 +03:00
Lauri Ojansivu e727ac92e5 Document Tok Pisin scheduling and time corrections
Thanks to xet7 !
2026-10-08 01:58:11 +03:00
Lauri Ojansivu 7575667f71 Correct Tok Pisin scheduling and time labels
Translate 16 mixed-language values and verify scheduling failures, time distinctions and source tokens. Wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:57:48 +03:00
Lauri Ojansivu ac28378d1b Document Tok Pisin monitoring corrections
Thanks to xet7 !
2026-10-08 01:57:08 +03:00
Lauri Ojansivu 6b512864bf Correct Tok Pisin migration and monitoring labels
Translate 29 mixed-language values and verify storage targets, administrator limits and progress states. Technical wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:56:51 +03:00
Lauri Ojansivu 0d5568db8c Document Tok Pisin account lockout corrections
Thanks to xet7 !
2026-10-08 01:56:08 +03:00
Lauri Ojansivu 0e3401bea1 Correct Tok Pisin account lockout labels
Translate 15 mixed-language values and verify account distinctions, failure thresholds and unlocking scope. Wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:55:51 +03:00
Lauri Ojansivu 1eb82614b4 Document Tok Pisin import control corrections
Thanks to xet7 !
2026-10-08 01:55:06 +03:00
Lauri Ojansivu 86be4a3eee Correct Tok Pisin import controls and progress messages
Translate 21 mixed-language values and preserve external tool names, file types, URLs and source tokens. Wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:54:49 +03:00
Lauri Ojansivu ca1d9406f9 Document Tok Pisin form and card-aging corrections
Thanks to xet7 !
2026-10-08 01:54:05 +03:00
Lauri Ojansivu 8c77e8e2a0 Correct Tok Pisin form and card-aging labels
Translate 19 mixed-language or misleading values and verify fading levels, role limits and source tokens. Wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:53:47 +03:00
Lauri Ojansivu da1948aab1 Document Tok Pisin file and card control corrections
Thanks to xet7 !
2026-10-08 01:52:50 +03:00
Lauri Ojansivu 15f3676221 Correct remaining Tok Pisin file and card controls
Translate 40 mixed-language values and verify visibility markup, operation scope and source tokens. Technical wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:52:33 +03:00
Lauri Ojansivu 4be63fbfd3 Document Tok Pisin confirmation corrections
Thanks to xet7 !
2026-10-08 01:51:21 +03:00
Lauri Ojansivu fd3f8014eb Correct Tok Pisin confirmations and connection help
Translate 18 mixed-language values and verify action scope, connection labels and source tokens. Wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:51:02 +03:00
Lauri Ojansivu 9fc68e812e Document Tok Pisin location and selection corrections
Thanks to xet7 !
2026-10-08 01:50:03 +03:00
Lauri Ojansivu 0427ef00d0 Correct Tok Pisin location and selection labels
Translate 13 mixed-language values and verify location outcomes, assigned-only roles and source tokens. Wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:49:45 +03:00
Lauri Ojansivu abc9ebca64 Document Tok Pisin navigation and display corrections
Thanks to xet7 !
2026-10-08 01:48:55 +03:00
Lauri Ojansivu c81f8b78e8 Correct Tok Pisin navigation and display messages
Translate 19 mixed-language values and verify assigned-only visibility, toggle behavior and matching titles. Technical wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:48:32 +03:00
Lauri Ojansivu 488da395e1 Document Tok Pisin backup and scheduled-job corrections
Thanks to xet7 !
2026-10-08 01:47:46 +03:00
Lauri Ojansivu 8619392761 Correct Tok Pisin backup and scheduled-job messages
Translate 19 mixed-language values and verify restoration choices, schedule limits and source tokens. Technical wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:47:25 +03:00
Lauri Ojansivu 98b92e70e9 Document Tok Pisin migration and repair corrections
Thanks to xet7 !
2026-10-08 01:46:32 +03:00
Lauri Ojansivu aff335693b Correct Tok Pisin migration and repair messages
Translate 36 mixed-language values, restore the Snap database setting name and verify command syntax, exclusions and repair scope. Technical wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:46:13 +03:00
Lauri Ojansivu 8ccd92cbf2 Document Tok Pisin diagnostic corrections
Thanks to xet7 !
2026-10-08 01:45:22 +03:00
Lauri Ojansivu 3e747a1412 Correct Tok Pisin diagnostics and storage status messages
Translate 21 English or mixed-language values and preserve numeric limits, repair scope and source tokens. Heap context wording is low confidence; technical wording needs fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:45:05 +03:00
Lauri Ojansivu 56453b7f46 Document Tok Pisin customization corrections
Thanks to xet7 !
2026-10-08 01:44:08 +03:00
Lauri Ojansivu 229bfc5b6d Correct Tok Pisin customization labels
Translate 16 mixed-language values and verify HTML boundaries, logo settings and source tokens. Technical wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:43:51 +03:00
Lauri Ojansivu e13a8e1277 Document Tok Pisin policy and scheduling corrections
Thanks to xet7 !
2026-10-08 01:42:57 +03:00
Lauri Ojansivu 6c97ac7a01 Correct Tok Pisin policy and scheduling messages
Translate 18 mixed-language values and verify shortcut conditions, import restrictions and source tokens. Technical wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:42:32 +03:00
Lauri Ojansivu 6ab8a0b6a4 Document Tok Pisin layout and maintenance corrections
Thanks to xet7 !
2026-10-08 01:41:37 +03:00
Lauri Ojansivu 9c9e08d986 Correct Tok Pisin layout and maintenance messages
Translate 21 mixed-language values and verify loading limits, storage states and maintenance behavior. Technical wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:41:20 +03:00
Lauri Ojansivu 260f51799c Document Tok Pisin cloud storage corrections
Thanks to xet7 !
2026-10-08 01:40:30 +03:00
Lauri Ojansivu 24e9e0543d Correct Tok Pisin cloud storage settings and instructions
Translate 26 mixed-language values and preserve external console labels, credential options and JSON identifiers. Technical wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:40:11 +03:00
Lauri Ojansivu 30ed009439 Document Tok Pisin storage translations
Thanks to xet7 !
2026-10-08 01:39:09 +03:00
Lauri Ojansivu 13b32af727 Translate Tok Pisin S3 and Sandstorm storage messages
Correct 38 English or mixed-language strings and restore literal paths and external menu labels. Technical wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:38:52 +03:00
Lauri Ojansivu efccb5615f Document Tok Pisin control and reporting corrections
Thanks to xet7 !
2026-10-08 01:37:38 +03:00
Lauri Ojansivu 17c98d0e10 Correct Tok Pisin control and reporting messages
Translate 22 mixed-language values and verify report scope, account status and template tokens. Technical wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:37:21 +03:00
Lauri Ojansivu b917e26354 Document Tok Pisin access and import corrections
Thanks to xet7 !
2026-10-08 01:36:26 +03:00
Lauri Ojansivu 10dfe9775c Correct Tok Pisin access and import messages
Translate 29 mixed-language values and verify permission limits, import identifiers and due-date distinctions. Technical wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:36:09 +03:00
Lauri Ojansivu adc77b02ec Document Tok Pisin administration corrections
Thanks to xet7 !
2026-10-08 01:34:55 +03:00
Lauri Ojansivu 14872ede15 Correct Tok Pisin administration and privacy descriptions
Translate 16 mixed-language or incorrect values, repair account anonymization confirmation and preserve configuration literals. Technical wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:34:37 +03:00
Lauri Ojansivu 1215ff00bd Document Tok Pisin help and migration corrections
Thanks to xet7 !
2026-10-08 01:33:19 +03:00
Lauri Ojansivu f8dd8a4129 Correct Tok Pisin help and migration instructions
Translate 25 mixed-language messages, preserve command examples and verify restore scope and warnings. Technical wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:33:03 +03:00
Lauri Ojansivu eb4c22eaf0 Document Tok Pisin label and compact-card corrections
Thanks to xet7 !
2026-10-08 01:31:35 +03:00
Lauri Ojansivu fd05f864f9 Correct Tok Pisin label and compact-card messages
Translate 18 mixed-language values and verify label actions, deletion warnings, file limits and source tokens. Wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:31:16 +03:00
Lauri Ojansivu b19449767b Document Tok Pisin filter and sorting corrections
Thanks to xet7 !
2026-10-08 01:30:30 +03:00
Lauri Ojansivu e9b744b1d6 Correct Tok Pisin filter and sorting labels
Translate 34 mixed-language or misleading labels, restore alphabetical endpoints and verify source tokens. Wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:30:08 +03:00
Lauri Ojansivu 4d6535f37f Document Tok Pisin visibility and sharing corrections
Thanks to xet7 !
2026-10-08 01:29:04 +03:00
Lauri Ojansivu 928f2148fe Correct Tok Pisin visibility and sharing messages
Translate 18 mixed-language or misleading messages and verify access descriptions, template scope, markup and source tokens. Wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:27:15 +03:00
Lauri Ojansivu c563202771 Document Tok Pisin error corrections
Thanks to xet7 !
2026-10-08 01:26:28 +03:00
Lauri Ojansivu 38f3f75a92 Correct Tok Pisin error messages
Translate 22 mixed-language errors and verify restrictions, validation, recovery instructions and source tokens. Wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:26:05 +03:00
Lauri Ojansivu ab0b13dbf8 Document Tok Pisin rule-action corrections
Thanks to xet7 !
2026-10-08 01:25:04 +03:00
Lauri Ojansivu 652496480f Correct Tok Pisin rule actions and conditions
Translate 25 mixed-language messages and verify movement scope, checklist actions, timing and placeholders. Wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:24:46 +03:00
Lauri Ojansivu be8cebc5bb Document Tok Pisin rule-builder corrections
Thanks to xet7 !
2026-10-08 01:23:54 +03:00
Lauri Ojansivu 3ff37b1511 Correct Tok Pisin rule-builder messages
Translate 32 mixed-language messages and verify trigger directions, import limitations and source tokens. Technical wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:23:32 +03:00
Lauri Ojansivu 23f0020824 Document Tok Pisin movement corrections
Thanks to xet7 !
2026-10-08 01:22:35 +03:00
Lauri Ojansivu 50b8617c46 Correct Tok Pisin movement and copy messages
Translate 24 mixed-language or incomplete messages and verify directions, scope, optional input and JSON examples. Wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:22:19 +03:00
Lauri Ojansivu c1f631fcbb Document Tok Pisin date and time corrections
Thanks to xet7 !
2026-10-08 01:21:14 +03:00
Lauri Ojansivu 648280808f Correct Tok Pisin date and time messages
Translate 19 mixed-language or ambiguous messages and verify dates, units, timestamp direction and source tokens. Wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:20:57 +03:00
Lauri Ojansivu 636add47f8 Document Tok Pisin role message corrections
Thanks to xet7 !
2026-10-08 01:19:55 +03:00
Lauri Ojansivu 0148a44c22 Correct Tok Pisin role and membership messages
Translate 19 mixed-language messages and verify permission boundaries, membership restrictions and activation actions. Wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:19:33 +03:00
Lauri Ojansivu c7c8b18495 Document Tok Pisin checklist corrections
Thanks to xet7 !
2026-10-08 01:18:48 +03:00
Lauri Ojansivu 3e4ed286d5 Correct Tok Pisin checklist and subtask messages
Translate 18 mixed-language or misleading messages and verify filters, order, counters and disabled defaults. Wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:18:29 +03:00
Lauri Ojansivu b9bd961e04 Document Tok Pisin notification corrections
Thanks to xet7 !
2026-10-08 01:17:42 +03:00
Lauri Ojansivu f464d15f5a Correct Tok Pisin notification messages
Translate 10 mixed-language messages and verify subscription scope, read states, reminder timing and source tokens. Wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:17:20 +03:00
Lauri Ojansivu 91a59b68f9 Document Tok Pisin custom-field corrections
Thanks to xet7 !
2026-10-08 01:16:26 +03:00
Lauri Ojansivu 17a37ef989 Correct Tok Pisin custom-field messages
Translate 13 mixed-language or mistranslated messages and verify deletion scope, formatting literals and source tokens. Wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:16:05 +03:00
Lauri Ojansivu b5d6e9d4fa Document Tok Pisin deletion message corrections
Thanks to xet7 !
2026-10-08 01:15:15 +03:00
Lauri Ojansivu dec92e7d37 Correct Tok Pisin deletion and restoration messages
Translate 24 mixed-language warnings and verify restrictions, irreversible actions, restoration counts and placeholders. Wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:14:59 +03:00
Lauri Ojansivu 48d53d25a2 Document Tok Pisin export message corrections
Thanks to xet7 !
2026-10-08 01:14:07 +03:00
Lauri Ojansivu e8c0a1c4ef Correct Tok Pisin export options and instructions
Translate 17 mixed-language messages, restore Jira API and Trello menu literals and verify export fields. Wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:13:42 +03:00
Lauri Ojansivu 569f414794 Document Tok Pisin import message corrections
Thanks to xet7 !
2026-10-08 01:12:31 +03:00
Lauri Ojansivu ad33767527 Correct Tok Pisin import instructions and errors
Translate 29 mixed-language messages, restore literal schema names and verify placeholders, archive errors and member mapping. Technical wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:12:12 +03:00
Lauri Ojansivu 9b29a37bd2 Document Tok Pisin file-handling corrections
Thanks to xet7 !
2026-10-08 01:10:52 +03:00
Lauri Ojansivu 3848a8d7f9 Correct Tok Pisin file-handling translations
Translate 25 mixed-language messages and verify transfer directions, limits, units and repair descriptions. Technical wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:10:32 +03:00
Lauri Ojansivu 3ec4286d25 Document Tok Pisin account message corrections
Thanks to xet7 !
2026-10-08 01:09:34 +03:00
Lauri Ojansivu b3c7b03222 Correct Tok Pisin account and invitation messages
Translate 34 mixed-language messages, preserve tokens, restore email paragraphs and verify validation meanings. Wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:09:15 +03:00
Lauri Ojansivu 145b3c36e6 Document Tok Pisin search-help corrections
Thanks to xet7 !
2026-10-08 01:07:47 +03:00
Lauri Ojansivu 8c9f34a3e6 Correct mixed English in Tok Pisin search help
Translate 40 search instructions and errors, preserve source tokens and verify filter semantics. Wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:07:16 +03:00
Lauri Ojansivu 761f3e878e Document Tok Pisin analytics wording corrections
Thanks to xet7 !
2026-10-08 01:05:48 +03:00
Lauri Ojansivu 70c93974a7 Correct Tok Pisin analytics translations
Replace the final 27 English-seeded prefix values and verify forecast limits, history gaps, fallback meanings and source tokens. Statistical wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:05:28 +03:00
Lauri Ojansivu 1e1cf900f4 Document Tok Pisin colour wording corrections
Thanks to xet7 !
2026-10-08 01:04:14 +03:00
Lauri Ojansivu 09bb20da8c Correct Tok Pisin colour translations
Replace 25 prefixed English labels with basic colour names and distinct shade descriptions. Less common shade descriptions remain low confidence pending fluent-speaker and visual review.

Thanks to xet7 !
2026-10-08 01:03:55 +03:00
Lauri Ojansivu 76a24fb69b Document Tok Pisin search wording corrections
Thanks to xet7 !
2026-10-08 01:02:50 +03:00
Lauri Ojansivu 087d779df9 Correct Tok Pisin search keyword translations
Replace 21 prefixed English values with parser-compatible keywords and verify operator uniqueness and source tokens. Compact forms and technical loans remain low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:02:27 +03:00
Lauri Ojansivu 5de8e5106f Document Tok Pisin general wording corrections
Thanks to xet7 !
2026-10-08 01:00:56 +03:00
Lauri Ojansivu 9e5ac7722d Correct Tok Pisin general control translations
Replace 68 mixed-language labels and verify controls, support wording, units and source tokens. Technical wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 01:00:33 +03:00
Lauri Ojansivu 2fed4e839e Document Tok Pisin card wording corrections
Thanks to xet7 !
2026-10-08 00:59:21 +03:00
Lauri Ojansivu f746e8e683 Correct Tok Pisin card and account translations
Replace 57 mixed-language labels and verify placeholders, account actions, ticket states and coordinate directions. Technical wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 00:59:02 +03:00
Lauri Ojansivu 43022b4b76 Document Tok Pisin system wording corrections
Thanks to xet7 !
2026-10-08 00:58:06 +03:00
Lauri Ojansivu efeb64ca3b Correct Tok Pisin system and connection translations
Replace 48 mixed-language labels and preserve configuration identifiers, memory metrics and connection distinctions. Technical wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 00:57:48 +03:00
Lauri Ojansivu b963d96204 Document Tok Pisin reporting wording corrections
Thanks to xet7 !
2026-10-08 00:56:35 +03:00
Lauri Ojansivu 913f291ada Correct Tok Pisin reporting and monitoring translations
Replace 41 mixed-language labels and verify chronology, queues, concurrent limits and source tokens. Technical wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 00:56:08 +03:00
Lauri Ojansivu a8c0569ab6 Document Tok Pisin storage wording corrections
Thanks to xet7 !
2026-10-08 00:55:07 +03:00
Lauri Ojansivu 81535636ef Correct Tok Pisin storage and migration translations
Replace 56 mixed-language labels and verify state changes, backup scope, units and source tokens. Technical wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 00:54:48 +03:00
Lauri Ojansivu 19f29437af Document Tok Pisin rule wording corrections
Thanks to xet7 !
2026-10-08 00:53:48 +03:00
Lauri Ojansivu 825a004b4f Correct Tok Pisin rule and scheduling translations
Replace 74 mixed-language values and verify recurrence, reminders, checklist actions and source tokens. Technical wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 00:53:27 +03:00
Lauri Ojansivu 3f19d1f3aa Document Tok Pisin interface wording corrections
Thanks to xet7 !
2026-10-08 00:52:12 +03:00
Lauri Ojansivu 8d9ebb2fa1 Correct prefixed English in Tok Pisin interface labels
Replace 83 mixed-language labels and verify states, time units, invitation labels and source tokens. Wording remains provisional pending fluent-speaker review; 417 other prefixed values remain for audit.

Thanks to xet7 !
2026-10-08 00:51:42 +03:00
Lauri Ojansivu 2f279ace76 Document Tok Pisin maths translations and remaining inventory
Thanks to xet7 !
2026-10-08 00:50:18 +03:00
Lauri Ojansivu 09b1db010f Translate Tok Pisin Blockly maths messages
Preserve placeholders, constants, limits and calculation semantics. Advanced mathematical loan terms and paraphrases are low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 00:49:56 +03:00
Lauri Ojansivu 4a33d05bcf Document Tok Pisin editing translations and remaining inventory
Thanks to xet7 !
2026-10-08 00:48:07 +03:00
Lauri Ojansivu 6bf4e05a42 Translate Tok Pisin editing and accessibility commands
Preserve placeholder inventories and distinguish deletion, copy and paste, screen-reader states and navigation directions. Technical wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 00:47:49 +03:00
Lauri Ojansivu 012aaa9180 Document Tok Pisin input translations and remaining inventory
Thanks to xet7 !
2026-10-08 00:46:33 +03:00
Lauri Ojansivu 88980db882 Translate Tok Pisin Blockly input and field labels
Preserve placeholders and distinguish bitmap states, input positions and arithmetic operands. Technical wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 00:46:08 +03:00
Lauri Ojansivu f4ff5338a6 Document Tok Pisin workspace translations and remaining inventory
Thanks to xet7 !
2026-10-08 00:44:50 +03:00
Lauri Ojansivu 9f1df3c9ad Translate Tok Pisin variables and workspace navigation
Translate colour, variable, keyboard navigation and workspace messages with placeholder and semantic regression coverage. Technical wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 00:44:28 +03:00
Lauri Ojansivu 755de613b3 Document Tok Pisin list translations and remaining inventory
Thanks to xet7 !
2026-10-08 00:43:04 +03:00
Lauri Ojansivu 33714c2e82 Translate Tok Pisin Blockly list operations
Preserve indexing, removal, insertion and list transformation semantics with regression coverage. Technical wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 00:42:45 +03:00
Lauri Ojansivu db0d7cabaa Document Tok Pisin loop translations and remaining inventory
Thanks to xet7 !
2026-10-08 00:41:28 +03:00
Lauri Ojansivu c2e74b8712 Translate Tok Pisin loop controls
Translate loop and conditional messages and verify branch semantics and placeholders. Technical wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 00:41:05 +03:00
Lauri Ojansivu 850e5b30f3 Record Tok Pisin logic translation coverage
Thanks to xet7 !
2026-10-08 00:38:29 +03:00
Lauri Ojansivu 5bd8ced1f7 Translate Tok Pisin Blockly logic and functions
Translate 50 values and preserve condition boundaries and function return behavior. Technical wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 00:38:05 +03:00
Lauri Ojansivu 9743b9f27a Record Tok Pisin text translation coverage
Thanks to xet7 !
2026-10-08 00:36:13 +03:00
Lauri Ojansivu d0233d3b3f Translate Tok Pisin Blockly text operations
Fill 55 English messages and preserve source tokens, search/replacement operands, case distinctions and trim directions. Technical wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 00:35:47 +03:00
Lauri Ojansivu cf0dd9b378 Record Tok Pisin Scrum translation coverage
Thanks to xet7 !
2026-10-08 00:34:00 +03:00
Lauri Ojansivu 5d5129e8d4 Translate Tok Pisin Scrum planning and reports
Fill 84 English messages and preserve source tokens, sprint membership and report caveats. Technical wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 00:33:31 +03:00
Lauri Ojansivu 87762bac8b Record Tok Pisin synchronization translation coverage
Thanks to xet7 !
2026-10-08 00:31:53 +03:00
Lauri Ojansivu bb3ce92dd8 Translate Tok Pisin synchronization messages
Fill 63 English messages and preserve conflict choices, retained content, report limits and estimate mappings. Technical wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 00:31:28 +03:00
Lauri Ojansivu 5a8e7d5834 Record Tok Pisin recovery translation coverage
Thanks to xet7 !
2026-10-08 00:29:49 +03:00
Lauri Ojansivu d0f781c349 Translate Tok Pisin recovery and rule editing
Fill 46 English messages and correct two English-prefixed rule labels. Preserve delivery uncertainty and cancellation limits; technical recovery wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 00:29:28 +03:00
Lauri Ojansivu 70fc70e808 Record Māori mathematics translation coverage
Thanks to xet7 !
2026-10-08 00:27:44 +03:00
Lauri Ojansivu 445aaa85e8 Translate Māori Blockly mathematics
Fill 86 English messages and preserve formula literals, statistical operations and interval boundaries. Compound wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 00:27:25 +03:00
Lauri Ojansivu 0a5ce14ea9 Record Māori editing translation coverage
Thanks to xet7 !
2026-10-08 00:25:14 +03:00
Lauri Ojansivu 3c6051128e Translate Māori Blockly editing and accessibility
Fill 46 English messages and preserve editing actions and accessibility state announcements. Technical wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 00:24:55 +03:00
Lauri Ojansivu 401b388d84 Record Māori input translation coverage
Thanks to xet7 !
2026-10-08 00:23:43 +03:00
Lauri Ojansivu 2b4ceae5d9 Translate Māori Blockly input and bitmap labels
Translate 61 values and preserve pixel states, operand roles, coordinates and shared labels. Accessibility compounds remain provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 00:23:19 +03:00
Lauri Ojansivu b3cae7f1ce Record Māori workspace translation coverage
Thanks to xet7 !
2026-10-08 00:22:03 +03:00
Lauri Ojansivu 68aed9007e Translate Māori Blockly workspace and navigation
Fill 82 English messages and preserve counts, search shortcuts, colour ranges and movement directions. Accessibility compounds remain provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 00:21:40 +03:00
Lauri Ojansivu 35c71e4075 README: 171 languages are essentially complete now.
tests/newLanguageWiring.test.cjs counts the locales whose translations are
essentially complete and failed on README's 167: translation work since
then completed four more.

Thanks to xet7 !
2026-10-08 00:20:36 +03:00
Lauri Ojansivu a98c221fa9 buildScriptParity: changelog-wrap.mjs is run by hand, not from a menu.
The new releases/changelog-wrap.mjs failed 'scripts build.sh cannot run'.
Like changelog-archive.mjs it is an editing aid with nothing for a build
menu to do: the format test names it when CHANGELOG lines run long. It is
recorded in the suite's list of by-hand scripts with that reason.

Thanks to xet7 !
2026-10-08 00:20:30 +03:00
Lauri Ojansivu 2f415fc108 Record Māori list translation coverage
Thanks to xet7 !
2026-10-08 00:20:03 +03:00
Lauri Ojansivu a558558467 Translate Māori Blockly list operations
Translate 75 values and preserve indexing, destructive action distinctions, copy semantics and separator behavior. Technical wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 00:19:42 +03:00
Lauri Ojansivu d51154462c continuousBackup test: stop the file engine on failure, and give watching 20 s.
Run 3 of EVERYTHING: under load the appended log line reached the file
stream after the test's 5 s wait (4 puts, not 5), and the failed
assertion then left the engine's seal and scan intervals running, so the
suite sat until its 300 s timeout. The engine is now stopped in t.after
whatever happens, and watching gets 20 s; the scan interval stays at an
hour, so the change is still proved to come from watching. Passes alone.

Thanks to xet7 !
2026-10-08 00:18:38 +03:00
Lauri Ojansivu 0e75991ceb Record Māori control and variable translation coverage
Thanks to xet7 !
2026-10-08 00:18:06 +03:00
Lauri Ojansivu 03b2bfa69d Translate Māori Blockly controls and variables
Translate 55 values and preserve loop conditions, variable types and deletion warnings. Technical wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 00:17:45 +03:00
Lauri Ojansivu d2bb28cf0f Open Upcoming with the CodeQL test fix, and never wrap a subsection header.
v12.22 was released, so the CodeQL fix 433e58a618 starts a new
"# Upcoming WeKan ® release" section, under the established header
"This release fixes the following SECURITY ISSUES found by GitHub CodeQL
code scanning:".

That header is 86 characters, and releases/changelog-wrap.mjs wrapped it
onto two lines - after which tests/changelogFormat.test.cjs no longer found
it as the first subsection header. Subsection headers ("This release ...:",
"and ...:") are one line by rule, so the wrap tool leaves them alone and the
format test does not count them as over-long; tests/changelogWrap.test.cjs
pins it.

Thanks to xet7 !
2026-10-08 00:17:23 +03:00
Lauri Ojansivu 433e58a618 Tests pass shell paths through the environment, not into bash -c scripts.
GitHub CodeQL code scanning alerts #549 and #550
(js/shell-command-injection-from-environment): tests/snapSettingsKeys and
tests/snapSocketDenied ran `bash -c` with a script that interpolated an
absolute path built from __dirname (`source "${configPath}"`,
`source "${helper}"`). A checkout path containing a quote, `$(` or a
backtick would change the command that runs. tests/linuxPackageManagers
and tests/snapCaddyNofile6552 had the same shape without an alert yet.

All four now pass the path (and the soft limit, and the wekan-control
function under test) in environment variables, so every script text is
constant. tests/testShellPathsFromEnv.test.cjs fails on any test that
sources an interpolated value, and checks it catches the flagged forms.
These are test files run on a developer machine or CI, so no attack can be
attempted against a running WeKan and nothing is logged in Admin Panel ->
Problems.

Thanks to GitHub CodeQL and xet7 !
2026-10-08 00:16:24 +03:00
Lauri Ojansivu 5704d598da Record Māori logic and function translation coverage
Thanks to xet7 !
2026-10-08 00:15:53 +03:00
Lauri Ojansivu ce8bbf7455 Translate Māori Blockly logic and functions
Translate 50 values with token and condition-boundary checks. Technical wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 00:15:33 +03:00
Lauri Ojansivu 485d42745d Record Māori Blockly text translation coverage
Thanks to xet7 !
2026-10-08 00:14:06 +03:00
Lauri Ojansivu 49feb5123e Translate Māori Blockly text operations
Fill 55 English messages and preserve tokens, search/replacement operands, case distinctions and trim directions. Technical wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 00:13:45 +03:00
Lauri Ojansivu d8dd040c85 Record Māori recovery translation coverage
Thanks to xet7 !
2026-10-08 00:12:11 +03:00
Lauri Ojansivu 1d9176bc01 Translate Māori activity recovery and rule editing
Fill 46 English messages while preserving notification-delivery uncertainty, cancellation limits and rule-editor constraints. Technical recovery wording remains provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 00:11:50 +03:00
Lauri Ojansivu aa4690516a Record Māori planning and synchronization coverage
Thanks to xet7 !
2026-10-08 00:10:11 +03:00
Lauri Ojansivu 8b381e19de Translate Māori Scrum and synchronization messages
Fill 147 English placeholders and cover source tokens, report limits, conflict choices and estimate mappings. Technical compounds remain provisional pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 00:09:46 +03:00
github-actions[bot] 0926049b7e Bump versions for v12.22 2026-10-07 21:07:58 +00:00
Lauri Ojansivu 93ed4e3fd3 Prepare v12.22 release 2026-10-08 00:06:07 +03:00
Lauri Ojansivu fa9a55ef90 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-10-08 00:05:36 +03:00
Lauri Ojansivu 3dd40f61f2 Translate Northern Sotho Blockly mathematics
Fill 86 English messages with token and mathematical-boundary regression coverage. Statistical compounds and trigonometric wording remain low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-08 00:05:11 +03:00
Lauri Ojansivu 3c5282f2ef Record Northern Sotho block-editing translations and refresh the translation status.
Thanks to xet7 !
2026-10-08 00:04:06 +03:00
Lauri Ojansivu 34418e3cce Add today marker in calendars to ChangeLog.
Thanks to mimZD and xet7 !
2026-10-08 00:02:45 +03:00
Lauri Ojansivu c7431dceab Changelog: D keyboard shortcut for the opened card's due date.
Thanks to mimZD and xet7 !
2026-10-08 00:02:12 +03:00
Lauri Ojansivu 519fb2c105 Mark today in the date popup calendar and the board Calendar view.
The due/start/end date popup's calendar now gives today's day button the
is-today class and aria-current="date", and its cell is-today-cell. Today
gets a ring and underline in the button's own text colour plus a subtle cell
tint, so it stays visible in every board colour theme, light and dark, and
in RTL. The board Calendar view keeps FullCalendar's today tint and also
marks today's day number with the board theme's colour.

Thanks to mimZD and xet7 !

Fixes #6751,
2026-10-08 00:01:56 +03:00
Lauri Ojansivu 56a4dabd1c Translate Northern Sotho block editing and accessibility
Fill 46 English placeholders and preserve editing actions and announcement tokens. Accessibility and backpack terminology needs fluent-speaker review.

Thanks to xet7 !
2026-10-08 00:01:52 +03:00
Lauri Ojansivu 784d529003 Add the d keyboard shortcut to open the opened card's Due date editor.
With keyboard shortcuts enabled, d (or Shift+D) clicks the due date + or
badge of the opened card, so it opens the same popup a click does and only
for a user who may edit the card. Inputs, textareas and editable fields are
left alone by the shared shortcut filter. Listed in the shortcuts help.

Thanks to mimZD and xet7 !

Fixes #6750,
2026-10-08 00:01:44 +03:00
Lauri Ojansivu 167bb01af9 Changelog: card filter access fix, browser spec fixes, the wrap tool and two translation notes.
Records 479f530da0 (filter matches follow access even when the board
observer stalls) under the bug fixes and in In short, the browser spec
fixes and releases/changelog-wrap.mjs under Tests, and the Tigre date and
Veps kuva commits under translations.

Thanks to xet7 !
2026-10-08 00:01:39 +03:00
Lauri Ojansivu b68eadebaa Record Northern Sotho input translations
Thanks to xet7 !
2026-10-07 23:58:46 +03:00
Lauri Ojansivu 57a5280664 Translate Northern Sotho Blockly input and bitmap labels
Translate 61 English values and verify states, coordinates, operand roles and tokens. Two catalog checks still encounter the concurrent shortcut key addition. Technical wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 23:58:21 +03:00
Lauri Ojansivu 5de9f418a8 WebKit specs: open the filter until it opens, and read SAML state after the last navigation.
saved-filter-presets clicked the filter toggle once, right after setting
the language; while the board header was still re-rendering the click
opened nothing and the filter field was never found. It now clicks until
the field is visible. saml-redirect-login read sessionStorage while WebKit
was still finishing the exchange's final navigation ("Execution context was
destroyed"); it now waits for the load and reads it again, at most three
times, and only for that error. Both specs passed in all 139 WebKit runs
against a server built from current main.

Thanks to xet7 !
2026-10-07 23:58:00 +03:00
Lauri Ojansivu ea7b6e382d Record Northern Sotho colour and navigation translations
Thanks to xet7 !
2026-10-07 23:56:54 +03:00
Lauri Ojansivu 4965535f8d Translate Northern Sotho Blockly colours and navigation
Fill 59 English placeholders and verify colour ranges, directions and movement states. Two catalog checks still encounter the concurrent due-date shortcut addition. Wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 23:56:32 +03:00
Lauri Ojansivu d7300b33e5 Record Northern Sotho workspace translations
Thanks to xet7 !
2026-10-07 23:55:03 +03:00
Lauri Ojansivu 02ac6af8c0 Translate Northern Sotho Blockly workspace messages
Fill 23 English placeholders and verify counts, keyboard navigation and editing actions. Two catalog checks still fail on the concurrent due-date shortcut addition. Technical wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 23:54:35 +03:00
Lauri Ojansivu 8bc9a8e80c Record Northern Sotho list translations and catalog gap
Thanks to xet7 !
2026-10-07 23:53:27 +03:00
Lauri Ojansivu 7c27073ac1 Translate Northern Sotho Blockly list operations
Translate 75 English values and test retrieval, removal, copies and ordering. 53 checks pass; two structural checks encounter the concurrently added shortcut-edit-due-date key missing from other catalogs. Technical wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 23:53:01 +03:00
Lauri Ojansivu 89c287927d Record Northern Sotho variable translations
Thanks to xet7 !
2026-10-07 23:51:11 +03:00
Lauri Ojansivu 360697a4f0 Translate Northern Sotho Blockly variables
Fill 22 English placeholders and verify name conflicts, deletion counts, rename scope and parameter tokens. Technical wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 23:50:47 +03:00
Lauri Ojansivu f2ce023b64 Record Northern Sotho control-flow translations
Thanks to xet7 !
2026-10-07 23:49:49 +03:00
Lauri Ojansivu 2c6d2b48d5 Translate Northern Sotho Blockly control flow
Translate 33 English values including seven short if/do labels. Verify loop conditions, continuation and source arguments. Technical wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 23:49:26 +03:00
Lauri Ojansivu 83e4bfe950 Record Northern Sotho function translations
Thanks to xet7 !
2026-10-07 23:48:11 +03:00
Lauri Ojansivu bc4dca3309 Translate Northern Sotho Blockly functions
Translate 24 English values and verify parameter tokens, output distinctions and disabled-definition warnings. Technical wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 23:47:50 +03:00
Lauri Ojansivu 70c35d5d62 Record Northern Sotho logic translations
Thanks to xet7 !
2026-10-07 23:46:33 +03:00
Lauri Ojansivu 8bfa28a515 Translate Northern Sotho Blockly logic
Translate 26 English values and verify truth conditions, comparison boundaries and interpolation tokens. Technical wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 23:46:11 +03:00
Lauri Ojansivu 479f530da0 Board filter matches re-check the board's access, not only its observer.
card-text-filter failed intermittently in the EVERYTHING runs: after a
member's assigned-only restriction was lifted, the text-filter matches
never widened again (still one match after 100 s). Instrumented on the
polling test server, the failing subscription's board observer delivered
its initial add and then nothing - neither the narrowing nor the widening
write - while the card observers of the same board kept firing; the
narrowing only applied because a card edit happened to trigger a rescan.
With nothing after the widening, nothing rescanned. The same stall would
also have kept a NARROWED member's old matches, which is an access leak.

publishBoardMatches (text matches, movement matches, table pages) now also
re-reads the board every 10 s and rescans when its access fields -
permission, members and the provider's own board fields - differ from
the ones the published result was computed from, unreadable boards
included. An unchanged board does not rescan.
tests/boardTextPublication.test.cjs widens, narrows and revokes with the
observer silent and only the re-check firing; both tests fail on the
previous code. card-text-filter passed 3 of 3 in Firefox afterwards.

Thanks to xet7 !
2026-10-07 23:46:07 +03:00
Lauri Ojansivu e7017ca745 Record Northern Sotho text translations
Thanks to xet7 !
2026-10-07 23:45:12 +03:00
Lauri Ojansivu 591e23ac5a Translate Northern Sotho Blockly text operations
Fill 55 English placeholders and verify argument inventories, indexing, replacement roles and text formatting. Technical wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 23:44:52 +03:00
Lauri Ojansivu 446f7e2869 Record Northern Sotho Scrum translations
Thanks to xet7 !
2026-10-07 23:43:22 +03:00
Lauri Ojansivu 679e49a509 Translate Northern Sotho Scrum planning and reports
Fill 84 English placeholders and verify lifecycle states, report limits and source tokens across 102 Scrum messages. Technical wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 23:42:53 +03:00
Lauri Ojansivu 308985df8c Record Northern Sotho Sync report translations
Thanks to xet7 !
2026-10-07 23:40:51 +03:00
Lauri Ojansivu 34206768f6 Translate Northern Sotho Sync reports and estimates
Fill 20 English placeholders and verify retention, permissions, recovery limits and explicit null semantics. Technical wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 23:40:27 +03:00
Lauri Ojansivu ef8d7a2c19 Record Northern Sotho Sync translations
Thanks to xet7 !
2026-10-07 23:39:06 +03:00
Lauri Ojansivu 045b0c9eca Translate Northern Sotho Sync conflicts and previews
Fill 43 English placeholders and verify local content retention, review scope and preview limits. Technical wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 23:38:41 +03:00
Lauri Ojansivu dedbf7eebc Record Northern Sotho recovery translations
Thanks to xet7 !
2026-10-07 23:37:09 +03:00
Lauri Ojansivu 60f69b7a80 Translate Northern Sotho rule editing and recovery
Fill 46 English placeholders and verify rule constraints, delivery uncertainty and irreversible cancellation. Technical wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 23:36:47 +03:00
Lauri Ojansivu 3e78175425 Record Somali mathematics translations and remaining backlog
Thanks to xet7 !
2026-10-07 23:35:04 +03:00
Lauri Ojansivu 07702ee6df Translate Somali Blockly mathematics
Fill 86 English placeholders while preserving formulas and constants. Verify numerical bounds, units, statistical operations and arithmetic distinctions. Mathematical wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 23:34:42 +03:00
Lauri Ojansivu e41fdbc43f Record Somali control translations and remaining backlog
Thanks to xet7 !
2026-10-07 23:32:22 +03:00
Lauri Ojansivu 2f9edc078d Translate Somali Blockly controls and announcements
Fill 26 English placeholders and verify open/close states, screen-reader toggles and conditional labels. Accessibility wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 23:31:56 +03:00
Lauri Ojansivu fd0b57e40a Record Somali input translations and remaining backlog
Thanks to xet7 !
2026-10-07 23:30:48 +03:00
Lauri Ojansivu cb908f51d2 Translate Somali Blockly input and bitmap labels
Translate 61 English values including the short on label outside the placeholder count. Verify operand roles, coordinates, pixel states and argument inventories. Technical wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 23:30:25 +03:00
Lauri Ojansivu 0b9383a607 Record Somali navigation translations and remaining backlog
Thanks to xet7 !
2026-10-07 23:28:50 +03:00
Lauri Ojansivu 8739ce5e17 Translate Somali Blockly keyboard navigation
Fill 45 English placeholders and verify directions, shortcut arguments and move-state distinctions. Accessibility wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 23:28:05 +03:00
Lauri Ojansivu aae2bca030 Record Somali workspace translations and remaining backlog
Thanks to xet7 !
2026-10-07 23:25:25 +03:00
Lauri Ojansivu 15426a5f11 Translate Somali Blockly variables and workspace controls
Fill 45 English placeholders and verify names, counts, navigation keys and editing distinctions. Technical wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 23:24:46 +03:00
Lauri Ojansivu 2f33d3bbf2 Record Somali text translations and remaining backlog
Thanks to xet7 !
2026-10-07 23:21:50 +03:00
Lauri Ojansivu 69c9be897b Translate Somali Blockly text operations
Fill 55 English placeholders and verify interpolation tokens, indexing, case conversion, replacement and whitespace handling. Technical wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 23:21:11 +03:00
Lauri Ojansivu 9817d33920 Record Somali logic translations and remaining backlog
Thanks to xet7 !
2026-10-07 23:18:11 +03:00
Lauri Ojansivu 935704e5c1 Translate Somali Blockly logic and functions
Translate 50 English values, including three short labels outside the placeholder count. Preserve null and test boolean conditions, function outputs and warnings. Technical wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 23:17:31 +03:00
Lauri Ojansivu 4e009b9c13 Record Somali list translations and remaining backlog
Thanks to xet7 !
2026-10-07 23:14:19 +03:00
Lauri Ojansivu 37525e3cee Translate Somali Blockly list operations
Translate 75 English values including two short fragments excluded from placeholder counts. Verify indexing, removal, copies, sort and conversion direction. Technical wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 23:13:35 +03:00
Lauri Ojansivu 99e7857ab2 Record Somali Blockly translations and remaining backlog
Thanks to xet7 !
2026-10-07 23:08:54 +03:00
Lauri Ojansivu 4415e8b4d4 Translate Somali Blockly editing and control flow
Fill 61 English placeholders and add regression checks for source tokens, loop conditions, variable deletion and colour ranges. Programming terms remain low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 23:08:15 +03:00
Lauri Ojansivu aaf8d6613b Record Somali recovery translations and remaining backlog
Thanks to xet7 !
2026-10-07 23:04:47 +03:00
Lauri Ojansivu d5744bafb8 Translate Somali rule editing and notification recovery
Fill 46 English placeholders and verify rule constraints, delivery uncertainty and recovery limitations. Technical compounds remain low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 23:04:06 +03:00
Lauri Ojansivu a8806fa1a9 Record Somali Sync translations and remaining backlog
Thanks to xet7 !
2026-10-07 23:00:55 +03:00
Lauri Ojansivu a370ad87e5 Translate Somali Sync conflicts and reports
Fill 63 English placeholders while preserving existing translations. Add regression checks for local content retention, report limits, recovery restrictions and missing versus null estimates. Technical compounds remain low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 23:00:05 +03:00
Lauri Ojansivu 61d5fbd6f4 Record Somali Scrum translations and remaining backlog
Thanks to xet7 !
2026-10-07 22:55:56 +03:00
Lauri Ojansivu d6600d9345 Translate Somali Scrum planning and reporting messages
Fill 84 English placeholders while preserving existing translations. Cover all 102 Scrum and related view messages with token and semantic regression checks. Technical compounds remain low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 22:55:17 +03:00
Lauri Ojansivu 5b787612ee Record Akan action and account corrections
Thanks to xet7 !
2026-10-07 22:50:07 +03:00
Lauri Ojansivu 89d9e0f131 Correct Akan action and account labels
Preserve Meteor branding, account creation wording and timing distinctions in 17 mixed-language labels. Add placeholder and semantic regression checks. Akan wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 22:49:32 +03:00
Lauri Ojansivu 19140ea038 Record Akan migration label corrections
Thanks to xet7 !
2026-10-07 22:42:54 +03:00
Lauri Ojansivu 0dfafdd3d6 Correct Akan migration labels
Replace 14 mixed-language values while preserving warning/error and execution-state distinctions. Technical wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 22:42:13 +03:00
Lauri Ojansivu 6a82b3c939 Record Akan navigation and swimlane corrections
Thanks to xet7 !
2026-10-07 22:40:11 +03:00
Lauri Ojansivu 51da67e877 Correct Akan navigation and swimlane messages
Replace 18 mixed-language values while preserving move/copy distinctions and home removal without deletion. Wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 22:39:32 +03:00
Lauri Ojansivu ca7b3adc2a Record Akan template and swimlane corrections
Thanks to xet7 !
2026-10-07 22:37:09 +03:00
Lauri Ojansivu ada36f426b Correct Akan template and swimlane labels
Replace 18 mixed-language values while preserving template targets, insertion direction and resize permissions. Wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 22:36:29 +03:00
Lauri Ojansivu b0c1abcfff Record Akan prompt and font sample corrections
Thanks to xet7 !
2026-10-07 22:34:34 +03:00
Lauri Ojansivu ea7df95fc5 Correct Akan prompts and font sample
Replace 10 mixed-language values while preserving membership restrictions and preview digits. Font sample is translated without claiming pangram coverage. Wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 22:33:54 +03:00
Lauri Ojansivu ffaeb6432b Record Akan voting and invitation corrections
Thanks to xet7 !
2026-10-07 22:31:34 +03:00
Lauri Ojansivu 8f927b07bb Correct Akan voting and invitation labels
Replace 12 mixed-language values while preserving inviter tokens, confirmations and provider endpoint alternatives. Wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 22:30:56 +03:00
Lauri Ojansivu 6d7172535d Record Akan rule fragment and history corrections
Thanks to xet7 !
2026-10-07 22:29:02 +03:00
Lauri Ojansivu fdd4a301db Correct Akan rule fragments and history states
Replace 18 malformed or mixed-language values while preserving direction, permissions and pending states. Wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 22:28:08 +03:00
Lauri Ojansivu 2d8816e536 Record Akan conversion and authorization corrections
Thanks to xet7 !
2026-10-07 22:26:13 +03:00
Lauri Ojansivu bc356adf21 Correct Akan conversion and authorization messages
Replace 14 mixed-language values while preserving import formats and distinct permission requirements. Wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 22:25:35 +03:00
Lauri Ojansivu 94f974f149 Record Akan checklist and activity corrections
Thanks to xet7 !
2026-10-07 22:23:29 +03:00
Lauri Ojansivu 39620087ca Correct Akan checklist and activity labels
Replace 21 mixed-language values while preserving activity tokens, completion sound defaults and avatar units. Wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 22:22:51 +03:00
Lauri Ojansivu 3571438cd2 Record Akan activity and assignment corrections
Thanks to xet7 !
2026-10-07 22:20:54 +03:00
Lauri Ojansivu 92684bde59 Correct Akan activity and assignment labels
Replace 25 mixed-language values while preserving activity tokens, action direction and assigned-only scope. Wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 22:20:18 +03:00
Lauri Ojansivu 10e34490cb Record Akan results and scheduling corrections
Thanks to xet7 !
2026-10-07 22:17:38 +03:00
Lauri Ojansivu 2d7f4743da Correct Akan results and scheduling messages
Replace 26 mixed-language values while preserving result counts, missing-only restoration and scheduling states. Wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 22:17:00 +03:00
Lauri Ojansivu 0a0a845d67 Record Akan view and customization corrections
Thanks to xet7 !
2026-10-07 22:14:30 +03:00
Lauri Ojansivu e2c0cb513d Correct Akan view and customization labels
Replace 25 mixed-language values while preserving visibility scope and technical filenames. Align matching view titles. Wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 22:13:53 +03:00
Lauri Ojansivu eae1a3a6f0 Record Akan display and attachment corrections
Thanks to xet7 !
2026-10-07 22:11:30 +03:00
Lauri Ojansivu 035fba3d42 Correct Akan display and attachment labels
Replace 23 mixed-language values while preserving units, links and assigned-only visibility. Align the Normal role label. Wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 22:10:56 +03:00
Lauri Ojansivu 0451ce3751 Record Akan account and connection corrections
Thanks to xet7 !
2026-10-07 22:08:28 +03:00
Lauri Ojansivu 03b327fcf9 Correct Akan account and connection guidance
Replace 15 mixed-language or misleading values and correct OAuth setting precedence. Preserve secret visibility and account states. Wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 22:07:46 +03:00
Lauri Ojansivu 90dcffd40e Record Akan movement and ordering corrections
Thanks to xet7 !
2026-10-07 22:05:40 +03:00
Lauri Ojansivu 937086b9fb Correct Akan movement and ordering labels
Replace 25 mixed-language values while preserving directions, list scope, ordering and provider menu names. Wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 22:05:04 +03:00
Lauri Ojansivu 1886afba9f Record Akan status and count corrections
Thanks to xet7 !
2026-10-07 22:02:42 +03:00
Lauri Ojansivu c5c1d2bca9 Correct Akan status and count messages
Replace 23 mixed-language or misleading values while preserving units, batch limits and permission scope. Wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 22:02:04 +03:00
Lauri Ojansivu cb316a4db3 Record Akan timing and completion corrections
Thanks to xet7 !
2026-10-07 21:59:46 +03:00
Lauri Ojansivu 61674384bf Correct Akan timing and completion messages
Replace 24 mixed-language values while preserving date distinctions, old/new activity values and timeout guidance. Wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 21:59:10 +03:00
Lauri Ojansivu 8509800115 Record Akan grouping and field summary corrections
Thanks to xet7 !
2026-10-07 21:57:06 +03:00
Lauri Ojansivu 0afec6037c Correct Akan grouping and field summary messages
Replace 18 mixed-language or misleading values while preserving aggregates, grouping actions and synchronization interval. Wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 21:56:29 +03:00
Lauri Ojansivu 09c67fd327 map-view: reopen the Board View popup if switching language closed it.
The WebKit run failed map-view in or_IN and pap on a 15 s click timeout
on the Map item of the Board View popup. Each test switches the language
right before opening the popup, and applying it re-renders the header,
which can close the popup between the two clicks. openMap now opens it
again, up to three times, until the item is clicked. All 68 locale runs
passed in WebKit with two workers.

Thanks to xet7 !
2026-10-07 21:55:18 +03:00
Lauri Ojansivu e2c0c3f130 Record Akan field and rule corrections
Thanks to xet7 !
2026-10-07 21:54:09 +03:00
Lauri Ojansivu 0991c3971e Correct Akan field and rule messages
Replace 20 mixed-language or misleading values while preserving timeline restore scope and administrator visibility. Wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 21:53:31 +03:00
Lauri Ojansivu 325a9ca375 Record Akan drag and account corrections
Thanks to xet7 !
2026-10-07 21:51:26 +03:00
Lauri Ojansivu af1499151c Correct Akan drag and account guidance
Replace 22 mixed-language or misleading values while preserving optional inputs, menu names and workspace tokens. Technical terminology remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 21:50:48 +03:00
Lauri Ojansivu da1f0359ec Record Akan repair and monitoring corrections
Thanks to xet7 !
2026-10-07 21:48:10 +03:00
Lauri Ojansivu 80d6628502 Correct Akan repair and monitoring labels
Replace 24 damaged or mixed-language values while preserving repair scope and technical identifiers. Technical terminology remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 21:47:17 +03:00
Lauri Ojansivu ae743c95ca Record Akan cloud storage corrections
Thanks to xet7 !
2026-10-07 21:45:11 +03:00
Lauri Ojansivu df69b20ffc Correct Akan cloud storage messages
Replace 25 damaged or mixed-language values and restore provider names, navigation labels and permission roles from the source. Technical terminology remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 21:44:27 +03:00
Lauri Ojansivu 20794257ad Record Akan support and lockout corrections
Thanks to xet7 !
2026-10-07 21:42:14 +03:00
Lauri Ojansivu be0032f2ed Correct Akan support and lockout labels
Replace 24 damaged or mixed-language values while preserving credential distinctions and storage states. Technical terminology remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 21:41:37 +03:00
Lauri Ojansivu 32377e0aae Record Akan sorting and report corrections
Thanks to xet7 !
2026-10-07 21:39:25 +03:00
Lauri Ojansivu d6d82b0023 Correct Akan sorting and report labels
Replace 24 damaged or mixed-language values and restore literal search syntax. Preserve format placeholders and HTML entities. Wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 21:38:48 +03:00
Lauri Ojansivu d79fc0cc79 Record Akan workflow and sorting corrections
Thanks to xet7 !
2026-10-07 21:36:54 +03:00
Lauri Ojansivu 9ec083daa1 Correct Akan workflow and sorting labels
Replace 24 damaged or misleading values while preserving toggle actions, archive direction and literal markup. Wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 21:36:17 +03:00
Lauri Ojansivu c23965130a Record Akan upload and diagnostics corrections
Thanks to xet7 !
2026-10-07 21:34:21 +03:00
Lauri Ojansivu d209fb83af Correct Akan upload and diagnostics labels
Replace 24 damaged or mixed-language values while preserving protocols and configuration identifiers. Technical terminology remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 21:33:45 +03:00
Lauri Ojansivu 34dcec5d74 Record Akan import and invitation corrections
Thanks to xet7 !
2026-10-07 21:31:42 +03:00
Lauri Ojansivu 6d2be5215a Correct Akan import and invitation messages
Replace 28 damaged or mixed-language values and restore the OpenProject endpoint. Preserve tokens and action meanings. Wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 21:31:04 +03:00
Lauri Ojansivu 4e4d8c579d Record Akan archive and color corrections
Thanks to xet7 !
2026-10-07 21:28:47 +03:00
Lauri Ojansivu ed00230a89 Correct Akan archive and color labels
Replace 25 damaged or mixed-language values while preserving archive targets and activity tokens. Wording remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 21:27:53 +03:00
Lauri Ojansivu d8f5a22750 Record Akan setting and action corrections
Thanks to xet7 !
2026-10-07 21:25:55 +03:00
Lauri Ojansivu efb8ce7837 Correct Akan setting and action labels
Replace 21 mixed-language values while preserving WIP alternatives, date meanings and empty-field matching. Technical terminology remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 21:25:17 +03:00
Lauri Ojansivu 72eeb050d7 Record Akan board and account corrections
Thanks to xet7 !
2026-10-07 21:23:18 +03:00
Lauri Ojansivu b3ea485bcf Correct Akan board controls and account emails
Replace 23 mixed-language or misleading values and preserve placeholders and opposite actions. Technical terminology remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 21:22:41 +03:00
Lauri Ojansivu 8e34894e7b Record Akan storage guidance corrections
Thanks to xet7 !
2026-10-07 21:20:02 +03:00
Lauri Ojansivu 13631ec0ce Correct Akan storage guidance and repair identifiers
Replace 20 mixed-language messages while preserving providers, configuration alternatives and repair identifiers. Technical terminology remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 21:19:24 +03:00
Lauri Ojansivu eebf84518b Record Akan file and memory corrections
Thanks to xet7 !
2026-10-07 21:17:12 +03:00
Lauri Ojansivu 052f48895e Correct Akan file and memory labels
Replace 24 mixed-language values and restore product names while preserving migration scope. Memory terminology remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 21:16:35 +03:00
Lauri Ojansivu 4cffdf4e4a Record Akan rule and search corrections
Thanks to xet7 !
2026-10-07 21:14:13 +03:00
Lauri Ojansivu 69a2b5fe30 Correct Akan rule and search messages
Replace 23 mixed-language values while preserving query syntax and opposite actions. Technical terminology remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 21:13:36 +03:00
Lauri Ojansivu 994e01a994 Firefox specs: wait for stable layout, saved settings and the recovery scan.
Four Firefox failures in the EVERYTHING run, each passing alone:

- minicard-checklist-reorder measured the drag source and target right
  after resizing the viewport, while the board was still re-laying itself
  out, and the drop missed. It now measures once both boxes hold still.
- admin-login-menu waited the default 15 s for the "clear stored secret"
  control after Save; it now first asserts the typed secret is still in
  the field (so a re-render that wiped it fails clearly) and allows 30 s
  for the save and the form's reload.
- activity-notification-recovery clicked Retry right after re-enabling the
  recipient, which is also when the one-second automatic recovery may take
  the intent and disable the button. It now accepts either path; the
  checks that matter stay: completed, exactly one email job, and a
  repeated retry is skipped.
- card-text-filter's raw-driver edits reach the page on the next poll; its
  checks get two poll cycles (40 s) instead of 15 s. Note: one step in it -
  the text-match stream widening back after a member's assigned-only
  restriction is lifted - also fails intermittently for a reason that is NOT
  timing (still one match after 100 s); that is investigated separately and
  the test is left to catch it.

Thanks to xet7 !
2026-10-07 21:12:39 +03:00
Lauri Ojansivu c4513b6c5b Record Akan card and import corrections
Thanks to xet7 !
2026-10-07 21:11:26 +03:00
Lauri Ojansivu 0dec93d704 Correct Akan card and import controls
Replace 28 mixed-language messages, preserving JSON keys, navigation labels and selection scope. Technical terminology remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 21:10:47 +03:00
Lauri Ojansivu 5989213363 Record Akan controls and storage corrections
Thanks to xet7 !
2026-10-07 21:08:10 +03:00
Lauri Ojansivu 303f5a5c49 Correct Akan controls and storage messages
Replace 29 mixed-language values and restore provider menu labels. Preserve input units and credential retention. Technical terminology remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 21:07:32 +03:00
Lauri Ojansivu 8a2c1bba6d Record Akan migration and deletion corrections
Thanks to xet7 !
2026-10-07 21:04:48 +03:00
Lauri Ojansivu 102c8ad390 Correct Akan migration and deletion messages
Replace 30 mixed-language values, preserving tokens, confirmation targets and migration outcomes. Technical terminology remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 21:04:11 +03:00
Lauri Ojansivu 80e63f3e7b Record Akan input and scheduled-job corrections
Thanks to xet7 !
2026-10-07 21:01:36 +03:00
Lauri Ojansivu 42da9d05ba Correct Akan input and scheduled-job messages
Replace 22 mixed-language values and preserve input limits and distinct scheduling outcomes. Technical terminology remains low confidence pending fluent-speaker review.

Thanks to xet7 !
2026-10-07 21:00:57 +03:00
Lauri Ojansivu ed8669e29e Record Akan display and selection corrections
Thanks to xet7 !
2026-10-07 20:58:21 +03:00
Lauri Ojansivu 771cf10c53 Correct Akan display toggles and selection messages
Rewrite 22 mixed-language entries while preserving state/action distinctions, one-board selection and numeric examples. Display terminology remains provisional.

Thanks to xet7 !
2026-10-07 20:53:07 +03:00
Lauri Ojansivu b529a67ff7 Record Akan embedded filler corrections
Thanks to xet7 !
2026-10-07 20:50:45 +03:00
Lauri Ojansivu 339a425511 Replace embedded Akan filler in activities and reports
Correct 40 values containing generic filler inside longer strings. Preserve report limits, activity tokens and date fallbacks. Statistical and forecasting wording remains low confidence.

Thanks to xet7 !
2026-10-07 20:50:05 +03:00
Lauri Ojansivu 936b4a6754 Record Akan account and repair result corrections
Thanks to xet7 !
2026-10-07 20:46:56 +03:00
Lauri Ojansivu fdee0c6fb3 Correct Akan account and repair result messages
Rewrite 20 mixed-language values and preserve temporary lockouts, partial repair counts, import formats and query syntax. Flow-history wording remains provisional.

Thanks to xet7 !
2026-10-07 20:46:11 +03:00
Lauri Ojansivu b53e457b82 Record Akan backup scope corrections
Thanks to xet7 !
2026-10-07 20:43:53 +03:00
Lauri Ojansivu 72333e3bd2 Correct Akan backup scope and migration descriptions
Rewrite 16 mixed-language values and preserve organization ownership, deletion restrictions and background behavior. Backup and migration wording remains provisional.

Thanks to xet7 !
2026-10-07 20:43:04 +03:00
Lauri Ojansivu e338d2e0e8 Record Akan account and loading corrections
Thanks to xet7 !
2026-10-07 20:40:00 +03:00
Lauri Ojansivu 53b178bb16 Correct Akan account states and card loading guidance
Rewrite 14 mixed-language entries while preserving opposite controls, loading configuration and experimental view limitations. Technical wording remains provisional.

Thanks to xet7 !
2026-10-07 20:39:06 +03:00
Lauri Ojansivu 6c92ea531a Record Akan storage maintenance corrections
Thanks to xet7 !
2026-10-07 20:35:58 +03:00
Lauri Ojansivu b79ac288c2 Correct Akan storage maintenance and support messages
Rewrite 14 mixed-language values while preserving compaction ordering, storage scope and support restrictions. Technical terms remain provisional.

Thanks to xet7 !
2026-10-07 20:34:59 +03:00
Lauri Ojansivu d61a1739d2 Record Akan diagnostic description corrections
Thanks to xet7 !
2026-10-07 20:31:50 +03:00
Lauri Ojansivu c174c06c92 Correct Akan diagnostic and report descriptions
Rewrite 18 mixed-language messages and preserve log commands, API settings, report scope and membership deletion guards. Specialized wording remains provisional.

Thanks to xet7 !
2026-10-07 20:30:51 +03:00
Lauri Ojansivu 4ab9236b65 Record Akan search help corrections
Thanks to xet7 !
2026-10-07 20:27:58 +03:00
Lauri Ojansivu 8b9845e4db Correct Akan search operator help
Rewrite 16 mixed-language descriptions while preserving operator syntax, placeholders and examples. Add semantics checks; search wording remains provisional.

Thanks to xet7 !
2026-10-07 20:27:03 +03:00
Lauri Ojansivu 2afdeeb922 Record Akan rule and reminder corrections
Thanks to xet7 !
2026-10-07 20:23:54 +03:00
Lauri Ojansivu a97cdfd445 Correct Akan rule imports and due reminders
Rewrite 30 mixed-language triggers, confirmations and search descriptions. Preserve source arguments, technical names, due states and permission scope. Specialized wording remains provisional.

Thanks to xet7 !
2026-10-07 20:22:59 +03:00
Lauri Ojansivu 2fc49df4c4 Record Akan invitation and membership corrections
Thanks to xet7 !
2026-10-07 20:20:15 +03:00
Lauri Ojansivu 8f254da4ec Correct Akan invitations and membership notices
Rewrite 24 mixed-language values and preserve invitation formatting, permission scope and separate Sandstorm access controls. Specialized membership and storage wording remains provisional.

Thanks to xet7 !
2026-10-07 20:19:28 +03:00
Lauri Ojansivu 21e22217e6 Record Akan filter and import corrections
Thanks to xet7 !
2026-10-07 20:17:05 +03:00
Lauri Ojansivu 367a9b2041 Correct Akan filter and import instructions
Rewrite 24 mixed-language values and restore JSON fields, API paths, spreadsheet headers and filter examples. Technical wording remains provisional.

Thanks to xet7 !
2026-10-07 20:16:22 +03:00
Lauri Ojansivu 6c74f42495 Record Akan permission and error corrections
Thanks to xet7 !
2026-10-07 20:13:31 +03:00
Lauri Ojansivu 5cc289611b Correct Akan permission and account error messages
Rewrite 24 mixed-language values and preserve access restrictions, count tokens and format identifiers. Role and soft-deletion wording remains provisional.

Thanks to xet7 !
2026-10-07 20:12:46 +03:00
Lauri Ojansivu 285e7d4133 Record Akan import and archive corrections
Thanks to xet7 !
2026-10-07 20:10:32 +03:00
Lauri Ojansivu 1bacb18aaa Correct Akan import activities and archive guidance
Rewrite 24 mixed-language values and fix the account anonymization title. Preserve activity targets, restoration behavior and mapping permission limits. Administrative wording remains provisional.

Thanks to xet7 !
2026-10-07 20:09:37 +03:00
Lauri Ojansivu d53dcc214a Record Akan migration confirmation corrections
Thanks to xet7 !
2026-10-07 20:07:14 +03:00
Lauri Ojansivu e47100c4b2 Correct Akan migration confirmations and diagnostics
Rewrite 12 mixed-language messages, preserving scope restrictions, numeric limits, identifiers and diagnostic quotes. Migration wording remains provisional.

Thanks to xet7 !
2026-10-07 20:06:25 +03:00
Lauri Ojansivu f7c1f9ee38 Record Akan display and data-control corrections
Thanks to xet7 !
2026-10-07 20:04:10 +03:00
Lauri Ojansivu ab532cc151 Correct Akan display and data-control descriptions
Rewrite 12 mixed-language settings and restore product names and literal examples. Verify defaults and feature scope; privacy and rich-text terminology remains provisional.

Thanks to xet7 !
2026-10-07 20:03:29 +03:00
Lauri Ojansivu bce3578035 Record Akan search and migration description corrections
Thanks to xet7 !
2026-10-07 20:00:46 +03:00
Lauri Ojansivu d05730226a Correct Akan search and migration guidance
Rewrite 15 mixed-language descriptions and restore damaged technical names and paths. Preserve examples and placeholders with regression coverage; specialized wording remains provisional.

Thanks to xet7 !
2026-10-07 20:00:01 +03:00
Lauri Ojansivu 983278529e Record Akan configuration translation corrections
Thanks to xet7 !
2026-10-07 19:57:20 +03:00
Lauri Ojansivu 291c84d5b1 Correct Akan configuration and rule trigger messages
Rewrite 24 mixed-language values, restore a damaged example domain and preserve activity placeholders and deletion conditions. Authentication and tenant wording remains provisional.

Thanks to xet7 !
2026-10-07 19:56:38 +03:00
Lauri Ojansivu 113be3975a Record Akan board warning corrections
Thanks to xet7 !
2026-10-07 19:54:13 +03:00
Lauri Ojansivu 857961d5d1 Correct mixed-language Akan board warnings
Rewrite 32 deletion, privacy, import and notification messages. Preserve source tokens and HTML and update board restore guidance. Administrative wording remains provisional.

Thanks to xet7 !
2026-10-07 19:53:33 +03:00
Lauri Ojansivu 665c77cd3a Record removal of generic Akan filler and remaining review
Thanks to xet7 !
2026-10-07 19:51:00 +03:00
Lauri Ojansivu f1a2ccfcc0 Replace remaining generic Akan status labels
Correct 99 filler values, preserve schedule intervals and technical identifiers, and reject the repeated phrase across the catalog. Migration and diagnostic terminology remains provisional.

Thanks to xet7 !
2026-10-07 19:50:21 +03:00
Lauri Ojansivu d31b83b339 Record Akan backup corrections and remaining audit
Thanks to xet7 !
2026-10-07 19:47:58 +03:00
Lauri Ojansivu 7bb67bc8a9 Correct Akan backup and migration translations
Replace 60 generic filler values. Preserve cloud identifiers, replacement scope and distinct migration controls. Technical terminology remains provisional.

Thanks to xet7 !
2026-10-07 19:47:24 +03:00
Lauri Ojansivu bd6e0b7e01 Record Akan storage corrections and remaining audit
Thanks to xet7 !
2026-10-07 19:45:38 +03:00
Lauri Ojansivu d254baf5a1 Correct Akan storage and account control translations
Replace 60 generic filler values and preserve storage names, units, IDs and opposite states. Add regression coverage; compaction and migration wording remains provisional.

Thanks to xet7 !
2026-10-07 19:45:07 +03:00
Lauri Ojansivu 40961715e8 Record Akan dependency corrections and remaining audit
Thanks to xet7 !
2026-10-07 19:43:08 +03:00
Lauri Ojansivu 088a3f1ef7 Correct Akan dependency and report labels
Replace 60 generic filler values. Preserve inverse relations, first and last timestamps, literal identifiers and distinct wait indicators. Specialized wording remains provisional.

Thanks to xet7 !
2026-10-07 19:42:30 +03:00
Lauri Ojansivu 44ecb39bc3 Record Akan search corrections and remaining audit work
Thanks to xet7 !
2026-10-07 19:40:25 +03:00
Lauri Ojansivu 91f7a5eba0 Correct Akan search operators and error messages
Replace 49 generic or mixed-language values, restore aliases and verify translated searches with the query parser. Compound technical search terms remain low confidence.

Thanks to xet7 !
2026-10-07 19:39:54 +03:00
Lauri Ojansivu d28a24cc1d Record Akan calendar corrections and remaining audit
Thanks to xet7 !
2026-10-07 19:38:14 +03:00
Lauri Ojansivu d53166480f Correct Akan calendar and settings translations
Replace 43 generic filler values and restore protocol identifiers and context separator. Cover weekdays, read states and shared labels; specialized wording remains provisional.

Thanks to xet7 !
2026-10-07 19:37:46 +03:00
Lauri Ojansivu 3b67c7eb7e Record Akan rule corrections and remaining audit work
Thanks to xet7 !
2026-10-07 19:36:14 +03:00
Lauri Ojansivu c2d88822cb Correct Akan rule schedules and action labels
Replace 60 generic filler values. Preserve cadence, weekday scope, time units and opposite checklist controls. Rule fragments remain provisional pending contextual review.

Thanks to xet7 !
2026-10-07 19:35:49 +03:00
Lauri Ojansivu 628a92e5c7 Record Akan administration corrections and remaining audit
Thanks to xet7 !
2026-10-07 19:34:12 +03:00
Lauri Ojansivu 183ba55791 Correct Akan administration and diagnostic translations
Replace 60 generic filler values. Preserve units and technical identifiers, distinguish modes and dates, and document provisional system terminology.

Thanks to xet7 !
2026-10-07 19:33:35 +03:00
Lauri Ojansivu 47975127a9 Record Akan color and field corrections
Thanks to xet7 !
2026-10-07 19:31:59 +03:00
Lauri Ojansivu d4ab5c8ad1 Correct Akan colors and account control translations
Replace 60 generic filler values and restore empty and literal labels. Verify color distinctions, units and placeholders. Uncommon shade, version and WIP wording remains provisional.

Thanks to xet7 !
2026-10-07 19:31:30 +03:00
Lauri Ojansivu 258cf372a8 Record Akan filler corrections and remaining audit work
Thanks to xet7 !
2026-10-07 19:29:35 +03:00
Lauri Ojansivu 51ba1fa103 Replace generic Akan board and display translations
Correct 60 unrelated filler values, restore literal poker choices and preserve control distinctions. Add regression checks; theme, dependency and template wording remains provisional.

Thanks to xet7 !
2026-10-07 19:29:03 +03:00
Lauri Ojansivu a63ed9009a Record Akan recovery translations and remaining coverage
Thanks to xet7 !
2026-10-07 19:27:13 +03:00
Lauri Ojansivu abdb3a92d7 Translate Akan notification recovery messages
Fill 27 messages while preserving retained work, no activity recreation and permanent cancellation behavior. Add regression checks and record provisional recovery wording.

Thanks to xet7 !
2026-10-07 19:26:44 +03:00
Lauri Ojansivu 215617bc34 Record Akan report translations and remaining coverage
Thanks to xet7 !
2026-10-07 19:25:10 +03:00
Lauri Ojansivu 40f6eefbc4 Translate Akan synchronization reports and email failures
Preserve retention limits, distinct outcomes, hour units and missing versus null estimate semantics. Add regression checks and document provisional diagnostics wording.

Thanks to xet7 !
2026-10-07 19:24:45 +03:00
Lauri Ojansivu b5302749c2 Record Akan synchronization translations and remaining coverage
Thanks to xet7 !
2026-10-07 19:23:04 +03:00
Lauri Ojansivu b47d761311 Translate Akan synchronization conflicts and previews
Fill 43 messages and preserve local content, unchanged subcards, retry reuse and display limits. Add regression coverage and document provisional technical wording.

Thanks to xet7 !
2026-10-07 19:22:31 +03:00
Lauri Ojansivu f3a0836611 Record Akan sprint report translations and remaining coverage
Thanks to xet7 !
2026-10-07 19:20:37 +03:00
Lauri Ojansivu fcd7871303 Translate Akan sprint reports and completion messages
Preserve unknown estimates, daily observation limits, partial reports and separate close and cancel behavior. Add regression coverage and note provisional terminology.

Thanks to xet7 !
2026-10-07 19:20:09 +03:00
Lauri Ojansivu e05c1509ca Record Akan planning translations and remaining coverage
Thanks to xet7 !
2026-10-07 19:18:12 +03:00
Lauri Ojansivu 44a707acf5 Translate Akan rule editing and Scrum planning controls
Fill 54 placeholders and correct the generic rules label. Cover arguments, distinct sprint actions and shared view labels; document provisional terminology.

Thanks to xet7 !
2026-10-07 19:17:46 +03:00
Lauri Ojansivu dfa43e3fa2 Record Akan workspace translations and remaining coverage
Thanks to xet7 !
2026-10-07 19:15:43 +03:00
Lauri Ojansivu 7085c23c55 Translate Akan Blockly variables and workspace controls
Preserve variable arguments, workspace counts, search shortcuts and shared labels. Add regression checks and document remaining contextual review.

Thanks to xet7 !
2026-10-07 19:15:15 +03:00
Lauri Ojansivu c53aecfa67 Record Akan text translations and remaining coverage
Thanks to xet7 !
2026-10-07 19:13:37 +03:00
Lauri Ojansivu aecde9ea77 Translate Akan Blockly text operations
Fill text construction, searches, character access, replacement, prompts and trimming. Preserve argument inventories and endpoint distinctions, with regression coverage and wording review notes.

Thanks to xet7 !
2026-10-07 19:13:11 +03:00
Lauri Ojansivu 4c73f35151 Record Akan navigation translations and remaining coverage
Thanks to xet7 !
2026-10-07 19:11:06 +03:00
Lauri Ojansivu dab652b0ae Translate Akan Blockly navigation shortcuts
Preserve navigation directions, movement and scrolling distinctions, and source placeholders. Add regression coverage and document terminology review needs.

Thanks to xet7 !
2026-10-07 19:10:37 +03:00
Lauri Ojansivu 194842ad68 Record Akan procedure and screen-reader translations
Thanks to xet7 !
2026-10-07 19:05:47 +03:00
Lauri Ojansivu 9f2a316369 Translate Akan Blockly procedures and screen-reader mode
Fill 31 messages while preserving procedure results, restrictions, rename scope and screen-reader state transitions. Record provisional accessibility terminology.

Thanks to xet7 !
2026-10-07 19:05:18 +03:00
Lauri Ojansivu b44e305a2b Record Akan unary-math and workspace translations
Thanks to xet7 !
2026-10-07 19:03:43 +03:00
Lauri Ojansivu fe7da49ef8 Translate Akan Blockly unary math and workspace controls
Fill 39 messages while preserving logarithm bases, angle units and variable distinctions. Record low-confidence mathematical terminology for review.

Thanks to xet7 !
2026-10-07 19:03:13 +03:00
Lauri Ojansivu 2579c288e8 Record Akan statistics and random-value translations
Thanks to xet7 !
2026-10-07 19:01:41 +03:00
Lauri Ojansivu cb52ef9928 Translate Akan Blockly statistics random values and rounding
Fill 27 messages while preserving numbered arguments, endpoint inclusion and distinct statistical operations. Record low-confidence mathematical terminology.

Thanks to xet7 !
2026-10-07 19:01:07 +03:00
Lauri Ojansivu 95bcee19ef Record Akan arithmetic and number-property translations
Thanks to xet7 !
2026-10-07 18:59:41 +03:00
Lauri Ojansivu 259c4fc7e8 rules-blocks: the Blocks context menu offers no comment, in any language.
64 locale runs of 'Blocks supports localized drag, field editing and
context menus' failed: c9cc757aa1 added an expectation that the block
context menu shows the translated "Add comment". The editor injects
Blockly with comments: false on purpose since d273b86ed5 - a workspace
comment has no rule equivalent - so that item never appears and the test
could not pass. The spec now asserts the opposite for every locale.
Seven locales passed against the running test server.

Thanks to xet7 !
2026-10-07 18:59:23 +03:00
Lauri Ojansivu c931f9aed9 Translate Akan Blockly arithmetic and number properties
Fill 30 messages while preserving mathematical notation, numbered arguments and numeric boundaries. Record low-confidence mathematical terminology for review.

Thanks to xet7 !
2026-10-07 18:59:11 +03:00
Lauri Ojansivu 3a92cee03b Record Akan list-mutation and logic translations
Thanks to xet7 !
2026-10-07 18:57:38 +03:00
Lauri Ojansivu 409950f730 card-first-position: place and click the minicard again when the board re-renders.
The EVERYTHING run failed 'LTR card first paint uses preferred side': the
click reached the fixed-position Alpha Card but no card opened. A
subscription can replace the board DOM just after it loads (the reason
BoardPage.clickCard retries), and then the click hit a detached minicard;
the new one also lacks the fixed rectangle the spec measures from. The
spec now repeats placing, measuring and clicking, up to three times,
until the card opens. 4 tests passed in each of three runs.

Thanks to xet7 !
2026-10-07 18:57:37 +03:00
Lauri Ojansivu d68f13d4a6 Translate Akan Blockly list mutation sorting and logic
Fill 47 messages while preserving numbered arguments, mutation semantics and comparison boundaries. Record provisional sorting and programming wording.

Thanks to xet7 !
2026-10-07 18:57:09 +03:00
Lauri Ojansivu cf5060c193 Browser specs wait for the data they wrote to the database before using it.
Two Chromium failures in the EVERYTHING run were the same race: a spec
wrote straight to the database and acted before the browser had received
the change, which the server publishes a moment later.

- directory-info-bleed waited only for some settings document, then read
  ldap.enabled from the one sent BEFORE its update: undefined instead of
  false. It now waits for the public field, so the private ones (host,
  bind DN) are checked on the changed document.
- "changing an existing due date saves the replacement (#6607)" could open
  the card before its due date arrived, click "+ add due date", and lose
  it to the date badge that replaced it - a timeout, and on a pass a test
  of adding rather than changing. It now waits for the badge.

The LDAP spec passed 3 of 3 and the due-date spec 5 of 5 against the
running test server; the due-date one failed 1 of 3 before.

Thanks to xet7 !
2026-10-07 18:55:54 +03:00
Lauri Ojansivu bc67c5188b Record Akan list-operation translations
Thanks to xet7 !
2026-10-07 18:55:16 +03:00
Lauri Ojansivu 99a908163a Translate Akan Blockly list creation and retrieval
Fill 46 messages while preserving arguments, position markers and distinct return/remove semantics. Record provisional list terminology for review.

Thanks to xet7 !
2026-10-07 18:54:43 +03:00
Lauri Ojansivu 55d5dd1ed8 Record Akan input and keyboard translations
Thanks to xet7 !
2026-10-07 18:52:42 +03:00
Lauri Ojansivu 5e1b2bd9fe Translate Akan Blockly input labels and keyboard guidance
Fill 58 messages while preserving numbered arguments, coordinate identifiers and distinct input roles. Record provisional mathematical and keyboard terminology.

Thanks to xet7 !
2026-10-07 18:51:55 +03:00
Lauri Ojansivu b0ee9e537a Record Akan editing and field-control translations
Thanks to xet7 !
2026-10-07 18:50:14 +03:00
Lauri Ojansivu cbb638273b Translate Akan Blockly editing and field controls
Fill 39 messages while preserving action scope, numbered arguments and opposite control states. Record provisional bitmap and multiline terminology.

Thanks to xet7 !
2026-10-07 18:49:44 +03:00
Lauri Ojansivu 5f1437dfcd Record Akan editing color and flow translations
Thanks to xet7 !
2026-10-07 18:48:05 +03:00
Lauri Ojansivu 541821bdf0 Translate Akan Blockly editing color and flow controls
Fill 44 messages and replace three unrelated color-label fillers. Preserve arguments, color bounds and opposite loop conditions; record provisional programming terminology.

Thanks to xet7 !
2026-10-07 18:47:32 +03:00
Lauri Ojansivu bff014968b Record Akan block and bubble translations
Thanks to xet7 !
2026-10-07 18:44:56 +03:00
Lauri Ojansivu 41797709b9 Translate Akan Blockly block and bubble labels
Fill 16 accessibility labels while preserving arguments and singular/plural distinctions. Extend shared regression coverage and record provisional technical wording.

Thanks to xet7 !
2026-10-07 18:44:28 +03:00
Lauri Ojansivu bc10f4dd87 Record Waray color and date-label corrections
Thanks to xet7 !
2026-10-07 18:42:53 +03:00
Lauri Ojansivu d212877d4b Correct Waray color and date-format labels
Replace 15 foreign or incomplete labels while preserving actual date-format codes and ISO-week meaning. Record low-confidence color descriptions for visual review.

Thanks to xet7 !
2026-10-07 18:42:26 +03:00
Lauri Ojansivu b764db3157 Record Waray diagnostic and chart language corrections
Thanks to xet7 !
2026-10-07 18:40:56 +03:00
Lauri Ojansivu e61c71a119 Correct Waray diagnostic and process-chart language
Replace 38 foreign or misleading values while preserving identifiers and distinctions among process statistics. Record provisional statistical terminology and remaining review.

Thanks to xet7 !
2026-10-07 18:40:17 +03:00
Lauri Ojansivu 99048a71fa Record Waray rule account and storage language corrections
Thanks to xet7 !
2026-10-07 18:38:54 +03:00
Lauri Ojansivu 6669aecc6b Correct foreign-language Waray rule account and storage labels
Replace 84 French, Walloon or mixed-language values while preserving identifiers, LDAP arguments and recurrence distinctions. Record provisional technical terminology and remaining audit work.

Thanks to xet7 !
2026-10-07 18:37:57 +03:00
Lauri Ojansivu af35f4a592 Record Waray board-view and account language corrections
Thanks to xet7 !
2026-10-07 18:36:21 +03:00
Lauri Ojansivu 7d4888c351 Correct foreign-language Waray board views and account labels
Replace 52 French or Walloon values while preserving shared labels, zoom directions, unread comments and timer arguments. Record provisional flow and milestone terminology.

Thanks to xet7 !
2026-10-07 18:35:48 +03:00
Lauri Ojansivu 1480d62ab3 Record Waray chart and interface language corrections
Thanks to xet7 !
2026-10-07 18:34:21 +03:00
Lauri Ojansivu c3d0a53fcb Correct foreign-language Waray chart and interface labels
Replace 41 French, Walloon or mixed-language values while preserving chart distinctions, recurrence controls and authentication state. Record remaining vocabulary and contextual review.

Thanks to xet7 !
2026-10-07 18:33:51 +03:00
Lauri Ojansivu c75655916f Record Waray search-alias language corrections
Thanks to xet7 !
2026-10-07 18:32:16 +03:00
Lauri Ojansivu f9714a0d04 Correct French search aliases in the Waray locale
Translate 42 operator and predicate values and exercise the actual parser for localized text queries, attachment existence and modified dates. Record provisional compact query wording.

Thanks to xet7 !
2026-10-07 18:31:50 +03:00
Lauri Ojansivu 54c3bf1dbb Record Waray list-sync language corrections
Thanks to xet7 !
2026-10-07 18:30:17 +03:00
Lauri Ojansivu 60c0cd1485 Correct Waray list synchronization and attachment query labels
Replace 21 French or Walloon values while preserving the sync interval, failure token and credential-state distinctions. Use single-word Waray attachment query aliases and record remaining search-label review.

Thanks to xet7 !
2026-10-07 18:29:47 +03:00
Lauri Ojansivu a415f7e75e Record Waray controls and authentication language corrections
Thanks to xet7 !
2026-10-07 18:28:18 +03:00
Lauri Ojansivu 9b89502663 Correct foreign-language text in Waray controls and authentication
Replace 44 French, Walloon or mixed-language values with Waray wording. Preserve authentication requirements, forecast tokens and file extensions; record provisional terminology and remaining audit work.

Thanks to xet7 !
2026-10-07 18:27:50 +03:00
Lauri Ojansivu b5a9b74529 Record Waray activity notification recovery translations
Thanks to xet7 !
2026-10-07 18:25:52 +03:00
Lauri Ojansivu 3ee6eedca2 Translate Waray activity notification recovery messages
Fill 27 messages while preserving retry behavior, pending-work retention and irreversible cancellation distinctions. Record provisional recovery terminology and outstanding locale review.

Thanks to xet7 !
2026-10-07 18:25:23 +03:00
Lauri Ojansivu 306d99e899 Record Waray synchronization reports and email translations
Thanks to xet7 !
2026-10-07 18:23:59 +03:00
Lauri Ojansivu e2225421b1 Translate Waray sync reports and email failures
Fill 29 remaining messages while preserving retention limits, access requirements, null semantics and delivery failure distinctions. Record provisional diagnostic terminology.

Thanks to xet7 !
2026-10-07 18:23:21 +03:00
Lauri Ojansivu cfd3dfe2f0 Record Waray synchronization conflict and preview translations
Thanks to xet7 !
2026-10-07 18:21:49 +03:00
Lauri Ojansivu 3d0cbfea57 Translate Waray synchronization conflicts and previews
Fill 43 messages while preserving local content, source-write restrictions, replacement reuse and report limits. Record provisional synchronization and parser terminology.

Thanks to xet7 !
2026-10-07 18:21:18 +03:00
Lauri Ojansivu dcd400e094 Record Waray Scrum report translations and Export correction
Thanks to xet7 !
2026-10-07 18:19:29 +03:00
Lauri Ojansivu 9b9c7ea815 Translate Waray Scrum reports and observations
Fill 40 remaining Scrum messages and correct the wrong-language Export label. Preserve report scope, unknown estimates, UTC observations and count tokens; record provisional terminology.

Thanks to xet7 !
2026-10-07 18:18:57 +03:00
Lauri Ojansivu 2464ead763 Record Waray rule-editor and Scrum-planning translations
Thanks to xet7 !
2026-10-07 18:16:55 +03:00
Lauri Ojansivu 3c6fa1d57c Translate Waray rule editor and Scrum planning messages
Fill 54 remaining messages while preserving rule validation, administrator permissions, conflict recovery and sprint action distinctions. Record provisional Scrum terminology for review.

Thanks to xet7 !
2026-10-07 18:16:21 +03:00
Lauri Ojansivu 579b55cc5d Record Waray variable and workspace translations
Thanks to xet7 !
2026-10-07 18:14:31 +03:00
Lauri Ojansivu a721b3054b Translate Waray Blockly variables and workspace descriptions
Fill 44 remaining messages while preserving shortcut names, fragment spacing, variable distinctions and numbered arguments. Record remaining contextual review.

Thanks to xet7 !
2026-10-07 18:14:01 +03:00
Lauri Ojansivu d688c3a50f Record Waray text-operation translations
Thanks to xet7 !
2026-10-07 18:12:21 +03:00
Lauri Ojansivu 001706d6b7 Translate Waray Blockly text operations
Fill 53 text-operation messages while preserving positions, replacement scope, copy semantics and whitespace behavior. Record contextual wording review still needed.

Thanks to xet7 !
2026-10-07 18:11:52 +03:00
Lauri Ojansivu dab10476e8 Record Waray accessibility and navigation translations
Thanks to xet7 !
2026-10-07 18:10:04 +03:00
Lauri Ojansivu 8e44e261b8 Translate Waray Blockly screen-reader and navigation shortcuts
Fill 42 remaining accessibility messages while preserving directions, state transitions and shortcut arguments. Record contextual accessibility review still needed.

Thanks to xet7 !
2026-10-07 18:09:33 +03:00
Lauri Ojansivu 5e45d9462b Record Waray trigonometry and procedure translations
Thanks to xet7 !
2026-10-07 18:08:01 +03:00
Lauri Ojansivu 3b4baa79cd Translate Waray Blockly trigonometry and procedure messages
Fill 51 remaining labels and tooltips while preserving function results, restrictions and numeric arguments. Record low-confidence mathematical terminology for review.

Thanks to xet7 !
2026-10-07 18:07:31 +03:00
Lauri Ojansivu d9e3806f33 Record Waray random-value and unary-math translations
Thanks to xet7 !
2026-10-07 18:05:29 +03:00
Lauri Ojansivu cfbda9b1d0 Translate Waray Blockly random values and unary mathematics
Fill 28 remaining messages while preserving random-number bounds, logarithm bases and operation distinctions. Record provisional mathematical wording for review.

Thanks to xet7 !
2026-10-07 18:04:58 +03:00
Lauri Ojansivu dd6201ee2b Record Waray numeric and statistical translations
Thanks to xet7 !
2026-10-07 18:03:19 +03:00
Lauri Ojansivu 8c9a90d987 Translate Waray Blockly numeric properties and statistics
Fill 25 remaining math messages and preserve argument tokens and operation distinctions. Statistical terminology remains provisional and is recorded for review.

Thanks to xet7 !
2026-10-07 18:02:44 +03:00
Lauri Ojansivu 075661004c Record Waray arithmetic translation progress
Update the remaining inventory and document validation and mathematical wording review.

Thanks to xet7 !
2026-10-07 17:57:36 +03:00
Lauri Ojansivu 44cf47b76b Translate Waray arithmetic and logical conditions
Fill 27 placeholders with preserved mathematical notation and arguments. Extend checks for condition quantifiers, angle ranges and inclusive limits; document uncertain terminology.

Thanks to xet7 !
2026-10-07 17:56:59 +03:00
Lauri Ojansivu cf98b3562c Record Waray logic translation progress
Update the remaining inventory and document verification and wording review limits.

Thanks to xet7 !
2026-10-07 17:55:15 +03:00
Lauri Ojansivu a0640e4f4a Translate Waray sorting and logic messages
Fill 29 placeholders while preserving arguments, comparison boundaries and negation meanings. Extend regressions and document provisional terminology.

Thanks to xet7 !
2026-10-07 17:54:42 +03:00
Lauri Ojansivu a53e82961e Record Waray indexing translation progress
Update the remaining inventory and document verification and wording review limits.

Thanks to xet7 !
2026-10-07 17:52:45 +03:00
Lauri Ojansivu a47ac3e61b Translate Waray list indexing and replacement messages
Fill 30 placeholders with preserved arguments, missing-result values and copy semantics. Extend operation regressions and document provisional wording.

Thanks to xet7 !
2026-10-07 17:52:12 +03:00
Lauri Ojansivu d86431c925 Record Waray list translation progress
Update the remaining inventory and document verification and wording review limits.

Thanks to xet7 !
2026-10-07 17:50:46 +03:00
Lauri Ojansivu a157eec211 Translate Waray Blockly list operations
Fill 27 placeholders while preserving list positions and retrieval/removal distinctions. Extend regression checks and document provisional programming terminology.

Thanks to xet7 !
2026-10-07 17:50:11 +03:00
Lauri Ojansivu d066ef236d Record Waray operand translation progress
Update the untranslated inventory and document verification and remaining review.

Thanks to xet7 !
2026-10-07 17:48:43 +03:00
Lauri Ojansivu fae17f2dfa Translate Waray math inputs and keyboard navigation
Fill 33 placeholders with preserved arguments, operand distinctions and navigation steps. Extend regression checks and document provisional wording.

Thanks to xet7 !
2026-10-07 17:48:06 +03:00
Lauri Ojansivu a4821d9d56 Record Waray input label translation progress
Update the remaining inventory and document verification and wording review limits.

Thanks to xet7 !
2026-10-07 17:46:41 +03:00
Lauri Ojansivu fa591af03f Translate Waray Blockly input and icon labels
Fill 34 placeholders while preserving arguments and icon-action direction. Extend condition and list-input distinctions and document provisional terminology.

Thanks to xet7 !
2026-10-07 17:46:03 +03:00
Lauri Ojansivu 0c1db80cc4 Record further Waray Blockly translations
Update the remaining inventory and document validation and wording review limits.

Thanks to xet7 !
2026-10-07 17:44:34 +03:00
Lauri Ojansivu 56d0813ea7 Translate Waray Blockly editing and loop controls
Fill 35 placeholders while preserving arguments and condition polarity. Extend opposite-action and bitmap regressions and document provisional terminology.

Thanks to xet7 !
2026-10-07 17:43:58 +03:00
Lauri Ojansivu c09de3f1ab Add releases/changelog-wrap.mjs, and name it when CHANGELOG lines run long.
tests/changelogFormat.test.cjs failed five times today on prose wider than
80 columns, each time from new translation progress entries, and each time
wrapped by hand. releases/changelog-wrap.mjs does it: only the lines the
test counts, bullets continued two spaces in, never a continuation line that
markdown would read as a heading, list, table or HTML (an issue number such
as #6514 is carried down with the word before it), fenced code untouched,
whitespace only, idempotent. The format test's failure now names it.
tests/changelogWrap.test.cjs pins those rules. CHANGELOG.md is wrapped with
it (24 lines, whitespace only).

Thanks to xet7 !
2026-10-07 17:43:49 +03:00
Lauri Ojansivu 90aabcce0f Record kuva as an attested shared Veps term for the Blockly image field.
tests/vepsMediaWikiTerms.test.cjs failed: the Blockly image field's Veps
value equals Finnish (kuva), and the suite requires every remaining
Finnish match to be reviewed. kuva is the Veps word for a picture already
used by add-cover ("koren kuva") and every avatar string ("kävutajan
kuva"), so it is recorded as an intentionally shared term.

Thanks to xet7 !
2026-10-07 17:42:55 +03:00
Lauri Ojansivu 969bb91fed Keep Tigre's one date word in the date field and date block.
96fd0432bd changed custom-field-date and blockly-ARIA_TYPE_FIELD_DATE to
ዕለት, reasoning that ተመር is also the date fruit. ተመር is the reviewed
Tigre "date" in 42 other values - `date` itself, every date popup title
(tests/tigreDateControls.test.cjs) and the audited repair table - so the
"Date" field type disagreed with every date popup, and
tests/tigreTranslationProgress.test.cjs and
tests/auditedTranslationCorrections.test.cjs failed; the repair tool would
also have reverted it. Both keys use ተመር again, and the new test now
requires them to equal `date`.

Whether ተመር should become ዕለት is a question for a Tigre speaker, to be
decided for every value at once.

Thanks to xet7 !
2026-10-07 17:42:31 +03:00
Lauri Ojansivu 7fbc6e498c Record Waray Blockly translation progress
Update the untranslated inventory and document coverage and remaining review.

Thanks to xet7 !
2026-10-07 17:42:28 +03:00
Lauri Ojansivu 5d9498914d Translate Waray Blockly colors and control flow
Fill 39 English placeholders with preserved arguments and ranges. Extend checks for loop and conditional distinctions and document provisional terminology.

Thanks to xet7 !
2026-10-07 17:41:47 +03:00
Lauri Ojansivu deebe7451c Record Waray repair translation corrections
Document corrected guidance, regression checks and remaining semantic review.

Thanks to xet7 !
2026-10-07 17:39:43 +03:00
Lauri Ojansivu 096fe6c6cb Correct Waray repair and import guidance
Replace 31 Walloon values while preserving repair counts, negative outcomes and search syntax. Extend regressions and document the limits of the language scan.

Thanks to xet7 !
2026-10-07 17:39:09 +03:00
Lauri Ojansivu 8714d4e4a7 Record Waray storage translation corrections
Document corrected labels, regression coverage and remaining language review.

Thanks to xet7 !
2026-10-07 17:37:06 +03:00
Lauri Ojansivu 92db84756c Correct Waray storage and backup labels
Replace 25 Walloon values while preserving product names, backup exclusions and restoration scope. Extend regression checks and document provisional terminology.

Thanks to xet7 !
2026-10-07 17:36:28 +03:00
Lauri Ojansivu ddc2d05cf5 Record Waray report translation corrections
Document corrected messages, available verification and remaining language review.

Thanks to xet7 !
2026-10-07 17:34:55 +03:00
Lauri Ojansivu 4dbef0cf0f Correct Waray reporting and recovery labels
Replace 20 Walloon values while preserving configuration identifiers, report aggregation and history distinctions. Extend regressions and document provisional technical wording.

Thanks to xet7 !
2026-10-07 17:34:16 +03:00
Lauri Ojansivu 2abda1117a Record Waray role translation corrections
Document corrected values and available validation, including the missing Blockly dependency.

Thanks to xet7 !
2026-10-07 17:32:31 +03:00
Lauri Ojansivu 98fb2bf52d Correct Waray role and table-view labels
Replace 25 Walloon values while preserving toggle direction, read-only status and default-off wording. Extend regressions and document provisional terminology.

Thanks to xet7 !
2026-10-07 17:31:55 +03:00
Lauri Ojansivu 4228c31c37 Changelog: card popup board lists and the browser tests' cookie refresh limit.
Records 9ae7388a7e and 63a7fba465 under Upcoming, removes the TODO Later
note about Meteor's cookie refresh limit that 63a7fba465 resolves, and
wraps prose added since the last wrap at 80 columns (whitespace only).

Thanks to xet7 !
2026-10-07 17:29:38 +03:00
Lauri Ojansivu c182b9ac42 Correct Waray login and rule labels
Replace 25 Walloon values while preserving template variables, configuration identifiers and authentication-state meanings. Extend regressions and document provisional terminology.

Thanks to xet7 !
2026-10-07 17:29:14 +03:00
Lauri Ojansivu 63a7fba465 Let the login-cookie refresh limit be raised, and stop browser tests signing themselves out.
Meteor answers more than 30 refreshes of the HttpOnly login cookie per 10
seconds from one client address with 429, and every page load is one. The
whole browser suite comes from localhost, so a quick run signed its own
test users out: 18-rtl-layout "list pages" landed on the sign-in page on
its third full navigation in one of two runs with two workers. TODO Later
recorded this on 2026-10-05.

ACCOUNTS_COOKIE_REFRESH_RATE_LIMIT now sets the allowance per 10 seconds;
anything but a positive whole number keeps Meteor's default, and the
limiter is never switched off. The browser test server sets it to 100000.
It also helps an install where many users reach WeKan through a proxy
that is not trusted for the client address and so share one address;
documented beside the login settings. loginWithToken additionally waits
out a 429 on the cookie refresh and resumes once, and only for that.

tests/cookieRefreshRateLimit.test.cjs runs the parser with valid and
invalid values and pins the server, build.sh, docs and helper wiring.

Thanks to xet7 !
2026-10-07 17:29:08 +03:00
Lauri Ojansivu 4ba3e10bb0 Record Waray organization translation corrections
Document corrected labels, regression coverage and remaining wording review.

Thanks to xet7 !
2026-10-07 17:27:33 +03:00
Lauri Ojansivu 7ff98db7a5 Correct Waray organization and template controls
Replace 25 Walloon values, preserve template variables and configuration identifiers, and extend regressions for admin restrictions and movement directions. Document provisional terminology.

Thanks to xet7 !
2026-10-07 17:26:56 +03:00
Lauri Ojansivu 3749df2873 Record further Waray language corrections
Document the 30 corrected values, restored import guidance and validation limits.

Thanks to xet7 !
2026-10-07 17:24:52 +03:00
Lauri Ojansivu 65fe9d7221 Correct Waray notes and favorites translated into Walloon
Replace 30 wrong-language labels, restore the complete Markdown import guidance and extend token and operation regressions. Document provisional terminology.

Thanks to xet7 !
2026-10-07 17:24:15 +03:00
Lauri Ojansivu 1e3264c1e8 Record Waray corrections and place translation entries in Upcoming
Document the 40 language corrections and move 39 translation entries not contained in v12.21 into Upcoming, as verified by the release ancestry check.

Thanks to xet7 !
2026-10-07 17:21:44 +03:00
Lauri Ojansivu 6c95ad716d Correct Walloon text in Waray board controls
Replace 40 wrong-language values covering boards, imports, deadlines and notes. Preserve tokens and non-deletion guarantees; add regressions and document remaining contamination and provisional wording.

Thanks to xet7 !
2026-10-07 17:19:30 +03:00
Lauri Ojansivu e4d6a66881 Merge remote-tracking branch 'origin/main' 2026-10-07 17:19:21 +03:00
Lauri Ojansivu b5e969e59d Record Walloon and Waray translation corrections
Update the remaining inventory and document the corrected locale identity, validation and review limits.

Thanks to xet7 !
2026-10-07 17:16:50 +03:00
Lauri Ojansivu 6c5433f4d1 Translate Walloon and Waray labels and correct locale confusion
Fill 32 block labels and correct 15 Walloon values mistakenly inserted into the Waray catalog. Repair the regression that enforced wrong-language labels, preserve arguments and document provisional wording.

Thanks to xet7 !
2026-10-07 17:15:54 +03:00
Lauri Ojansivu 77c1c8eaf5 Record Wu Chinese and Venetian translation progress
Refresh the remaining translation inventory and document verification and contextual review limits.

Thanks to xet7 !
2026-10-07 17:13:26 +03:00
Lauri Ojansivu f8107ec9b2 Translate Wu Chinese and Venetian block labels
Fill 32 placeholders with preserved numbered arguments, extend regression checks and document provisional regional and technical wording.

Thanks to xet7 !
2026-10-07 17:12:55 +03:00
Lauri Ojansivu b70f7dccf3 Record Tsonga and Venda translation progress
Refresh the remaining translation inventory and document verification and wording review limits.

Thanks to xet7 !
2026-10-07 17:11:06 +03:00
Lauri Ojansivu 7ef3e29237 Translate Tsonga and Venda block labels
Fill 32 placeholders, preserve numbered arguments and extend regression checks. Document vocabulary references and provisional technical wording.

Thanks to xet7 !
2026-10-07 17:10:29 +03:00
Lauri Ojansivu 27f64b2ee8 Record four-language block label progress
Refresh the remaining translation inventory and document verification and contextual review limits.

Thanks to xet7 !
2026-10-07 17:08:50 +03:00
Lauri Ojansivu 4276db8d68 Translate block labels in four African languages
Fill 64 placeholders in Kinyarwanda, Kirundi, Luganda and Chichewa. Preserve numbered arguments and document vocabulary corrections and provisional wording.

Thanks to xet7 !
2026-10-07 17:08:19 +03:00
Lauri Ojansivu 9ae7388a7e Card More and multi-selection list every board again for a user without a templates board.
The browser suite failed "Card -> More lists the other parents and removes
one" in 66 of 67 locales. Two faults, traced in the running app:

The board list was empty. Both helpers filtered
`_id: { $ne: currentUser.getTemplatesBoardId() }`, and that id is undefined
for a user without a templates board. The client ReactiveCache keys each
query by EJSON.stringify(selector) and runs the PARSED key, which drops
undefined: the query became `_id: {}` and matched no board. So "Change
card's parent -> Source board" and the multi-selection destination picker
offered nothing to such a user. The templates board is now excluded only
when there is one. (dialogWithBoardSwimlaneList queries Boards directly and
was not affected.)

The spec opened More from document.body, without the card behind it, and
straight after writing the parents to the database, before this browser had
received parentId. It now opens More from the card's menu as a member does,
waits until the client has the primary parent, and asserts the board and
the parent card are offered.

tests/reactiveCacheUndefinedSelector.test.cjs shows the EJSON round trip,
pins both helpers, and fails on any ReactiveCache selector that puts a
call such as getTemplatesBoardId() under $ne (it flags the previous
cardDetails.js). Run against the bundle built before this change, the spec
now fails only on the board list - the bug fixed here.

Thanks to xet7 !
2026-10-07 17:07:39 +03:00
Lauri Ojansivu 70d4ffc8f4 Add the pbkdf2 3.1.7 update to the CHANGELOG.
Thanks to dependabot and xet7 !
2026-10-07 17:06:20 +03:00
Lauri Ojansivu 331fbde2b3 Record Sotho-Tswana block label progress
Update the remaining translation inventory and document validation and contextual review limits.

Thanks to xet7 !
2026-10-07 17:05:57 +03:00
Lauri Ojansivu f5836c3a37 Update pbkdf2 to 3.1.7 for GHSA-477h-4r7f-fvrx.
pbkdf2 <= 3.1.6 hashes an over-long password again on every iteration, so a
1 MiB password costs a full hash per round (CVE-2026-102414, denial of
service). 3.1.7 hashes it once.

meteor-node-stubs 1.2.30 bundles pbkdf2 3.1.3 inside its own tarball, where
npm overrides cannot reach it, and no meteor-node-stubs release bundles 3.1.7.
Instead of forking meteor-node-stubs again, WeKan now depends on pbkdf2
^3.1.7 directly and the new postinstall script
scripts/patch-meteor-node-stubs-pbkdf2.cjs removes the bundled copy, so
crypto-browserify resolves the patched top-level package, and drops the
bundled entries from package-lock.json. It fails the install if the patched
package is missing.

The vendored jade lockfile moves from pbkdf2 3.1.5 to 3.1.7.

tests/dependencySecurityUpdates.test.cjs fails when any tracked lockfile
holds pbkdf2 <= 3.1.6, flags a synthetic 3.1.3 bundled entry, and drives the
postinstall script against fixture trees.

Thanks to dependabot and xet7 !
2026-10-07 17:05:40 +03:00
Lauri Ojansivu 2c16c3f173 Show the Rules list selection checkboxes, so Select all / Unselect all visibly work.
The Select all / Unselect all handlers did set the selection, and each
row's checkbox followed it, but the checkboxes were never on screen:
forms.css hides every native checkbox app-wide (display: none,
visibility: hidden, position: absolute and a -9999px offset), and the
rules list used native ones with nothing undoing that. So the buttons
changed invisible state, and no rule could be ticked by hand for Delete
selected / Export selected either.

rules.css now undoes all parts of that rule for .js-rule-select, with a
selector that outranks [type="checkbox"]:checked, and the checkbox is
labelled with its rule's title. tests/rulesSelectAllCheckboxes.test.cjs
runs the select-all / unselect-all / single-toggle logic with several
rules, pins the override, and as the negative test checks that no native
checkbox under client/components/rules is left hidden. Playwright spec
44-rules checks the boxes are visible and follow both buttons.

Thanks to titver968 and xet7 !

Fixes #6749,
2026-10-07 17:05:36 +03:00
Lauri Ojansivu b69e4681c4 Translate Sotho-Tswana block and bubble labels
Fill 48 placeholders in Sesotho, Setswana and Northern Sotho, preserve numbered arguments and extend regression coverage. Document vocabulary evidence and provisional technical wording.

Thanks to xet7 !
2026-10-07 17:05:32 +03:00
Lauri Ojansivu f98fc86946 Add the Rules Set color to fix to the CHANGELOG.
Thanks to titver968 and xet7 !
2026-10-07 17:04:45 +03:00
Lauri Ojansivu 1ad558f371 Rules: let the "Set color to" action pick colors other than green.
The swatch click handler of the rule action color popup read the color
from Template.currentData(). Inside a Blaze event handler that is the data
of the template the handler is defined on - the popup, opened with the
cardActions context { ruleName, triggerVar, ruleId } - not the clicked
swatch, so every click set the selection to undefined and the action could
only ever be saved with the "green" default. The handler now reads the
swatch's own data with Blaze.getData(event.currentTarget), like the card,
list and swimlane color popups, and ignores anything that is not a card
color. The Save button is type="button" so it cannot submit the form.

Tests: tests/ruleSetColorActionPicker.test.cjs drives the real handlers
with Blaze event semantics (every color is selectable and saved, a click
without a valid color keeps the selection, nothing applies without Save)
and scans every palette click handler under client/ so none takes its
color from Template.currentData() first; a Playwright spec picks red in
the Rules editor and checks the stored action.

Thanks to titver968 and xet7 !

Fixes #6748,
2026-10-07 17:04:32 +03:00
Lauri Ojansivu 0cd9df9492 Record Nguni block label translation progress
Refresh the remaining translation inventory and document verification and wording review limitations.

Thanks to xet7 !
2026-10-07 17:03:42 +03:00
Lauri Ojansivu c910255500 Translate Nguni block and bubble labels
Fill 80 placeholders across Zulu, Xhosa, Swati and Northern Ndebele catalogs. Preserve numbered arguments, extend regression checks and document lower-confidence wording and number agreement.

Thanks to xet7 !
2026-10-07 17:03:19 +03:00
Lauri Ojansivu 1778e54c8c Record four-language block label progress
Refresh the remaining translation count and document validation and wording review limitations.

Thanks to xet7 !
2026-10-07 17:01:31 +03:00
Lauri Ojansivu 9e71da2b4e Translate block labels in Bislama, Tok Pisin, Fijian and Papiamento
Fill 64 placeholders with preserved arguments, extend translation regressions and record provisional technical wording and contextual bubble paraphrases.

Thanks to xet7 !
2026-10-07 17:01:05 +03:00
Lauri Ojansivu 2d83523594 Changelog: Launchpad retry fix, test and translation fixes, and correct two Snap links.
Records a3048e4a7a (a stopped Launchpad build is retried with a new build),
576a8438aa (the activity notification intent test), the two CHANGELOG
wrapping commits and the Tigre and Tamazight fixes under Upcoming. The two
Snap entries added for #6746 linked 941aa91929, a translation commit that
landed between that fix and its changelog commit; they now link the fix,
f927cb3c50. Prose added since the last wrap is wrapped at 80 columns again
(whitespace only).

Thanks to xet7 !
2026-10-07 17:00:21 +03:00
Lauri Ojansivu a3048e4a7a Snap Launchpad: retry a stopped build with a new build, and name a build-farm proxy failure.
The v12.21 s390x snap failed. Attempt 1's Launchpad build stopped
because the build farm's own package proxy answered "501 Gateway error"
for libyaml-cpp0.8_0.8.0+dfsg-6build1_s390x.deb while every neighbouring
package downloaded. Attempt 2 submitted a new build that also stopped,
and snapcraft crashed fetching its log (MissingSchema "Invalid URL
'None'"). Attempt 3 then ran `remote-build --recover`, which reconnects to
that SAME stopped build, and was over in eight seconds: a finished build
cannot be recovered into a snap. Each attempt also reprinted every
earlier attempt's log, so attempt 1's failure appeared again under
attempt 2.

Now an attempt whose build ended "Stopped:"/"Failed:"/"Cancelled:" for
the architecture makes the next attempt submit a new build; recovery is
still what reconnects after a TLS/polling error or an auth failure, as
before. Each attempt prints only the build logs it downloaded, and says
so when it got none. A 5xx from the build farm's package proxy is named
as Launchpad infrastructure with the package that failed, so the next
step is a re-run rather than an edit of snapcraft.yaml.

tests/releaseSnapRecovery.test.cjs runs the real step with a stand-in
snapcraft: a stopped build is retried without --recover and then builds;
negatively, three stopped builds with the proxy error fail with the
infrastructure message, print attempt 1's log once, and are not called a
deterministic stage-package failure. Both new tests fail on the previous
workflow. Not run on GitHub Actions or Launchpad here.

Thanks to xet7 !
2026-10-07 16:59:18 +03:00
Lauri Ojansivu c16141d81d Record Polynesian block label translations
Update the remaining translation inventory and record verification and contextual review limits.

Thanks to xet7 !
2026-10-07 16:59:08 +03:00
Lauri Ojansivu b74cc1a147 Translate Polynesian block and bubble labels
Fill 64 placeholders in Maori, Samoan, Hawaiian and Tongan. Preserve arguments, extend distinction checks and document provisional terminology with dictionary references.

Thanks to xet7 !
2026-10-07 16:58:42 +03:00
Lauri Ojansivu b5afc03235 Record four more block label translations
Refresh the remaining translation inventory and document validation and wording review limits.

Thanks to xet7 !
2026-10-07 16:56:01 +03:00
Lauri Ojansivu be02e9bf43 Translate block labels in Somali, Moroccan Arabic, Odia and Konkani
Fill 64 remaining block and bubble labels, preserve numbered arguments and extend translation regressions. Document lower-confidence technical wording and pending accessibility review.

Thanks to xet7 !
2026-10-07 16:55:41 +03:00
Lauri Ojansivu 19931d1855 Record four-language block label translations
Update the remaining translation inventory and document validation and wording review limitations.

Thanks to xet7 !
2026-10-07 16:54:04 +03:00
Lauri Ojansivu cad48f4a20 Translate block and bubble labels in four more languages
Fill 64 placeholders in Yiddish, Turkmen, Bhojpuri and Maithili while preserving numbered arguments. Extend translation regressions and record lower-confidence terminology and pending contextual review.

Thanks to xet7 !
2026-10-07 16:53:41 +03:00
Lauri Ojansivu 3f7e963e71 Record Kurdish and Tatar block label progress
Thanks to xet7 !
2026-10-07 16:48:28 +03:00
Lauri Ojansivu fa8167b30a Translate Kurdish and Tatar block and bubble labels
Fill 48 labels while preserving numbered arguments. Add distinction checks and record technical wording that needs contextual review.

Thanks to xet7 !
2026-10-07 16:48:02 +03:00
Lauri Ojansivu a22e6953bf Record Cherokee field translation progress
Thanks to xet7 !
2026-10-07 16:46:05 +03:00
Lauri Ojansivu a71977d577 Translate Cherokee field types
Fill 11 field labels and extend regression checks. Record very low confidence in technical phrases and the need for grammatical review.

Thanks to xet7 !
2026-10-07 16:45:39 +03:00
Lauri Ojansivu 5a6416e33a Record Wolaytta field translation progress
Thanks to xet7 !
2026-10-07 16:43:19 +03:00
Lauri Ojansivu 8cc6c614f8 Translate Wolaytta field labels and remove English control filler
Fill ten field labels and correct three controls. Add token and distinction regressions and document low-confidence wording; angle remains pending vocabulary evidence.

Thanks to xet7 !
2026-10-07 16:42:36 +03:00
Lauri Ojansivu a450cb0b23 Record Tigre translation progress
Thanks to xet7 !
2026-10-07 16:39:38 +03:00
Lauri Ojansivu 96fd0432bd Translate Tigre field types and correct calendar date label
Fill 11 field labels, add date regression coverage and record low-confidence technical terminology.

Thanks to xet7 !
2026-10-07 16:39:05 +03:00
Lauri Ojansivu 74dadb1679 Record Tamazight translation progress
Thanks to xet7 !
2026-10-07 16:36:51 +03:00
Lauri Ojansivu ff8c9bd0e6 Translate Tamazight field types
Fill 11 field labels, extend regression coverage and record low-confidence technical wording.

Thanks to xet7 !
2026-10-07 16:36:27 +03:00
Lauri Ojansivu 27c8546589 Record Inuktitut translation progress
Thanks to xet7 !
2026-10-07 16:34:50 +03:00
Lauri Ojansivu 0f4a4545cf Translate Inuktitut field types
Fill 11 field labels and extend regression coverage. Document vocabulary references and low-confidence technical paraphrases.

Thanks to xet7 !
2026-10-07 16:34:30 +03:00
Lauri Ojansivu 582470ccde Record Greenlandic translation progress
Thanks to xet7 !
2026-10-07 16:32:13 +03:00
Lauri Ojansivu f38e44d41a Translate Greenlandic field types
Fill 11 field labels and extend regression checks. Record vocabulary references and low-confidence technical wording.

Thanks to xet7 !
2026-10-07 16:31:47 +03:00
Lauri Ojansivu 117c9c58f7 Record Nahuatl translation progress
Thanks to xet7 !
2026-10-07 16:30:24 +03:00
Lauri Ojansivu f3ab4a79cf Translate Nahuatl field types
Fill 11 field labels and extend regression checks. Record low-confidence technical paraphrases and their vocabulary references.

Thanks to xet7 !
2026-10-07 16:30:05 +03:00
Lauri Ojansivu a04ded4984 Record Ewe and Fulah translation progress
Thanks to xet7 !
2026-10-07 16:28:08 +03:00
Lauri Ojansivu c7df9973e1 Translate Ewe and Fulah field types
Fill 22 field labels, extend regression coverage and document low-confidence technical wording.

Thanks to xet7 !
2026-10-07 16:27:47 +03:00
Lauri Ojansivu 77c97f0ed1 Record Volapük and Klingon translation progress
Thanks to xet7 !
2026-10-07 16:25:46 +03:00
Lauri Ojansivu 0cf084715f Translate Volapük and Klingon field types
Fill 22 field labels and correct three foreign-language controls. Document low-confidence technical paraphrases and extend regression coverage.

Thanks to xet7 !
2026-10-07 16:24:41 +03:00
Lauri Ojansivu 92bc59aa65 Record Akan, Bambara and Wolof translation progress
Thanks to xet7 !
2026-10-07 16:19:29 +03:00
Lauri Ojansivu 6f02165e88 Translate Akan, Bambara and Wolof field types
Fill 33 field labels and correct two Akan selector labels. Record low-confidence technical wording and extend regression coverage.

Thanks to xet7 !
2026-10-07 16:19:08 +03:00
Lauri Ojansivu 576a8438aa Activity notification intent test: name each intent by its activity, not by arrival order.
The mocha test "stores intent before dispatch, preserves pending failures
and suppresses deferred or disabled capture" failed about one run in four
with "notification hook did not settle". Traced with in-memory lease,
recovery and gate events: the two activities' insert hooks dispatch
concurrently, and when the second activity reached the subscriber first,
intentIds[0] held ITS intent - the one the test makes fail on purpose. The
test then waited for that intent to complete, which never happens, while
the one-second recovery scan kept retrying it. The application behaved as
designed; the test assumed an order the hooks never promised.

Intents are now recorded against their activity. The checks that counted
subscriber calls also allowed for the recovery scan, which legitimately
retries a pending intent on its own: the recipient is denied before the
replacement subscriber is installed, calls are compared with a snapshot,
the final retry waits out a scan that holds the lease at that instant, and
exactly one delivery is required whichever of the two made it.

The test passed 12 of 12 runs in isolation afterwards; it failed 1 of 4
before.

Thanks to xet7 !
2026-10-07 16:15:44 +03:00
Lauri Ojansivu f6a58f29e3 Record Sami Veps and Acehnese translation progress
Thanks to xet7 !
2026-10-07 16:12:50 +03:00
Lauri Ojansivu 92a60e35f0 Translate Sami Veps and Acehnese field types
Fill 33 English placeholders and correct two Malay-seeded Acehnese selectors. Extend semantic and token regressions and record provisional technical wording.

Thanks to xet7 !
2026-10-07 16:12:24 +03:00
Lauri Ojansivu e9c8d3c266 Record further field-type localization progress
Thanks to xet7 !
2026-10-07 16:10:25 +03:00
Lauri Ojansivu 8d4d8c91dd Translate Tibetan Dzongkha Tigrinya and Kashmiri field types
Fill 44 English placeholders and extend field-type distinction checks while preserving existing translations and source arguments. Record provisional technical terminology.

Thanks to xet7 !
2026-10-07 16:10:01 +03:00
Lauri Ojansivu 783fad2fe5 Record Quechua Aymara and Guarani translation progress
Thanks to xet7 !
2026-10-07 16:08:07 +03:00
Lauri Ojansivu ea88c056a5 Translate Quechua Aymara and Guarani field types
Fill 33 English placeholders and correct four mixed-language selectors. Extend field-type and semantic regressions; document low-confidence technical wording.

Thanks to xet7 !
2026-10-07 16:07:38 +03:00
Lauri Ojansivu 47bf1dab92 Record Buryat Chuvash and Sakha translation progress
Thanks to xet7 !
2026-10-07 16:05:54 +03:00
Lauri Ojansivu 6007aa45b2 Translate Buryat Chuvash and Sakha field types
Fill 33 English placeholders, preserve existing translations and source arguments, and extend field-type distinctions. Document vocabulary sources and provisional terminology.

Thanks to xet7 !
2026-10-07 16:05:30 +03:00
Lauri Ojansivu 6cea2adaa4 Record Maori Tongan and Hawaiian translation progress
Thanks to xet7 !
2026-10-07 16:03:50 +03:00
Lauri Ojansivu cc1086bcf2 Translate Maori Tongan and Hawaiian field types
Fill 33 English placeholders and repair four selector labels. Extend semantic and token coverage and document vocabulary sources and provisional technical phrasing.

Thanks to xet7 !
2026-10-07 16:03:26 +03:00
Lauri Ojansivu b32a728a61 Record Pacific field-type translation progress
Thanks to xet7 !
2026-10-07 16:01:27 +03:00
Lauri Ojansivu 4282d46703 Translate Pacific Blockly field types
Fill 44 English placeholders in Bislama, Tok Pisin, Fijian and Samoan and correct four mixed-language selector labels. Extend regressions and record provisional technical wording.

Thanks to xet7 !
2026-10-07 16:01:04 +03:00
Lauri Ojansivu 800fa7ab46 Record five-language field-type translation progress
Thanks to xet7 !
2026-10-07 15:59:21 +03:00
Lauri Ojansivu a1f34d5d40 Translate field types in five further African languages
Fill 55 English placeholders and replace two mixed-language Luganda control labels. Extend semantic and argument regressions and record provisional terminology.

Thanks to xet7 !
2026-10-07 15:58:40 +03:00
Lauri Ojansivu bc3f99aa1b Wrap the newest CHANGELOG prose at 80 columns again.
28 lines added since the last wrap - the new Upcoming section and
translation progress notes - ran past 80 columns and failed
tests/changelogFormat.test.cjs. Whitespace only; the rendered text is the
same.

Thanks to xet7 !
2026-10-07 15:57:38 +03:00
Lauri Ojansivu f14b8b8d00 Record further southern African translation progress
Thanks to xet7 !
2026-10-07 15:56:58 +03:00
Lauri Ojansivu 6105415af3 Changelog: AppArmor-refused snap database connections and the snapctl get errors.
Related to #6746.

Thanks to fabiosalles and xet7 !
2026-10-07 15:56:53 +03:00
Lauri Ojansivu 941aa91929 Translate further southern African field types
Fill 44 English labels and replace five foreign or filler values in Venda and Tsonga. Extend semantic and token regressions and document low-confidence technical wording.

Thanks to xet7 !
2026-10-07 15:56:40 +03:00
Lauri Ojansivu f927cb3c50 Snap: name an AppArmor-refused database connection at once, and stop the snapctl get errors.
The logs in #6746 show the real cause of "write: permission denied" from
database-restore and of WeKan waiting forever for FerretDB: 1300 kernel
lines of apparmor="DENIED" operation="file_perm" class="net" for
snap.wekan.wekan on 127.0.0.1:27019, with the same denial for snap-store
and standard-notes on that computer. FerretDB was running and accepting
connections; the kernel refused the first write. That is snapd 2.77.1 on a
kernel with an AppArmor socket mediation bug (Launchpad snapd bug 2169038),
fixed by a newer kernel and worked around by `snap revert snapd`.

WeKan cannot lift that denial, so it now says what it is. A new
bin/socket-denied recognises the refusal and prints the denial to look for,
the upstream bug and both ways out. wekan-control's FerretDB and MongoDB
wait loops keep each attempt's error and print it on the first refused
attempt instead of "not ready yet" for two minutes; database-backup and
database-restore stop at once instead of starting an already running
service and waiting 90 seconds.

The same log began with "error: snapctl: get which option?" four times:
SAML_IDP_PROFILE, SAML_WANT_RESPONSE_SIGNED, SAML_WANT_ASSERTIONS_SIGNED
and SAML_LOGIN_FLOW were listed in bin/config without a KEY_ name, so
wekan-read-settings ran a bare `snapctl get` for them and `snap set` could
never reach them. They now have their snap keys, the CORS_EXPOSE_HEADERS
description has its right name, and a key without a snap name falls back
to its default instead of calling snapctl.

Tests: snapSocketDenied, snapSettingsKeys, and extended snapDatabaseRestore
and ferretdbWaitTimeout.

Related to #6746: the reporter still has to update the kernel or revert
snapd, so the issue stays open until that is confirmed.

Thanks to fabiosalles and xet7 !
2026-10-07 15:56:19 +03:00
Lauri Ojansivu 6a5afd8c92 Record southern African field-type translation progress
Thanks to xet7 !
2026-10-07 15:54:41 +03:00
Lauri Ojansivu 918a76c246 Translate southern African Blockly field types
Fill 66 English placeholders in six locale paths, preserve existing translations and source arguments, and extend type-distinction regressions. Document terminology sources and provisional wording.

Thanks to xet7 !
2026-10-07 15:54:22 +03:00
Lauri Ojansivu 944a931f8b Record Walloon Venetian and Manx translation progress
Thanks to xet7 !
2026-10-07 15:52:31 +03:00
Lauri Ojansivu 5a416160e0 Translate Walloon Venetian and Manx field types
Fill 34 English placeholders and correct three foreign seed labels in Walloon. Extend field-type and language regression coverage; record provisional terminology.

Thanks to xet7 !
2026-10-07 15:52:11 +03:00
Lauri Ojansivu 1708f6bed4 Record further field-type translation progress
Thanks to xet7 !
2026-10-07 15:50:38 +03:00
Lauri Ojansivu d674ee1e7f Translate Blockly field types in five more languages
Fill 55 English placeholders in Turkmen, Odia, Konkani, Papiamento and Wu Chinese. Extend field-type distinction checks and record provisional terminology.

Thanks to xet7 !
2026-10-07 15:50:07 +03:00
Lauri Ojansivu 867154fd4a Use the attested Tamazight checklist noun in the rule trigger variables hint.
tests/upcomingTamazightPlaceholderRepair.test.cjs failed: the
r-trigger-vars-hint translation from 58c0d84d7a named checklist fields
with the verification noun the suite rejects as unsupported. It now uses
the file's own checklist term, the one `checklist` itself carries. The
placeholders are unchanged; every suite that reads the Tamazight file
passes.

Thanks to xet7 !
2026-10-07 15:49:22 +03:00
Lauri Ojansivu e034a16978 Record field-type translation progress
Thanks to xet7 !
2026-10-07 15:48:36 +03:00
Lauri Ojansivu 9eb2f5ebb8 Translate Blockly field types in eight languages
Fill 88 English placeholders, preserve source arguments and existing translations, and test distinct image, selector and name types. Record provisional terminology and outstanding spoken accessibility review.

Thanks to xet7 !
2026-10-07 15:48:18 +03:00
Lauri Ojansivu 256a09d555 Use the Tigre card, name and file nouns in 15 recently added Tigre strings.
tests/tigreAllBoardCardContexts.test.cjs and
tests/tigreAllEstablishedNounContexts.test.cjs failed: newly translated
hints for auto-archiving, list age, Leo import, parent cards, rule
triggers, rule people variables, notification settings, saved filters,
card-text filtering and map placement used the Tigrinya forms ካርድ, ስም,
አስማት and ፋይል instead of the established Tigre ones. They now use ወረቀት
ካርድ for card, ስሜት for name, the corpus-attested plural አስማይ for names
and ፈይል for file, as the rest of the Tigre file does. Placeholders and
variables are unchanged.

Low confidence: the surrounding wording is still provisional and needs a
Tigre speaker's review. All Tigre suites and the human-preference checks
pass.

Thanks to xet7 !
2026-10-07 15:47:55 +03:00
Lauri Ojansivu 93196e99c0 Record Inuktitut accessibility translation progress
Thanks to xet7 !
2026-10-07 15:46:20 +03:00
Lauri Ojansivu 1f4f00c880 Translate Inuktitut Blockly comment and accessibility labels
Fill fifteen English placeholders and extend opposite-action, argument and context-menu coverage. Record vocabulary references and low-confidence technical paraphrases for review.

Thanks to xet7 !
2026-10-07 15:46:03 +03:00
Lauri Ojansivu a46d2a0438 Record Greenlandic accessibility translation progress
Thanks to xet7 !
2026-10-07 15:44:02 +03:00
Lauri Ojansivu 2632b0d8e1 Wrap CHANGELOG prose at 80 columns and stop showing two hashes as link text.
tests/changelogFormat.test.cjs failed twice over. Two links in the v12.16
translation entry used the commit hashes b76be84186 and c9e622f1b5 as
their text; the text now says what each commit translated and the links
are unchanged. And 1,542 prose lines - bullets and paragraphs, mostly
translation progress notes - ran past 80 columns. They are wrapped at word
boundaries, bullet continuations indented two spaces, never starting a
line with something markdown reads as a heading or a list, so the
rendered text is the same; links, summaries, language lists and the
closing sentence stay on one line as the test allows. The TODO Later note
asking whether to correct the released link text is removed.

Every suite that reads CHANGELOG.md passes.

Thanks to xet7 !
2026-10-07 15:43:53 +03:00
Lauri Ojansivu 22a15f2b10 Translate Greenlandic Blockly accessibility controls
Fill fifteen English placeholders, extend control and context-menu regressions, and document vocabulary sources and low-confidence technical phrasing.

Thanks to xet7 !
2026-10-07 15:43:44 +03:00
Lauri Ojansivu 0a5130d8b6 Record Tamazight accessibility translation progress
Thanks to xet7 !
2026-10-07 15:42:12 +03:00
Lauri Ojansivu b983642a9f Translate Tamazight Blockly accessibility labels
Fill thirteen English placeholders while preserving existing comment commands and source arguments. Extend control regressions and browser assertions. Record low-confidence technical wording for review.

Thanks to xet7 !
2026-10-07 15:41:45 +03:00
Lauri Ojansivu 48524f0121 Document Wu Chinese and Nahuatl translation progress
Thanks to xet7 !
2026-10-07 15:36:54 +03:00
Lauri Ojansivu 4c0a5ecc5f Translate Wu Chinese and Nahuatl Blockly accessibility labels
Fill comment and input controls, extend placeholder and opposing-action checks, and document provisional wording and vocabulary references.

Thanks to xet7 !
2026-10-07 15:36:33 +03:00
Lauri Ojansivu e4935ff4d8 Document Veps accessibility translation progress
Thanks to xet7 !
2026-10-07 15:33:07 +03:00
Lauri Ojansivu 87d6e1fc06 Translate Veps Blockly comment and accessibility controls
Use researched Veps vocabulary, preserve source arguments, extend regression coverage and record low-confidence technical adaptations.

Thanks to xet7 !
2026-10-07 15:32:50 +03:00
github-actions[bot] 47857855fe Bump versions for v12.21 2026-10-07 12:29:57 +00:00
Lauri Ojansivu dd731c2335 Prepare v12.21 release 2026-10-07 15:27:59 +03:00
Lauri Ojansivu 2a1025846a Document further comment and accessibility translations
Thanks to xet7 !
2026-10-07 15:25:07 +03:00
Lauri Ojansivu 28b9ce7407 Translate Blockly controls in Quechua Aymara Guarani Volapuk and Klingon
Preserve existing labels, correct three generic text labels, and extend placeholder and opposite-action checks. Document provisional technical wording.

Thanks to xet7 !
2026-10-07 15:24:41 +03:00
Lauri Ojansivu 121a3aee19 Document Buryat through Tigrinya translation progress
Thanks to xet7 !
2026-10-07 15:21:44 +03:00
Lauri Ojansivu 2d695b240b Translate Blockly accessibility labels from Buryat through Tigrinya
Fill comment and input labels, preserve existing translations, extend regression coverage and record provisional technical phrasing.

Thanks to xet7 !
2026-10-07 15:21:24 +03:00
Lauri Ojansivu 903525709a Updated count of lines per committer.
Thanks to xet7 !
2026-10-07 15:20:18 +03:00
Lauri Ojansivu 538f482e13 Document six-locale Blockly translation progress
Thanks to xet7 !
2026-10-07 15:18:27 +03:00
Lauri Ojansivu 0d2e5bbb6e Add changelog entry for the Snap backup and restore database start.
Records fa75d59fd0 under Upcoming as a Snap bug fix, and lists #6746 in
TODO Later until the reporter's FerretDB service log shows why FerretDB
did not answer.

Thanks to fabiosalles and xet7 !
2026-10-07 15:18:09 +03:00
Lauri Ojansivu 8ed3dcc268 Translate Blockly accessibility controls in six more locales
Preserve existing Ewe translations, correct the Akan text label, and extend placeholder and opposite-action checks. Record low-confidence technical phrasing for review.

Thanks to xet7 !
2026-10-07 15:18:02 +03:00
Lauri Ojansivu fa75d59fd0 Snap: start the database before backup and restore, and say why it does not answer.
wekan.database-restore and wekan.database-backup talked to whatever was
on the database port and nothing else. After `snap stop wekan`, which the
restore steps tell people to run, nothing is there; on a FerretDB snap the
database is its own service that starting WeKan alone does not bring back.
The tools then failed after a 30-second server-selection timeout with a
driver topology dump naming neither the service nor its log.

The new bin/database-ready asks bin/database-role which database holds
the data, starts that service when nothing answers, waits for it, and when
it still does not answer stops with the driver's own error and the
`snap logs` / `snap services` commands that show the reason. A "permission
denied" is explained as a security-policy denial on this system, not the
database, with the journal command that names the rule.

WeKan's own wait for FerretDB discarded the ping error, so its log said only
"FerretDB not ready yet" for every cause. db-eval now prints why a command
failed on stderr, and the wait's one-time hint shows it. The same hint
suggested `snap set wekan database=mongodb`, a setting the configure hook
removes, so that advice did nothing; it is gone.

The backup script also sources its settings and passes the archive path
quoted now, so a path with a space works.

tests/snapDatabaseRestore.test.cjs runs both scripts in a stand-in snap:
FerretDB or MongoDB started by role, a running database left alone, and,
negatively, a database that never answers stopping the tool before it
runs, with the error shown. A source guard requires both tools to wait for
the database first. tests/ferretdbWaitTimeout.test.cjs now requires the
ping error in the hint and rejects the removed setting. Not run inside an
installed snap here.

Thanks to fabiosalles and xet7 !
2026-10-07 15:17:42 +03:00
Lauri Ojansivu fd89507d14 Document five-locale Blockly translation progress
Thanks to xet7 !
2026-10-07 15:15:26 +03:00
Lauri Ojansivu 30a40c8747 Translate Blockly accessibility labels in five more locales
Fill comment and input labels while preserving existing Manx and Aromanian translations. Extend regression coverage and document provisional wording.

Thanks to xet7 !
2026-10-07 15:15:04 +03:00
Lauri Ojansivu a1bb4d5247 Document additional Blockly accessibility translations
Thanks to xet7 !
2026-10-07 15:12:18 +03:00
Lauri Ojansivu 2dc46f81c0 Translate Blockly accessibility controls in nine more locales
Fill comment and input labels, correct the Tongan text filler, and extend placeholder and opposing-action checks. Record provisional wording and vocabulary references.

Thanks to xet7 !
2026-10-07 15:11:55 +03:00
Lauri Ojansivu bb8b1eed57 Document southern African Blockly translation progress
Thanks to xet7 !
2026-10-07 15:09:22 +03:00
Lauri Ojansivu a2b84d3afc Translate Blockly accessibility labels for southern African locales
Fill comment and input controls across ten locale paths, extend placeholder and opposite-action checks, and record provisional wording and vocabulary references.

Thanks to xet7 !
2026-10-07 15:09:04 +03:00
Lauri Ojansivu efd841a00b Document further Blockly accessibility translations
Thanks to xet7 !
2026-10-07 15:06:07 +03:00
Lauri Ojansivu f6d4f5f920 Translate Blockly accessibility controls in eight more languages
Fill comment and input controls with intact placeholders and distinct opposing actions. Extend regression coverage and document provisional technical phrasing.

Thanks to xet7 !
2026-10-07 15:05:50 +03:00
Lauri Ojansivu 1e5a580c51 Document Blockly accessibility translation progress
Thanks to xet7 !
2026-10-07 15:03:39 +03:00
Lauri Ojansivu c9cc757aa1 Translate Blockly accessibility controls in seven languages
Fill comment and input-control labels, preserve source arguments and distinguish opposite actions. Correct the Tatar text label and record provisional technical wording.

Thanks to xet7 !
2026-10-07 15:03:20 +03:00
Lauri Ojansivu 1375757ad5 Document Cherokee Blockly announcement translations
Thanks to xet7 !
2026-10-07 15:00:30 +03:00
Lauri Ojansivu 361dab8b2b Translate Cherokee Blockly movement announcements
Complete this announcement group across all non-English locales and extend placeholder and direction checks. Record low-confidence Cherokee phrasing for further review.

Thanks to xet7 !
2026-10-07 15:00:10 +03:00
Lauri Ojansivu 890f5fe379 Merge remote-tracking branch 'origin/main' 2026-10-07 14:58:55 +03:00
Lauri Ojansivu 28c8482ee5 Document Tigre Blockly announcement translations
Refresh the remaining translation inventory and record validation and wording-review limits.

Thanks to xet7 !
2026-10-07 14:54:36 +03:00
Lauri Ojansivu db6c28e86b Translate Tigre Blockly movement announcements
Fill twelve movement and scrolling announcements, preserve arguments and extend regression coverage. Record low-confidence technical clauses and the spatial vocabulary references used.

Thanks to xet7 !
2026-10-07 14:54:15 +03:00
Lauri Ojansivu 2712c3efc7 Document Greenlandic and Inuktitut announcement translations
Refresh the remaining translation inventory and record validation and wording-review limits.

Thanks to xet7 !
2026-10-07 14:52:02 +03:00
Lauri Ojansivu 036e902d6b Translate Greenlandic and Inuktitut Blockly announcements
Fill 24 movement and scrolling announcements, preserve arguments and extend regression coverage. Record low-confidence technical wording and case endings in both locales.

Thanks to xet7 !
2026-10-07 14:51:39 +03:00
Lauri Ojansivu 4de4bc73df Document four further Blockly announcement translations
Refresh the translation inventory and record validation and wording-review limits.

Thanks to xet7 !
2026-10-07 14:49:46 +03:00
Lauri Ojansivu 72b1f80e75 Translate Blockly announcements in four further locales
Fill 48 announcements in Nahuatl, Wolaytta, Tamazight and Wu Chinese. Preserve arguments and extend regression coverage. Record low-confidence technical clauses in Nahuatl, Wolaytta and Tamazight.

Thanks to xet7 !
2026-10-07 14:49:23 +03:00
Lauri Ojansivu 71cfd03bc3 Document six more Blockly announcement translations
Refresh the translation inventory and record validation and wording-review limits.

Thanks to xet7 !
2026-10-07 14:46:13 +03:00
Lauri Ojansivu 26d74f05aa Translate Blockly announcements in six more locales
Fill 72 announcements in Quechua, Aymara, Guarani, Veps, Volapuk and Klingon. Preserve arguments and extend regression coverage. Record provisional wording, particularly Aymara, Veps, Volapuk and Klingon.

Thanks to xet7 !
2026-10-07 14:45:49 +03:00
Lauri Ojansivu 333a8ca98e Document Asian and African Blockly announcement translations
Refresh the remaining translation inventory and record validation and wording-review limits.

Thanks to xet7 !
2026-10-07 14:42:57 +03:00
Lauri Ojansivu 25e8a15337 Translate Blockly announcements in six Asian and African locales
Fill 72 announcements in Buryat, Chuvash, Sakha, Tibetan, Dzongkha and Tigrinya. Preserve arguments and extend regression coverage. Record provisional technical wording, particularly Buryat, Chuvash, Sakha and Dzongkha.

Thanks to xet7 !
2026-10-07 14:42:36 +03:00
Lauri Ojansivu 06f4fe9b0e Document six further Blockly announcement translations
Update the remaining translation inventory and record validation and wording-review limits.

Thanks to xet7 !
2026-10-07 14:40:04 +03:00
Lauri Ojansivu 5f205bba2e Translate Blockly announcements in six further locales
Fill 72 announcements in Akan, Bambara, Ewe, Wolof, Fulah and Kashmiri. Preserve arguments, extend regression coverage and record provisional technical wording, particularly Bambara, Ewe and Fulah.

Thanks to xet7 !
2026-10-07 14:39:44 +03:00
Lauri Ojansivu 011afe3541 Document five further Blockly announcement translations
Refresh the remaining translation inventory and record validation and wording-review limits.

Thanks to xet7 !
2026-10-07 14:37:07 +03:00
Lauri Ojansivu 1879399ba2 Translate Blockly announcements in five further locales
Fill 60 announcements in Walloon, Waray, Acehnese, Northern Sami and Venetian. Preserve existing Manx and Aromanian values and extend coverage to all seven locales. Record provisional technical wording, particularly Walloon, Acehnese and Northern Sami.

Thanks to xet7 !
2026-10-07 14:36:43 +03:00
Lauri Ojansivu 144362226c Document nine further Blockly announcement translations
Refresh the translation inventory and record validation and wording-review limits.

Thanks to xet7 !
2026-10-07 14:33:55 +03:00
Lauri Ojansivu 7f4403c8e7 Translate Blockly announcements in nine Pacific and African locales
Fill 108 movement and scrolling announcements, preserve arguments and extend regression coverage. Record provisional technical wording, particularly Tongan, Hawaiian and Luganda.

Thanks to xet7 !
2026-10-07 14:33:30 +03:00
Lauri Ojansivu 812c7158ad Document southern African Blockly announcement translations
Update the remaining placeholder inventory and record validation and wording-review limits.

Thanks to xet7 !
2026-10-07 14:30:29 +03:00
Lauri Ojansivu 3dd7baf95b Translate Blockly announcements in southern African locales
Fill 120 movement and scrolling announcements across ten locale files, preserving arguments and existing translations. Extend regression coverage and record low-confidence technical wording, particularly Swati, Northern Ndebele and Venda.

Thanks to xet7 !
2026-10-07 14:30:07 +03:00
Lauri Ojansivu 8485b07e34 Document eight further Blockly announcement translations
Thanks to xet7 !
2026-10-07 14:24:23 +03:00
Lauri Ojansivu e3cbe00a74 Translate Blockly movement announcements in eight further locales
Preserve arguments and directions across 96 values. Technical phrasing remains provisional, particularly Maithili, Konkani and Papiamento.

Thanks to xet7 !
2026-10-07 14:24:00 +03:00
Lauri Ojansivu 5339bdddb4 Document Blockly movement announcement translations
Thanks to xet7 !
2026-10-07 14:21:40 +03:00
Lauri Ojansivu c5bfa8121b Translate Blockly movement announcements in seven locales
Preserve arguments and distinct directions across 84 values. Extend locale checks and editor browser coverage. Chichewa and Samoan technical phrasing remains provisional.

Thanks to xet7 !
2026-10-07 14:21:15 +03:00
Lauri Ojansivu fe4afcc2a7 Document completed drag label placeholder coverage
Thanks to xet7 !
2026-10-07 14:18:45 +03:00
Lauri Ojansivu 23dd54615d Complete drag permission label placeholder coverage
Add a provisional Cherokee ability-to-pull phrase and check the label in all non-English catalogs. The derived Cherokee grammar remains low-confidence and requires review.

Thanks to xet7 !
2026-10-07 14:18:22 +03:00
Lauri Ojansivu d59d571a99 Document six further drag permission translations
Thanks to xet7 !
2026-10-07 14:16:57 +03:00
Lauri Ojansivu f8b31018e6 Translate drag permission label in six further locales
Extend localized settings coverage. All six labels remain low-confidence technical drafts; the Cherokee label is still pending.

Thanks to xet7 !
2026-10-07 14:16:33 +03:00
Lauri Ojansivu 8e9d86e1d1 Document twelve further drag permission translations
Thanks to xet7 !
2026-10-07 14:14:42 +03:00
Lauri Ojansivu 9a52f25da9 Translate drag permission label in twelve further locales
Extend localized settings coverage and refine the Klingon pull paraphrase. Technical wording remains provisional, particularly Chuvash, Veps, Volapuk and Klingon.

Thanks to xet7 !
2026-10-07 14:14:20 +03:00
Lauri Ojansivu df9bb7ef1b Document thirteen further drag permission translations
Thanks to xet7 !
2026-10-07 14:12:56 +03:00
Lauri Ojansivu 44fe1d4316 Translate drag permission label in thirteen further locales
Extend localized settings coverage. Technical wording remains provisional, particularly Aromanian, Fulah and Kashmiri; the Fulah vocabulary lookup was inconclusive.

Thanks to xet7 !
2026-10-07 14:12:33 +03:00
Lauri Ojansivu 7791a87a98 Document nineteen further drag permission translations
Thanks to xet7 !
2026-10-07 14:11:02 +03:00
Lauri Ojansivu d96d8a564f Translate drag permission label in nineteen further locales
Extend localized settings coverage. Pronoun agreement and technical wording remain provisional, particularly Swati, Northern Ndebele, Venda, Fijian and Tongan.

Thanks to xet7 !
2026-10-07 14:10:32 +03:00
Lauri Ojansivu 9e247c76cf Document drag permission translation progress
Thanks to xet7 !
2026-10-07 14:08:49 +03:00
Lauri Ojansivu 665d33e141 Translate board drag permission label in fifteen locales
Use ability-to-drag wording and extend localized settings coverage. Chichewa, Maithili and Konkani wording remains provisional.

Thanks to xet7 !
2026-10-07 14:08:25 +03:00
Lauri Ojansivu 1edac96e89 Document completed duplicate label placeholder coverage
Thanks to xet7 !
2026-10-07 14:06:34 +03:00
Lauri Ojansivu e31679f46a Complete duplicate relationship placeholder coverage
Fill seven remaining locales and check both labels in all non-English catalogs. The final seven sets remain low-confidence drafts, especially Cherokee.

Thanks to xet7 !
2026-10-07 14:06:09 +03:00
Lauri Ojansivu 2670cf018f Document twelve further duplicate label translations
Thanks to xet7 !
2026-10-07 14:04:37 +03:00
Lauri Ojansivu 99bb6df956 Translate duplicate relationship labels in twelve further locales
Preserve both directions and extend localized checks. Technical wording remains provisional, especially Chuvash, Veps, Volapuk and Klingon.

Thanks to xet7 !
2026-10-07 14:04:10 +03:00
Lauri Ojansivu 3902ab3d97 Document thirteen further duplicate label translations
Thanks to xet7 !
2026-10-07 14:02:29 +03:00
Lauri Ojansivu 37c941609f Translate duplicate relationship labels in thirteen further locales
Keep both directions distinct and extend browser coverage. Manx, Aromanian, Fulah and Kashmiri wording remains particularly provisional; the Fulah vocabulary lookup was inconclusive.

Thanks to xet7 !
2026-10-07 14:02:01 +03:00
Lauri Ojansivu 0bef7e3c9a Document nineteen further duplicate label translations
Thanks to xet7 !
2026-10-07 13:59:58 +03:00
Lauri Ojansivu 7e42efb2e5 Translate duplicate relationship labels in nineteen further locales
Cover both directions and localized editing. Pronoun agreement and technical wording remain provisional, particularly Swati, Northern Ndebele, Venda, Fijian and Tongan.

Thanks to xet7 !
2026-10-07 13:59:35 +03:00
Lauri Ojansivu 73d6d3fc6d Document duplicate relationship translation progress
Thanks to xet7 !
2026-10-07 13:58:01 +03:00
Lauri Ojansivu 6cd38f6de0 Translate duplicate relationship labels in fifteen locales
Distinguish both relationship directions and extend localized editing coverage. Chichewa, Samoan and Konkani wording remains provisional.

Thanks to xet7 !
2026-10-07 13:57:34 +03:00
Lauri Ojansivu ac2b22dfdf Document completed template hint placeholder coverage
Thanks to xet7 !
2026-10-07 13:55:20 +03:00
Lauri Ojansivu 8675861577 Complete String Template hint placeholder coverage
Add Cherokee, Inuktitut and Tigre drafts and verify examples in all non-English locales. These final translations remain low-confidence, especially the Cherokee technical clause.

Thanks to xet7 !
2026-10-07 13:54:57 +03:00
Lauri Ojansivu 5632b04a7b Document four further template hint translations
Thanks to xet7 !
2026-10-07 13:53:18 +03:00
Lauri Ojansivu b6d81e1fd9 Translate String Template hints in four further locales
Replace Wolaytta English filler and preserve literal code examples. All four translations remain low-confidence technical drafts requiring semantic review.

Thanks to xet7 !
2026-10-07 13:52:55 +03:00
Lauri Ojansivu 7ddcc71a3b Document six further template hint translations
Thanks to xet7 !
2026-10-07 13:51:15 +03:00
Lauri Ojansivu 314e003555 Translate String Template hints in six further locales
Preserve literal examples and extend localized checks. All six sets remain low-confidence technical drafts, particularly Veps, Volapuk and Klingon.

Thanks to xet7 !
2026-10-07 13:50:54 +03:00
Lauri Ojansivu d49d7c776d Document twelve further template hint translations
Thanks to xet7 !
2026-10-07 13:49:10 +03:00
Lauri Ojansivu edef644ef7 Translate String Template hints in twelve further locales
Preserve literal variable examples and extend localized coverage. These technical translations remain low-confidence drafts requiring semantic review.

Thanks to xet7 !
2026-10-07 13:48:47 +03:00
Lauri Ojansivu a8049ea380 Document seven further template hint translations
Thanks to xet7 !
2026-10-07 13:46:31 +03:00
Lauri Ojansivu 75312bf9b4 Translate seven further String Template hints
Preserve literal variables and extend localized coverage. Technical wording remains provisional, particularly Manx, Northern Sami and Aromanian.

Thanks to xet7 !
2026-10-07 13:46:11 +03:00
Lauri Ojansivu 33f033f0bb Document Pacific and African String Template translations
Thanks to xet7 !
2026-10-07 13:44:36 +03:00
Lauri Ojansivu 5338d4a8eb Translate String Template hints in nine further locales
Preserve code examples and cover localized hint visibility. Technical wording remains provisional, especially in Fijian, Tongan and Hawaiian.

Thanks to xet7 !
2026-10-07 13:44:12 +03:00
Lauri Ojansivu 7f3879bea6 Document further String Template hint translations
Thanks to xet7 !
2026-10-07 13:42:23 +03:00
Lauri Ojansivu 21396dc192 Translate String Template hints in eighteen further locales
Preserve variable examples and replace a Xitsonga filler label. Technical wording remains provisional, particularly in Konkani, Swati, Northern Ndebele and Venda.

Thanks to xet7 !
2026-10-07 13:41:56 +03:00
Lauri Ojansivu fcc1220dca Document String Template hint translation progress
Thanks to xet7 !
2026-10-07 13:39:35 +03:00
Lauri Ojansivu d79b10f588 Translate String Template hints in seven more locales
Preserve executable context examples and URL encoding syntax. Correct mixed-language Tatar format labels and its malformed space entity. Chichewa and Samoan technical wording remains provisional.

Thanks to xet7 !
2026-10-07 13:39:14 +03:00
Lauri Ojansivu 4bd000f594 Document completed saved filter placeholder coverage
Thanks to xet7 !
2026-10-07 13:33:55 +03:00
Lauri Ojansivu de482cddc5 Complete saved filter placeholder coverage
Fill thirty English placeholders in Cherokee, Inuktitut and Tigre and check the ten saved-filter strings across all 234 non-English locales. The new technical clauses remain low-confidence drafts requiring semantic review.

Thanks to xet7 !
2026-10-07 13:33:26 +03:00
Lauri Ojansivu 7524b74c4f Document four further locale saved filter translations
Thanks to xet7 !
2026-10-07 13:31:02 +03:00
Lauri Ojansivu 99ee5e0ddc Fill saved filter translations in four further locales
Fill forty English placeholders and extend positive and negative filter coverage. All four sets remain low-confidence technical drafts requiring semantic review.

Thanks to xet7 !
2026-10-07 13:30:35 +03:00
Lauri Ojansivu e8e02e6248 Document six further saved filter translations
Thanks to xet7 !
2026-10-07 13:28:06 +03:00
Lauri Ojansivu 5791adc9f5 Fill six further saved filter translations
Fill sixty English placeholders and extend positive and negative filter coverage. All six locale sets remain low-confidence technical drafts, particularly Veps, Volapük and Klingon.

Thanks to xet7 !
2026-10-07 13:27:35 +03:00
Lauri Ojansivu eee09f3d58 Document six more locale saved filter translations
Thanks to xet7 !
2026-10-07 13:25:25 +03:00
Lauri Ojansivu e46f22e4e3 Translate saved filter controls in six more locales
Fill sixty English placeholders and extend positive and negative filter coverage. Technical clauses remain provisional, especially Buryat, Chuvash and Dzongkha.

Thanks to xet7 !
2026-10-07 13:24:56 +03:00
Lauri Ojansivu 5cc37e9d55 Document six further locale saved filter translations
Thanks to xet7 !
2026-10-07 13:21:33 +03:00
Lauri Ojansivu af96f4e963 Translate saved filter controls in six further locales
Fill sixty English placeholders and extend positive and negative filter coverage. Technical clauses remain provisional, especially Ewe, Fulah and Kashmiri.

Thanks to xet7 !
2026-10-07 13:21:05 +03:00
Lauri Ojansivu 1eedc87879 Document seven further locale saved filter translations
Thanks to xet7 !
2026-10-07 13:18:29 +03:00
Lauri Ojansivu 9dad4b31bc Translate saved filter controls in seven further locales
Fill seventy English placeholders, correct wrong-language filter labels and extend positive and negative filter coverage. Technical clauses remain provisional, especially Waray, Acehnese, Manx and Aromanian.

Thanks to xet7 !
2026-10-07 13:18:05 +03:00
Lauri Ojansivu 4eb73b8f0c Document nine further locale saved filter translations
Thanks to xet7 !
2026-10-07 13:15:15 +03:00
Lauri Ojansivu 7015e111ee Translate saved filter controls in nine further locales
Fill ninety English placeholders and extend positive and negative filter coverage. Technical clauses remain provisional, especially Fijian, Tongan, Hawaiian and Kirundi.

Thanks to xet7 !
2026-10-07 13:14:47 +03:00
Lauri Ojansivu cbc55bbb72 Document ten African locale saved filter translations
Thanks to xet7 !
2026-10-07 13:12:28 +03:00
Lauri Ojansivu a60e55e3a5 Translate saved filter controls in ten African locale files
Fill one hundred English placeholders and extend positive and negative filter coverage. Technical clauses remain provisional, especially Swati, Northern Ndebele, Tsonga and Venda.

Thanks to xet7 !
2026-10-07 13:11:59 +03:00
Lauri Ojansivu 9fd065fe00 Document eight further locale saved filter translations
Thanks to xet7 !
2026-10-07 13:09:36 +03:00
Lauri Ojansivu 4ef7469ce5 Translate saved filter controls in eight further locales
Fill eighty English placeholders and extend positive and negative filter coverage. Technical clauses remain provisional, especially Konkani and Papiamentu.

Thanks to xet7 !
2026-10-07 13:09:13 +03:00
Lauri Ojansivu 0b286507a0 Document seven locale saved filter translations
Thanks to xet7 !
2026-10-07 13:06:59 +03:00
Lauri Ojansivu 78ca2530a2 Translate saved filter controls in seven locales
Fill seventy English placeholders, correct mixed-language Tatar filter wording and extend positive and negative filter coverage. Technical clauses remain provisional, especially Chichewa and Samoan.

Thanks to xet7 !
2026-10-07 13:06:33 +03:00
Lauri Ojansivu 6a932497e6 Document completed rule email report placeholder coverage
Thanks to xet7 !
2026-10-07 13:03:32 +03:00
Lauri Ojansivu 30ccbcfe8e Complete rule email report placeholder coverage
Fill thirty English placeholders in Cherokee, Inuktitut and Tigre and check the ten report strings across all 234 non-English locales. The new technical clauses remain low-confidence drafts requiring semantic review.

Thanks to xet7 !
2026-10-07 13:03:08 +03:00
Lauri Ojansivu b050335e0b Document Nahuatl Wolaytta and Tamazight report translations
Thanks to xet7 !
2026-10-07 13:00:11 +03:00
Lauri Ojansivu 33f35c657c Translate rule email reports in Nahuatl Wolaytta and Tamazight
Fill thirty English placeholders and extend report regression coverage. All three sets remain low-confidence technical drafts requiring semantic review.

Thanks to xet7 !
2026-10-07 12:59:37 +03:00
Lauri Ojansivu 0715d183bb Document six further rule email report translations
Thanks to xet7 !
2026-10-07 12:57:03 +03:00
Lauri Ojansivu 6800674da7 Fill six further rule email report translations
Fill sixty English placeholders and extend report regression coverage. All six locale batches remain low-confidence technical drafts, particularly Veps, Volapük and Klingon.

Thanks to xet7 !
2026-10-07 12:56:27 +03:00
Lauri Ojansivu ffe6b2522b Document six more locale report translations
Thanks to xet7 !
2026-10-07 12:53:34 +03:00
Lauri Ojansivu bf44a8bcdf Translate rule email reports in six more locales
Fill sixty English placeholders and extend report regression coverage. Technical clauses remain provisional, especially Buryat, Chuvash and Dzongkha.

Thanks to xet7 !
2026-10-07 12:53:11 +03:00
Lauri Ojansivu 954234522a Document six further locale report translations
Thanks to xet7 !
2026-10-07 12:49:47 +03:00
Lauri Ojansivu f561ee6ca6 Translate rule email reports in six further locales
Fill sixty English placeholders and extend report regression coverage. Technical clauses remain provisional, especially Ewe, Fulah and Kashmiri.

Thanks to xet7 !
2026-10-07 12:49:25 +03:00
Lauri Ojansivu 18d1e49da2 Document seven further locale report translations
Thanks to xet7 !
2026-10-07 12:46:53 +03:00
Lauri Ojansivu 7535a1c0dc Translate rule email reports in seven further locales
Fill seventy English placeholders and extend report regression coverage. Technical clauses remain provisional, especially Acehnese, Manx and Aromanian.

Thanks to xet7 !
2026-10-07 12:46:23 +03:00
Lauri Ojansivu 7b6a66c197 Document nine locale rule email report translations
Thanks to xet7 !
2026-10-07 12:43:46 +03:00
Lauri Ojansivu 04c10bc9e6 Translate rule email reports in nine further locales
Fill ninety English placeholders and extend report regression coverage. Technical clauses remain provisional, particularly Fijian, Tongan, Hawaiian and Kirundi.

Thanks to xet7 !
2026-10-07 12:43:10 +03:00
Lauri Ojansivu b31828fda4 Document ten African locale report translations
Thanks to xet7 !
2026-10-07 12:40:09 +03:00
Lauri Ojansivu 39f31def59 Translate rule email reports in ten African locale files
Fill one hundred English placeholders and extend report regression coverage. Full technical clauses remain provisional, especially Swati, Northern Ndebele, Tsonga and Venda.

Thanks to xet7 !
2026-10-07 12:39:41 +03:00
Lauri Ojansivu 8a767fbd65 Document eight locale rule email report translations
Thanks to xet7 !
2026-10-07 12:36:55 +03:00
Lauri Ojansivu 5d803eda6d Translate rule email reports in eight further locales
Fill eighty English placeholders and extend report regression coverage. Technical clauses remain provisional, especially Konkani and Papiamentu.

Thanks to xet7 !
2026-10-07 12:36:29 +03:00
Lauri Ojansivu 2d3ba08c4a Document seven locale rule email report translations
Thanks to xet7 !
2026-10-07 12:33:55 +03:00
Lauri Ojansivu 6f663811a0 Translate rule email reports in seven more locales
Fill seventy English placeholders and extend report regression coverage. Technical clauses remain provisional, especially Chichewa and Samoan.

Thanks to xet7 !
2026-10-07 12:33:33 +03:00
Lauri Ojansivu 37b0b7916d Document completed rule variable picker translations
Thanks to xet7 !
2026-10-07 12:29:40 +03:00
Lauri Ojansivu 0777dd3ba7 Complete rule variable picker placeholder coverage
Fill twelve remaining labels and cover all 234 non-English locales. The twelve complete labels remain low-confidence drafts requiring linguistic review. Preserve existing translations and locale punctuation. Focused checks pass; browser execution remains unverified.

Thanks to xet7 !
2026-10-07 12:26:14 +03:00
Lauri Ojansivu 4b3734b39b Document the next ten variable picker translation drafts
Thanks to xet7 !
2026-10-07 12:22:46 +03:00
Lauri Ojansivu 5da7829eb1 Fill ten further rule variable picker translations
Preserve existing translations and extend locale and browser checks. Full labels remain provisional, especially Bambara, Ewe, Aymara, Guarani, Manx and Aromanian. Focused checks pass; browser execution remains unverified.

Thanks to xet7 !
2026-10-07 12:22:27 +03:00
Lauri Ojansivu f1edf5a2fe Document ten further variable picker translations
Thanks to xet7 !
2026-10-07 12:20:27 +03:00
Lauri Ojansivu 2ab05383f4 Translate ten more rule variable picker labels
Preserve existing translations and extend locale and browser coverage. Full clauses remain provisional, especially Dzongkha, Buryat, Chuvash, Sakha and Acehnese. Focused checks pass; browser execution remains unverified.

Thanks to xet7 !
2026-10-07 12:20:06 +03:00
Lauri Ojansivu dd85597863 Document the next seventeen variable picker translations
Thanks to xet7 !
2026-10-07 12:18:13 +03:00
Lauri Ojansivu 84fc9203dd Translate seventeen more rule variable picker labels
Preserve existing translations and extend locale and browser coverage. Technical paraphrases remain provisional, especially Northern Sotho, Tsonga, Venda, Fijian, Tongan, Hawaiian and Kirundi. Focused checks pass; browser execution remains unverified.

Thanks to xet7 !
2026-10-07 12:17:56 +03:00
Lauri Ojansivu 3b5e273efe Document seventeen rule variable picker translations
Thanks to xet7 !
2026-10-07 12:16:00 +03:00
Lauri Ojansivu af5ba32503 Translate the rule variable picker label in seventeen locales
Explain the last focused field and extend locale and browser coverage. Technical phrasing remains provisional, especially Konkani, Southern Sotho, Tswana, Chichewa and Samoan. Focused checks pass; browser execution remains unverified.

Thanks to xet7 !
2026-10-07 12:15:42 +03:00
Lauri Ojansivu f69002a897 Document Leo placeholder coverage and refresh translation backlog
Thanks to xet7 !
2026-10-07 12:12:50 +03:00
Lauri Ojansivu 9afc5bf221 Fill the Cherokee Leo instruction and cover all locales
Complete non-English placeholder coverage for the Leo instruction and remove its pending key. The Cherokee draft remains very low-confidence and wider linguistic review stays open. Focused checks pass; browser execution remains unverified.

Thanks to xet7 !
2026-10-07 12:12:26 +03:00
Lauri Ojansivu 6970549f50 Document Tigre and Wolaytta Leo instruction drafts
Thanks to xet7 !
2026-10-07 12:10:35 +03:00
Lauri Ojansivu 598893c70b Add Tigre and Wolaytta Leo import instructions
Preserve existing translations and extend locale and browser checks. Both full instructions remain very low-confidence drafts requiring vocabulary and grammar review. Focused checks pass; browser execution remains unverified.

Thanks to xet7 !
2026-10-07 12:10:15 +03:00
Lauri Ojansivu 989c751826 Document Nahuatl and Tamazight Leo instruction drafts
Thanks to xet7 !
2026-10-07 12:07:32 +03:00
Lauri Ojansivu 09429dcfed Add Nahuatl and Tamazight Leo import instructions
Preserve existing translations and extend locale, script and browser checks. Both complete instructions remain low-confidence drafts requiring linguistic review. Focused checks pass; browser execution remains unverified.

Thanks to xet7 !
2026-10-07 12:07:15 +03:00
Lauri Ojansivu b50e4df0e3 Document Greenlandic and Inuktitut Leo instruction drafts
Thanks to xet7 !
2026-10-07 12:05:18 +03:00
Lauri Ojansivu 48d3e71648 Add Greenlandic and Inuktitut Leo import instructions
Preserve existing translations and extend locale and browser coverage. Both full instructions remain low-confidence drafts requiring vocabulary and grammar review. Focused checks pass; browser execution remains unverified.

Thanks to xet7 !
2026-10-07 12:05:01 +03:00
Lauri Ojansivu c9a8ff3232 Document five further Leo translation drafts
Thanks to xet7 !
2026-10-07 12:03:10 +03:00
Lauri Ojansivu fc9749b113 Translate five more Leo import instructions
Extend locale and browser coverage while preserving existing translations. Kashmiri, Fulah, Veps, Volapuk and Klingon instructions remain low-confidence drafts. Focused checks pass; browser execution remains unverified.

Thanks to xet7 !
2026-10-07 12:02:52 +03:00
Lauri Ojansivu 3977df082b Document six further Leo translation drafts
Thanks to xet7 !
2026-10-07 11:59:33 +03:00
Lauri Ojansivu e56723274b Translate six more Leo import instructions
Preserve existing translations and extend locale and browser checks. All six technical drafts remain provisional, especially Dzongkha, Quechua, Aymara and Guarani. Focused checks pass; browser execution remains unverified.

Thanks to xet7 !
2026-10-07 11:59:08 +03:00
Lauri Ojansivu dcc6aeb5e1 Document seven further Leo translations
Thanks to xet7 !
2026-10-07 11:56:08 +03:00
Lauri Ojansivu ebb12f9537 Translate seven more Leo import instructions
Preserve existing translations and extend locale and browser coverage. Full clauses remain provisional, especially Bambara, Ewe, Buryat, Chuvash and Sakha hierarchy terminology. Focused checks pass; browser execution remains unverified.

Thanks to xet7 !
2026-10-07 11:55:49 +03:00
Lauri Ojansivu 68be9547e6 Document Leo translations and Darija label correction
Thanks to xet7 !
2026-10-07 11:53:20 +03:00
Lauri Ojansivu 5afa11bde0 Translate eight Leo instructions and correct Darija completion label
Extend locale and browser coverage while preserving existing translations. Acehnese, Manx, Northern Sami and Aromanian clauses remain low-confidence drafts. Focused checks pass; browser execution remains unverified.

Thanks to xet7 !
2026-10-07 11:53:02 +03:00
Lauri Ojansivu 626d5b231d Document twelve more Leo instruction translations
Thanks to xet7 !
2026-10-07 11:50:23 +03:00
Lauri Ojansivu a0874a292d Translate Leo import instructions in twelve more locales
Preserve existing translations and extend locale and browser import coverage. Full technical clauses remain provisional, especially Fijian, Tongan, Hawaiian, Oromo and Kirundi. Focused checks pass; browser execution remains unverified.

Thanks to xet7 !
2026-10-07 11:50:03 +03:00
Lauri Ojansivu ed880a24b9 Document fourteen Leo instruction translations
Thanks to xet7 !
2026-10-07 11:47:20 +03:00
Lauri Ojansivu 3e174595ba Translate Leo import instructions in fourteen more locales
Preserve existing translations and cover the new instructions in locale and browser checks. Technical phrasing remains provisional, especially Konkani, Swati, Northern Ndebele and Venda. Focused checks pass; browser execution remains unverified.

Thanks to xet7 !
2026-10-07 11:47:01 +03:00
Lauri Ojansivu 8e4164c828 Document the next Leo instruction translation batch
Thanks to xet7 !
2026-10-07 11:43:45 +03:00
Lauri Ojansivu 8a2a7dbd0d Translate Leo import instructions in seven more locales
Fill remaining English instructions and extend locale and browser coverage. Technical phrasing, especially Somali and Chichewa, remains provisional. Browser execution is unverified; focused Node checks pass.

Thanks to xet7 !
2026-10-07 11:43:19 +03:00
Lauri Ojansivu a2cb74a54e Record parent-control coverage and remaining translation backlog
Thanks to xet7 !
2026-10-07 11:38:19 +03:00
Lauri Ojansivu d19b35b6f9 Fill remaining parent-card controls and verify all-locale coverage
Add Tigre and Wolaytta drafts with low-confidence limitations; remove three keys from the pending inventory after checking all 234 non-English locales.

Thanks to xet7 !
2026-10-07 11:37:55 +03:00
Lauri Ojansivu c857b38f8b Record Cherokee parent-control translation progress
Thanks to xet7 !
2026-10-07 11:36:15 +03:00
Lauri Ojansivu a129d9709e Translate parent-card controls into Cherokee
Record low-confidence phrasing and extend locale and browser coverage.

Thanks to xet7 !
2026-10-07 11:35:57 +03:00
Lauri Ojansivu 103a8008c2 Record Nahuatl and Moroccan Tamazight parent-control progress
Thanks to xet7 !
2026-10-07 11:34:50 +03:00
Lauri Ojansivu 3c79e6d538 Translate parent-card controls into Nahuatl and Moroccan Tamazight
Record low-confidence phrasing and extend locale and browser regression coverage.

Thanks to xet7 !
2026-10-07 11:34:33 +03:00
Lauri Ojansivu 8cd73901ea Record Fulah and Veps parent-control progress
Thanks to xet7 !
2026-10-07 11:33:28 +03:00
Lauri Ojansivu 73030eb1c5 Translate parent-card controls into Fulah and Veps
Record low-confidence wording and extend locale and browser coverage.

Thanks to xet7 !
2026-10-07 11:33:13 +03:00
Lauri Ojansivu 578a73e805 Record Klingon and Volapuk parent-control progress
Thanks to xet7 !
2026-10-07 11:32:08 +03:00
Lauri Ojansivu 4e372a695f Translate parent-card controls into Klingon and Volapuk
Extend regression coverage and document provisional software metaphors.

Thanks to xet7 !
2026-10-07 11:31:49 +03:00
Lauri Ojansivu 43594cb566 Record Inuktitut and Greenlandic parent-control progress
Thanks to xet7 !
2026-10-07 11:30:34 +03:00
Lauri Ojansivu 14177f58d0 Merge pull request #6747 from wekan/dependabot/npm_and_yarn/npm_and_yarn-77d2d34402
Bump the npm_and_yarn group across 1 directory with 2 updates
2026-10-07 11:30:22 +03:00
Lauri Ojansivu 6005d0ab61 Translate parent-card controls into Inuktitut and Greenlandic
Record low-confidence phrasing and extend locale and browser regression coverage.

Thanks to xet7 !
2026-10-07 11:30:10 +03:00
Lauri Ojansivu 6eff098dce Record five-locale parent-control translation progress
Thanks to xet7 !
2026-10-07 11:29:01 +03:00
Lauri Ojansivu 333561b9ea Translate parent-card controls in five more locales
Cover Moroccan Arabic Manx Venetian Aromanian and Northern Sami; document low-confidence terminology and extend regression coverage.

Thanks to xet7 !
2026-10-07 11:28:45 +03:00
Lauri Ojansivu f87c279cdb Record Quechua Aymara and Guarani parent-control progress
Thanks to xet7 !
2026-10-07 11:27:21 +03:00
Lauri Ojansivu 89b4a55333 Translate parent-card controls into Quechua Aymara and Guarani
Extend regression coverage and record provisional technical terminology.

Thanks to xet7 !
2026-10-07 11:27:05 +03:00
Lauri Ojansivu c3a8b9911c Record Tibetan Dzongkha and Tigrinya parent-control progress
Thanks to xet7 !
2026-10-07 11:25:53 +03:00
Lauri Ojansivu 813c9baa25 Translate parent-card controls into Tibetan Dzongkha and Tigrinya
Extend regression coverage and record provisional technical wording.

Thanks to xet7 !
2026-10-07 11:25:39 +03:00
Lauri Ojansivu 13bb046beb Record Buryat Chuvash and Sakha parent-control progress
Thanks to xet7 !
2026-10-07 11:24:16 +03:00
Lauri Ojansivu 00011c073c Translate parent-card controls into Buryat Chuvash and Sakha
Extend regression coverage and document provisional technical terminology and inflections.

Thanks to xet7 !
2026-10-07 11:24:00 +03:00
Lauri Ojansivu f6c6306b61 Record four-locale parent-control translation progress
Thanks to xet7 !
2026-10-07 11:22:49 +03:00
Lauri Ojansivu 69598b56d8 Translate parent-card controls in four more locales
Cover Papiamento, Walloon, Waray and Acehnese; record low-confidence wording and extend regression coverage.

Thanks to xet7 !
2026-10-07 11:22:33 +03:00
Lauri Ojansivu 3a12332afc Record further African parent-control translation progress
Thanks to xet7 !
2026-10-07 11:21:16 +03:00
Lauri Ojansivu b6a25535cd Translate parent-card controls in eight more African locales
Extend regression coverage and document provisional technical wording and vocabulary references.

Thanks to xet7 !
2026-10-07 11:20:58 +03:00
Lauri Ojansivu a928a11fd1 Record Pacific parent-control translation progress
Thanks to xet7 !
2026-10-07 11:19:06 +03:00
Lauri Ojansivu 797d236c7b Translate parent-card controls in seven Pacific locales
Extend regression coverage and document provisional technical wording.

Thanks to xet7 !
2026-10-07 11:18:20 +03:00
Lauri Ojansivu 1aee8ef8a0 Record southern African parent-control translation progress
Thanks to xet7 !
2026-10-07 11:16:35 +03:00
Lauri Ojansivu d58d0ffa4e Translate parent-card controls in southern African locales
Extend regression coverage and record provisional terminology and language-quality limitations.

Thanks to xet7 !
2026-10-07 11:16:16 +03:00
Lauri Ojansivu 2c075e0f40 Record South Asian parent-control translation progress
Thanks to xet7 !
2026-10-07 11:14:37 +03:00
Lauri Ojansivu 0d484b2e17 Translate parent-card controls in five South Asian locales
Extend locale and browser coverage and record low-confidence Kashmiri phrasing.

Thanks to xet7 !
2026-10-07 11:14:16 +03:00
Lauri Ojansivu e18a937c74 Record parent-control translation progress
Thanks to xet7 !
2026-10-07 11:12:33 +03:00
Lauri Ojansivu f5990d89ac Translate multiple-parent controls in seven more locales
Clarify relationship removal and extend label and card-preservation coverage. Record provisional technical wording in the translation audit.

Thanks to xet7 !
2026-10-07 11:12:13 +03:00
Lauri Ojansivu 74c911ad48 Record map translation coverage and remaining backlog
Thanks to xet7 !
2026-10-07 11:09:59 +03:00
Lauri Ojansivu 399d8787fb Fill Wolaytta map strings and verify coverage across all locales
Document low-confidence wording and remove seven map keys from the pending inventory after checking all 234 non-English locale paths.

Thanks to xet7 !
2026-10-07 11:09:35 +03:00
Lauri Ojansivu 5434a7afef Record Tigre map translation progress
Thanks to xet7 !
2026-10-07 11:07:34 +03:00
Lauri Ojansivu 6131d3eed7 Translate map view into Tigre
Record low-confidence terminology and grammar limitations; extend locale and browser coverage.

Thanks to xet7 !
2026-10-07 11:07:17 +03:00
Lauri Ojansivu 968ca009f9 Record Cherokee map translation progress
Thanks to xet7 !
2026-10-07 11:05:44 +03:00
Lauri Ojansivu 29a4180ec3 Translate map view into Cherokee
Record low-confidence technical wording and vocabulary references; extend locale and browser regression coverage.

Thanks to xet7 !
2026-10-07 11:05:25 +03:00
Lauri Ojansivu 9bf8e03b46 Record Inuktitut map translation progress
Thanks to xet7 !
2026-10-07 11:03:48 +03:00
Lauri Ojansivu 9169523ae2 Translate map view into Inuktitut
Technical wording remains low confidence; record terminology evidence and extend locale and browser coverage.

Thanks to xet7 !
2026-10-07 11:03:28 +03:00
Lauri Ojansivu aceb1071b4 Record Nahuatl and Moroccan Tamazight map translation progress
Thanks to xet7 !
2026-10-07 11:01:39 +03:00
Lauri Ojansivu f23a9d2a88 Translate map view for Nahuatl and Moroccan Tamazight
Fill 14 placeholders and extend locale and browser coverage. Technical sentences are low-confidence drafts; floor-plan and map/card wording require language review. Focused checks pass; browser execution remains pending.

Thanks to xet7 !
2026-10-07 10:58:30 +03:00
Lauri Ojansivu 1a6d012815 Record map translation progress for Veps and Greenlandic
Thanks to xet7 !
2026-10-07 10:55:24 +03:00
Lauri Ojansivu f4b7124442 Translate map view for Veps and Greenlandic
Fill 14 placeholders and extend locale and browser coverage. Veps compounds are low-confidence drafts; Greenlandic technical wording remains provisional. Focused checks pass; browser execution remains pending.

Thanks to xet7 !
2026-10-07 10:55:05 +03:00
Lauri Ojansivu 4eccb8abe3 Record map translation progress for Klingon and Volapuk
Thanks to xet7 !
2026-10-07 10:53:37 +03:00
Lauri Ojansivu 15e8f59fba Translate map view for Klingon and Volapuk
Fill 14 placeholders and extend locale and browser coverage. Check map, drag, drawing and click vocabulary against dictionaries. Full technical sentences remain provisional. Focused checks pass; browser execution remains pending.

Thanks to xet7 !
2026-10-07 10:53:15 +03:00
Lauri Ojansivu 571f6487b7 Record map translation progress for Northern Sami Fulah and Kashmiri
Thanks to xet7 !
2026-10-07 10:50:58 +03:00
Lauri Ojansivu d6b4fe7e57 Translate map view for Northern Sami Fulah and Kashmiri
Fill 21 placeholders and extend locale and browser coverage. Fulah and Kashmiri technical clauses are low-confidence drafts; floor-plan wording remains provisional. Focused checks pass; browser execution remains pending.

Thanks to xet7 !
2026-10-07 10:50:38 +03:00
Lauri Ojansivu d1c7731b72 Record map translation progress for Quechua Aymara and Guarani
Thanks to xet7 !
2026-10-07 10:48:52 +03:00
Lauri Ojansivu dc4bfee6d9 Translate map view for Quechua Aymara and Guarani
Fill 21 placeholders and extend locale and browser coverage. Map compounds, floor-plan and upload wording remain provisional. Focused checks pass; browser execution remains pending.

Thanks to xet7 !
2026-10-07 10:48:32 +03:00
Lauri Ojansivu e420f69964 Record map translation progress for Akan Ewe and Bambara
Thanks to xet7 !
2026-10-07 10:46:42 +03:00
Lauri Ojansivu 172e75c074 Translate map view for Akan Ewe and Bambara
Fill 21 placeholders and extend locale and browser coverage. Technical clauses remain provisional, particularly floor-plan, upload and click wording. Focused checks pass; browser execution remains pending.

Thanks to xet7 !
2026-10-07 10:46:24 +03:00
Lauri Ojansivu aeba69a1d9 Record map translation progress for Manx Venetian and Aromanian
Thanks to xet7 !
2026-10-07 10:44:31 +03:00
Lauri Ojansivu e064776ff1 Translate map view for Manx Venetian and Aromanian
Fill 21 placeholders and extend locale and browser coverage. Aromanian technical clauses are low-confidence drafts; floor-plan wording remains provisional. Focused checks pass; browser execution remains pending.

Thanks to xet7 !
2026-10-07 10:44:06 +03:00
Lauri Ojansivu b36ec72a22 Record map translation progress for Tibetan Dzongkha and Tigrinya
Thanks to xet7 !
2026-10-07 10:42:01 +03:00
Lauri Ojansivu 4cf96773d9 Translate map view for Tibetan Dzongkha and Tigrinya
Fill 21 placeholders and extend locale and browser coverage. Technical wording remains provisional, particularly Dzongkha floor-plan terminology. Focused checks pass; browser execution remains pending.

Thanks to xet7 !
2026-10-07 10:41:41 +03:00
Lauri Ojansivu a31ada8304 Record map translation progress for three locales
Thanks to xet7 !
2026-10-07 10:39:12 +03:00
Lauri Ojansivu 3e73aa11f9 Translate map view for Buryat Chuvash and Sakha
Fill 21 placeholders and extend locale and browser coverage. Technical sentences remain provisional, particularly Chuvash floor-plan wording. Focused checks pass; browser execution remains pending.

Thanks to xet7 !
2026-10-07 10:38:55 +03:00
Lauri Ojansivu 1070392002 Record map translation progress for five more locales
Thanks to xet7 !
2026-10-07 10:37:03 +03:00
Lauri Ojansivu ba1845e9ee Translate map view for Waray Fijian Tongan Luganda and Wolof
Fill 35 placeholders and extend locale and browser coverage. Technical phrases, especially floor-plan wording and the Wolof map/card distinction, remain provisional. Focused checks pass; browser execution remains pending.

Thanks to xet7 !
2026-10-07 10:36:44 +03:00
Lauri Ojansivu 9db5a91958 Record four-language map translation progress
Thanks to xet7 !
2026-10-07 10:34:45 +03:00
Lauri Ojansivu 3008049d23 Translate map view for Papiamento Walloon Acehnese and Hawaiian
Fill 28 placeholders and extend locale and browser coverage. Walloon and Acehnese technical phrases are low-confidence drafts; floor-plan wording remains provisional. Focused checks pass; browser execution remains pending.

Thanks to xet7 !
2026-10-07 10:34:25 +03:00
Lauri Ojansivu 4febb21e63 Record map translation progress for four Pacific locales
Thanks to xet7 !
2026-10-07 10:32:30 +03:00
Lauri Ojansivu 44e3865673 Translate map view for Bislama Tok Pisin Maori and Samoan
Fill 28 placeholders and extend locale and browser coverage. Floor-plan phrases and Samoan UI wording remain provisional. Focused checks pass; browser execution remains pending.

Thanks to xet7 !
2026-10-07 10:32:06 +03:00
Lauri Ojansivu 6c068aac96 Record map translation progress for four locales
Thanks to xet7 !
2026-10-07 10:30:15 +03:00
Lauri Ojansivu 0b1aab5795 Translate map view for Swati Ndebele Tsonga and Venda
Fill 28 placeholders and extend locale and browser coverage. Technical wording remains provisional, especially floor-plan and click terms. Focused checks pass; browser execution remains pending.

Thanks to xet7 !
2026-10-07 10:29:50 +03:00
Lauri Ojansivu 32f840ce0b Record map translation progress across six locales
Thanks to xet7 !
2026-10-07 10:27:55 +03:00
Lauri Ojansivu 521228a31b Translate map view across six southern African locales
Fill 42 placeholders for Sesotho, Setswana, Northern Sotho, Zulu and Xhosa. Floor-plan terminology remains provisional, especially Northern Sotho. Extend locale and browser coverage; focused checks pass and browser execution remains pending.

Thanks to xet7 !
2026-10-07 10:27:35 +03:00
Lauri Ojansivu fbd74d7bdc Record map translation progress for five African locales
Thanks to xet7 !
2026-10-07 10:25:49 +03:00
Lauri Ojansivu a394fc63bc Translate map view for five African language locales
Fill 35 Somali, Oromo, Kinyarwanda, Kirundi and Chichewa placeholders. Distinguish map and task card terms in Kinyarwanda and Kirundi. Technical sentences and floor-plan terms remain provisional. Extend regression and browser coverage; browser execution remains pending.

Thanks to xet7 !
2026-10-07 10:25:06 +03:00
Lauri Ojansivu 68dda39afd Record map translation progress for five locales
Thanks to xet7 !
2026-10-07 10:23:12 +03:00
Lauri Ojansivu 5b02a46d52 Translate map view into five more languages
Fill 35 Moroccan Arabic, Bhojpuri, Maithili, Odia and Konkani placeholders. Technical phrases remain provisional, particularly Konkani floor-plan wording. Extend locale and browser coverage; focused tests pass and browser execution remains pending.

Thanks to xet7 !
2026-10-07 10:22:49 +03:00
Lauri Ojansivu d03465c69b Record five-language map translation progress
Thanks to xet7 !
2026-10-07 10:20:13 +03:00
Lauri Ojansivu bab737e364 Translate map view for Kurdish Sorani Tatar Turkmen and Yiddish
Fill 35 placeholders and extend locale and browser checks. Technical wording remains provisional, particularly Turkmen. Focused and human-preference checks pass; browser execution remains pending.

Thanks to xet7 !
2026-10-07 10:19:51 +03:00
Lauri Ojansivu e66a674019 Record all-locale history recovery translation coverage
Thanks to xet7 !
2026-10-07 10:17:21 +03:00
Lauri Ojansivu 208c052c84 Complete history recovery placeholder coverage across locales
Fill five Cherokee recovery messages with provisional low-confidence wording. Extend coverage to all 234 non-English locales and remove the five completed keys from the pending queue. Add the Cherokee browser scenario; focused and human-preference checks pass, browser execution remains pending.

Thanks to xet7 !
2026-10-07 10:16:56 +03:00
Lauri Ojansivu 5492807d39 Record Tigre recovery translation progress
Thanks to xet7 !
2026-10-07 10:12:24 +03:00
Lauri Ojansivu 69b151f2e5 Translate history recovery in Tigre
Add recovery drafts and regression coverage. Full sentences and technical phrasing remain low confidence and need language review.

Thanks to xet7 !
2026-10-07 10:12:02 +03:00
Lauri Ojansivu 37adb996e8 Record Wolaytta recovery translation progress
Thanks to xet7 !
2026-10-07 10:10:19 +03:00
Lauri Ojansivu 9daaa68ccd Translate history recovery in Wolaytta
Add recovery drafts and regression coverage. Full sentences and technical phrasing remain low confidence and need language review.

Thanks to xet7 !
2026-10-07 10:09:57 +03:00
Lauri Ojansivu 0934be558e Record Inuktitut recovery translation progress
Thanks to xet7 !
2026-10-07 10:08:33 +03:00
Lauri Ojansivu aade6ac1ec Translate history recovery in Inuktitut
Add recovery drafts and regression coverage. Full sentences and technical phrasing remain low confidence and need language review.

Thanks to xet7 !
2026-10-07 10:08:12 +03:00
Lauri Ojansivu 9db00c4fc9 Record Nahuatl and Tamazight recovery translation progress
Thanks to xet7 !
2026-10-07 10:06:07 +03:00
Lauri Ojansivu d5666d8d5d Translate history recovery in Nahuatl and Tamazight
Add recovery drafts and regression coverage. Full sentences and technical phrasing remain low confidence and need language review.

Thanks to xet7 !
2026-10-07 10:05:49 +03:00
Lauri Ojansivu c467fd94e2 Record Veps and Greenlandic recovery translation progress
Thanks to xet7 !
2026-10-07 10:03:51 +03:00
Lauri Ojansivu fafca4f49e Translate history recovery in Veps and Greenlandic
Add recovery drafts and regression coverage. Full sentences and technical phrasing remain low confidence and need language review.

Thanks to xet7 !
2026-10-07 10:03:33 +03:00
Lauri Ojansivu 679de4ff7b Record Klingon and Volapuk recovery translation progress
Thanks to xet7 !
2026-10-07 10:01:38 +03:00
Lauri Ojansivu 4708cfacb8 Translate history recovery in Klingon and Volapuk
Add recovery drafts and regression coverage. Full sentences and technical phrasing remain provisional for language review.

Thanks to xet7 !
2026-10-07 10:01:10 +03:00
Lauri Ojansivu a4428cfe8c Record Northern Sami Fulah and Kashmiri recovery translation progress
Thanks to xet7 !
2026-10-07 09:59:17 +03:00
Lauri Ojansivu 4e99ccda86 Translate history recovery in Northern Sami Fulah and Kashmiri
Add recovery drafts and regression coverage. Full sentences and technical phrasing remain low confidence and need language review.

Thanks to xet7 !
2026-10-07 09:58:56 +03:00
Lauri Ojansivu fa5064c57b Record Quechua Aymara and Guarani recovery translation progress
Thanks to xet7 !
2026-10-07 09:56:18 +03:00
Lauri Ojansivu ef2dcedf2a Translate history recovery in Quechua Aymara and Guarani
Add recovery drafts and regression coverage. Full sentences and technical phrasing remain low confidence and need language review.

Thanks to xet7 !
2026-10-07 09:55:58 +03:00
Lauri Ojansivu 5e8a3a9b7b Record Akan Ewe and Bambara recovery translation progress
Thanks to xet7 !
2026-10-07 09:54:24 +03:00
Lauri Ojansivu 119e6648c8 Translate history recovery in Akan Ewe and Bambara
Add recovery drafts and regression coverage. Full sentences and technical phrasing remain low confidence and need language review.

Thanks to xet7 !
2026-10-07 09:54:08 +03:00
Lauri Ojansivu b54aa078c9 Record Manx Venetian and Aromanian recovery translation progress
Thanks to xet7 !
2026-10-07 09:52:27 +03:00
Lauri Ojansivu 291e4a4e5f Translate history recovery in Manx Venetian and Aromanian
Add recovery drafts and regression coverage. Full sentences and technical phrasing remain low confidence and need language review.

Thanks to xet7 !
2026-10-07 09:52:07 +03:00
Lauri Ojansivu 5bd556d8af Record Tibetan Dzongkha and Tigrinya recovery translation progress
Thanks to xet7 !
2026-10-07 09:50:11 +03:00
Lauri Ojansivu ee0cddff89 Translate history recovery in Tibetan Dzongkha and Tigrinya
Add recovery drafts and regression coverage. Full sentences and technical phrasing remain low confidence and need language review.

Thanks to xet7 !
2026-10-07 09:49:46 +03:00
Lauri Ojansivu d3c5d1750c Record Buryat Chuvash and Sakha recovery translation progress
Thanks to xet7 !
2026-10-07 09:47:20 +03:00
Lauri Ojansivu 4bb22322f5 Translate history recovery in Buryat Chuvash and Sakha
Add recovery drafts and regression coverage. Full sentences and technical phrasing remain low confidence and need language review.

Thanks to xet7 !
2026-10-07 09:47:02 +03:00
Lauri Ojansivu c7e36b5afc Record five-language history recovery translation progress
Thanks to xet7 !
2026-10-07 09:45:13 +03:00
Lauri Ojansivu 0f9c719692 Translate history recovery in five more languages
Add Waray, Fijian, Tongan, Luganda and Wolof drafts with regression coverage. Full clauses remain provisional for language review.

Thanks to xet7 !
2026-10-07 09:44:53 +03:00
Lauri Ojansivu 547c043bdc Record four more history recovery translation locales
Thanks to xet7 !
2026-10-07 09:42:32 +03:00
Lauri Ojansivu 5f7f293639 Translate history recovery in Papiamentu Walloon Acehnese and Hawaiian
Keep sentence-level translations provisional for language review.

Thanks to xet7 !
2026-10-07 09:42:12 +03:00
Lauri Ojansivu bbec2e5b21 Record Pacific history recovery translation progress
Thanks to xet7 !
2026-10-07 09:40:34 +03:00
Lauri Ojansivu 8a52901739 Translate history recovery in Bislama Tok Pisin Maori and Samoan
Fill twenty recovery messages and extend regression coverage. Full clauses and software terminology remain provisional pending language review.

Thanks to xet7 !
2026-10-07 09:38:04 +03:00
Lauri Ojansivu c108b2c8cd Record four-language history recovery translation progress
Thanks to xet7 !
2026-10-07 09:36:43 +03:00
Lauri Ojansivu 57afc10e49 Translate history recovery in Swati Ndebele Tsonga and Venda
Fill twenty recovery messages and extend regression coverage. Full clauses and software terminology remain provisional pending language review.

Thanks to xet7 !
2026-10-07 09:36:29 +03:00
Lauri Ojansivu 07d1b137b4 Record southern African history recovery translation progress
Thanks to xet7 !
2026-10-07 09:35:05 +03:00
Lauri Ojansivu c3c1a3ddfb Translate history recovery in southern African locales
Fill Southern Sotho, Tswana, Northern Sotho, Zulu and Xhosa recovery messages and extend regression coverage. Full clauses and technical terminology remain provisional pending language review.

Thanks to xet7 !
2026-10-07 09:34:45 +03:00
Lauri Ojansivu 0e7ca8dce3 Record African-language history recovery translation progress
Thanks to xet7 !
2026-10-07 09:33:29 +03:00
Lauri Ojansivu 5349fc057c Translate history recovery in five African languages
Fill Somali, Oromo, Kinyarwanda, Kirundi and Chichewa recovery messages and extend regression coverage. Full clauses and software terms remain provisional pending language review.

Thanks to xet7 !
2026-10-07 09:33:12 +03:00
Lauri Ojansivu 5234935193 Record additional history recovery translation progress
Thanks to xet7 !
2026-10-07 09:31:39 +03:00
Lauri Ojansivu 18c0b64026 Translate more history recovery messages
Fill Moroccan Arabic, Bhojpuri, Maithili, Odia and Konkani recovery messages and extend regression coverage. Full clauses and software terminology remain provisional pending language review.

Thanks to xet7 !
2026-10-07 09:31:01 +03:00
Lauri Ojansivu 608b54a53a Record five-language history recovery translation progress
Thanks to xet7 !
2026-10-07 09:29:20 +03:00
Lauri Ojansivu 0acb21a162 Translate history recovery in five languages
Fill Kurmanji, Sorani, Tatar, Turkmen and Yiddish recovery messages, preserving undo/redo and retry/forget distinctions. Extend the existing browser scenario and token coverage. Full clauses and minority-language software terms remain provisional pending language review.

Thanks to xet7 !
2026-10-07 09:29:04 +03:00
Lauri Ojansivu 6acb217c5b Record all-locale import-report translation coverage
Thanks to xet7 !
2026-10-07 09:26:53 +03:00
Lauri Ojansivu 44cecb2cb7 Complete import-report placeholder coverage across locales
Translate the remaining Cherokee messages, extend regression coverage to all 234 non-English locales and remove the three filled keys from the pending inventory. Preserve RTL navigation arrows. Cherokee full sentences and technical phrasing remain low confidence pending language review.

Thanks to xet7 !
2026-10-07 09:26:35 +03:00
Lauri Ojansivu 64ba65c6d0 Record Tigre import-report translation progress
Thanks to xet7 !
2026-10-07 09:24:20 +03:00
Lauri Ojansivu 802440b7c0 Translate import reports in Tigre
Fill three messages and replace the Tigrinya Admin Panel phrase. Full sentences, warning paraphrase and technical terminology remain low confidence pending language review.

Thanks to xet7 !
2026-10-07 09:24:02 +03:00
Lauri Ojansivu 08f17c0a5a Record Tamazight import-report translation progress
Thanks to xet7 !
2026-10-07 09:21:52 +03:00
Lauri Ojansivu f6400c6f9f Translate import reports in Tamazight
Fill three messages and replace the Arabic Admin Panel seed with Tamazight wording. Full sentences and technical terminology remain low confidence pending language review.

Thanks to xet7 !
2026-10-07 09:21:37 +03:00
Lauri Ojansivu d39f3723aa Record Inuktitut import-report translation progress
Thanks to xet7 !
2026-10-07 09:20:05 +03:00
Lauri Ojansivu 34bfeb77ca Translate import reports in Inuktitut
Fill three messages with the recovery navigation path and extend regression coverage. Full sentences and software terminology remain low confidence pending language review.

Thanks to xet7 !
2026-10-07 09:19:49 +03:00
Lauri Ojansivu 452a7e8dde Record Wolaytta import-report translation progress
Thanks to xet7 !
2026-10-07 09:18:32 +03:00
Lauri Ojansivu 9af64cf719 Translate import reports in Wolaytta
Fill three messages and replace two prefixed-English recovery menu labels. Full sentences and technical terminology remain low confidence pending language review.

Thanks to xet7 !
2026-10-07 09:18:19 +03:00
Lauri Ojansivu 6205373199 Record Greenlandic and Nahuatl translation progress
Thanks to xet7 !
2026-10-07 09:16:41 +03:00
Lauri Ojansivu ae9af70f64 Translate import reports in Greenlandic and Nahuatl
Fill six import-report messages with preserved navigation paths and extend regression coverage. Full clauses and technical terminology remain low confidence pending language review.

Thanks to xet7 !
2026-10-07 09:16:23 +03:00
Lauri Ojansivu 94ce1e0d57 Record Veps import-report translation progress
Thanks to xet7 !
2026-10-07 09:14:30 +03:00
Lauri Ojansivu 90e7223559 Translate import reports in Veps
Fill import report messages and replace the Venda recovery-menu seed. Veps clauses, warning paraphrase and derived recovery terminology remain low confidence pending language review.

Thanks to xet7 !
2026-10-07 09:14:12 +03:00
Lauri Ojansivu 5a8587901e Record Volapuk import-report translation progress
Thanks to xet7 !
2026-10-07 09:10:21 +03:00
Lauri Ojansivu ec3a8cd798 Translate import reports in Volapuk
Fill three English placeholders using dictionary vocabulary and extend regression coverage. Passive tense, agreement and technical use of the import verb have low confidence and need fluent review. Browser coverage was syntax-checked only.

Thanks to xet7 !
2026-10-07 09:10:06 +03:00
Lauri Ojansivu b485a136e1 Record Tigrinya and Klingon import translations
Thanks to xet7 !
2026-10-07 09:08:57 +03:00
Lauri Ojansivu d94f256dcb Translate import reports in Tigrinya and Klingon
Fill six English values and extend source-order, token and localized browser coverage. Technical phrasing and agreement in both drafts have low confidence and require fluent review. Browser cases were syntax-checked only.

Thanks to xet7 !
2026-10-07 09:08:40 +03:00
Lauri Ojansivu 4c1c1ebbed Record Guarani Quechua and Aymara import translations
Thanks to xet7 !
2026-10-07 09:06:59 +03:00
Lauri Ojansivu b6e0289a42 Translate import reports in Guarani Quechua and Aymara
Fill nine English values, replace four prefixed English menu labels and extend regression coverage. All three drafts have low-confidence technical nouns, incomplete-transfer clauses and inflection requiring fluent review. Browser cases were syntax-checked only.

Thanks to xet7 !
2026-10-07 09:06:45 +03:00
Lauri Ojansivu c55955aa42 Record Manx Walloon and Aromanian import translations
Thanks to xet7 !
2026-10-07 09:05:00 +03:00
Lauri Ojansivu cdd93651ca Translate import reports in Manx Walloon and Aromanian
Fill nine English placeholders and extend source-order, token and localized browser coverage. Technical phrasing, Aromanian derivation and grammatical agreement have low confidence and need fluent review. Browser cases were syntax-checked only.

Thanks to xet7 !
2026-10-07 09:04:43 +03:00
Lauri Ojansivu b995f3a4a3 Record Tibetan Dzongkha and Kashmiri import translations
Thanks to xet7 !
2026-10-07 09:03:04 +03:00
Lauri Ojansivu 9b9a31cc10 Translate import reports in Tibetan Dzongkha and Kashmiri
Fill nine English placeholders and extend source-order, token and localized browser coverage. Technical phrasing and grammatical agreement in all three drafts have low confidence and need fluent review. Browser cases were syntax-checked only.

Thanks to xet7 !
2026-10-07 09:02:49 +03:00
Lauri Ojansivu eda3162fe2 Record four-language import-report translation progress
Thanks to xet7 !
2026-10-07 09:00:37 +03:00
Lauri Ojansivu ec468ac7ea Translate import reports in Buryat Chuvash Sakha and Northern Sami
Fill twelve English placeholders and extend source-order, token and localized browser coverage. All four drafts have low-confidence case endings, passive and negative constructions, and technical phrasing requiring fluent review. Browser cases were syntax-checked only.

Thanks to xet7 !
2026-10-07 09:00:21 +03:00
Lauri Ojansivu 31171906e4 Record six-language import-report translation progress
Thanks to xet7 !
2026-10-07 08:58:39 +03:00
Lauri Ojansivu 14fa33a3ff Translate import reports in six further languages
Fill 18 English values and correct two prefixed English Tongan menu labels. Extend token, source-order, vocabulary and localized browser coverage. Acehnese, Bambara, Ewe and Fulah phrasing has low confidence and needs fluent review. Browser cases were syntax-checked only.

Thanks to xet7 !
2026-10-07 08:58:23 +03:00
Lauri Ojansivu 2a92631bbd Record import translations and recovery-path corrections
Thanks to xet7 !
2026-10-07 08:56:30 +03:00
Lauri Ojansivu b6d6908fad Translate eight more import reports and repair recovery paths
Fill 24 English values and correct eleven generic or wrong-language menu labels. Extend source-order, token, vocabulary and localized browser coverage. Wolof, Swati, Venda, Waray and Venetian phrasing has lower confidence and needs fluent review. Browser cases were syntax-checked only.

Thanks to xet7 !
2026-10-07 08:56:14 +03:00
Lauri Ojansivu 6884189439 Record import report translations and menu repairs
Thanks to xet7 !
2026-10-07 08:53:49 +03:00
Lauri Ojansivu 4ab324cfeb Translate import reports in eight locales and repair recovery labels
Fill 24 English values, replace five mixed-language or malformed menu values and extend regression coverage. Samoan, Hawaiian, Papiamentu and Northern Ndebele technical phrasing has lower confidence and needs fluent review. Browser cases were syntax-checked only.

Thanks to xet7 !
2026-10-07 08:53:34 +03:00
Lauri Ojansivu fe8a7627b6 Record African locale import-report translation progress
Thanks to xet7 !
2026-10-07 08:51:22 +03:00
Lauri Ojansivu b53206d9df Translate import reports in ten African locale files
Fill 30 English placeholders and extend source-order, token and localized browser coverage. Preserve existing translations and the recovery menu path. Oromo, Kirundi, Chichewa and Northern Sotho technical phrasing has lower confidence and needs fluent review. Browser cases were syntax-checked only.

Thanks to xet7 !
2026-10-07 08:51:08 +03:00
Lauri Ojansivu f8fe06bc79 Record import warning translation progress
Thanks to xet7 !
2026-10-07 08:49:20 +03:00
Lauri Ojansivu 8e0084b1c7 Translate import warning reports in ten further languages
Fill 30 English placeholders and correct two Tatar recovery menu labels. Preserve source order, tokens and existing translations. Add report text and rejected-input UI coverage.

Konkani, Bhojpuri, Maithili, Turkmen and Kurdish technical phrasing has lower confidence and needs fluent review. Browser cases were syntax-checked only.

Thanks to xet7 !
2026-10-07 08:49:01 +03:00
Lauri Ojansivu 6b1f6fa1c7 Record all-locale board visibility translation progress
Thanks to xet7 !
2026-10-07 08:43:15 +03:00
Lauri Ojansivu d5aa3a4f10 Fill final board visibility translations and verify all locales
Add three Cherokee drafts and extend board visibility translation coverage to every non-English locale. Remove the filled group from the pending inventory. Record low-confidence Cherokee grammar and technical phrasing for fluent-speaker review.

Thanks to xet7 !
2026-10-07 08:42:58 +03:00
Lauri Ojansivu 3db01ccb00 Record Tigre board visibility translation progress
Thanks to xet7 !
2026-10-07 08:41:29 +03:00
Lauri Ojansivu 1365619b6a Translate board visibility in Tigre
Fill three English placeholders and extend translation regression coverage. Document grammar references and low-confidence software vocabulary and agreement for fluent-speaker review.

Thanks to xet7 !
2026-10-07 08:41:16 +03:00
Lauri Ojansivu 411d8246ac Record Wolaytta board visibility translation progress
Thanks to xet7 !
2026-10-07 08:40:00 +03:00
Lauri Ojansivu feea910049 Translate board visibility in Wolaytta
Fill three English placeholders and extend translation regression coverage. Document low-confidence login phrasing, negation and membership wording for fluent-speaker review.

Thanks to xet7 !
2026-10-07 08:39:42 +03:00
Lauri Ojansivu e80d0797fa Record Inuktitut board visibility translation progress
Thanks to xet7 !
2026-10-07 08:38:27 +03:00
Lauri Ojansivu bbc75e16f4 Translate board visibility in Inuktitut
Fill three English placeholders in syllabics and extend translation regression coverage. Document low-confidence technical phrasing and inflection for fluent-speaker review.

Thanks to xet7 !
2026-10-07 08:37:57 +03:00
Lauri Ojansivu cd79c98337 Record Tamazight board visibility translation progress
Thanks to xet7 !
2026-10-07 08:36:42 +03:00
Lauri Ojansivu 64ed67170d Translate board visibility in Standard Moroccan Tamazight
Fill three English placeholders in Tifinagh and extend translation regression coverage. Document low-confidence login phrasing, passive morphology and dialect consistency for fluent-speaker review.

Thanks to xet7 !
2026-10-07 08:36:29 +03:00
Lauri Ojansivu 27ccf2bb70 Record Nahuatl board visibility translation progress
Thanks to xet7 !
2026-10-07 08:35:18 +03:00
Lauri Ojansivu 6a516f1442 Translate board visibility in Nahuatl
Fill three English placeholders and extend translation regression coverage. Document references and low-confidence software terminology, passive construction and dialect consistency for fluent-speaker review.

Thanks to xet7 !
2026-10-07 08:35:05 +03:00
Lauri Ojansivu 0c0f635f40 Record Greenlandic board visibility translation progress
Thanks to xet7 !
2026-10-07 08:33:52 +03:00
Lauri Ojansivu ab8fd1d8ab Translate board visibility in Greenlandic
Fill three English placeholders and extend translation regression coverage. Document vocabulary references and lower-confidence technical phrasing and inflection for fluent-speaker review.

Thanks to xet7 !
2026-10-07 08:33:35 +03:00
Lauri Ojansivu 78913e1303 Record Veps board visibility translation progress
Thanks to xet7 !
2026-10-07 08:32:25 +03:00
Lauri Ojansivu 053ed2d22b Translate board visibility in Veps
Fill three English placeholders and extend translation regression coverage. Record references and low-confidence login phrasing and case endings for fluent-speaker review.

Thanks to xet7 !
2026-10-07 08:31:59 +03:00
Lauri Ojansivu 1e10c6567e Record Volapuk board visibility translation progress
Thanks to xet7 !
2026-10-07 08:30:38 +03:00
Lauri Ojansivu 6b1005d653 Translate board visibility in Volapuk
Fill three English placeholders and extend translation regression coverage. Document vocabulary references and lower-confidence technical phrasing for fluent-speaker review.

Thanks to xet7 !
2026-10-07 08:30:25 +03:00
Lauri Ojansivu ff4be9ebfb Record Tigrinya and Klingon translation progress
Thanks to xet7 !
2026-10-07 08:29:20 +03:00
Lauri Ojansivu 712e503516 Translate board visibility in Tigrinya and Klingon
Fill six English placeholders and extend token and markup regression coverage. Document lower-confidence technical phrasing and Klingon clause structure for fluent-speaker review.

Thanks to xet7 !
2026-10-07 08:29:07 +03:00
Lauri Ojansivu d1ae962eeb Record Guarani Quechua and Aymara translation progress
Thanks to xet7 !
2026-10-07 08:27:15 +03:00
Lauri Ojansivu 6cfd9b64ea Translate board visibility in Guarani Quechua and Aymara
Fill nine English placeholders and extend translation regression coverage. Document vocabulary references and lower-confidence login phrasing and dialect choices for fluent-speaker review.

Thanks to xet7 !
2026-10-07 08:26:58 +03:00
Lauri Ojansivu 4221843ed5 Record Manx Walloon and Aromanian translation progress
Thanks to xet7 !
2026-10-07 08:25:44 +03:00
Lauri Ojansivu 168c9b97b0 Translate board visibility in Manx Walloon and Aromanian
Fill nine English placeholders and extend token and markup coverage. Document references and lower-confidence grammar and dialect choices for fluent-speaker review.

Thanks to xet7 !
2026-10-07 08:25:32 +03:00
Lauri Ojansivu ec4d07cc6e Record Tibetan Dzongkha and Kashmiri translation progress
Thanks to xet7 !
2026-10-07 08:23:55 +03:00
Lauri Ojansivu 4dbca343d7 Translate board visibility in Tibetan Dzongkha and Kashmiri
Fill nine English placeholders and extend translation regression coverage. Record lower-confidence technical phrasing and grammar for fluent-speaker review.

Thanks to xet7 !
2026-10-07 08:23:41 +03:00
Lauri Ojansivu fbbaed1042 Record four-locale board visibility translation progress
Thanks to xet7 !
2026-10-07 08:22:07 +03:00
Lauri Ojansivu 663833ed9a Translate board visibility in four further locales
Fill 12 English placeholders in Buryat, Chuvash, Sakha and Northern Sámi. Extend translation regression coverage and document terminology references and lower-confidence grammar for fluent-speaker review.

Thanks to xet7 !
2026-10-07 08:21:50 +03:00
Lauri Ojansivu b528a9dc85 Record six-locale board visibility translation progress
Thanks to xet7 !
2026-10-07 08:20:24 +03:00
Lauri Ojansivu 1b9c99d97d Translate board visibility in six further locales
Fill 18 English values in Acehnese, Bambara, Ewe, Fulah, Fijian and Tongan. Extend translation regression coverage and record lower-confidence login terminology and grammar for fluent-speaker review.

Thanks to xet7 !
2026-10-07 08:20:10 +03:00
Lauri Ojansivu 35bf6303c6 Record Akan through Venetian translation progress
Thanks to xet7 !
2026-10-07 08:18:38 +03:00
Lauri Ojansivu 8ac1110946 Translate board visibility in Akan through Venetian
Fill 24 English placeholders in eight locales and extend source-order, placeholder, markup and rendered-emphasis regression checks. Record lower-confidence technical phrasing for fluent-speaker review.

Thanks to xet7 !
2026-10-07 08:18:26 +03:00
Lauri Ojansivu fe7eb65666 Record eight-locale board visibility translation batch
Thanks to xet7 !
2026-10-07 08:16:44 +03:00
Lauri Ojansivu 5e46d3e208 Translate board visibility in eight further locales
Fill 24 English values and extend token, markup and rendered-emphasis coverage. Record lower-confidence Hawaiian, Samoan and Northern Ndebele phrasing for fluent-speaker review.

Thanks to xet7 !
2026-10-07 08:16:30 +03:00
Lauri Ojansivu af436105f4 Record further board visibility translation progress
Thanks to xet7 !
2026-10-07 08:15:12 +03:00
Lauri Ojansivu abcdd5c352 Translate board visibility in ten more locales
Fill 30 English placeholders and extend markup and token regression coverage. Record lower-confidence Oromo, Kirundi and Sotho–Tswana wording for fluent-speaker review.

Thanks to xet7 !
2026-10-07 08:14:58 +03:00
Lauri Ojansivu 5280e3cf6f Record signed-in board visibility translation progress
Thanks to xet7 !
2026-10-07 08:13:16 +03:00
Lauri Ojansivu b985c0c590 Translate signed-in board visibility in ten locales
Fill 30 English placeholders while preserving confirmation markup and source tokens. Add locale regression coverage and record remaining work in the translation audit.

Lower-confidence wording in Moroccan Arabic, Kurdish varieties, Bhojpuri, Maithili, Konkani and Yiddish needs fluent-speaker review.

Thanks to xet7 !
2026-10-07 08:12:53 +03:00
Lauri Ojansivu 19be27a029 Document all-locale URL hint coverage and remaining backlog
Thanks to xet7 !
2026-10-07 08:08:31 +03:00
Lauri Ojansivu 486543fc04 Fill Cherokee URL hint and verify all locale scheme identifiers
Discover all non-English locales in the regression suite and remove the filled hint from the pending inventory. Record low-confidence Cherokee wording and ongoing quality review.

Thanks to xet7 !
2026-10-07 08:08:16 +03:00
Lauri Ojansivu fa3b26a3f5 Document Tigre URL hint translation
Thanks to xet7 !
2026-10-07 08:06:02 +03:00
Lauri Ojansivu a8db74f371 Translate URL scheme hint in Tigre
Preserve literal scheme names and extend regression coverage. Document low-confidence technical wording and agreement for fluent review.

Thanks to xet7 !
2026-10-07 08:05:49 +03:00
Lauri Ojansivu 7d29c3cf9a Document Wolaytta URL hint translation
Thanks to xet7 !
2026-10-07 08:04:21 +03:00
Lauri Ojansivu c98ea17f1a Translate URL scheme hint in Wolaytta
Preserve literal scheme names and extend regression coverage. Document lower-confidence technical wording and grammatical agreement for review.

Thanks to xet7 !
2026-10-07 08:04:08 +03:00
Lauri Ojansivu d51bb02f29 Document Inuktitut URL hint translation
Thanks to xet7 !
2026-10-07 08:02:59 +03:00
Lauri Ojansivu aa5f00e7eb Translate URL scheme hint in Inuktitut
Preserve literal scheme names and extend regression coverage. Record lower-confidence technical wording and grammatical agreement for review.

Thanks to xet7 !
2026-10-07 08:02:46 +03:00
Lauri Ojansivu edfb352c92 Document Tamazight URL hint translation
Thanks to xet7 !
2026-10-07 08:01:05 +03:00
Lauri Ojansivu e6789dc75f Translate URL scheme hint in Standard Moroccan Tamazight
Preserve literal scheme names and extend regression coverage. Document lower-confidence terminology and grammatical agreement for review.

Thanks to xet7 !
2026-10-07 08:00:49 +03:00
Lauri Ojansivu 7a97b309f9 Document Nahuatl URL hint translation
Thanks to xet7 !
2026-10-07 07:59:32 +03:00
Lauri Ojansivu a6e89219ff Translate URL scheme hint in Nahuatl
Preserve literal scheme identifiers and extend regression coverage. Record lower-confidence regional grammar and technical wording for review.

Thanks to xet7 !
2026-10-07 07:59:01 +03:00
Lauri Ojansivu 7f278eec88 Document Klingon URL hint translation
Thanks to xet7 !
2026-10-07 07:57:54 +03:00
Lauri Ojansivu 1a6d51f502 Translate URL scheme hint in Klingon
Preserve literal scheme identifiers and extend regression coverage. Document lower-confidence technical phrasing for fluent-speaker review.

Thanks to xet7 !
2026-10-07 07:57:42 +03:00
Lauri Ojansivu 36351f3a74 Document Veps and Volapük URL hint translations
Thanks to xet7 !
2026-10-07 07:56:16 +03:00
Lauri Ojansivu bc81d9e999 Translate URL scheme hint in Veps and Volapük
Preserve scheme identifiers and extend regression coverage. Record lower-confidence technical wording and dictionary references for review.

Thanks to xet7 !
2026-10-07 07:56:03 +03:00
Lauri Ojansivu 4b8738acbb Document Greenlandic and Kashmiri URL hint translations
Thanks to xet7 !
2026-10-07 07:54:34 +03:00
Lauri Ojansivu f1d05258ab Translate URL scheme hint in Greenlandic and Kashmiri
Preserve literal scheme names and extend regression coverage. Document lower-confidence wording and outstanding mixed-script browser review.

Thanks to xet7 !
2026-10-07 07:54:19 +03:00
Lauri Ojansivu a065fd748c Document Bambara, Ewe and Fulah URL hint translations
Thanks to xet7 !
2026-10-07 07:53:11 +03:00
Lauri Ojansivu deac86fb7e Translate URL scheme hint in Bambara, Ewe and Fulah
Preserve scheme identifiers and extend regression coverage. Record lower-confidence technical wording for native review.

Thanks to xet7 !
2026-10-07 07:52:56 +03:00
Lauri Ojansivu 74c58e9759 Document Tibetan and Dzongkha URL hint translations
Thanks to xet7 !
2026-10-07 07:51:28 +03:00
Lauri Ojansivu 6ebdb47bea Translate URL scheme hint in Tibetan and Dzongkha
Preserve literal scheme identifiers and extend regression coverage. Record lower-confidence URI scheme terminology for native review.

Thanks to xet7 !
2026-10-07 07:51:08 +03:00
Lauri Ojansivu c14f070f78 Document Quechua and Aymara URL hint translations
Thanks to xet7 !
2026-10-07 07:49:44 +03:00
Lauri Ojansivu b9b1f93307 Translate URL scheme hint in Quechua and Aymara
Preserve literal scheme identifiers and extend regression coverage. Record lower-confidence technical wording and regional spelling for review.

Thanks to xet7 !
2026-10-07 07:49:30 +03:00
Lauri Ojansivu 880d25b10d Document Buryat, Sakha and Chuvash URL hint translations
Thanks to xet7 !
2026-10-07 07:48:34 +03:00
Lauri Ojansivu c8fcaf3e06 Translate URL scheme hint in Buryat, Sakha and Chuvash
Preserve literal scheme names and extend regression coverage. Document lower-confidence technical wording for native review.

Thanks to xet7 !
2026-10-07 07:48:20 +03:00
Lauri Ojansivu 20465a8a03 Document four further URL scheme hint translations
Thanks to xet7 !
2026-10-07 07:47:08 +03:00
Lauri Ojansivu 5f79e3b437 Translate URL scheme hint in Tigrinya, Akan, Wolof and Guarani
Preserve scheme identifiers and extend regression coverage. Record lower-confidence technical wording for native review.

Thanks to xet7 !
2026-10-07 07:46:54 +03:00
Lauri Ojansivu adc72f3ea3 Document Manx and Northern Sami URL hint translations
Thanks to xet7 !
2026-10-07 07:45:27 +03:00
Lauri Ojansivu 978296fa9d Translate URL scheme hint in Manx and Northern Sami
Fill English placeholders and preserve literal scheme identifiers. Record lower confidence in technical wording and extend regression coverage.

Thanks to xet7 !
2026-10-07 07:45:09 +03:00
Lauri Ojansivu d3ce31b02a Document Waray and Acehnese URL scheme translations
Thanks to xet7 !
2026-10-07 07:42:39 +03:00
Lauri Ojansivu cdaa7d0cb3 Translate URL scheme hint in Waray and Acehnese
Preserve literal identifiers and extend hint checks to 39 recently filled locales. Parser, sanitizer, structural and human-preference checks pass. Record vocabulary sources and lower-confidence wording; browser scenarios were syntax-checked but not run without the app stack.

Thanks to xet7 !
2026-10-07 07:42:36 +03:00
Lauri Ojansivu f4731658fa Document Venetian Walloon and Aromanian hint translations
Thanks to xet7 !
2026-10-07 07:41:37 +03:00
Lauri Ojansivu efac273833 Translate URL scheme hint in Venetian Walloon and Aromanian
Preserve literal identifiers and extend hint checks to 37 recently filled locales. Parser, sanitizer, structural and human-preference checks pass. Record lower-confidence wording; browser scenarios were syntax-checked but not run without the app stack.

Thanks to xet7 !
2026-10-07 07:41:34 +03:00
Lauri Ojansivu 01484ae6ba Document Pacific-language URL scheme translations
Thanks to xet7 !
2026-10-07 07:40:27 +03:00
Lauri Ojansivu ca45662e64 Translate URL scheme hint in five Pacific languages
Preserve all scheme identifiers and extend checks to 34 recently filled locales. Parser, sanitizer, structural and human-preference checks pass. Record lower-confidence technical wording; browser scenarios were syntax-checked but not run without the app stack.

Thanks to xet7 !
2026-10-07 07:40:16 +03:00
Lauri Ojansivu 4b5983908d Document five-language URL scheme translation batch
Thanks to xet7 !
2026-10-07 07:39:02 +03:00
Lauri Ojansivu a2a6a537be Translate URL scheme hint in five more African languages
Fill Kinyarwanda, Kirundi, Luganda, Tsonga and Venda values with exact scheme identifiers. Parser, sanitizer, structural and human-preference checks pass. Record lower-confidence technical wording; browser scenarios were syntax-checked but not run without the app stack.

Thanks to xet7 !
2026-10-07 07:38:59 +03:00
Lauri Ojansivu bdc67bb32b Document southern African URL scheme translations
Thanks to xet7 !
2026-10-07 07:37:44 +03:00
Lauri Ojansivu 4c84b64379 Translate URL scheme hint in southern African locales
Fill eight values and extend exact-identifier checks to 24 recently filled locales. Parser, sanitizer, structural and human-preference checks pass. Record lower-confidence technical wording; browser scenarios were syntax-checked but not run without the app stack.

Thanks to xet7 !
2026-10-07 07:37:41 +03:00
Lauri Ojansivu d93607ea89 Document six-language URL scheme translation batch
Thanks to xet7 !
2026-10-07 07:36:20 +03:00
Lauri Ojansivu 27f499c7f8 Translate URL scheme hint in six further languages
Preserve scheme identifiers and extend translation coverage to 16 recently filled locales. Parser, sanitizer, structural and human-preference checks pass. Record lower-confidence Oromo and Nyanja prose; browser scenarios were syntax-checked but not run without the app stack.

Thanks to xet7 !
2026-10-07 07:36:17 +03:00
Lauri Ojansivu 28e124ba98 Document URL scheme hint translation progress
Thanks to xet7 !
2026-10-07 07:34:59 +03:00
Lauri Ojansivu e2e304eb60 Translate custom URL scheme hint in ten locales
Preserve all five literal scheme identifiers and extend positive and negative regression coverage. Parser, sanitizer, structural and human-preference checks pass. Record lower-confidence technical wording; browser scenarios were syntax-checked but not run without the app stack.

Thanks to xet7 !
2026-10-07 07:34:57 +03:00
Lauri Ojansivu 8180fa8cc6 Document placement-label coverage across locales
Thanks to xet7 !
2026-10-07 07:33:03 +03:00
Lauri Ojansivu af64e879df Complete placement label placeholders across locales
Fill Cherokee and Wolaytta labels and verify the pair across all 234 non-English locales. Remove two filled pending keys. Record lower-confidence wording for native review. Runtime, structural and human-preference checks pass; browser scenarios were syntax-checked but not run without the app stack.

Thanks to xet7 !
2026-10-07 07:32:59 +03:00
Lauri Ojansivu 4d13314042 Document Nahuatl Tamazight and Tigre placement translations
Thanks to xet7 !
2026-10-07 07:31:53 +03:00
Lauri Ojansivu 91054ae84d Translate placement labels in Nahuatl Tamazight and Tigre
Fill six labels and extend placement checks to 64 recently filled locales. Record vocabulary sources and lower-confidence wording. Runtime, structural and human-preference checks pass; browser scenarios were syntax-checked but not run without the app stack.

Thanks to xet7 !
2026-10-07 07:31:50 +03:00
Lauri Ojansivu cca25d09c2 Document three-language placement translation batch
Thanks to xet7 !
2026-10-07 07:30:41 +03:00
Lauri Ojansivu 737de798ce Translate placement labels in Dzongkha Greenlandic and Inuktitut
Fill six spatial placement labels and extend translation checks to 61 recently filled locales. Record vocabulary sources and lower-confidence standalone wording. Runtime, structural and human-preference checks pass; browser scenarios were syntax-checked but not run without the app stack.

Thanks to xet7 !
2026-10-07 07:30:38 +03:00
Lauri Ojansivu 2c5b503ccb Document four-language placement translation batch
Thanks to xet7 !
2026-10-07 07:29:20 +03:00
Lauri Ojansivu 9fc66b2b71 Translate placement labels in Acehnese Fula Guarani and Veps
Fill eight labels and extend placement checks to 58 recently filled locales. Record vocabulary references and lower-confidence Veps wording. Runtime, structural and human-preference checks pass; browser scenarios were syntax-checked but not run without the app stack.

Thanks to xet7 !
2026-10-07 07:29:17 +03:00
Lauri Ojansivu 0c46c35e00 Document five-language placement translation batch
Thanks to xet7 !
2026-10-07 07:28:14 +03:00
Lauri Ojansivu 88f728a49d Translate placement labels in five further languages
Fill ten Buryat, Chuvash, Yakut, Aromanian and Klingon labels. Record vocabulary references and native-review limitations. Placement checks now cover 54 recently filled locales; runtime, structural and human-preference checks pass. Browser scenarios were syntax-checked but not run without the app stack.

Thanks to xet7 !
2026-10-07 07:28:11 +03:00
Lauri Ojansivu 7d672aeec9 Document further placement translation progress
Thanks to xet7 !
2026-10-07 07:26:55 +03:00
Lauri Ojansivu ba6d41e1e5 Translate placement labels in 13 more locales
Fill 26 before/after labels using spatial terminology and extend translation checks to 49 recently filled locales. Record vocabulary references and lower-confidence Aymara, Northern Sami and Waray wording. Runtime, structural and human-preference checks pass; browser scenarios were syntax-checked but not run without the app stack.

Thanks to xet7 !
2026-10-07 07:26:53 +03:00
Lauri Ojansivu d5777dcf77 Document move-position translation progress
Refresh the TODO Later pending-key snapshot.

Thanks to xet7 !
2026-10-07 07:25:28 +03:00
Lauri Ojansivu e32e50a5e1 Translate move-position labels in 36 locales
Fill 72 before/after labels and verify distinct placement choices, source tokens and key order. Preserve existing translations. Lower-confidence minority-language wording is recorded for review. Runtime and structural checks pass; browser scenarios were syntax-checked but not run without the app stack.

Thanks to xet7 !
2026-10-07 07:25:25 +03:00
Lauri Ojansivu 0e0f3a7d18 Document rule-builder translation coverage across locales
Thanks to xet7 !
2026-10-07 07:23:27 +03:00
Lauri Ojansivu 20c986cc20 Complete rule-builder placeholder translations across locales
Fill seven Cherokee strings and check the group across all 234 non-English locale paths. Preserve literal rule variables and source tokens; retire the seven pending keys. Technical Cherokee wording remains low confidence and requires native review. Runtime, structural and human-preference checks pass; browser scenarios were syntax-checked but not run without the app stack.

Thanks to xet7 !
2026-10-07 07:23:14 +03:00
Lauri Ojansivu 4dec280cc8 Document Tigre rule-builder translations
Thanks to xet7 !
2026-10-07 07:21:19 +03:00
Lauri Ojansivu fccbc4ebdd Translate rule-builder instructions in Tigre
Fill seven pending strings and extend literal-variable regression coverage. Runtime, structural and human-preference checks pass. Technical wording is low confidence and requires Tigre speaker review; browser scenarios remain unrun without the app stack.

Thanks to xet7 !
2026-10-07 07:21:15 +03:00
Lauri Ojansivu 37e59174ea Document Wolaytta rule-builder translations
Thanks to xet7 !
2026-10-07 07:19:48 +03:00
Lauri Ojansivu c0b3b45fbe Translate rule-builder instructions in Wolaytta
Fill seven instructions and correct five prefixed rule labels. Preserve literal variables and cover the values with translation regressions. Runtime, structural and human-preference checks pass. Technical wording is lower confidence and needs native review; browser scenarios could not run without the app stack.

Thanks to xet7 !
2026-10-07 07:19:34 +03:00
Lauri Ojansivu 819af97287 Document Inuktitut rule translations
Record validation and remaining translation and language-review work.

Thanks to xet7 !
2026-10-07 07:16:57 +03:00
Lauri Ojansivu 15af3c9f52 Translate rule-builder instructions in Inuktitut
Fill seven pending values and extend literal-variable regression checks. Runtime matching, all-locale structural and human-preference checks pass. Technical prose has lower confidence and needs native terminology and dialect review. Browser scenarios were not run because the app stack was unavailable.

Thanks to xet7 !
2026-10-07 07:16:42 +03:00
Lauri Ojansivu 38ceac2c38 Document Moroccan Tamazight rule translations
Record validation and remaining translation and language-review work.

Thanks to xet7 !
2026-10-07 07:15:26 +03:00
Lauri Ojansivu 58c0d84d7a Translate rule-builder instructions in Moroccan Tamazight
Fill seven pending values and extend literal-variable regression checks. Runtime matching, all-locale structural and human-preference checks pass. Technical prose has lower confidence and needs native terminology and regional-usage review. Browser scenarios were not run because the app stack was unavailable.

Thanks to xet7 !
2026-10-07 07:14:58 +03:00
Lauri Ojansivu 2b21c97ee1 Document Klingon rule translations
Record validation and remaining translation and language-review work.

Thanks to xet7 !
2026-10-07 07:13:39 +03:00
Lauri Ojansivu 3ff08a0597 Translate rule-builder instructions in Klingon
Fill seven pending values and extend literal-variable regression checks. Runtime matching, all-locale structural and human-preference checks pass. Technical prose has lower confidence and uses the borrowed software word card; speaker review remains open. Browser scenarios were not run because the app stack was unavailable.

Thanks to xet7 !
2026-10-07 07:13:26 +03:00
Lauri Ojansivu 3ffcd8c846 Document Nahuatl rule translations
Record validation and remaining translation and language-review work.

Thanks to xet7 !
2026-10-07 07:12:29 +03:00
Lauri Ojansivu 5ebae861be Translate rule-builder instructions in Nahuatl
Fill seven pending values and extend literal-variable regression checks. Runtime matching, all-locale structural and human-preference checks pass. Technical prose has lower confidence and needs native terminology and dialect review. Browser scenarios were not run because the app stack was unavailable.

Thanks to xet7 !
2026-10-07 07:12:16 +03:00
Lauri Ojansivu c118f30012 Document Greenlandic rule translations
Record validation and remaining translation and language-review work.

Thanks to xet7 !
2026-10-07 07:11:17 +03:00
Lauri Ojansivu 93db0b817b Translate rule-builder instructions in Greenlandic
Fill seven pending values and extend literal-variable regression checks. Runtime matching, all-locale structural and human-preference checks pass. Technical prose has lower confidence and needs native terminology review. Browser scenarios were not run because the app stack was unavailable.

Thanks to xet7 !
2026-10-07 07:11:05 +03:00
Lauri Ojansivu caed8761ef Document Volapuk rule translations
Record checked translations, corrected labels and remaining review work.

Thanks to xet7 !
2026-10-07 07:10:03 +03:00
Lauri Ojansivu 1c5ecfcddb Translate rule-builder instructions in Volapuk
Fill seven pending values and correct three labels. Extend literal-variable and label regression checks. Runtime matching, all-locale structural and human-preference checks pass. Technical prose has lower confidence and needs speaker review. Browser scenarios were not run because the app stack was unavailable.

Thanks to xet7 !
2026-10-07 07:09:50 +03:00
Lauri Ojansivu fd9e735ccf Document Veps rule translations
Record validation and remaining translation and language-review work.

Thanks to xet7 !
2026-10-07 07:08:42 +03:00
Lauri Ojansivu 3009b5267e Translate rule-builder instructions in Veps
Fill seven pending values and extend literal-variable regression checks. Runtime matching, all-locale structural and human-preference checks pass. Technical prose has lower confidence and needs native terminology review. Browser scenarios were not run because the app stack was unavailable.

Thanks to xet7 !
2026-10-07 07:08:25 +03:00
Lauri Ojansivu 32b896a5cb Document Fulah rule translations
Record validation and remaining translation and language-review work.

Thanks to xet7 !
2026-10-07 07:07:17 +03:00
Lauri Ojansivu 4b82329cf1 Translate rule-builder instructions in Fulah
Fill seven pending values and extend literal-variable regression checks. Runtime matching, all-locale structural and human-preference checks pass. Technical prose has lower confidence and needs native dialect and terminology review. Browser scenarios were not run because the app stack was unavailable.

Thanks to xet7 !
2026-10-07 07:07:04 +03:00
Lauri Ojansivu 76083efa37 Document Guarani rule translations
Record validation and remaining translation and language-review work.

Thanks to xet7 !
2026-10-07 07:05:56 +03:00
Lauri Ojansivu 2b44322d56 Translate rule-builder instructions in Guarani
Fill seven pending values and extend literal-variable regression checks. Runtime matching, all-locale structural and human-preference checks pass. Technical prose has lower confidence and needs native review. Browser scenarios were not run because the app stack was unavailable.

Thanks to xet7 !
2026-10-07 07:05:42 +03:00
Lauri Ojansivu 870005f369 Document Quechua and Aymara rule translations
Record checked translations, corrected labels and remaining review work.

Thanks to xet7 !
2026-10-07 07:04:44 +03:00
Lauri Ojansivu 53c658698d Translate rule-builder instructions in Quechua and Aymara
Fill 14 pending values and replace three prefixed English labels. Extend literal-variable and label regression checks. Runtime matching, all-locale structural and human-preference checks pass. Both translations have lower confidence and need native terminology review. Browser scenarios were not run because the app stack was unavailable.

Thanks to xet7 !
2026-10-07 07:04:31 +03:00
Lauri Ojansivu 0286cc2675 Document Tigrinya and Kashmiri rule translations
Record validation and remaining translation and language-review work.

Thanks to xet7 !
2026-10-07 07:03:11 +03:00
Lauri Ojansivu 54a1bf6699 Translate rule-builder instructions in Tigrinya and Kashmiri
Fill 14 pending values and extend literal-variable regression checks. Runtime matching, all-locale structural and human-preference checks pass. Kashmiri prose has lower confidence; both locales need native terminology review. Browser scenarios were not run because the app stack was unavailable.

Thanks to xet7 !
2026-10-07 07:02:59 +03:00
Lauri Ojansivu 4ec1aab02a Document Tibetan and Dzongkha rule translations
Record validation and remaining translation and language-review work.

Thanks to xet7 !
2026-10-07 07:01:43 +03:00
Lauri Ojansivu ed136c7037 Translate rule-builder instructions in Tibetan and Dzongkha
Fill 14 pending values and extend literal-variable regression checks. Runtime matching, all-locale structural and human-preference checks pass. Both translations have lower confidence and need native terminology review. Browser scenarios were not run because the app stack was unavailable.

Thanks to xet7 !
2026-10-07 07:01:31 +03:00
Lauri Ojansivu 7cf78e8fe1 Document Northern Sami and Acehnese rule translations
Record validation and remaining translation and language-review work.

Thanks to xet7 !
2026-10-07 06:59:40 +03:00
Lauri Ojansivu 013b40fa07 Translate rule-builder instructions in Northern Sami and Acehnese
Fill 14 pending values and extend literal-variable regression checks. Runtime matching, all-locale structural and human-preference checks pass. Both translations have lower confidence and need native terminology review. Browser scenarios were not run because the app stack was unavailable.

Thanks to xet7 !
2026-10-07 06:59:28 +03:00
Lauri Ojansivu 6c4aa0a6a5 Document Chuvash and Venda rule translations
Record checked translations, corrected labels and remaining review work.

Thanks to xet7 !
2026-10-07 06:58:29 +03:00
Lauri Ojansivu e825c8e1dd Translate rule-builder instructions in Chuvash and Venda
Fill 14 pending values and correct three Nguni seed labels in Venda. Extend literal-variable and label regression checks. Runtime matching, all-locale structural and human-preference checks pass. Both translations have lower confidence and need native terminology review. Browser scenarios were not run because the app stack was unavailable.

Thanks to xet7 !
2026-10-07 06:58:15 +03:00
Lauri Ojansivu 8d5a18b533 Document Buryat and Sakha rule translations
Record validation and remaining translation and language-review work.

Thanks to xet7 !
2026-10-07 06:57:04 +03:00
Lauri Ojansivu 04719178b3 Translate rule-builder instructions in Buryat and Sakha
Fill 14 pending values and extend literal-variable regression checks. Runtime matching, all-locale structural and human-preference checks pass. Both translations have lower confidence and need native terminology review. Browser scenarios were not run because the app stack was unavailable.

Thanks to xet7 !
2026-10-07 06:56:39 +03:00
Lauri Ojansivu 498f2fcd10 Document Aromanian and Venetian rule translations
Record checked translations, corrected labels and remaining review work.

Thanks to xet7 !
2026-10-07 06:55:23 +03:00
Lauri Ojansivu ceca036475 Translate rule-builder instructions in Aromanian and Venetian
Fill 14 pending values and correct five wrong-language labels. Extend literal-variable and label regression checks. Runtime matching, all-locale structural and human-preference checks pass. Aromanian prose has lower confidence; both locales need native terminology review. Browser scenarios were not run because the app stack was unavailable.

Thanks to xet7 !
2026-10-07 06:55:11 +03:00
Lauri Ojansivu 9aea92715f Document Bambara and Ewe rule translations
Record validation and remaining translation and language-review work.

Thanks to xet7 !
2026-10-07 06:53:59 +03:00
Lauri Ojansivu 73af562b00 Translate rule-builder instructions in Bambara and Ewe
Fill 14 pending values and extend literal-variable regression checks. Runtime matching, all-locale structural and human-preference checks pass. Both translations have lower confidence and need native terminology review. Browser scenarios were not run because the app stack was unavailable.

Thanks to xet7 !
2026-10-07 06:53:48 +03:00
Lauri Ojansivu bcb44fc1af Document Luganda and Wolof rule translations
Record checked translations, corrected label and remaining review work.

Thanks to xet7 !
2026-10-07 06:52:45 +03:00
Lauri Ojansivu 04b3ad18b9 Translate rule-builder instructions in Luganda and Wolof
Fill 14 pending values and correct the English trigger label in Luganda. Extend literal-variable and label regression checks. Runtime matching, all-locale structural and human-preference checks pass. Technical prose has lower confidence and needs native review. Browser scenarios were not run because the app stack was unavailable.

Thanks to xet7 !
2026-10-07 06:52:29 +03:00
Lauri Ojansivu 8d6b9bb732 Document Waray and Akan rule translations
Record checked translations, corrected labels and remaining review work.

Thanks to xet7 !
2026-10-07 06:51:24 +03:00
Lauri Ojansivu 808829a9dc Translate rule-builder instructions in Waray and Akan
Fill 14 pending values and correct three wrong-language seed labels in Waray. Extend literal-variable and seed-label regression checks. Runtime matching, all-locale structural and human-preference checks pass. Akan technical prose has lower confidence and needs native review. Browser scenarios were not run because the app stack was unavailable.

Thanks to xet7 !
2026-10-07 06:51:13 +03:00
Lauri Ojansivu 5ec75af9a3 Document Manx and Walloon rule translations
Record validation and remaining translation and language-review work.

Thanks to xet7 !
2026-10-07 06:50:05 +03:00
Lauri Ojansivu 6d9a245163 Translate rule-builder instructions in Manx and Walloon
Fill 14 pending values and extend literal-variable regression checks. Runtime matching, all-locale structural and human-preference checks pass. Manx prose has lower confidence; technical vocabulary and composed date fragments need native review. Browser scenarios were not run because the app stack was unavailable.

Thanks to xet7 !
2026-10-07 06:49:54 +03:00
Lauri Ojansivu d2ab8375fc Document Oromo, Fijian and Tongan rule translations
Record checked translations, corrected labels and remaining review work.

Thanks to xet7 !
2026-10-07 06:48:40 +03:00
Lauri Ojansivu df715c2438 Translate rule-builder instructions in Oromo, Fijian and Tongan
Fill 21 pending values and replace two prefixed English labels in Tongan. Preserve literal variables and extend regression checks. Runtime matching, all-locale structural and human-preference checks pass. Fijian and Tongan technical prose has lower confidence and needs native review. Browser scenarios were not run because the app stack was unavailable.

Thanks to xet7 !
2026-10-07 06:48:29 +03:00
Lauri Ojansivu 00834b9af1 Document four southern African rule translation updates
Record checks, lower-confidence wording and remaining translation work.

Thanks to xet7 !
2026-10-07 06:47:04 +03:00
Lauri Ojansivu a87d0a82d6 Translate rule-builder instructions in four southern African languages
Fill 28 values in Northern Ndebele, Swati, Northern Sotho and Tsonga. Preserve literal variables and extend regression checks. Runtime matching, all-locale structural and human-preference checks pass. Northern Ndebele and Swati prose has lower confidence; native terminology and browser review remain open. Browser scenarios were not run because the app stack was unavailable.

Thanks to xet7 !
2026-10-07 06:46:51 +03:00
Lauri Ojansivu 29913a2e31 Document Moroccan Arabic and Yiddish rule translations
Record validation, corrected seed labels and the remaining translation backlog.

Thanks to xet7 !
2026-10-07 06:45:26 +03:00
Lauri Ojansivu 4e2d9d4ff7 Translate rule-builder instructions in Moroccan Arabic and Yiddish
Fill 14 pending values and correct three Persian seed labels in the Moroccan Arabic locale. Extend exact variable and wrong-language regression checks. Runtime matching, all-locale structural and human-preference checks pass. Native terminology and RTL browser review remain open; browser scenarios were not run because the app stack was unavailable.

Thanks to xet7 !
2026-10-07 06:45:14 +03:00
Lauri Ojansivu 81adfc0361 Document Odia and Konkani rule translations
Record validation, terminology review and the remaining translation backlog.

Thanks to xet7 !
2026-10-07 06:44:07 +03:00
Lauri Ojansivu 56c95029c0 Translate rule-builder instructions in Odia and Konkani
Fill 14 pending values and extend exact variable preservation checks. Runtime matching, all-locale structural and human-preference checks pass. Konkani prose has lower confidence; terminology and composed date labels need native/UI review. Browser scenarios were not run because the app stack was unavailable.

Thanks to xet7 !
2026-10-07 06:43:38 +03:00
Lauri Ojansivu 772cec9bc2 Document Bhojpuri and Maithili rule translations 2026-10-07 06:41:12 +03:00
Lauri Ojansivu 132706de4f Translate rule-builder instructions in Bhojpuri and Maithili
Fill 14 values while preserving literal variables. Extend translation checks to 21 locales; technical terminology and composed date labels need native and UI review.

Thanks to xet7.
2026-10-07 06:41:10 +03:00
Lauri Ojansivu bbfee35623 Document Kinyarwanda Kirundi and Chichewa rule translations 2026-10-07 06:40:17 +03:00
Lauri Ojansivu abde97a3de Translate rule-builder instructions in Kinyarwanda Kirundi and Chichewa
Fill 21 values while preserving literal variables. Extend translation checks to 19 locales. Technical terminology and composed date labels need native and UI review, especially the Kirundi phrases.

Thanks to xet7.
2026-10-07 06:40:07 +03:00
Lauri Ojansivu 62490a9b7d Document Sesotho and Setswana rule translations 2026-10-07 06:39:07 +03:00
Lauri Ojansivu 1ce1dfb018 Translate rule-builder instructions in Sesotho and Setswana
Fill 14 values while preserving literal variables. Extend translation checks to 16 locales. Technical phrases and composed date labels have lower confidence and need native and UI review.

Thanks to xet7.
2026-10-07 06:39:05 +03:00
Lauri Ojansivu d437d8897f Document Zulu and Xhosa rule translations 2026-10-07 06:38:11 +03:00
Lauri Ojansivu 3edf06cee8 Translate rule-builder instructions in Zulu and Xhosa
Fill 21 values across three locale paths while preserving literal variables. Extend translation checks to 14 locales. Composed date labels and trigger terminology need native and UI review.

Thanks to xet7.
2026-10-07 06:38:07 +03:00
Lauri Ojansivu 2454ffb699 Document Maori Samoan and Hawaiian rule translations 2026-10-07 06:37:02 +03:00
Lauri Ojansivu 8928e1510e Translate rule-builder instructions in Maori Samoan and Hawaiian
Fill 21 values and preserve literal variable expressions. Review date fragments against the condition selector. Samoan and Hawaiian phrases and technical terminology need native and UI review.

Thanks to xet7.
2026-10-07 06:36:59 +03:00
Lauri Ojansivu e29803c6bd Document Tok Pisin and Bislama rule translations 2026-10-07 06:35:42 +03:00
Lauri Ojansivu 2b28b6f6c9 Translate rule-builder instructions in Tok Pisin and Bislama
Fill 14 values and preserve literal variable expressions. Extend translation checks to eight locales; trigger terminology and composed date fragments need native and UI review.

Thanks to xet7.
2026-10-07 06:35:39 +03:00
Lauri Ojansivu 538e0893f1 Document the next rule-builder translation batch 2026-10-07 06:34:36 +03:00
Lauri Ojansivu 1c2fe314e5 Translate rule-builder instructions in Kurdish Sorani and Papiamento
Fill 21 values and preserve literal variable examples. Extend translation checks to six locales. Date-condition fragments and trigger terminology need native and UI review.

Thanks to xet7.
2026-10-07 06:34:34 +03:00
Lauri Ojansivu 73ddcbd2b7 Document rule-builder translation progress 2026-10-07 06:33:28 +03:00
Lauri Ojansivu b4beb5559b Translate rule-builder instructions in Turkmen Tatar and Somali
Fill 21 values and preserve literal rule-variable examples. Extend trigger-variable tests to cover translated brace tokens. Trigger and swimlane terms and date-condition fragments need native and UI review.

Thanks to xet7.
2026-10-07 06:33:25 +03:00
Lauri Ojansivu 6752426d0b Record SAML message coverage and refresh pending count 2026-10-07 06:31:59 +03:00
Lauri Ojansivu 4ab30ce556 Fill remaining SAML errors and reconcile all-locale coverage
Add low-confidence Tigre and Cherokee phrases requiring native review. Verify non-English placeholder coverage across 234 locales and remove the key from the pending queue. Borrowed Cherokee browser-tab labels remain a terminology review item.

Thanks to xet7.
2026-10-07 06:31:55 +03:00
Lauri Ojansivu 7e51a79059 Document Nahuatl and Tamazight SAML translations 2026-10-07 06:30:53 +03:00
Lauri Ojansivu 6023f4a7e8 Translate SAML error into Nahuatl and Tamazight
Extend popup-error checks to 64 recently filled locales. Full phrases and browser-tab terminology are low confidence and need native review.

Thanks to xet7.
2026-10-07 06:30:50 +03:00
Lauri Ojansivu 9c22b69aa6 Document Greenlandic and Inuktitut SAML translations 2026-10-07 06:30:02 +03:00
Lauri Ojansivu 7c75ebfb88 Translate SAML error into Greenlandic and Inuktitut
Extend popup-error checks to 62 recently filled locales. Full phrases and browser-tab terms are low confidence and need native review.

Thanks to xet7.
2026-10-07 06:30:00 +03:00
Lauri Ojansivu 263de20691 Document Veps and Wolaytta SAML translations 2026-10-07 06:29:15 +03:00
Lauri Ojansivu e3e9c342dd Translate SAML error into Veps and Wolaytta
Extend popup-error checks to 60 recently filled locales. Full phrases and browser-tab terms are low confidence and need native review.

Thanks to xet7.
2026-10-07 06:29:13 +03:00
Lauri Ojansivu ceb03d56cd Document Volapuk and Klingon SAML translation progress 2026-10-07 06:28:24 +03:00
Lauri Ojansivu 1fb61838b9 Translate SAML error into Volapuk and Klingon
Extend popup-error checks to 58 recently filled locales. Full phrases are low confidence; Klingon browser and tab terms remain borrowed and need terminology review.

Thanks to xet7.
2026-10-07 06:28:21 +03:00
Lauri Ojansivu 1713b68867 Document another four SAML error translations 2026-10-07 06:27:25 +03:00
Lauri Ojansivu afd51d4b50 Translate SAML error into Quechua Aymara Guarani and Fulah
Extend popup-error checks to 56 recently filled locales. Full phrases and browser-tab terminology are low confidence and need native review.

Thanks to xet7.
2026-10-07 06:27:22 +03:00
Lauri Ojansivu d50c4e473b Document five more SAML error translations 2026-10-07 06:26:32 +03:00
Lauri Ojansivu e188bb0f89 Translate SAML browser-tab error in five more locales
Extend popup-error checks to 52 recently filled locales. Browser-tab terminology and full Acehnese and Dzongkha phrases are low confidence and need native review.

Thanks to xet7.
2026-10-07 06:26:24 +03:00
Lauri Ojansivu 190c7756b7 Document SAML translation coverage in another seven locales 2026-10-07 06:25:15 +03:00
Lauri Ojansivu ec117c6246 Translate SAML browser-tab error in seven more locales
Extend popup-error checks to 47 recently filled locales. Aromanian phrases and browser-tab terminology are low confidence and need native review.

Thanks to xet7.
2026-10-07 06:25:12 +03:00
Lauri Ojansivu cab2250370 Record further SAML translation progress 2026-10-07 06:24:11 +03:00
Lauri Ojansivu c53be38b00 Translate SAML browser-tab error in eight more locales
Extend popup-error translation checks to 40 recently filled locales. Manx and Walloon phrases and browser/tab terminology are low confidence and need native review.

Thanks to xet7.
2026-10-07 06:24:08 +03:00
Lauri Ojansivu c783c5cb83 Document the next SAML translation batch 2026-10-07 06:23:05 +03:00
Lauri Ojansivu 3b25eeeb53 Translate SAML browser-tab error in 13 more locales
Preserve existing translations and extend popup-error checks to 32 recently filled locales. Browser/tab terminology in the southern African, Oromo, Fijian and Tongan phrases is lower confidence and needs native review.

Thanks to xet7.
2026-10-07 06:23:03 +03:00
Lauri Ojansivu 91864380dd Document SAML error translation progress 2026-10-07 06:21:48 +03:00
Lauri Ojansivu 8096c6976d Translate SAML browser-tab error in 19 locales
Preserve existing translations and extend popup-error translation checks. Pacific and southern African browser-tab terminology has lower confidence and needs native review.

Thanks to xet7.
2026-10-07 06:21:45 +03:00
Lauri Ojansivu be3a322dd6 Record reminder coverage and refresh translation backlog 2026-10-07 06:19:55 +03:00
Lauri Ojansivu c1f411e1a5 Translate Cherokee reminders and reconcile all-locale coverage
Fill six Cherokee placeholders with low-confidence phrases requiring native review. Verify the reminder group across 234 non-English locales and remove its six keys from the pending queue; language-quality review remains open.

Thanks to xet7.
2026-10-07 06:19:52 +03:00
Lauri Ojansivu ffe2cad721 Document Tigre reminder translations and remaining work 2026-10-07 06:18:08 +03:00
Lauri Ojansivu 256a6b36cf Translate due reminders into Tigre
Fill six English placeholders and extend reminder regression coverage. Full phrases and technical terminology are low confidence and need native review.

Thanks to xet7.
2026-10-07 06:17:58 +03:00
Lauri Ojansivu 22c2191c62 Document Wolaytta reminder translations and remaining work 2026-10-07 06:16:42 +03:00
Lauri Ojansivu dc84805547 Translate due reminders into Wolaytta
Fill six English placeholders and extend reminder regression coverage. Full phrases and technical terminology are low confidence and need native review.

Thanks to xet7.
2026-10-07 06:16:30 +03:00
Lauri Ojansivu 7c1ad6e9df Document Inuktitut reminder translations and remaining work 2026-10-07 06:15:37 +03:00
Lauri Ojansivu 8924e8c936 Translate due reminders into Inuktitut
Fill six English placeholders and extend reminder regression coverage. Full phrases and technical terminology are low confidence and need native review.

Thanks to xet7.
2026-10-07 06:15:27 +03:00
Lauri Ojansivu 9963a3976c Document Tamazight reminder translations and remaining work 2026-10-07 06:14:05 +03:00
Lauri Ojansivu a5f345f6cf Translate due reminders into Standard Moroccan Tamazight
Fill six English placeholders in Tifinagh and extend reminder regression coverage. Full phrases and technical terminology are low confidence and require native review.

Thanks to xet7.
2026-10-07 06:13:53 +03:00
Lauri Ojansivu 1af1a147e8 Document Veps reminder translations and remaining review 2026-10-07 06:12:41 +03:00
Lauri Ojansivu 2b5d755ae6 Translate due reminder messages into Veps
Fill six placeholders and extend reminder regression coverage. Veps phrases and technical terms are low confidence and need native review; unrelated wrong-language values remain recorded in the audit.

Thanks to xet7.
2026-10-07 06:12:25 +03:00
Lauri Ojansivu 43db51778d Document Nahuatl reminder translations
Thanks to xet7.
2026-10-07 06:09:48 +03:00
Lauri Ojansivu e13b997904 Translate due reminders into Nahuatl
Preserve signed offsets, defaults, limits and outgoing webhooks. Extend translation checks and browser scenarios. Full instructions, comparisons and technical compounds remain low-confidence drafts for native review, including dialect consistency.

Thanks to xet7.
2026-10-07 06:09:32 +03:00
Lauri Ojansivu 9db44627a5 Document Greenlandic reminder translations
Thanks to xet7.
2026-10-07 06:08:18 +03:00
Lauri Ojansivu 89b573a778 Translate due reminders into Greenlandic
Preserve signed offsets, defaults, limits and outgoing webhooks. Extend translation checks and browser scenarios. Complete instructions, inflections and technical compounds remain low-confidence drafts for native review.

Thanks to xet7.
2026-10-07 06:08:04 +03:00
Lauri Ojansivu 5c335b6e17 Document Klingon reminder translations
Thanks to xet7.
2026-10-07 06:06:50 +03:00
Lauri Ojansivu ef61b69b8d Translate due reminders into Klingon
Preserve signed offsets, defaults, limits and outgoing webhooks. Extend translation checks and browser scenarios. Complete instructions, comparisons and technical compounds remain low-confidence drafts for fluent-speaker review.

Thanks to xet7.
2026-10-07 06:06:35 +03:00
Lauri Ojansivu 4133cf691e Document Volapuk reminder translations
Thanks to xet7.
2026-10-07 06:05:27 +03:00
Lauri Ojansivu 9bedd51e10 Translate due reminders into Volapuk
Preserve signed offsets, defaults, limits and outgoing webhooks. Extend translation checks and browser scenarios. Complete instructions and technical compounds remain low-confidence drafts for fluent-speaker review.

Thanks to xet7.
2026-10-07 06:05:10 +03:00
Lauri Ojansivu 07b83889ba Document Fulah reminder translations
Thanks to xet7.
2026-10-07 06:04:03 +03:00
Lauri Ojansivu c092aabc2e Translate due reminders into Fulah
Preserve signed offsets, defaults, limits and outgoing webhooks. Extend translation checks and browser scenarios. Complete instructions and technical compounds remain low-confidence drafts for native review, including dialect consistency.

Thanks to xet7.
2026-10-07 06:03:50 +03:00
Lauri Ojansivu 05010c6fb9 Document Guarani reminder translations
Thanks to xet7.
2026-10-07 06:03:00 +03:00
Lauri Ojansivu 14ce70fe73 Translate due reminders into Guarani
Preserve signed offsets, defaults, limits and outgoing webhooks. Extend translation checks and browser scenarios. Complete instructions and technical compounds remain low-confidence drafts for native review.

Thanks to xet7.
2026-10-07 06:02:43 +03:00
Lauri Ojansivu 69bd5c88a9 Document Aymara reminder translations
Thanks to xet7.
2026-10-07 06:01:55 +03:00
Lauri Ojansivu a434d1b4de Translate due reminders into Aymara
Preserve signed offsets, defaults, limits and outgoing webhooks. Extend translation checks and browser scenarios. Complete instructions and technical compounds remain low-confidence drafts for native review.

Thanks to xet7.
2026-10-07 06:01:21 +03:00
Lauri Ojansivu 16f338f173 Document Quechua reminder translations
Thanks to xet7.
2026-10-07 06:00:25 +03:00
Lauri Ojansivu 229ef7bbcb Translate due reminders into Quechua
Preserve signed offsets, defaults, limits and outgoing webhooks. Extend translation checks and browser scenarios. Complete instructions and technical compounds remain low-confidence drafts for native review, including dialect consistency.

Thanks to xet7.
2026-10-07 06:00:11 +03:00
Lauri Ojansivu 529b21e56c Document Kashmiri reminder translations
Thanks to xet7.
2026-10-07 05:59:19 +03:00
Lauri Ojansivu a7816d3e4c Translate due reminders into Kashmiri
Preserve signed offsets, defaults, limits and outgoing webhooks. Extend translation checks and browser scenarios. Complete instructions and technical compounds remain low-confidence drafts for native review.

Thanks to xet7.
2026-10-07 05:59:05 +03:00
Lauri Ojansivu a575f1cefa Document Tigrinya reminder translations
Thanks to xet7.
2026-10-07 05:58:06 +03:00
Lauri Ojansivu 3b2dd52c71 Translate due reminders into Tigrinya
Preserve signed offsets, defaults, limits and outgoing webhooks. Extend translation checks and browser scenarios, and record remaining native-review scope.

Thanks to xet7.
2026-10-07 05:57:50 +03:00
Lauri Ojansivu 2fcadcf5a9 Document Dzongkha reminder translations
Thanks to xet7.
2026-10-07 05:56:45 +03:00
Lauri Ojansivu 887f1a1a2a Translate due reminders into Dzongkha
Preserve signed offsets, defaults, limits and outgoing webhooks. Extend translation checks and browser scenarios. Complete instructions and technical compounds remain low-confidence drafts for native review.

Thanks to xet7.
2026-10-07 05:56:31 +03:00
Lauri Ojansivu b555b6bd5c Document Tibetan reminder translations
Thanks to xet7.
2026-10-07 05:55:15 +03:00
Lauri Ojansivu 8674408266 Translate due reminders into Tibetan
Preserve signed offsets, defaults, limits and outgoing webhooks. Extend translation checks and browser scenarios, and record the remaining native-review scope.

Thanks to xet7.
2026-10-07 05:55:01 +03:00
Lauri Ojansivu bbacbba7cb Document Acehnese reminder translations
Thanks to xet7.
2026-10-07 05:53:50 +03:00
Lauri Ojansivu 981101ab21 Translate due reminders into Acehnese
Preserve signed offsets, defaults, limits and outgoing webhooks. Extend translation checks and browser scenarios. Complete instructions and technical compounds remain low-confidence drafts for native review.

Thanks to xet7.
2026-10-07 05:53:35 +03:00
Lauri Ojansivu e0ab573448 Document Northern Sami reminder translations
Thanks to xet7.
2026-10-07 05:52:39 +03:00
Lauri Ojansivu 885ceae233 Translate due reminders into Northern Sami
Preserve signed offsets, defaults, limits and outgoing webhooks. Extend translation checks and browser scenarios. Complete instructions and technical compounds remain low-confidence drafts for native review.

Thanks to xet7.
2026-10-07 05:52:22 +03:00
Lauri Ojansivu ad05da503f Document Venda reminder translations
Thanks to xet7.
2026-10-07 05:51:04 +03:00
Lauri Ojansivu c6a880f1bb Translate due reminders into Venda
Preserve signed offsets, defaults, limits and outgoing webhooks. Extend translation checks and browser scenarios. Technical compounds remain low-confidence drafts for native review.

Thanks to xet7.
2026-10-07 05:50:50 +03:00
Lauri Ojansivu e5f2e762ee Document Chuvash reminder translations
Thanks to xet7.
2026-10-07 05:50:00 +03:00
Lauri Ojansivu a7d1033d26 Translate due reminders into Chuvash
Preserve signed offsets, defaults, limits and outgoing webhooks. Extend translation checks and browser scenarios. Full instructions and technical compounds remain low-confidence drafts for native review.

Thanks to xet7.
2026-10-07 05:49:46 +03:00
Lauri Ojansivu f877d5ad69 Document Buryat and Sakha reminder translations
Thanks to xet7.
2026-10-07 05:48:55 +03:00
Lauri Ojansivu 76eeac26a5 Translate due reminders into Buryat and Sakha
Preserve signed offsets, defaults, limits and outgoing webhooks. Extend translation checks and browser scenarios. Full instructions and technical compounds remain low-confidence drafts for native review.

Thanks to xet7.
2026-10-07 05:48:41 +03:00
Lauri Ojansivu c53665f5cf Document Aromanian and Venetian reminder translations
Thanks to xet7.
2026-10-07 05:47:30 +03:00
Lauri Ojansivu 135341a09e Translate due reminders into Aromanian and Venetian
Preserve signed offsets, defaults, limits and outgoing webhooks. Extend translation checks and browser scenarios. Aromanian instructions and technical compounds remain low-confidence drafts for native review.

Thanks to xet7.
2026-10-07 05:47:13 +03:00
Lauri Ojansivu d3cd8dd416 Document Bambara, Wolof and Ewe reminder translations
Thanks to xet7.
2026-10-07 05:46:11 +03:00
Lauri Ojansivu 876845a721 Translate due reminders into Bambara, Wolof and Ewe
Preserve signed offsets, defaults, limits and outgoing webhooks. Extend translation checks and browser scenarios. Complete instructions and technical compounds remain low-confidence drafts for native review.

Thanks to xet7.
2026-10-07 05:45:57 +03:00
Lauri Ojansivu 7272ccfa2e Document Akan and Luganda reminder translations
Thanks to xet7.
2026-10-07 05:44:30 +03:00
Lauri Ojansivu 6c1d3f0d4a Translate due reminders into Akan and Luganda
Preserve signed offsets, defaults, limits and outgoing webhooks. Extend translation checks and browser scenarios. Technical compounds remain low-confidence drafts for native review.

Thanks to xet7.
2026-10-07 05:44:15 +03:00
Lauri Ojansivu 4794bbc2dd Document Manx, Walloon and Waray reminder translations
Thanks to xet7.
2026-10-07 05:43:17 +03:00
Lauri Ojansivu a8ca9e47da Translate due reminders into Manx, Walloon and Waray
Preserve signed offsets, defaults, limits and outgoing webhooks. Extend translation checks and browser scenarios. Technical compounds and complete Manx and Walloon instructions remain low-confidence drafts for native review.

Thanks to xet7.
2026-10-07 05:43:00 +03:00
Lauri Ojansivu 09b555d43b Document Oromo, Fijian and Tongan reminder translations
Thanks to xet7.
2026-10-07 05:41:29 +03:00
Lauri Ojansivu e6690139f1 Translate due reminders into Oromo, Fijian and Tongan
Preserve signed offsets, defaults, limits and webhook delivery, and extend translation checks and browser scenarios. Technical compounds and complete Fijian and Tongan instructions remain low-confidence drafts for native review.

Thanks to xet7.
2026-10-07 05:41:15 +03:00
Lauri Ojansivu 201973bb48 Document southern African reminder translations
Thanks to xet7.
2026-10-07 05:39:41 +03:00
Lauri Ojansivu ae2860bac5 Translate due reminders into four southern African languages
Fill Northern Ndebele, Swati, Northern Sotho and Tsonga reminders and extend regression checks and browser scenarios. Northern Ndebele and Swati instructions and technical compounds remain low-confidence drafts for native review.

Thanks to xet7.
2026-10-07 05:39:27 +03:00
Lauri Ojansivu fe15410357 Document Moroccan Arabic and Yiddish reminder translations
Thanks to xet7.
2026-10-07 05:38:04 +03:00
Lauri Ojansivu 11a85ea772 Translate due reminders into Moroccan Arabic and Yiddish
Preserve signed day offsets, defaults, bounds and webhook delivery. Extend translation checks and browser scenarios for both locales.

Thanks to xet7.
2026-10-07 05:37:49 +03:00
Lauri Ojansivu 69b1a8e555 Document four-language due-reminder translations
Thanks to xet7.
2026-10-07 05:35:30 +03:00
Lauri Ojansivu 16f5a92edb Translate due reminders into Bhojpuri Maithili Odia and Konkani
Fill 24 strings preserving signed offset directions, numeric limits and server fallback. Extend translation and UI coverage to twenty-three locales. Structural and human-preference checks pass; browser coverage is syntax-checked only.

Thanks to xet7.
2026-10-07 05:35:16 +03:00
Lauri Ojansivu f746135269 Document Kinyarwanda Kirundi and Chichewa due reminders
Thanks to xet7.
2026-10-07 05:33:23 +03:00
Lauri Ojansivu b05e65ac09 Translate due reminders into Kinyarwanda Kirundi and Chichewa
Fill 18 strings preserving offset directions, numeric limits, comma separation and server fallback. Extend translation and UI coverage to nineteen locales. Structural and human-preference checks pass; browser coverage is syntax-checked only.

Thanks to xet7.
2026-10-07 05:32:49 +03:00
Lauri Ojansivu abadf32384 Document southern African due-reminder translations
Thanks to xet7.
2026-10-07 05:31:42 +03:00
Lauri Ojansivu 5cb537180e Translate due reminders into Zulu Xhosa Sesotho and Setswana
Fill 30 strings across five locale paths while preserving signed offset directions and numeric limits. Extend translation and UI coverage to sixteen locales. Structural and human-preference checks pass; browser coverage is syntax-checked only.

Thanks to xet7.
2026-10-07 05:31:27 +03:00
Lauri Ojansivu 9b0986f460 Document Maori Samoan and Hawaiian due-reminder translations
Thanks to xet7.
2026-10-07 05:30:12 +03:00
Lauri Ojansivu 590f813fde Translate due reminders into Maori Samoan and Hawaiian
Fill 18 strings while preserving signed offsets, numeric limits, comma separation and server fallback. Extend translation and UI coverage to eleven locales. Structural and human-preference checks pass; browser coverage is syntax-checked only.

Thanks to xet7.
2026-10-07 05:29:54 +03:00
Lauri Ojansivu de9971a0e1 Document five-language due-reminder translations
Thanks to xet7.
2026-10-07 05:28:51 +03:00
Lauri Ojansivu 4becd76fa0 Translate due reminders into five more languages
Fill 30 strings in Kurdish, Sorani, Papiamento, Tok Pisin and Bislama. Preserve signed offset direction, limits and server fallback. Extend translation and UI coverage to eight locales; structural and human-preference checks pass, browser tests are syntax-checked only.

Thanks to xet7.
2026-10-07 05:28:37 +03:00
Lauri Ojansivu 3db4f11051 Document Turkmen Tatar and Somali due-reminder translations
Thanks to xet7.
2026-10-07 05:26:41 +03:00
Lauri Ojansivu 0ba3f9fc23 Translate due reminders into Turkmen Tatar and Somali
Fill 18 reminder strings while preserving offset direction, comma-separated input, server fallback and validation limits. Add translation checks and extend browser coverage for labels and saved/error messages. Structural and human-preference checks pass; browser coverage is syntax-checked only.

Thanks to xet7.
2026-10-07 05:26:24 +03:00
Lauri Ojansivu 26eeb19877 Document notification pending-key reconciliation
Thanks to xet7.
2026-10-07 05:24:45 +03:00
Lauri Ojansivu b77648b8fd Reconcile filled notification keys with translation backlog
Remove 13 filled keys from the pending manifest after structural checks across all 234 non-English locale paths. Keep language-quality review open and record the next due-reminder group. Pending keys decrease to 188; ordinary missing values remain 51,575.

Thanks to xet7.
2026-10-07 05:24:30 +03:00
Lauri Ojansivu 06b0b47cac Document all-locale notification translation coverage
Thanks to xet7.
2026-10-07 05:23:36 +03:00
Lauri Ojansivu b5818255c9 Translate Cherokee notification settings and check all locales
Fill 13 notification placeholders with provisional Cherokee wording and document review needs. Verify this group has no empty or English-placeholder values across all 234 non-English locale paths. Structural and human-preference checks pass; browser coverage is syntax-checked only.

Thanks to xet7.
2026-10-07 05:23:16 +03:00
Lauri Ojansivu e7bd371866 Document Tigre notification translations
Thanks to xet7.
2026-10-07 05:21:21 +03:00
Lauri Ojansivu 7138ed19e0 Translate Tigre notification settings
Fill 13 notification placeholders and document low-confidence vocabulary and grammar needing fluent review. Translation and human-preference checks pass; browser coverage is syntax-checked only.

Thanks to xet7.
2026-10-07 05:21:05 +03:00
Lauri Ojansivu 39ec03f24e Document Wolaytta notification translations
Thanks to xet7.
2026-10-07 05:19:37 +03:00
Lauri Ojansivu 56fd350ada Translate Wolaytta notification settings
Fill 13 notification placeholders and document low-confidence wording and reference limitations. Translation and human-preference checks pass; browser coverage is syntax-checked only.

Thanks to xet7.
2026-10-07 05:19:22 +03:00
Lauri Ojansivu cd1e6474bd Document Inuktitut notification translations
Thanks to xet7.
2026-10-07 05:18:20 +03:00
Lauri Ojansivu 360ffe7c50 Translate Inuktitut notification settings
Fill 13 notification placeholders in syllabics. Document terminology sources and low-confidence wording. Translation and human-preference checks pass; browser coverage is syntax-checked only.

Thanks to xet7.
2026-10-07 05:18:05 +03:00
Lauri Ojansivu 72778d339d Document Tamazight notification translations
Thanks to xet7.
2026-10-07 05:16:29 +03:00
Lauri Ojansivu cdf21e4dbb Translate Tamazight notification settings
Fill 13 notification placeholders in Tifinagh and extend script and token checks. Document dictionary references and low-confidence wording. Translation and human-preference checks pass; browser coverage is syntax-checked only.

Thanks to xet7.
2026-10-07 05:16:15 +03:00
Lauri Ojansivu 1aa104b309 Document Veps notification translations
Thanks to xet7.
2026-10-07 05:14:47 +03:00
Lauri Ojansivu 54f5243cc8 Translate Veps notification settings
Fill 13 notification placeholders while preserving existing locale values. Record dictionary sources and low-confidence wording. Translation and human-preference checks pass; browser coverage is syntax-checked only.

Thanks to xet7.
2026-10-07 05:14:34 +03:00
Lauri Ojansivu 1457d89da5 Document Nahuatl notification translations
Thanks to xet7.
2026-10-07 05:13:05 +03:00
Lauri Ojansivu 17f85c385c Translate Nahuatl notification settings
Fill 13 notification placeholders using existing locale terms. Record dictionary references and low-confidence wording for review. Translation and human-preference checks pass; browser coverage is syntax-checked only.

Thanks to xet7.
2026-10-07 05:12:51 +03:00
Lauri Ojansivu 196202b052 Document Greenlandic notification translations
Thanks to xet7.
2026-10-07 05:11:48 +03:00
Lauri Ojansivu 7c365d7749 Translate Greenlandic notification settings
Fill 13 English placeholders using existing locale terminology. Document vocabulary sources and low-confidence wording. Translation and human-preference checks pass; browser coverage is syntax-checked only.

Thanks to xet7.
2026-10-07 05:11:30 +03:00
Lauri Ojansivu 524647e347 Document Klingon notification translations
Thanks to xet7.
2026-10-07 05:10:11 +03:00
Lauri Ojansivu 0a2fa65863 Translate Klingon notification settings
Fill 13 notification strings and replace the French assignee label. Document provisional technical wording and vocabulary sources. Translation and human-preference checks pass; browser coverage is syntax-checked only.

Thanks to xet7.
2026-10-07 05:09:56 +03:00
Lauri Ojansivu 963e645de6 Document Volapuk notification translations
Thanks to xet7.
2026-10-07 05:08:31 +03:00
Lauri Ojansivu 96aa165b5d Translate Volapuk notification settings
Fill 13 notification preferences and replace the Esperanto member label with Volapuk. Record low-confidence technical compounds in the translation audit. Translation and human-preference checks pass; browser coverage is syntax-checked but was not run.

Thanks to xet7.
2026-10-07 05:08:18 +03:00
Lauri Ojansivu edb100a875 Document Fulfulde notification translations
Thanks to xet7.
2026-10-07 05:05:37 +03:00
Lauri Ojansivu eb30d42396 Translate Fulfulde notification settings
Fill 13 English placeholders and extend regression coverage. Technical wording and dialect consistency are low confidence pending speaker review. Browser scenarios are syntax-checked only.

Thanks to xet7.
2026-10-07 05:05:22 +03:00
Lauri Ojansivu f3c120ad44 Document Guarani notification translations
Thanks to xet7.
2026-10-07 05:04:17 +03:00
Lauri Ojansivu d082f1d54f Translate Guarani notification settings
Fill 13 English placeholders and extend regression coverage. Technical wording is low confidence pending speaker review. Browser scenarios are syntax-checked only.

Thanks to xet7.
2026-10-07 05:04:02 +03:00
Lauri Ojansivu 64dbc96afd Document Quechua and Aymara notification translations
Thanks to xet7.
2026-10-07 05:03:08 +03:00
Lauri Ojansivu e779130f22 Translate Quechua and Aymara notification settings
Fill 26 English placeholders and extend regression coverage. Technical wording and dialect consistency are low confidence pending speaker review. Browser scenarios are syntax-checked only.

Thanks to xet7.
2026-10-07 05:02:55 +03:00
Lauri Ojansivu b93a9ea8d1 Document Kashmiri notification translations
Thanks to xet7.
2026-10-07 05:01:49 +03:00
Lauri Ojansivu 7542d278a6 Translate Kashmiri notification settings
Fill 13 English placeholders and extend regression coverage. Technical wording and diacritics are low confidence pending speaker review. Browser scenarios are syntax-checked only.

Thanks to xet7.
2026-10-07 05:01:35 +03:00
Lauri Ojansivu adf8f53f1f Document Tigrinya notification translations
Thanks to xet7.
2026-10-07 05:00:43 +03:00
Lauri Ojansivu 136928499c Translate Tigrinya notification settings
Fill 13 English placeholders and extend regression coverage. Technical wording is provisional pending speaker review. Browser scenarios are syntax-checked only.

Thanks to xet7.
2026-10-07 05:00:26 +03:00
Lauri Ojansivu da8da6ec71 Document Dzongkha notification translations
Thanks to xet7.
2026-10-07 04:59:28 +03:00
Lauri Ojansivu c6839432bf Translate Dzongkha notification settings
Fill 13 English placeholders and extend regression coverage. Technical wording is low confidence pending speaker review. Browser scenarios are syntax-checked only.

Thanks to xet7.
2026-10-07 04:58:56 +03:00
Lauri Ojansivu 6c96f833c2 Document Tibetan notification translations
Thanks to xet7.
2026-10-07 04:57:09 +03:00
Lauri Ojansivu 161bca4aab Translate Tibetan notification settings
Fill 13 English placeholders and extend regression coverage. Technical wording is provisional pending speaker review. Browser scenarios are syntax-checked only.

Thanks to xet7.
2026-10-07 04:56:56 +03:00
Lauri Ojansivu a5b9e6ea70 Document Acehnese notification translations
Thanks to xet7.
2026-10-07 04:55:49 +03:00
Lauri Ojansivu 96c33f68d5 Translate Acehnese notification settings
Fill 13 English placeholders and extend regression coverage. Technical wording is low confidence pending speaker review. Browser scenarios are syntax-checked only.

Thanks to xet7.
2026-10-07 04:55:34 +03:00
Lauri Ojansivu f2c66fc7e8 Document Northern Sami notification translations
Thanks to xet7.
2026-10-07 04:54:41 +03:00
Lauri Ojansivu b9a183544d Translate Northern Sami notification settings
Fill 13 English placeholders and extend notification regression coverage. Preserve existing translations and the reminder/mention exception. Technical wording is low confidence pending speaker review; browser scenarios are syntax-checked only.

Thanks to xet7.
2026-10-07 04:54:28 +03:00
Lauri Ojansivu 1aa6c65abe Document Venda and Venetian notification translations
Thanks to xet7.
2026-10-07 04:53:29 +03:00
Lauri Ojansivu 28fad83e87 Translate Venda and Venetian notification settings
Fill 26 English placeholders and replace six Zulu/Afrikaans labels in Venda. Extend notification and wrong-language regression checks. Technical wording is low confidence pending speaker review; browser scenarios are syntax-checked only.

Thanks to xet7.
2026-10-07 04:53:15 +03:00
Lauri Ojansivu 841d8019e3 Document Buryat, Sakha and Chuvash notification translations
Thanks to xet7.
2026-10-07 04:51:54 +03:00
Lauri Ojansivu 8c3b630cec Translate Buryat, Sakha and Chuvash notification settings
Fill 39 English placeholders and extend notification regression coverage. Preserve existing translations and the reminder/mention exception. Technical wording is low confidence pending speaker review; browser scenarios are syntax-checked only.

Thanks to xet7.
2026-10-07 04:51:41 +03:00
Lauri Ojansivu 0a07f26575 Document Aromanian notification translations
Thanks to xet7.
2026-10-07 04:50:09 +03:00
Lauri Ojansivu 33831dc768 Translate Aromanian notification settings
Fill 13 English placeholders and extend notification regression coverage. Preserve existing translations and the reminder/mention exception. Technical wording is low confidence pending speaker review; browser scenarios are syntax-checked only.

Thanks to xet7.
2026-10-07 04:49:56 +03:00
Lauri Ojansivu 4756e58e42 Document Wolof and Ewe notification translations
Thanks to xet7.
2026-10-07 04:48:36 +03:00
Lauri Ojansivu 91c6e2ec7e Translate Wolof and Ewe notification settings
Fill 26 English placeholders and extend notification regression coverage. Preserve existing translations and the reminder/mention exception. Technical wording is low confidence pending speaker review; browser scenarios are syntax-checked only.

Thanks to xet7.
2026-10-07 04:48:24 +03:00
Lauri Ojansivu 385cedd810 Document Akan, Luganda and Bambara notification translations
Thanks to xet7.
2026-10-07 04:46:43 +03:00
Lauri Ojansivu 173ce86ceb Translate Akan, Luganda and Bambara notification settings
Fill 39 English placeholders and extend notification regression coverage. Preserve existing translations and the reminder/mention exception. Technical wording is low confidence pending speaker review; browser scenarios are syntax-checked only.

Thanks to xet7.
2026-10-07 04:46:21 +03:00
Lauri Ojansivu a271cf2465 Document Manx, Walloon and Waray notification translations
Thanks to xet7.
2026-10-07 04:44:46 +03:00
Lauri Ojansivu 7d2b5f717c Translate Manx, Walloon and Waray notification settings
Fill 39 English placeholders and extend notification regression coverage. Preserve existing translations and the reminder/mention exception. Technical wording is low confidence pending speaker review; browser scenarios are syntax-checked only.

Thanks to xet7.
2026-10-07 04:44:31 +03:00
Lauri Ojansivu 054a2636b6 Record Oromo Fijian and Tongan translation progress
Link the checked batch and document pending-key coverage.

Thanks to xet7.
2026-10-07 04:41:27 +03:00
Lauri Ojansivu 279fbe4c5e Translate Oromo Fijian and Tongan notification settings
Fill 39 English placeholders and extend translation and mute/unmute browser coverage. Preserve the reminder and mention exception and document provisional technical wording for review.

Thanks to xet7.
2026-10-07 04:41:12 +03:00
Lauri Ojansivu 672b96b133 Record Northern Sotho and Tsonga translation progress
Link the checked batch and document pending-key coverage.

Thanks to xet7.
2026-10-07 04:39:55 +03:00
Lauri Ojansivu e656bc76aa Translate Northern Sotho and Tsonga notification settings
Fill 26 English placeholders and extend translation and mute/unmute browser coverage. Preserve the reminder and mention exception and record provisional technical wording for review.

Thanks to xet7.
2026-10-07 04:39:41 +03:00
Lauri Ojansivu cd0a058fa7 Record Yiddish Ndebele and Swati translation progress
Link the checked batch and document pending-key coverage.

Thanks to xet7.
2026-10-07 04:38:37 +03:00
Lauri Ojansivu 14be365fbc Translate Yiddish Ndebele and Swati notification settings
Fill 39 English placeholders and extend translation and mute/unmute browser coverage. Preserve the reminder and mention exception. Record provisional technical wording for review.

Thanks to xet7.
2026-10-07 04:38:23 +03:00
Lauri Ojansivu 1c1a5546d6 Record Konkani and Moroccan Arabic translation progress
Link the checked batch and document pending-key coverage.

Thanks to xet7.
2026-10-07 04:37:13 +03:00
Lauri Ojansivu 5f21598c84 Translate Konkani and Moroccan Arabic notification settings
Fill 26 English placeholders and extend translation and mute/unmute browser coverage. Preserve the reminder and mention exception and distinguish members from assigned workers.

Thanks to xet7.
2026-10-07 04:36:58 +03:00
Lauri Ojansivu 26c2565ef7 Record Bhojpuri Maithili and Odia translation progress
Link the checked batch and document pending-key coverage.

Thanks to xet7.
2026-10-07 04:36:01 +03:00
Lauri Ojansivu 6cdbc0b985 Translate Bhojpuri Maithili and Odia notification settings
Fill 39 English placeholders and extend translation and mute/unmute browser coverage. Preserve the reminder and mention exception and distinguish members from assigned workers.

Thanks to xet7.
2026-10-07 04:35:46 +03:00
Lauri Ojansivu 8017da7a64 Record three-language notification translation progress
Link the checked batch and document pending-key coverage.

Thanks to xet7.
2026-10-07 04:34:31 +03:00
Lauri Ojansivu 5f1c4f8f97 Translate Kinyarwanda Kirundi and Chichewa notification settings
Fill 39 English placeholders and extend translation and mute/unmute browser coverage. Preserve the reminder and mention exception and distinguish members from assigned workers.

Thanks to xet7.
2026-10-07 04:34:15 +03:00
Lauri Ojansivu 4d0df77d8d Record eight-locale notification translation progress
Link the checked batch and document pending-key coverage and review limits.

Thanks to xet7.
2026-10-07 04:33:17 +03:00
Lauri Ojansivu 01d5eb0002 Translate notification preferences into eight more locales
Fill 104 English placeholders in Māori, Samoan, Hawaiian, both Zulu locales, Xhosa, Sesotho and Setswana. Extend translation and browser coverage while preserving the reminder and mention exception. Record technical terminology review needs.

Thanks to xet7.
2026-10-07 04:33:01 +03:00
Lauri Ojansivu c4f110930f Record five-language notification translation progress
Link the checked batch and document its pending-key coverage.

Thanks to xet7.
2026-10-07 04:30:50 +03:00
Lauri Ojansivu 06000469c2 Translate activity notifications into five more languages
Fill 65 English placeholders in Kurmanji, Sorani, Papiamento, Tok Pisin and Bislama. Preserve the reminder and mention exception and extend translation and mute/unmute browser coverage.

Thanks to xet7.
2026-10-07 04:30:14 +03:00
Lauri Ojansivu a61218261a Record activity notification translation progress
Link the three-language batch and explain its separately tracked pending values.

Thanks to xet7.
2026-10-07 04:28:33 +03:00
Lauri Ojansivu 36be01e480 Translate activity notification settings into three languages
Fill 39 English placeholders in Turkmen, Tatar and Somali. Preserve the due-reminder and mention exception, distinguish members from assignees, and extend translation and mute/unmute browser coverage.

Thanks to xet7.
2026-10-07 04:28:17 +03:00
Lauri Ojansivu 292572cd2c Record all-locale archiving translation coverage
Link the checked Cherokee batch and update TODO Later with the remaining translation and review scope.

Thanks to xet7.
2026-10-07 04:26:11 +03:00
Lauri Ojansivu 48737eb6af Fill Cherokee archiving translations and verify every locale
Fill 23 English placeholders. Expand feature checks to all 234 non-English locale paths, accept equivalent native digits, and remove completed auto-archive keys from the pending queue. Document low-confidence Cherokee wording and broader review limitations.

Thanks to xet7.
2026-10-07 04:25:53 +03:00
Lauri Ojansivu 0f1dd217e8 Record Inuktitut translation progress
Link the checked batch and refresh the TODO Later backlog.

Thanks to xet7.
2026-10-07 04:22:51 +03:00
Lauri Ojansivu 86ab777a6a Translate Inuktitut archiving and date filters
Fill 23 English placeholders and extend feature and browser coverage. Preserve literal query syntax and document low-confidence technical wording for speaker review.

Thanks to xet7.
2026-10-07 04:22:38 +03:00
Lauri Ojansivu e169af7e6c Record Wolaytta translation progress
Link the checked batch and refresh the TODO Later backlog.

Thanks to xet7.
2026-10-07 04:20:47 +03:00
Lauri Ojansivu 278be50392 Translate Wolaytta archiving and date filters
Fill 23 English placeholders and repair three English-prefixed time labels. Extend feature and browser coverage; document low-confidence wording for speaker review.

Thanks to xet7.
2026-10-07 04:20:32 +03:00
Lauri Ojansivu 4fa50167f9 Record Tamazight translation progress
Link the checked batch and refresh the TODO Later backlog.

Thanks to xet7.
2026-10-07 04:19:25 +03:00
Lauri Ojansivu ada662a24a Translate Tamazight archiving and date filters
Fill 23 English placeholders in Tifinagh and extend feature and browser coverage. Preserve query literals and document low-confidence regional and technical wording for review.

Thanks to xet7.
2026-10-07 04:19:09 +03:00
Lauri Ojansivu 611e10923d Record Tigre translation progress
Link the checked batch and refresh the TODO Later backlog.

Thanks to xet7.
2026-10-07 04:17:49 +03:00
Lauri Ojansivu 7fc4940250 Translate Tigre archiving and date filters
Fill 23 English placeholders and extend feature and browser coverage. Document low-confidence grammar and terminology requiring speaker review.

Thanks to xet7.
2026-10-07 04:17:25 +03:00
Lauri Ojansivu b5b8e8a9cc Record Dzongkha translation progress
Link the verified batch and refresh the TODO Later backlog.

Thanks to xet7.
2026-10-07 04:15:40 +03:00
Lauri Ojansivu 07f20cf879 Translate Dzongkha archiving and date filters
Fill 23 English placeholders and extend feature and browser coverage. Record vocabulary references and low-confidence wording for speaker review.

Thanks to xet7.
2026-10-07 04:15:23 +03:00
Lauri Ojansivu ab4547e8a4 Record Greenlandic translation progress
Link the verified batch and refresh the TODO Later backlog.

Thanks to xet7.
2026-10-07 04:13:19 +03:00
Lauri Ojansivu 5a17ee898e Translate Greenlandic archiving and date filters
Fill 23 English placeholders and extend feature and browser coverage. Document terminology references and low-confidence wording for speaker review.

Thanks to xet7.
2026-10-07 04:13:04 +03:00
Lauri Ojansivu 7fc2e0084c Record Nahuatl translation progress
Link the verified batch and refresh the TODO Later backlog.

Thanks to xet7.
2026-10-07 04:11:49 +03:00
Lauri Ojansivu 5c4176bcbf Translate Nahuatl archiving and date filters
Fill 23 English placeholders and extend feature and browser coverage. Document low-confidence grammar, technical loans and dialect review needs.

Thanks to xet7.
2026-10-07 04:11:34 +03:00
Lauri Ojansivu 74aa860a41 Record Volapük translation progress
Link the verified translation batch and update TODO Later.

Thanks to xet7.
2026-10-07 04:10:22 +03:00
Lauri Ojansivu 59c0b6a8d8 Translate Volapük archiving and date filters
Fill 23 English placeholders and correct date, week and month labels. Extend feature and browser coverage; document low-confidence wording and dictionary evidence for review.

Thanks to xet7.
2026-10-07 04:10:09 +03:00
Lauri Ojansivu 1090ac7696 Record Sakha translation progress
Link the verified translation batch and refresh the TODO Later backlog count.

Thanks to xet7.
2026-10-07 04:08:28 +03:00
Lauri Ojansivu 960f063bb3 Translate Sakha archiving and date filters
Fill 23 English placeholders, preserve query syntax, and extend feature and browser coverage. Record low-confidence wording and terminology sources for speaker review.

Thanks to xet7.
2026-10-07 04:08:14 +03:00
Lauri Ojansivu b9acf95dec Record Acehnese and Bambara translation progress
Link the verified translation batch and update the TODO Later backlog.

Thanks to xet7.
2026-10-07 04:07:01 +03:00
Lauri Ojansivu 5c44005eb8 Translate Acehnese and Bambara archiving and date filters
Fill 46 English placeholders and correct the Acehnese days label. Extend regression coverage and document low-confidence wording and vocabulary sources for speaker review.

Thanks to xet7.
2026-10-07 04:06:46 +03:00
Lauri Ojansivu ef93ec9e66 Record Akan and Luganda translation progress
Update TODO Later with the verified remaining count and link the translation batch.

Thanks to xet7.
2026-10-07 04:05:10 +03:00
Lauri Ojansivu f8868aaf08 Translate Akan and Luganda archiving and date filters
Fill 46 English placeholders and correct Akan day and list labels. Extend feature and browser coverage. Document terminology sources and low-confidence wording for speaker review.

Thanks to xet7.
2026-10-07 04:04:57 +03:00
Lauri Ojansivu fe72532712 Record Aymara and Quechua translation progress
Link the verified translation batch and refresh the TODO Later backlog count.

Thanks to xet7.
2026-10-07 04:03:17 +03:00
Lauri Ojansivu 5d1980d895 Translate Aymara and Quechua archiving and date filters
Fill 46 English placeholders and repair four prefixed time labels. Extend feature and browser coverage; document low-confidence grammar and terminology requiring speaker review.

Thanks to xet7.
2026-10-07 04:03:01 +03:00
Lauri Ojansivu 547b6ae5eb Record five-language archiving translation coverage
Update the remaining translation snapshot and link the verified implementation. Keep speaker review and the broader backlog open.

Thanks to xet7.
2026-10-07 04:00:52 +03:00
Lauri Ojansivu 5e7dd86142 Translate archiving and date filters into five more languages
Fill 115 English placeholders in Guaraní, Ewe, Wolof, Fulah and Klingon. Preserve existing translations and literal query syntax, and extend feature and browser coverage. Wording remains low confidence pending speaker review; record terminology sources and review limitations.

Thanks to xet7.
2026-10-07 04:00:33 +03:00
Lauri Ojansivu 2b82fd5fef Record nine-language archiving translation progress
Update the remaining backlog and preserve the low-confidence terminology and unrun browser checks in the release notes.

Thanks to xet7 !
2026-10-07 03:54:57 +03:00
Lauri Ojansivu fc48f99193 Translate archiving and date filters into nine more languages
Fill 207 English placeholders in Tibetan, Buryat, Chuvash, Kashmiri, Tigrinya, Manx, Venetian, Veps and Venda. Preserve existing translations, numeric bounds and literal filter syntax. Extend the shared regression suite and browser mutation scenario.

Wording is provisional and low confidence pending speaker review, especially inclusive ranges and list-age explanations. Record vocabulary sources and remaining wrong-language seed text. Locale-wide key/order/token, archiving and human-preference checks pass; browser scenarios are syntax-checked only.

Thanks to xet7 !
2026-10-07 03:54:32 +03:00
Lauri Ojansivu 7e4de832af Record all-locale login-setting fill and remaining translation work
Update the pending queue count and validation evidence without marking the wider translation or semantic audit complete.

Thanks to xet7 !
2026-10-07 03:46:11 +03:00
Lauri Ojansivu 9cf2455ab5 Fill login-setting messages in every locale
Add the remaining 101 non-English catalogs and align eleven English variants. All 234 non-English locale paths now contain the three login-setting messages. Preserve all existing translations and remove the completed keys from the pending queue.

All 237 translation suites pass. Native-script and related-language regression checks cover the new labels; existing browser scenarios remain syntax-checked only.

Minority-language technical compounds are provisional, especially Cherokee, Inuktitut, Tigre, Wolaytta, Aymara, Nahuatl, Tamazight, Veps, Ladin, Aromanian, Volapuk and Klingon. Record terminology sources and broader mixed-language findings for continued review.

Thanks to xet7 !
2026-10-07 03:45:50 +03:00
Lauri Ojansivu 171c6c186d Record extended login-setting translation coverage
Track 133 translated locale paths and provisional terminology while leaving the remaining all-language work open.

Thanks to xet7 !
2026-10-07 03:35:24 +03:00
Lauri Ojansivu 2f148be4ac Extend login-setting translations to 32 more locales
Translate header authentication, stored-value removal and restart guidance in 31 physical catalogs plus a shared Khmer alias. Preserve existing values and source tokens; extend regression coverage to 133 locale paths.

Kyrgyz, Mongolian, Burmese, Khmer, Pashto, Sindhi and Uyghur technical wording is provisional and needs speaker review. Structural and human-preference checks pass.

Thanks to xet7 !
2026-10-07 03:34:44 +03:00
Lauri Ojansivu ea85dc9771 Record resumed translation work and login-setting coverage
Refresh TODO Later with the current backlog and distinguish pending Transifex keys from the standard report. Record the 101-locale login batch and its validation.

Changelog link and release membership tests pass. The format suite retains its pre-existing failure on two visible Turkmen commit hashes, reproduced against the prior changelog.

Thanks to xet7 !
2026-10-07 03:33:13 +03:00
Lauri Ojansivu 30881d1c41 Translate login-setting messages in 101 locales
Add HTTP-header login, stored-value removal and restart guidance in 100 physical catalogs and their shared regional alias. Preserve every existing translation and source placeholder. Add locale-wide structural checks and Finnish, Arabic and Japanese browser scenarios.

Locale checks, human preference, URL placeholders, menu wiring and board-item tests pass. Browser scenarios are syntax-checked; Playwright and a running application are unavailable here. Remaining languages and feature messages stay in progress.

Thanks to xet7 !
2026-10-07 03:32:27 +03:00
dependabot[bot] 67dc55a353 Bump the npm_and_yarn group across 1 directory with 2 updates
Bumps the npm_and_yarn group with 2 updates in the / directory: [shell-quote](https://github.com/ljharb/shell-quote) and [source-map-js](https://github.com/7rulnik/source-map-js).


Updates `shell-quote` from 1.10.0 to 1.12.0
- [Changelog](https://github.com/ljharb/shell-quote/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/shell-quote/compare/v1.10.0...v1.12.0)

Updates `source-map-js` from 1.2.1 to 1.2.2
- [Release notes](https://github.com/7rulnik/source-map-js/releases)
- [Changelog](https://github.com/7rulnik/source-map-js/blob/main/CHANGELOG.md)
- [Commits](https://github.com/7rulnik/source-map-js/compare/v1.2.1...v1.2.2)

---
updated-dependencies:
- dependency-name: shell-quote
  dependency-version: 1.12.0
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: source-map-js
  dependency-version: 1.2.2
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-07 00:13:45 +00:00
github-actions[bot] 420792f4a7 Bump versions for v12.20 2026-10-07 00:12:06 +00:00
Lauri Ojansivu 36a8ced5ea Prepare v12.20 release 2026-10-07 03:10:24 +03:00
Lauri Ojansivu dbf7a27e2d Add changelog entries for the large board card open and close fixes.
Thanks to markusst1982 and xet7 !
2026-10-07 03:08:49 +03:00
Lauri Ojansivu 96dff057a1 Card details: stop the unsaved-edits subscription when the card closes.
Template.cardDetails.onCreated called Meteor.subscribe('unsaved-edits') in
no computation, so nothing stopped it: every card open added one more live
publication on the server for that user. It is the template's own
subscription now.

Thanks to markusst1982 and xet7 !

Fixes #6745.
2026-10-07 03:08:49 +03:00
Lauri Ojansivu 2122214fb6 Snap: default cards-loading to auto, like every other platform.
The snap set CARDS_LOADING=all whenever cards-loading was unset, so every
board on a snap shipped all of its cards, comments, checklists and attachment
records to the browser, while the code's own default is auto (a board with
more than 500 cards loads only the visible cards). The snap default, its
description, wekan-help (which pointed at an Admin Panel toggle that no longer
exists) and server/cards-loading.js now say auto.

Thanks to markusst1982 and xet7 !

Fixes #6745.
2026-10-07 03:08:48 +03:00
Lauri Ojansivu a0f2550e5d Large board: set jQuery UI sortable options only when they changed.
The card, list and swimlane sortables re-applied handle, items and disabled
from autoruns. jQuery UI does not skip an unchanged value: setting handle
re-tags every card of the list, about k^2 work for k cards, in every list.
setSortableOptions (models/lib/sortableOptions.js) sets only what differs. The
swimlanes autorun also asked canModifyCard() with no card, which reads the
open card, so it re-ran on every open and close; it asks canModifyBoard().

Thanks to markusst1982 and xet7 !

Fixes #6745.
2026-10-07 03:08:48 +03:00
Lauri Ojansivu ecbfe59e50 Large board: clear the popup card Session keys to null instead of deleting them.
The routes set popupCardId and popupCardBoardId to null, and a card open or
close deleted them. A delete followed by set(null) is a change, and
Utils.getCurrentBoardId() reads popupCardBoardId, so every minicard re-ran its
currentBoard helpers. Every place now clears them to null.

Thanks to markusst1982 and xet7 !

Fixes #6745.
2026-10-07 03:08:48 +03:00
Lauri Ojansivu 4387185002 Large board: open and close a card by its address without re-creating the board.
The card and board routes rendered the board layout on every navigation, and
flow-router-extra re-creates the layout each time: every swimlane, list and
minicard was torn down and built again for a card link, the up/down card
keys, back/forward, and closing a card opened from a link. The router now
remembers which board the layout is mounted for (models/lib/boardLayoutMount.js)
and the card and board routes reuse it; the board already follows Session.
Another board, a first load and the list/swimlane routes still render.

tests/playwright/specs/large-board-card-open.e2e.js checks in Chromium,
Firefox and WebKit that a minicard element survives a card opened and closed
by its address, and counts the Tracker work of a click open and close.

Thanks to markusst1982 and xet7 !

Fixes #6745.
2026-10-07 03:08:48 +03:00
Lauri Ojansivu 6d5f24ee27 Large board: read only the user fields each card and list needs.
Opening a card writes the user document (profile.cardLastViews, #3078). The
board view (Utils.storedBoardView and allowBoardView, which every list's card
loop reads) and the helpers every minicard runs read the WHOLE user document,
so that one write re-ran every list's card loop, and Blaze then re-evaluated
every minicard: 37,452 Tracker invalidations for one card open on a 40-card
board. Measured in the running app, it is now 390.

client/lib/currentUserWith.js reads the current user with only the named
fields; minimongo re-runs a field-limited query only when a projected field
changes. The board view, feature preview, unread comments, folds, drag
handles, label text, dependency layers, date format, week number, mobile mode
and the admin-only custom field check now use it. setCardLastViewed $sets one
entry instead of rewriting the whole map, which is also capped at 5,000.

Thanks to markusst1982 and xet7 !

Fixes #6745.
2026-10-07 03:08:48 +03:00
Lauri Ojansivu 6e97cb1fdd Removed non-existing screenshot 2026-10-06 18:32:23 +03:00
Lauri Ojansivu bfc906c598 Add changelog entry confirming GHSA-r3c4-5xwp-vf54 does not apply.
Thanks to a25370 and xet7 !
2026-10-06 18:27:38 +03:00
Lauri Ojansivu cb703fd326 Confirm GET /api/boards/:boardId/domains is site-admin only; GHSA-r3c4-5xwp-vf54 does not apply.
The advisory reports that the route checks only the login, because it calls
Authentication.checkUserId, and that any account can read any board's domain
sharing. checkUserId is the site admin check: a logged-in caller who is not an
admin gets 403. The route has answered only site admins since #5850 added it.
Before v12.15 the check refused `admin === undefined`, and Meteor's
findOneAsync gives undefined for no match, so it refused everyone else then too.

Nothing to fix in the route. Its OpenAPI description claimed anyone able to read
the board may list the domains; it now says site admin, and that members get the
list with GET /api/boards/:boardId. checkUserId gets a comment saying what it is.

tests/restBoardDomainsAccess.test.cjs runs the real handler with the real
Authentication object: the reporter's non-member, members, ended memberships and
logged-out callers are refused, a missing board answers like an existing one, a
site admin reads the list. A whole-tree negative test checks that every REST
route with :boardId checks that board or a site admin, and that a login check
alone would be caught. tests/playwright/specs/rest-board-domains-access.e2e.js
runs the report's proof of concept against a running WeKan in Chromium, WebKit
and Firefox: 403.

Thanks to a25370 and xet7 !
2026-10-06 18:27:20 +03:00
Lauri Ojansivu 9f8bf02a63 Updates 2026-10-06 08:04:20 +03:00
Lauri Ojansivu f1a3880e52 Added screenshot 2026-10-06 07:58:44 +03:00
Lauri Ojansivu e0739adce6 Updated readme.
Thanks to xet7 !
2026-10-06 07:39:47 +03:00
Lauri Ojansivu d27e2eb895 Updated readme.
Thanks to xet7 !
2026-10-06 07:38:38 +03:00
Lauri Ojansivu febf65bfa6 Updated WeKan features to readme.
Thanks to xet7 !
2026-10-06 07:31:01 +03:00
github-actions[bot] 4af35e8569 Bump versions for v12.19 2026-10-05 18:29:14 +00:00
Lauri Ojansivu c9dc95eb89 Prepare v12.19 release 2026-10-05 21:27:12 +03:00
Lauri Ojansivu ab109a7111 Record in TODO Later what the login settings work left open.
The *_FILE secrets for mail, MongoDB and S3, LDAP Test connection without a
service account, the unread LOGOUT_* variables, header login switched on from
the Admin Panel, the unloaded ldap_sync_now, the released section that fails
changelogFormat, the cookie refresh limit in browser tests, the three pending
strings, a real Active Directory check of #6744, and the UCS and Nextcloud
settings kept in other repositories - each with the reason it is not done.

Thanks to xet7 !
2026-10-05 21:16:20 +03:00
Lauri Ojansivu 8e6f713a94 Add changelog entries for login secret files and the default sign-in method override.
Thanks to Roemer, CrashOverride-lab, ww-daniel-mora and xet7 !
2026-10-05 21:11:44 +03:00
Lauri Ojansivu 0314406656 Read login secrets from *_FILE, and let the Admin Panel override DEFAULT_AUTHENTICATION_METHOD.
LDAP_AUTHENTIFICATION_PASSWORD_FILE and OAUTH2_SECRET_FILE were listed in the
Dockerfile, snap, docker-compose and start-wekan since #5724, but no code read
them, so a password could only be an environment variable. WeKan now reads the
file itself, at each login (a rotated secret applies at once), so the password
is never in the environment - the point of Docker / Kubernetes secrets. The
order is the Admin Panel, the variable, then its file; one trailing line break
is dropped. The application code is the same on every platform, so it works in
Docker, the snap (files under /var/snap/wekan/common), bundles, AppImage,
Flatpak, Mac and Windows. Admin Panel / People shows when the password comes
from the file and when the file cannot be read, never the path or content.
The path itself is an environment variable only: settable in the Admin Panel
next to the LDAP host or the OAuth2 token endpoint, it would let the server
read any file and send it there as the password.

DEFAULT_AUTHENTICATION_METHOD is a field of the Login pane's settings form.
Choosing a method overrides the variable; Default gives it back. Before, the
variable rewrote the stored method at every start, so an administrator's
choice was lost on restart. The published method is now the override, else the
variable, else password. On upgrade, a method chosen with the old dropdown
while no variable was set becomes the override, once, so it is not lost. The
old dropdown is removed: two controls for one setting on one page.

The snap key for the LDAP password file was spelled
ldap-authentification-password-file, unlike its siblings and its help text;
it is ldap-authentication-password-file now (it had no effect before).
MAIL_SERVICE_PASSWORD_FILE, MONGO_PASSWORD_FILE and S3_SECRET_FILE are still
not read by WeKan; secrets/README.md says so.

Thanks to Roemer, CrashOverride-lab, ww-daniel-mora and xet7 !
2026-10-05 21:11:12 +03:00
Lauri Ojansivu 2340acb38a Add changelog entries for overriding every login environment variable in the Admin Panel.
Thanks to xet7 !
2026-10-05 19:49:29 +03:00
Lauri Ojansivu eb49a4b1b6 Override every login environment variable in Admin Panel / People.
Each login method has its own section: LDAP, OAuth2 (OpenID Connect and
Oracle OIM), CAS and Header login are new or complete, and the Login pane
gets PASSWORD_LOGIN_ENABLED and ACCOUNTS_COMMON_LOGIN_EXPIRATION_IN_DAYS.
models/lib/authConfigCatalog.js lists all 103 variables; server/lib/
authConfig.js resolves each one at run time (Admin Panel value first, then
the environment variable) for the app and, through
globalThis.__wekanAuthEnv, for the wekan-ldap, wekan-oidc and
wekan-accounts-cas packages. OAuth2 and CAS service configurations and LDAP
background sync are reapplied when their settings are saved.

Passwords and client secrets (LDAP_AUTHENTIFICATION_PASSWORD, OAUTH2_SECRET)
never reach the browser: the page learns only whether one is set and where
it comes from. Other values have credentials written inside a URL masked on
the server, and a URL field refuses them.

Fixed on the way:
- The Admin Panel LDAP overrides never applied. The bridge read Settings with
  a server-side findOne(), which Meteor 3 refuses; the error was swallowed and
  the environment used. The settings are now held in an observed cache.
- LDAP Test connection never existed on the server (testConnection.js was not
  imported) and used the synchronous Meteor.user().
- PROPAGATE_OIDC_DATA=false turned the feature on.
- CAS read only the misspelled CASE_VALIDATE_URL; CAS_VALIDATE_URL now works
  and the old name still does.
- snap help named three group-filter keys snap set does not know.

Every catalog variable is now listed in the Dockerfile, the docker-compose
files, snap config and help, and start-wekan.sh/.bat; 14 were missing.
tests/authConfigCatalog.test.cjs pins resolution, secrets, input validation,
platform coverage, and - across the whole tree - that no code reads a login
variable outside the catalog or straight from process.env, and that no
server code calls the synchronous Settings.findOne() or Meteor.user().
tests/playwright/specs/admin-login-env-overrides.e2e.js drives the pages.

Thanks to xet7 !
2026-10-05 19:48:48 +03:00
Lauri Ojansivu bbf2f40b69 Add changelog entry for nested Active Directory groups in the LDAP group filter.
Thanks to rmb82 and xet7 !
2026-10-05 18:54:24 +03:00
Lauri Ojansivu dc2e7592a3 Accept nested Active Directory groups in the LDAP group filter.
Since v12.08 (DirectoryGroupBleed) LDAP_USER_AUTHENTICATION=true logins check
LDAP_GROUP_FILTER_ENABLE, which they did not do before. The membership clause
(member=<user DN>) matches direct members only, so in Active Directory, where
access is granted through a team group nested in the access group, legitimate
users were refused, REST clients got 401, and the refusals were listed in
Admin Panel / Problems as ldap.group-denied.

LDAP_GROUP_FILTER_NESTED=true makes both group searches use AD's
LDAP_MATCHING_RULE_IN_CHAIN, (member:1.2.840.113556.1.4.1941:=<user DN>), so
the login filter and admin, role and org/team sync see nested membership. It
is off by default, and an attribute already written as the extensible match
is used as written. The user DN stays escaped, and a user who cannot be named
is still refused without a search.

Documented in LDAP.md with an upgrade note, and added to the Dockerfiles,
docker-compose files, snap config and start-wekan scripts.
tests/ldapNestedGroups.test.cjs drives isUserInGroup and getUserGroups with and
without the setting, and checks that every member clause in ldap.js goes through
groupMemberClause.

Thanks to rmb82 and xet7 !

Fixes #6744.
2026-10-05 18:53:52 +03:00
Lauri Ojansivu 2e42ce00d9 Merge remote-tracking branch 'origin/main' 2026-10-04 17:13:44 +03:00
Lauri Ojansivu 3401d908ac Add changelog entry for attaching release files from their own jobs.
Thanks to xet7 !
2026-10-04 17:11:40 +03:00
Lauri Ojansivu 37bc66403f Attach each release file from the job that built it, also when cancelled.
Every job that builds a release file now attaches it to the GitHub Release
as its own last step, right after the build and its checks pass, through
releases/github-release-upload.sh (retried, bounded, verified by name and
size). Nothing waits for the other builds any more:

- build-amd64 creates the release (releases/ensure-github-release.sh) and
  attaches its zip; build-arm64 attaches its own. The release job no
  longer downloads and uploads them; it writes the notes and checks both
  base bundles and their .sha256sum are on the release.
- The win64, win-arm64, win32, mac-arm64, mac-x64, extra-arch, Sandstorm
  and snap jobs use the shared uploader.
- AppImage.yml, mac.yml and Flatpak.yml attach from each matrix build job
  instead of a final publish job; windows.yml attaches from its build job
  and its publish job is gone. release-all-missing.yml's extra-arches
  attach uses the shared uploader.

A cancelled run keeps what finished: every attach step runs on always()
plus its build step's success, and the final jobs (release notes, what is
still missing, the AppImage/Mac/Flatpak summaries and the Flatpak
repository) run on always() while still requiring the release to exist.

The uploader no longer needs GNU timeout, so it also runs on the macOS
runners and in Windows Git Bash.

Thanks to xet7 !
2026-10-04 17:10:53 +03:00
github-actions[bot] 4e7d95921c Bump versions for v12.18 2026-10-04 01:25:45 +00:00
Lauri Ojansivu 437a1d4e7c Prepare v12.18 release 2026-10-04 04:23:44 +03:00
Lauri Ojansivu 9af7640bc5 Document upcoming dependency updates after v12.17
Add the release summary and linked Sharp, MongoDB, Chai and TypeScript ESLint updates, with Dependabot credit. The read-only release preflight and both changelog regression suites pass.

Thanks to dependabot and xet7 !
2026-10-04 03:32:04 +03:00
Lauri Ojansivu 17d8f838cb Merge pull request #6743 from wekan/dependabot/npm_and_yarn/typescript-eslint/eslint-plugin-8.71.0
Bump @typescript-eslint/eslint-plugin from 8.70.1 to 8.71.0
2026-10-04 03:27:23 +03:00
Lauri Ojansivu 4f21459552 Merge pull request #6742 from wekan/dependabot/npm_and_yarn/tests/playwright/mongodb-7.7.0
Bump mongodb from 7.6.0 to 7.7.0 in /tests/playwright
2026-10-04 03:27:07 +03:00
Lauri Ojansivu b0c8925718 Merge pull request #6741 from wekan/dependabot/npm_and_yarn/chai-6.3.0
Bump chai from 6.2.2 to 6.3.0
2026-10-04 03:26:54 +03:00
dependabot[bot] 66db856dbf Bump @typescript-eslint/eslint-plugin from 8.70.1 to 8.71.0
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 8.70.1 to 8.71.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.71.0/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.71.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-03 21:42:17 +00:00
dependabot[bot] 56bd2fbbfe Bump chai from 6.2.2 to 6.3.0
Bumps [chai](https://github.com/chaijs/chai) from 6.2.2 to 6.3.0.
- [Release notes](https://github.com/chaijs/chai/releases)
- [Changelog](https://github.com/chaijs/chai/blob/main/History.md)
- [Commits](https://github.com/chaijs/chai/compare/v6.2.2...v6.3.0)

---
updated-dependencies:
- dependency-name: chai
  dependency-version: 6.3.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-03 21:42:12 +00:00
Lauri Ojansivu 273522e12e Merge pull request #6740 from wekan/dependabot/npm_and_yarn/sharp-0.35.5
Bump sharp from 0.35.4 to 0.35.5
2026-10-04 00:40:19 +03:00
Lauri Ojansivu a015965537 Merge pull request #6739 from wekan/dependabot/npm_and_yarn/mongodb-7.7.0
Bump mongodb from 7.6.0 to 7.7.0
2026-10-04 00:40:01 +03:00
Lauri Ojansivu 0f26d449de Merge pull request #6738 from wekan/dependabot/npm_and_yarn/typescript-eslint/parser-8.71.0
Bump @typescript-eslint/parser from 8.70.1 to 8.71.0
2026-10-04 00:39:48 +03:00
dependabot[bot] 41840dd324 Bump mongodb from 7.6.0 to 7.7.0 in /tests/playwright
Bumps [mongodb](https://github.com/mongodb/node-mongodb-native) from 7.6.0 to 7.7.0.
- [Release notes](https://github.com/mongodb/node-mongodb-native/releases)
- [Changelog](https://github.com/mongodb/node-mongodb-native/blob/main/HISTORY.md)
- [Commits](https://github.com/mongodb/node-mongodb-native/compare/v7.6.0...v7.7.0)

---
updated-dependencies:
- dependency-name: mongodb
  dependency-version: 7.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-03 20:33:22 +00:00
dependabot[bot] a688411bbe Bump sharp from 0.35.4 to 0.35.5
Bumps [sharp](https://github.com/lovell/sharp) from 0.35.4 to 0.35.5.
- [Release notes](https://github.com/lovell/sharp/releases)
- [Commits](https://github.com/lovell/sharp/compare/v0.35.4...v0.35.5)

---
updated-dependencies:
- dependency-name: sharp
  dependency-version: 0.35.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-03 20:33:06 +00:00
dependabot[bot] 0c5617c0e0 Bump mongodb from 7.6.0 to 7.7.0
Bumps [mongodb](https://github.com/mongodb/node-mongodb-native) from 7.6.0 to 7.7.0.
- [Release notes](https://github.com/mongodb/node-mongodb-native/releases)
- [Changelog](https://github.com/mongodb/node-mongodb-native/blob/main/HISTORY.md)
- [Commits](https://github.com/mongodb/node-mongodb-native/compare/v7.6.0...v7.7.0)

---
updated-dependencies:
- dependency-name: mongodb
  dependency-version: 7.7.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-03 20:33:01 +00:00
dependabot[bot] 69c55764a7 Bump @typescript-eslint/parser from 8.70.1 to 8.71.0
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 8.70.1 to 8.71.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.71.0/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.71.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-03 20:32:55 +00:00
4604 changed files with 928320 additions and 509456 deletions

No files matched your search

@@ -72,7 +72,7 @@ installer permanently to global npm packages.
| Latest supported CLI | Run `npx meteor`. |
| Pinned installer package | Run `npx meteor@<version> install`, then verify the reported Meteor release. |
| Linux or macOS alternative | `curl https://install.meteor.com/ \| sh` remains documented. Keep `npx meteor` as the primary cross-platform command. |
| npm fallback | Run `npm install -g meteor --foreground-script` only after the documented `npx` recovery steps fail. |
| npm fallback | Run `npm install -g meteor --foreground-scripts --allow-scripts=meteor` only after the documented `npx` recovery steps fail. |
| Existing project needs another release | Use the project's pin or a Meteor `--release` command. Do not replace the user-wide installation first. |
Never run `npx install meteor`. Never add the npm `meteor` installer to an
@@ -48,7 +48,7 @@ Do not offer the shell installer on Windows. If `npx` installation still fails
after cache recovery, use the documented npm fallback:
```bash
npm install -g meteor --foreground-script
npm install -g meteor --foreground-scripts --allow-scripts=meteor
```
Do not place `meteor` in an application's dependencies or devDependencies.
+24 -1
View File
@@ -105,6 +105,29 @@ ENV \
LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE="" \
LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE="" \
LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT="" \
LDAP_GROUP_FILTER_NESTED=false \
LDAP_BACKGROUND_SYNC_DISABLE_NONEXISTANT_USERS="" \
LDAP_MIGRATION_ALLOW_PASSWORD_LOGIN="" \
LDAP_USER_ATTRIBUTES="" \
LDAP_GROUP_BASEDN="" \
LDAP_SYNC_ORGANIZATIONS="" \
LDAP_SYNC_ORGANIZATIONS_GROUPS="" \
LDAP_SYNC_TEAMS="" \
LDAP_SYNC_TEAMS_GROUPS="" \
OAUTH2_LOGOUT_ENDPOINT="" \
OAUTH2_AVATAR_MAP="" \
OAUTH2_MERGE_EXISTING_USERS="" \
OAUTH2_AUTO_REGISTRATION="" \
OAUTH2_ADMIN_GROUPS="" \
OAUTH2_ALLOWED_EMAIL_DOMAINS="" \
PROPAGATE_OIDC_DATA="" \
OAUTH2_SECRET_JWT_KEY_PATH="" \
OAUTH2_SECRET_JWT_ISSUER="" \
OAUTH2_SECRET_JWT_KEY_ID="" \
OAUTH2_SECRET_JWT_AUDIENCE="" \
OAUTH2_SECRET_JWT_SUBJECT="" \
OAUTH2_SECRET_JWT_EXPIRES_IN="" \
CAS_MERGE_EXISTING_USERS="" \
LDAP_GROUP_FILTER_GROUP_NAME="" \
LDAP_UNIQUE_IDENTIFIER_FIELD="" \
LDAP_UTF8_NAMES_SLUGIFY=true \
@@ -184,7 +207,7 @@ ENV \
OAUTH_MEETUP_CLIENT_ID="" \
OAUTH_MEETUP_SECRET="" \
OAUTH_MEETUP_SECRET_FILE="" \
OAUTH_PROVIDERS_LOGIN_STYLE="popup" \
OAUTH_PROVIDERS_LOGIN_STYLE="redirect" \
OAUTH_PROVIDERS_MERGE_EXISTING_USERS=false \
PASSWORDLESS_ENABLED=false \
ORACLE_OIM_ENABLED=false \
+18
View File
@@ -0,0 +1,18 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module",
"project": [
"./tsconfig.lint.json"
]
},
"plugins": [
"@typescript-eslint"
],
"rules": {
"@typescript-eslint/no-floating-promises": "error",
"@typescript-eslint/no-misused-promises": "error"
}
}
+185 -67
View File
@@ -81,8 +81,9 @@ on:
default: ''
type: string
# Read-only by default; the job that writes is granted it below.
permissions:
contents: write
contents: read
concurrency:
group: wekan-appimage-${{ inputs.tag || github.ref }}
@@ -97,19 +98,23 @@ jobs:
tag: ${{ steps.find.outputs.tag }}
version: ${{ steps.find.outputs.version }}
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- id: find
env:
GH_TOKEN: ${{ github.token }}
INPUT_TAG: ${{ inputs.tag }}
GITHUB_CTX_REPOSITORY: ${{ github.repository }}
run: |
set -euo pipefail
tag='${{ inputs.tag }}'
tag="${INPUT_TAG}"
# --repo on every gh call, whether or not the job happens to have
# checked out: a call that relies on an inferred remote breaks the day
# somebody removes the checkout, and the error it gives - "fatal: not
# a git repository" - names neither gh nor the workflow.
[ -n "$tag" ] || tag="$(gh release list --repo "${{ github.repository }}" \
[ -n "$tag" ] || tag="$(gh release list --repo "${GITHUB_CTX_REPOSITORY}" \
--limit 1 --json tagName --jq '.[0].tagName')"
if [ -z "$tag" ]; then
echo "::error::No release to wrap, and no tag was given."
@@ -122,7 +127,13 @@ jobs:
echo "Wrapping the bundles from $tag (version $version)"
build:
name: AppImage ${{ matrix.appimage_arch }}
permissions:
contents: write
# The file's architecture, -musl included: two jobs of one name write their
# logs to one directory of the run's log archive, and its files then ask
# to be overwritten when it is unpacked (v12.54: AppImage x86_64 and
# aarch64, twice each).
name: AppImage ${{ matrix.name_arch || matrix.appimage_arch }}
needs: release
runs-on: ${{ matrix.runs_on }}
# A 32-bit job that cannot run on the runner it was given must not fail the
@@ -161,6 +172,20 @@ jobs:
bundle_arch: armhf
runs_on: ubuntu-24.04-arm
tolerate_failure: true
# Alpine and the other musl systems: the musl bundles musl.yml builds
# (releases/musl/build-bundle.sh). The same AppImage runtime - it is
# static, so it runs on musl - around a musl Node.js, named apart from
# the glibc AppImage and started inside Alpine to be checked.
- appimage_arch: x86_64
name_arch: x86_64-musl
bundle_arch: linux-x64-musl
runs_on: ubuntu-24.04
musl: true
- appimage_arch: aarch64
name_arch: aarch64-musl
bundle_arch: linux-arm64-musl
runs_on: ubuntu-24.04-arm
musl: true
# WHICH entries this run wants, decided here because it cannot be decided
# where it belongs. A job-level `if:` cannot see `matrix` - GitHub exposes
# that context to runs-on, env, name, steps and a few others, but not to a
@@ -170,10 +195,14 @@ jobs:
# A job-level env CAN see matrix, so the answer is computed once here and
# every step below asks for it.
env:
BUILD_THIS: ${{ inputs.only == '' || contains(fromJSON(inputs.only), matrix.appimage_arch) }}
BUILD_THIS: ${{ inputs.only == '' || contains(fromJSON(inputs.only), matrix.name_arch || matrix.appimage_arch) }}
# The file's name: the CPU, plus -musl for a musl AppImage.
NAME_ARCH: ${{ matrix.name_arch || matrix.appimage_arch }}
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
if: ${{ env.BUILD_THIS == 'true' }}
# An AppImage only WRAPS an existing release bundle
@@ -188,14 +217,21 @@ jobs:
if: ${{ env.BUILD_THIS == 'true' }}
env:
GH_TOKEN: ${{ github.token }}
NEEDS_RELEASE_VERSION: ${{ needs.release.outputs.version }}
MATRIX_BUNDLE_ARCH: ${{ matrix.bundle_arch }}
GITHUB_CTX_REPOSITORY: ${{ github.repository }}
NEEDS_RELEASE_TAG: ${{ needs.release.outputs.tag }}
run: |
set -euo pipefail
zip="wekan-${{ needs.release.outputs.version }}-${{ matrix.bundle_arch }}.zip"
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."
zip="wekan-${NEEDS_RELEASE_VERSION}-${MATRIX_BUNDLE_ARCH}.zip"
# Read the complete response before searching: grep -q can close a
# pipe early and turn a present asset into a SIGPIPE failure.
assets=$(gh release view --repo "${GITHUB_CTX_REPOSITORY}" "${NEEDS_RELEASE_TAG}" \
--json assets --jq '.assets[].name')
if grep -Fxq "$zip" <<< "$assets"; then
echo "OK: $zip is on ${NEEDS_RELEASE_TAG}; building its AppImage."
else
echo "::notice::$zip is not on ${{ needs.release.outputs.tag }} yet - skipping this AppImage (it can only wrap a bundle that exists)."
echo "::notice::$zip is not on ${NEEDS_RELEASE_TAG} yet - skipping this AppImage (it can only wrap a bundle that exists)."
echo "BUILD_THIS=false" >> "$GITHUB_ENV"
fi
@@ -203,15 +239,37 @@ jobs:
if: ${{ env.BUILD_THIS == 'true' }}
env:
GH_TOKEN: ${{ github.token }}
NEEDS_RELEASE_VERSION: ${{ needs.release.outputs.version }}
MATRIX_BUNDLE_ARCH: ${{ matrix.bundle_arch }}
NEEDS_RELEASE_TAG: ${{ needs.release.outputs.tag }}
GITHUB_CTX_REPOSITORY: ${{ github.repository }}
MATRIX_MUSL: ${{ matrix.musl }}
run: |
set -euo pipefail
zip="wekan-${{ needs.release.outputs.version }}-${{ matrix.bundle_arch }}.zip"
gh release download '${{ needs.release.outputs.tag }}' \
--repo "${{ github.repository }}" --pattern "$zip" --clobber
zip="wekan-${NEEDS_RELEASE_VERSION}-${MATRIX_BUNDLE_ARCH}.zip"
gh release download "${NEEDS_RELEASE_TAG}" \
--repo "${GITHUB_CTX_REPOSITORY}" --pattern "$zip" --clobber
unzip -q "$zip"
# A zip that exists is not a bundle that runs; these are the two files
# the AppImage is built around.
for f in bundle/main.js bundle/node bundle/node-runtime/libatomic.so.1 bundle/ferretdb bundle/start-wekan.sh; do
# A musl bundle's node-runtime holds GCC's C++ runtime, which its
# Node.js needs and a bare Alpine lacks; a glibc one holds libatomic.
required="bundle/main.js bundle/node bundle/ferretdb bundle/start-wekan.sh"
if [ "${MATRIX_MUSL}" = true ]; then
required="$required bundle/node-runtime/libstdc++.so.6 bundle/node-runtime/libgcc_s.so.1"
else
required="$required bundle/node-runtime/libatomic.so.1"
fi
# And every other library a musl Node.js needs: x86-64's also needs
# libatomic, and the v12.53 x86_64 musl AppImage did not start in a
# bare Alpine without it.
if [ "${MATRIX_MUSL}" = true ]; then
for lib in $(readelf -dW bundle/node | sed -n 's/.*(NEEDED).*\[\(.*\)\]/\1/p'); do
case "$lib" in libc.musl-*|ld-musl-*|libc.so) continue ;; esac
required="$required bundle/node-runtime/$lib"
done
fi
for f in $required; do
if [ ! -e "$f" ]; then
echo "::error::$zip has no $f - it is not a runnable WeKan bundle."
exit 1
@@ -223,10 +281,13 @@ jobs:
- name: Build the AppDir
if: ${{ env.BUILD_THIS == 'true' }}
env:
NEEDS_RELEASE_VERSION: ${{ needs.release.outputs.version }}
MATRIX_APPIMAGE_ARCH: ${{ matrix.appimage_arch }}
run: |
set -euo pipefail
version='${{ needs.release.outputs.version }}'
arch='${{ matrix.appimage_arch }}'
version="${NEEDS_RELEASE_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
@@ -344,14 +405,21 @@ jobs:
APPRUN
sed -i "s/@APPIMAGE_ARCH@/$arch/" AppDir/AppRun
chmod +x AppDir/AppRun
# The AppImage runs as whoever starts it, not as this runner, so
# everything in it must be readable by all. v12.69's FerretDB and
# mongo-tools were 0711 and appimagetool refused to pack them.
chmod -R a+rX AppDir
- name: Pack the AppImage
if: ${{ env.BUILD_THIS == 'true' }}
env:
NEEDS_RELEASE_VERSION: ${{ needs.release.outputs.version }}
MATRIX_APPIMAGE_ARCH: ${{ matrix.appimage_arch }}
run: |
set -euo pipefail
version='${{ needs.release.outputs.version }}'
arch='${{ matrix.appimage_arch }}'
name="WeKan-${version}-${arch}"
version="${NEEDS_RELEASE_VERSION}"
arch="${MATRIX_APPIMAGE_ARCH}"
name="WeKan-${version}-${NAME_ARCH}"
curl -fSL --retry 5 --retry-delay 10 -o appimagetool \
"https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-${arch}.AppImage"
@@ -375,16 +443,41 @@ jobs:
ls -lh dist
- name: Check that it starts
id: smoke
if: ${{ env.BUILD_THIS == 'true' }}
env:
NEEDS_RELEASE_VERSION: ${{ needs.release.outputs.version }}
MATRIX_MUSL: ${{ matrix.musl }}
MATRIX_APPIMAGE_ARCH: ${{ matrix.appimage_arch }}
run: |
set -euo pipefail
version='${{ needs.release.outputs.version }}'
app="dist/WeKan-${version}-${{ matrix.appimage_arch }}.AppImage"
version="${NEEDS_RELEASE_VERSION}"
app="dist/WeKan-${version}-${NAME_ARCH}.AppImage"
# An AppImage that packs is not an AppImage that runs. Start it, wait
# for WeKan to answer on its port, then stop it - so a broken bundle
# is caught here rather than by whoever downloads it.
export WRITABLE_PATH="$PWD/smoke-data" PORT=8080
# A musl AppImage runs its musl Node.js, which an Ubuntu runner cannot,
# so it is started where it is meant to run: inside Alpine.
if [ "${MATRIX_MUSL}" = true ]; then
docker run --rm --network host -v "$PWD:/work" -w /work -e APP="$app" alpine:3.23 sh -ec '
apk add --no-cache curl >/dev/null
export WRITABLE_PATH=/work/smoke-data PORT=8080
APPIMAGE_EXTRACT_AND_RUN=1 "./$APP" > smoke.log 2>&1 &
pid=$!
for _ in $(seq 1 90); do
if curl -fsS -o /dev/null "http://127.0.0.1:8080/sign-in"; then echo "WeKan answered on port 8080 in Alpine."; kill $pid; exit 0; fi
kill -0 $pid 2>/dev/null || break
sleep 2
done
kill $pid 2>/dev/null || true
echo "::error::the musl AppImage did not answer on port 8080 in Alpine; its log follows"
tail -n 100 smoke.log
exit 1'
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
@@ -393,7 +486,7 @@ jobs:
# AppDir is the exact tree just packed, so this also avoids extracting
# the large image merely to make the capability decision.
if ! LD_LIBRARY_PATH="$PWD/AppDir/usr/lib/wekan/bundle/node-runtime${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" 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."
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
@@ -414,74 +507,99 @@ jobs:
fi
echo "WeKan answered on port 8080."
- uses: actions/upload-artifact@v7
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
if: ${{ env.BUILD_THIS == 'true' }}
with:
name: appimage-${{ matrix.appimage_arch }}
name: appimage-${{ matrix.name_arch || matrix.appimage_arch }}
path: dist/
if-no-files-found: error
# Attached HERE, by the job that packed and smoke-tested it, the moment it
# passed - not by a later job that waited for every architecture and then
# uploaded them all. The x86_64 AppImage is on the release while armhf is
# still building, and one slow or failed architecture never holds the
# others back.
#
# always() + the smoke test's outcome: a cancel that arrives after the
# AppImage was built and checked still attaches it.
- name: Attach this AppImage to the release
if: ${{ always() && env.BUILD_THIS == 'true' && steps.smoke.outcome == 'success' && inputs.publish != false }}
env:
GH_TOKEN: ${{ github.token }}
GITHUB_CTX_REPOSITORY: ${{ github.repository }}
NEEDS_RELEASE_TAG: ${{ needs.release.outputs.tag }}
run: |
set -euo pipefail
shopt -s nullglob
files=(dist/*.AppImage dist/*.AppImage.md5sum dist/*.AppImage.sha256sum)
if [ "${#files[@]}" -eq 0 ]; then
echo "::error::dist/ holds no AppImage to attach although the build and smoke test passed."
exit 1
fi
# --repo inside the script, so gh never has to infer the repository
# from a git remote. Retried, bounded, and verified by name and size.
bash releases/github-release-upload.sh "${GITHUB_CTX_REPOSITORY}" "${NEEDS_RELEASE_TAG}" "${files[@]}"
# The last job no longer uploads anything: each build job above attached its
# own AppImage as soon as it was built and checked. This one only says, at the
# end, what is on the release and what is not - and it runs even when the run
# was cancelled (always()), so the summary covers whatever did get attached.
publish:
timeout-minutes: 30
name: attach to the release
name: check what was attached
needs: [release, build]
if: ${{ !cancelled() && inputs.publish != false }}
if: ${{ always() && needs.release.result == 'success' && inputs.publish != false }}
runs-on: ubuntu-24.04
steps:
- uses: actions/download-artifact@v8
with:
pattern: appimage-*
path: assets
merge-multiple: true
- name: Say what was built, and what was not
env:
GH_TOKEN: ${{ github.token }}
NEEDS_RELEASE_VERSION: ${{ needs.release.outputs.version }}
GITHUB_CTX_REPOSITORY: ${{ github.repository }}
NEEDS_RELEASE_TAG: ${{ needs.release.outputs.tag }}
NEEDS_BUILD_RESULT: ${{ needs.build.result }}
INPUTS_ONLY: ${{ inputs.only }}
run: |
set -euo pipefail
ls -lh assets 2>/dev/null || true
if ls assets/*.AppImage >/dev/null 2>&1; then
version="${NEEDS_RELEASE_VERSION}"
# The architectures THIS run builds: the musl run asked after the
# glibc AppImages, which another run attaches, and warned of all
# four as missing (v12.54).
wants="x86_64 aarch64 i686 armhf x86_64-musl aarch64-musl"
if [ -n "${INPUTS_ONLY}" ]; then
wants="$(python3 -c 'import json, sys; print(" ".join(json.loads(sys.argv[1])))' "${INPUTS_ONLY}")"
fi
gh release view --repo "${GITHUB_CTX_REPOSITORY}" "${NEEDS_RELEASE_TAG}" \
--json assets --jq '.assets[].name' > published.txt
if grep -qE "^WeKan-${version}-.*\.AppImage$" published.txt; then
# Every architecture the matrix builds. A missing one WARNS rather
# than fails: the 32-bit pair is allowed not to build, and an
# architecture whose base bundle was never published has nothing to
# wrap. What must not happen silently is one going missing unnoticed.
for want in x86_64 aarch64 i686 armhf; do
ls assets/*"-$want.AppImage" >/dev/null 2>&1 || \
echo "::warning::no AppImage for $want - see that build job's log"
# than fails: the 32-bit pair is allowed not to build, and an
# architecture whose base bundle was never published has nothing to
# wrap. What must not happen silently is one going missing unnoticed.
for want in $wants; do
for f in "WeKan-${version}-${want}.AppImage" "WeKan-${version}-${want}.AppImage.sha256sum"; do
grep -qxF "$f" published.txt || \
echo "::warning::$f is not on ${NEEDS_RELEASE_TAG} - see the AppImage $want build job's log"
done
done
echo "AppImages on $(gh release view --repo "${GITHUB_CTX_REPOSITORY}" "${NEEDS_RELEASE_TAG}" --json url --jq .url):"
grep -E "^WeKan-${version}-.*\.AppImage" published.txt || true
else
# No AppImage was produced. That is only a FAILURE if there was a base
# No AppImage was attached. 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 --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
grep -qx "wekan-${version}-${arch}.zip" published.txt && base_present=true
done
if [ "$base_present" = false ]; then
echo "::notice::No base bundle (wekan-${{ needs.release.outputs.version }}-{amd64,arm64}.zip) is on ${{ needs.release.outputs.tag }} yet, so there is nothing to wrap - skipping. release-all.yml builds the AppImages once the base bundles are published."
echo "::notice::No base bundle (wekan-${version}-{amd64,arm64}.zip) is on ${NEEDS_RELEASE_TAG} yet, so there is nothing to wrap - skipping. release-all.yml builds the AppImages once the base bundles are published."
exit 0
fi
echo "::error::A base bundle is published but no architecture produced an AppImage - every build job failed. Their logs say why; nothing is uploaded."
if [ "${NEEDS_BUILD_RESULT}" = "cancelled" ]; then
echo "::warning::The run was cancelled before any AppImage finished building; nothing was attached."
exit 0
fi
echo "::error::A base bundle is published but no architecture attached an AppImage - every build job failed. Their logs say why."
exit 1
fi
- name: Upload them
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
if ! ls assets/*.AppImage >/dev/null 2>&1; then
echo "Nothing to upload (no base bundle was published to wrap)."
exit 0
fi
# --repo, because this job does NOT check the repository out - it only
# downloads artifacts - so `gh` has no git remote to infer the repo
# from and fails with "fatal: not a git repository". Every other gh
# 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 --repo "${GITHUB_REPOSITORY:-wekan/wekan}" '${{ needs.release.outputs.tag }}' assets/* \
--repo "${{ github.repository }}" --clobber
echo "Uploaded to $(gh release view --repo "${GITHUB_REPOSITORY:-wekan/wekan}" '${{ needs.release.outputs.tag }}' \
--repo "${{ github.repository }}" --json url --jq .url)"
+83 -44
View File
@@ -83,8 +83,9 @@ on:
default: ''
type: string
# Read-only by default; the job that writes is granted it below.
permissions:
contents: write
contents: read
concurrency:
group: wekan-flatpak-${{ inputs.tag || github.ref }}
@@ -99,17 +100,21 @@ jobs:
tag: ${{ steps.find.outputs.tag }}
version: ${{ steps.find.outputs.version }}
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- id: find
env:
GH_TOKEN: ${{ github.token }}
INPUT_TAG: ${{ inputs.tag }}
GITHUB_CTX_REPOSITORY: ${{ github.repository }}
run: |
set -euo pipefail
tag='${{ inputs.tag }}'
tag="${INPUT_TAG}"
# --repo on every gh call, whether or not the job happens to have
# checked out: one that relies on an inferred remote breaks the day
# somebody removes the checkout.
[ -n "$tag" ] || tag="$(gh release list --repo "${{ github.repository }}" \
[ -n "$tag" ] || tag="$(gh release list --repo "${GITHUB_CTX_REPOSITORY}" \
--limit 1 --json tagName --jq '.[0].tagName')"
if [ -z "$tag" ]; then
echo "::error::No release to wrap, and no tag was given."
@@ -121,6 +126,8 @@ jobs:
echo "Wrapping the bundles from $tag (version $version)"
build:
permissions:
contents: write
name: flatpak ${{ matrix.flatpak_arch }}
needs: release
runs-on: ${{ matrix.runs_on }}
@@ -147,7 +154,9 @@ jobs:
BUILD_THIS: ${{ inputs.only == '' || contains(fromJSON(inputs.only), matrix.flatpak_arch) }}
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
if: ${{ env.BUILD_THIS == 'true' }}
- name: Install flatpak
@@ -164,11 +173,15 @@ jobs:
if: ${{ env.BUILD_THIS == 'true' }}
env:
GH_TOKEN: ${{ github.token }}
NEEDS_RELEASE_VERSION: ${{ needs.release.outputs.version }}
MATRIX_BUNDLE_ARCH: ${{ matrix.bundle_arch }}
NEEDS_RELEASE_TAG: ${{ needs.release.outputs.tag }}
GITHUB_CTX_REPOSITORY: ${{ github.repository }}
run: |
set -euo pipefail
zip="wekan-${{ needs.release.outputs.version }}-${{ matrix.bundle_arch }}.zip"
gh release download '${{ needs.release.outputs.tag }}' \
--repo "${{ github.repository }}" --pattern "$zip" --clobber
zip="wekan-${NEEDS_RELEASE_VERSION}-${MATRIX_BUNDLE_ARCH}.zip"
gh release download "${NEEDS_RELEASE_TAG}" \
--repo "${GITHUB_CTX_REPOSITORY}" --pattern "$zip" --clobber
unzip -q "$zip"
for f in bundle/main.js bundle/node bundle/node-runtime/libatomic.so.1 bundle/ferretdb bundle/start-wekan.sh; do
if [ ! -e "$f" ]; then
@@ -186,10 +199,13 @@ jobs:
# export it into a repository.
- name: Build the Flatpak
if: ${{ env.BUILD_THIS == 'true' }}
env:
NEEDS_RELEASE_VERSION: ${{ needs.release.outputs.version }}
MATRIX_FLATPAK_ARCH: ${{ matrix.flatpak_arch }}
run: |
set -euo pipefail
version='${{ needs.release.outputs.version }}'
arch='${{ matrix.flatpak_arch }}'
version="${NEEDS_RELEASE_VERSION}"
arch="${MATRIX_FLATPAK_ARCH}"
rm -rf build-dir repo
flatpak build-init --arch="$arch" build-dir \
@@ -271,14 +287,18 @@ jobs:
flatpak build-export --arch="$arch" repo build-dir stable
- name: Make the single-file bundle
id: bundle
if: ${{ env.BUILD_THIS == 'true' }}
env:
NEEDS_RELEASE_VERSION: ${{ needs.release.outputs.version }}
MATRIX_FLATPAK_ARCH: ${{ matrix.flatpak_arch }}
run: |
set -euo pipefail
version='${{ needs.release.outputs.version }}'
name="WeKan-${version}-${{ matrix.flatpak_arch }}"
version="${NEEDS_RELEASE_VERSION}"
name="WeKan-${version}-${MATRIX_FLATPAK_ARCH}"
mkdir -p dist
flatpak build-bundle repo "dist/${name}.flatpak" \
--arch='${{ matrix.flatpak_arch }}' io.github.wekan.WeKan stable
--arch="${MATRIX_FLATPAK_ARCH}" io.github.wekan.WeKan stable
# Checksum keeps the .flatpak extension in its name, matching the zip
# convention and what releases/expected-assets.sh looks for; dropping it
# meant "what is missing" never found the checksum and rebuilt the
@@ -288,45 +308,86 @@ jobs:
sha256sum "${name}.flatpak" > "${name}.flatpak.sha256sum" )
ls -lh dist
- uses: actions/upload-artifact@v7
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
if: ${{ env.BUILD_THIS == 'true' }}
with:
name: flatpak-${{ matrix.flatpak_arch }}
path: dist/
if-no-files-found: error
- uses: actions/upload-artifact@v7
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
if: ${{ env.BUILD_THIS == 'true' }}
with:
name: flatpak-repo-${{ matrix.flatpak_arch }}
path: repo/
if-no-files-found: warn
# Attached HERE, by the job that built it, the moment it exists - not by
# the publish job below, which used to wait for both architectures and
# then upload them together. always() + the bundle step's outcome: a
# cancel that arrives after the bundle was made still attaches it.
#
# The bundles BY NAME, not dist/*: only the .flatpak and its checksums are
# release files. (The ostree repository the flatpak was exported through
# is build scaffolding; it lives in repo/, goes up as an artifact for the
# website repository, and must never be attached - TSC hit exactly that.)
- name: Attach this Flatpak to the release
if: ${{ always() && env.BUILD_THIS == 'true' && steps.bundle.outcome == 'success' && inputs.publish != false }}
env:
GH_TOKEN: ${{ github.token }}
GITHUB_CTX_REPOSITORY: ${{ github.repository }}
NEEDS_RELEASE_TAG: ${{ needs.release.outputs.tag }}
run: |
set -euo pipefail
shopt -s nullglob
files=(dist/*.flatpak dist/*.flatpak.md5sum dist/*.flatpak.sha256sum)
if [ "${#files[@]}" -eq 0 ]; then
echo "::error::dist/ holds no .flatpak to attach although the bundle step passed."
exit 1
fi
# Retried, bounded, and verified by name and size from the release side.
bash releases/github-release-upload.sh "${GITHUB_CTX_REPOSITORY}" "${NEEDS_RELEASE_TAG}" "${files[@]}"
# This job no longer attaches the bundles: each build job above attached its
# own as soon as it was built. What is left for the end is what needs BOTH
# architectures at once - the multi-architecture repository - and the summary
# of what was built. always(), so a cancelled run still gets them for
# whatever did finish.
publish:
timeout-minutes: 60
name: attach to the release, and build the repository
name: say what was built, and build the repository
needs: [release, build]
if: ${{ !cancelled() }}
if: ${{ always() && needs.release.result == 'success' }}
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/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/download-artifact@v8
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
# A cancelled run may have uploaded none; the next step says so.
continue-on-error: true
with:
pattern: flatpak-[0-9a-z]*
path: assets
merge-multiple: true
- name: Say what was built, and what was not
env:
NEEDS_BUILD_RESULT: ${{ needs.build.result }}
run: |
set -euo pipefail
ls -lh assets 2>/dev/null || true
if ! ls assets/*.flatpak >/dev/null 2>&1; then
echo "::error::No architecture produced a Flatpak bundle; nothing is uploaded."
if [ "${NEEDS_BUILD_RESULT}" = "cancelled" ]; then
echo "::warning::The run was cancelled before any Flatpak bundle finished building."
exit 0
fi
echo "::error::No architecture produced a Flatpak bundle; nothing was attached."
exit 1
fi
for want in x86_64 aarch64; do
@@ -334,30 +395,8 @@ jobs:
echo "::warning::no Flatpak for $want - see that build job's log"
done
- name: Upload the bundles
if: ${{ inputs.publish != false }}
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
# --repo, because this job does NOT check the repository out - it
# only downloads artifacts - so `gh` has no git remote to infer the
# repo from and fails with "fatal: not a git repository". The same
# line was wrong in AppImage.yml for the same reason.
#
# And the bundles BY NAME, not assets/*: the artifacts also carry the
# ostree repository the flatpak was exported through - config,
# 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 --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 --repo "${GITHUB_REPOSITORY:-wekan/wekan}" '${{ needs.release.outputs.tag }}' \
--repo "${{ github.repository }}" --json url --jq .url)"
# ---- the repository -------------------------------------------------
- uses: actions/download-artifact@v8
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
if: ${{ inputs.publish_repo != false }}
with:
pattern: flatpak-repo-*
@@ -429,7 +468,7 @@ jobs:
TXT
du -sh flatpak-repo
- uses: actions/upload-artifact@v7
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
if: ${{ inputs.publish_repo != false }}
with:
name: flatpak-repository
+204
View File
@@ -0,0 +1,204 @@
name: Android bundles
# WeKan on Android, for Termux: the published Linux bundle of each CPU,
# rebuilt inside Termux's own image (real Bionic and Android clang, the ARM
# ones on ARM runners) by releases/android/build-bundle.sh with wekan/
# node-patches' node-android-<cpu> of the same version, then started and
# required to answer HTTP 200. The same pattern as the musl and BSD bundles.
# A target whose Linux bundle, or whose Android Node.js, is not published yet
# is SKIPPED, not failed, and builds by itself once it is.
on:
workflow_dispatch:
inputs:
tag:
description: 'Existing WeKan release tag (empty = latest published release)'
type: string
required: false
publish:
description: 'Attach the Android bundles and checksums to that release'
type: boolean
default: true
only:
description: 'JSON target list, e.g. ["android-arm64"] (empty = all)'
type: string
default: ''
workflow_call:
inputs:
tag:
type: string
required: true
publish:
type: boolean
default: true
only:
type: string
default: ''
permissions:
contents: read
concurrency:
group: wekan-android-${{ inputs.tag || github.ref }}
cancel-in-progress: false
jobs:
plan:
runs-on: ubuntu-24.04
timeout-minutes: 10
outputs:
tag: ${{ steps.plan.outputs.tag }}
matrix: ${{ steps.plan.outputs.matrix }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- id: plan
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ inputs.tag }}
ONLY: ${{ inputs.only }}
run: |
set -euo pipefail
tag="${TAG:-$(gh release view --repo "$GITHUB_REPOSITORY" --json tagName -q .tagName)}"
echo "tag=$tag" >> "$GITHUB_OUTPUT"
matrix="$(python3 -c '
import json, os
targets = json.load(open("releases/android/targets.json"))
only = json.loads(os.environ.get("ONLY") or "[]")
if only:
targets = [t for t in targets if t["target"] in only]
print(json.dumps({"include": targets}))')"
echo "matrix=$matrix" >> "$GITHUB_OUTPUT"
echo "Release $tag; targets: $matrix"
build:
needs: plan
name: Android ${{ matrix.target }}
runs-on: ${{ matrix.runner }}
# 32-bit ARM runs on the arm64 runner only when its kernel can; a target
# that cannot run there must not fail the others.
continue-on-error: ${{ matrix.tolerate_failure || false }}
timeout-minutes: 150
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.plan.outputs.matrix) }}
env:
TAG: ${{ needs.plan.outputs.tag }}
TARGET: ${{ matrix.target }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Fetch and verify the Linux bundle and the Android Node.js
id: fetch
env:
GH_TOKEN: ${{ github.token }}
BASE: ${{ matrix.base }}
run: |
set -euo pipefail
version="${TAG#v}"
zip="wekan-${version}-${BASE}.zip"
if ! gh release download "$TAG" --repo "$GITHUB_REPOSITORY" --pattern "$zip" --pattern "$zip.sha256sum" || [ ! -f "$zip" ]; then
echo "::notice::$TARGET: $TAG has no $zip; skipped."
echo "skip=true" >> "$GITHUB_OUTPUT"; exit 0
fi
[ -f "$zip.sha256sum" ] || { echo "::error::$zip has no published .sha256sum; a bundle is never rebuilt from an unverified one."; exit 1; }
sha256sum -c "$zip.sha256sum"
unzip -q "$zip"
rm -f "$zip" "$zip.sha256sum"
node_version="$(grep -aoE 'nodejs\.org/download/release/v[0-9]+\.[0-9]+\.[0-9]+/' bundle/node | head -n1 | sed -E 's#.*/(v[0-9.]+)/#\1#')"
[ -n "$node_version" ] || { echo "::error::Could not read the Node.js version of bundle/node."; exit 1; }
rc=0; out="$(bash releases/resolve-node-source.sh "$TARGET" "$node_version")" || rc=$?
if [ "$rc" = 3 ] || ! printf '%s\n' "$out" | grep -q '^node_found=true'; then
echo "::notice::$TARGET: wekan/node-patches has no node-$TARGET for $node_version yet; skipped."
echo "skip=true" >> "$GITHUB_OUTPUT"; exit 0
fi
eval "$(printf '%s\n' "$out" | grep -E '^node_(url|sha256|kind|member)=')"
mkdir -p android-tools
bash releases/fetch.sh -o android-node.download "$node_url"
[ -n "$node_sha256" ] || { echo "::error::$node_url has no published SHA256."; exit 1; }
echo "$node_sha256 android-node.download" | sha256sum -c -
case "$node_kind" in
binary) mv android-node.download android-tools/node ;;
tar.xz) tar -xJf android-node.download -C android-tools && mv "android-tools/$node_member" android-tools/node && rm -f android-node.download ;;
*) echo "::error::unexpected Android Node.js kind: $node_kind"; exit 1 ;;
esac
chmod +x android-tools/node
file android-tools/node bundle/ferretdb
echo "version=$version" >> "$GITHUB_OUTPUT"
- name: Rebuild and start it in Termux
if: steps.fetch.outputs.skip != 'true'
env:
TERMUX_ARCH: ${{ matrix.termux_arch }}
ARCH: ${{ matrix.arch }}
run: |
# Termux's entrypoint clears the Docker environment when it switches
# to its unprivileged user, so the values go after the switch (as
# bun.yml does), and that user needs a writable bind mount. Writable
# is not enough: npm chmods every package's bin when it installs, and
# only a file's OWNER may chmod it (EPERM on uuid/dist/bin/uuid,
# v12.51), so the checkout is handed to that user. Package takes the
# bundle back.
chmod -R a+rwX .
owner="$(docker run --rm "termux/termux-docker:$TERMUX_ARCH" sh -c 'echo "$(id -u):$(id -g)"' | tr -d '\r' | tail -n1)"
case "$owner" in
*[0-9]:[0-9]*) sudo chown -R "$owner" . ;;
*) echo "::error::Could not read the uid of Termux's user (got: $owner)."; exit 1 ;;
esac
docker run --rm --security-opt seccomp=unconfined -v "$PWD:/work" -w /work \
"termux/termux-docker:$TERMUX_ARCH" env "ARCH=$ARCH" DO_NOT_TRACK=1 bash -ec '
apt-get update
apt-get install -y nodejs-lts python make clang pkg-config libvips curl procps file
bash releases/android/build-bundle.sh "$ARCH"'
- name: Package
if: steps.fetch.outputs.skip != 'true'
env:
VERSION: ${{ steps.fetch.outputs.version }}
run: |
set -euo pipefail
sudo chown -R "$(id -u):$(id -g)" .
out="wekan-${VERSION}-${TARGET}.zip"
zip -qry "$out" bundle
sha256sum "$out" > "$out.sha256sum"
ls -la "$out" "$out.sha256sum"
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
if: steps.fetch.outputs.skip != 'true'
with:
name: android-${{ matrix.target }}
path: wekan-*-${{ matrix.target }}.zip*
retention-days: 3
compression-level: 0
publish:
needs: [plan, build]
if: ${{ !cancelled() && inputs.publish }}
runs-on: ubuntu-24.04
timeout-minutes: 60
permissions:
contents: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
pattern: android-*
path: artifacts
- name: Attach every Android bundle that was built and started
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ needs.plan.outputs.tag }}
run: |
set -euo pipefail
shopt -s nullglob
published=0
for f in artifacts/android-*/wekan-*.zip; do
(cd "$(dirname "$f")" && sha256sum -c "$(basename "$f").sha256sum")
bash releases/github-release-upload.sh "$GITHUB_REPOSITORY" "$TAG" "$f" "$f.sha256sum"
published=$((published + 1))
done
echo "Published $published Android bundles to $TAG."
+223
View File
@@ -0,0 +1,223 @@
name: BSD bundles
# WeKan Node.js bundles for the BSDs (docs/Platforms/FOSS/OS/BSD.md): the
# release's amd64 bundle, with this BSD's Node.js (wekan/node-patches) and
# FerretDB (wekan/FerretDB), its native modules compiled inside a VM of that
# BSD, started there with its own start-wekan.sh, and attached to the release.
#
# A target whose Node.js is not published anywhere yet is SKIPPED, not failed:
# the same rule as every other bundle (releases/resolve-node-source.sh). So
# OpenBSD and NetBSD start building by themselves the day node-patches
# publishes their Node.js.
on:
workflow_dispatch:
inputs:
tag:
description: 'Existing WeKan release tag (empty = latest published release)'
type: string
required: false
publish:
description: 'Attach tested BSD bundles and checksums to that release'
type: boolean
default: true
only:
description: 'JSON target list, e.g. ["freebsd-x64"] (empty = all)'
type: string
default: ''
workflow_call:
inputs:
tag:
type: string
required: true
publish:
type: boolean
default: true
only:
type: string
default: ''
permissions:
contents: read
concurrency:
group: wekan-bsd-${{ inputs.tag || github.ref }}
cancel-in-progress: false
env:
NODE_VERSION: '26'
jobs:
plan:
runs-on: ubuntu-24.04
timeout-minutes: 10
outputs:
tag: ${{ steps.plan.outputs.tag }}
matrix: ${{ steps.plan.outputs.matrix }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- id: plan
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ inputs.tag }}
ONLY: ${{ inputs.only }}
run: |
set -euo pipefail
tag="${TAG:-$(gh release view --repo "$GITHUB_REPOSITORY" --json tagName -q .tagName)}"
echo "tag=$tag" >> "$GITHUB_OUTPUT"
matrix="$(python3 -c '
import json, os
targets = json.load(open("releases/bsd/targets.json"))
only = json.loads(os.environ.get("ONLY") or "[]")
if only:
targets = [t for t in targets if t["target"] in only]
print(json.dumps({"include": targets}))')"
echo "matrix=$matrix" >> "$GITHUB_OUTPUT"
echo "Release $tag; targets: $matrix"
build:
needs: plan
name: BSD ${{ matrix.target }}
runs-on: ubuntu-24.04
timeout-minutes: 180
permissions:
contents: write
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.plan.outputs.matrix) }}
env:
TAG: ${{ needs.plan.outputs.tag }}
TARGET: ${{ matrix.target }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
# Node.js first: no Node.js for this BSD anywhere means no bundle this
# release, which is a skip, not a failure.
- name: Where this BSD's Node.js comes from
id: node
run: |
set -euo pipefail
out="$(bash releases/resolve-node-source.sh "$TARGET" "$NODE_VERSION")" || rc=$?
if [ "${rc:-0}" = 3 ] || ! printf '%s\n' "$out" | grep -q '^node_found=true'; then
echo "::notice::$TARGET is not built this release: wekan/node-patches has no node-$TARGET yet."
echo "skip=true" >> "$GITHUB_OUTPUT"
exit 0
fi
printf '%s\n' "$out" | grep -E '^node_(full|from|url|kind|sha256)=' >> "$GITHUB_OUTPUT"
printf '%s\n' "$out"
- name: Fetch and verify the release bundle, Node.js, npm and FerretDB
if: steps.node.outputs.skip != 'true'
env:
GH_TOKEN: ${{ github.token }}
NODE_URL: ${{ steps.node.outputs.node_url }}
NODE_SHA256: ${{ steps.node.outputs.node_sha256 }}
NODE_FULL: ${{ steps.node.outputs.node_full }}
FERRETDB_ARCH: ${{ matrix.ferretdb_arch }}
run: |
set -euo pipefail
version="${TAG#v}"
gh release download "$TAG" --repo "$GITHUB_REPOSITORY" --pattern "wekan-${version}-amd64.zip" --pattern "wekan-${version}-amd64.zip.sha256sum"
sha256sum -c "wekan-${version}-amd64.zip.sha256sum"
unzip -q "wekan-${version}-amd64.zip"
mkdir -p bsd-tools/bin bsd-tools/npm
bash releases/fetch.sh -o bsd-tools/bin/node "$NODE_URL"
[ -z "$NODE_SHA256" ] || echo "$NODE_SHA256 bsd-tools/bin/node" | sha256sum -c -
# npm is JavaScript: the one in nodejs.org's Linux tarball of the same
# version runs on any Node.js 26.
bash releases/fetch.sh -o npm.tar.xz "https://nodejs.org/dist/${NODE_FULL}/node-${NODE_FULL}-linux-x64.tar.xz"
tar -xJf npm.tar.xz --strip-components=4 -C bsd-tools/npm "node-${NODE_FULL}-linux-x64/lib/node_modules/npm"
rm -f npm.tar.xz
# The prebuilt SQLite-only ferretdb-sqlite-<bsd>-<arch> from the newest
# wekan/FerretDB release that has it (the full ferretdb-<bsd>-<arch> of
# the newest release until one does), verified against its .sha256sum;
# releases/ferretdb-asset.sh says in the log which one it took.
ferret_meta="$(bash releases/ferretdb-asset.sh "$FERRETDB_ARCH" -o bundle/ferretdb)" \
|| { echo "::error::FerretDB for ${FERRETDB_ARCH} could not be put in the bundle; the lines above say why."; exit 1; }
eval "$ferret_meta"
# The inherited amd64 Linux MongoDB Database Tools are the wrong OS
# here: replaced by this BSD's, or removed when there are none.
rc=0; bash releases/fetch-mongo-tools.sh bundle "$FERRETDB_ARCH" || rc=$?
[ "$rc" != 2 ] || { echo "::error::A MongoDB Database Tools download did not match its checksum."; exit 1; }
[ "$rc" = 0 ] || echo "::warning::No MongoDB Database Tools for ${FERRETDB_ARCH}; the bundle ships without them."
file bsd-tools/bin/node bundle/ferretdb
- name: Build and start in FreeBSD
if: steps.node.outputs.skip != 'true' && matrix.os == 'freebsd'
uses: vmactions/freebsd-vm@c46abacb49f09938ca4e1702d15d836285d694cc
with:
release: ${{ matrix.release }}
arch: ${{ matrix.vm_arch }}
sync: rsync
copyback: true
prepare: pkg install -y bash python3 gmake pkgconf curl
run: bash releases/bsd/build-bundle.sh freebsd ${{ matrix.arch }}
- name: Build and start in OpenBSD
if: steps.node.outputs.skip != 'true' && matrix.os == 'openbsd'
uses: vmactions/openbsd-vm@f1ca8bc8ecc5b6ef7218e5b914836e067e1546d3 # v1.4.9
with:
release: ${{ matrix.release }}
arch: ${{ matrix.vm_arch }}
sync: rsync
copyback: true
prepare: pkg_add bash python%3 gmake curl
# V8 writes and runs machine code: OpenBSD allows that only from a
# filesystem mounted wxallowed (/usr/local is, by default). The
# bundle is started here, from the workspace, so that is mounted so.
run: |
mount -u -o wxallowed "$(df -P . | awk 'NR==2 {print $6}')" || true
bash releases/bsd/build-bundle.sh openbsd ${{ matrix.arch }}
- name: Build and start in NetBSD
if: steps.node.outputs.skip != 'true' && matrix.os == 'netbsd'
uses: vmactions/netbsd-vm@c8a0d7ddb84619a7a8cc4e652efa7950b3fd9f92 # v1.5.2
with:
release: ${{ matrix.release }}
sync: rsync
copyback: true
prepare: |
/usr/sbin/pkg_add pkgin
pkgin -y install bash python313 gmake curl gcc14
# Node 26's headers need C++20, which NetBSD 10's own GCC lacks: the
# native modules are compiled with pkgsrc's GCC 14, as Node.js itself
# is (wekan/node-patches dist/netbsd).
run: |
CC=/usr/pkg/gcc14/bin/gcc CXX=/usr/pkg/gcc14/bin/g++ \
bash releases/bsd/build-bundle.sh netbsd ${{ matrix.arch }}
- name: Zip, checksum and attach
if: steps.node.outputs.skip != 'true'
env:
GH_TOKEN: ${{ github.token }}
PUBLISH: ${{ inputs.publish }}
MATRIX_OS: ${{ matrix.os }}
run: |
set -euo pipefail
version="${TAG#v}"
zip="wekan-${version}-${TARGET}.zip"
python3 releases/check-telemetry.py --bundle bundle
zip -qr "$zip" bundle
unzip -l "$zip" > zip-listing.txt
for want in bundle/main.js bundle/node bundle/ferretdb bundle/start-wekan.sh; do
grep -q " $want$" zip-listing.txt || { echo "::error::$zip has no $want."; exit 1; }
done
if [ "${MATRIX_OS}" = freebsd ]; then
grep -q " bundle/rc.d/wekan$" zip-listing.txt || { echo "::error::$zip has no bundle/rc.d/wekan (#2662)."; exit 1; }
fi
sha256sum "$zip" > "$zip.sha256sum"
cat "$zip.sha256sum"
if [ "$PUBLISH" = true ]; then
bash releases/github-release-upload.sh "$GITHUB_REPOSITORY" "$TAG" "$zip" "$zip.sha256sum"
fi
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
if: steps.node.outputs.skip != 'true'
with:
name: bundle-${{ matrix.target }}
path: wekan-*-${{ matrix.target }}.zip*
retention-days: 3
compression-level: 0
+181
View File
@@ -0,0 +1,181 @@
name: Bun single-file WeKan
# One executable per Bun platform (docs/Platforms/Single-File.md, "Bun single
# files"): that platform's published Bun bundle ZIP, unchanged, inside its own
# Bun, made by releases/single-file/bun/build.mjs. Each one is built ON its
# platform with the bundle's own bun - Alpine for musl, as bun.yml does - so
# nothing is cross-compiled and the runtime is the one the bundle was tested
# with. A target whose Bun bundle the release does not have is SKIPPED, not
# failed. FreeBSD and Android are not built: GitHub has no runner for them,
# and their bundles are used unpacked.
on:
workflow_dispatch:
inputs:
tag:
description: 'Existing WeKan release tag (empty = latest published release)'
type: string
required: false
publish:
description: 'Attach the single files and checksums to that release'
type: boolean
default: true
only:
description: 'JSON target list, e.g. ["linux-x64"] (empty = all)'
type: string
default: ''
workflow_call:
inputs:
tag:
type: string
required: true
publish:
type: boolean
default: true
only:
type: string
default: ''
permissions:
contents: read
concurrency:
group: wekan-bun-single-file-${{ inputs.tag || github.ref }}
cancel-in-progress: false
jobs:
plan:
runs-on: ubuntu-24.04
timeout-minutes: 10
outputs:
tag: ${{ steps.plan.outputs.tag }}
matrix: ${{ steps.plan.outputs.matrix }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- id: plan
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ inputs.tag }}
ONLY: ${{ inputs.only }}
run: |
set -euo pipefail
tag="${TAG:-$(gh release view --repo "$GITHUB_REPOSITORY" --json tagName -q .tagName)}"
echo "tag=$tag" >> "$GITHUB_OUTPUT"
matrix="$(python3 -c '
import json, os
targets = [t for t in json.load(open("releases/bun/targets.json"))
if t["kind"] in ("native", "musl")]
only = json.loads(os.environ.get("ONLY") or "[]")
if only:
targets = [t for t in targets if t["target"] in only]
print(json.dumps({"include": targets}))')"
echo "matrix=$matrix" >> "$GITHUB_OUTPUT"
echo "Release $tag; targets: $matrix"
build:
needs: plan
name: Build ${{ matrix.target }}
runs-on: ${{ matrix.runner }}
timeout-minutes: 45
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.plan.outputs.matrix) }}
env:
TAG: ${{ needs.plan.outputs.tag }}
TARGET: ${{ matrix.target }}
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
ref: ${{ needs.plan.outputs.tag }}
- name: Fetch and verify ${{ matrix.target }}'s published Bun bundle
id: fetch
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
version="${TAG#v}"
zip="wekan-${version}-bun-${TARGET}.zip"
if ! gh release download "$TAG" --repo "$GITHUB_REPOSITORY" --pattern "$zip" --pattern "$zip.sha256sum" || [ ! -f "$zip" ]; then
echo "::notice::$TARGET: $TAG has no $zip; skipped."
echo "skip=true" >> "$GITHUB_OUTPUT"
exit 0
fi
[ -f "$zip.sha256sum" ] || { echo "::error::$zip has no published .sha256sum; a single file is never built from an unverified bundle."; exit 1; }
# A .sha256sum written on Windows before releases/bun/assets.py wrote LF
# (v12.44 windows-arm64) ends in CRLF; the line is the same without it.
tr -d '\r' < "$zip.sha256sum" > "$zip.sha256sum.lf" && mv "$zip.sha256sum.lf" "$zip.sha256sum"
if command -v sha256sum >/dev/null; then sha256sum -c "$zip.sha256sum"; else shasum -a 256 -c "$zip.sha256sum"; fi
# Only the bundle's own Bun, to build with - the ZIP itself is
# embedded as it is.
case "$TARGET" in windows-*) bun=bun.exe ext=.exe ;; *) bun=bun ext= ;; esac
unzip -q -j "$zip" "bundle/$bun" -d builder
chmod +x "builder/$bun"
echo "zip=$zip" >> "$GITHUB_OUTPUT"
echo "bun=builder/$bun" >> "$GITHUB_OUTPUT"
echo "out=wekan-${version}-bun-${TARGET}-single-file${ext}" >> "$GITHUB_OUTPUT"
echo "version=$version" >> "$GITHUB_OUTPUT"
- name: Build and start it (Linux, macOS, Windows)
if: steps.fetch.outputs.skip != 'true' && matrix.kind == 'native'
env:
ZIP: ${{ steps.fetch.outputs.zip }}
BUN: ${{ steps.fetch.outputs.bun }}
OUT: ${{ steps.fetch.outputs.out }}
VERSION: ${{ steps.fetch.outputs.version }}
run: bash releases/single-file/bun/build-and-check.sh
- name: Build and start it (musl, in Alpine)
if: steps.fetch.outputs.skip != 'true' && matrix.kind == 'musl'
env:
ZIP: ${{ steps.fetch.outputs.zip }}
BUN: ${{ steps.fetch.outputs.bun }}
OUT: ${{ steps.fetch.outputs.out }}
VERSION: ${{ steps.fetch.outputs.version }}
run: |
docker run --rm -v "$PWD:/work" -w /work -e ZIP -e BUN -e OUT -e VERSION -e TARGET \
alpine:3.23 sh -ec 'apk add --no-cache bash coreutils libstdc++ libgcc && bash releases/single-file/bun/build-and-check.sh'
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
if: steps.fetch.outputs.skip != 'true'
with:
name: bun-single-file-${{ matrix.target }}
path: wekan-*-bun-${{ matrix.target }}-single-file*
retention-days: 3
compression-level: 0
publish:
needs: [plan, build]
if: ${{ !cancelled() && inputs.publish }}
runs-on: ubuntu-24.04
timeout-minutes: 60
permissions:
contents: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
pattern: bun-single-file-*
path: artifacts
- name: Attach every Bun single file that was built and started
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ needs.plan.outputs.tag }}
run: |
set -euo pipefail
shopt -s nullglob
published=0
for f in artifacts/bun-single-file-*/wekan-*-single-file*; do
case "$f" in *.sha256sum) continue ;; esac
(cd "$(dirname "$f")" && sha256sum -c "$(basename "$f").sha256sum")
bash releases/github-release-upload.sh "$GITHUB_REPOSITORY" "$TAG" "$f" "$f.sha256sum"
published=$((published + 1))
done
echo "Published $published Bun single files to $TAG."
+271
View File
@@ -0,0 +1,271 @@
name: Bun
# Build one ESM app, then rebuild its native addons on each Bun target.
# Experimental until meteor/meteor#14751 ships; the isolated 3.6 port preserves
# WeKan's release/package pins and is never used by the ordinary Node builds.
on:
workflow_dispatch:
inputs:
tag:
description: 'Existing WeKan release tag (empty = latest published release)'
type: string
required: false
publish:
description: 'Attach tested Bun archives and checksums to that release'
type: boolean
default: true
only:
description: 'JSON target list, e.g. ["linux-arm64", "android-arm64"] (empty = all)'
type: string
default: ''
missing_only:
description: 'Skip targets already carrying an archive AND checksum'
type: boolean
default: false
workflow_call:
inputs:
tag:
type: string
required: true
publish:
type: boolean
default: true
only:
type: string
default: ''
missing_only:
type: boolean
default: false
permissions:
contents: read
concurrency:
group: wekan-bun-${{ inputs.tag || github.ref }}
cancel-in-progress: false
jobs:
plan:
runs-on: ubuntu-24.04
timeout-minutes: 15
outputs:
tag: ${{ steps.plan.outputs.tag }}
bun_tag: ${{ steps.plan.outputs.bun_tag }}
bun_version: ${{ steps.plan.outputs.bun_version }}
matrix: ${{ steps.plan.outputs.matrix }}
count: ${{ steps.plan.outputs.count }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- id: plan
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ inputs.tag }}
ONLY: ${{ inputs.only }}
MISSING_ONLY: ${{ inputs.missing_only }}
run: python3 releases/bun/assets.py plan
esm:
needs: plan
if: needs.plan.outputs.count != '0'
runs-on: ubuntu-24.04
timeout-minutes: 180
steps:
# Keep workflow helpers from this workflow revision, and the app from the
# exact release tag. Never put current main's code on an older release.
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
path: build-tools
persist-credentials: false
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: refs/tags/${{ needs.plan.outputs.tag }}
path: app
persist-credentials: false
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: '26'
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version: ${{ needs.plan.outputs.bun_version }}
- name: Prepare isolated Meteor with ESM support
working-directory: app
run: |
mkdir -p releases/bun
cp -R ../build-tools/releases/bun/. releases/bun/
bash releases/bun/prepare-meteor.sh
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: |
~/.meteor
~/.npm
app/.tools/meteor-bun/dev_bundle
app/.tools/meteor-bun/.babel-cache
app/.tools/meteor-bun/packages/*/.npm
key: bun-meteor-${{ runner.os }}-${{ hashFiles('app/.meteor/release', 'app/.meteor/versions', 'build-tools/releases/bun/meteor-3.6-rc.1.patch') }}
- name: Build the release's ESM application once
working-directory: app
env:
DO_NOT_TRACK: '1'
run: |
export TMPDIR="$PWD/.tools/tmp"
# Rspack and its native compiler are devDependencies needed to build.
meteor npm ci --include=dev --include=optional
test -s node_modules/@meteorjs/rspack/rspack.config.js
meteor build ../esm-output --directory --server-only --format=esm
test -s ../esm-output/bundle/index.mjs
git rev-parse HEAD > ../esm-output/bundle/wekan-commit.txt
node --test .tools/meteor-bun/scripts/ci/__tests__/*.node-test.js
tar -cf - -C ../esm-output bundle | gzip -1 > ../esm-bundle.tar.gz
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: bun-esm-source
path: esm-bundle.tar.gz
compression-level: 0
if-no-files-found: error
retention-days: 3
build:
needs: [plan, esm]
permissions:
contents: write
actions: read
name: Bun ${{ matrix.target }}
runs-on: ${{ matrix.runner }}
timeout-minutes: 180
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.plan.outputs.matrix) }}
env:
TARGET: ${{ matrix.target }}
TARGET_PLATFORM: ${{ matrix.platform }}
TARGET_ARCH: ${{ matrix.arch }}
RELEASE_TAG: ${{ needs.plan.outputs.tag }}
BUN_TAG: ${{ needs.plan.outputs.bun_tag }}
DO_NOT_TRACK: '1'
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: bun-esm-source
- uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
with:
python-version: '3.13'
- name: Unpack ESM and fetch verified target Bun
run: |
tar -xzf esm-bundle.tar.gz
export WEKAN_COMMIT="$(cat bundle/wekan-commit.txt)"
python3 releases/bun/assets.py fetch "$BUN_TAG" "$TARGET"
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
if: matrix.kind == 'native'
with:
node-version: '26'
architecture: ${{ matrix.arch }}
- name: Install Chromium smoke test dependencies
if: matrix.kind == 'native' && matrix.platform == 'linux'
run: |
mkdir -p .tools/tmp
export TMPDIR="$PWD/.tools/tmp"
# Where releases/tools-env.sh, which build-target.sh loads, looks for
# them. Installed in ~/.cache instead, the smoke test found no browser
# (linux-arm64, v12.66).
export PLAYWRIGHT_BROWSERS_PATH="$PWD/.tools/ms-playwright"
npm ci --prefix tests/playwright --ignore-scripts
tests/playwright/node_modules/.bin/playwright install --with-deps chromium
- name: Start an isolated MongoDB for Linux HTTP and DDP tests
if: matrix.kind == 'native' && matrix.platform == 'linux'
run: |
# Not --rm: a container that dies must leave its log behind. v12.56
# linux-x64 found nothing on 27019 ("Topology is closed") and there
# was no way to see why. Wait until it answers, or show why not.
# 9.0.4, not mongo:8: on the runner's 6.19+ kernel mongo:8 refused to
# start ("Linux kernel versions 6.19 and newer has a known
# incompatibility", v12.66). 9.0.4 recognises Ubuntu kernels that
# have the fix; see docs/Databases/Supported.md.
docker run -d --name bun-smoke-mongo -p 127.0.0.1:27019:27017 mongo:9.0.4
for i in $(seq 1 60); do
if docker exec bun-smoke-mongo mongosh --quiet --eval 'db.runCommand({ ping: 1 }).ok' 2>/dev/null | grep -qx 1; then
echo "MongoDB answers on 127.0.0.1:27019 after ${i}s."; exit 0
fi
sleep 1
done
echo "::error::the smoke-test MongoDB did not answer within 60s; its state and log follow."
docker ps -a --filter name=bun-smoke-mongo
docker logs bun-smoke-mongo 2>&1 | tail -n 80
exit 1
- name: Native Linux, macOS or Windows build and smoke tests
if: matrix.kind == 'native'
env:
BUN_SMOKE_MONGO_URL: ${{ matrix.platform == 'linux' && 'mongodb://127.0.0.1:27019/bun_smoke' || '' }}
BUN_SMOKE_BROWSER: ${{ matrix.platform == 'linux' && '1' || '0' }}
run: bash releases/bun/build-target.sh
- name: Native musl container build and smoke tests
if: matrix.kind == 'musl'
run: |
docker run --rm -v "$PWD:/work" -w /work \
-e TARGET -e TARGET_PLATFORM -e TARGET_ARCH -e RELEASE_TAG \
alpine:3.23 sh -ec '
apk add --no-cache bash nodejs npm python3 make g++ linux-headers
bash releases/bun/build-target.sh'
- name: Native Android Bionic build and smoke tests
if: matrix.kind == 'android'
env:
TERMUX_ARCH: ${{ matrix.termux_arch }}
run: |
# Termux uses Android libc/linker and native Android clang, not glibc.
# Its entrypoint clears Docker env when switching to the system user.
# Pass build metadata as env arguments AFTER that switch.
# Its unprivileged system user needs a writable bind mount.
chmod -R a+rwX bundle
mkdir -p dist .tools/tmp
chmod a+rwx dist .tools .tools/tmp
docker run --rm --security-opt seccomp=unconfined \
-v "$PWD:/work" -w /work \
-e TARGET -e TARGET_PLATFORM -e TARGET_ARCH -e RELEASE_TAG \
"termux/termux-docker:$TERMUX_ARCH" env \
"TARGET=$TARGET" "TARGET_PLATFORM=$TARGET_PLATFORM" \
"TARGET_ARCH=$TARGET_ARCH" "RELEASE_TAG=$RELEASE_TAG" \
DO_NOT_TRACK=1 bash -ec '
apt-get update
apt-get install -y nodejs-lts python make clang pkg-config libvips
bash releases/bun/build-target.sh'
- name: FreeBSD VM build and smoke tests
if: matrix.kind == 'freebsd'
uses: vmactions/freebsd-vm@c46abacb49f09938ca4e1702d15d836285d694cc # v1.5.9
with:
release: '14.5'
arch: ${{ matrix.vm_arch }}
envs: TARGET TARGET_PLATFORM TARGET_ARCH RELEASE_TAG
sync: rsync
copyback: true
cache-after-prepare: true
prepare: pkg install -y bash node24 npm-node24 python3 gmake pkgconf vips
run: bash releases/bun/build-target.sh
- name: Check finished archive exists
id: ready
run: |
files=(dist/*.zip)
[ "${#files[@]}" -eq 1 ]
test -s "${files[0]}"
test -s "${files[0]}.sha256sum"
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
if: always() && steps.ready.outcome == 'success'
with:
name: wekan-bun-${{ matrix.target }}
path: dist/*
compression-level: 0
if-no-files-found: error
retention-days: 7
- name: Attach this target immediately after validation
if: ${{ always() && steps.ready.outcome == 'success' && inputs.publish }}
env:
GH_TOKEN: ${{ github.token }}
run: |
files=(dist/*.zip dist/*.sha256sum)
bash releases/github-release-upload.sh "$GITHUB_REPOSITORY" "$RELEASE_TAG" "${files[@]}"
+4 -2
View File
@@ -10,6 +10,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: 'Checkout Repository'
uses: actions/checkout@v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: 'Dependency Review'
uses: actions/dependency-review-action@v5
uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0
+12 -4
View File
@@ -33,7 +33,9 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
# The Dockerfile DOWNLOADS the prebuilt release zip that release-all.yml
# publishes (and `exit 8`s if it is missing), so this image can only be
@@ -47,12 +49,18 @@ jobs:
id: rel
env:
GH_TOKEN: ${{ github.token }}
GITHUB_CTX_REPOSITORY: ${{ github.repository }}
run: |
set -euo pipefail
VERSION="$(sed -n 's/^ARG VERSION=//p' Dockerfile | head -1)"
asset="wekan-${VERSION}-amd64.zip"
if gh release view "v${VERSION}" --repo "${{ github.repository }}" \
--json assets --jq '.assets[].name' 2>/dev/null | grep -qx "$asset"; then
# The list is read whole BEFORE matching. `gh … | grep -q` lets grep exit
# at the first match while gh is still writing; gh gets SIGPIPE, pipefail
# makes the pipeline fail, and a PUBLISHED asset was reported missing - the
# v12.39 Mac apps were skipped that way, with the bundles already attached.
assets="$(gh release view "v${VERSION}" --repo "${GITHUB_CTX_REPOSITORY}" \
--json assets --jq '.assets[].name' 2>/dev/null || true)"
if grep -qx "$asset" <<< "$assets"; then
echo "build=yes" >> "$GITHUB_OUTPUT"
echo "OK: $asset is on the v${VERSION} release; building the image."
else
@@ -64,7 +72,7 @@ jobs:
# https://github.com/docker/login-action
- name: Log into registry ${{ env.REGISTRY }}
if: github.event_name != 'pull_request'
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f
uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
+23 -12
View File
@@ -62,6 +62,7 @@ jobs:
- name: Check DOCKERHUB_AUTH is configured and works
env:
DOCKERHUB_AUTH: ${{ secrets.DOCKERHUB_AUTH }}
INPUT_IMAGE: ${{ inputs.image }}
run: |
set -uo pipefail
if [ -z "${DOCKERHUB_AUTH:-}" ]; then
@@ -79,16 +80,19 @@ jobs:
user="${creds%%:*}"
token="${creds#*:}"
if ! printf '%s' "$token" | docker login docker.io -u "$user" --password-stdin; then
echo "::error::docker-variant: DOCKERHUB_AUTH was REJECTED by docker.io (login failed as user '$user'). The token is wrong, expired, or lacks push rights for wekanteam/${{ inputs.image }}."
echo "::error::docker-variant: DOCKERHUB_AUTH was REJECTED by docker.io (login failed as user '$user'). The token is wrong, expired, or lacks push rights for wekanteam/${INPUT_IMAGE}."
exit 1
fi
echo "docker-variant: DOCKERHUB_AUTH works for docker.io (user '$user')."
- name: Work out which version to publish
id: v
env:
INPUT_VERSION: ${{ inputs.version }}
INPUT_IMAGE: ${{ inputs.image }}
run: |
set -euo pipefail
version="${{ inputs.version }}"
version="${INPUT_VERSION}"
if [ -z "$version" ]; then
# The newest release of wekan/wekan, asked of GitHub rather than guessed.
version="$(curl -sSL -H 'Accept: application/vnd.github+json' \
@@ -101,16 +105,20 @@ jobs:
exit 1
fi
echo "version=$version" >> "$GITHUB_OUTPUT"
echo "Publishing wekanteam/${{ inputs.image }} from wekanteam/wekan:v$version"
echo "Publishing wekanteam/${INPUT_IMAGE} from wekanteam/wekan:v$version"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
- name: Retag wekanteam/wekan as wekanteam/${{ inputs.image }}
env:
STEP_V_VERSION: ${{ steps.v.outputs.version }}
INPUT_IMAGE: ${{ inputs.image }}
INPUT_ALSO_LATEST: ${{ inputs.also_latest }}
run: |
set -euo pipefail
src="wekanteam/wekan:v${{ steps.v.outputs.version }}"
dst="wekanteam/${{ inputs.image }}:v${{ steps.v.outputs.version }}"
src="wekanteam/wekan:v${STEP_V_VERSION}"
dst="wekanteam/${INPUT_IMAGE}:v${STEP_V_VERSION}"
# The source must exist and be the full multi-arch manifest, or the
# variant tag would quietly cover fewer architectures than wekan does.
@@ -128,8 +136,8 @@ jobs:
done
tags=(-t "$dst")
if [ "${{ inputs.also_latest }}" = "true" ]; then
tags+=(-t "wekanteam/${{ inputs.image }}:latest")
if [ "${INPUT_ALSO_LATEST}" = "true" ]; then
tags+=(-t "wekanteam/${INPUT_IMAGE}:latest")
fi
# imagetools create copies the MANIFEST, so the variant tag points at the
# very digests the release built and verified. Nothing is rebuilt.
@@ -149,12 +157,15 @@ jobs:
- name: Job result
if: always()
env:
JOB_STATUS: ${{ job.status }}
INPUT_IMAGE: ${{ inputs.image }}
run: |
case "${{ job.status }}" in
case "${JOB_STATUS}" in
success) mark='OK ' ;;
failure) mark='FAILED ' ;;
cancelled) mark='CANCELLED' ;;
*) mark='${{ job.status }}' ;;
*) mark="${JOB_STATUS}" ;;
esac
echo "$mark docker-variant wekanteam/${{ inputs.image }}"
echo "$mark docker-variant wekanteam/${{ inputs.image }}" >> "$GITHUB_STEP_SUMMARY"
echo "$mark docker-variant wekanteam/${INPUT_IMAGE}"
echo "$mark docker-variant wekanteam/${INPUT_IMAGE}" >> "$GITHUB_STEP_SUMMARY"
+11 -3
View File
@@ -16,7 +16,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
# The Dockerfile does not build WeKan from source — it DOWNLOADS the prebuilt
# release zip (wekan-<VERSION>-amd64.zip) that release-all.yml publishes, and
@@ -31,12 +33,18 @@ jobs:
id: rel
env:
GH_TOKEN: ${{ github.token }}
GITHUB_CTX_REPOSITORY: ${{ github.repository }}
run: |
set -euo pipefail
VERSION="$(sed -n 's/^ARG VERSION=//p' Dockerfile | head -1)"
asset="wekan-${VERSION}-amd64.zip"
if gh release view "v${VERSION}" --repo "${{ github.repository }}" \
--json assets --jq '.assets[].name' 2>/dev/null | grep -qx "$asset"; then
# The list is read whole BEFORE matching. `gh … | grep -q` lets grep exit
# at the first match while gh is still writing; gh gets SIGPIPE, pipefail
# makes the pipeline fail, and a PUBLISHED asset was reported missing - the
# v12.39 Mac apps were skipped that way, with the bundles already attached.
assets="$(gh release view "v${VERSION}" --repo "${GITHUB_CTX_REPOSITORY}" \
--json assets --jq '.assets[].name' 2>/dev/null || true)"
if grep -qx "$asset" <<< "$assets"; then
echo "build=yes" >> "$GITHUB_OUTPUT"
echo "OK: $asset is on the v${VERSION} release; building the image."
else
+63 -31
View File
@@ -34,8 +34,9 @@ on:
default: ''
type: string
# Read-only by default; the job that writes is granted it below.
permissions:
contents: write
contents: read
concurrency:
group: wekan-mac-app-${{ inputs.tag || github.ref }}
@@ -65,6 +66,8 @@ jobs:
echo "version=${tag#v}" >> "$GITHUB_OUTPUT"
build:
permissions:
contents: write
name: Mac app ${{ matrix.arch }}
needs: release
runs-on: ${{ matrix.runner }}
@@ -82,7 +85,9 @@ jobs:
env:
BUILD_THIS: ${{ inputs.only == '' || contains(fromJSON(inputs.only), matrix.arch) }}
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
if: ${{ env.BUILD_THIS == 'true' }}
- name: Download and verify the native macOS bundle
if: ${{ env.BUILD_THIS == 'true' }}
@@ -95,8 +100,13 @@ jobs:
run: |
set -euo pipefail
zip="wekan-${VERSION}-${BUNDLE_ARCH}.zip"
if ! gh release view "$TAG" --repo "$GITHUB_REPOSITORY" \
--json assets --jq '.assets[].name' | grep -qxF "$zip"; then
# The list is read whole BEFORE matching. `gh … | grep -q` lets grep exit
# at the first match while gh is still writing; gh gets SIGPIPE, pipefail
# makes the pipeline fail, and a PUBLISHED asset was reported missing - the
# v12.39 Mac apps were skipped that way, with the bundles already attached.
assets="$(gh release view "$TAG" --repo "$GITHUB_REPOSITORY" \
--json assets --jq '.assets[].name')"
if ! grep -qxF "$zip" <<< "$assets"; then
echo "::notice::$zip has not been published yet; this app cannot be wrapped."
echo "available=false" >> "$GITHUB_OUTPUT"
exit 0
@@ -128,6 +138,7 @@ jobs:
grep -q "WeKan-${VERSION}-mac-${MAC_ARCH}.app/Contents/MacOS/WeKan" "$TMPDIR/app-contents.txt"
- name: Smoke test Node.js, FerretDB and Meteor
id: smoke
timeout-minutes: 5
if: ${{ env.BUILD_THIS == 'true' && steps.bundle.outputs.available == 'true' }}
env:
@@ -135,6 +146,9 @@ jobs:
MAC_ARCH: ${{ matrix.arch }}
run: |
set -euo pipefail
mkdir -p "$PWD/.tools/tmp"
export TMPDIR="$PWD/.tools/tmp"
python3 -B tests/mac-smoke-app.py
app="$PWD/dist/WeKan-${VERSION}-mac-${MAC_ARCH}.app"
command="$app/Contents/Resources/launch-wekan.command"
export WRITABLE_PATH="$PWD/.tools/tmp/mac-smoke-data"
@@ -144,14 +158,14 @@ jobs:
"$PWD/.tools/tmp/mac-smoke.log"
- name: Keep Mac smoke log
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
if: ${{ always() && env.BUILD_THIS == 'true' && steps.bundle.outputs.available == 'true' }}
with:
name: mac-smoke-${{ matrix.arch }}
path: .tools/tmp/mac-smoke.log
if-no-files-found: ignore
- uses: actions/upload-artifact@v7
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
if: ${{ env.BUILD_THIS == 'true' && steps.bundle.outputs.available == 'true' }}
with:
name: mac-app-${{ matrix.arch }}
@@ -160,37 +174,55 @@ jobs:
dist/*.app.zip.sha256sum
if-no-files-found: error
publish:
name: Attach Mac apps to the release
needs: [release, build]
if: ${{ !cancelled() && inputs.publish != false }}
runs-on: ubuntu-24.04
timeout-minutes: 15
steps:
- uses: actions/download-artifact@v8
continue-on-error: true
with:
pattern: mac-app-*
path: assets
merge-multiple: true
- name: Upload available Mac apps
# Attached HERE, by the job that made and smoke-tested the app, the moment
# it passed - not by a later job that waited for both architectures and
# then uploaded them together. always() + the smoke test's outcome: a
# cancel that arrives after the app was built and checked still attaches
# it.
- name: Attach this Mac app to the release
if: ${{ always() && env.BUILD_THIS == 'true' && steps.bundle.outputs.available == 'true' && steps.smoke.outcome == 'success' && inputs.publish != false }}
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ needs.release.outputs.tag }}
VERSION: ${{ needs.release.outputs.version }}
MAC_ARCH: ${{ matrix.arch }}
run: |
set -euo pipefail
if ! ls assets/*.app.zip >/dev/null 2>&1; then
zip="dist/WeKan-${VERSION}-mac-${MAC_ARCH}.app.zip"
# The checksum is checked against the zip before it goes up, the same
# check the old collect-and-upload job made.
( cd dist && shasum -a 256 -c "$(basename "$zip").sha256sum" )
# Retried, bounded, and verified by name and size from the release side.
bash releases/github-release-upload.sh "$GITHUB_REPOSITORY" "$TAG" "$zip" "${zip}.sha256sum"
# The last job no longer uploads anything: each build job above attached its
# own app as soon as it was built and smoke-tested. This one only says, at
# the end, which apps are on the release - and it runs even when the run was
# cancelled (always()), so the summary covers whatever did get attached.
publish:
name: Check which Mac apps are on the release
needs: [release, build]
if: ${{ always() && needs.release.result == 'success' && inputs.publish != false }}
runs-on: ubuntu-24.04
timeout-minutes: 15
steps:
- name: Say which Mac apps are attached
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ needs.release.outputs.tag }}
VERSION: ${{ needs.release.outputs.version }}
run: |
set -euo pipefail
gh release view "$TAG" --repo "$GITHUB_REPOSITORY" --json assets \
--jq '.assets[].name' > published.txt
if ! grep -qE '\.app\.zip$' published.txt; then
echo "::notice::No Mac app could be wrapped; check the macOS bundle jobs."
exit 0
fi
for zip in assets/*.app.zip; do
( cd assets && shasum -a 256 -c "$(basename "$zip").sha256sum" )
done
gh release upload "$TAG" assets/* --repo "$GITHUB_REPOSITORY" --clobber
gh release view "$TAG" --repo "$GITHUB_REPOSITORY" --json assets \
--jq '.assets[].name' > published.txt
for file in assets/*; do
grep -qxF "$(basename "$file")" published.txt || {
echo "::error::$(basename "$file") was not attached to $TAG"; exit 1;
}
for arch in arm64 amd64; do
for file in "WeKan-${VERSION}-mac-${arch}.app.zip" "WeKan-${VERSION}-mac-${arch}.app.zip.sha256sum"; do
grep -qxF "$file" published.txt || \
echo "::warning::$file is not on $TAG - see the Mac app $arch build job's log"
done
done
grep -E '\.app\.zip' published.txt || true
+13 -19
View File
@@ -32,23 +32,16 @@ jobs:
runs-on: ubuntu-24.04
timeout-minutes: 120
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.event.inputs.ref || 'main' }}
persist-credentials: false
- uses: actions/setup-node@v7
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: ${{ env.NODE_VERSION }}
- name: Use npm 12.0.2
run: npm install --global npm@12.0.2
- name: Enable unprivileged user namespaces (Ubuntu 24.04 restricts them)
# Same as sandstorm.yml: Ubuntu 24.04's AppArmor blocks the unprivileged user
# namespaces the Sandstorm install relies on; relax it so the install (whose
# sandstorm-http-bridge build-deps.sh copies in) can run on the runner.
run: |
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 || true
sudo sysctl -w kernel.unprivileged_userns_clone=1 2>/dev/null || true
- name: Use release npm version
run: bash releases/use-release-npm.sh
- name: Install system dependencies
run: |
@@ -58,13 +51,13 @@ jobs:
# build-deps.sh's [bridge] step copies the modern
# /opt/sandstorm/latest/bin/sandstorm-http-bridge into the deps, so Sandstorm
# must be installed first — same ordering as sandstorm.yml.
run: curl https://install.sandstorm.io | sudo bash -s -- -d
run: curl https://install.sandstorm.org | sudo bash -s -- -d
- name: Assemble modern meteor-spk.deps (Node 26 + FerretDB + Mongo 3.x CLIs)
# Base = upstream meteor-spk 0.6.0 from dl.sandstorm.io; build-deps.sh swaps in
# Base = upstream meteor-spk 0.6.0 from dl.sandstorm.org; build-deps.sh swaps in
# Node 26, FerretDB, the Mongo 3.x CLIs, the migration launcher/importer, keeps
# niscud, and swaps in the modern sandstorm-http-bridge. Produces
# $HOME/projects/meteor-spk/meteor-spk-0.6.0 (the meteor-spk tool + deps).
# $GITHUB_WORKSPACE/.tools/projects/meteor-spk/meteor-spk-0.6.0 (the meteor-spk tool + deps).
run: bash sandstorm-src/build-deps.sh
- name: Zip the meteor-spk bundle
@@ -73,7 +66,7 @@ jobs:
set -euo pipefail
STAMP="$(date +'%Y-%m-%d_%H_%M_%S')"
ZIP="meteor-spk-${STAMP}.zip"
BUNDLE_DIR="$HOME/projects/meteor-spk"
BUNDLE_DIR="$GITHUB_WORKSPACE/.tools/projects/meteor-spk"
if [ ! -d "$BUNDLE_DIR/meteor-spk-0.6.0" ]; then
echo "::error::Expected meteor-spk bundle at $BUNDLE_DIR/meteor-spk-0.6.0 was not produced by build-deps.sh."
exit 1
@@ -87,7 +80,7 @@ jobs:
ls -la "$GITHUB_WORKSPACE/$ZIP"
echo "zip=$ZIP" >> "$GITHUB_OUTPUT"
- uses: actions/upload-artifact@v7
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: ${{ steps.zip.outputs.zip }}
path: ${{ steps.zip.outputs.zip }}
@@ -97,6 +90,7 @@ jobs:
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
STEP_ZIP_ZIP: ${{ steps.zip.outputs.zip }}
run: |
set -euo pipefail
TAG="$(gh release list --limit 1 --json tagName --jq '.[0].tagName' 2>/dev/null || true)"
@@ -104,5 +98,5 @@ jobs:
echo "::warning::No existing GitHub Release found; the meteor-spk bundle is available only as a workflow artifact."
exit 0
fi
echo "Uploading ${{ steps.zip.outputs.zip }} to the newest release: $TAG"
gh release upload --repo "${GITHUB_REPOSITORY:-wekan/wekan}" "$TAG" "${{ steps.zip.outputs.zip }}" --clobber
echo "Uploading ${STEP_ZIP_ZIP} to the newest release: $TAG"
gh release upload --repo "${GITHUB_REPOSITORY:-wekan/wekan}" "$TAG" "${STEP_ZIP_ZIP}" --clobber
+184
View File
@@ -0,0 +1,184 @@
name: musl bundles
# WeKan for Alpine and the other musl systems: the published Linux bundle of
# each CPU, rebuilt inside Alpine by releases/musl/build-bundle.sh with the
# musl Node.js of the same version and every native addon compiled against
# musl, then started and required to answer HTTP 200. The same pattern as
# the BSD bundles (bsd.yml). A target whose Linux bundle or musl Node.js the
# release does not have is SKIPPED, not failed.
on:
workflow_dispatch:
inputs:
tag:
description: 'Existing WeKan release tag (empty = latest published release)'
type: string
required: false
publish:
description: 'Attach the musl bundles and checksums to that release'
type: boolean
default: true
only:
description: 'JSON target list, e.g. ["linux-x64-musl"] (empty = all)'
type: string
default: ''
workflow_call:
inputs:
tag:
type: string
required: true
publish:
type: boolean
default: true
only:
type: string
default: ''
permissions:
contents: read
concurrency:
group: wekan-musl-${{ inputs.tag || github.ref }}
cancel-in-progress: false
jobs:
plan:
runs-on: ubuntu-24.04
timeout-minutes: 10
outputs:
tag: ${{ steps.plan.outputs.tag }}
matrix: ${{ steps.plan.outputs.matrix }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- id: plan
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ inputs.tag }}
ONLY: ${{ inputs.only }}
run: |
set -euo pipefail
tag="${TAG:-$(gh release view --repo "$GITHUB_REPOSITORY" --json tagName -q .tagName)}"
echo "tag=$tag" >> "$GITHUB_OUTPUT"
matrix="$(python3 -c '
import json, os
targets = json.load(open("releases/musl/targets.json"))
only = json.loads(os.environ.get("ONLY") or "[]")
if only:
targets = [t for t in targets if t["target"] in only]
print(json.dumps({"include": targets}))')"
echo "matrix=$matrix" >> "$GITHUB_OUTPUT"
echo "Release $tag; targets: $matrix"
build:
needs: plan
name: musl ${{ matrix.target }}
runs-on: ${{ matrix.runner }}
timeout-minutes: 120
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.plan.outputs.matrix) }}
env:
TAG: ${{ needs.plan.outputs.tag }}
TARGET: ${{ matrix.target }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Fetch and verify the Linux bundle and the musl Node.js
id: fetch
env:
GH_TOKEN: ${{ github.token }}
BASE: ${{ matrix.base }}
NODE_PLATFORM: ${{ matrix.node }}
run: |
set -euo pipefail
version="${TAG#v}"
zip="wekan-${version}-${BASE}.zip"
if ! gh release download "$TAG" --repo "$GITHUB_REPOSITORY" --pattern "$zip" --pattern "$zip.sha256sum" || [ ! -f "$zip" ]; then
echo "::notice::$TARGET: $TAG has no $zip; skipped."
echo "skip=true" >> "$GITHUB_OUTPUT"; exit 0
fi
[ -f "$zip.sha256sum" ] || { echo "::error::$zip has no published .sha256sum; a bundle is never rebuilt from an unverified one."; exit 1; }
sha256sum -c "$zip.sha256sum"
unzip -q "$zip"
rm -f "$zip" "$zip.sha256sum"
# The musl Node.js of exactly the bundle's version.
node_version="$(grep -aoE 'nodejs\.org/download/release/v[0-9]+\.[0-9]+\.[0-9]+/' bundle/node | head -n1 | sed -E 's#.*/(v[0-9.]+)/#\1#')"
[ -n "$node_version" ] || { echo "::error::Could not read the Node.js version of bundle/node."; exit 1; }
rc=0; out="$(bash releases/resolve-node-source.sh "$NODE_PLATFORM" "$node_version")" || rc=$?
if [ "$rc" = 3 ] || ! printf '%s\n' "$out" | grep -q '^node_found=true'; then
echo "::notice::$TARGET: no musl Node.js $node_version is published yet; skipped."
echo "skip=true" >> "$GITHUB_OUTPUT"; exit 0
fi
eval "$(printf '%s\n' "$out" | grep -E '^node_(url|sha256|kind)=')"
[ "$node_kind" = tar.xz ] || { echo "::error::expected a tar.xz musl Node.js, got $node_kind"; exit 1; }
bash releases/fetch.sh -o node-musl.tar.xz "$node_url"
[ -n "$node_sha256" ] || { echo "::error::$node_url has no published SHA256."; exit 1; }
echo "$node_sha256 node-musl.tar.xz" | sha256sum -c -
mkdir -p musl-tools
tar -xJf node-musl.tar.xz --strip-components=1 -C musl-tools
rm -f node-musl.tar.xz
file musl-tools/bin/node bundle/ferretdb
echo "version=$version" >> "$GITHUB_OUTPUT"
- name: Rebuild and start it in Alpine
if: steps.fetch.outputs.skip != 'true'
env:
ARCH: ${{ matrix.arch }}
run: |
docker run --rm -v "$PWD:/work" -w /work -e ARCH \
alpine:3.23 sh -ec '
apk add --no-cache bash coreutils curl file python3 make g++ linux-headers binutils libstdc++ libgcc libatomic
bash releases/musl/build-bundle.sh "$ARCH"'
- name: Package
if: steps.fetch.outputs.skip != 'true'
env:
VERSION: ${{ steps.fetch.outputs.version }}
run: |
set -euo pipefail
sudo chown -R "$(id -u):$(id -g)" bundle
out="wekan-${VERSION}-${TARGET}.zip"
zip -qry "$out" bundle
sha256sum "$out" > "$out.sha256sum"
ls -la "$out" "$out.sha256sum"
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
if: steps.fetch.outputs.skip != 'true'
with:
name: musl-${{ matrix.target }}
path: wekan-*-${{ matrix.target }}.zip*
retention-days: 3
compression-level: 0
publish:
needs: [plan, build]
if: ${{ !cancelled() && inputs.publish }}
runs-on: ubuntu-24.04
timeout-minutes: 60
permissions:
contents: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
pattern: musl-*
path: artifacts
- name: Attach every musl bundle that was built and started
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ needs.plan.outputs.tag }}
run: |
set -euo pipefail
shopt -s nullglob
published=0
for f in artifacts/musl-*/wekan-*.zip; do
(cd "$(dirname "$f")" && sha256sum -c "$(basename "$f").sha256sum")
bash releases/github-release-upload.sh "$GITHUB_REPOSITORY" "$TAG" "$f" "$f.sha256sum"
published=$((published + 1))
done
echo "Published $published musl bundles to $TAG."
+169
View File
@@ -0,0 +1,169 @@
name: Nextcloud App Store
run-name: Nextcloud ${{ inputs.version }}
on:
workflow_dispatch:
inputs:
version:
required: true
type: string
description: 'Existing WeKan release version, without v'
permissions:
contents: write
packages: write
concurrency:
group: wekan-nextcloud-${{ inputs.version }}
cancel-in-progress: false
jobs:
nextcloud:
runs-on: ubuntu-latest
timeout-minutes: 60
env:
VERSION: ${{ inputs.version }}
steps:
- name: Check required secrets
id: guard
env:
REPO_TOKEN: ${{ secrets.WEKAN_REPO_TOKEN }}
NC_KEY: ${{ secrets.NEXTCLOUD_APP_PRIVATE_KEY }}
NC_CRT: ${{ secrets.NEXTCLOUD_APP_PUBLIC_CRT }}
NC_TOKEN: ${{ secrets.NEXTCLOUD_APPSTORE_TOKEN }}
REGISTRY_AUTH: ${{ secrets.GHCR_AUTH }}
run: |
set -eu
if [ -z "$REPO_TOKEN" ] || [ -z "$NC_KEY" ] || [ -z "$NC_CRT" ] || [ -z "$NC_TOKEN" ] || [ -z "$REGISTRY_AUTH" ]; then
echo 'ok=false' >> "$GITHUB_OUTPUT"
echo '::notice::Nextcloud publishing not configured. See docs/Platforms/FOSS/Container/Docker/Nextcloud/README.md.'
else
echo 'ok=true' >> "$GITHUB_OUTPUT"
fi
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
if: steps.guard.outputs.ok == 'true'
- name: Prepare temporary directory and validate release
if: steps.guard.outputs.ok == 'true'
env:
GH_TOKEN: ${{ github.token }}
run: |
mkdir -p "$GITHUB_WORKSPACE/.tools/tmp"
echo "TMPDIR=$GITHUB_WORKSPACE/.tools/tmp" >> "$GITHUB_ENV"
python3 -c 'import sys; sys.path.insert(0,"releases"); import importlib; importlib.import_module("nextcloud-release").app_version(sys.argv[1])' "$VERSION"
gh release view "v$VERSION" --repo wekan/wekan --json tagName
- name: Checkout the ExApp definition
if: steps.guard.outputs.ok == 'true'
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: wekan/nextcloud
token: ${{ secrets.WEKAN_REPO_TOKEN }}
persist-credentials: false
path: exapp
- name: Check App Store and prepare versioned metadata
id: plan
if: steps.guard.outputs.ok == 'true'
run: python3 releases/nextcloud-release.py --version "$VERSION"
- name: Set up QEMU
if: steps.guard.outputs.ok == 'true' && steps.plan.outputs.skip != 'true'
uses: docker/setup-qemu-action@99012661954931238ded8c8b007157a8430204e1 # v4.4.0
with:
platforms: arm64
- name: Set up Docker Buildx
if: steps.guard.outputs.ok == 'true' && steps.plan.outputs.skip != 'true'
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
- name: Publish the missing versioned ExApp image
if: steps.guard.outputs.ok == 'true' && steps.plan.outputs.skip != 'true'
env:
GHCR_AUTH: ${{ secrets.GHCR_AUTH }}
run: |
set +x
set -euo pipefail
credentials="$(printf '%s' "$GHCR_AUTH" | base64 -d)"
[[ "$credentials" == *:* && -n "${credentials%%:*}" && -n "${credentials#*:}" ]]
printf '%s' "${credentials#*:}" | docker login ghcr.io -u "${credentials%%:*}" --password-stdin
unset credentials
docker pull "ghcr.io/wekan/wekan:v$VERSION"
# Old releases lack the connection-bound Nextcloud authentication.
docker run --rm --entrypoint /bin/sh "ghcr.io/wekan/wekan:v$VERSION" -ec \
'test -f /build/nextcloud-exapp-capability.json && test -x /build/ferretdb'
node --test exapp/tests/*.test.mjs
image="ghcr.io/wekan/wekan-exapp:v$VERSION"
if docker buildx imagetools inspect "$image" > .tools/exapp-image.txt 2> .tools/exapp-image-error.txt; then
echo 'Keeping the existing versioned ExApp image.'
elif grep -Eqi 'manifest unknown|name unknown|not found' .tools/exapp-image-error.txt; then
docker buildx build --push --provenance=mode=max --platform linux/amd64,linux/arm64 \
--tag "$image" --build-arg "WEKAN_VERSION=$VERSION" exapp
else
cat .tools/exapp-image-error.txt >&2
echo '::error::Cannot inspect the Nextcloud ExApp image; refusing to treat a registry error as an absent image.' >&2
exit 1
fi
# No latest tag or companion-branch update: recovery of an older release
# must not roll back a newer deployment or the companion source tree.
- name: Package, sign and publish to the Nextcloud App Store
if: steps.guard.outputs.ok == 'true' && steps.plan.outputs.skip != 'true'
env:
APP_ID: ${{ steps.plan.outputs.app_id }}
APP_PRIVATE_KEY: ${{ secrets.NEXTCLOUD_APP_PRIVATE_KEY }}
APP_PUBLIC_CRT: ${{ secrets.NEXTCLOUD_APP_PUBLIC_CRT }}
APPSTORE_TOKEN: ${{ secrets.NEXTCLOUD_APPSTORE_TOKEN }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set +x
set -euo pipefail
umask 077
task_dir="$(mktemp -d "$TMPDIR/nextcloud-sign.XXXXXX")"
trap 'rm -rf -- "$task_dir"' EXIT
archive="${APP_ID}.tar.gz"
new_archive=false
gh release view --repo wekan/wekan "v$VERSION" --json assets > "$task_dir/assets.json"
if jq -e --arg name "$archive" '.assets | any(.name == $name)' "$task_dir/assets.json" >/dev/null; then
gh release download --repo wekan/wekan "v$VERSION" --pattern "$archive" --dir "$task_dir"
else
releases/nextcloud-pack.sh exapp "$APP_ID" "$task_dir/$archive"
new_archive=true
fi
python3 releases/nextcloud-release.py --version "$VERSION" --archive "$task_dir/$archive" --app-id "$APP_ID"
if [ "$new_archive" = true ]; then
gh release upload --repo wekan/wekan "v$VERSION" "$task_dir/$archive"
fi
printf '%s' "$APP_PRIVATE_KEY" > "$task_dir/app.key"
printf '%s' "$APP_PUBLIC_CRT" > "$task_dir/app.crt"
openssl x509 -in "$task_dir/app.crt" -pubkey -noout > "$task_dir/public.key"
openssl dgst -sha512 -sign "$task_dir/app.key" -out "$task_dir/signature" "$task_dir/$archive"
openssl dgst -sha512 -verify "$task_dir/public.key" -signature "$task_dir/signature" "$task_dir/$archive"
signature="$(openssl base64 -A -in "$task_dir/signature")"
download="https://github.com/wekan/wekan/releases/download/v${VERSION}/${archive}"
jq -n --arg download "$download" --arg signature "$signature" \
'{download: $download, signature: $signature, nightly: false}' > "$task_dir/release.json"
curl --fail-with-body --silent --show-error --max-time 300 \
-X POST https://apps.nextcloud.com/api/v1/apps/releases \
-H "Authorization: Token $APPSTORE_TOKEN" -H 'Content-Type: application/json' \
--data-binary "@$task_dir/release.json"
- name: Job result
if: always()
env:
ENABLED: ${{ steps.guard.outputs.ok }}
SKIPPED: ${{ steps.plan.outputs.skip }}
RESULT: ${{ job.status }}
run: |
if [ "$ENABLED" != true ]; then
echo 'Nextcloud: not configured; no publication attempted.' >> "$GITHUB_STEP_SUMMARY"
elif [ "$RESULT" != success ]; then
echo 'Nextcloud: FAILED; inspect this job.' >> "$GITHUB_STEP_SUMMARY"
elif [ "$SKIPPED" = true ]; then
echo 'Nextcloud: already published; left unchanged.' >> "$GITHUB_STEP_SUMMARY"
else
echo 'Nextcloud: App Store upload accepted.' >> "$GITHUB_STEP_SUMMARY"
fi
+3 -3
View File
@@ -21,7 +21,7 @@ jobs:
- uses: actions/checkout@v7
- name: Setup Node 26
uses: actions/setup-node@v6
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: '26'
cache: 'npm'
@@ -92,7 +92,7 @@ jobs:
- uses: actions/checkout@v7
- name: Setup Node 26
uses: actions/setup-node@v6
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: '26'
cache: 'npm'
@@ -220,7 +220,7 @@ jobs:
- uses: actions/checkout@v7
- name: Setup Node 26
uses: actions/setup-node@v6
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: '26'
cache: 'npm'
+115 -42
View File
@@ -10,7 +10,11 @@ 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, and never touches Docker, snap or the website.
# tags, and does not rebuild the main Docker images, snap or the website.
# UCS and Nextcloud portal publishing runs separately through their manual workflows.
# UCS migration tools are part of the main WeKan Docker image. recover retries
# its failed Docker build/publish job, including the bundled-tools smoke check;
# there is no separate migration image to build or registry package to publish.
#
# 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
@@ -53,9 +57,9 @@ on:
required: false
type: string
# Read-only by default; the job that writes is granted it below.
permissions:
contents: write
actions: write
contents: read
env:
DO_NOT_TRACK: '1'
@@ -70,10 +74,15 @@ concurrency:
jobs:
recover:
permissions:
contents: read
actions: write
runs-on: ubuntu-latest
timeout-minutes: 360
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Check dependency risk indicators before recovery
run: python3 releases/remote-release.py --audit
- name: Retry failed jobs of the matching full release
@@ -88,6 +97,12 @@ jobs:
plan:
needs: recover
# The planner runs whatever recover's rerun ended with. A rerun that still
# fails - Launchpad answering 503, a fault that is in the tagged code and
# so in every rerun of it (v12.68's OpenBSD start) - used to skip the
# planner and every build after it, so not even the assets that COULD be
# filled were. recover stays red and names the jobs; the rest goes on.
if: ${{ !cancelled() }}
timeout-minutes: 15
name: what the release is missing
runs-on: ubuntu-latest
@@ -95,13 +110,16 @@ jobs:
version: ${{ steps.plan.outputs.version }}
extra: ${{ steps.plan.outputs.extra }}
appimage: ${{ steps.plan.outputs.appimage }}
bun: ${{ steps.plan.outputs.bun }}
mac: ${{ steps.plan.outputs.mac }}
flatpak: ${{ steps.plan.outputs.flatpak }}
windows: ${{ steps.plan.outputs.windows }}
manual: ${{ steps.plan.outputs.manual }}
total: ${{ steps.plan.outputs.total }}
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Check dependency risk indicators before release work
shell: bash
@@ -114,10 +132,11 @@ jobs:
- id: plan
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
INPUT_VERSION: ${{ inputs.version }}
run: |
set -euo pipefail
V='${{ inputs.version }}'
V="${INPUT_VERSION}"
V="${V#v}"
if [ -z "$V" ]; then
V="$(gh release view --repo "${GITHUB_REPOSITORY:-wekan/wekan}" --json tagName --jq .tagName)"
@@ -149,7 +168,7 @@ jobs:
total="$(wc -l < missing.txt | tr -d ' ')"
echo "total=$total" >> "$GITHUB_OUTPUT"
for kind in extra appimage mac flatpak windows; do
for kind in extra appimage mac flatpak windows bun; 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='[]'
@@ -184,16 +203,21 @@ jobs:
- name: Say plainly what this run will not do
if: ${{ steps.plan.outputs.manual != '' }}
env:
STEP_PLAN_VERSION: ${{ steps.plan.outputs.version }}
STEP_PLAN_MANUAL: ${{ steps.plan.outputs.manual }}
run: |
echo "::error::v${{ steps.plan.outputs.version }} still lacks ${{ steps.plan.outputs.manual }} after recovery. Inspect the matching full-release run; older unnamed runs need their failed jobs retried manually."
echo "::error::v${STEP_PLAN_VERSION} still lacks ${STEP_PLAN_MANUAL} after recovery. Inspect the matching full-release run; older unnamed runs need their failed jobs retried manually."
exit 1
# The six architectures nobody else builds for, repacked from the PUBLISHED
# amd64 bundle - which is what makes this possible without a Meteor build.
extra-arches:
permissions:
contents: write
name: ${{ matrix.arch }}
needs: plan
if: ${{ needs.plan.outputs.extra != '[]' }}
if: ${{ !cancelled() && needs.plan.result == 'success' && needs.plan.outputs.extra != '[]' }}
runs-on: ubuntu-24.04
timeout-minutes: 300
strategy:
@@ -222,15 +246,20 @@ jobs:
steps:
- name: Skip the architectures this release already has
id: want
env:
NEEDS_PLAN_EXTRA: ${{ needs.plan.outputs.extra }}
MATRIX_ARCH: ${{ matrix.arch }}
run: |
if printf '%s' '${{ needs.plan.outputs.extra }}' | jq -e --arg a '${{ matrix.arch }}' 'index($a)' >/dev/null; then
if printf '%s' "${NEEDS_PLAN_EXTRA}" | jq -e --arg a "${MATRIX_ARCH}" 'index($a)' >/dev/null; then
echo "build=yes" >> "$GITHUB_OUTPUT"
else
echo "build=no" >> "$GITHUB_OUTPUT"
echo "v${VERSION} already has ${{ matrix.arch }}; nothing to do."
echo "v${VERSION} already has ${MATRIX_ARCH}; nothing to do."
fi
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Audit telemetry source before release work
shell: bash
@@ -243,7 +272,7 @@ jobs:
- name: Set up QEMU for ${{ matrix.arch }} emulation
if: ${{ steps.want.outputs.build == 'yes' }}
uses: docker/setup-qemu-action@v4
uses: docker/setup-qemu-action@99012661954931238ded8c8b007157a8430204e1 # v4.4.0
# Before the download, because actions/checkout cleans the workspace. if: ${{ steps.want.outputs.build == 'yes' }}
@@ -271,6 +300,7 @@ jobs:
rm -f "$zip" "${zip}.sha256sum"
- name: Repack it for ${{ matrix.arch }}
id: repack
if: ${{ steps.want.outputs.build == 'yes' }}
env:
ARCH: ${{ matrix.arch }}
@@ -280,30 +310,48 @@ jobs:
PLATFORM: ${{ matrix.platform }}
run: bash releases/repack-bundle-for-arch.sh
# Attached by THIS job, the moment the repack (which builds and checks
# the zip) has finished - the same rule as every build job in
# release-all.yml. always() + the repack step's outcome: a cancel that
# arrives after the zip was built still attaches it.
#
# It used to upload WITHOUT --clobber, reasoning that this architecture
# was absent so an overwrite meant the release changed under this run.
# The shared uploader retries, and a retry after a half-finished upload
# has to replace its own partial asset, so it always clobbers. The plan
# job has already established the asset is missing, and the concurrency
# group keeps a full release run from uploading the same names at once.
- name: Attach ${{ matrix.arch }} to the release
if: ${{ steps.want.outputs.build == 'yes' }}
if: ${{ always() && steps.want.outputs.build == 'yes' && steps.repack.outcome == 'success' }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
MATRIX_ARCH: ${{ matrix.arch }}
run: |
set -euo pipefail
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 --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 --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
}
echo "OK: $asset attached ($(($(wc -c < "$asset")/1024/1024)) MiB)."
asset="wekan-${VERSION}-${MATRIX_ARCH}.zip"
# Retried, bounded, and verified by name AND size from the release
# side - an upload that reports success and leaves no asset behind is
# the failure nobody notices until a download 404s.
bash releases/github-release-upload.sh "${GITHUB_REPOSITORY:-wekan/wekan}" "v${VERSION}" "$asset" "${asset}.sha256sum"
bun:
name: Missing Bun runtime bundles
needs: plan
if: ${{ !cancelled() && needs.plan.result == 'success' && needs.plan.outputs.bun != '[]' }}
uses: ./.github/workflows/bun.yml
permissions:
contents: write
actions: read
with:
tag: v${{ needs.plan.outputs.version }}
publish: true
only: ${{ needs.plan.outputs.bun }}
missing_only: true
appimage:
name: AppImages
needs: plan
if: ${{ needs.plan.outputs.appimage != '[]' }}
if: ${{ !cancelled() && needs.plan.result == 'success' && needs.plan.outputs.appimage != '[]' }}
uses: ./.github/workflows/AppImage.yml
permissions:
contents: write
@@ -315,7 +363,7 @@ jobs:
mac:
name: Mac apps
needs: plan
if: ${{ needs.plan.outputs.mac != '[]' }}
if: ${{ !cancelled() && needs.plan.result == 'success' && needs.plan.outputs.mac != '[]' }}
uses: ./.github/workflows/mac.yml
permissions:
contents: write
@@ -327,7 +375,7 @@ jobs:
flatpak:
name: Flatpaks
needs: plan
if: ${{ needs.plan.outputs.flatpak != '[]' }}
if: ${{ !cancelled() && needs.plan.result == 'success' && needs.plan.outputs.flatpak != '[]' }}
uses: ./.github/workflows/Flatpak.yml
permissions:
contents: write
@@ -339,7 +387,7 @@ jobs:
windows-exe:
name: Windows single EXE
needs: plan
if: ${{ needs.plan.outputs.windows != '[]' }}
if: ${{ !cancelled() && needs.plan.result == 'success' && needs.plan.outputs.windows != '[]' }}
uses: ./.github/workflows/windows.yml
permissions:
contents: write
@@ -347,6 +395,22 @@ jobs:
tag: v${{ needs.plan.outputs.version }}
publish: true
# A single file per platform, for every platform whose bundle the release
# has and whose single file it does not (docs/Platforms/Single-File.md).
# Not in expected-assets.sh: a platform without a bundle has no single file
# to miss. After extra-arches, which may have just published a bundle.
single-file:
name: Missing single-file WeKan
needs: [plan, extra-arches]
if: ${{ !cancelled() && needs.plan.result == 'success' }}
uses: ./.github/workflows/single-file.yml
permissions:
contents: write
with:
tag: v${{ needs.plan.outputs.version }}
publish: true
missing_only: true
# ─── The Helm chart, for a release that never got one ───────────────────────
#
@@ -367,12 +431,11 @@ jobs:
timeout-minutes: 30
name: Helm chart
needs: plan
if: ${{ !cancelled() && needs.plan.outputs.version != '' }}
if: ${{ !cancelled() && needs.plan.result == 'success' && 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
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
path: wekan
@@ -414,7 +477,7 @@ jobs:
- name: Checkout wekan/charts (main + gh-pages)
if: steps.check.outputs.skip != 'true'
uses: actions/checkout@v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: wekan/charts
token: ${{ secrets.WEKAN_REPO_TOKEN }}
@@ -423,12 +486,13 @@ jobs:
- name: Install Helm
if: steps.check.outputs.skip != 'true'
uses: azure/setup-helm@v5
uses: azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310 # v5.0.1
- name: Publish the chart for this release
if: steps.check.outputs.skip != 'true'
env:
CHARTS_DIR: ${{ github.workspace }}/charts
NEEDS_PLAN_VERSION: ${{ needs.plan.outputs.version }}
run: |
set -euo pipefail
cd charts
@@ -438,27 +502,36 @@ jobs:
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 }}"
./wekan/releases/release-charts.sh "${NEEDS_PLAN_VERSION}"
- name: Job result
if: always()
env:
JOB_STATUS: ${{ job.status }}
run: |
case "${{ job.status }}" in
case "${JOB_STATUS}" in
success) mark='OK ' ;;
failure) mark='FAILED ' ;;
cancelled) mark='CANCELLED' ;;
*) mark='${{ job.status }}' ;;
*) mark="${JOB_STATUS}" ;;
esac
echo "$mark charts" >> "$GITHUB_STEP_SUMMARY"
# The end of the run attaches nothing: every job above attached its own files
# as it finished them. This only reports what the release still lacks, and it
# runs on always() - not !cancelled() - so a cancelled run still says what
# did and did not get attached. It needs plan to have succeeded, because the
# version comes from there.
done:
timeout-minutes: 15
name: what is still missing
needs: [plan, extra-arches, appimage, mac, flatpak, charts]
if: ${{ !cancelled() }}
needs: [plan, extra-arches, appimage, mac, flatpak, windows-exe, charts, bun]
if: ${{ always() && needs.plan.result == 'success' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Audit telemetry source before release work
shell: bash
run: python3 ./releases/check-telemetry.py --source .
File diff suppressed because it is too large. Load diff
+8 -2
View File
@@ -4,6 +4,11 @@ on:
push:
branches:
- main
# This repository has no charts/ directory: the Helm chart is published to
# wekan/charts by release-all.yml. Without this filter every push to main
# cloned the full history (447 MB) to find no chart to release.
paths:
- 'charts/**'
permissions:
contents: read
@@ -16,9 +21,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false
- name: Configure Git
run: |
@@ -26,6 +32,6 @@ jobs:
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
- name: Run chart-releaser
uses: helm/chart-releaser-action@v1.7.0
uses: helm/chart-releaser-action@df287104677278f869ca5480bd260358397cc3f9 # v1.8.0
env:
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
+12 -12
View File
@@ -38,18 +38,15 @@ jobs:
runs-on: ubuntu-24.04
timeout-minutes: 120
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/setup-node@v7
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: ${{ env.NODE_VERSION }}
- name: Use npm 12.0.2
run: npm install --global npm@12.0.2
- name: Enable unprivileged user namespaces (Ubuntu 24.04 restricts them)
run: |
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 || true
sudo sysctl -w kernel.unprivileged_userns_clone=1 2>/dev/null || true
- name: Use release npm version
run: bash releases/use-release-npm.sh
- name: Install system dependencies
run: |
@@ -58,12 +55,12 @@ jobs:
- name: Install Sandstorm (dev, unattended)
# Provides the `spk` command used to publish, and the sandstorm-http-bridge that
# build-deps.sh copies into the meteor-spk deps.
run: curl https://install.sandstorm.io | sudo bash -s -- -d
run: curl https://install.sandstorm.org | sudo bash -s -- -d
- name: Assemble modern meteor-spk.deps (same as sandstorm.yml)
run: |
bash sandstorm-src/build-deps.sh
echo "$HOME/projects/meteor-spk/meteor-spk-0.6.0" >> "$GITHUB_PATH"
echo "$GITHUB_WORKSPACE/.tools/projects/meteor-spk/meteor-spk-0.6.0" >> "$GITHUB_PATH"
- name: Restore the Sandstorm signing keyring
env:
@@ -143,4 +140,7 @@ jobs:
fi
echo "Publishing $SPK with $SPK_BIN ..."
# Same command as releases/release-sandstorm.sh: spk publish wekan-<VERSION>.spk
"$SPK_BIN" publish "$SPK"
# Publish to apps.sandstorm.org
"$SPK_BIN" publish --webkey=https://api-b72c46193b266fd6c33e1299dccc9bfa.oasis.sandstorm.org#iCDu7VYl4qZxVbDLXLIE2XSU0bEadaBgApEMqgCkLmP "$SPK"
# Publish to apps.sandstorm.io
"$SPK_BIN" publish --webkey=https://alpha-api.sandstorm.io/#Rs-0TT13YrNSbv7Fiz5K9bBkLaJn3E5TB0PU1GSn1HE "$SPK"
+31 -23
View File
@@ -5,6 +5,11 @@ name: Build Sandstorm .spk
# Release as an attachment, so you can download it, test whether the Sandstorm
# build works, and see any errors — without running a full release.
#
# It builds only what a grain runs (docs/Platforms/FOSS/Container/Sandstorm/
# Smaller-wekan-spk.md): WeKan with the Sandstorm package profile - Sandstorm
# login only, no password/OAuth2/OIDC/CAS/LDAP/SAML packages - FerretDB from
# source with only its SQLite handler, and the deps meteor-spk.deps needs.
#
# gh workflow run sandstorm.yml
#
# EXPERIMENTAL in CI (same caveats as the build-sandstorm job in release-all.yml):
@@ -32,27 +37,18 @@ jobs:
runs-on: ubuntu-24.04
timeout-minutes: 120
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# Build the dispatched ref so a fix branch can be tested before it is
# merged to main; defaults to main for a plain `gh workflow run`.
ref: ${{ github.event.inputs.ref || 'main' }}
persist-credentials: false
- uses: actions/setup-node@v7
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: ${{ env.NODE_VERSION }}
- name: Use npm 12.0.2
run: npm install --global npm@12.0.2
- name: Enable unprivileged user namespaces (Ubuntu 24.04 restricts them)
# Ubuntu 24.04's AppArmor defaults kernel.apparmor_restrict_unprivileged_userns=1,
# which blocks the unprivileged user namespaces that the Sandstorm install and
# the spk supervisor rely on (the "fragile part" noted above). Relax it so
# `install.sandstorm.io` and meteor-spk can create sandboxes on the runner.
# `|| true`: on kernels/runners without these knobs, setting them is a no-op.
run: |
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 || true
sudo sysctl -w kernel.unprivileged_userns_clone=1 2>/dev/null || true
- name: Use release npm version
run: bash releases/use-release-npm.sh
- name: Install system dependencies and Meteor
run: |
@@ -64,17 +60,24 @@ jobs:
# Installed BEFORE assembling the deps: build-deps.sh's [bridge] step copies
# the modern /opt/sandstorm/latest/bin/sandstorm-http-bridge over the ancient
# bundled one, so the Sandstorm install must be in place first.
run: curl https://install.sandstorm.io | sudo bash -s -- -d
run: curl https://install.sandstorm.org | sudo bash -s -- -d
- name: Assemble modern meteor-spk.deps (Node 26 + FerretDB + Mongo 3.x CLIs)
# Base = upstream meteor-spk 0.6.0 from dl.sandstorm.io (has mongod 3.0 +
- name: Assemble modern meteor-spk.deps (Node 26 + SQLite-only FerretDB + Mongo 3.x CLIs)
# Base = upstream meteor-spk 0.6.0 from dl.sandstorm.org (has mongod 3.0 +
# niscud). build-deps.sh swaps in Node 26, adds FerretDB + the Mongo 3.x
# CLIs + the migration launcher/importer, keeps niscud, and swaps in the
# modern sandstorm-http-bridge from the Sandstorm install above. No dependency
# on the retired releases.wekan.team or the old projects.7z.
# FerretDB is the PREBUILT SQLite-only ferretdb-sqlite-amd64 from the newest
# wekan/FerretDB release that has it (the full ferretdb-amd64 of the newest
# release until one does), picked by releases/ferretdb-asset.sh - nothing
# is compiled here (docs/Platforms/FOSS/Container/Sandstorm/Smaller-wekan-spk.md).
env:
FERRETDB_SOURCE: download
GH_TOKEN: ${{ github.token }}
run: |
bash sandstorm-src/build-deps.sh
echo "$HOME/projects/meteor-spk/meteor-spk-0.6.0" >> "$GITHUB_PATH"
echo "$GITHUB_WORKSPACE/.tools/projects/meteor-spk/meteor-spk-0.6.0" >> "$GITHUB_PATH"
- name: Restore the Sandstorm signing keyring
env:
@@ -90,7 +93,7 @@ jobs:
chmod 600 "$HOME/.sandstorm-keyring"
echo "Restored ~/.sandstorm-keyring ($(wc -c < "$HOME/.sandstorm-keyring") bytes)."
else
echo "::error::SANDSTORM_KEYRING secret is not set. meteor-spk pack signs the .spk with the WeKan Sandstorm app's private key, which lives in this keyring, so packing cannot proceed without it. Add the base64 of your ~/.sandstorm-keyring file as the SANDSTORM_KEYRING repository secret (e.g. 'base64 -w0 ~/.sandstorm-keyring')."
echo "::error::SANDSTORM_KEYRING secret is not set. spk pack signs the .spk with the WeKan Sandstorm app's private key, which lives in this keyring, so packing cannot proceed without it. Add the base64 of your ~/.sandstorm-keyring file as the SANDSTORM_KEYRING repository secret (e.g. 'base64 -w0 ~/.sandstorm-keyring')."
exit 1
fi
@@ -112,7 +115,11 @@ jobs:
set -euo pipefail
STAMP="$(date +'%Y_%m_%d-%H_%M_%S')"
SPK="wekan-sandstorm-${STAMP}.spk"
meteor-spk pack "$SPK"
# The Sandstorm package profile, the linux/x64 sockjs trim and a
# `spk pack` of the trimmed trees (not `meteor-spk pack`, which would
# rebuild the bundle and undo the trim). The script restores
# .meteor/packages and .meteor/versions when it ends.
bash releases/sandstorm-slim-spk.sh "$SPK"
# Report the packed size and warn if it exceeds Cloudflare's 100 MB upload
# cap (a proxied browser upload to a Cloudflare-fronted Sandstorm 413s above
# this; install via a DNS-only host / SSH tunnel, or trim further).
@@ -124,7 +131,7 @@ jobs:
ls -la "$SPK"
echo "spk=$SPK" >> "$GITHUB_OUTPUT"
- uses: actions/upload-artifact@v7
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: ${{ steps.build.outputs.spk }}
path: ${{ steps.build.outputs.spk }}
@@ -134,6 +141,7 @@ jobs:
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
STEP_BUILD_SPK: ${{ steps.build.outputs.spk }}
run: |
set -euo pipefail
TAG="$(gh release list --limit 1 --json tagName --jq '.[0].tagName' 2>/dev/null || true)"
@@ -141,5 +149,5 @@ jobs:
echo "::error::No existing GitHub Release found to attach the .spk to."
exit 1
fi
echo "Uploading ${{ steps.build.outputs.spk }} to the newest release: $TAG"
gh release upload --repo "${GITHUB_REPOSITORY:-wekan/wekan}" "$TAG" "${{ steps.build.outputs.spk }}" --clobber
echo "Uploading ${STEP_BUILD_SPK} to the newest release: $TAG"
gh release upload --repo "${GITHUB_REPOSITORY:-wekan/wekan}" "$TAG" "${STEP_BUILD_SPK}" --clobber
+392
View File
@@ -0,0 +1,392 @@
name: Single-file WeKan
# One executable per platform (docs/Platforms/Single-File.md): that
# platform's published WeKan bundle - its Node.js, FerretDB and native
# addons, all built where they run - repackaged by releases/single-file/
# build.mjs as a Node.js single executable application whose ZIP Node mounts
# as a virtual file system. Nothing is compiled here.
#
# Per platform: verify the bundle's checksum, build the single file (on a Mac
# for macOS, which has to re-sign it), start it on a runner of that platform
# where GitHub has one (with a browser test on amd64), and attach it to the
# release. A platform whose bundle the release does not have yet - the BSDs
# until their Node.js is published, Android until it has a bundle - is
# SKIPPED, not failed, and starts building by itself once it does.
on:
workflow_dispatch:
inputs:
tag:
description: 'Existing WeKan release tag (empty = latest published release)'
type: string
required: false
publish:
description: 'Attach the single files and checksums to that release'
type: boolean
default: true
only:
description: 'JSON target list, e.g. ["amd64","win64"] (empty = all)'
type: string
default: ''
missing_only:
description: 'Only the platforms whose single file the release does not have yet'
type: boolean
default: false
workflow_call:
inputs:
tag:
type: string
required: true
publish:
type: boolean
default: true
only:
type: string
default: ''
missing_only:
type: boolean
default: false
permissions:
contents: read
concurrency:
group: wekan-single-file-${{ inputs.tag || github.ref }}
cancel-in-progress: false
jobs:
plan:
runs-on: ubuntu-24.04
timeout-minutes: 10
outputs:
tag: ${{ steps.plan.outputs.tag }}
matrix: ${{ steps.plan.outputs.matrix }}
any: ${{ steps.plan.outputs.any }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- id: plan
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ inputs.tag }}
ONLY: ${{ inputs.only }}
MISSING_ONLY: ${{ inputs.missing_only }}
run: |
set -euo pipefail
tag="${TAG:-$(gh release view --repo "$GITHUB_REPOSITORY" --json tagName -q .tagName)}"
echo "tag=$tag" >> "$GITHUB_OUTPUT"
: > existing.txt
if [ "$MISSING_ONLY" = true ]; then
gh release view "$tag" --repo "$GITHUB_REPOSITORY" --json assets -q '.assets[].name' > existing.txt
fi
matrix="$(VERSION="${tag#v}" python3 -c '
import json, os
targets = json.load(open("releases/single-file/targets.json"))
only = json.loads(os.environ.get("ONLY") or "[]")
if only:
targets = [t for t in targets if t["target"] in only]
existing = set(open("existing.txt").read().split())
def name(t):
ext = ".exe" if t["target"].startswith("win") else ""
return "wekan-%s-%s-single-file%s" % (os.environ["VERSION"], t["target"], ext)
targets = [t for t in targets if not (name(t) in existing and name(t) + ".sha256sum" in existing)]
print(json.dumps({"include": targets}))')"
echo "matrix=$matrix" >> "$GITHUB_OUTPUT"
echo "any=$([ "$matrix" = '{"include": []}' ] && echo false || echo true)" >> "$GITHUB_OUTPUT"
echo "Release $tag; targets: $matrix"
build:
needs: plan
if: needs.plan.outputs.any == 'true'
name: Build ${{ matrix.target }}
runs-on: ${{ matrix.build_on }}
timeout-minutes: 60
permissions:
contents: write
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.plan.outputs.matrix) }}
env:
TAG: ${{ needs.plan.outputs.tag }}
TARGET: ${{ matrix.target }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
ref: ${{ needs.plan.outputs.tag }}
- name: Fetch and verify ${{ matrix.target }}'s published bundle
id: fetch
env:
GH_TOKEN: ${{ github.token }}
ZIP_PATTERN: ${{ matrix.zip }}
run: |
set -euo pipefail
version="${TAG#v}"
zip="${ZIP_PATTERN//\{version\}/$version}"
if ! gh release download "$TAG" --repo "$GITHUB_REPOSITORY" --pattern "$zip" --pattern "$zip.sha256sum"; then
echo "::notice::$TARGET is not built this release: $TAG has no $zip."
echo "skip=true" >> "$GITHUB_OUTPUT"
exit 0
fi
[ -f "$zip" ] || { echo "::notice::$TARGET: $TAG has no $zip."; echo "skip=true" >> "$GITHUB_OUTPUT"; exit 0; }
[ -f "$zip.sha256sum" ] || { echo "::error::$zip has no published .sha256sum; a single file is never built from an unverified bundle."; exit 1; }
if command -v sha256sum >/dev/null; then sha256sum -c "$zip.sha256sum"; else shasum -a 256 -c "$zip.sha256sum"; fi
unzip -q "$zip"
rm -f "$zip"
node_bin=bundle/node
[ -f "$node_bin" ] || node_bin=bundle/node.exe
node_version="$(grep -aoE 'nodejs\.org/download/release/v[0-9]+\.[0-9]+\.[0-9]+/' "$node_bin" | head -n1 | sed -E 's#.*/(v[0-9.]+)/#\1#')"
[ -n "$node_version" ] || { echo "::error::Could not read the Node.js version of $node_bin."; exit 1; }
echo "node_version=$node_version" >> "$GITHUB_OUTPUT"
echo "version=$version" >> "$GITHUB_OUTPUT"
echo "$TARGET: bundle Node.js $node_version"
# The SEA blob format belongs to one Node.js version: the builder runs
# on the same version the bundle embeds (build.mjs checks it too).
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
if: steps.fetch.outputs.skip != 'true'
with:
node-version: ${{ steps.fetch.outputs.node_version }}
# A 32-bit target's SEA blob must be written by a 32-bit Node.js: the blob
# stores its lengths as size_t (build.mjs refuses otherwise). The i386
# bundle's Node.js, of the same version, writes it for every 32-bit
# target - win32, i386 and 32-bit ARM read the same little-endian format.
- name: A 32-bit Node.js to build a 32-bit target
id: host32
if: steps.fetch.outputs.skip != 'true' && contains(fromJSON('["win32","i386","armv7","armhf","armv6"]'), matrix.target)
env:
GH_TOKEN: ${{ github.token }}
VERSION: ${{ steps.fetch.outputs.version }}
run: |
set -euo pipefail
zip="wekan-${VERSION}-i386.zip"
mkdir -p host32
gh release download "$TAG" --repo "$GITHUB_REPOSITORY" --pattern "$zip" --pattern "$zip.sha256sum" --dir host32 \
|| { echo "::error::$TARGET needs the i386 bundle's Node.js to build, and $TAG has no $zip."; exit 1; }
(cd host32 && sha256sum -c "$zip.sha256sum" && unzip -q "$zip" 'bundle/node' 'bundle/node-runtime/*' && rm -f "$zip")
sudo dpkg --add-architecture i386
sudo apt-get update -qq
sudo apt-get install -y -qq libc6:i386 libstdc++6:i386 >/dev/null
host="$PWD/host32/bundle/node"
[ -d host32/bundle/node-runtime ] && echo "libs=$PWD/host32/bundle/node-runtime" >> "$GITHUB_OUTPUT"
echo "node=$host" >> "$GITHUB_OUTPUT"
# A 32-bit ELF target's blob is injected in a 64-bit process, by
# LIEF 1.0.0 - the version Node.js vendors - not inside this 32-bit
# Node.js, which ran out of memory doing it (build.mjs, seaSplit).
if [ "$TARGET" != win32 ]; then
python3 -m venv "$RUNNER_TEMP/lief"
"$RUNNER_TEMP/lief/bin/python" -m pip install --quiet 'lief==1.0.0'
echo "python=$RUNNER_TEMP/lief/bin/python" >> "$GITHUB_OUTPUT"
fi
- name: Build the single file
id: build
if: steps.fetch.outputs.skip != 'true'
env:
VERSION: ${{ steps.fetch.outputs.version }}
HOST_NODE: ${{ steps.host32.outputs.node }}
HOST_LIBS: ${{ steps.host32.outputs.libs }}
SEA_PYTHON: ${{ steps.host32.outputs.python }}
run: |
set -euo pipefail
case "$TARGET" in win*) ext=.exe ;; *) ext= ;; esac
out="wekan-${VERSION}-${TARGET}-single-file${ext}"
# The i386 Node.js finds its own libraries; the libraries are ignored by
# the x64 Node.js that runs build.mjs (another architecture).
# Exit 3: this Node.js is one LIEF cannot inject the SEA into
# correctly (releases/single-file/sea-inject.mjs says which and why).
# That is a missing single file, not a failed release.
rc=0
LD_LIBRARY_PATH="${HOST_LIBS:-${LD_LIBRARY_PATH:-}}" \
node releases/single-file/build.mjs --bundle bundle --target "$TARGET" --version "$VERSION" --out "$out" \
${HOST_NODE:+--host-node "$HOST_NODE"} 2> build.err || rc=$?
cat build.err >&2
rm -rf bundle
if [ "$rc" = 3 ]; then
reason="$(sed -n 's/^single-file build: //p' build.err | head -n1)"
echo "::notice::$reason"
printf '%s\n' "$reason" > "single-file-skipped-$TARGET.txt"
rm -f build.err; exit 0
fi
rm -f build.err
[ "$rc" = 0 ] || exit "$rc"
ls -la "$out" "$out.sha256sum"
# A target that was not built still leaves an artifact - one line saying
# why - so its Start job finds something to read instead of logging an
# "Artifact not found" error for a release that is fine.
- name: Say why there is no single file
if: steps.fetch.outputs.skip == 'true'
run: |
[ -f "single-file-skipped-$TARGET.txt" ] || echo "$TARGET: this release has no bundle to build it from." > "single-file-skipped-$TARGET.txt"
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: single-file-${{ matrix.target }}
path: |
wekan-*-${{ matrix.target }}-single-file*
single-file-skipped-${{ matrix.target }}.txt
if-no-files-found: error
retention-days: 3
compression-level: 0
smoke:
needs: [plan, build]
if: ${{ !cancelled() && needs.plan.outputs.any == 'true' }}
name: Start ${{ matrix.target }}
runs-on: ${{ matrix.smoke == 'none' && 'ubuntu-24.04' || matrix.smoke }}
timeout-minutes: 45
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.plan.outputs.matrix) }}
env:
TARGET: ${{ matrix.target }}
defaults:
run:
shell: bash
steps:
- name: Get the single file
id: get
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
continue-on-error: true
with:
name: single-file-${{ matrix.target }}
path: sf
- name: Is there one to start here?
id: what
env:
STEP_GET_OUTCOME: ${{ steps.get.outcome }}
MATRIX_SMOKE: ${{ matrix.smoke }}
run: |
set -euo pipefail
if [ "${STEP_GET_OUTCOME}" != success ]; then
echo "run=false" >> "$GITHUB_OUTPUT"; echo "::notice::$TARGET was not built this release."; exit 0
fi
if [ -f "sf/single-file-skipped-$TARGET.txt" ]; then
echo "run=false" >> "$GITHUB_OUTPUT"; echo "::notice::$(cat "sf/single-file-skipped-$TARGET.txt")"; exit 0
fi
if [ "${MATRIX_SMOKE}" = none ]; then
echo "run=false" >> "$GITHUB_OUTPUT"
echo "::notice::GitHub has no runner for $TARGET: the single file is published without being started (its bundle was tested where it was built)."
mkdir -p ok && echo "$TARGET untested" > "ok/$TARGET"; exit 0
fi
exe="$(ls sf/wekan-*-single-file* | grep -v '\.sha256sum$' | head -n1)"
chmod +x "$exe"
echo "exe=$PWD/$exe" >> "$GITHUB_OUTPUT"
echo "run=true" >> "$GITHUB_OUTPUT"
- name: Start it, as a user starts it
if: steps.what.outputs.run == 'true'
env:
EXE: ${{ steps.what.outputs.exe }}
run: |
set -euo pipefail
cd "$(dirname "$EXE")"
"$EXE" version
"$EXE" set PORT 3456
# Fast polling, so a browser test sees a change made in the database
# within its timeout; the default is 30 s.
"$EXE" set METEOR_POLLING_INTERVAL_MS 2000
"$EXE" start --background
for i in $(seq 1 120); do
code="$(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:3456/ || true)"
[ "$code" = 200 ] && break
sleep 3
done
"$EXE" status
if [ "$code" != 200 ]; then
echo "::error::$TARGET: WeKan did not answer HTTP 200 within 6 minutes."
tail -n 200 wekan-files/logs/wekan.log || true
exit 1
fi
ls wekan-files wekan-files/bin
echo "WeKan answered HTTP 200 from the $TARGET single file."
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
if: steps.what.outputs.run == 'true' && matrix.target == 'amd64'
with:
persist-credentials: false
ref: ${{ needs.plan.outputs.tag }}
path: src
- name: Browser test against the single file
if: steps.what.outputs.run == 'true' && matrix.target == 'amd64'
env:
EXE: ${{ steps.what.outputs.exe }}
run: |
set -euo pipefail
files="$(dirname "$EXE")/wekan-files"
cd src/tests/playwright
npm ci
npx playwright install --with-deps chromium
WEKAN_BASE_URL=http://localhost:3456 \
WEKAN_MONGO_URL=mongodb://127.0.0.1:27017/wekan \
WEKAN_SINGLE_FILE_FILES="$files" \
WEKAN_FILES_PATH="$files" \
WEKAN_PLAYWRIGHT_PROJECT=chromium \
npx playwright test specs/single-file.e2e.js specs/01-boards-users.e2e.js specs/07-attachments-links.e2e.js --reporter=line
- name: Stop it
if: always() && steps.what.outputs.run == 'true'
env:
EXE: ${{ steps.what.outputs.exe }}
run: |
cd "$(dirname "$EXE")"
"$EXE" stop || true
"$EXE" status || true
[ -f wekan-files/logs/wekan.log ] && tail -n 60 wekan-files/logs/wekan.log || true
mkdir -p ok && echo "$TARGET started" > "ok/$TARGET"
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
if: success() && (steps.what.outputs.run == 'true' || matrix.smoke == 'none')
with:
name: single-file-ok-${{ matrix.target }}
path: |
ok
sf/ok
if-no-files-found: ignore
retention-days: 3
publish:
needs: [plan, build, smoke]
if: ${{ !cancelled() && inputs.publish && needs.plan.outputs.any == 'true' }}
runs-on: ubuntu-24.04
timeout-minutes: 60
permissions:
contents: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
pattern: single-file-*
path: artifacts
- name: Attach every single file that started, or has no runner to start on
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ needs.plan.outputs.tag }}
run: |
set -euo pipefail
shopt -s nullglob
published=0
for dir in artifacts/single-file-*; do
target="${dir#artifacts/single-file-}"
case "$target" in ok-*) continue ;; esac
# Not built, and its build job said why in a notice already.
[ -f "$dir/single-file-skipped-$target.txt" ] && continue
if [ ! -d "artifacts/single-file-ok-$target" ]; then
echo "::warning::$target: not published - it did not start on its runner."
continue
fi
for f in "$dir"/wekan-*-single-file*; do
case "$f" in *.sha256sum) continue ;; esac
(cd "$dir" && sha256sum -c "$(basename "$f").sha256sum")
bash releases/github-release-upload.sh "$GITHUB_REPOSITORY" "$TAG" "$f" "$f.sha256sum"
published=$((published + 1))
done
done
echo "Published $published single files to $TAG."
+141
View File
@@ -0,0 +1,141 @@
name: UCS App Center
run-name: UCS ${{ inputs.version || 'latest' }}
on:
workflow_dispatch:
inputs:
version:
description: 'WeKan release version without v; leave empty for latest published release'
required: false
type: string
permissions:
contents: read
# This publisher is started separately with workflow_dispatch. Do not race
# creation of a portal version or overwrite an image built by another attempt.
concurrency:
group: wekan-ucs-publish
cancel-in-progress: false
jobs:
publish:
runs-on: ubuntu-latest
timeout-minutes: 45
env:
VERSION: ${{ inputs.version }}
UCS_SOURCE_APP: ${{ vars.UCS_SOURCE_APP }}
UCS_TARGET_UCS: ${{ vars.UCS_TARGET_UCS }}
steps:
- name: Check UCS publishing configuration
id: config
env:
PROVIDER_USER: ${{ secrets.UNIVENTION_APP_PROVIDER_USER }}
PROVIDER_PASSWORD: ${{ secrets.UNIVENTION_APP_PROVIDER_PASSWORD }}
run: |
set -eu
if [ -z "$PROVIDER_USER" ] || [ -z "$PROVIDER_PASSWORD" ] || [ -z "$UCS_SOURCE_APP" ] || [ -z "$UCS_TARGET_UCS" ]; then
echo '::notice::UCS publishing not configured. See docs/Platforms/FOSS/Container/Docker/UCS/UCS-upgrades.md.'
echo 'enabled=false' >> "$GITHUB_OUTPUT"
else
echo 'enabled=true' >> "$GITHUB_OUTPUT"
fi
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
if: steps.config.outputs.enabled == 'true'
- name: Resolve published WeKan and FerretDB versions
if: steps.config.outputs.enabled == 'true'
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
mkdir -p "$GITHUB_WORKSPACE/.tools/tmp"
echo "TMPDIR=$GITHUB_WORKSPACE/.tools/tmp" >> "$GITHUB_ENV"
if [ -n "$VERSION" ]; then
python3 -c 'import sys; sys.path.insert(0,"releases"); import importlib; importlib.import_module("ucs-release").version(sys.argv[1])' "$VERSION"
gh release view "v$VERSION" --repo wekan/wekan --json tagName,isDraft,isPrerelease > .tools/tmp/ucs-wekan-release.json
else
gh release view --repo wekan/wekan --json tagName,isDraft,isPrerelease > .tools/tmp/ucs-wekan-release.json
fi
gh release view --repo wekan/FerretDB --json tagName,isDraft,isPrerelease > .tools/tmp/ucs-ferretdb-release.json
python3 releases/ucs-release.py resolve \
--wekan-release .tools/tmp/ucs-wekan-release.json \
--ferretdb-release .tools/tmp/ucs-ferretdb-release.json
- name: Report selected versions
if: steps.config.outputs.enabled == 'true'
run: |
printf 'WeKan: ghcr.io/wekan/wekan:v%s\nFerretDB: %s\nUCS section: %s\n' \
"$VERSION" "$UCS_FERRETDB_IMAGE" "$UCS_TARGET_UCS" >> "$GITHUB_STEP_SUMMARY"
- name: Get the official Provider Portal CLI
if: steps.config.outputs.enabled == 'true'
run: |
bash releases/fetch.sh -o .tools/univention-appcenter-control \
https://provider-portal.software-univention.de/appcenter-selfservice/univention-appcenter-control
# The official CLI imports distutils.version (Python 3.12 removed it).
python3 -m pip install 'setuptools==80.9.0'
python3 .tools/univention-appcenter-control upload --help
- name: Check whether UCS already published this version
id: portal
if: steps.config.outputs.enabled == 'true'
env:
UNIVENTION_APP_PROVIDER_USER: ${{ secrets.UNIVENTION_APP_PROVIDER_USER }}
UNIVENTION_APP_PROVIDER_PASSWORD: ${{ secrets.UNIVENTION_APP_PROVIDER_PASSWORD }}
run: python3 releases/ucs-release.py plan --version "$VERSION"
- name: Checkout the app definition
if: steps.config.outputs.enabled == 'true' && steps.portal.outputs.skip != 'true'
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: wekan/univention
ref: master
path: univention
persist-credentials: false
- name: Prepare versioned compose and migration hook
id: prepare
if: steps.config.outputs.enabled == 'true' && steps.portal.outputs.skip != 'true'
run: python3 releases/ucs-release.py prepare --version "$VERSION"
# The standard WeKan release already published the migration tools inside
# the app image on Docker Hub, GHCR and Quay. upload checks anonymous pulls
# with mirror fallback and runs start.sh --check before any portal write.
- name: Upload or finish the UCS portal draft
id: upload
if: steps.config.outputs.enabled == 'true' && steps.portal.outputs.skip != 'true'
env:
UNIVENTION_APP_PROVIDER_USER: ${{ secrets.UNIVENTION_APP_PROVIDER_USER }}
UNIVENTION_APP_PROVIDER_PASSWORD: ${{ secrets.UNIVENTION_APP_PROVIDER_PASSWORD }}
run: python3 releases/ucs-release.py upload --version "$VERSION"
- name: Keep the exact submitted app definition
if: steps.upload.outputs.result == 'uploaded'
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: ucs-${{ env.VERSION }}
path: .tools/ucs-release/component
- name: Report UCS result
if: always()
env:
ENABLED: ${{ steps.config.outputs.enabled }}
EXISTING: ${{ steps.portal.outputs.result }}
UPLOADED: ${{ steps.upload.outputs.result }}
RESULT: ${{ job.status }}
run: |
if [ "$ENABLED" != true ]; then
echo 'UCS: not configured; no upload attempted.' >> "$GITHUB_STEP_SUMMARY"
elif [ "$RESULT" != success ]; then
echo 'UCS: FAILED. See this job; portal publication was not verified.' >> "$GITHUB_STEP_SUMMARY"
elif [ "$EXISTING" = published ]; then
echo 'UCS: already published; left unchanged.' >> "$GITHUB_STEP_SUMMARY"
elif [ "$UPLOADED" = uploaded ]; then
echo 'UCS: draft uploaded and read back. UCS testing and provider/Univention approval remain.' >> "$GITHUB_STEP_SUMMARY"
else
echo 'UCS: no verified upload.' >> "$GITHUB_STEP_SUMMARY"
fi
+14 -6
View File
@@ -38,36 +38,44 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout newest WeKan content
uses: actions/checkout@v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: wekan/wekan
ref: ${{ inputs.source_ref }}
path: source
- name: Checkout wekan/${{ matrix.repository }}
uses: actions/checkout@v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: wekan/${{ matrix.repository }}
token: ${{ secrets.WEKAN_REPO_TOKEN }}
ref: main
path: target
fetch-depth: 0
# Shallow: one commit is added on top of main and pushed. The full
# history of these repositories is ~160 MB each and is not read.
- name: Copy WeKan and restore ${{ matrix.package }} package identities
env:
MATRIX_PACKAGE: ${{ matrix.package }}
MATRIX_TITLE: ${{ matrix.title }}
run: |
source/releases/prepare-variant-repo.sh \
source target "${{ matrix.package }}" "${{ matrix.title }}"
source target "${MATRIX_PACKAGE}" "${MATRIX_TITLE}"
- name: Commit and push wekan/${{ matrix.repository }}
working-directory: target
env:
MATRIX_REPOSITORY: ${{ matrix.repository }}
INPUT_SOURCE_REF: ${{ inputs.source_ref }}
MATRIX_PACKAGE: ${{ matrix.package }}
run: |
set -euo pipefail
git config user.name 'wekan-release-bot'
git config user.email 'release@wekan.team'
git add -A
if git diff --cached --quiet; then
echo "wekan/${{ matrix.repository }} already matches ${{ inputs.source_ref }}."
echo "wekan/${MATRIX_REPOSITORY} already matches ${INPUT_SOURCE_REF}."
exit 0
fi
git commit -m "Sync newest WeKan as ${{ matrix.package }}"
git commit -m "Sync newest WeKan as ${MATRIX_PACKAGE}"
git push origin HEAD:main
+66 -38
View File
@@ -46,8 +46,9 @@ on:
default: true
type: boolean
# Read-only by default; the job that writes is granted it below.
permissions:
contents: write
contents: read
concurrency:
group: wekan-windows-exe-${{ inputs.tag || github.ref }}
@@ -63,21 +64,30 @@ jobs:
version: ${{ steps.find.outputs.version }}
available: ${{ steps.find.outputs.available }}
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- id: find
env:
GH_TOKEN: ${{ github.token }}
INPUT_TAG: ${{ inputs.tag }}
GITHUB_CTX_REPOSITORY: ${{ github.repository }}
run: |
set -euo pipefail
tag='${{ inputs.tag }}'
[ -n "$tag" ] || tag="$(gh release list --repo "${{ github.repository }}" \
tag="${INPUT_TAG}"
[ -n "$tag" ] || tag="$(gh release list --repo "${GITHUB_CTX_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
# The list is read whole BEFORE matching. `gh … | grep -q` lets grep exit
# at the first match while gh is still writing; gh gets SIGPIPE, pipefail
# makes the pipeline fail, and a PUBLISHED asset was reported missing - the
# v12.39 Mac apps were skipped that way, with the bundles already attached.
assets="$(gh release view "$tag" --repo "${GITHUB_CTX_REPOSITORY}" --json assets \
--jq '.assets[].name')"
if grep -qx "wekan-${version}-win64.zip" <<< "$assets"; then
available=true
else
echo "::notice::wekan-${version}-win64.zip is not published; there is no Windows bundle to wrap."
@@ -87,28 +97,35 @@ jobs:
echo "available=$available" >> "$GITHUB_OUTPUT"
build:
permissions:
contents: write
name: self-extracting win64 EXE
needs: release
if: ${{ needs.release.outputs.available == 'true' }}
runs-on: windows-latest
timeout-minutes: 90
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Fetch and verify the published win64 bundle
shell: pwsh
env:
GH_TOKEN: ${{ github.token }}
NEEDS_RELEASE_VERSION: ${{ needs.release.outputs.version }}
GITHUB_CTX_REPOSITORY: ${{ github.repository }}
NEEDS_RELEASE_TAG: ${{ needs.release.outputs.tag }}
run: |
$ErrorActionPreference = 'Stop'
$zip = 'wekan-${{ needs.release.outputs.version }}-win64.zip'
$assets = gh release view --repo '${{ github.repository }}' `
'${{ needs.release.outputs.tag }}' --json assets --jq '.assets[].name'
$zip = "wekan-$($env:NEEDS_RELEASE_VERSION)-win64.zip"
$assets = gh release view --repo $env:GITHUB_CTX_REPOSITORY `
$env:NEEDS_RELEASE_TAG --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."
throw "$zip is not on $($env:NEEDS_RELEASE_TAG). The single EXE can only wrap a published win64 bundle."
}
gh release download '${{ needs.release.outputs.tag }}' `
--repo '${{ github.repository }}' --pattern "$zip*" --clobber
gh release download $env:NEEDS_RELEASE_TAG `
--repo $env:GITHUB_CTX_REPOSITORY --pattern "$zip*" --clobber
# The ZIP is the payload, so it is checked as a whole against the
# checksum published beside it rather than unpacked here.
if (Test-Path "$zip.sha256sum" -PathType Leaf) {
@@ -140,24 +157,26 @@ jobs:
python releases/check-telemetry.py --archive $zip
if ($LASTEXITCODE -ne 0) { throw 'Telemetry audit rejected the Windows bundle.' }
- uses: actions/setup-node@v7
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: 26
- name: Use npm 12.0.2
run: npm install --global npm@12.0.2
- name: Use release npm version
run: bash releases/use-release-npm.sh
- name: Work out which members cannot be virtual
shell: pwsh
# The launcher unpacks these and nothing else. The list is computed from
# the archive, not written by hand, so a bundle that gains an addon or a
# database tool does not silently lose it at runtime.
env:
NEEDS_RELEASE_VERSION: ${{ needs.release.outputs.version }}
run: |
$ErrorActionPreference = 'Stop'
node releases/append-windows-payload.mjs `
--manifest "wekan-${{ needs.release.outputs.version }}-win64.zip" `
--manifest "wekan-$($env:NEEDS_RELEASE_VERSION)-win64.zip" `
--header wekan-real-files.h
- uses: ilammy/msvc-dev-cmd@v1
- uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # v1.13.0
- name: Compile the native launcher
shell: cmd
@@ -169,9 +188,11 @@ jobs:
- name: Append the bundle and its trailer to the launcher
shell: pwsh
env:
NEEDS_RELEASE_VERSION: ${{ needs.release.outputs.version }}
run: |
$ErrorActionPreference = 'Stop'
$version = '${{ needs.release.outputs.version }}'
$version = $env:NEEDS_RELEASE_VERSION
New-Item -ItemType Directory -Force dist | Out-Null
node releases/append-windows-payload.mjs `
--launcher wekan-launcher.exe `
@@ -224,6 +245,7 @@ jobs:
$global:LASTEXITCODE = 0
- name: Check that the single EXE starts, twice, without ever restarting
id: smoke
shell: pwsh
# Twice, because the two runs are different code paths: the first
# unpacks the payload into wekan-app, the second must find it already
@@ -234,9 +256,11 @@ jobs:
# loop still answers on port 8080 eventually and a poll-until-ready
# check calls it healthy. This one fails on the restart line and on
# a SyntaxError, so a bundle Node.js cannot load can no longer ship.
env:
NEEDS_RELEASE_VERSION: ${{ needs.release.outputs.version }}
run: |
$ErrorActionPreference = 'Stop'
$version = '${{ needs.release.outputs.version }}'
$version = $env:NEEDS_RELEASE_VERSION
# Run from its own directory: the EXE unpacks ~700 MB beside itself
# and dist/ is what gets uploaded as the release artifact.
New-Item -ItemType Directory -Force smoke | Out-Null
@@ -318,30 +342,34 @@ jobs:
Start-Once 'second' 120
- uses: actions/upload-artifact@v7
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
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
# Attached HERE, by the job that built and smoke-tested the EXE, the
# moment it passed. It used to go up from a separate publish job that
# downloaded this artifact first. always() + the smoke test's outcome: a
# cancel that arrives after the EXE was built and checked still attaches
# it. bash (Git Bash on this runner), because the shared uploader is a
# bash script; it does not rely on Windows' own `timeout` command.
- name: Attach the EXE and checksum to the release
if: ${{ always() && steps.smoke.outcome == 'success' && inputs.publish != false }}
shell: bash
env:
GH_TOKEN: ${{ github.token }}
GITHUB_CTX_REPOSITORY: ${{ github.repository }}
NEEDS_RELEASE_TAG: ${{ needs.release.outputs.tag }}
run: |
set -euo pipefail
gh release upload --repo '${{ github.repository }}' \
'${{ needs.release.outputs.tag }}' assets/* --clobber
gh release view --repo '${{ github.repository }}' \
'${{ needs.release.outputs.tag }}' --json url --jq .url
shopt -s nullglob
files=(dist/*)
if [ "${#files[@]}" -eq 0 ]; then
echo "::error::dist/ holds no EXE to attach although the smoke test passed."
exit 1
fi
# Retried, bounded, and verified by name and size from the release side.
bash releases/github-release-upload.sh "${GITHUB_CTX_REPOSITORY}" "${NEEDS_RELEASE_TAG}" "${files[@]}"
gh release view --repo "${GITHUB_CTX_REPOSITORY}" \
"${NEEDS_RELEASE_TAG}" --json url --jq .url
+27 -27
View File
@@ -6,12 +6,12 @@
meteor-base@1.5.2
# Build system
ecmascript@0.19.2-beta360.3
standard-minifier-js@3.3.0-beta360.3
rspack@1.4.0-beta360.3
ecmascript@0.19.2-rc360.1
standard-minifier-js@3.3.0-rc360.1
rspack@1.4.0-rc360.1
# 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.4-beta360.3
modern-browsers@0.2.4-rc360.1
blaze@3.0.2
@@ -19,11 +19,11 @@ blaze@3.0.2
aldeed:collection2
aldeed:schema-index@4.0.0-beta.5
reywood:publish-composite
mongo@2.5.2-beta360.3
mongo@2.5.2-rc360.1
# Account system
accounts-password@3.3.2-beta360.3
accounts-2fa@3.1.1-beta360.3
accounts-password@3.3.2-rc360.1
accounts-2fa@3.1.1-rc360.1
accounts-express@1.0.0
communitypackages:core
communitypackages:unstyled
@@ -39,19 +39,19 @@ wekan-oidc
wekan-accounts-oidc
# Utilities
check@1.5.1-beta360.3
random@1.2.3-beta360.3
reactive-dict@1.3.3-beta360.3
session@1.2.4-beta360.3
tracker@1.3.5-beta360.3
check@1.5.1-rc360.1
random@1.2.3-rc360.1
reactive-dict@1.3.3-rc360.1
session@1.2.4-rc360.1
tracker@1.3.5-rc360.1
audit-argument-checks@1.0.8
# UI components
ostrio:i18n
reactive-var@1.0.13
shell-server@0.7.1-beta360.3
email@3.2.1-beta360.3
dynamic-import@0.7.5-beta360.3
shell-server@0.7.2-rc360.1
email@3.2.1-rc360.1
dynamic-import@0.8.1-rc360.1
wekan-markdown
quave:synced-cron
ostrio:cookies
@@ -59,21 +59,21 @@ ostrio:files
# lmieulet:meteor-coverage
meteortesting:mocha@3.0.0
matb33:collection-hooks
service-configuration@1.3.6-beta360.3
service-configuration@1.3.6-rc360.1
minifier-css@2.0.2
ejson@1.2.1-beta360.3
logging@1.3.7-beta360.3
ejson@1.4.1-rc360.1
logging@1.3.7-rc360.1
wekan-fullcalendar
wekan-fontawesome
accounts-google@1.4.2-beta360.3
accounts-github@1.5.2-beta360.3
accounts-facebook@1.3.5-beta360.3
accounts-twitter@1.5.3-beta360.3
accounts-meteor-developer@1.5.2-beta360.3
accounts-weibo@1.4.2-beta360.3
accounts-meetup@1.5.2-beta360.3
accounts-passwordless@3.1.2-beta360.3
instrumentation@0.0.1-beta360.3
accounts-google@1.4.2-rc360.1
accounts-github@1.5.2-rc360.1
accounts-facebook@1.3.5-rc360.1
accounts-twitter@1.5.3-rc360.1
accounts-meteor-developer@1.5.2-rc360.1
accounts-weibo@1.4.2-rc360.1
accounts-meetup@1.5.2-rc360.1
accounts-passwordless@3.1.2-rc360.1
instrumentation@0.0.1-rc360.1
# Prevent Meteor build/run tools from sending application package statistics.
package-stats-opt-out@1.0.8
+1 -1
View File
@@ -1 +1 @@
METEOR@3.6-beta.3
METEOR@3.6-rc.1
+74 -74
View File
@@ -1,130 +1,130 @@
accounts-2fa@3.1.1-beta360.3
accounts-base@3.4.0-beta360.3
accounts-2fa@3.1.1-rc360.1
accounts-base@3.4.0-rc360.1
accounts-express@1.0.0
accounts-facebook@1.3.5-beta360.3
accounts-github@1.5.2-beta360.3
accounts-google@1.4.2-beta360.3
accounts-meetup@1.5.2-beta360.3
accounts-meteor-developer@1.5.2-beta360.3
accounts-oauth@1.4.8-beta360.3
accounts-password@3.3.2-beta360.3
accounts-passwordless@3.1.2-beta360.3
accounts-twitter@1.5.3-beta360.3
accounts-weibo@1.4.2-beta360.3
accounts-facebook@1.3.5-rc360.1
accounts-github@1.5.2-rc360.1
accounts-google@1.4.2-rc360.1
accounts-meetup@1.5.2-rc360.1
accounts-meteor-developer@1.5.2-rc360.1
accounts-oauth@1.4.8-rc360.1
accounts-password@3.3.2-rc360.1
accounts-passwordless@3.1.2-rc360.1
accounts-twitter@1.5.3-rc360.1
accounts-weibo@1.4.2-rc360.1
aldeed:collection2@4.2.2
aldeed:schema-index@4.0.0
aldeed:simple-schema@1.13.1
allow-deny@2.1.1-beta360.3
allow-deny@2.1.2-rc360.1_1
audit-argument-checks@1.0.8
autoupdate@2.0.2-beta360.3
babel-compiler@7.15.2-beta360.3
autoupdate@2.0.2-rc360.1
babel-compiler@7.15.2-rc360.1
babel-runtime@1.5.2
base64@1.0.15-beta360.3
binary-heap@1.0.14-beta360.3
base64@1.0.15-rc360.1
binary-heap@1.0.14-rc360.1
blaze@3.0.3
blaze-tools@2.0.1
boilerplate-generator@2.2.0-beta360.3
boilerplate-generator@2.2.0-rc360.1
caching-compiler@2.0.1
caching-html-compiler@2.0.1
callback-hook@1.8.1-beta360.3
check@1.5.1-beta360.3
callback-hook@1.8.1-rc360.1
check@1.5.1-rc360.1
communitypackages:core@2.0.3
communitypackages:flow-routing@2.1.0
communitypackages:unstyled@2.0.1
core-runtime@1.0.0
ddp@1.4.3-beta360.3
ddp-client@3.4.2-beta360.3
ddp-common@1.4.6-beta360.3
ddp-rate-limiter@1.3.1-beta360.3
ddp-server@3.4.1-beta360.3
diff-sequence@1.1.4-beta360.3
dynamic-import@0.7.5-beta360.3
ecmascript@0.19.2-beta360.3
ddp@1.4.3-rc360.1
ddp-client@3.4.2-rc360.1
ddp-common@1.4.6-rc360.1
ddp-rate-limiter@1.3.1-rc360.1
ddp-server@3.6.0-rc360.1_1
diff-sequence@1.2.1-rc360.1
dynamic-import@0.8.1-rc360.1_1
ecmascript@0.19.2-rc360.1
ecmascript-runtime@0.8.3
ecmascript-runtime-client@0.13.0
ecmascript-runtime-server@0.11.1
ejson@1.2.1-beta360.3
email@3.2.1-beta360.3
ejson@1.4.1-rc360.1
email@3.2.1-rc360.1
es5-shim@4.8.1
facebook-oauth@1.11.7-beta360.3
facts-base@1.0.3-beta360.3
fetch@0.2.1-beta360.3
geojson-utils@1.0.13-beta360.3
github-oauth@1.4.3-beta360.3
google-oauth@1.4.6-beta360.3
facebook-oauth@1.12.1-rc360.1_1
facts-base@1.0.3-rc360.1
fetch@0.2.1-rc360.1
geojson-utils@1.0.13-rc360.1
github-oauth@1.4.3-rc360.1
google-oauth@1.5.1-rc360.1_1
hot-code-push@1.0.5
html-tools@2.0.1
htmljs@2.0.2
id-map@1.2.1-beta360.3
instrumentation@0.0.1-beta360.3
inter-process-messaging@0.1.4-beta360.3
id-map@1.2.1-rc360.1
instrumentation@0.0.1-rc360.1
inter-process-messaging@0.1.4-rc360.1
jquery@3.0.2
lai:collection-extensions@1.0.0
localstorage@1.2.1
logging@1.3.7-beta360.3
logging@1.3.7-rc360.1
matb33:collection-hooks@2.1.0
meetup-oauth@1.1.4-beta360.3
meteor@2.3.2-beta360.3
meetup-oauth@1.1.4-rc360.1
meteor@2.3.2-rc360.1
meteor-base@1.5.2
meteor-developer-oauth@1.3.4-beta360.3
meteor-developer-oauth@1.3.4-rc360.1
meteortesting:browser-tests@1.8.0
meteortesting:mocha@3.4.0
meteortesting:mocha-core@8.2.0
minifier-css@2.0.2
minifier-js@3.3.2-beta360.3
minimongo@2.2.1-beta360.3
modern-browsers@0.2.4-beta360.3
minifier-js@3.3.2-rc360.1
minimongo@2.2.1-rc360.1
modern-browsers@0.2.4-rc360.1
modules@0.20.3
modules-runtime@0.13.2
mongo@2.5.2-beta360.3
mongo@2.5.2-rc360.1
mongo-decimal@0.2.0
mongo-dev-server@1.1.1
mongo-id@1.0.10-beta360.3
npm-mongo@6.16.3
oauth@3.0.5-beta360.3
oauth1@1.5.4-beta360.3
mongo-id@1.0.10-rc360.1
npm-mongo@6.18.1-rc360.1
oauth@3.0.5-rc360.1
oauth1@1.5.4-rc360.1
oauth2@1.3.3
observe-sequence@2.0.1
ordered-dict@1.2.1-beta360.3
ordered-dict@1.2.1-rc360.1
ostrio:cookies@2.9.2
ostrio:cstorage@4.0.1
ostrio:files@3.0.1
ostrio:flow-router-extra@3.15.3
ostrio:i18n@3.2.1
package-stats-opt-out@1.0.8
promise@1.0.1-beta360.3
promise@1.0.1-rc360.1
quave:synced-cron@2.4.0
raix:eventemitter@1.0.0
random@1.2.3-beta360.3
rate-limit@1.2.1-beta360.3
random@1.2.3-rc360.1
rate-limit@1.2.1-rc360.1
react-fast-refresh@0.3.0
reactive-dict@1.3.3-beta360.3
reactive-dict@1.3.3-rc360.1
reactive-var@1.0.13
reload@1.3.3-beta360.3
retry@1.1.2-beta360.3
reload@1.3.3-rc360.1
retry@1.1.2-rc360.1
reywood:publish-composite@1.9.0
routepolicy@1.1.3-beta360.3
rspack@1.4.0-beta360.3
service-configuration@1.3.6-beta360.3
session@1.2.4-beta360.3
sha@1.0.11-beta360.3
shell-server@0.7.1-beta360.3
socket-stream-client@0.7.2-beta360.3
routepolicy@1.1.3-rc360.1
rspack@1.4.0-rc360.1
service-configuration@1.3.6-rc360.1
session@1.2.4-rc360.1
sha@1.0.11-rc360.1
shell-server@0.7.2-rc360.1_1
socket-stream-client@0.7.2-rc360.1
spacebars@2.0.1
spacebars-compiler@2.0.1
standard-minifier-js@3.3.0-beta360.3
standard-minifier-js@3.3.0-rc360.1
templating@1.4.5
templating-compiler@2.0.1
templating-runtime@2.0.2
templating-tools@2.0.1
tools-core@1.4.0-beta360.3
tracker@1.3.5-beta360.3
twitter-oauth@1.3.5-beta360.3
typescript@5.11.2-beta360.3
url@1.3.7-beta360.3
webapp@2.3.1-beta360.3
webapp-hashing@1.1.3-beta360.3
weibo-oauth@1.3.4-beta360.3
tools-core@1.4.0-rc360.1
tracker@1.3.5-rc360.1
twitter-oauth@1.3.5-rc360.1
typescript@5.11.2-rc360.1
url@1.3.7-rc360.1
webapp@2.3.1-rc360.1
webapp-hashing@1.1.3-rc360.1
weibo-oauth@1.3.4-rc360.1
wekan-accounts-cas@0.2.0
wekan-accounts-lockout@1.1.0
wekan-accounts-oidc@1.0.10
+1 -1
View File
@@ -8,7 +8,7 @@ npm-packages/
# would hide those suites, making `meteor test` report "0 passing".
/tests/
# Build/tool scratch space: a full Node 24 install (headers for EVERY openssl arch),
# Build/tool scratch space: a full Node 26 install (headers for EVERY openssl arch),
# a Go toolchain with its module/build caches, build logs and repro dirs. 20,535
# DIRECTORIES, none of it app code. Meteor's watcher takes one inotify watch per
# directory, and the per-user limit (fs.inotify.max_user_watches, 65536 here) is
+102 -27
View File
@@ -35,7 +35,7 @@ contributor — that offer is the mistake, not the rule.
The maintainer's half is the release flow, not taste. Releases here are frequent —
several a day when a fault is being chased — and `releases/release-all.sh` cuts one
from whatever is on `main`, taking the version from the `# Upcoming WeKan ® release`
section of `CHANGELOG.md`. Work parked on a branch is work that is not in the next
section of `CHANGELOG-big.md`. Work parked on a branch is work that is not in the next
release, and `tests/changelogEntriesBelongToTheirRelease.test.cjs` fails when a
released section links a commit that release does not contain.
@@ -340,7 +340,7 @@ directly after the merge.
the two patch repositories under `.tools/` — node-patches and mongo-tools-patches.
- Directory structure:
- `wekan` — this repo (https://github.com/wekan/wekan); see
`docs/DeveloperDocs/Directory-Structure.md`; `CHANGELOG.md` at root. It is at
`docs/DeveloperDocs/Directory-Structure.md`; `CHANGELOG-big.md` and `CHANGELOG.md` at root. It is at
`~/repos/wekan` on Linux, `~/Documents/repos/wekan` on macOS and
`Downloads\repos\wekan` on Windows. The maintainer commits on `main`; a
contributor works on a branch and opens a pull request — see "Where this
@@ -373,7 +373,7 @@ directly after the merge.
| --- | --- | --- | --- |
| `.tools/FerretDB` | wekan/FerretDB | `main-v1` | the FerretDB v1 fork WeKan ships as its default database |
| `.tools/node-patches` | wekan/node-patches | `main` | patches to upstream Node.js; builds the `node-<platform>` binaries the bundles, the Docker image and the snap embed |
| `.tools/mongo-tools-patches` | wekan/mongo-tools-patches | `main` | patches to the MongoDB Database Tools; builds `<tool>-<arch>` |
| `.tools/mongo-tools-patches` | wekan/mongo-tools-patches | `main` | patches to the MongoDB Database Tools; builds `mongo-tools-<arch>`, all eight tools as one program |
| `.tools/TSC` | Secretchronicles/TSC | `devel` | the game xet7 also maintains — same author and maintainer mode, its own GNU ChangeLog and release process |
| `.tools/sandstorm` | sandstorm-io/sandstorm | — | upstream Sandstorm, when present — somebody else's project, cloned for reference |
@@ -414,7 +414,7 @@ directly after the merge.
| Repository | File | Format |
| --- | --- | --- |
| `wekan/wekan` | `CHANGELOG.md` | the WeKan format this section describes: `# Status` (Platforms/Version/TODO Later nested as `<details>`), then `# v<MAJOR>.<MINOR> YYYY-MM-DD WeKan ® release` sections of `<details>` entries whose `<summary>` links the commit |
| `wekan/wekan` | `CHANGELOG-big.md` (and the generated `CHANGELOG.md`, below) | the WeKan format this section describes: `# Status` (Platforms/Version/TODO Later nested as `<details>`), then `# v<MAJOR>.<MINOR> YYYY-MM-DD WeKan ® release` sections of `<details>` entries whose `<summary>` links the commit |
| `wekan/node-patches` | `CHANGELOG.md` | the same WeKan format, with `# Upcoming node-patches release` |
| `wekan/mongo-tools-patches` | `CHANGELOG.md` | the same WeKan format, with `# Upcoming mongo-tools-patches release` |
| `wekan/FerretDB` | `CHANGELOG.md` | **upstream FerretDB's** format, not WeKan's: `## [v1.48.0](tag URL) (YYYY-MM-DD)` and `### New Features 🎉` / `### Fixed 🐛` / `### Other Changes 🤖` bullets ending `by @xet7. Thanks to xet7.` |
@@ -426,6 +426,52 @@ directly after the merge.
out and would break the tooling that parses them. When this file and the file being
edited disagree, **the file being edited wins** — and everything below in this
section is about the WeKan format specifically.
- **WeKan has TWO changelogs, and only one is written.** `CHANGELOG-big.md` is
the changelog: `# Status`, `# Upcoming WeKan ® release` and every release in
full, in the `<details>` format below - every rule in the rest of this section
is about it, and every entry, fix and release goes into it. `CHANGELOG.md` is
the SHORT changelog, generated from it by `node releases/changelog-short.mjs`
and never edited by hand:
| | `CHANGELOG.md` |
| --- | --- |
| Status, TODO Later | none |
| First | the unreleased `# Upcoming WeKan ® release`, when there is one: its **In short** paragraph only, then `[All changes so far](CHANGELOG-big.md#upcoming-wekan--release)` |
| Below it | the five newest `# v<MAJOR>.<MINOR> YYYY-MM-DD WeKan ® release` |
| Per release | its **In short** paragraph only, then `[All changes in v…](CHANGELOG-big.md#anchor)` |
| Markup | plain Markdown: no `<details>`, `<summary>`, `</summary>` or `</details>`, and every HTML link a Markdown link |
It exists because the big file is for whoever writes and audits a release,
and most readers - GitHub's front page, the Sandstorm App Market
(`sandstorm-pkgdef.capnp` embeds it), the Helm chart's changelog link - want
what changed lately, at a glance. `releases/release-all.sh` regenerates it
right after naming the new release, so it is in the same release commit, and
`releases/changelog.sh` does the same; `tests/changelogShort.test.cjs` fails
when it is not exactly what the generator writes. So: write
`CHANGELOG-big.md`, run the generator, commit both - and never edit
`CHANGELOG.md` to fix something, because the next run overwrites it.
- **"Update wekan changelog" means both files, in this order:**
1. Find what is not in the changelog yet: `git log` since the newest commit
`CHANGELOG-big.md`'s Upcoming section (or, when it has none, the newest
release's `Prepare v… release` commit) already covers, by anybody - a
merged pull request and the maintainer's own commits alike.
2. Check whether a release was cut since the last update (`release-all.sh`
renames Upcoming to `# v<NEW> …` and opens no new one). If there is no
`# Upcoming WeKan ® release` heading, add it from
[docs/DeveloperDocs/Changelog-Upcoming-Template.md](docs/DeveloperDocs/Changelog-Upcoming-Template.md)
above the newest release - never add an entry to a released section.
3. Write the entries into that Upcoming section of `CHANGELOG-big.md`, in the
format described below (subsections, topic groups, `<details>` entries
linking each commit), and update its **In short** paragraph so it still
names the release's main outcomes.
4. Run `node releases/changelog-short.mjs`: `CHANGELOG.md` then shows that
Upcoming **In short** first and the five newest releases below it, with the
longer details only in `CHANGELOG-big.md`.
5. Run the changelog tests - `tests/changelogShort.test.cjs`,
`tests/changelogFormat.test.cjs`,
`tests/changelogEntriesBelongToTheirRelease.test.cjs` (or
`node tests/run-node-suites.cjs changelog`) - and commit
`CHANGELOG-big.md` and `CHANGELOG.md` together.
- During development, add entries under a new `# Upcoming WeKan ® release` section above
the newest release (FerretDB uses `## Upcoming FerretDB release`; the patch repos use
`# Upcoming <repo> release`). Do **not** hand-edit
@@ -438,32 +484,39 @@ directly after the merge.
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-big.md stays small enough for GitHub to show it.** GitHub renders
Markdown up to 500 KiB; above that it shows only plain source text, and above
about 2 MB nothing at all ("we can't show files that are this big right
now"). The changelog reached 2.6 MB over 1,100 releases back to 2015 (#6580),
and keeping only the current month still came to 2.1 MB, because releases
here are frequent and large. So the cut is by SIZE: 450,000 bytes
(`BUDGET` in `releases/changelog-archive.mjs`), under GitHub's limit with
room for the Status links to grow:
| | |
| --- | --- |
| `CHANGELOG.md` | the current month, plus `# Status` (Platforms/TODO Later inside it), `# Upcoming` |
| `old-CHANGELOG/<year>/<MM>.md` | earlier months of the current year |
| `old-CHANGELOG/<year>.md` | years that are over, whole |
| `CHANGELOG-big.md` | `# Status` (Platforms/TODO Later inside it), `# Upcoming`, and the newest releases that fit the budget |
| `old-CHANGELOG/<year>/<MM>.md` | a month's releases, newest first; a month over the budget continues in `<MM>-part2.md`, `<MM>-part3.md`, ... |
| `old-CHANGELOG/<year>.md` | 2015–2025, 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 `# Status`'s "Newest WeKan at
these platforms" details links every one.
Past years 2015–2025 stay one file each because they are already small
(30–107 KB). Every archive part fits the budget too, so it renders on GitHub;
only a single release larger than the budget gets a part of its own and is
shown as text. Each archive opens with a **release count** — per month in a
year file, per day in a month part — a split month's parts link each other,
and a bullet in `# Status`'s "Newest WeKan at these platforms" details 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.
Run `node releases/changelog-archive.mjs` whenever CHANGELOG-big.md has grown -
after a release is the natural moment. It is idempotent — a run with nothing
to move only refreshes the tables and parts — and it decides from the FILE
rather than the clock, so two people running it agree.
`tests/changelogArchive.test.cjs` fails when CHANGELOG-big.md is over the budget.
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. There is
exactly ONE `#` heading before the releases: `# Status`. Platforms, Version
and TODO Later are `<details>` blocks nested inside it, not headings of
@@ -475,7 +528,9 @@ directly after the merge.
whose `<summary>` is `Newest WeKan at these platforms`, holding the line
`Newest WeKan at these platforms:` and the Install / Upgrade / Docs / Mac
ChangeLog bullets, plus the `Older releases:` bullet linking the per-year
archives. Then a `<details>` whose `<summary>` is `Version` holding
archives. Then a `<details>` whose `<summary>` is `Databases` listing
which databases WeKan supports and how each is tested, kept in step with
`docs/Databases/Supported.md`. Then a `<details>` whose `<summary>` is `Version` holding
"which WeKan version uses what". Then a `<details>` whose `<summary>` is
`TODO Later`, itself holding a `<details>` whose `<summary>` is `Carried
to a future release.` explaining the list, then one `<details>` per
@@ -555,15 +610,20 @@ directly after the merge.
`<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.
- **Neither CHANGELOG.md nor release notes may contain a binaries provenance
- **Neither changelog nor the release notes may contain a binaries provenance
section or a Platform/Bundle/Binary/From/Version/Checked/SHA256 table.**
Do not add "Binaries in these bundles" at the top or bottom of either.
Release notes summarize each Translations group as only a list of updated
languages. Add `**Languages updated:**` with comma-separated full language
names below the group label; keep detailed entries in the changelog and audit.
When more than 20 languages were updated, too many to list, give only their
count instead: `**Languages updated:** 172 languages.`, which the release
notes show as "Updated translations at 172 languages." Every part of the
Upcoming section is optional: a part with nothing to say - no translations,
no security fixes - is left out rather than added empty.
Release notes have only In short, Security, Translations (language list),
the standard thanks line and More details at ChangeLog with the release
anchor link. Other details stay in CHANGELOG.md. Keep download
anchor link into CHANGELOG-big.md. Other details stay in CHANGELOG-big.md. Keep download
verification and `provenance.tsv` build artifacts for diagnostics; do not
append their contents or `releases/provenance-table.sh` output to notes.
- **Inside a subsection, entries are GROUPED BY TOPIC/AREA.** A release touches a handful
@@ -622,7 +682,7 @@ directly after the merge.
Because CRITICAL comes first, it keeps the `This release ` prefix; a following non-security
subsection becomes `and …` per the rule above.
- **TODO Later** — a `<details>` nested inside `# Status` near the TOP of
`CHANGELOG.md` (above the version sections), a triage backlog for open
`CHANGELOG-big.md` (above the version sections), a triage backlog for open
issues that were **investigated but not fixed here**, each
recorded with a concrete REASON so whoever picks it up next knows why. Use it when working
through open issues (the "Fix open issues" process): for each issue, either **fix it** (commit
@@ -703,7 +763,7 @@ have cost a released section its accuracy:
- **Work continues immediately after a release**, so an entry written right
after one has to go somewhere. `release-all.sh` renames `# Upcoming WeKan ®
release` to `# v<NEW> …` and stops there — it does **not** create a new,
empty `# Upcoming` section. CHANGELOG.md never carries an
empty `# Upcoming` section. CHANGELOG-big.md never carries an
`**In short:** nothing here yet.` placeholder sitting between releases; it
used to (via a now-deleted `releases/changelog-open-next.mjs`), and that
meant the file always had a section saying nothing, from the moment a
@@ -855,6 +915,21 @@ have cost a released section its accuracy:
sizes them at 24px, and keeps the icon and its red drop on one line at every width
(they are one icon) — the table itself stays percentage-sized with no fixed or
minimum width, so it fills the page at every browser width.
- **A report that arrives WITHOUT a GHSA is upgraded when its GHSA arrives.** The
stars follow `.tools/wekan.fi/hall-of-fame/Audit-Stars.md`: a GHSA with a
suggested fix is 5, a report sent to security@wekan.fi or any other way, with a
suggested fix, is 3. When that researcher is later able to add a GitHub security
advisory for the same report, the maintainer requests the CVE through the GHSA
process, and the row's 3 stars become 5. So whenever an AI updates or audits the
Hall of Fame, it checks each 3-star row whose Process says the report came
without a GHSA against [WeKan Security
Advisories](https://github.com/wekan/wekan/security/advisories): when a GHSA for
that report now exists, it sets the row to 5 stars on the index AND on the row's
own page, links the GHSA (and the CVE in the CVE cell once one is assigned),
rewrites the Process cell to say the report later arrived as a GHSA, and records
the change in Audit-Stars.md. Match by the report's subject and reporter, not by
a nearby advisory - a GHSA about something else is no upgrade. Until the GHSA
exists, the row stays at 3 and its Process cell says the GHSA is awaited.
- **On the Hall of Fame index page, the Process and Vulnerabilities cells are
collapsed.** Those two columns carry the prose — how the report arrived, and the
whole story of the vulnerability — so the page would be one wall of text and the
+8121
View File
File diff suppressed because it is too large. Load diff
+44 -23468
View File
File diff suppressed because it is too large. Load diff
+102 -27
View File
@@ -35,7 +35,7 @@ contributor — that offer is the mistake, not the rule.
The maintainer's half is the release flow, not taste. Releases here are frequent —
several a day when a fault is being chased — and `releases/release-all.sh` cuts one
from whatever is on `main`, taking the version from the `# Upcoming WeKan ® release`
section of `CHANGELOG.md`. Work parked on a branch is work that is not in the next
section of `CHANGELOG-big.md`. Work parked on a branch is work that is not in the next
release, and `tests/changelogEntriesBelongToTheirRelease.test.cjs` fails when a
released section links a commit that release does not contain.
@@ -340,7 +340,7 @@ directly after the merge.
the two patch repositories under `.tools/` — node-patches and mongo-tools-patches.
- Directory structure:
- `wekan` — this repo (https://github.com/wekan/wekan); see
`docs/DeveloperDocs/Directory-Structure.md`; `CHANGELOG.md` at root. It is at
`docs/DeveloperDocs/Directory-Structure.md`; `CHANGELOG-big.md` and `CHANGELOG.md` at root. It is at
`~/repos/wekan` on Linux, `~/Documents/repos/wekan` on macOS and
`Downloads\repos\wekan` on Windows. The maintainer commits on `main`; a
contributor works on a branch and opens a pull request — see "Where this
@@ -373,7 +373,7 @@ directly after the merge.
| --- | --- | --- | --- |
| `.tools/FerretDB` | wekan/FerretDB | `main-v1` | the FerretDB v1 fork WeKan ships as its default database |
| `.tools/node-patches` | wekan/node-patches | `main` | patches to upstream Node.js; builds the `node-<platform>` binaries the bundles, the Docker image and the snap embed |
| `.tools/mongo-tools-patches` | wekan/mongo-tools-patches | `main` | patches to the MongoDB Database Tools; builds `<tool>-<arch>` |
| `.tools/mongo-tools-patches` | wekan/mongo-tools-patches | `main` | patches to the MongoDB Database Tools; builds `mongo-tools-<arch>`, all eight tools as one program |
| `.tools/TSC` | Secretchronicles/TSC | `devel` | the game xet7 also maintains — same author and maintainer mode, its own GNU ChangeLog and release process |
| `.tools/sandstorm` | sandstorm-io/sandstorm | — | upstream Sandstorm, when present — somebody else's project, cloned for reference |
@@ -414,7 +414,7 @@ directly after the merge.
| Repository | File | Format |
| --- | --- | --- |
| `wekan/wekan` | `CHANGELOG.md` | the WeKan format this section describes: `# Status` (Platforms/Version/TODO Later nested as `<details>`), then `# v<MAJOR>.<MINOR> YYYY-MM-DD WeKan ® release` sections of `<details>` entries whose `<summary>` links the commit |
| `wekan/wekan` | `CHANGELOG-big.md` (and the generated `CHANGELOG.md`, below) | the WeKan format this section describes: `# Status` (Platforms/Version/TODO Later nested as `<details>`), then `# v<MAJOR>.<MINOR> YYYY-MM-DD WeKan ® release` sections of `<details>` entries whose `<summary>` links the commit |
| `wekan/node-patches` | `CHANGELOG.md` | the same WeKan format, with `# Upcoming node-patches release` |
| `wekan/mongo-tools-patches` | `CHANGELOG.md` | the same WeKan format, with `# Upcoming mongo-tools-patches release` |
| `wekan/FerretDB` | `CHANGELOG.md` | **upstream FerretDB's** format, not WeKan's: `## [v1.48.0](tag URL) (YYYY-MM-DD)` and `### New Features 🎉` / `### Fixed 🐛` / `### Other Changes 🤖` bullets ending `by @xet7. Thanks to xet7.` |
@@ -426,6 +426,52 @@ directly after the merge.
out and would break the tooling that parses them. When this file and the file being
edited disagree, **the file being edited wins** — and everything below in this
section is about the WeKan format specifically.
- **WeKan has TWO changelogs, and only one is written.** `CHANGELOG-big.md` is
the changelog: `# Status`, `# Upcoming WeKan ® release` and every release in
full, in the `<details>` format below - every rule in the rest of this section
is about it, and every entry, fix and release goes into it. `CHANGELOG.md` is
the SHORT changelog, generated from it by `node releases/changelog-short.mjs`
and never edited by hand:
| | `CHANGELOG.md` |
| --- | --- |
| Status, TODO Later | none |
| First | the unreleased `# Upcoming WeKan ® release`, when there is one: its **In short** paragraph only, then `[All changes so far](CHANGELOG-big.md#upcoming-wekan--release)` |
| Below it | the five newest `# v<MAJOR>.<MINOR> YYYY-MM-DD WeKan ® release` |
| Per release | its **In short** paragraph only, then `[All changes in v…](CHANGELOG-big.md#anchor)` |
| Markup | plain Markdown: no `<details>`, `<summary>`, `</summary>` or `</details>`, and every HTML link a Markdown link |
It exists because the big file is for whoever writes and audits a release,
and most readers - GitHub's front page, the Sandstorm App Market
(`sandstorm-pkgdef.capnp` embeds it), the Helm chart's changelog link - want
what changed lately, at a glance. `releases/release-all.sh` regenerates it
right after naming the new release, so it is in the same release commit, and
`releases/changelog.sh` does the same; `tests/changelogShort.test.cjs` fails
when it is not exactly what the generator writes. So: write
`CHANGELOG-big.md`, run the generator, commit both - and never edit
`CHANGELOG.md` to fix something, because the next run overwrites it.
- **"Update wekan changelog" means both files, in this order:**
1. Find what is not in the changelog yet: `git log` since the newest commit
`CHANGELOG-big.md`'s Upcoming section (or, when it has none, the newest
release's `Prepare v… release` commit) already covers, by anybody - a
merged pull request and the maintainer's own commits alike.
2. Check whether a release was cut since the last update (`release-all.sh`
renames Upcoming to `# v<NEW> …` and opens no new one). If there is no
`# Upcoming WeKan ® release` heading, add it from
[docs/DeveloperDocs/Changelog-Upcoming-Template.md](docs/DeveloperDocs/Changelog-Upcoming-Template.md)
above the newest release - never add an entry to a released section.
3. Write the entries into that Upcoming section of `CHANGELOG-big.md`, in the
format described below (subsections, topic groups, `<details>` entries
linking each commit), and update its **In short** paragraph so it still
names the release's main outcomes.
4. Run `node releases/changelog-short.mjs`: `CHANGELOG.md` then shows that
Upcoming **In short** first and the five newest releases below it, with the
longer details only in `CHANGELOG-big.md`.
5. Run the changelog tests - `tests/changelogShort.test.cjs`,
`tests/changelogFormat.test.cjs`,
`tests/changelogEntriesBelongToTheirRelease.test.cjs` (or
`node tests/run-node-suites.cjs changelog`) - and commit
`CHANGELOG-big.md` and `CHANGELOG.md` together.
- During development, add entries under a new `# Upcoming WeKan ® release` section above
the newest release (FerretDB uses `## Upcoming FerretDB release`; the patch repos use
`# Upcoming <repo> release`). Do **not** hand-edit
@@ -438,32 +484,39 @@ directly after the merge.
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-big.md stays small enough for GitHub to show it.** GitHub renders
Markdown up to 500 KiB; above that it shows only plain source text, and above
about 2 MB nothing at all ("we can't show files that are this big right
now"). The changelog reached 2.6 MB over 1,100 releases back to 2015 (#6580),
and keeping only the current month still came to 2.1 MB, because releases
here are frequent and large. So the cut is by SIZE: 450,000 bytes
(`BUDGET` in `releases/changelog-archive.mjs`), under GitHub's limit with
room for the Status links to grow:
| | |
| --- | --- |
| `CHANGELOG.md` | the current month, plus `# Status` (Platforms/TODO Later inside it), `# Upcoming` |
| `old-CHANGELOG/<year>/<MM>.md` | earlier months of the current year |
| `old-CHANGELOG/<year>.md` | years that are over, whole |
| `CHANGELOG-big.md` | `# Status` (Platforms/TODO Later inside it), `# Upcoming`, and the newest releases that fit the budget |
| `old-CHANGELOG/<year>/<MM>.md` | a month's releases, newest first; a month over the budget continues in `<MM>-part2.md`, `<MM>-part3.md`, ... |
| `old-CHANGELOG/<year>.md` | 2015–2025, 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 `# Status`'s "Newest WeKan at
these platforms" details links every one.
Past years 2015–2025 stay one file each because they are already small
(30–107 KB). Every archive part fits the budget too, so it renders on GitHub;
only a single release larger than the budget gets a part of its own and is
shown as text. Each archive opens with a **release count** — per month in a
year file, per day in a month part — a split month's parts link each other,
and a bullet in `# Status`'s "Newest WeKan at these platforms" details 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.
Run `node releases/changelog-archive.mjs` whenever CHANGELOG-big.md has grown -
after a release is the natural moment. It is idempotent — a run with nothing
to move only refreshes the tables and parts — and it decides from the FILE
rather than the clock, so two people running it agree.
`tests/changelogArchive.test.cjs` fails when CHANGELOG-big.md is over the budget.
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. There is
exactly ONE `#` heading before the releases: `# Status`. Platforms, Version
and TODO Later are `<details>` blocks nested inside it, not headings of
@@ -475,7 +528,9 @@ directly after the merge.
whose `<summary>` is `Newest WeKan at these platforms`, holding the line
`Newest WeKan at these platforms:` and the Install / Upgrade / Docs / Mac
ChangeLog bullets, plus the `Older releases:` bullet linking the per-year
archives. Then a `<details>` whose `<summary>` is `Version` holding
archives. Then a `<details>` whose `<summary>` is `Databases` listing
which databases WeKan supports and how each is tested, kept in step with
`docs/Databases/Supported.md`. Then a `<details>` whose `<summary>` is `Version` holding
"which WeKan version uses what". Then a `<details>` whose `<summary>` is
`TODO Later`, itself holding a `<details>` whose `<summary>` is `Carried
to a future release.` explaining the list, then one `<details>` per
@@ -555,15 +610,20 @@ directly after the merge.
`<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.
- **Neither CHANGELOG.md nor release notes may contain a binaries provenance
- **Neither changelog nor the release notes may contain a binaries provenance
section or a Platform/Bundle/Binary/From/Version/Checked/SHA256 table.**
Do not add "Binaries in these bundles" at the top or bottom of either.
Release notes summarize each Translations group as only a list of updated
languages. Add `**Languages updated:**` with comma-separated full language
names below the group label; keep detailed entries in the changelog and audit.
When more than 20 languages were updated, too many to list, give only their
count instead: `**Languages updated:** 172 languages.`, which the release
notes show as "Updated translations at 172 languages." Every part of the
Upcoming section is optional: a part with nothing to say - no translations,
no security fixes - is left out rather than added empty.
Release notes have only In short, Security, Translations (language list),
the standard thanks line and More details at ChangeLog with the release
anchor link. Other details stay in CHANGELOG.md. Keep download
anchor link into CHANGELOG-big.md. Other details stay in CHANGELOG-big.md. Keep download
verification and `provenance.tsv` build artifacts for diagnostics; do not
append their contents or `releases/provenance-table.sh` output to notes.
- **Inside a subsection, entries are GROUPED BY TOPIC/AREA.** A release touches a handful
@@ -622,7 +682,7 @@ directly after the merge.
Because CRITICAL comes first, it keeps the `This release ` prefix; a following non-security
subsection becomes `and …` per the rule above.
- **TODO Later** — a `<details>` nested inside `# Status` near the TOP of
`CHANGELOG.md` (above the version sections), a triage backlog for open
`CHANGELOG-big.md` (above the version sections), a triage backlog for open
issues that were **investigated but not fixed here**, each
recorded with a concrete REASON so whoever picks it up next knows why. Use it when working
through open issues (the "Fix open issues" process): for each issue, either **fix it** (commit
@@ -703,7 +763,7 @@ have cost a released section its accuracy:
- **Work continues immediately after a release**, so an entry written right
after one has to go somewhere. `release-all.sh` renames `# Upcoming WeKan ®
release` to `# v<NEW> …` and stops there — it does **not** create a new,
empty `# Upcoming` section. CHANGELOG.md never carries an
empty `# Upcoming` section. CHANGELOG-big.md never carries an
`**In short:** nothing here yet.` placeholder sitting between releases; it
used to (via a now-deleted `releases/changelog-open-next.mjs`), and that
meant the file always had a section saying nothing, from the moment a
@@ -855,6 +915,21 @@ have cost a released section its accuracy:
sizes them at 24px, and keeps the icon and its red drop on one line at every width
(they are one icon) — the table itself stays percentage-sized with no fixed or
minimum width, so it fills the page at every browser width.
- **A report that arrives WITHOUT a GHSA is upgraded when its GHSA arrives.** The
stars follow `.tools/wekan.fi/hall-of-fame/Audit-Stars.md`: a GHSA with a
suggested fix is 5, a report sent to security@wekan.fi or any other way, with a
suggested fix, is 3. When that researcher is later able to add a GitHub security
advisory for the same report, the maintainer requests the CVE through the GHSA
process, and the row's 3 stars become 5. So whenever an AI updates or audits the
Hall of Fame, it checks each 3-star row whose Process says the report came
without a GHSA against [WeKan Security
Advisories](https://github.com/wekan/wekan/security/advisories): when a GHSA for
that report now exists, it sets the row to 5 stars on the index AND on the row's
own page, links the GHSA (and the CVE in the CVE cell once one is assigned),
rewrites the Process cell to say the report later arrived as a GHSA, and records
the change in Audit-Stars.md. Match by the report's subject and reporter, not by
a nearby advisory - a GHSA about something else is no upgrade. Until the GHSA
exists, the row stays at 3 and its Process cell says the GHSA is awaited.
- **On the Hall of Fame index page, the Process and Vulnerabilities cells are
collapsed.** Those two columns carry the prose — how the report arrived, and the
whole story of the vulnerability — so the page would be one wall of text and the
+1 -1
View File
@@ -13,7 +13,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/Features/Login/Forgot-Password.md)
- For someone elses installed WeKan, see [Forgot Password](https://github.com/wekan/wekan/blob/main/docs/Features/Login-Page/Forgot-Password.md)
# Reporting about violations or something else
+195 -206
View File
@@ -11,27 +11,147 @@
# glibc cannot run the hard-float node-armv6 in the armv6 bundle. The docker job
# therefore asks this base what it publishes and drops a platform it lacks; see
# docs/Platforms/FOSS/Container/Docker/CPU-platforms.md.
# ── Stage 1: the release bundle, prepared ON THE BUILD MACHINE ───────────────
#
# Everything a release bundle needs is already done when this runs: each
# wekan-<version>-<arch>.zip was built by its own release-all.yml leg, with
# programs/server installed, its native modules built for that CPU, the
# security bumps applied, the tree trimmed and pruned, the telemetry check
# passed, and the Node.js for that CPU embedded as bundle/node - resolved by
# releases/resolve-node-source.sh and verified against the SHA256 its source
# published (embed-verified-node.sh / install-node-for-arch.sh).
#
# This image used to do all of it AGAIN, inside the target platform: apt-get
# a compiler toolchain, resolve and download Node.js, npm install, prune, bump,
# trim, prune, and purge the toolchain - seven times, six of them under QEMU.
# That was 27 of the docker job's 29 minutes in v12.32, for a result the .zip
# already was. So this stage runs with --platform=$BUILDPLATFORM, natively on
# the runner, for whichever TARGET it is building: it downloads that target's
# .zip, checks it against the release's own .sha256sum, re-runs the telemetry
# check, and checks that bundle/node really is a binary for the target CPU.
# Nothing in it executes target code, so nothing is emulated.
#
# The image's node is the bundle's own node: the same file the .zip ships,
# rather than a second download that had to agree with it.
FROM --platform=$BUILDPLATFORM debian:trixie AS bundle
ARG TARGETARCH
ARG TARGETVARIANT
ARG VERSION=12.94
ARG DEBIAN_FRONTEND=noninteractive
COPY --chmod=755 releases/check-telemetry.py /tmp/check-telemetry.py
COPY releases/risk-baseline.json /tmp/risk-baseline.json
RUN <<EOR
set -o xtrace
# Fail hard on any error so a missing release zip / failed download can never
# produce a "successful" image with an empty /build (Cannot find /build/main.js).
set -eo pipefail
apt-get update --assume-yes
apt-get install --assume-yes --no-install-recommends ca-certificates wget unzip python3
# Multi-arch mapping: Docker TARGETARCH -> WeKan's own platform name, which is
# the bundle .zip's name. amd64/arm64 have MongoDB Community;
# ppc64le/s390x/riscv64 have no MongoDB server and ship FerretDB v1 instead (the
# ferretdb binary is baked into their .zip and started by wekan-entrypoint.sh).
# Debian's 32-bit ARM port is armhf (ARMv7, VFPv3-D16), which is what
# linux/arm/v7 runs. ELF_MACHINE is the e_machine the bundle's node must carry.
case "${TARGETARCH}" in
"amd64") WEKAN_ARCH="amd64"; ELF_MACHINE=62 ;;
"arm64") WEKAN_ARCH="arm64"; ELF_MACHINE=183 ;;
"ppc64le") WEKAN_ARCH="ppc64le"; ELF_MACHINE=21 ;;
"s390x") WEKAN_ARCH="s390x"; ELF_MACHINE=22 ;;
"riscv64") WEKAN_ARCH="riscv64"; ELF_MACHINE=243 ;;
"386") WEKAN_ARCH="i386"; ELF_MACHINE=3 ;;
# BOTH linux/arm/v6 and linux/arm/v7 arrive here as TARGETARCH=arm - the CPU
# generation is in TARGETVARIANT, not in TARGETARCH - so this branch MUST read
# the variant. Mapping "arm" straight to armhf, as it did, would hand an ARMv6
# board (Raspberry Pi 1, Zero) the armhf bundle, which is built to Debian's
# ARMv7-A baseline and whose instructions that board cannot execute.
"arm")
ELF_MACHINE=40
case "${TARGETVARIANT}" in
"v6") WEKAN_ARCH="armv6" ;;
# v7, and an unset variant, are Debian's armhf port: ARMv7-A,
# VFPv3-D16 hard-float. That is what linux/arm/v7 runs.
"v7"|"") WEKAN_ARCH="armhf" ;;
# v5 is armel: FerretDB and the MongoDB tools publish it (they are Go),
# but Node.js does not exist for ARMv5 at all, so there is no bundle to
# put in an image and refusing is the only honest answer.
*) echo "Unsupported 32-bit ARM variant: ${TARGETVARIANT} (only v6 and v7 have a WeKan bundle)"; exit 1 ;;
esac ;;
*) echo "Unsupported architecture: ${TARGETARCH}${TARGETVARIANT:+/${TARGETVARIANT}}"; exit 1 ;;
esac
mkdir -p /work
cd /work
# Retry the release-asset download: even though the CI `docker` job needs the
# `release` job (so the asset is already uploaded), GitHub's
# releases/download/<tag>/<asset> URL can briefly return 404 right after upload
# (CDN/propagation lag). A plain wget treats 404 as fatal, which failed the
# build; retry on transient HTTP errors so propagation lag no longer breaks it.
WEKAN_ZIP="wekan-${VERSION}-${WEKAN_ARCH}.zip"
WEKAN_ZIP_URL="https://github.com/wekan/wekan/releases/download/v${VERSION}/${WEKAN_ZIP}"
wget --tries=20 --waitretry=20 --retry-on-http-error=404,403,500,502,503 "${WEKAN_ZIP_URL}" \
|| { echo "Failed to download ${WEKAN_ZIP_URL} after retries"; exit 8; }
wget --tries=20 --waitretry=20 --retry-on-http-error=404,403,500,502,503 "${WEKAN_ZIP_URL}.sha256sum" \
|| { echo "Failed to download ${WEKAN_ZIP_URL}.sha256sum after retries"; exit 8; }
# The file the image is built from is the file the release published.
sha256sum -c "${WEKAN_ZIP}.sha256sum"
unzip -q "${WEKAN_ZIP}"
rm "${WEKAN_ZIP}" "${WEKAN_ZIP}.sha256sum"
python3 /tmp/check-telemetry.py --bundle ./bundle
# The bundle's own node is the image's node, so it must be FOR the target CPU:
# a .zip of the wrong architecture would otherwise become an image whose node
# cannot start, and nothing here runs it to find out.
test -s bundle/node || { echo "${WEKAN_ZIP} has no bundle/node"; exit 1; }
python3 - "${ELF_MACHINE}" bundle/node <<'EOP'
import struct, sys
want, path = int(sys.argv[1]), sys.argv[2]
head = open(path, 'rb').read(20)
if head[:4] != b'\x7fELF':
sys.exit(f'{path} is not an ELF binary')
got = struct.unpack('>H' if head[5] == 2 else '<H', head[18:20])[0]
if got != want:
sys.exit(f'{path} is for ELF machine {got}, not {want}: the bundle is for another CPU')
print(f'{path}: ELF machine {got}, as expected')
EOP
# /out is laid out as the image's root: the app at /build, node on PATH.
mkdir -p /out/usr/local/bin
mv bundle/node /out/usr/local/bin/node
chmod 755 /out/usr/local/bin/node
ln -s "/usr/local/bin/node" "/out/usr/local/bin/nodejs"
# The .zip's self-contained launchers start its bundled node; the image starts
# wekan-entrypoint.sh instead. /build/ferretdb (used by the entrypoint) and the
# per-arch MongoDB Database Tools stay, for backup/restore inside the container.
rm -f bundle/start-wekan.sh bundle/start-wekan.bat
mv bundle /out/build
EOR
# ── Stage 2: the image, for the TARGET platform ──────────────────────────────
#
# The only work left that runs as the target CPU (under QEMU for all but amd64):
# the WeKan user, an OS update, and the three runtime packages the app needs.
FROM debian:trixie
LABEL maintainer="wekan"
LABEL org.opencontainers.image.ref.name="debian"
LABEL org.opencontainers.image.version="trixie"
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=12.17
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 \
DDP_TRANSPORT=sockjs \
NODE_VERSION=v26.10.0 \
METEOR_RELEASE=METEOR@3.6-beta.3 \
NODE_VERSION=v26.13.0 \
METEOR_RELEASE=METEOR@3.6-rc.1 \
USE_EDGE=false \
NPM_VERSION=12.2.0 \
NPM_VERSION=12.2.1 \
SRC_PATH=./ \
WITH_API=true \
MONGO_OPLOG_URL="" \
@@ -50,6 +170,7 @@ ENV \
AVATARS_UPLOAD_EXTERNAL_PROGRAM="" \
AVATARS_UPLOAD_MIME_TYPES="" \
AVATARS_UPLOAD_MAX_SIZE=72000 \
DEFAULT_AVATAR_URL="" \
CARD_OPENED_WEBHOOK_ENABLED=false \
MAX_IMAGE_PIXEL="" \
IMAGE_COMPRESS_RATIO="" \
@@ -124,6 +245,22 @@ ENV \
LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE="" \
LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE="" \
LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT="" \
LDAP_GROUP_FILTER_NESTED=false \
LDAP_MIGRATION_ALLOW_PASSWORD_LOGIN="" \
LDAP_USER_ATTRIBUTES="" \
LDAP_GROUP_BASEDN="" \
OAUTH2_AVATAR_MAP="" \
OAUTH2_MERGE_EXISTING_USERS="" \
OAUTH2_ALLOWED_EMAIL_DOMAINS="" \
OAUTH2_DEFAULT_ORGANIZATION="" \
PROPAGATE_OIDC_DATA="" \
OAUTH2_SECRET_JWT_KEY_PATH="" \
OAUTH2_SECRET_JWT_ISSUER="" \
OAUTH2_SECRET_JWT_KEY_ID="" \
OAUTH2_SECRET_JWT_AUDIENCE="" \
OAUTH2_SECRET_JWT_SUBJECT="" \
OAUTH2_SECRET_JWT_EXPIRES_IN="" \
CAS_MERGE_EXISTING_USERS="" \
LDAP_GROUP_FILTER_GROUP_NAME="" \
LDAP_UNIQUE_IDENTIFIER_FIELD="" \
LDAP_UTF8_NAMES_SLUGIFY=true \
@@ -154,6 +291,33 @@ ENV \
LOGOUT_IN="" \
LOGOUT_ON_HOURS="" \
LOGOUT_ON_MINUTES="" \
USERS_DISABLE_INACTIVE_AFTER_DAYS="" \
LOGOUT_IDLE_MINUTES="" \
REQUIRE_2FA="" \
PASSWORD_MIN_LENGTH="" \
PASSWORD_MIN_LENGTH_ENFORCE="" \
PASSWORD_CHECK_PWNED="" \
HSTS_MAX_AGE="" \
HSTS_INCLUDE_SUBDOMAINS="" \
HSTS_PRELOAD="" \
ATTACHMENTS_RETENTION_DAYS="" \
ATTACHMENTS_RETENTION_MODE="" \
ATTACHMENTS_PURGE_DELETED_AFTER_DAYS="" \
ATTACHMENTS_RETENTION_DRY_RUN="" \
ATTACHMENTS_RETENTION_MAX_PER_RUN="" \
BACKUP_KEEP_DAYS="" \
ACCOUNTS_COOKIE_REFRESH_RATE_LIMIT="" \
INBOUND_IMAP_POLL_SECONDS="" \
INBOUND_EMAIL_PER_ADDRESS_PER_HOUR="" \
INBOUND_EMAIL_WEBHOOK_SECRET="" \
INBOUND_EMAIL_MAX_BYTES="" \
INBOUND_EMAIL_MAX_ATTACHMENT_BYTES="" \
EMAIL_VIEW_SENDS_PER_HOUR="" \
INBOUND_IMAP_DISABLED="" \
WEKAN_DISK_MONITOR="" \
WEKAN_DISK_WATCH_PATHS="" \
WEKAN_RAM_MONITOR="" \
WEKAN_IMPORT_RUN_STALE_MS="" \
CORS="" \
CORS_ALLOW_HEADERS="" \
CORS_EXPOSE_HEADERS="" \
@@ -207,232 +371,50 @@ ENV \
OAUTH_MEETUP_CLIENT_ID="" \
OAUTH_MEETUP_SECRET="" \
OAUTH_MEETUP_SECRET_FILE="" \
OAUTH_PROVIDERS_LOGIN_STYLE="popup" \
OAUTH_PROVIDERS_LOGIN_STYLE="redirect" \
OAUTH_PROVIDERS_MERGE_EXISTING_USERS=false \
OAUTH_PROVIDERS_ALLOWED_EMAIL_DOMAINS="" \
PASSWORDLESS_ENABLED=false \
ORACLE_OIM_ENABLED=false \
WAIT_SPINNER="" \
WRITABLE_PATH=/data \
S3="" \
MAIL_SERVICE_PASSWORD_FILE="" \
MONGO_PASSWORD_FILE="" \
S3_SECRET_FILE=""
# Where this image's Node.js comes from is decided by the SAME script the .zip
# bundles and the snap use - official nodejs.org, then unofficial-builds, then
# wekan/node-patches - so the image and the bundle of the same architecture can
# 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
COPY --chmod=755 releases/check-telemetry.py /tmp/check-telemetry.py
COPY releases/risk-baseline.json /tmp/risk-baseline.json
COPY --chmod=755 releases/prepare-bundle-npm.mjs /tmp/prepare-bundle-npm.mjs
# 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
MAIL_URL_FILE="" \
MONGO_URL_FILE="" \
S3_SECRET_KEY_FILE=""
RUN <<EOR
set -o xtrace
# Fail hard on any error so a missing release zip / failed download can never
# produce a "successful" image with an empty /build (Cannot find /build/main.js).
set -eo pipefail
# Create Wekan user. --create-home is required because --system users do not
# get a home directory by default; without it /home/wekan never exists and the
# later `chown ... /home/wekan/` aborts the build (now that set -e is active).
# get a home directory by default.
useradd --user-group --system --create-home --home-dir /home/wekan wekan
# OS Updates
apt-get update --assume-yes
apt-get upgrade --assume-yes
apt-get install --assume-yes --no-install-recommends ${BUILD_DEPS}
# Runtime MIME detection and Node.js atomic operations survive build cleanup.
# Runtime MIME detection and Node.js atomics. Nothing in this image compiles
# anything, so no compiler is installed (and none has to be purged).
# No docker-cli: it is a Go program whose Go 1.24 standard library was most of
# the image's vulnerability scan, for a client only the UCS migration used. That
# talks to the Docker socket itself now (releases/ucs-migration/docker-exec.mjs).
apt-get install --assume-yes --no-install-recommends file libatomic1
# Multi-arch mapping: Docker TARGETARCH -> WeKan's own platform name, which is
# both the bundle .zip's name and what resolve-node-source.sh is asked about.
# amd64/arm64 have MongoDB Community; ppc64le/s390x/riscv64 have no MongoDB
# server and ship FerretDB v1 instead (the ferretdb binary is baked into their
# .zip and started by wekan-entrypoint.sh). Debian's 32-bit ARM port is armhf
# (ARMv7, VFPv3-D16), which is what linux/arm/v7 runs.
case "${TARGETARCH}" in
"amd64") WEKAN_ARCH="amd64" ;;
"arm64") WEKAN_ARCH="arm64" ;;
"ppc64le") WEKAN_ARCH="ppc64le" ;;
"s390x") WEKAN_ARCH="s390x" ;;
"riscv64") WEKAN_ARCH="riscv64" ;;
"386") WEKAN_ARCH="i386" ;;
# BOTH linux/arm/v6 and linux/arm/v7 arrive here as TARGETARCH=arm - the CPU
# generation is in TARGETVARIANT, not in TARGETARCH - so this branch MUST read
# the variant. Mapping "arm" straight to armhf, as it did, would hand an ARMv6
# board (Raspberry Pi 1, Zero) the armhf bundle, which is built to Debian's
# ARMv7-A baseline and whose instructions that board cannot execute.
"arm")
case "${TARGETVARIANT}" in
"v6") WEKAN_ARCH="armv6" ;;
# v7, and an unset variant, are Debian's armhf port: ARMv7-A,
# VFPv3-D16 hard-float. That is what linux/arm/v7 runs.
"v7"|"") WEKAN_ARCH="armhf" ;;
# v5 is armel: FerretDB and the MongoDB tools publish it (they are Go),
# but Node.js does not exist for ARMv5 at all, so there is no bundle to
# put in an image and refusing is the only honest answer.
*) echo "Unsupported 32-bit ARM variant: ${TARGETVARIANT} (only v6 and v7 have a WeKan bundle)"; exit 1 ;;
esac ;;
*) echo "Unsupported architecture: ${TARGETARCH}${TARGETVARIANT:+/${TARGETVARIANT}}"; exit 1 ;;
esac
# Node.js installation - official nodejs.org, then unofficial-builds.nodejs.org,
# then wekan/node-patches, in that order, for EVERY architecture.
#
# The choice is made by releases/resolve-node-source.sh, copied in above and used
# by the .zip bundles and the snap as well, so the image and the bundle of one
# architecture always carry the same Node.js from the same place. It is asked
# about the MAJOR, so a CPU whose newest build lags a release still gets its
# newest: it answers with the exact file, what shape that file is, and the
# SHA256 the source published for it.
#
# npm is arch-independent JavaScript, so it is grafted from the official amd64
# tarball of the version that was resolved - a build-time tool, not the shipped
# node. (node-patches publishes a bare node binary and no npm at all, which is
# why npm is fetched separately rather than taken from the archive above.)
cd /tmp
NODE_MAJOR="${NODE_VERSION#v}"; NODE_MAJOR="${NODE_MAJOR%%.*}"
if ! NODE_META="$(bash /tmp/resolve-node-source.sh "${WEKAN_ARCH}" "${NODE_MAJOR}")"; then
echo "No Node.js ${NODE_MAJOR}.x for ${WEKAN_ARCH} at nodejs.org, unofficial-builds.nodejs.org or wekan/node-patches, so this image cannot be built for it. Build node-${WEKAN_ARCH} in wekan/node-patches, or drop ${TARGETARCH} from the image's platform list." >&2
exit 1
fi
eval "${NODE_META}"
echo "Node.js ${node_full} for ${WEKAN_ARCH}: ${node_from} (${node_url})"
wget --tries=20 --waitretry=20 --retry-on-http-error=403,500,502,503 -O node-download "${node_url}"
echo "${node_sha256} node-download" | sha256sum -c -
case "${node_kind}" in
binary)
cp node-download /usr/local/bin/node
;;
tar.xz|tar.gz|tar)
# bsdtar (libarchive-tools) is installed below for the bundle; plain tar
# here, letting it detect the compression rather than naming it.
tar -xf node-download --no-same-owner "${node_member}"
cp "${node_member}" /usr/local/bin/node
;;
*)
echo "resolve-node-source.sh returned an unknown kind '${node_kind}'." >&2
exit 1
;;
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.
# 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" \
"node-${node_full}-linux-x64/bin/npm" \
"node-${node_full}-linux-x64/bin/npx"
rm -rf node-download "node-${node_full}-linux-x64.tar.gz" "node-${node_full}-linux-x64" SHASUMS256.txt
ln -s "/usr/local/bin/node" "/usr/local/bin/nodejs"
# NPM configuration
npm install -g npm@${NPM_VERSION}
chown --recursive wekan:wekan /home/wekan/
# Temporary Tar swap for Meteor bundle
cp $(which tar) $(which tar)~
ln -sf $(which bsdtar) $(which tar)
# WeKan Bundle Installation
mkdir -p /home/wekan/app
cd /home/wekan/app
# Retry the release-asset download: even though the CI `docker` job needs the
# `release` job (so the asset is already uploaded), GitHub's
# releases/download/<tag>/<asset> URL can briefly return 404 right after upload
# (CDN/propagation lag). A plain wget treats 404 as fatal, which failed the
# build; retry on transient HTTP errors so propagation lag no longer breaks it.
WEKAN_ZIP_URL="https://github.com/wekan/wekan/releases/download/v${VERSION}/wekan-${VERSION}-${WEKAN_ARCH}.zip"
wget --tries=20 --waitretry=20 --retry-on-http-error=404,403,500,502,503 "${WEKAN_ZIP_URL}" \
|| { echo "Failed to download ${WEKAN_ZIP_URL} after retries"; exit 8; }
unzip "wekan-${VERSION}-${WEKAN_ARCH}.zip"
rm "wekan-${VERSION}-${WEKAN_ARCH}.zip"
node /tmp/prepare-bundle-npm.mjs ./bundle
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
python3 /tmp/check-telemetry.py --bundle ./bundle
mv /home/wekan/app/bundle /build
# The .zip bundle now ships a self-contained launcher + its own Node.js for the
# offline downloads; the Docker image installs its own Node and uses
# wekan-entrypoint.sh, so drop the redundant bundled node + launchers. Keeps
# /build/ferretdb (used by the entrypoint) and the per-arch MongoDB Database Tools
# (bsondump, mongodump, mongorestore, … from wekan/mongo-tools-patches, embedded in the
# bundle) for backup/restore inside the container. Saves ~80 MB per arch.
rm -f /build/node /build/start-wekan.sh /build/start-wekan.bat
# Restore original tar
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
apt-get remove --purge --assume-yes ${BUILD_DEPS}
apt-get autoremove --assume-yes
apt-get clean --assume-yes
rm -Rf /tmp/*
rm -Rf /var/lib/apt/lists/*
rm -Rf /home/wekan/app
mkdir -p /data
chown wekan:wekan --recursive /data
EOR
# The bundle and its node, prepared in stage 1. npm is not in this image at all:
# nothing here installs anything, and the entrypoint never calls it.
COPY --from=bundle /out/ /
# Database-backend selector entrypoint. Every arch's bundle now ships a FerretDB
# binary (baked into the .zip and moved to /build/ferretdb above); this script
# starts FerretDB v1 (SQLite) or leaves the DB external based on WEKAN_DB and the
@@ -447,6 +429,13 @@ COPY --chmod=644 releases/ferretdb/recovery-bridge.mjs /build/recovery-bridge.mj
# for a reverse proxy to time out on.
COPY --chmod=644 releases/ferretdb/db-ready.mjs /build/db-ready.mjs
# UCS runs this same image with an explicit migration command. Only that
# temporary container receives the Docker socket; normal startup is unchanged.
COPY releases/ucs-migration /opt/ucs-migration
RUN chmod 755 /opt/ucs-migration/start.sh /opt/ucs-migration/tools/*
COPY releases/nextcloud/capability.json /build/nextcloud-exapp-capability.json
USER wekan
ENV PORT=8080
EXPOSE $PORT
+140 -31
View File
@@ -4,6 +4,142 @@
https://wekan.fi/install/
## Screenshot
![Screenshot of WeKan Swimlanes view](https://wekan.fi/wekan-board-views.png)
## About WeKan ®
WeKan ® is a FLOSS collaborative kanban board application with MIT license.
Whether you’re maintaining a personal todo list, planning your holidays with some friends,
or working in a team on your next revolutionary idea, Kanban boards are an unbeatable tool
to keep your things organized. They give you a visual overview of the current state of your project,
and make you productive by allowing you to focus on the few items that matter the most.
Since WeKan ® is a free software, you don’t have to trust us with your data and can
install Wekan on your own computer or server. In fact we encourage you to do
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/) into 234 locale catalogs;
234 have non-English text for over 90% of source keys. This measures text coverage,
not translation quality; language review and remaining translations are ongoing.
- [Databases](https://github.com/wekan/wekan/blob/main/docs/Databases/Supported.md):
- FerretDB v1 with SQLite, the default on every platform - bundle, Docker,
Snap and Kubernetes - so no database server is needed.
- FerretDB v1 with PostgreSQL works; with MySQL, MariaDB or SAP HANA it is
experimental.
- MongoDB 6, 7, 8 and 9. MongoDB refuses to start on Linux kernels 6.19 to
7.0.13, which FerretDB is not affected by.
- All of them - SAP HANA only when asked - answer the same 110-query
conformance catalogue the same way, checked by EVERYTHING in build.sh and
build.bat.
- [Features](https://github.com/wekan/wekan/tree/main/docs/Features):
- Real-time user interface
- All Boards page, Drag drop reorder with one or Multi-Selection:
- Board icons at Remaining, (Sub)Workspaces, Archive
- Shared Templates: Boards, Lists, Cards
- Add Board / Import ([all import and export formats](docs/Features/Board-Sidebar/Board-Settings/Import-Export/External-Tools.md)):
- WeKan JSON/.zip, CSV/TSV, Excel, Markdown, Obsidian Kanban, Leo, OPML, Org mode, todo.txt
- Trello, Jira, Asana, Zenkit, Focalboard, Todoist, Microsoft Planner, MeisterTask, Linear, TickTick, ClickUp, Nullboard, Kanri, Pivotal Tracker, Redmine, Tasks.org, monday.com,
Super Productivity, Taiga, Vikunja, Wrike (Excel and workflow), Quire, Teamwork.com, Businessmap, Notion, Plane
- Kanboard, Nextcloud Deck, OpenProject, Taskwarrior
- GitHub, GitLab, Gitea, Forgejo
- Import many boards at once: several export files, or a .zip of them, each its own board;
One board per project splits an export of a whole app into a board per project
- Change Color: Board theme and background image, Swimlane, List, Card
- Overrides: 1 Admin Panel, 2 Board Settings, 3 Member Settings
- Drag drop reorder with one or Multi-Selection:
- Board icons to Workspaces at All Boards page
- Swimlane, List, Minicard
- Board
- Top header: Mobile/Desktop view, Show/Hide Drag Handles, Starred Boards Bookmarks, Sort Boards, Filter, Search, Notifications, Member Settings
- Member Settings:
- Show My Dependencies, Show Board Dependencies, Import/Export JSON/SVG
- Dependencies are for SAFe PI Plannings, drawing colored lines between cards, like Kendis or Scaled Agile Framework. Import Export as JSON and SVG.
- My Cards, Due Cards, My Attachments, Search All Boards, Date Settings,
Change color - override theme, Nofications Settings (Tray, Email, etc),
Font change and Font size change, Change Avatar,
Change Language (250+ languages)
- Change Settings: Show desktop drag handles, Submit editors with Enter,
Open many cards at once, Play a sound when a checklist item is completed,
Show cards count if list contains more than ... cards,
Show rescue dialoque before closing for unsaved card descriptions
- Board Views:
- Swimlanes, Lists
- Minicard: At list there is card, that has not been not opened yet
- Card: Opened card popup of minicard
- Attachments viewer for PDF and Office documents
- Drag drop reorder one or Multi-Selection: Swimlanes, Lists, Minicards, Checklists, Checklist Items
- Collapse/Uncollapse: Menus, Swimlanes, Lists, Cards
- Table, Calendar, Multi Board Calendar, Time, Timeline
- Statistics, Group by Assignee
- Gantt, Frappe Gantt, DHTMLX Gantt
- Scrum: Product Backlog, Sprints, Sprint Report, Velocity, Roadmap, Dashboard, Bigboard
- Burndown, Burnup, Cumulative Flow, Control, Cycle Time, Flow Efficiency,
Lead Time, Throughtput Histogram, WIP Run, Pulse, Aging WIP,
Blocker Analysis, Monte Carlo Forecasts, Process Behavior (XmR),
Work Item Size vs Cycle Time, Map
- Right Sidebar / Board Settings:
- Rules: like IFTTT like Trello Butler
- Lists view for Rules
- Workflow view: like Jira Workflows
- Blocks: like Scratch or Jira AutoBlocks, shows same IFTTT Rules
- Import / Export IFTTT Rules; import n8n, Node-RED and Trello Butler; import / export Wrike workflows
- Export board :
- Select what to include: Card details, Board, Activities, Labels, People (Creator, Owner, Members, Assignees), Board Info (Board, List, Swimlane), Dates (Created, Received, Start, Due, End), Description, Custom Fields, Checklists, Subtasks, Comments, Attachments, Votin, Plannin Poker, Stickers, Location (link to map based on coordinates), Dependencies, Sort, Scrum Settings
- Export to ([all formats](docs/Features/Board-Sidebar/Board-Settings/Import-Export/External-Tools.md)): PDF, Excel, HTML, Calendar feed (iCal), Dependencies JSON/SVG,
CSV , ; TSV, JSON with/without attachmeents, .zip (Attachments),
Kanboard, Markdown, Leo, OPML, Org mode, todo.txt, Taskwarrior, Focalboard, Todoist,
Microsoft Planner, MeisterTask, Obsidian Kanban, Linear, TickTick, ClickUp, Nullboard, Kanri, Pivotal Tracker, Redmine, Tasks.org, monday.com,
Super Productivity, Taiga, Vikunja, Wrike (Excel and workflow), Quire, Teamwork.com, Businessmap, Notion,
Trello, Jira, NextCloud Deck, OpenProject, GitHub, GitLab,
Gitea, Forgejo, Asana, Zenkit
- Export all boards at once (All Boards sidebar, or Multi-Selection): Excel as one workbook
with a sheet per board, every other format as a .zip with a file per board
- Scrum Settings
- Change color theme, Change Background Image, Date settings
- Settings:
- Board View: Show/Hide and drag drop reorder what is shown atat Public Board and Private Board
- Swimlane: Scrum settings, Lock resize swimlane height, WIP Limit Groups
- Board View: Show/Hide and drag drop reorder what is shown at Minicard and Card
- Admin Panel
- Settings
- Version
- Visibility: What is shown/hidden, Whitelabeling
- Announcement
- Accessibility
- Translation: Add custom translation strings
- PWA
- Global Webhooks
- People
- Email, Notifications, Domains, Organizations, Teams, People
- Locked Users, Roles, Shared Templates
- Login, SAML, LDAP, OAuth2 login providers, Passwordless login
- Attachments
- Scheduled Backups, Continuous realtime backups, Move Attachments, Default Save Storage, Limits
- Storage: MongoDB GridFS, Filesystem, S3/MinIO, Azure Blob, Google Cloud
- Database migration
- Problems
- Summary
- Security
- Delete: Optional enable permanent delete.
- History Undo/Redo available at Board, List, Card, etc menus at Swimlanes and List views
- Notifications
- Reports
- Security: shows any attempted attacks based on names from https://wekan.fi/hall-of-fame/ like built-in SIEM with country flag and city based of location of IP address based on location headers like CloudFlare etc
- Impersonation
- Performance, Speed, Tests, CPU usage, Instrumentation, Broken Cards, Files, Rules, Boards, Recovery, Offices, API, Database problems, Filesystem integrity
- [Platforms](https://wekan.fi/install/):
- Windows/Mac/Linux bundle .zip
- Linux [Snap](https://snapcraft.io/wekan): Automatic updates
- Linux AppImage
- Docker, Kubernetes
- [SaaS](https://wekan.fi/saas/): Free with App or Paid with Admin Panel
- [Commercial Support](https://wekan.fi/commercial-support/)
## Docker Containers
- [GitHub](https://ghcr.io/wekan/wekan)
@@ -23,8 +159,8 @@ docker-compose.yml at https://github.com/wekan/wekan/blob/main/docker-compose.ym
## Standards
- [WeKan and Standard for Public Code](https://wekan.fi/standard-for-public-code/) assessment was made at 2023-11.
Currently Wekan meets 8 out of 16 criteria out of the box.
- [WeKan and Standard for Public Code](https://wekan.fi/standard-for-public-code/) assessment was made at 2023-11. At 2026 WeKan becameß full FLOSS with [FerretDB v1 Fork](https://github.com/wekan/FerretDB), also see 2026 talk WeKan 10 years at https://wekan.fi/docs for more details.
Currently Wekan meets 9 out of 16 criteria out of the box.
Some others could be met with small changes.
## Code stats
@@ -48,11 +184,6 @@ It's better than at chat where details get lost when chat scrolls up.
[IRC](https://github.com/wekan/wekan/blob/main/docs/FAQ/IRC-FAQ.md)
## Docker: Latest tag has newest release
You can use latest tag to get newest release tag.
See bottom of https://github.com/wekan/wekan/issues/3874
## FAQ
**NOTE**:
@@ -60,28 +191,6 @@ See bottom of https://github.com/wekan/wekan/issues/3874
- Please read the [FAQ](https://github.com/wekan/wekan/blob/main/docs/FAQ/FAQ.md) first
- Please don't feed the [trolls](https://github.com/wekan/wekan/blob/main/docs/FAQ/FAQ.md#why-am-i-called-a-troll) and [spammers](https://github.com/wekan/wekan/blob/main/docs/FAQ/FAQ.md#why-am-i-called-a-spammer) that are mentioned in the FAQ :)
## About WeKan ®
WeKan ® is a completely [Open Source][open_source] and [Free software][free_software]
collaborative kanban board application with MIT license.
Whether you’re maintaining a personal todo list, planning your holidays with some friends,
or working in a team on your next revolutionary idea, Kanban boards are an unbeatable tool
to keep your things organized. They give you a visual overview of the current state of your project,
and make you productive by allowing you to focus on the few items that matter the most.
Since WeKan ® is a free software, you don’t have to trust us with your data and can
install Wekan on your own computer or server. In fact we encourage you to do
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 234 languages,
167 of them essentially complete.
- [Features][https://github.com/wekan/wekan/tree/main/docs/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.
## Requirements
- 1 GB RAM minimum free for WeKan ®. Production server should have minimum total 4 GB RAM.
@@ -113,7 +222,7 @@ By working directly with WeKan ® you get the benefit of active maintenance and
## Getting Started with Development
The main branch uses Meteor 3.6-beta.1 with Node.js 26.x.
The main branch uses Meteor 3.x with Node.js 26.x.
See [CHANGELOG.md](https://github.com/wekan/wekan/blob/main/CHANGELOG.md) for the latest runtime updates.
To contribute, [create a fork](https://github.com/wekan/wekan/blob/main/docs/DeveloperDocs/Build-and-Create-Pull-Request.md#2-create-fork-of-httpsgithubcomwekanwekan-at-github-web-page) and run `./build.sh` (or `./build.bat` on Windows) as detailed [here](https://github.com/wekan/wekan/blob/main/docs/DeveloperDocs/Build-and-Create-Pull-Request.md#3-install-dependencies-build-wekan-and-run-the-dev-server). Once you're ready, please test your code and [submit a pull request (PR)](https://github.com/wekan/wekan/blob/main/docs/DeveloperDocs/Build-and-Create-Pull-Request.md#7-test).
@@ -169,7 +278,7 @@ If a dev server is already running on that port, the **Dev server** options stop
### WSL Users
WSL users can use Snap Candidate. See [install docs](https://wekan.fi/install/).
WSL users can use Snap. See [install docs](https://wekan.fi/install/).
## License
+13 -5
View File
@@ -29,7 +29,15 @@ because WeKan is NOT Big Tech. WeKan is FLOSS.
- **Remediation**: Please wait for a new WeKan release that addresses the issue.
Fixes are announced at the top of the [ChangeLog](https://github.com/wekan/wekan/blob/main/CHANGELOG.md).
- **Recognition**: We will acknowledge your contribution by adding you to our
[Hall of Fame](https://wekan.fi/hall-of-fame/).
[Hall of Fame](https://wekan.fi/hall-of-fame/). The stars follow the
[Stars Audit](https://github.com/wekan/wekan.fi/blob/main/hall-of-fame/Audit-Stars.md):
a GitHub security advisory (GHSA) with details and a suggested fix gets 5 stars,
a report sent to security@wekan.fi or some other way, with a suggested fix, gets 3.
- **Adding the GHSA later**: if you sent your report to security@wekan.fi, or some
other way, and are later able to add it as a "New draft security advisory" at
[WeKan Security Advisories](https://github.com/wekan/wekan/security/advisories),
please do. The maintainer of WeKan then requests the CVE through that GHSA, and
your Hall of Fame row is upgraded from 3 stars to 5.
- **CVE Policy**: WeKan Security requests CVE at GitHub when releasing Security Advisory.
### 3. Post-Release and Public Disclosure:
@@ -264,13 +272,13 @@ Meteor.startup(() => {
On the Sandstorm platform, Standalone WeKan features like the Admin Panel are turned off using environment variables, because Sandstorm platform provides SSO for all apps running on Sandstorm.
[Sandstorm](https://sandstorm.io) is a separate Open Source platform that has been
[security audited](https://sandstorm.io/news/2017-03-02-security-review) and found bugs fixed.
[Sandstorm](https://sandstorm.org) is a separate Open Source platform that has been
[security audited](https://sandstorm.org/news/2017-03-02-security-review) and found bugs fixed.
Sandstorm also has passwordless login, LDAP, SAML, Google, and other auth options already.
On Sandstorm, code is read-only and signed by app maintainers; only grain content can be modified.
WeKan on Sandstorm runs in a sandboxed grain; it does not have access elsewhere without a user-visible PowerBox request or opening a randomly-generated API key URL.
Also read [Sandstorm Security Practices](https://docs.sandstorm.io/en/latest/using/security-practices/) and
[Sandstorm Security non-events](https://docs.sandstorm.io/en/latest/using/security-non-events/).
Also read [Sandstorm Security Practices](https://docs.sandstorm.org/en/latest/using/security-practices/) and
[Sandstorm Security non-events](https://docs.sandstorm.org/en/latest/using/security-non-events/).
For Sandstorm specific security issues you can contact [kentonv](https://github.com/kentonv) by email.
## What WeKan bugs are eligible?
+1 -1
View File
@@ -1,5 +1,5 @@
appId: wekan-public/apps/77b94f60-dec9-0136-304e-16ff53095928
appVersion: "v12.17.0"
appVersion: "v12.94.0"
files:
userUploads:
- README.md
+235 -144
View File
@@ -8,13 +8,6 @@
# TODO:
# addcustomfieldtoboard: There is error: Settings must be object. So adding does not work yet.
try:
# python 3
from urllib.parse import urlencode
except ImportError:
# python 2
from urllib import urlencode
import json
import requests
import sys
@@ -77,8 +70,10 @@ If *nix: chmod +x api.py => ./api.py users
python3 api.py importboard EXPORT.json # Import a whole board (with rules/workflows) from a WeKan export
python3 api.py migratefromwekan REMOTE_URL REMOTE_USER REMOTE_PASS # Import ALL boards+workflows+rules from another WeKan
python3 api.py exportboardpdf BOARDID OUTPUT.pdf # Export a whole board to PDF
python3 api.py importboardfrom SOURCE EXPORT.json # Import from trello/wekan/csv/jira/kanboard/excel/deck/openproject/github/gitlab/gitea/forgejo/asana/zenkit
python3 api.py exportboardformat BOARDID FORMAT OUTPUT.json # Export to kanboard/trello/jira/deck/openproject/github/gitlab/gitea/forgejo/asana/zenkit
python3 api.py importboardfrom SOURCE FILE [--csv-mapping MAPPING.json] # Import a tool's export file (csv and excel: --csv-mapping says which column is which field); SOURCE is a key of the import page (trello, jira, csv, excel, planner, wrike, notion, ...), see docs/Features/Board-Sidebar/Board-Settings/Import-Export
python3 api.py exportboardformat BOARDID FORMAT OUTPUTFILE # Export to a tool's format; FORMAT is a key of the export menu (kanboard, trello, planner, wrike, wrikeworkflow, ...)
python3 api.py importboardsfrom SOURCE [--split] FILE_OR_DIR ... # Import many boards: each file (or each file of a directory or .zip) becomes a board; --split makes one board per project
python3 api.py exportallboards FORMAT OUTPUTFILE [--boards ID1,ID2] # Export all boards: Excel as one workbook with a sheet per board, other formats as a .zip with a file per board
python3 api.py importics BOARDID SWIMLANEID LISTID CALENDAR.ics # Import an iCalendar (.ics) file into a board as cards (one card per VEVENT)
Board Member API (Issue #5998):
@@ -161,7 +156,7 @@ If *nix: chmod +x api.py => ./api.py users
if arguments == 0:
print(syntax)
exit
sys.exit()
# TODO:
# print(" python3 api.py attachmentjson BOARDID ATTACHMENTID # One attachment as JSON base64")
@@ -327,7 +322,7 @@ if arguments == 8:
if new_card_id:
edcard = wekanurl + apiboards + boardid + s + l + s + listid + s + cs + s + new_card_id
put_data = {'labelIds': labelIds}
body = requests.put(edcard, data=put_data, headers=headers)
requests.put(edcard, data=put_data, headers=headers)
print("=== EDIT CARD ===\n")
body = requests.get(edcard, headers=headers)
data2 = body.text.replace('}', "}\n")
@@ -370,7 +365,7 @@ if arguments == 6:
print(edcard)
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
put_data = {'title': '{}'.format(newcardtitle), 'description': '{}'.format(newcarddescription)}
body = requests.put(edcard, data=put_data, headers=headers)
requests.put(edcard, data=put_data, headers=headers)
print("=== EDIT CARD ===\n")
body = requests.get(edcard, headers=headers)
data2 = body.text.replace('}',"}\n")
@@ -409,7 +404,7 @@ if arguments == 5:
print(edcard)
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
put_data = {'labelIds': labelIds}
body = requests.put(edcard, data=put_data, headers=headers)
requests.put(edcard, data=put_data, headers=headers)
print("=== ADD LABEL ===\n")
body = requests.get(edcard, headers=headers)
data2 = body.text.replace('}',"}\n")
@@ -435,7 +430,7 @@ if arguments == 5:
print(edcard)
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
put_data = {'color': '{}'.format(newcolor)}
body = requests.put(edcard, data=put_data, headers=headers)
requests.put(edcard, data=put_data, headers=headers)
print("=== EDIT CARD COLOR ===\n")
body = requests.get(edcard, headers=headers)
data2 = body.text.replace('}', "}\n")
@@ -454,7 +449,7 @@ if arguments >= 4:
print(edlist)
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
put_data = {'title': '{}'.format(newlisttitle)}
body = requests.put(edlist, data=put_data, headers=headers)
requests.put(edlist, data=put_data, headers=headers)
print("=== EDIT LIST ===\n")
body = requests.get(edlist, headers=headers)
data2 = body.text.replace('}', "}\n")
@@ -480,7 +475,7 @@ if arguments >= 4:
print(edlist)
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
put_data = {'color': '{}'.format(newcolor)}
body = requests.put(edlist, data=put_data, headers=headers)
requests.put(edlist, data=put_data, headers=headers)
print("=== EDIT LIST COLOR ===\n")
body = requests.get(edlist, headers=headers)
data2 = body.text.replace('}', "}\n")
@@ -1048,7 +1043,7 @@ if arguments >= 1:
if arguments < 6:
print("Usage: python3 api.py uploadattachment BOARDID SWIMLANEID LISTID CARDID FILEPATH [STORAGE_BACKEND]")
print("Storage backends: fs, gridfs, s3")
exit(1)
sys.exit(1)
boardid = sys.argv[2]
swimlaneid = sys.argv[3]
@@ -1057,45 +1052,22 @@ if arguments >= 1:
filepath = sys.argv[6]
storage_backend = sys.argv[7] if arguments > 6 else None
# Read file and convert to base64
# The file is streamed as the request body (the raw upload of
# /api/attachment/upload), never read whole or turned into base64.
import os, mimetypes
if not os.path.isfile(filepath):
print(f"Error: File '{filepath}' not found")
sys.exit(1)
filename = os.path.basename(filepath)
file_type = mimetypes.guess_type(filepath)[0] or 'application/octet-stream'
params = {'boardId': boardid, 'swimlaneId': swimlaneid, 'listId': listid, 'cardId': cardid,
'fileName': filename, 'fileType': file_type}
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey),
'Content-Type': 'application/octet-stream'}
upload_url = wekanurl + 'api/attachment/upload'
try:
with open(filepath, 'rb') as f:
file_data = f.read()
import base64
base64_data = base64.b64encode(file_data).decode('utf-8')
except FileNotFoundError:
print(f"Error: File '{filepath}' not found")
exit(1)
except Exception as e:
print(f"Error reading file: {e}")
exit(1)
# Get file info
import os
filename = os.path.basename(filepath)
import mimetypes
file_type = mimetypes.guess_type(filepath)[0] or 'application/octet-stream'
# Prepare request data
upload_data = {
'boardId': boardid,
'swimlaneId': swimlaneid,
'listId': listid,
'cardId': cardid,
'fileData': base64_data,
'fileName': filename,
'fileType': file_type
}
if storage_backend:
upload_data['storageBackend'] = storage_backend
# Make API call
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey), 'Content-Type': 'application/json'}
upload_url = wekanurl + 'api/attachment/upload'
try:
response = requests.post(upload_url, headers=headers, json=upload_data)
response = requests.post(upload_url, headers=headers, params=params, data=f)
response.raise_for_status()
result = response.json()
print(f"Upload successful!")
@@ -1113,35 +1085,25 @@ if arguments >= 1:
# ------- DOWNLOAD ATTACHMENT START -----------
if arguments < 3:
print("Usage: python3 api.py downloadattachment ATTACHMENTID OUTPUTPATH")
exit(1)
sys.exit(1)
attachmentid = sys.argv[2]
outputpath = sys.argv[3]
# Make API call
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
# The file itself (?raw=1), written to disk as it arrives.
headers = {'Authorization': 'Bearer {}'.format(apikey)}
download_url = wekanurl + f'api/attachment/download/{attachmentid}'
try:
response = requests.get(download_url, headers=headers)
response.raise_for_status()
result = response.json()
if result.get('success'):
# Decode base64 data and save to file
import base64
file_data = base64.b64decode(result.get('base64Data'))
with requests.get(download_url, headers=headers, params={'raw': '1'}, stream=True) as response:
response.raise_for_status()
size = 0
with open(outputpath, 'wb') as f:
f.write(file_data)
print(f"Download successful!")
print(f"File saved to: {outputpath}")
print(f"Original filename: {result.get('fileName')}")
print(f"Size: {result.get('fileSize')} bytes")
print(f"Storage: {result.get('storageBackend')}")
else:
print(f"Download failed: {result.get('message', 'Unknown error')}")
for chunk in response.iter_content(chunk_size=1024 * 1024):
f.write(chunk)
size += len(chunk)
print(f"Download successful!")
print(f"File saved to: {outputpath}")
print(f"Size: {size} bytes")
except requests.exceptions.RequestException as e:
print(f"Download failed: {e}")
if hasattr(e, 'response') and e.response is not None:
@@ -1152,7 +1114,7 @@ if arguments >= 1:
# ------- ATTACHMENT INFO START -----------
if arguments < 2:
print("Usage: python3 api.py attachmentinfo ATTACHMENTID")
exit(1)
sys.exit(1)
attachmentid = sys.argv[2]
@@ -1191,7 +1153,7 @@ if arguments >= 1:
# ------- LIST CARD ATTACHMENTS START -----------
if arguments < 5:
print("Usage: python3 api.py listcardattachments BOARDID SWIMLANEID LISTID CARDID")
exit(1)
sys.exit(1)
boardid = sys.argv[2]
swimlaneid = sys.argv[3]
@@ -1230,7 +1192,7 @@ if arguments >= 1:
# ------- COPY/MOVE ATTACHMENT START -----------
if arguments < 6:
print("Usage: python3 api.py copymoveattachment ATTACHMENTID TARGETBOARDID TARGETSWIMLANEID TARGETLISTID TARGETCARDID [copy|move]")
exit(1)
sys.exit(1)
attachmentid = sys.argv[2]
targetboardid = sys.argv[3]
@@ -1241,7 +1203,7 @@ if arguments >= 1:
if operation not in ['copy', 'move']:
print("Operation must be 'copy' or 'move'")
exit(1)
sys.exit(1)
# Prepare request data
request_data = {
@@ -1284,7 +1246,7 @@ if arguments >= 1:
# ------- DELETE ATTACHMENT START -----------
if arguments < 2:
print("Usage: python3 api.py deleteattachment ATTACHMENTID")
exit(1)
sys.exit(1)
attachmentid = sys.argv[2]
@@ -1313,7 +1275,7 @@ if arguments >= 1:
# ------- BOARD MEMBER / CARD FIELD API ENDPOINTS START -----------
# These commands wrap REST endpoints that already exist in Wekan.
# See CHANGELOG.md "Upcoming" section for related issues and design notes.
# See CHANGELOG-big.md "Upcoming" section for related issues and design notes.
# Map a human readable ROLE to the board member permission boolean flags
# used by /api/boards/:boardId/members/:userId/add and .../members/:memberId
@@ -1353,7 +1315,7 @@ if arguments >= 1:
# ------- ADD BOARD MEMBER START -----------
if arguments < 3:
print("Usage: python3 api.py addboardmember BOARDID USERID ROLE")
exit(1)
sys.exit(1)
boardid = sys.argv[2]
userid = sys.argv[3]
role = sys.argv[4] if arguments > 3 else 'normal'
@@ -1370,7 +1332,7 @@ if arguments >= 1:
# ------- REMOVE BOARD MEMBER START -----------
if arguments < 3:
print("Usage: python3 api.py removeboardmember BOARDID USERID")
exit(1)
sys.exit(1)
boardid = sys.argv[2]
userid = sys.argv[3]
remove_member_url = wekanurl + apiboards + boardid + s + 'members' + s + userid + s + 'remove'
@@ -1385,7 +1347,7 @@ if arguments >= 1:
# ------- SET BOARD MEMBER ROLE START -----------
if arguments < 4:
print("Usage: python3 api.py setboardmemberrole BOARDID USERID ROLE")
exit(1)
sys.exit(1)
boardid = sys.argv[2]
userid = sys.argv[3]
role = sys.argv[4]
@@ -1401,7 +1363,7 @@ if arguments >= 1:
# ------- LIST BOARD DOMAINS START -----------
if arguments < 2:
print("Usage: python3 api.py boarddomains BOARDID")
exit(1)
sys.exit(1)
boardid = sys.argv[2]
domains_url = wekanurl + apiboards + boardid + s + 'domains'
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
@@ -1414,7 +1376,7 @@ if arguments >= 1:
# ------- ADD BOARD DOMAIN START -----------
if arguments < 3:
print("Usage: python3 api.py addboarddomain BOARDID DOMAIN")
exit(1)
sys.exit(1)
boardid = sys.argv[2]
domain = sys.argv[3]
add_domain_url = wekanurl + apiboards + boardid + s + 'domains'
@@ -1429,7 +1391,7 @@ if arguments >= 1:
# ------- REMOVE BOARD DOMAIN START -----------
if arguments < 3:
print("Usage: python3 api.py removeboarddomain BOARDID DOMAIN")
exit(1)
sys.exit(1)
boardid = sys.argv[2]
domain = sys.argv[3]
remove_domain_url = wekanurl + apiboards + boardid + s + 'domains' + s + domain
@@ -1444,7 +1406,7 @@ if arguments >= 1:
if arguments < 4:
print("Usage: python3 api.py {} BOARDID LISTID CARDID IDS".format(sys.argv[1]))
print("IDS = comma-separated userIds, or '' to clear")
exit(1)
sys.exit(1)
boardid = sys.argv[2]
listid = sys.argv[3]
cardid = sys.argv[4]
@@ -1466,7 +1428,7 @@ if arguments >= 1:
print("Usage: python3 api.py setcarddate BOARDID LISTID CARDID DATETYPE DATEVALUE")
print("DATETYPE: received, start, due, end")
print("DATEVALUE: ISO 8601, e.g. 2026-06-07T12:00:00.000Z")
exit(1)
sys.exit(1)
boardid = sys.argv[2]
listid = sys.argv[3]
cardid = sys.argv[4]
@@ -1484,7 +1446,7 @@ if arguments >= 1:
edcard = wekanurl + apiboards + boardid + s + l + s + listid + s + cs + s + cardid
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
put_data = {datemap[datetype]: datevalue}
body = requests.put(edcard, data=put_data, headers=headers)
requests.put(edcard, data=put_data, headers=headers)
print("=== SET CARD DATE {} ===\n".format(datetype.upper()))
print(body.text)
# ------- SET CARD DATE END -----------
@@ -1494,7 +1456,7 @@ if arguments >= 1:
if arguments < 4:
print("Usage: python3 api.py setcardlabels BOARDID LISTID CARDID LABELIDS")
print("LABELIDS = comma-separated labelIds, or '' to clear")
exit(1)
sys.exit(1)
boardid = sys.argv[2]
listid = sys.argv[3]
cardid = sys.argv[4]
@@ -1503,7 +1465,7 @@ if arguments >= 1:
edcard = wekanurl + apiboards + boardid + s + l + s + listid + s + cs + s + cardid
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey), 'Content-Type': 'application/json'}
# NOTE: this REPLACES the card's labels. Bulk add/remove (merge) is not yet
# implemented server-side, see Issue #5819 in CHANGELOG.md "Upcoming".
# implemented server-side, see Issue #5819 in CHANGELOG-big.md "Upcoming".
put_data = {'labelIds': labelids if labelids else []}
body = requests.put(edcard, json=put_data, headers=headers)
print("=== SET CARD LABELS ===\n")
@@ -1514,7 +1476,7 @@ if arguments >= 1:
# ------- MOVE CARD START -----------
if arguments < 7:
print("Usage: python3 api.py movecard BOARDID LISTID CARDID NEWBOARDID NEWSWIMLANEID NEWLISTID")
exit(1)
sys.exit(1)
boardid = sys.argv[2]
listid = sys.argv[3]
cardid = sys.argv[4]
@@ -1528,7 +1490,7 @@ if arguments >= 1:
'newSwimlaneId': newswimlaneid,
'newListId': newlistid,
}
body = requests.put(edcard, data=put_data, headers=headers)
requests.put(edcard, data=put_data, headers=headers)
print("=== MOVE CARD ===\n")
print(body.text)
# ------- MOVE CARD END -----------
@@ -1539,7 +1501,7 @@ if arguments >= 1:
print("Usage: python3 api.py bulkaddcards BOARDID LISTID AUTHORID SWIMLANEID CARDSJSONFILE")
print("CARDSJSONFILE = JSON file with a list of titles, e.g. [\"Card A\", \"Card B\"],")
print(" or a list of card objects [{\"title\": \"Card A\", \"description\": \"...\"}, ...]")
exit(1)
sys.exit(1)
boardid = sys.argv[2]
listid = sys.argv[3]
authorid = sys.argv[4]
@@ -1547,16 +1509,16 @@ if arguments >= 1:
cardsjsonfile = sys.argv[6] if arguments > 5 else None
if not cardsjsonfile:
print("CARDSJSONFILE is required")
exit(1)
sys.exit(1)
try:
with open(cardsjsonfile, 'r') as f:
raw_cards = json.load(f)
except Exception as e:
print("Error reading CARDSJSONFILE: {}".format(e))
exit(1)
sys.exit(1)
if not isinstance(raw_cards, list) or len(raw_cards) == 0:
print("CARDSJSONFILE must contain a non-empty JSON list")
exit(1)
sys.exit(1)
cards = []
for item in raw_cards:
if isinstance(item, str):
@@ -1565,7 +1527,7 @@ if arguments >= 1:
cards.append(item)
else:
print("Each card must be a string (title) or an object")
exit(1)
sys.exit(1)
bulk_url = wekanurl + apiboards + boardid + s + l + s + listid + s + cs + s + 'bulk'
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey), 'Content-Type': 'application/json'}
post_data = {'authorId': authorid, 'swimlaneId': swimlaneid, 'cards': cards}
@@ -1579,7 +1541,7 @@ if arguments >= 1:
if arguments < 3:
print("Usage: python3 api.py bulkdeletecards BOARDID CARDIDS")
print("CARDIDS = comma-separated cardIds")
exit(1)
sys.exit(1)
boardid = sys.argv[2]
cardids_raw = sys.argv[3]
cardids = [x for x in cardids_raw.split(',') if x]
@@ -1596,7 +1558,7 @@ if arguments >= 1:
if arguments < 5:
print("Usage: python3 api.py bulkcardlabels BOARDID CARDIDS ADDLABELIDS REMOVELABELIDS")
print("CARDIDS / ADDLABELIDS / REMOVELABELIDS = comma-separated, or '' for none")
exit(1)
sys.exit(1)
boardid = sys.argv[2]
cardids = [x for x in sys.argv[3].split(',') if x]
add_labelids = [x for x in sys.argv[4].split(',') if x]
@@ -1613,7 +1575,7 @@ if arguments >= 1:
# ------- CREATE LINKED CARD START -----------
if arguments < 6:
print("Usage: python3 api.py linkcard BOARDID LISTID SWIMLANEID AUTHORID LINKEDCARDID")
exit(1)
sys.exit(1)
boardid = sys.argv[2]
listid = sys.argv[3]
swimlaneid = sys.argv[4]
@@ -1631,7 +1593,7 @@ if arguments >= 1:
# ------- ADD/REMOVE CARD MEMBER / ASSIGNEE START -----------
if arguments < 5:
print("Usage: python3 api.py {} BOARDID LISTID CARDID USERID".format(sys.argv[1]))
exit(1)
sys.exit(1)
boardid = sys.argv[2]
listid = sys.argv[3]
cardid = sys.argv[4]
@@ -1651,14 +1613,14 @@ if arguments >= 1:
# ------- COPY CARD START -----------
if arguments < 4:
print("Usage: python3 api.py copycard BOARDID LISTID CARDID TOSWIMLANEID [TOBOARDID] [TOLISTID] [POSITION]")
exit(1)
sys.exit(1)
boardid = sys.argv[2]
listid = sys.argv[3]
cardid = sys.argv[4]
toswimlaneid = sys.argv[5] if arguments > 4 else None
if not toswimlaneid:
print("TOSWIMLANEID is required")
exit(1)
sys.exit(1)
toboardid = sys.argv[6] if arguments > 5 else boardid
tolistid = sys.argv[7] if arguments > 6 else listid
url = wekanurl + apiboards + boardid + s + l + s + listid + s + cs + s + cardid + s + 'copy'
@@ -1675,7 +1637,7 @@ if arguments >= 1:
# ------- COPY/MOVE SWIMLANE START -----------
if arguments < 3:
print("Usage: python3 api.py {} BOARDID SWIMLANEID [TOBOARDID] [POSITION]".format(sys.argv[1]))
exit(1)
sys.exit(1)
boardid = sys.argv[2]
swimlaneid = sys.argv[3]
toboardid = sys.argv[4] if arguments > 3 else boardid
@@ -1694,7 +1656,7 @@ if arguments >= 1:
# ------- COPY/MOVE LIST START -----------
if arguments < 3:
print("Usage: python3 api.py {} BOARDID LISTID [TOSWIMLANEID] [TOBOARDID] [POSITION]".format(sys.argv[1]))
exit(1)
sys.exit(1)
boardid = sys.argv[2]
listid = sys.argv[3]
toswimlaneid = sys.argv[4] if arguments > 3 else None
@@ -1733,7 +1695,7 @@ if arguments >= 1:
# ------- GET CARD SETTINGS START -----------
if arguments < 2:
print("Usage: python3 api.py getcardsettings BOARDID")
exit(1)
sys.exit(1)
boardid = sys.argv[2]
url = wekanurl + apiboards + boardid + s + 'cardSettings'
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
@@ -1748,7 +1710,7 @@ if arguments >= 1:
if arguments < 4:
print("Usage: python3 api.py setcardsetting BOARDID KEY VALUE")
print("e.g. python3 api.py setcardsetting BOARDID allowsDueDate true")
exit(1)
sys.exit(1)
boardid = sys.argv[2]
key = sys.argv[3]
value = sys.argv[4]
@@ -1964,40 +1926,35 @@ if arguments >= 5 and sys.argv[1] == 'setcardlocations':
if arguments >= 3 and sys.argv[1] == 'uploadbackground':
boardid = sys.argv[2]
filepath = sys.argv[3]
import base64, os, mimetypes
import os, mimetypes
# Streamed as the request body (the raw upload), never read whole.
params = {'boardId': boardid, 'fileName': os.path.basename(filepath),
'fileType': mimetypes.guess_type(filepath)[0] or 'image/png'}
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey),
'Content-Type': 'application/octet-stream'}
url = wekanurl + 'api/attachment/upload-background'
try:
with open(filepath, 'rb') as f:
base64_data = base64.b64encode(f.read()).decode('utf-8')
response = requests.post(url, headers=headers, params=params, data=f)
except Exception as e:
print(f"Error reading file: {e}")
exit(1)
upload_data = {
'boardId': boardid,
'fileData': base64_data,
'fileName': os.path.basename(filepath),
'fileType': mimetypes.guess_type(filepath)[0] or 'image/png',
}
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey), 'Content-Type': 'application/json'}
url = wekanurl + 'api/attachment/upload-background'
response = requests.post(url, headers=headers, json=upload_data)
sys.exit(1)
print(response.text)
if arguments >= 3 and sys.argv[1] == 'downloadbackground':
boardid = sys.argv[2]
outputpath = sys.argv[3]
import base64
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
# The image itself (?raw=1), written to disk as it arrives.
headers = {'Authorization': 'Bearer {}'.format(apikey)}
url = wekanurl + f'api/attachment/download-background/{boardid}'
response = requests.get(url, headers=headers)
result = response.json()
if result.get('success'):
with open(outputpath, 'wb') as f:
f.write(base64.b64decode(result.get('base64Data')))
print(f"Background saved to: {outputpath}")
print(f"Original filename: {result.get('fileName')}")
print(f"Storage: {result.get('storageBackend')}")
else:
print(f"Download failed: {result}")
with requests.get(url, headers=headers, params={'raw': '1'}, stream=True) as response:
if response.status_code == 200:
with open(outputpath, 'wb') as f:
for chunk in response.iter_content(chunk_size=1024 * 1024):
f.write(chunk)
print(f"Background saved to: {outputpath}")
else:
print(f"Download failed: {response.text}")
# ------- BOARD BACKGROUND IMAGE UPLOAD / DOWNLOAD END -----------
@@ -2064,28 +2021,162 @@ if arguments >= 3 and sys.argv[1] == 'exportboardpdf':
# JSON shape. Sources/formats: trello, wekan, csv, jira, kanboard, excel, deck,
# openproject, github, gitlab, gitea, forgejo.
#
# python3 api.py importboardfrom SOURCE EXPORT.json # e.g. github issues.json (SOURCE = deck/openproject/github/gitlab/gitea/forgejo/asana/zenkit/trello/jira/kanboard/csv/excel/wekan)
# python3 api.py exportboardformat BOARDID FORMAT OUTPUT.json # FORMAT = trello/jira/deck/openproject/github/gitlab/gitea/forgejo/asana/zenkit/kanboard
# python3 api.py importboardfrom SOURCE FILE # e.g. github issues.json, wrike board.xlsx, notion export.zip
# python3 api.py exportboardformat BOARDID FORMAT OUTPUTFILE # e.g. BOARDID wrike board.xlsx
if arguments >= 3 and sys.argv[1] == 'importboardfrom':
# The document importBoard expects for SOURCE, built from the file as the
# import page builds it (models/lib/importSourceShape.js): an Excel workbook as
# { excelBase64 } (Plane's as { xlsxBase64 }), an export .zip as { zipBase64 },
# a CSV/TSV file as rows, a text format as its text, and the rest as JSON.
EXCEL_IMPORT_SOURCES = {'excel', 'planner', 'monday', 'wrike', 'teamwork', 'businessmap'}
ZIP_IMPORT_SOURCES = {'vikunja', 'notion', 'plane'}
TEXT_IMPORT_SOURCES = {'markdown', 'todotxt', 'taskwarrior', 'focalboard', 'todoist', 'meistertask', 'obsidian',
'linear', 'ticktick', 'clickup', 'nullboard', 'pivotal', 'redmine', 'superproductivity',
'quire', 'orgmode', 'leo', 'opml', 'vikunja', 'notion', 'plane'}
def import_document(source, filepath):
import base64, csv, io, os
ext = os.path.splitext(filepath)[1].lower()
if source in EXCEL_IMPORT_SOURCES or (source == 'plane' and ext == '.xlsx'):
with open(filepath, 'rb') as f:
data = base64.b64encode(f.read()).decode('ascii')
return {'xlsxBase64': data} if source == 'plane' else {'excelBase64': data}
if source in ZIP_IMPORT_SOURCES and ext == '.zip':
with open(filepath, 'rb') as f:
return {'zipBase64': base64.b64encode(f.read()).decode('ascii')}
with open(filepath, encoding='utf-8-sig') as f:
text = f.read()
if source == 'csv':
# As the import page: a file whose first line has a tab is read with
# tabs, so a value with a comma in it stays one value; otherwise the
# separator with more of it on the first line, a semicolon or a comma.
first = text.split('\n', 1)[0]
if '\t' in first:
delimiter = '\t'
else:
delimiter = ';' if first.count(';') > first.count(',') else ','
return [row for row in csv.reader(io.StringIO(text), delimiter=delimiter)]
if source in TEXT_IMPORT_SOURCES:
return text
return json.loads(text)
# A WeKan .zip export (the document and its attachment files) is imported as
# a new board by /api/import/zip?newBoard=1, which unpacks it on the server.
def post_wekan_zip(filepath):
with open(filepath, 'rb') as f:
data = f.read()
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey), 'Content-Type': 'application/zip'}
return requests.post(wekanurl + 'api/import/zip', headers=headers, params={'newBoard': '1'}, data=data)
if arguments >= 3 and sys.argv[1] == 'importboardfrom' and sys.argv[2] == 'wekan' and sys.argv[3].lower().endswith('.zip'):
print(post_wekan_zip(sys.argv[3]).text)
elif arguments >= 3 and sys.argv[1] == 'importboardfrom':
source = sys.argv[2]
filepath = sys.argv[3]
with open(filepath) as f:
board = json.load(f)
board = import_document(source, filepath)
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey), 'Content-Type': 'application/json'}
url = wekanurl + apiboards + 'import/' + source
response = requests.post(url, headers=headers, json={'board': board})
body = {'board': board}
# CSV/TSV and Excel: --csv-mapping MAPPING.json says which column holds
# which card field, as the import page's mapping step does, e.g.
# {"columns": {"title": 0, "list": 2}} or {"columns": {"title": 0}, "listName": "To do"}.
if '--csv-mapping' in sys.argv:
with open(sys.argv[sys.argv.index('--csv-mapping') + 1], encoding='utf-8') as f:
body['csvMapping'] = json.load(f)
response = requests.post(url, headers=headers, json=body)
print(response.text)
# Import many boards: every FILE (a directory gives its files, a .zip that is
# not itself one export of SOURCE gives the files inside it), each as its own
# board - what the import page's "Import many boards" does. --split makes one
# board per project (per swimlane) of each file, for the generalized importer.
# python3 api.py importboardsfrom SOURCE [--split] FILE_OR_DIR ...
def many_import_files(source, paths):
import os, zipfile
files = []
for path in paths:
if os.path.isdir(path):
for root, _, names in os.walk(path):
files.extend(os.path.join(root, name) for name in sorted(names) if not name.startswith('.'))
elif path.lower().endswith('.zip') and source not in ZIP_IMPORT_SOURCES and source != 'wekan':
import tempfile
target = tempfile.mkdtemp(prefix='wekan-import-')
with zipfile.ZipFile(path) as archive:
for info in archive.infolist():
name = info.filename
if info.is_dir() or name.startswith('__MACOSX/') or os.path.basename(name).startswith('.'):
continue
if os.path.isabs(name) or '..' in name.split('/'):
continue
archive.extract(info, target)
files.append(os.path.join(target, name))
else:
files.append(path)
return files
if arguments >= 3 and sys.argv[1] == 'importboardsfrom':
source = sys.argv[2]
rest = sys.argv[3:]
split = '--split' in rest
paths = [path for path in rest if path != '--split']
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey), 'Content-Type': 'application/json'}
url = wekanurl + apiboards + 'import/' + source
for filepath in many_import_files(source, paths):
try:
if source == 'wekan' and filepath.lower().endswith('.zip'):
print('{}: {}'.format(filepath, post_wekan_zip(filepath).text))
continue
body = {'board': import_document(source, filepath)}
if split:
body['splitBy'] = 'swimlane'
response = requests.post(url, headers=headers, json=body)
print('{}: {}'.format(filepath, response.text))
except Exception as error:
print('{}: not imported: {}'.format(filepath, error))
# Export all boards: every board the user can export, in FORMAT, in one
# download - Excel as one workbook with a sheet per board, every other format
# as a .zip with one file per board. --boards ID1,ID2 exports only those.
# python3 api.py exportallboards FORMAT OUTPUTFILE [--boards ID1,ID2]
if arguments >= 3 and sys.argv[1] == 'exportallboards':
fmt = sys.argv[2]
outputpath = sys.argv[3]
params = {}
if '--boards' in sys.argv:
params['boardIds'] = sys.argv[sys.argv.index('--boards') + 1]
headers = {'Authorization': 'Bearer {}'.format(apikey)}
response = requests.get(wekanurl + 'api/export-all-boards/' + fmt, headers=headers, params=params)
if response.status_code != 200:
print('Export failed ({}): {}'.format(response.status_code, response.text))
else:
with open(outputpath, 'wb') as f:
f.write(response.content)
print('All boards exported ({}) to: {}'.format(fmt, outputpath))
if arguments >= 4 and sys.argv[1] == 'exportboardformat':
boardid = sys.argv[2]
fmt = sys.argv[3]
outputpath = sys.argv[4]
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
url = wekanurl + apiboards + boardid + s + 'export' + s + fmt
response = requests.get(url, headers=headers)
with open(outputpath, 'w') as f:
f.write(response.text)
# The formats with routes of their own; every tool format is export/FORMAT.
params = {}
if fmt in ('wekan', 'json'):
url = wekanurl + apiboards + boardid + s + 'export'
elif fmt == 'zip':
url = wekanurl + apiboards + boardid + s + 'exportZip'
elif fmt == 'excel':
url = wekanurl + apiboards + boardid + s + 'exportExcel'
elif fmt == 'pdf':
url = wekanurl + apiboards + boardid + s + 'exportPDF'
elif fmt in ('csv', 'scsv', 'tsv'):
url = wekanurl + apiboards + boardid + s + 'export' + s + 'csv'
params = {'delimiter': {'csv': ',', 'scsv': ';', 'tsv': '\t'}[fmt]}
else:
url = wekanurl + apiboards + boardid + s + 'export' + s + fmt
response = requests.get(url, headers=headers, params=params)
# Bytes, so an .xlsx or .zip export is written as it was sent.
with open(outputpath, 'wb') as f:
f.write(response.content)
print('Board exported ({}) to: {}'.format(fmt, outputpath))
if arguments >= 5 and sys.argv[1] == 'importics':
+192 -31
View File
@@ -39,6 +39,11 @@ if /I "%~1"=="help" goto cli_help_exit
if /I "%~1"=="-l" goto cli_list_exit
if /I "%~1"=="--list" goto cli_list_exit
if /I "%~1"=="list" goto cli_list_exit
REM The same non-interactive EVERYTHING as `./build.sh --run-everything [mode]`.
if /I "%~1"=="--run-everything" (
if "%~2"=="" (set "WEKAN_EVERYTHING_MODE=two-worker") else (set "WEKAN_EVERYTHING_MODE=%~2")
goto test_everything
)
call :cli_run %*
exit /b %errorlevel%
:cli_help_exit
@@ -55,6 +60,10 @@ exit /b 0
REM --- Repo root = folder of this script (strip trailing backslash) ---
set "REPO=%~dp0"
if "%REPO:~-1%"=="\" set "REPO=%REPO:~0,-1%"
REM As build.sh: everything this script writes - temporary files, downloads,
REM Node.js, Meteor, the npm cache, Go and the Playwright browsers - stays in
REM .tools\, and those tools come first on PATH (releases\tools-env.bat).
call "%REPO%\releases\tools-env.bat"
cd /d "%REPO%"
REM Give the Meteor build tool and Node processes a larger heap so long
@@ -189,6 +198,11 @@ echo 14^) All databases ^(sequential^): build newest FerretDB v1, run every que
echo against every database with an image for this CPU, compare the answers
echo 15^) Run all FerretDB tests - SEQUENTIAL ^(unit, vet, integration^)
echo 16^) EVERYTHING source one by one ^(Dev server nobuild, all browsers^)
echo 17^) Live tests with your own accounts: GitLab write-back with the mirror's
echo glab login ^(only scratch items it creates and removes^) and the API
echo imports, read only ^(releases\live-tests\README.md^)
echo 18^) Database speed test: the common WeKan queries on every database with an
echo image for this CPU, compared, written to SpeedTest\Results.md
set "choice="
set /p "choice=Choose: "
if "%choice%"=="1" goto test_everything_two
@@ -207,6 +221,8 @@ if "%choice%"=="13" goto count_tests
if "%choice%"=="14" goto test_all_databases
if "%choice%"=="15" goto test_ferretdb
if "%choice%"=="16" goto test_everything_source
if "%choice%"=="17" goto test_live
if "%choice%"=="18" goto test_db_speed
if "%choice%"=="0" goto menu
goto menu_tests
@@ -816,7 +832,13 @@ REM as the image the compose file references, so the following "up -d" runs your
REM freshly built container instead of a possibly-stale prebuilt one. All WeKan
REM compose files reference ghcr.io/wekan/wekan:latest.
if not exist "%REPO%\Dockerfile" ( echo ERROR: Dockerfile not found in %REPO%. & goto end )
set "WK_IMG=ghcr.io/wekan/wekan:latest"
REM The wekan-app image tag THIS compose file uses, as build.sh
REM wekan_docker_build_image reads it; ghcr.io/wekan/wekan:latest only if the
REM file names none. A commented-out image line is not one.
set "WK_IMG="
for /f "tokens=1,* delims=:" %%a in ('findstr /r /c:"^ *image: *[^#]*wekan/wekan" %CF% 2^>nul') do if not defined WK_IMG set "WK_IMG=%%b"
if defined WK_IMG for /f "tokens=* delims= " %%i in ("!WK_IMG!") do set "WK_IMG=%%i"
if not defined WK_IMG set "WK_IMG=ghcr.io/wekan/wekan:latest"
echo ==^> Building wekan-app image from local source, tagging it as: %WK_IMG%
docker build -t %WK_IMG% -f "%REPO%\Dockerfile" "%REPO%"
if errorlevel 1 ( echo ERROR: Docker build failed. & goto end )
@@ -829,17 +851,65 @@ REM ===========================================================================
echo Installing WeKan dependencies for Windows.
rem Full MIME detection uses the libmagic WASM engine/database installed by npm.
rem No external file.exe installation is needed.
echo This uses Chocolatey to install git, curl, Node.js LTS and MongoDB tools,
echo then installs Meteor via npm.
echo This uses Chocolatey to install git, curl and the MongoDB tools, then puts
echo Node.js and Meteor into .tools\ of this checkout - not into the system.
where choco >nul 2>&1
if errorlevel 1 (
echo Installing Chocolatey package manager ...
@"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -InputFormat None -ExecutionPolicy Bypass -Command "iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" && SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin"
)
call choco install -y git curl nodejs-lts mongodb-shell
echo Installing Meteor (npm -g install meteor) ...
call npm -g install meteor
echo Done. Open a new terminal so PATH changes take effect, then re-run this script.
REM Node.js and npm are the versions the Dockerfile builds with (NODE_VERSION,
REM NPM_VERSION), as build.sh installs them - not whatever LTS Chocolatey has.
set "WK_NODE_VERSION="
set "WK_NPM_VERSION="
for /f "tokens=2 delims== " %%v in ('findstr /r /c:"NODE_VERSION=v[0-9]" "%REPO%\Dockerfile"') do if not defined WK_NODE_VERSION set "WK_NODE_VERSION=%%v"
for /f "tokens=2 delims== " %%v in ('findstr /r /c:"NPM_VERSION=[0-9]" "%REPO%\Dockerfile"') do if not defined WK_NPM_VERSION set "WK_NPM_VERSION=%%v"
if defined WK_NODE_VERSION set "WK_NODE_VERSION=%WK_NODE_VERSION:v=%"
call choco install -y git curl mongodb-shell
REM Node.js from nodejs.org's zip, unpacked into .tools\node-v<version>-win-<arch>
REM and verified against its published SHASUMS256.txt first - not a system-wide
REM Chocolatey install. curl.exe and tar.exe come with Windows 10 and 11.
if not defined WK_NODE_VERSION (
echo ERROR: could not read NODE_VERSION from the Dockerfile.
goto end
)
set "WK_NODE_ARCH=x64"
if /I "%PROCESSOR_ARCHITECTURE%"=="ARM64" set "WK_NODE_ARCH=arm64"
set "WK_NODE_NAME=node-v%WK_NODE_VERSION%-win-%WK_NODE_ARCH%"
if exist "%WEKAN_TOOLS_DIR%\%WK_NODE_NAME%\node.exe" (
echo Node.js %WK_NODE_VERSION% is already in .tools\%WK_NODE_NAME%.
) else (
echo Installing Node.js %WK_NODE_VERSION% ^(Dockerfile NODE_VERSION^) into .tools ...
curl -fsSL -o "%TEMP%\%WK_NODE_NAME%.zip" "https://nodejs.org/dist/v%WK_NODE_VERSION%/%WK_NODE_NAME%.zip" || goto end
curl -fsSL -o "%TEMP%\SHASUMS256.txt" "https://nodejs.org/dist/v%WK_NODE_VERSION%/SHASUMS256.txt" || goto end
powershell -NoProfile -Command "$want = ((Select-String -Path $env:TEMP\SHASUMS256.txt -Pattern ([regex]::Escape($env:WK_NODE_NAME + '.zip') + '$')).Line -split '\s+')[0]; $got = (Get-FileHash -Algorithm SHA256 ($env:TEMP + '\' + $env:WK_NODE_NAME + '.zip')).Hash; if (-not $want -or $got -ne $want.ToUpper()) { Write-Error ('SHA256 mismatch for ' + $env:WK_NODE_NAME + '.zip'); exit 1 }" || goto end
tar -xf "%TEMP%\%WK_NODE_NAME%.zip" -C "%WEKAN_TOOLS_DIR%" || goto end
del "%TEMP%\%WK_NODE_NAME%.zip" "%TEMP%\SHASUMS256.txt"
)
REM npm's global installs (.tools\npm-global) before Node.js's own npm, so the
REM npm installed next is the one found.
set "PATH=%npm_config_prefix%;%WEKAN_TOOLS_DIR%\%WK_NODE_NAME%;%PATH%"
if defined WK_NPM_VERSION (
echo Installing npm %WK_NPM_VERSION% ^(Dockerfile NPM_VERSION^) ...
call npm install -g npm@%WK_NPM_VERSION%
)
REM Meteor as https://docs.meteor.com/about/install.html installs it on Windows,
REM Linux and macOS alike: npx meteor. The meteor npm package is only an
REM installer; npx runs it directly, into %LOCALAPPDATA%\.meteor - so
REM LOCALAPPDATA is .tools for the installer only, and Meteor lands in
REM .tools\.meteor. npm -g install meteor ran the same installer as an install
REM script, which npm 12 refuses to run.
if exist "%WEKAN_TOOLS_DIR%\.meteor\meteor.bat" (
echo Meteor is already installed in .tools\.meteor.
) else (
echo Installing Meteor ^(npx meteor^) into .tools\.meteor ...
setlocal
set "LOCALAPPDATA=%WEKAN_TOOLS_DIR%"
call npx -y meteor
endlocal
)
call "%REPO%\releases\tools-env.bat"
echo Done. build.bat puts .tools\ Node.js and Meteor on PATH itself; re-run this script.
goto end
REM ===========================================================================
@@ -918,6 +988,40 @@ powershell -NoProfile -Command "& cmd /d /c $env:WEKAN_LOG_COMMAND 2>&1 | Tee-Ob
exit /b %errorlevel%
:buildcommon
REM The same diagnostics as build.sh build_wekan: npm verbose output and
REM foreground install scripts, METEOR_PROFILE and the resolver command/output
REM tracer, so a failed Windows build leaves the same evidence in its log. Node
REM module resolution (one line per require, gigabytes a minute) only with
REM WEKAN_BUILD_TRACE_MODULES=1. WEKAN_BUILD_HEAP_SNAPSHOT=1 caps the heap at
REM WEKAN_BUILD_HEAP_SNAPSHOT_MB (4096) and writes a heap snapshot near the limit.
setlocal
set "npm_config_loglevel=verbose"
set "npm_config_foreground_scripts=true"
if not defined METEOR_PROFILE set "METEOR_PROFILE=100"
if "%WEKAN_BUILD_TRACE_MODULES%"=="1" (
if defined NODE_DEBUG (set "NODE_DEBUG=!NODE_DEBUG!,module") else (set "NODE_DEBUG=module")
)
if "%WEKAN_BUILD_HEAP_SNAPSHOT%"=="1" (
if not defined WEKAN_BUILD_HEAP_SNAPSHOT_MB set "WEKAN_BUILD_HEAP_SNAPSHOT_MB=4096"
set "TOOL_NODE_FLAGS=--max-old-space-size=!WEKAN_BUILD_HEAP_SNAPSHOT_MB! --heapsnapshot-near-heap-limit=1"
set "NODE_OPTIONS=--max-old-space-size=!WEKAN_BUILD_HEAP_SNAPSHOT_MB! --heapsnapshot-near-heap-limit=1"
echo Heap snapshot: ON, heap capped at !WEKAN_BUILD_HEAP_SNAPSHOT_MB! MB; the build will fail sooner and write Heap.*.heapsnapshot.
)
set "NODE_OPTIONS=%NODE_OPTIONS% --require="%REPO%\tools\build-command-output.cjs""
echo Build diagnostics: resolver command/output tracing, npm verbose output, foreground install scripts, METEOR_PROFILE=%METEOR_PROFILE% ^(milliseconds^).
call :buildcommon_steps
set "BUILD_RC=%errorlevel%"
if not "%BUILD_RC%"=="0" if defined WEKAN_BUILD_LOG (
findstr /c:"JavaScript heap out of memory" "%WEKAN_BUILD_LOG%" >nul 2>&1 && (
echo.
echo The build ran out of JavaScript heap ^(allowed %WEKAN_BUILD_HEAP_MB% MB^).
echo To see what fills it, run once with WEKAN_BUILD_HEAP_SNAPSHOT=1 and open the
echo Heap.*.heapsnapshot it writes in Chrome DevTools: Memory -^> Load.
)
)
endlocal & exit /b %BUILD_RC%
:buildcommon_steps
if exist "%REPO%\node_modules" call :build_logged rmdir /s /q "%REPO%\node_modules"
if exist "%REPO%\node_modules\.cache" call :build_logged rmdir /s /q "%REPO%\node_modules\.cache"
if exist "%REPO%\.meteor\local" call :build_logged rmdir /s /q "%REPO%\.meteor\local"
@@ -972,7 +1076,7 @@ goto end
call :ensure_dirs
call :set_dev_env
set "WARN_WHEN_USING_OLD_API=true"
set "NODE_OPTIONS=--trace-warnings --max-old-space-size=8192"
set "NODE_OPTIONS=--trace-warnings --max-old-space-size=%WEKAN_BUILD_HEAP_MB%"
set "ROOT_URL=http://localhost:3000"
call :runlog --port 3000
goto end
@@ -1019,24 +1123,40 @@ call :runlog --port %PORT%
goto end
:dev_customurl
REM Parity with build.sh's "CUSTOM PORT + SUBDOMAIN": ask for the port and the
REM ROOT_URL host. An empty answer or a bare name is local, so the port is added;
REM a full URL or a dotted name is public/proxied and the port is NOT added -
REM the same rule build.sh's ask_dev_url applies.
REM Parity with build.sh's "CUSTOM PORT + SUBDOMAIN" (ask_dev_url): ask for the
REM port and the ROOT_URL host, unless WEKAN_DEV_PORT / WEKAN_DEV_ROOT_URL /
REM WEKAN_DEV_HOST already say. A port that is not a number from 1 to 65535 is
REM 3000. Empty is http://localhost:PORT; a bare name is a subdomain of
REM localhost, http://NAME.localhost:PORT; a full URL is used as it is; a dotted
REM name is public/proxied, https://NAME, and the port is NOT added. A trailing
REM slash is removed, as it would double up in built URLs.
call :ensure_dirs
set "DEV_PORT="
set /p "DEV_PORT=Port for the dev server to listen on [3000]: "
set "DEV_PORT=%WEKAN_DEV_PORT%"
if not defined DEV_PORT set /p "DEV_PORT=Port for the dev server to listen on [3000]: "
if not defined DEV_PORT set "DEV_PORT=3000"
echo ROOT_URL: empty or a bare name = local ^(the port is added^);
echo a full URL or a dotted name = public/proxied ^(the port is NOT added^).
set "DEV_HOST="
set /p "DEV_HOST=ROOT_URL [http://localhost:%DEV_PORT%]: "
if not defined DEV_HOST set "DEV_HOST=localhost"
echo %DEV_PORT%| findstr /r /x "[0-9][0-9]*" >nul
if errorlevel 1 (
echo Not a number: '%DEV_PORT%' - using 3000.
set "DEV_PORT=3000"
)
set /a DEV_PORT_N=%DEV_PORT% 2>nul
if %DEV_PORT_N% LSS 1 (echo Port out of range: %DEV_PORT% - using 3000.& set "DEV_PORT=3000")
if %DEV_PORT_N% GTR 65535 (echo Port out of range: %DEV_PORT% - using 3000.& set "DEV_PORT=3000")
set "DEV_HOST=%WEKAN_DEV_ROOT_URL%"
if not defined DEV_HOST set "DEV_HOST=%WEKAN_DEV_HOST%"
if not defined DEV_HOST (
echo ROOT_URL: empty or a bare name = local ^(the port is added^);
echo a full URL or a dotted name = public/proxied ^(the port is NOT added^).
set /p "DEV_HOST=ROOT_URL [http://localhost:%DEV_PORT%]: "
)
if defined DEV_HOST if "!DEV_HOST:~-1!"=="/" set "DEV_HOST=!DEV_HOST:~0,-1!"
set "DEV_ROOT_URL="
echo %DEV_HOST% | findstr /r /c:"^^https*://" >nul && set "DEV_ROOT_URL=%DEV_HOST%"
if not defined DEV_ROOT_URL echo %DEV_HOST% | findstr /r /c:"\." >nul && set "DEV_ROOT_URL=https://%DEV_HOST%"
if not defined DEV_ROOT_URL set "DEV_ROOT_URL=http://%DEV_HOST%:%DEV_PORT%"
echo ROOT_URL=%DEV_ROOT_URL%
if not defined DEV_HOST set "DEV_ROOT_URL=http://localhost:%DEV_PORT%"
if not defined DEV_ROOT_URL echo !DEV_HOST!| findstr /r /c:"://" >nul && set "DEV_ROOT_URL=!DEV_HOST!"
if not defined DEV_ROOT_URL echo !DEV_HOST!| findstr /r /c:"\." >nul && set "DEV_ROOT_URL=https://!DEV_HOST!"
if not defined DEV_ROOT_URL set "DEV_ROOT_URL=http://!DEV_HOST!.localhost:%DEV_PORT%"
echo ==^> Meteor listens on port %DEV_PORT%
echo ==^> ROOT_URL=%DEV_ROOT_URL%
call :set_dev_env
set "ROOT_URL=%DEV_ROOT_URL%"
call :runlog --port %DEV_PORT%
@@ -1335,10 +1455,19 @@ call :tee test_mocha_body
goto end
:test_mocha_body
echo Running Mocha tests: meteor test --once --driver-package meteortesting:mocha --port 3100
echo Running Mocha tests: meteor test --full-app --once --driver-package meteortesting:mocha --port 3100
echo (server-side unit/security/API-logic tests; browser/client tests are covered by Playwright options)
call meteor test --once --driver-package meteortesting:mocha --port 3100
exit /b 0
REM The same run as build.sh: --full-app so the integration tests that need the
REM app's hooks run instead of skipping themselves, the receipt sweep fast enough
REM for its tests, and a build directory of its own so a running dev server's
REM .meteor\local is not shared. Its exit status is the run's, so a failing test
REM is not reported as success.
setlocal
set "EMAIL_RECEIPT_SWEEP_INTERVAL_MS=1000"
if not defined WRITABLE_PATH set "WRITABLE_PATH=.."
set "METEOR_LOCAL_DIR=.meteor\local-test"
call meteor test --full-app --once --driver-package meteortesting:mocha --port 3100
endlocal & exit /b %errorlevel%
:test_import
call :onelog import
@@ -1397,7 +1526,10 @@ if /i "%INSTALL_DEPS%"=="y" (
call meteor npm exec playwright install %PW_PROJECT%
)
call :onelog playwright-%PW_PROJECT%
call meteor npm exec playwright test -- --project=%PW_PROJECT% 2>&1 | powershell -NoProfile -Command "$input | Tee-Object -FilePath '%ONELOG%'"
REM tests\playwright\playwright.config.js reads WEKAN_PLAYWRIGHT_PROJECT to run
REM only this browser's project, as build.sh run_playwright_single sets it.
set "WEKAN_PLAYWRIGHT_PROJECT=%PW_PROJECT%"
call meteor npm exec playwright test -- --project=%PW_PROJECT% 2>&1 | powershell -NoProfile -Command "$input | Tee-Object -FilePath '%ONELOG%'; exit $LASTEXITCODE"
goto end
REM ===========================================================================
@@ -1464,17 +1596,22 @@ echo Ensuring .eslintrc.json includes @typescript-eslint plugin and no-floating-
node -e "const fs=require('fs');const p='.eslintrc.json';const c=JSON.parse(fs.readFileSync(p,'utf8'));c.plugins=Array.isArray(c.plugins)?c.plugins:[];if(!c.plugins.includes('@typescript-eslint'))c.plugins.push('@typescript-eslint');c.rules=c.rules||{};c.rules['@typescript-eslint/no-floating-promises']='error';fs.writeFileSync(p,JSON.stringify(c,null,2)+'\n');"
echo Checking whether @typescript-eslint/no-floating-promises is configured in .eslintrc.json
REM FAIL, not a warning, as in build.sh floating_promises_checks: a guard that
REM only warns lets an unawaited permission check through.
set "FLOATING_RC=0"
findstr /c:"@typescript-eslint/no-floating-promises" .eslintrc.json >nul 2>&1
if errorlevel 1 (
echo WARNING: Rule @typescript-eslint/no-floating-promises is NOT configured in .eslintrc.json
echo FAIL: Rule @typescript-eslint/no-floating-promises is NOT configured in .eslintrc.json
set "FLOATING_RC=1"
) else (
echo OK: Rule @typescript-eslint/no-floating-promises is configured in .eslintrc.json
)
echo.
echo Scanning for unawaited Authentication.checkBoardAccess/checkBoardWriteAccess in server\models
node -e "const fs=require('fs'),path=require('path');function walk(d,acc){for(const e of fs.readdirSync(d,{withFileTypes:true})){const fp=path.join(d,e.name);if(e.isDirectory())walk(fp,acc);else if(/\.js$/.test(e.name))acc.push(fp);}return acc;}let found=false;for(const f of walk('server/models',[])){const lines=fs.readFileSync(f,'utf8').split(/\r?\n/);lines.forEach((ln,i)=>{if(/Authentication\.checkBoard(Access|WriteAccess)\(/.test(ln)&&!/await Authentication\.checkBoard/.test(ln)){found=true;console.log(f+':'+(i+1)+': '+ln.trim());}});}console.log(found?'WARNING: Found possible unawaited board auth checks above':'OK: No unawaited board auth checks found');"
exit /b 0
node -e "const fs=require('fs'),path=require('path');function walk(d,acc){for(const e of fs.readdirSync(d,{withFileTypes:true})){const fp=path.join(d,e.name);if(e.isDirectory())walk(fp,acc);else if(/\.js$/.test(e.name))acc.push(fp);}return acc;}let found=false;for(const f of walk('server/models',[])){const lines=fs.readFileSync(f,'utf8').split(/\r?\n/);lines.forEach((ln,i)=>{if(/Authentication\.checkBoard(Access|WriteAccess)\(/.test(ln)&&!/await Authentication\.checkBoard/.test(ln)){found=true;console.log(f+':'+(i+1)+': '+ln.trim());}});}console.log(found?'FAIL: these board auth checks are not awaited':'OK: No unawaited board auth checks found');process.exit(found?1:0);"
if errorlevel 1 set "FLOATING_RC=1"
exit /b %FLOATING_RC%
REM ===========================================================================
:save_deps
@@ -1490,6 +1627,13 @@ for /f "usebackq delims=" %%P in (`powershell.exe -NoProfile -Command "[Environm
if not "%FORGE_INSTALL_STATUS%"=="0" echo One or more mirror tools could not be installed. See MISSING entries above.
exit /b %FORGE_INSTALL_STATUS%
REM ===========================================================================
:test_live
REM releases/live-tests/run.mjs runs every live test there (the GitLab one is
REM releases\live-tests\gitlab-write-back.mjs); each skips a service with no login.
node "%REPO%\releases\live-tests\run.mjs"
goto end
REM ===========================================================================
:mirror_forge
call "%REPO%\releases\mirror.bat"
@@ -1665,6 +1809,9 @@ REM Best-effort by image name; the per-port free below does the real work and is
REM narrow enough not to touch unrelated Node apps.
taskkill /F /IM meteor.exe /T >nul 2>&1
taskkill /F /IM mongod.exe /T >nul 2>&1
REM `rspack build --watch` listens on no port, so the per-port free below cannot
REM find it; stop it by its command line, as build.sh's pkill does.
powershell -NoProfile -Command "Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -match 'rspack build --watch' } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }" >nul 2>&1
for %%p in (%DEV_SERVER_PORTS%) do call :free_port %%p
REM Wait for the ports to free (up to ~10s).
for /l %%i in (1,1,10) do (
@@ -1833,7 +1980,7 @@ netstat -ano | findstr /r /c:":3001 .*LISTENING" >nul 2>&1
if not errorlevel 1 (
echo ==^> Reusing the MongoDB already listening on :3001 ^(not started or stopped by this run^).
) else (
for %%i in ("%REPO%\..\mongodb-test-3001") do set "DBPATH=%%~fi"
for %%i in ("%REPO%\.tools\mongodb-test-3001") do set "DBPATH=%%~fi"
if not exist "!DBPATH!" md "!DBPATH!"
echo ==^> Starting MongoDB ^(Meteor's mongod^) on :3001, dbpath !DBPATH!.
start "WekanTestMongo" /MIN "%MONGOD_BIN%" --port 3001 --dbpath "!DBPATH!" --bind_ip 127.0.0.1 --logpath "%RUN_LOGDIR%\wekan-test-mongod.log"
@@ -1940,6 +2087,20 @@ if errorlevel 1 (
bash ./releases/db-conformance.sh
goto end
REM ===========================================================================
:test_db_speed
REM build.sh's "Database speed test": db-conformance.sh's databases, started the
REM same way, each loaded with the same dummy WeKan data and timed on the common
REM WeKan queries. See docs\Databases\FerretDB\1\SpeedTest\README.md.
where bash >nul 2>&1
if errorlevel 1 (
echo ERROR: bash was not found. It comes with Git for Windows ^(Git Bash^) and with WSL.
echo Install either, or run this from WSL: ./docs/Databases/FerretDB/1/SpeedTest/speedtest.sh
goto end
)
bash ./docs/Databases/FerretDB/1/SpeedTest/speedtest.sh
goto end
REM ===========================================================================
:test_ferretdb
REM All of FerretDB's own tests, one at a time: unit, vet, integration.
+147 -55
View File
@@ -103,6 +103,10 @@ WEKAN_TOOLS_DIR="$WEKAN_DIR/.tools"
export WEKAN_DIR WEKAN_TOOLS_DIR
export PATH="$WEKAN_TOOLS_DIR/bin:${GOBIN:+$GOBIN:}$PATH"
. "$WEKAN_DIR/releases/ensure-tools.sh"
# Everything this script and the releases/ scripts write - temporary files,
# downloads, Meteor, Node.js, the npm cache - stays in .tools/, on Linux, macOS
# and Windows alike (releases/tools-env.sh).
. "$WEKAN_DIR/releases/tools-env.sh"
# Prefer the repository-local toolchain installed under .tools. A fresh shell
# does not normally have .tools/.meteor on PATH, which made unattended test and
@@ -112,6 +116,8 @@ if [ -x "$WEKAN_TOOLS_DIR/.meteor/meteor" ]; then
PATH="$WEKAN_TOOLS_DIR/.meteor:$PATH"
fi
_wekan_node_version="$(sed -n 's/.*NODE_VERSION=v\([^ \\]*\).*/\1/p' "$WEKAN_DIR/Dockerfile" | head -1)"
# npm comes from the Dockerfile too (CLAUDE.md), never a number typed here.
_wekan_npm_version="$(sed -n 's/.*NPM_VERSION=\([^ \\]*\).*/\1/p' "$WEKAN_DIR/Dockerfile" | head -1)"
case "$(uname -m 2>/dev/null)" in
x86_64|amd64) _wekan_node_arch=x64 ;;
aarch64|arm64) _wekan_node_arch=arm64 ;;
@@ -462,10 +468,17 @@ function build_wekan(){
echo "===== wekan build started $(date '+%F %T') ====="
export npm_config_loglevel=verbose npm_config_foreground_scripts=true
export METEOR_PROFILE="${METEOR_PROFILE:-100}"
export NODE_DEBUG="${NODE_DEBUG:+$NODE_DEBUG,}module"
# Node module resolution, one line per require. OFF unless
# WEKAN_BUILD_TRACE_MODULES=1: a Meteor build writes it at gigabytes a
# minute - 1.1 GB in the first three minutes of a menu dev build, over
# 12 GB in the first half hour of EVERYTHING on macOS, written twice
# through tee - and slows the build it is tracing.
if [ "${WEKAN_BUILD_TRACE_MODULES:-0}" = 1 ]; then
export NODE_DEBUG="${NODE_DEBUG:+$NODE_DEBUG,}module"
fi
# NODE_OPTIONS parses quoted paths, including checkouts with spaces.
export NODE_OPTIONS="${NODE_OPTIONS:-} --require=\"$(pwd)/tools/build-command-output.cjs\""
echo "Build diagnostics: resolver command/output tracing, Node module resolution, npm verbose output, foreground install scripts, METEOR_PROFILE=$METEOR_PROFILE (milliseconds)."
echo "Build diagnostics: resolver command/output tracing, npm verbose output, foreground install scripts, METEOR_PROFILE=$METEOR_PROFILE (milliseconds), Node module resolution $([ "${WEKAN_BUILD_TRACE_MODULES:-0}" = 1 ] && echo on || echo 'off (WEKAN_BUILD_TRACE_MODULES=1 turns it on)')."
build_stage "1/4 Remove dependencies and build caches" rm -rf node_modules node_modules/.cache .meteor/local .build _build || return $?
# Updating npm metadata has historically been best effort; installation is required.
build_stage "2/4 Compile app to resolve Meteor plugin npm dependencies" meteor update --npm || echo "WARNING: Meteor plugin npm dependency compilation failed; trying dependency installation."
@@ -639,8 +652,18 @@ function run_playwright_docker(){
local docker_base_url="${WEKAN_BASE_URL:-http://127.0.0.1:3000}"
local docker_mongo_url="${WEKAN_MONGO_URL:-mongodb://127.0.0.1:3001/meteor}"
local docker_host_proxy_port=""
local docker_network="host"
# Docker Desktop on macOS keeps the host outside the container's loopback.
if [ "$(uname -s)" = Darwin ]; then
# Its host network loses connections to host.docker.internal: from a
# container on that network about 1 connect in 75 never completed, so a
# test page waited 60 s for a document that never came (Firefox, 2026-11-01:
# 8 of 600 direct connects, against 0 of 1000 from the bridge network).
# Nothing here needs the host network on macOS - WeKan and Mongo are reached
# through host.docker.internal and SMTP through docker exec - so the
# container uses Docker's bridge network, where the proxy below still
# serves localhost.
docker_network="bridge"
# Meteor's dynamic imports use ROOT_URL (localhost in the test server).
# Keep the browser on that same origin and bridge its loopback to the host.
# Browsing host.docker.internal while ROOT_URL is localhost makes Firefox
@@ -664,35 +687,53 @@ function run_playwright_docker(){
mkdir -p "$filesroot"
echo "Running Playwright $browser in Docker ($image)."
echo "Expecting WeKan at $docker_base_url."
local smtp_proxy_pid="" smtp_proxy_ready="" container_name="wekan-playwright-${browser}-$$"
if [ "$(uname -s)" = Darwin ] && [ -n "${WEKAN_TEST_SMTP_PORT:-}" ]; then
smtp_proxy_ready="$(mktemp "$WEKAN_DIR/.tools/tmp/smtp-proxy.XXXXXX")"
node "$pwdir/helpers/docker-smtp-proxy.cjs" "$container_name" "$WEKAN_TEST_SMTP_PORT" "$smtp_proxy_ready" &
smtp_proxy_pid=$!
for i in 1 2 3 4 5 6 7 8 9 10; do
[ -s "$smtp_proxy_ready" ] && break
kill -0 "$smtp_proxy_pid" 2>/dev/null || break
sleep 0.1
local container_name="wekan-playwright-${browser}-$$"
# Docker Desktop keeps the container's loopback from the host, so a server a
# spec starts in the container cannot be reached by WeKan outside it. Each
# port below is forwarded from the host into the container through docker
# exec (helpers/docker-smtp-proxy.cjs, which carries any TCP): the test SMTP
# server, and a small pool for the mock Jira/GitLab servers of the list-sync
# specs, which take a free port from WEKAN_TEST_REVERSE_PORTS
# (helpers/reachable-server.cjs). Elsewhere they listen on any port.
local forward_pids="" forward_ready="" reverse_ports=""
if [ "$(uname -s)" = Darwin ]; then
reverse_ports="${WEKAN_TEST_REVERSE_PORTS:-39101 39102 39103 39104}"
local fport fready fpid i
for fport in ${WEKAN_TEST_SMTP_PORT:-} $reverse_ports; do
fready="$(mktemp "$WEKAN_DIR/.tools/tmp/docker-port-proxy.XXXXXX")"
node "$pwdir/helpers/docker-smtp-proxy.cjs" "$container_name" "$fport" "$fready" &
fpid=$!
forward_pids="$forward_pids $fpid"; forward_ready="$forward_ready $fready"
for i in 1 2 3 4 5 6 7 8 9 10; do
[ -s "$fready" ] && break
kill -0 "$fpid" 2>/dev/null || break
sleep 0.1
done
if [ ! -s "$fready" ]; then
kill $forward_pids 2>/dev/null; wait $forward_pids 2>/dev/null
rm -f $forward_ready
echo "ERROR: local test proxy for port $fport did not start."
return 1
fi
done
if [ ! -s "$smtp_proxy_ready" ]; then
kill "$smtp_proxy_pid" 2>/dev/null; wait "$smtp_proxy_pid" 2>/dev/null
rm -f "$smtp_proxy_ready"
echo "ERROR: local SMTP test proxy did not start."
return 1
fi
fi
# Mount the whole repo so specs that reach the repo-root node_modules
# (e.g. @wekanteam/exceljs) and .tools resolve; run from tests/playwright.
# The repo and the files root are mounted at their OWN host paths, so a path
# names the same file for the specs in here and for the server outside:
# admin-continuous-backup gives the server a key file it wrote and reads the
# stream the server wrote, and with the repo at /repo neither side found the
# other's file.
# Run as the host user (--user) with a writable HOME so the container does
# not leave root-owned files under test-results/ (which would later make
# native Chromium/Firefox runs fail with "EACCES: permission denied, mkdir
# .../test-results/.playwright-artifacts-N").
docker_exec run --rm --init --ipc=host --network host \
docker_exec run --rm --init --ipc=host --network "$docker_network" \
--name "$container_name" \
--label org.wekan.test-run=everything \
--user "$(id -u):$(id -g)" \
-e HOME=/repo/.tools/tmp \
-e TMPDIR=/repo/.tools/tmp \
-e HOME="$reporoot/.tools/tmp" \
-e TMPDIR="$reporoot/.tools/tmp" \
-e WEKAN_BASE_URL="$docker_base_url" \
-e WEKAN_DOCKER_HOST_PROXY_PORT="$docker_host_proxy_port" \
-e WEKAN_MONGO_URL="$docker_mongo_url" \
@@ -704,16 +745,17 @@ function run_playwright_docker(){
-e WEKAN_TEST_SERIAL="${WEKAN_TEST_SERIAL:-0}" \
-e WEKAN_TEST_SERVER_MODE="${WEKAN_TEST_SERVER_MODE:-bundle}" \
-e WEKAN_TEST_SMTP_PORT="${WEKAN_TEST_SMTP_PORT:-}" \
-e WEKAN_TEST_REVERSE_PORTS="$reverse_ports" \
-e PLAYWRIGHT_HTML_OPEN=never \
-e WEKAN_FILES_PATH=/wekan-files \
-e WEKAN_FILES_PATH="$filesroot" \
-e PLAYWRIGHT_JSON_OUTPUT_NAME="${PLAYWRIGHT_JSON_OUTPUT_NAME:-}" \
-v "$filesroot":/wekan-files \
-v "$reporoot":/repo -w /repo/tests/playwright \
-v "$filesroot":"$filesroot" \
-v "$reporoot":"$reporoot" -w "$pwdir" \
"$image" \
sh -c '
export PATH=/repo/tests/playwright/node_modules/.bin:$PATH
export PATH="$PWD/node_modules/.bin:$PATH"
if [ -n "$WEKAN_DOCKER_HOST_PROXY_PORT" ]; then
node /repo/tests/playwright/helpers/docker-host-proxy.cjs &
node "$PWD/helpers/docker-host-proxy.cjs" &
proxy_pid=$!
trap '\''kill "$proxy_pid" 2>/dev/null || true; wait "$proxy_pid" 2>/dev/null || true'\'' EXIT
ready=0
@@ -726,9 +768,9 @@ function run_playwright_docker(){
npx playwright test --project="$0" "$@"
' "$browser" "$@"
local playwright_rc=$?
if [ -n "$smtp_proxy_pid" ]; then
kill "$smtp_proxy_pid" 2>/dev/null; wait "$smtp_proxy_pid" 2>/dev/null
rm -f "$smtp_proxy_ready"
if [ -n "$forward_pids" ]; then
kill $forward_pids 2>/dev/null; wait $forward_pids 2>/dev/null
rm -f $forward_ready
fi
return "$playwright_rc"
}
@@ -1214,7 +1256,7 @@ function run_all_tests(){
PW_FAILURES=""
TEST_SERVER_PID=""
MONGOD_PID=""
STATDIR="$(mktemp -d)"
STATDIR="$(mktemp -d "$TMPDIR/wekan-stat.XXXXXX")"
BPIDS=""
# STOP WHAT THIS RUN STARTED, on EVERY way out - not only the happy path.
@@ -1373,6 +1415,10 @@ function run_all_tests(){
# The Docker and Snap default, so the browser tests see the framing policy
# (FrameBleed) the way a real install sends it.
export BROWSER_POLICY_ENABLED=true
# Every page load refreshes the login cookie, and Meteor allows 30 per 10
# seconds per address; the whole browser suite comes from localhost, so a
# quick run signed its own test users out (server/accounts-common.js).
export ACCOUNTS_COOKIE_REFRESH_RATE_LIMIT="${ACCOUNTS_COOKIE_REFRESH_RATE_LIMIT:-100000}"
if [ "${WEKAN_TEST_SERVER_MODE:-bundle}" = source ]; then
export WEKAN_PLAYWRIGHT_PROBE=0
local WRITABLE_ABS="$WEKAN_DIR/.tools/test-writable"
@@ -1496,7 +1542,11 @@ function run_all_tests(){
echo "ERROR: nothing is listening on :$TEST_DB_PORT and no mongod was found (Meteor dev_bundle or PATH)."
echo " Meteor's bundled mongod is normally at <dev_bundle>/mongodb/bin/mongod."; rm -rf "$STATDIR"; return 1
fi
local DBPATH="../mongodb-test-$TEST_DB_PORT"
# In .tools/, where everything this script writes lives (CLAUDE.md;
# tests/toolsDirOnly.test.cjs). It was in the checkout's PARENT directory,
# which a sandboxed editor cannot write - the test database never
# started there.
local DBPATH="$WEKAN_DIR/.tools/mongodb-test-$TEST_DB_PORT"
mkdir -p "$DBPATH"
echo "==> Starting MongoDB (Meteor's mongod) on :$TEST_DB_PORT, dbpath $DBPATH."
{ echo "===== mongod :$TEST_DB_PORT - started $(date '+%Y-%m-%d %H:%M:%S %Z') ====="; exec "$MONGOD_BIN" --port "$TEST_DB_PORT" --dbpath "$DBPATH" --bind_ip 127.0.0.1 --nounixsocket; } > "$RUN_LOGDIR/wekan-test-mongod.log" 2>&1 &
@@ -1524,7 +1574,7 @@ function run_all_tests(){
export WEKAN_FILES_PATH_HOST="$WRITABLE_ABS/files"
# ...and the same path for a LOCAL playwright run, not only the Docker one.
# Only WEKAN_FILES_PATH_HOST was exported, which is read when the browsers run
# in a container (it is mounted there as /wekan-files). A local run fell back
# in a container (it is mounted there at the same path). A local run fell back
# to the spec's own default of .build/bundle/files, so a spec that writes a
# stored attachment wrote it OUTSIDE the root this server reads - and then
# tested what WeKan does with a file it does not own.
@@ -2383,8 +2433,8 @@ function git_fix_changelog_links(){
local script="$WEKAN_DIR/releases/fix-changelog-hashes.sh"
[ -f "$script" ] || return 1
bash "$script" || return 1
if [ -n "$(git status --porcelain -- CHANGELOG.md 2>/dev/null)" ]; then
git add CHANGELOG.md
if [ -n "$(git status --porcelain -- CHANGELOG-big.md CHANGELOG.md 2>/dev/null)" ]; then
git add CHANGELOG-big.md CHANGELOG.md
git commit -q -m "CHANGELOG: repoint commit links after history moved.
A rebase rewrote the commits these entries link to, so the links named commits
@@ -2595,7 +2645,7 @@ choose() {
# snap/bundle, not on a maintainer's machine) and the superseded old-*.sh and
# translations/fill_translations.py.
# Release notes summarize translations as affected languages; other changelog
# details stay in CHANGELOG.md. Notes include only In short, Security,
# details stay in CHANGELOG-big.md. Notes include only In short, Security,
# Translations language list, thanks and the changelog link. Binary provenance remains
# a build artifact and must not be appended by any release entry point.
RELEASE_SCRIPTS=( "Release|Release All: audit, commit, push and build with Actions|releases/release-all.sh|||"
@@ -2647,6 +2697,7 @@ RELEASE_SCRIPTS=( "Release|Release All: audit, commit, push and build with Actio
"Bundles|Fetch the built bundle from the ppc64le build host|releases/up-o.sh|WeKan version, e.g. 10.50||"
"Bundles|Fetch the built bundle from the s390x build host|releases/up-s.sh|WeKan version, e.g. 10.50||"
"Bundles|Upload the Windows bundle to the download server|releases/up-w.sh|WeKan version, e.g. 10.50||"
"Docker images|Test Nextcloud 32-35 with native local services|tests/nextcloud/run.sh|Nextcloud majors (default 32 33 34 35)|linux|nextcloud-tests"
"Docker images|Build the WeKan Docker image|releases/docker-build.sh|||"
"Docker images|Create the multi-platform buildx builder|releases/docker-build-deps.sh|||"
"Docker images|Publish a variant image (wekan-gantt-gpl / wekan-ondra)|releases/docker-publish-variant.sh|Image and version, e.g. wekan-gantt-gpl 10.50||"
@@ -2909,7 +2960,9 @@ while [ -z "$opt" ]; do
"Floating-promises guard|Check floating promises guard (@typescript-eslint/no-floating-promises + auth await scan)" \
"Count tests by category|Count amount of tests by category" \
"Run all FerretDB tests - SEQUENTIAL|Run all FerretDB tests - SEQUENTIAL: unit, vet and the integration suite of the FerretDB subdirectory of this repo, one at a time, logs in log/<datetime>/" \
"All databases (sequential)|Test all databases that have a Docker image for this CPU, SEQUENTIALLY: build newest FerretDB v1 from source, then run every FerretDB v1 query type against each database and compare that they all answer the same (results in log/<datetime>/)" ;;
"Live tests|Run live tests against real services with your own accounts: GitLab write-back with the mirror's glab login (only scratch items it creates and removes) and the API imports, read only (releases/live-tests/README.md); a service with no login here is skipped, log in .tools/log/<datetime>/" \
"All databases (sequential)|Test all databases that have a Docker image for this CPU, SEQUENTIALLY: build newest FerretDB v1 from source, then run every FerretDB v1 query type against each database and compare that they all answer the same (results in log/<datetime>/)" \
"Database speed test|Time the common WeKan queries on every database that has a Docker image for this CPU, SEQUENTIALLY, with twice a production WeKan of dummy data; check they all answer the same and write docs/Databases/FerretDB/1/SpeedTest/Results.md" ;;
"Tools")
choose "Tools" \
"Save Meteor deps list|Save Meteor dependency chain to ../meteor-deps.txt" \
@@ -2963,12 +3016,35 @@ for _once in 1; do
;;
*) echo "Unsupported Linux distribution; install a C/C++ toolchain, git, curl, wget, 7zip, zip, unzip, file and npm." >&2; exit 1 ;;
esac
#sudo chown -R $(id -u):$(id -g) $HOME/.npm
sudo npm -g install n
sudo n "$_wekan_node_version"
sudo npm install -g npm@12.0.2
sudo npm -g install meteor --unsafe-perm
#sudo chown -R $(id -u):$(id -g) $HOME/.npm $HOME/.meteor
# Node.js into .tools/n with n's N_PREFIX, as this user: not into
# /usr/local with sudo. releases/tools-env.sh puts .tools/n/bin on
# PATH, and npm's global installs go to that same prefix.
N_PREFIX="$WEKAN_TOOLS_DIR/n" npx -y n "$_wekan_node_version"
export PATH="$WEKAN_TOOLS_DIR/n/bin:$PATH"
hash -r
npm install -g "npm@${_wekan_npm_version:-latest}"
hash -r
# Meteor the way https://docs.meteor.com/about/install.html installs
# it on Linux, macOS and Windows alike: `npx meteor`, as this user and
# never with sudo. The meteor npm package is only an installer; npx
# runs its cli.js, which installs into $HOME/.meteor and adds that to
# PATH in the shell rc files - so HOME is .tools for the installer
# only, and Meteor and those rc lines land in .tools/. `sudo npm -g install meteor` ran the
# same installer as an install script instead, which npm 12 refuses
# to run (and which also refused --unsafe-perm and the documented
# --foreground-script).
#
# The global meteor package such a setup left behind is only that
# installer, never Meteor itself (~/.meteor is not touched).
sudo npm -g uninstall meteor >/dev/null 2>&1 || true
# npx caches in npm_config_cache (.tools/npm-cache).
mkdir -p "$npm_config_cache"
if [ -x "$WEKAN_TOOLS_DIR/.meteor/meteor" ]; then
echo "Meteor is already installed in $WEKAN_TOOLS_DIR/.meteor"
else
HOME="$WEKAN_TOOLS_DIR" npx -y meteor
fi
. "$WEKAN_DIR/releases/tools-env.sh"
elif [[ "$OSTYPE" == "darwin"* ]]; then
echo "macOS"
# Node comes from nvm, not from Homebrew's node@26 keg.
@@ -2980,13 +3056,14 @@ for _once in 1; do
# puts that on PATH itself, so this does not go stale the way a
# pinned version does. npm comes with the Node it installs, so
# there is no `brew install npm` either.
export NVM_DIR="${NVM_DIR:-$HOME/.nvm}"
# NVM_DIR is .tools/nvm (releases/tools-env.sh).
mkdir -p "$NVM_DIR"
if [ ! -s "$NVM_DIR/nvm.sh" ]; then
echo "Installing nvm into $NVM_DIR ..."
# A PINNED tag, not master: this pipes a downloaded script into
# a shell, so it must be a revision somebody has looked at.
curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.6/install.sh | bash
# PROFILE=/dev/null: the installer edits no shell rc file.
curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.6/install.sh | PROFILE=/dev/null bash
fi
# nvm is a shell FUNCTION, not a binary, so it has to be sourced -
# `command -v nvm` finds nothing until this line has run.
@@ -3006,19 +3083,20 @@ for _once in 1; do
nvm install 26
nvm alias default 26
nvm use 26
npm install -g npm@12.0.2
npm install -g "npm@${_wekan_npm_version:-latest}"
echo "Node $(node --version), npm $(npm --version)"
# Let new shells find nvm too. Its installer appends these itself,
# but only to the rc file it detects and only when IT did the
# install - so a machine that already had nvm keeps working.
touch "$HOME/.zshrc"
for rc in "$HOME/.zshrc" "$HOME/.bashrc"; do
[ -e "$rc" ] || continue
grep -qxF 'export NVM_DIR="$HOME/.nvm"' "$rc" || echo 'export NVM_DIR="$HOME/.nvm"' >> "$rc"
grep -qxF '[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"' "$rc" || echo '[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"' >> "$rc"
done
npx -y meteor
export PATH=~/.meteor:$PATH
# No shell rc file is edited: build.sh and the releases/ scripts put
# .tools/nvm's Node.js and .tools/.meteor on PATH themselves
# (releases/tools-env.sh).
# Meteor as https://docs.meteor.com/about/install.html installs it:
# `npx meteor`, as this user, never sudo, with HOME=.tools for the
# installer only (see the Linux branch).
if [ -x "$WEKAN_TOOLS_DIR/.meteor/meteor" ]; then
echo "Meteor is already installed in $WEKAN_TOOLS_DIR/.meteor"
else
HOME="$WEKAN_TOOLS_DIR" npx -y meteor
fi
. "$WEKAN_DIR/releases/tools-env.sh"
exit;
elif [[ "$OSTYPE" == "cygwin" ]]; then
# POSIX compatibility layer and Linux environment emulation for Windows
@@ -3299,6 +3377,14 @@ for _once in 1; do
break
;;
"Run live tests against real services with your own accounts: GitLab write-back with the mirror's glab login (only scratch items it creates and removes) and the API imports, read only (releases/live-tests/README.md); a service with no login here is skipped, log in .tools/log/<datetime>/")
# releases/live-tests/run.mjs: every live test there; each skips a
# service it has no login for. Human-run only - they write to the
# services (scratch items they remove), so no automatic run calls them.
node "$WEKAN_DIR/releases/live-tests/run.mjs"
break
;;
"Mirror GitHub to active mirrors: code, issues, PR conversations, releases and assets")
mirror_forge
break
@@ -3328,6 +3414,12 @@ for _once in 1; do
./releases/db-conformance.sh
;;
"Time the common WeKan queries on every database that has a Docker image for this CPU, SEQUENTIALLY, with twice a production WeKan of dummy data; check they all answer the same and write docs/Databases/FerretDB/1/SpeedTest/Results.md")
# docs/Databases/FerretDB/1/SpeedTest/README.md: db-conformance.sh's databases,
# the speed test's queries.
./docs/Databases/FerretDB/1/SpeedTest/speedtest.sh
;;
"Count amount of tests by category")
LOG="$(one_log test-counts)"
echo "Log: $LOG"
+36 -1
View File
@@ -31,6 +31,16 @@ if (typeof window !== 'undefined') {
window.Meteor = Meteor;
}
// #6752: a ROOT_URL left at the snap's 127.0.0.1 default made every
// copied link point at 127.0.0.1. When the browser reached WeKan through
// another address, build absolute links from that address instead
// (models/lib/browserRootUrl.js says when, and why only then).
try {
const { browserRootUrl } = require('/models/lib/browserRootUrl');
const rootUrl = browserRootUrl(Meteor.absoluteUrl.defaultOptions.rootUrl, window.location.href);
if (rootUrl) Meteor.absoluteUrl.defaultOptions.rootUrl = rootUrl;
} catch (_) {}
// Fix bug in jam:offline 0.4.1: s?.message.includes() crashes when s.message is
// undefined (e.g. during WebSocket reconnect errors that don't carry a .message).
// Wrap _debug so the optional chain is complete: s?.message?.includes().
@@ -52,7 +62,10 @@ if (typeof window !== 'undefined') {
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
const swPath = new URL('pwa-service-worker.js', Meteor.absoluteUrl()).pathname;
navigator.serviceWorker.register(swPath);
navigator.serviceWorker.register(swPath).catch(error => {
// No service worker (a private window, an old browser): the app works without the offline cache.
if (process.env.DEBUG === 'true') console.warn('service worker registration failed:', error);
});
});
}
@@ -101,6 +114,28 @@ Meteor.startup(() => {
}
});
// LOGOUT_IDLE_MINUTES: report the user's activity while signed in, if the
// limit is set (models/lib/idleLogout.js).
const { createIdleActivityReporter } = require('/client/lib/idleActivity');
const idleReporter = createIdleActivityReporter({
target: document,
touch: () => Meteor.call('touchActivity', () => {}),
});
let idleCheckedFor = null;
Tracker.autorun(() => {
const userId = Meteor.userId();
if (!userId) {
idleCheckedFor = null;
idleReporter.stop();
return;
}
if (idleCheckedFor === userId) return;
idleCheckedFor = userId;
Meteor.call('getIdleLogout', (error, result) => {
if (!error && result && result.enabled && Meteor.userId() === userId) idleReporter.start();
});
});
// Initialize mobile mode on startup for iOS devices
// This ensures mobile mode is applied correctly on page load
Tracker.afterFlush(() => {
+13 -5
View File
@@ -1,6 +1,11 @@
.new-comment {
position: relative;
margin: 0 0 20px 38px;
/* #6805: the room for the avatar is on the side the avatar is drawn - the
reading start (inset-inline-start below). A physical left margin kept it on
the left while a right-to-left page drew the avatar on the right, outside
the card. */
margin-block: 0 20px;
margin-inline: 38px 0;
}
.new-comment .member {
opacity: 0.7;
@@ -29,7 +34,8 @@
border: 0;
box-shadow: 0 1px 2px rgba(0,0,0,0.23);
height: 36px;
margin: 4px 4px 6px 0;
margin-block: 4px 6px;
margin-inline: 0 4px;
padding: 9px 11px;
width: 100%;
}
@@ -47,9 +53,10 @@
background-color: #fff;
border: 0;
box-shadow: 0 1px 2px rgba(0,0,0,0.23);
color: #8c8c8c;
color: var(--wekan-quiet-text, #595959);
height: 36px;
margin: 4px 4px 6px 0;
margin-block: 4px 6px;
margin-inline: 0 4px;
width: 92%;
}
.comment-item:hover {
@@ -161,7 +168,8 @@
background: #f4f4f4;
border-inline-start: 2px solid #ccc;
padding: 4px 6px;
margin: 0 4px 6px 0;
margin-block: 0 6px;
margin-inline: 0 4px;
font-size: 0.85em;
color: #666;
display: flex;
+9 -7
View File
@@ -15,7 +15,7 @@ template(name="commentForm")
a.js-cancel-reply(title="{{_ 'close' }}")
i.fa.fa-times-thin
form.js-new-comment-form
+editor(class="js-new-comment-input")
+editor(class="js-new-comment-input" labelKey="comment")
| {{getUnsavedValue 'cardComment' currentCard._id}}
.add-controls
button.primary.confirm.clear.js-add-comment(type="submit") {{_ 'comment'}}
@@ -40,7 +40,7 @@ template(name="comment")
= parentComment.text
+inlinedForm(classNames='js-edit-comment')
+editor(autofocus=true)
+editor(autofocus=true labelKey="comment")
= text
.edit-controls
button.primary(type="submit" title="{{_ 'edit'}}" aria-label="{{_ 'edit'}}")
@@ -66,6 +66,10 @@ template(name="comment")
if currentUser.isBoardMember
= ' - '
a.js-reply-comment {{_ "comment-reply"}}
//- Action items, Show on Card (docs/Features/Cards/Action-Items.md).
if actionItemsOf this
= ' - '
a.js-comment-action-items {{_ "action-items-create"}}
if canEditThisComment
+editOrDeleteComment
@@ -86,11 +90,9 @@ template(name="commentReactions")
span.reaction(class="{{#if isSelected reaction.userIds}}selected{{/if}}" data-codepoint="#{reaction.reactionCodepoint}" title="{{userNames reaction.userIds}}")
span.reaction-codepoint !{reaction.reactionCodepoint}
span.reaction-count #{reaction.userIds.length}
if (currentUser.isBoardMember)
unless currentUser.isReadOnly
unless currentUser.isReadAssignedOnly
a.open-comment-reaction-popup(title="{{_ 'addReactionPopup-title'}}")
i.fa.fa-smile-o(title="{{_ 'addReactionPopup-title' }}")
if canComment
a.open-comment-reaction-popup(title="{{_ 'addReactionPopup-title'}}")
i.fa.fa-smile-o(title="{{_ 'addReactionPopup-title' }}")
i.fa.fa-plus(title="{{_ 'add' }}")
template(name="addReactionPopup")
+2
View File
@@ -211,6 +211,8 @@ Template.comment.events({
'click .js-comment-permalink'() {
if (this._id) Session.set('revealCommentId', this._id);
},
// docs/Features/Cards/Action-Items.md
'click .js-comment-action-items': Popup.open('actionItems', { titleKey: 'action-items' }),
'click .js-copy-comment-link'(evt, tpl) {
evt.preventDefault();
const url = commentPermalinkFor(this);
+22 -2
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/Features/Page/Search.md, docs/Features/Page/Multi-Selection.md
//- docs/Features/Board-Header/Search.md, docs/Features/General/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/Features/Page/All-Boards.md
it was written. docs/Features/All-Boards/All-Boards.md
a.sidebar-btn.js-open-boards-sort(class="{{#unless isBoardsSort 'custom'}}emphasis{{/unless}}")
i.fa.fa-sort
span {{_ 'sort-boards'}}
@@ -58,6 +58,11 @@ template(name="allBoardsHomeSidebar")
i.fa.fa-check-square-o
span.sidebar-list-item-description {{_ 'multi-selection'}}
hr
//- "Export all boards" (server/routes/exportAllBoards.js): every board you
can export, in one format, in one download.
a.sidebar-btn.js-export-all-boards
i.fa.fa-download
span {{_ 'export-all-boards'}}
//- Archived boards had a handler in the header bar and no button to reach it.
a.sidebar-btn.js-open-archived-board
i.fa.fa-archive
@@ -110,6 +115,9 @@ template(name="allBoardsMultiSelectionSidebar")
a.sidebar-btn.js-duplicate-selected-boards
i.fa.fa-clipboard
span {{_ 'duplicate-board'}}
a.sidebar-btn.js-export-selected-boards
i.fa.fa-download
span {{_ 'export-selected-boards'}}
hr
a.sidebar-btn.js-multiselection-reset
i.fa.fa-times-thin
@@ -130,3 +138,15 @@ template(name="duplicateSelectedBoardsPopup")
p.warning(role="alert") {{copyError}}
button.primary(type="submit" disabled=copyBusy) {{_ 'duplicate-board'}}
button.js-copy-cancel(type="button" disabled=copyBusy) {{_ 'cancel'}}
//- The formats "Export all boards" offers, each a download of every board (or
//- of the selected ones): Excel as one workbook with a sheet per board, every
//- other format as a .zip with a file per board.
template(name="exportAllBoardsPopup")
p.quiet {{_ 'export-all-boards-hint'}}
ul.pop-over-list
each formats
li
a.export-all-boards-format(href="{{url}}" download="{{filename}}" rel="noopener")
i.fa.fa-download
| {{label}}
+28 -5
View File
@@ -9,6 +9,7 @@ const {
} = require('/models/lib/allBoardsUrls');
import { TAPi18n } from '/imports/i18n';
import { BoardMultiSelection } from '/client/lib/boardMultiSelection';
import { massExportFormats, massExportUrl } from '/client/components/boards/exportScope';
import { allBoardsSearchVar, allBoardsMenuVar } from '/client/lib/allBoardsView';
import {
allBoardsSidebarView,
@@ -24,10 +25,11 @@ import {
sidebarViewTemplate,
} from '/models/lib/allBoardsSidebar';
import { selectedStarTitleKey, selectedStarAction } from '/models/lib/selectedStars';
import { errorMessage } from '/client/lib/errorMessage';
// The All Boards right sidebar. Its shell mirrors the board sidebar's; what it
// shows is this page's own. docs/Features/Page/Search.md,
// docs/Features/Page/Multi-Selection.md
// shows is this page's own. docs/Features/Board-Header/Search.md,
// docs/Features/General/Multi-Selection.md
Template.allBoardsSidebar.helpers({
isSidebarOpen() {
@@ -90,11 +92,15 @@ Template.allBoardsHomeSidebar.events({
// Boards in Archive is a page. This row had no handler at all: the one it
// relied on lived in the header bar's events map and went when that map was
// rewritten, so the button did nothing.
'click .js-export-all-boards'(evt) {
evt.preventDefault();
Popup.open('exportAllBoards').call({ boardIds: null }, evt);
},
'click .js-open-archived-board'(evt) {
evt.preventDefault();
closeAllBoardsSidebar();
// The SECTION of this page, not the full-width page it replaced.
// docs/Features/Page/Archive.md
// docs/Features/All-Boards/Archive.md
FlowRouter.go(allBoardsPath(SECTION_ARCHIVE, []));
},
});
@@ -182,14 +188,31 @@ function selectedHomeBoardIdOrWarn() {
return ids[0];
}
Template.exportAllBoardsPopup.helpers({
formats() {
const boardIds = (this && this.boardIds) || null;
return massExportFormats().map(format => ({
label: format.label,
url: massExportUrl(format.key, boardIds),
filename: format.key === 'excel' ? 'wekan-boards.xlsx' : `wekan-boards-${format.key}.zip`,
}));
},
});
Template.allBoardsMultiSelectionSidebar.events({
'click .js-export-selected-boards'(evt) {
evt.preventDefault();
const boardIds = selectedBoardIdsOrWarn();
if (!boardIds) return;
Popup.open('exportAllBoards', { titleKey: 'export-selected-boards' }).call({ boardIds }, evt);
},
'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');
alert(errorMessage(err));
return;
}
BoardMultiSelection.reset();
@@ -221,7 +244,7 @@ Template.allBoardsMultiSelectionSidebar.events({
) {
selectedBoards.forEach((boardId) => {
Meteor.call('archiveBoard', boardId, (err) => {
if (err) alert(err?.reason || err?.message || 'Failed to archive board');
if (err) alert(errorMessage(err));
});
});
BoardMultiSelection.reset();
@@ -0,0 +1,21 @@
//- #1566: the board's own announcement (models/lib/boardAnnouncement.js),
in the site announcement's style. Plain text: Blaze escapes it.
template(name="boardAnnouncement")
if showBoardAnnouncement
.announcement.board-announcement.js-board-announcement(role="status" aria-live="polite")
ul
li
i.fa.fa-bullhorn(aria-hidden="true")
| #{boardAnnouncementBody}
a.js-close-board-announcement(aria-label="{{_ 'close'}}" title="{{_ 'close'}}")
i.fa.fa-times-thin(aria-hidden="true")
//- Board menu / Board announcement, for a board admin.
template(name="boardAnnouncementPopup")
form.js-board-announcement-form
label(for="board-announcement-text") {{_ 'board-announcement'}}
textarea.js-board-announcement-text#board-announcement-text(rows="5" maxlength="2000")= announcementBody
a.flex.js-board-announcement-enabled(class="{{#if announcementEnabled.get}}is-checked{{/if}}")
.materialCheckBox(class="{{#if announcementEnabled.get}}is-checked{{/if}}")
span {{_ 'board-announcement-enabled'}}
button.primary.wide.js-board-announcement-save(type="submit") {{_ 'save'}}
@@ -0,0 +1,60 @@
import { Meteor } from 'meteor/meteor';
import { Template } from 'meteor/templating';
import { ReactiveVar } from 'meteor/reactive-var';
import { ReactiveCache } from '/imports/reactiveCache';
import { Utils } from '/client/lib/utils';
// #1566: a board's own announcement. models/lib/boardAnnouncement.js decides
// whether it shows; dismissing records the version the server reads.
const { showBoardAnnouncement } = require('/models/lib/boardAnnouncement');
Template.boardAnnouncement.helpers({
showBoardAnnouncement() {
const board = Utils.getCurrentBoard();
const user = ReactiveCache.getCurrentUser();
return showBoardAnnouncement(board, user && user.profile && user.profile.dismissedBoardAnnouncements);
},
boardAnnouncementBody() {
const board = Utils.getCurrentBoard();
return (board && board.announcement && board.announcement.body) || '';
},
});
Template.boardAnnouncement.events({
'click .js-close-board-announcement'(event) {
event.preventDefault();
const boardId = Utils.getCurrentBoardId();
if (boardId) Meteor.call('dismissBoardAnnouncement', boardId);
},
});
Template.boardAnnouncementPopup.onCreated(function () {
const board = Utils.getCurrentBoard();
this.announcementEnabled = new ReactiveVar(!!(board && board.announcement && board.announcement.enabled));
});
Template.boardAnnouncementPopup.helpers({
announcementEnabled() { return Template.instance().announcementEnabled; },
announcementBody() {
const board = Utils.getCurrentBoard();
return (board && board.announcement && board.announcement.body) || '';
},
});
Template.boardAnnouncementPopup.events({
'click .js-board-announcement-enabled'(event, tpl) {
event.preventDefault();
tpl.announcementEnabled.set(!tpl.announcementEnabled.get());
},
async 'submit .js-board-announcement-form'(event, tpl) {
event.preventDefault();
const board = Utils.getCurrentBoard();
if (!board) return;
try {
await board.setAnnouncement(tpl.announcementEnabled.get(), tpl.find('.js-board-announcement-text').value || '');
Popup.back();
} catch (error) {
console.error('[boardAnnouncement] save failed:', error);
}
},
});
+1 -1
View File
@@ -3,7 +3,7 @@ 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/Features/Page/Table.md): the archive pages
// The app's one rows-per-page (docs/Features/General/Table.md): the archive pages
// like every other paginated page, whatever pager it draws.
const ARCHIVED_BOARDS_PER_PAGE = TABLE_PAGE_ROWS_PER_PAGE;
+22
View File
@@ -40,6 +40,28 @@
min-width: 100%;
}
/* #1566: a board's announcement sits above its canvas. The canvas fills the
wrapper from its top, so it lay over the banner and its swimlane header
took the close button's clicks: the announcement could not be dismissed.
While a banner shows, the wrapper is a column - the banner, then the
canvas taking the rest - and without one nothing changes. */
.board-wrapper:has(> .board-announcement) {
display: flex;
flex-direction: column;
}
.board-wrapper > .board-announcement {
flex: 0 0 auto;
position: relative;
z-index: 1;
}
.board-wrapper:has(> .board-announcement) > .board-canvas {
position: relative;
top: auto;
bottom: auto;
flex: 1 1 auto;
min-height: 0;
}
/* Ensure horizontal scrollbar is visible for high zoom levels */
.board-wrapper .board-canvas[style*="overflow-x: auto"] {
overflow-x: auto !important;
+16 -1
View File
@@ -12,6 +12,8 @@ template(name="board")
//-- XXX We need a better error message in case the board has been archived
+message(label="board-not-found")
//-- | {{goHome}}
else if boardLoadError
+message(label="board-load-failed")
else
+spinner
@@ -24,12 +26,15 @@ template(name="boardBody")
.debug-info(style="position: fixed; top: 0; inset-inline-start: 0; background: rgba(0,0,0,0.8); color: white; padding: 10px; z-index: 9999; font-size: 12px;")
| {{_ 'board'}}: {{currentBoard.title}} | {{_ 'view'}}: {{boardView}} | {{_ 'has-swimlanes'}}: {{hasSwimlanes}} | {{_ 'swimlanes'}}: {{currentBoard.swimlanes.length}}
.board-wrapper(class="{{themeColorClass}}" class="{{#if isMiniScreen}}mobile-view{{/if}}")
//- #1566: the board's own announcement.
+boardAnnouncement
.board-canvas.js-swimlanes.dragscroll(
class="{{#if Sidebar.isOpen}}is-sibling-sidebar-open{{/if}}"
class="{{#if MultiSelection.isActive}}is-multiselection-active{{/if}}"
class="{{#if draggingActive}}is-dragging-active{{/if}}"
class="{{#unless isVerticalScrollbars}}no-scrollbars{{/unless}}"
class="{{#if isMiniScreen}}mobile-view{{/if}}")
class="{{#if isMiniScreen}}mobile-view{{/if}}"
class="{{#if currentBoard.hasSharedListHeaders}}shared-list-headers{{/if}}")
//- #6732: drawn when THIS user shows My or Board Dependencies.
if showAnyDependencies
+dependencyOverlay
@@ -49,6 +54,8 @@ template(name="boardBody")
+listsGroup(currentBoard)
else if isViewCalendar
+calendarView
else if isViewCalendarMode
+calendarModeView
else if isViewMultiboardCalendar
+multiboardCalendarView
else if isViewGantt
@@ -107,6 +114,14 @@ template(name="boardBody")
+pulseView
else if isViewMap
+mapView
else if isViewEmail
+emailView
else if isViewChat
+chatView
else if isViewVersionControl
+versionControlView
else if isViewColumnSets
+columnSetsView
else
// Default view - show swimlanes if they exist, otherwise show lists
if hasSwimlanes
+85 -79
View File
@@ -1,4 +1,6 @@
import { markBoardVisited } from '/client/lib/boardVisits';
import { ReactiveCache } from '/imports/reactiveCache';
import { setSortableOptions } from '/client/lib/sortableOptions';
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
import { TAPi18n } from '/imports/i18n';
import {
@@ -15,6 +17,7 @@ import { formatDateByUserPreference } from '/imports/lib/dateUtils';
import { calendarDateDisplayOptions } from '/client/lib/dateDisplay';
import { toFullCalendarFirstDay } from '/client/lib/calendarFirstDay';
import { weekNumberByFirstDay } from '/models/lib/weekStart';
import { calendarBaseOptions } from '/models/lib/calendarBaseOptions';
import Swimlanes from '/models/swimlanes';
import Lists from '/models/lists';
import TableVisibilityModeSettings from '/models/tableVisibilityModeSettings';
@@ -37,6 +40,10 @@ let BoardBody = null;
Template.board.onCreated(function () {
this.isBoardReady = new ReactiveVar(false);
// The board publication stopped with an error: say so instead of spinning
// forever (a board restored from an old WeKan whose data the publication
// could not read showed only the loading animation).
this.boardLoadError = new ReactiveVar(false);
// Kept for the template/jade which still references it; conversion is a no-op.
this.isConverting = new ReactiveVar(false);
this._swimlaneCreated = new Set(); // boards where a default swimlane was ensured
@@ -230,6 +237,7 @@ Template.board.onCreated(function () {
if (this.subscriptionBoardId !== currentBoardId) {
this.subscriptionBoardId = currentBoardId;
this.isBoardReady.set(false);
this.boardLoadError.set(false);
}
if (!currentBoardId) return;
@@ -238,6 +246,14 @@ Template.board.onCreated(function () {
currentBoardId,
false,
subscriptionGeneration,
{
onStop: (error) => {
if (error && this.subscriptionBoardId === currentBoardId) {
console.error('[board] the board publication stopped:', error.reason || error.message || error.error);
this.boardLoadError.set(true);
}
},
},
);
// Learn this board's card-loading mode (lazy vs eager) so 'auto' can decide
// per board by size; the flag drives isLazyCards(boardId). #6480.
@@ -300,6 +316,9 @@ Template.board.helpers({
return Template.instance().isConverting.get();
},
boardLoadError() {
return Template.instance().boardLoadError.get();
},
isBoardReady() {
return Template.instance().isBoardReady.get();
},
@@ -322,6 +341,12 @@ Template.boardBody.onCreated(function () {
// Store global reference for external access (swimlanes, cardDetails, etc.)
BoardBody = this;
// Remember this visit, once per board per page load, for Board menu ->
// Since your last visit (client/lib/boardVisits.js).
this.autorun(() => {
markBoardVisited(Session.get('currentBoard'));
});
// Methods on the template instance for programmatic access
this.setIsDragging = (bool) => {
this.draggingActive.set(bool);
@@ -549,7 +574,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/Features/Page/Header.md */
it moved to the first top header bar: docs/Features/Header-Bar/Header.md */
.js-toggle-page-sidebar .fa-bars {
color: #fff !important;
}
@@ -735,30 +760,26 @@ Template.boardBody.onRendered(function () {
resetBoardDragscroll();
if ($swimlanesDom.data('uiSortable') || $swimlanesDom.data('sortable')) {
if (Utils.isTouchScreenOrShowDesktopDragHandles()) {
$swimlanesDom.sortable(
'option',
'handle',
'.js-swimlane-header-handle',
);
} else {
$swimlanesDom.sortable('option', 'handle', '.swimlane-header');
}
// #6446: enable swimlane drag-drop — which also carries moving cards BETWEEN
// swimlanes — for any board member with write access, not only board admins.
// The old !isBoardAdmin() check disabled this sortable for every non-admin, so
// a normal member could still move a card WITHIN a swimlane but not BETWEEN
// swimlanes (a card crossing swimlanes is received across this sortable), and
// could not reorder swimlanes. canModifyCard() is the client write-access
// predicate (board member, not comment-only / worker / read-only), matching
// the server's allowIsBoardMemberWithWriteAccess.
$swimlanesDom.sortable('option', 'items', Utils.canDragBoardObject('swimlane') ? '.swimlane:not(.placeholder)' : '.no-draggable-swimlanes');
$swimlanesDom.sortable(
'option',
'disabled',
!Utils.canModifyCard(),
);
// could not reorder swimlanes. Write access to the BOARD is the test
// (board member, not comment-only / worker / read-only), matching the
// server's allowIsBoardMemberWithWriteAccess.
//
// #6745: canModifyBoard(), not canModifyCard() with no argument - that one
// reads the OPEN card, so this whole autorun (dragscroll reset and all)
// re-ran on every card open and close. And only the options that changed.
setSortableOptions($swimlanesDom, {
handle: Utils.isTouchScreenOrShowDesktopDragHandles()
? '.js-swimlane-header-handle'
: '.swimlane-header',
items: Utils.canDragBoardObject('swimlane') ? '.swimlane:not(.placeholder)' : '.no-draggable-swimlanes',
disabled: !Utils.canModifyBoard(),
});
}
});
@@ -831,6 +852,9 @@ Template.boardBody.helpers({
isViewCalendar() {
return Utils.boardView() === 'board-view-cal';
},
isViewCalendarMode() {
return Utils.boardView() === 'board-view-calendar-mode';
},
isViewMultiboardCalendar() {
return Utils.boardView() === 'board-view-multiboard-cal';
@@ -943,6 +967,18 @@ Template.boardBody.helpers({
isViewMap() {
return Utils.boardView() === 'board-view-map';
},
isViewEmail() {
return Utils.boardView() === 'board-view-email';
},
isViewChat() {
return Utils.boardView() === 'board-view-chat';
},
isViewColumnSets() {
return Utils.boardView() === 'board-view-column-sets';
},
isViewVersionControl() {
return Utils.boardView() === 'board-view-version-control';
},
isViewPulse() {
return Utils.boardView() === 'board-view-pulse';
@@ -1203,82 +1239,34 @@ Template.calendarView.onRendered(function () {
Template.calendarView.helpers({
calendarOptions() {
const t = (key, fallback) => {
const translated = TAPi18n.__(key);
return translated && translated !== key ? translated : fallback;
};
// #5521: respect the user's "start day of week" setting instead of the
// FullCalendar locale default. getStartDayOfWeek() returns a zero-based day
// index (0=Sunday..6=Saturday), the same convention FullCalendar's firstDay
// uses; toFullCalendarFirstDay() validates it and falls back to Monday (1).
// #5521: getStartDayOfWeek() returns a zero-based day index
// (0=Sunday..6=Saturday), the convention FullCalendar's firstDay uses;
// toFullCalendarFirstDay() validates it and falls back to Monday (1).
const currentUser = ReactiveCache.getCurrentUser();
const firstDay = toFullCalendarFirstDay(
currentUser ? currentUser.getStartDayOfWeek() : 1,
);
const displayOptions = calendarDateDisplayOptions('calendar-view');
return {
id: 'calendar-view',
...displayOptions,
initialView: displayOptions.initialView || 'dayGridMonth',
firstDay,
...calendarBaseOptions({
id: 'calendar-view',
displayOptions: calendarDateDisplayOptions('calendar-view'),
firstDay,
weekNumberOf: weekNumberByFirstDay,
translate: key => TAPi18n.__(key),
locale: TAPi18n.getLanguage(),
rtl: TAPi18n.isRTL(),
}),
editable: true,
selectable: true,
weekNumbers: true,
// #4946: FullCalendar's default weekNumberCalculation is locale-based
// (Sunday-anchored for many locales), so the week-number column ignored
// the user's start-day-of-week setting even though `firstDay` moved the
// grid. Compute the number from the SAME firstDay so the label matches the
// way the week is laid out.
weekNumberCalculation: date => weekNumberByFirstDay(date, firstDay),
// Use non-localized AM/PM time format to avoid confusing notations like 上/下/中
// Use full 'am'/'pm' instead of single-letter 'a'/'p' for clarity
eventTimeFormat: {
hour: 'numeric',
minute: '2-digit',
meridiem: 'short',
},
slotLabelFormat: {
hour: 'numeric',
minute: '2-digit',
meridiem: 'short',
},
// #6686: Today/Previous/Next used to sit in their own group under the
// title, with the Day/Week/Month view toggles in a third, CENTER group
// that pushed everything onto a second row. All the buttons now sit
// together in ONE group on the right side of the title, which stays
// alone on the left - see calendarView.css for the title's vertical
// centering against that single row.
headerToolbar: {
left: 'title',
center: '',
right:
'today prev,next timeGridDay,listDay timeGridWeek,listWeek dayGridMonth,listMonth',
},
buttonIcons: false,
buttonText: {
prev: t('previous', 'Previous'),
next: t('next', 'Next'),
today: t('today', 'Today'),
day: t('day', 'Day'),
week: t('week', 'Week'),
month: t('month', 'Month'),
list: t('list', 'List'),
},
// height: 'parent', nope, doesn't work as the parent might be small
height: 'auto',
/* TODO: lists as resources: https://fullcalendar.io/docs/vertical-resource-view */
navLinks: true,
nowIndicator: true,
businessHours: {
// days of week. an array of zero-based day of week integers (0=Sunday)
daysOfWeek: [1, 2, 3, 4, 5], // Monday - Friday
start: '8:00',
end: '18:00',
},
locale: TAPi18n.getLanguage(),
direction: TAPi18n.isRTL() ? 'rtl' : 'ltr',
events(fetchInfo, callback) {
const currentBoard = Utils.getCurrentBoard();
// #5656: apply the active board Filter (member / assignee / due-date /
@@ -1354,6 +1342,24 @@ Template.calendarView.helpers({
'calendar-event-end',
);
});
// #1618: dated checklist items, as "card: item", opening their card.
// Their `ci:` ids never match a card, so dragging one is reverted.
const { datedChecklistItems } = require('/models/lib/checklistItemSchedule');
datedChecklistItems(
ReactiveCache.getChecklistItems({ boardId: currentBoard._id, dueAt: { $exists: true } }),
id => ReactiveCache.getCard(id), { from: fetchInfo.start, to: fetchInfo.end },
).forEach(item => {
events.push({
id: item.id,
title: item.title,
start: item.dueAt,
end: new Date(item.dueAt.getTime() + 36e5),
allDay: false,
editable: false,
url: FlowRouter.path('card', { boardId: currentBoard._id, slug: currentBoard.slug, cardId: item.cardId }),
className: `calendar-event-checklist-item${item.isFinished ? ' is-finished' : ''}`,
});
});
events.sort(function (first, second) {
return first.id > second.id ? 1 : -1;
});
File diff suppressed because it is too large. Load diff
-65
View File
@@ -642,71 +642,6 @@
transform: none !important;
}
/* Force mobile mode to override any media query styles */
@media screen and (min-width: 801px) {
.mobile-mode .board-canvas {
display: block !important;
flex-direction: column !important;
flex-wrap: nowrap !important;
align-items: stretch !important;
justify-content: flex-start !important;
width: 100% !important;
max-width: 100% !important;
min-width: 100% !important;
overflow-x: hidden !important;
overflow-y: auto !important;
}
.mobile-mode .swimlane {
display: block !important;
width: 100% !important;
max-width: 100% !important;
min-width: 100% !important;
margin: 0 !important;
padding: 0 !important;
float: none !important;
clear: both !important;
}
.mobile-mode .swimlane .lists {
display: block !important;
width: 100% !important;
max-width: 100% !important;
min-width: 100% !important;
margin: 0 !important;
padding: 0 !important;
flex-direction: column !important;
flex-wrap: nowrap !important;
align-items: stretch !important;
justify-content: flex-start !important;
}
.mobile-mode .list {
display: block !important;
width: 100% !important;
max-width: 100% !important;
min-width: 100% !important;
margin: 0 !important;
padding: 0 !important;
float: none !important;
clear: both !important;
border-inline-start: none !important;
border-inline-end: none !important;
border-top: none !important;
border-bottom: 2px solid #ccc !important;
flex: none !important;
flex-basis: auto !important;
flex-grow: 0 !important;
flex-shrink: 0 !important;
position: static !important;
inset-inline-start: auto !important;
inset-inline-end: auto !important;
top: auto !important;
bottom: auto !important;
transform: none !important;
}
}
/* Hide desktop-only elements in mobile mode (like mobile media queries do) */
.mobile-mode .board-header-btn i.fa + span {
display: none !important;
+31 -11
View File
@@ -11,7 +11,7 @@
//-
//- 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/Features/Page/Header.md
//- phone; icons do, so one set serves both. docs/Features/Header-Bar/Header.md
template(name="boardHeaderButtons")
if currentBoard
if currentUser
@@ -50,7 +50,7 @@ template(name="boardHeaderButtons")
//- 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
docs/Features/Header-Bar/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}}
@@ -126,6 +126,16 @@ template(name="boardVisibilityList")
template(name="boardChangeVisibilityPopup")
+boardVisibilityList
//- #4575: an open board can let every signed-in user edit it, where Admin
//- Panel / Settings / Visibility allows that.
if instanceEditingOffered
hr
ul.pop-over-list
li
a.flex.js-toggle-instance-editing(role="checkbox" aria-checked="{{#if currentBoardInstanceEditing}}true{{else}}false{{/if}}")
.materialCheckBox(class="{{#if currentBoardInstanceEditing}}is-checked{{/if}}")
span {{_ 'instance-editing'}}
p.quiet {{_ 'instance-editing-desc'}}
template(name="boardChangeWatchPopup")
ul.pop-over-list.described-list
@@ -161,7 +171,7 @@ template(name="boardChangeWatchPopup")
(models/lib/boardViewSettings.js boardViewMenuEntries), so the popup and
the menu can not drift apart. Each anchor keeps its per-view class
(js-open-<view>-view), which is what the click handlers in boardHeader.js
listen for. docs/Features/Board/Board-View-Settings.md
listen for. docs/Features/Board-Sidebar/Board-Settings/Board-View.md
template(name="boardChangeViewPopup")
ul.pop-over-list
each boardViewMenuEntries
@@ -261,13 +271,15 @@ template(name="boardChangeTitlePopup")
label
| {{_ 'description'}}
textarea.js-board-desc(dir="auto")= description
//- #2449/#2450: the board key card numbers show with (KEY-7).
label
| {{_ 'board-key'}}
input.js-board-key(type="text" value=key maxlength="10" autocapitalize="characters" spellcheck="false")
p.quiet {{_ 'board-key-desc'}}
if boardKeyError
p.error {{_ boardKeyError}}
input.primary.wide(type="submit" value="{{_ 'rename'}}")
template(name="boardCreateRulePopup")
p {{_ 'close-board-pop'}}
button.js-confirm.negate.full(type="submit") {{_ 'archive'}}
template(name="cardsSortPopup")
ul.pop-over-list
li
@@ -294,6 +306,12 @@ template(name="cardsSortPopup")
a.js-sort-votes
i.fa.fa-thumbs-o-up
| {{_ 'sort-by-votes'}}
hr
//- #2040: by the board's label order (edit a label to move it).
li
a.js-sort-label-order
i.fa.fa-tags
| {{_ 'sort-by-label-order'}}
//- The board's view menu, in the FIRST top header bar.
@@ -303,7 +321,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/Features/Page/Header.md
//- ways on a templates board. docs/Features/Header-Bar/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 -
@@ -311,7 +329,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/Features/Page/Header.md
//- docs/Features/Header-Bar/Header.md
template(name="boardStarButton")
if currentBoard
a.board-header-btn.js-star-board(class="{{#if isStarred}}is-active{{/if}}"
@@ -326,7 +344,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/Features/Page/Header.md
makes a word beside the icon affordable. docs/Features/Header-Bar/Header.md
a.board-header-btn.js-toggle-board-view(title="{{boardViewName}}")
i.fa.fa-caret-down
if $eq boardView 'board-view-swimlanes'
@@ -335,6 +353,8 @@ template(name="boardViewMenu")
i.fa.fa-trello
if $eq boardView 'board-view-cal'
i.fa.fa-calendar
if $eq boardView 'board-view-calendar-mode'
i.fa.fa-calendar-check-o
if $eq boardView 'board-view-gantt'
i.fa.fa-bar-chart
if $eq boardView 'board-view-table'
+78 -8
View File
@@ -44,6 +44,16 @@ function setCardsSortBy(sortBy) {
} catch (e) {}
}
Template.boardChangeTitlePopup.onCreated(function () {
this.boardKeyError = new ReactiveVar('');
});
Template.boardChangeTitlePopup.helpers({
boardKeyError() {
return Template.instance().boardKeyError.get();
},
});
Template.boardChangeTitlePopup.events({
async submit(event, templateInstance) {
event.preventDefault();
@@ -58,7 +68,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/Features/Page/All-Boards.md), and on
// context is that row's board (docs/Features/All-Boards/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();
@@ -67,6 +77,18 @@ Template.boardChangeTitlePopup.events({
? context
: Utils.getCurrentBoard();
if (board) {
// The key first: a key that is taken or not valid keeps the popup
// open with the reason, and nothing else is saved yet.
const keyInput = templateInstance.$('.js-board-key');
const newKey = keyInput.length ? String(keyInput.val() || '').trim().toUpperCase() : '';
if (newKey && newKey !== board.key) {
try {
await Meteor.callAsync('setBoardKey', board._id, newKey);
} catch (error) {
templateInstance.boardKeyError.set(error && error.error === 'board-key-taken' ? 'board-key-taken' : 'board-key-invalid');
return;
}
}
await board.rename(newTitle);
await board.setDescription(newDesc);
}
@@ -201,7 +223,7 @@ Template.boardHeaderButtons.events({
'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/Features/Page/Archive.md
// replaced. docs/Features/All-Boards/Archive.md
'click .js-open-archived-board'() {
FlowRouter.go(allBoardsPath(SECTION_ARCHIVE, []));
},
@@ -266,7 +288,7 @@ Template.boardHeaderButtons.events({
},
});
// Board Settings / Board View (docs/Features/Board/Board-View-Settings.md):
// Board Settings / Board View (docs/Features/Board-Sidebar/Board-Settings/Board-View.md):
// a view the board admin un-ticked for this board's visibility (public or
// private) is not offered. Reads the board reactively, so switching the
// board Private <-> Public swaps the menu on the spot.
@@ -276,7 +298,7 @@ Template.boardChangeViewPopup.helpers({
boardViewMenuEntries() {
const board = Utils.getCurrentBoard();
// #6736: a view the instance disabled is not drawn at all.
return boardViewMenuEntries(board, Utils.boardView(), allowBoardView).map(entry => ({
return boardViewMenuEntries(board, Utils.boardView(), allowBoardView, Meteor.userId()).map(entry => ({
...entry,
icon: `fa ${entry.icon}`,
}));
@@ -306,6 +328,10 @@ Template.boardChangeViewPopup.events({
Utils.setBoardView('board-view-cal');
Popup.back();
},
'click .js-open-calendar-mode-view'() {
Utils.setBoardView('board-view-calendar-mode');
Popup.back();
},
'click .js-open-multiboard-cal-view'() {
Utils.setBoardView('board-view-multiboard-cal');
Popup.back();
@@ -430,6 +456,22 @@ Template.boardChangeViewPopup.events({
Utils.setBoardView('board-view-map');
Popup.back();
},
'click .js-open-email-view'() {
Utils.setBoardView('board-view-email');
Popup.back();
},
'click .js-open-chat-view'() {
Utils.setBoardView('board-view-chat');
Popup.back();
},
'click .js-open-version-control-view'() {
Utils.setBoardView('board-view-version-control');
Popup.back();
},
'click .js-open-column-sets-view'() {
Utils.setBoardView('board-view-column-sets');
Popup.back();
},
'click .js-open-pulse-view'() {
Utils.setBoardView('board-view-pulse');
Popup.back();
@@ -650,13 +692,16 @@ Template.boardVisibilityList.helpers({
// Settings / Visibility (Settings.customPrivateBoardDesc /
// customPublicBoardDesc); empty/unset falls back to the i18n default,
// byte-identical to before this setting existed.
// TAPi18n.__ reads `this` (its reactive language dependency), so it is
// passed bound: handed over bare it threw inside the helper and both
// descriptions rendered as an empty span in every language.
privateDesc() {
const setting = ReactiveCache.getCurrentSetting();
return visibilityDesc(setting?.customPrivateBoardDesc, TAPi18n.__, 'private-desc');
return visibilityDesc(setting?.customPrivateBoardDesc, key => TAPi18n.__(key), 'private-desc');
},
publicDesc() {
const setting = ReactiveCache.getCurrentSetting();
return visibilityDesc(setting?.customPublicBoardDesc, TAPi18n.__, 'public-desc');
return visibilityDesc(setting?.customPublicBoardDesc, key => TAPi18n.__(key), 'public-desc');
},
});
@@ -665,6 +710,15 @@ Template.boardChangeVisibilityPopup.onCreated(function () {
});
Template.boardChangeVisibilityPopup.helpers({
instanceEditingOffered() {
const board = Utils.getCurrentBoard();
return !!(board && ['instance', 'public'].includes(board.permission)
&& TableVisibilityModeSettings.findOne('tableVisibilityMode-allowInstanceEditing')?.booleanValue);
},
currentBoardInstanceEditing() {
const board = Utils.getCurrentBoard();
return !!(board && board.allowsInstanceEditing);
},
notAllowPrivateVisibilityOnly(){
return !TableVisibilityModeSettings.findOne('tableVisibilityMode-allowPrivateOnly')?.booleanValue;
},
@@ -675,6 +729,11 @@ Template.boardChangeVisibilityPopup.helpers({
});
Template.boardChangeVisibilityPopup.events({
'click .js-toggle-instance-editing'(event) {
event.preventDefault();
const board = Utils.getCurrentBoard();
if (board) Boards.updateAsync(board._id, { $set: { allowsInstanceEditing: !board.allowsInstanceEditing } });
},
'click .js-select-visibility'() {
const currentBoard = Utils.getCurrentBoard();
const visibility = this;
@@ -846,6 +905,12 @@ Template.cardsSortPopup.events({
// so this is a marker only - client.components.lists.listBody's
// cardsWithLimit() recognizes it and re-sorts the rendered cards in JS
// instead of passing it through as a Mongo sort spec.
// #2040: a display order like votes - the cards' `sort` is not changed.
'click .js-sort-label-order'() {
setCardsSortBy({ labelOrder: 1 });
sortCardsBy.set(TAPi18n.__('sort-by-label-order'));
Popup.back();
},
'click .js-sort-votes'() {
const sortBy = {
votes: -1,
@@ -858,7 +923,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/Features/Page/Header.md
// docs/Features/Header-Bar/Header.md
Template.boardViewMenu.events({
'click .js-toggle-board-view': Popup.open('boardChangeView'),
});
@@ -868,12 +933,13 @@ 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/Features/Page/Header.md
// this is the only place the name appears. docs/Features/Header-Bar/Header.md
boardViewName() {
const names = {
'board-view-swimlanes': 'swimlanes',
'board-view-lists': 'lists',
'board-view-cal': 'calendar',
'board-view-calendar-mode': 'board-view-calendar-mode',
'board-view-multiboard-cal': 'board-view-multiboard-cal',
'board-view-gantt': 'gantt',
'board-view-gantt-frappe': 'board-view-gantt-frappe',
@@ -906,6 +972,10 @@ Template.boardViewMenu.helpers({
'board-view-process-behavior': 'board-view-process-behavior',
'board-view-size-cycle-time': 'board-view-size-cycle-time',
'board-view-map': 'board-view-map',
'board-view-email': 'board-view-email',
'board-view-chat': 'board-view-chat',
'board-view-version-control': 'board-view-version-control',
'board-view-column-sets': 'board-view-column-sets',
};
return TAPi18n.__(names[Utils.boardView()] || 'board-view');
},
+69 -23
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/Features/Page/Left-Menu.md */
docs/Features/All-Boards/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/Features/Page/Left-Menu.md */
entries. docs/Features/All-Boards/Left-Menu.md */
position: relative;
}
/* The left menu is styled like the Admin Panel's (settingBody.css,
docs/Features/Page/Left-Menu.md): a PANEL with its own background, border and
docs/Features/All-Boards/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/Features/Page/Left-Menu.md */
on a flush edge shows the grey behind it. docs/Features/All-Boards/Left-Menu.md */
border: 1px solid #f0f0f0;
border-inline-start: 0;
border-top: 0;
@@ -173,7 +173,7 @@
crossed it - which is the bug that had to be fixed in the Admin Panel's. */
.boards-left-menu .menu-item.active a,
.boards-left-menu .menu-item.active a:hover {
background: var(--theme-accent, #2980b9);
background: var(--theme-accent, #2573a7);
}
/* The Admin Panel puts the selected background on the LI, not the anchor, so
the fill reaches the row's full width. Here the anchor is what fills, so it
@@ -212,10 +212,10 @@
rather than being a grey box in a coloured page. `--theme-accent` is set by
the applied theme (boardColors.css); the fallback is the default header blue,
for the themes that predate the variable.
docs/Features/Board/Starred.md */
docs/Features/Pages/Starred.md */
.board-list .board-list-item-bookmark .board-list-item {
border: 4px solid #fff;
background: var(--theme-accent, #2980b9);
background: var(--theme-accent, #2573a7);
color: #fff;
/* Room at the end for the unstar button, which sits over the tile. */
padding-inline-end: 34px;
@@ -277,7 +277,7 @@
the board area so it cannot be missed, and turns red under the icon. Red at
REST would be a standing warning about a board nobody is touching; red on
hover is the answer to "what happens if I let go here".
docs/Features/Board/Home.md */
docs/Features/All-Boards/Home.md */
.home-remove-target {
display: flex;
align-items: center;
@@ -312,7 +312,7 @@
/* An empty Home section: the row exists before there is a board at it, so the
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 */
docs/Features/All-Boards/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
@@ -377,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/Features/Page/Left-Menu.md */
docs/Features/All-Boards/Left-Menu.md */
margin: 8px 4px;
}
@@ -417,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/Features/Page/Workspaces.md */
docs/Features/All-Boards/Workspaces.md */
.workspace-tree {
list-style: none;
padding-inline-start: 10px;
@@ -466,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/Features/Page/Workspaces.md */
docs/Features/All-Boards/Workspaces.md */
.workspace-node.drop-before > .workspace-node-content,
.workspace-node.drop-after > .workspace-node-content,
.workspace-node.drop-inside > .workspace-node-content {
@@ -481,7 +481,7 @@
content: '';
display: block;
height: 30px;
border: 2px dashed var(--theme-accent, #2980b9);
border: 2px dashed var(--theme-accent, #2573a7);
border-radius: 4px;
background: rgba(41, 128, 185, 0.08);
margin: 2px 0;
@@ -567,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/Features/Page/All-Boards.md */
with handles off. docs/Features/All-Boards/All-Boards.md */
.workspace-node .js-select-space[draggable="true"] {
user-select: none;
-webkit-user-select: none;
@@ -603,7 +603,7 @@
`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/Features/Page/All-Boards.md */
ones had not. docs/Features/All-Boards/All-Boards.md */
.workspace-node .workspace-count {
background: #ddd;
padding: 2px 6px;
@@ -694,7 +694,7 @@
as the pointer crossed it. */
.workspace-node.active > .workspace-node-content .js-select-space,
.workspace-node.active > .workspace-node-content:hover .js-select-space {
background: var(--theme-accent, #2980b9);
background: var(--theme-accent, #2573a7);
}
.workspace-node.active > .workspace-node-content .js-select-space,
.workspace-node.active > .workspace-node-content .js-select-space i {
@@ -714,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/Features/Page/All-Boards.md */
with. docs/Features/All-Boards/All-Boards.md */
.boards-right-grid {
min-height: 200px;
@@ -735,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/Features/Page/Left-Menu.md */
docs/Features/All-Boards/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
@@ -758,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/Features/Page/All-Boards.md */
docs/Features/All-Boards/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
@@ -1046,7 +1046,7 @@
display: flex;
align-items: center;
justify-content: center;
background-color: #999; /* Darker background for better text contrast */
background-color: #767676; /* Grey with white text: 4.54, above the 4.5 of WCAG 1.4.3 (#999 gave 2.84) */
border-radius: 3px;
/* Same total vertical padding as .board-list-item (42px -> same tile height),
but shifted up (top 24->18, bottom 18->24) so the "+ Add Board" text sits
@@ -1661,7 +1661,7 @@
.js-board {
display: block;
background-color: #999; /* Default gray background if no color class is applied */
background-color: #767676; /* Default gray background if no color class is applied */
border-radius: 3px; /* Rounded corners for board items */
overflow: hidden; /* Ensure children respect rounded corners */
margin: 0; /* grid gap spaces the tiles now (#6523) */
@@ -1724,7 +1724,7 @@
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));
background: var(--theme-accent-fill, var(--theme-accent, #2573a7));
}
body.has-theme-board-tiles .board-list > li.js-board .board-list-item:not(.has-background-image) {
background-color: transparent;
@@ -2232,7 +2232,7 @@ body.has-theme-board-tiles .board-list > li.js-board .board-list-card-count {
}
/* All Boards: the Table view.
* Design: docs/Features/Page/All-Boards.md, which is docs/Features/Page/Table.md.
* Design: docs/Features/All-Boards/All-Boards.md, which is docs/Features/General/Table-Page.md.
* Only what is particular to these rows; the table itself is tablePage.css. */
.all-boards-row {
cursor: default;
@@ -2258,3 +2258,49 @@ body.has-theme-board-tiles .board-list > li.js-board .board-list-card-count {
white-space: normal;
word-break: break-word;
}
/* #4791: the All Boards facet bar. */
.board-facets {
display: flex;
flex-direction: column;
gap: 0.3em;
margin: 0.4em 0 0.6em;
}
.board-facet-row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.3em;
}
.board-facet {
display: inline-flex;
align-items: center;
gap: 0.3em;
padding: 0.15em 0.6em;
border-radius: 1em;
background: rgba(0, 0, 0, 0.06);
cursor: pointer;
}
.board-facet.is-active {
background: #2b6cb0;
color: #fff;
}
.board-facet-count {
font-size: 0.8em;
opacity: 0.75;
}
.board-label-apply {
display: inline-flex;
flex-wrap: wrap;
gap: 0.3em;
}
.board-label-apply input {
width: auto;
margin: 0;
}
/* #802: an organization or team tab's members. */
.group-members-panel { margin: 0.4em 0 0.8em; padding: 0.5em; background: rgba(0, 0, 0, 0.04); border-radius: 4px; }
.group-members { display: flex; flex-wrap: wrap; gap: 0.3em 1em; list-style: none; padding: 0; }
.group-members-panel form { display: inline-flex; gap: 0.3em; }
.group-members-panel input { width: auto; margin: 0; }
+102 -14
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/Features/Page/Left-Menu.md
docs/Features/All-Boards/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/Features/Page/Left-Menu.md
docs/Features/All-Boards/Left-Menu.md
+leftMenuCollapse
ul.menu
//- ONE row, rendered four times, rather than four copies of the same
@@ -66,6 +66,17 @@ template(name="boardList")
// Workspaces tree
+workspaceTree(nodes=workspacesTree selectedWorkspaceId=selectedWorkspaceId)
hr.boards-menu-divider
//- #802: a tab per organization and team the member is in: its boards,
and its members, managed there by its admins.
if myGroups.length
ul.menu.group-tabs
each myGroups
li(class="menu-item {{#if isSelectedGroup}}active{{/if}}")
a.js-select-group(data-kind=kind data-id=id)
span.menu-label
i.fa(class="{{#if $eq kind 'org'}}fa-sitemap{{else}}fa-users{{/if}}")
| {{name}}
hr.boards-menu-divider
// Existing filter by orgs/teams (kept)
ul.AllBoardTeamsOrgs
@@ -99,21 +110,21 @@ 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/Features/Page/Left-Menu.md
docs/Features/All-Boards/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/Features/Page/All-Boards.md
page is named by the FIRST header bar. docs/Features/All-Boards/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/Features/Page/All-Boards.md
at one size and colour. docs/Features/All-Boards/All-Boards.md
+paneTitle(allBoardsPaneTitle)
if showsBoardSelectionControls
.board-multiselection-controls
@@ -124,7 +135,7 @@ template(name="boardList")
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/Features/Page/Archive.md
docs/Features/All-Boards/Archive.md
if isAllBoardsView 'table'
//- The create action is supplied to tablePage with its other controls,
rather than occupying an otherwise empty row of its own. Archive and
@@ -138,13 +149,77 @@ template(name="boardList")
span.boards-page-info {{boardsCurrentPage}} / {{boardsTotalPages}}
button.js-boards-next-page(class="{{#if hasBoardsNextPage}}{{else}}disabled{{/if}}")
| &gt;
//- #802: the selected organization or team - who is in it; its admins
add and remove members here. A directory-synced group is read-only.
if selectedGroup
.group-members-panel
h3
i.fa(class="{{#if $eq selectedGroup.kind 'org'}}fa-sitemap{{else}}fa-users{{/if}}")
| {{selectedGroup.name}}
ul.group-members
each groupMembers
li
| {{username}}
if fullname
span.quiet {{fullname}}
if isAdmin
span.quiet ({{_ 'admin'}})
if canManageGroup
a.js-remove-group-member(data-user-id=_id title="{{_ 'group-member-remove'}}")
i.fa.fa-times
if canManageGroup
form.js-add-group-member
input.js-group-member-username(type="text" placeholder="{{_ 'username'}}" aria-label="{{_ 'username'}}")
button.js-add-group-member-button(type="submit") {{_ 'group-member-add'}}
else
p.quiet {{_ 'group-members-read-only'}}
//- #4791: All Boards organized automatically - by first character,
organization, team and the member's own board labels, each with
its count; a click shows that group only.
.board-facets
if hasBoardFacets
.board-facet-row
each boardFacets.chars
a.board-facet.js-board-facet(data-type="char" data-value=key class="{{#if isFacet 'char'}}is-active{{/if}}")
| {{key}}
span.board-facet-count {{count}}
if boardFacets.orgs.length
.board-facet-row
i.fa.fa-sitemap
each boardFacets.orgs
a.board-facet.js-board-facet(data-type="org" data-value=key class="{{#if isFacet 'org'}}is-active{{/if}}")
| {{name}}
span.board-facet-count {{count}}
if boardFacets.teams.length
.board-facet-row
i.fa.fa-users
each boardFacets.teams
a.board-facet.js-board-facet(data-type="team" data-value=key class="{{#if isFacet 'team'}}is-active{{/if}}")
| {{name}}
span.board-facet-count {{count}}
.board-facet-row
i.fa.fa-tag
each boardFacets.labels
a.board-facet.js-board-facet(data-type="label" data-value=key class="{{#if isFacet 'label'}}is-active{{/if}}")
| {{key}}
span.board-facet-count {{count}}
span.js-remove-board-label(data-value=key title="{{_ 'board-label-remove'}}")
i.fa.fa-times
form.board-label-apply.js-apply-board-label
input.js-board-label-name(type="text" maxlength="40" placeholder="{{_ 'board-label-name'}}" aria-label="{{_ 'board-label-name'}}")
input.js-board-label-text(type="text" placeholder="{{_ 'board-label-name-includes'}}" aria-label="{{_ 'board-label-name-includes'}}")
button.js-apply-board-label-button(type="submit") {{_ 'board-label-apply'}}
if hasBoardFacet
a.js-board-facet-clear
i.fa.fa-times
| {{_ 'board-facet-clear'}}
ul.board-list.clearfix.js-boards.dragscroll(class="{{#if isMiniScreen}}mobile-view{{/if}} {{#if BoardMultiSelection.isActive}}is-multiselection-active{{/if}}")
//- The Remove bar, drawn only while a board from Home is actually
being dragged - the launcher's, which appears at the top of the
screen when an icon is picked up and is not there the rest of the
time. Dropping a board here takes it off Home; the board itself
stays where it lives, the way an app stays in the drawer.
docs/Features/Board/Home.md
docs/Features/All-Boards/Home.md
if showsHomeRemoveTarget
li.home-remove-target.js-home-remove
i.fa.fa-trash
@@ -183,7 +258,7 @@ template(name="boardList")
and the URL says what it is, which is what a bookmark bar shows.
Dragging one past another reorders them, and that order is the
order of the header dropdown - the two are views of one array.
docs/Features/Board/Starred.md
docs/Features/Pages/Starred.md
if isSelectedMenu 'starred'
each starredPages
li.js-bookmark.nodragscroll.board-list-item-bookmark(data-url="{{url}}", draggable="true")
@@ -326,6 +401,13 @@ template(name="boardList")
title="{{_ 'star-board-title'}}")
span.emoji-icon
i.fa(class="fa-star{{#unless isStarred}}-o{{/unless}}")
//- #4265: a board this user reaches only through a team,
organization or e-mail domain can be left: it is hidden
for them, and Member Settings / Hidden boards restores it.
if canHideBoard
a.js-hide-board(title="{{_ 'hide-board'}}")
span.emoji-icon
i.fa.fa-eye-slash
//- #2220 Home-board toggle is disabled: the icon and the
//- set-default-board action are intentionally not rendered.
@@ -425,7 +507,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/Features/Page/All-Boards.md
docs/Features/All-Boards/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
@@ -436,7 +518,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/Features/Page/Workspaces.md
docs/Features/All-Boards/Workspaces.md
if workspaceHasChildren
a.workspace-collapse-indicator.js-collapse-workspace(
title="{{workspaceCollapseLabel}}"
@@ -479,7 +561,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/Features/Page/Workspaces.md
docs/Features/All-Boards/Workspaces.md
if workspaceShowsChildren
+workspaceTree(nodes=children selectedWorkspaceId=../selectedWorkspaceId)
@@ -520,7 +602,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/Features/Page/Header.md
//- docs/Features/Header-Bar/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
@@ -530,7 +612,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/Features/Page/All-Boards.md
//- docs/Features/All-Boards/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
@@ -538,12 +620,18 @@ 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'}}
//- Export all boards (server/routes/exportAllBoards.js). It was a row of the
sidebar's home view only, which nothing on this page opens since All Boards
lost its hamburger: reachable only through Search and the back arrow.
a.board-header-btn.js-export-all-boards(title="{{_ 'export-all-boards'}}")
i.fa.fa-download
span.board-header-btn-label {{_ 'export-all-boards'}}
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/Features/Page/Multi-Selection.md
find the row inside the sidebar. docs/Features/General/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}}")
+179 -33
View File
@@ -10,7 +10,7 @@ import getSlug from 'limax';
// The archived-at line on a tile in the Archive, in the reader's own format.
import { boardCreationAllowed } from '/client/lib/boardCreationAllowed';
// The All Boards URLs, and the slug path of a workspace in the tree.
// docs/Features/Page/All-Boards-URLs.md
// docs/Features/All-Boards/URLs.md
import {
ALL_BOARDS_SECTIONS,
defaultSection,
@@ -43,7 +43,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/Features/Page/Workspaces.md
// docs/Features/All-Boards/Workspaces.md
const {
BEFORE: DROP_BEFORE,
AFTER: DROP_AFTER,
@@ -59,7 +59,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/Features/Page/Search.md, docs/Features/Page/Multi-Selection.md
// instances. docs/Features/Board-Header/Search.md, docs/Features/General/Multi-Selection.md
import {
openAllBoardsSidebar,
closeAllBoardsSidebar,
@@ -70,6 +70,7 @@ import {
SIDEBAR_SEARCH,
SIDEBAR_MULTISELECTION,
} from '/models/lib/allBoardsSidebar';
import { errorMessage } from '/client/lib/errorMessage';
// SubsManager removed for Meteor 3 migration
@@ -228,7 +229,7 @@ function hasStarredBoards() {
// board has always been stored in (#2220), which is what "opened after login"
// reads on its way past. Home is a VIEW of that one field, not a second copy of
// it, so setting a Home board from the menu and setting it from Multi-Selection
// cannot disagree. docs/Features/Board/Home.md
// cannot disagree. docs/Features/All-Boards/Home.md
function homeBoardId() {
const user = ReactiveCache.getCurrentUser();
return (user && user.profile && user.profile.defaultBoardId) || null;
@@ -245,7 +246,7 @@ function homeBoardId() {
// dragover is where it matters. Dragging a board out of Home may only end at
// the Trash - the one gesture that takes it off Home - so every other target
// has to REFUSE the drop while it is still in the air, which means answering
// before the drop happens. docs/Features/Board/Home.md
// before the drop happens. docs/Features/All-Boards/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';
@@ -254,7 +255,7 @@ 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
// DRAG_FROM_HOME is - a target has to decide whether to accept the drop before
// the drop happens. docs/Features/Board/Starred.md
// the drop happens. docs/Features/Pages/Starred.md
const BOOKMARK_DRAG = 'application/x-wekan-bookmark';
function isBookmarkDrag(evt) {
@@ -380,7 +381,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/Features/Page/Search.md
// page cannot read a ReactiveVar on it. docs/Features/Board-Header/Search.md
isSidebarOpen() {
return isAllBoardsSidebarOpen();
},
@@ -401,11 +402,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/Features/Page/Search.md, docs/Features/Page/Multi-Selection.md
// here. docs/Features/Board-Header/Search.md, docs/Features/General/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/Features/Page/All-Boards.md
// docs/Features/All-Boards/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`
@@ -433,7 +434,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/Features/Page/All-Boards.md
// fires for its own copy. docs/Features/All-Boards/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 -
@@ -472,6 +473,12 @@ Template.allBoardsHeaderButtons.events({
BoardMultiSelection.activate();
openAllBoardsSidebar(SIDEBAR_MULTISELECTION);
},
// Every board you can export, in one download (the same popup the sidebar's
// row opens).
'click .js-export-all-boards'(evt) {
evt.preventDefault();
Popup.open('exportAllBoards').call({ boardIds: null }, evt);
},
// The way OFF, beside the button that turned it on - the same pair the
// board's own Multi-Selection has. `stopPropagation` because this X sits
// inside the bar, and a click that also reached the button beside it would
@@ -489,7 +496,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/Features/Page/All-Boards-URLs.md
// the previous entry. docs/Features/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);
@@ -498,7 +505,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/Features/Page/All-Boards.md
// "which boards am I looking at". docs/Features/All-Boards/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.
@@ -506,7 +513,7 @@ 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/Features/Page/Archive.md
// opposite of every other one's. docs/Features/All-Boards/Archive.md
const showsArchive = tpl.selectedMenu.get() === 'archive';
let query = {
$and: [
@@ -562,6 +569,13 @@ function boardsForView(tpl) {
if (membershipOrs.length) {
query.$and.splice(2, 0, { $or: membershipOrs });
}
// #4265: boards this user hid by leaving them stay out of All Boards.
const hidden = currUser?.profile?.hiddenBoards;
if (Array.isArray(hidden) && hidden.length) query.$and.push({ _id: { $nin: hidden } });
// #4791: only the chosen facet's boards (the drag-ordered view).
const { facetSelector } = require('/models/lib/allBoardsFacets');
const facetPart = facetSelector(tpl.boardFacet && tpl.boardFacet.get(), currUser?.profile?.boardLabels);
if (facetPart) query.$and.push(facetPart);
} else if (
allowPrivateVisibilityOnly !== undefined &&
!allowPrivateVisibilityOnly.booleanValue
@@ -712,7 +726,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/Features/Page/All-Boards.md
// docs/Features/All-Boards/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
@@ -740,7 +754,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/Features/Page/All-Boards-URLs.md
// docs/Features/All-Boards/URLs.md
this.autorun(() => {
const slugPath = Session.get('boardListWorkspacePath') || [];
const tree = this.workspacesTreeVar.get();
@@ -783,6 +797,24 @@ Template.boardList.onCreated(function () {
// #5799: server-side pagination state for the sorted (non-custom) modes.
this.boardsPageVar = new ReactiveVar(1);
this.pagedBoardsVar = new ReactiveVar({ ids: [], total: 0 });
// #4791: the facet bar - first characters, organizations, teams and the
// member's own board labels with their counts - and the chosen group.
this.boardFacet = new ReactiveVar(null);
this.boardFacets = new ReactiveVar({ chars: [], orgs: [], teams: [], labels: [] });
this.loadBoardFacets = () => Meteor.call('allBoardsFacets', (error, facets) => {
if (!error && facets) this.boardFacets.set(facets);
});
this.loadBoardFacets();
// #802: the organization or team tab picked, and its members.
this.selectedGroup = new ReactiveVar(null);
this.groupMembers = new ReactiveVar({ canManage: false, members: [] });
this.loadGroupMembers = () => {
const group = this.selectedGroup.get();
if (!group) return;
Meteor.call('groupMembers', group.kind, group.id, (error, result) => {
this.groupMembers.set(error ? { canManage: false, members: [] } : result);
});
};
// #5850: the user's orgs/teams that have the per-org/team Shared Templates
// flag set (plus email domains), fetched via a non-admin server method since
// the org/team publications are admin-only. Gates the drag-to-share targets.
@@ -811,7 +843,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/Features/Page/Workspaces.md
// the answer. docs/Features/All-Boards/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
@@ -887,9 +919,10 @@ Template.boardList.onCreated(function () {
const search = (this.boardSearchVar.get() || '').trim();
const menu = this.selectedMenu.get();
const page = this.boardsPageVar.get();
const facet = this.boardFacet.get();
Meteor.call(
'getAllBoardsPage',
{ search, sortBy, menu, page, perPage: BOARDS_PER_PAGE },
{ search, sortBy, menu, page, perPage: BOARDS_PER_PAGE, ...(facet ? { facet } : {}) },
(err, res) => {
if (err) {
console.error('getAllBoardsPage failed:', err);
@@ -961,6 +994,29 @@ Template.boardList.onRendered(function () {
});
Template.boardList.helpers({
// #4791: the facet bar.
boardFacets() { return Template.instance().boardFacets.get(); },
hasBoardFacets() {
const f = Template.instance().boardFacets.get();
return f.chars.length + f.orgs.length + f.teams.length + f.labels.length > 1;
},
isFacet(type) {
const f = Template.instance().boardFacet.get();
return !!(f && f.type === type && f.value === (this.key || ''));
},
hasBoardFacet() { return !!Template.instance().boardFacet.get(); },
// #802
myGroups() {
const { myGroups } = require('/models/lib/groupMembers');
return myGroups(ReactiveCache.getCurrentUser());
},
isSelectedGroup() {
const g = Template.instance().selectedGroup.get();
return !!(g && g.kind === this.kind && g.id === this.id);
},
selectedGroup() { return Template.instance().selectedGroup.get(); },
groupMembers() { return Template.instance().groupMembers.get().members; },
canManageGroup() { return Template.instance().groupMembers.get().canManage; },
userHasTeams() {
if (ReactiveCache.getCurrentUser()?.teams?.length > 0) return true;
else return false;
@@ -994,7 +1050,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/Features/Page/All-Boards.md
// docs/Features/All-Boards/All-Boards.md
tablePageData() {
const tpl = Template.instance();
const all = boardsForView(tpl);
@@ -1071,6 +1127,14 @@ Template.boardList.helpers({
return user && user.hasStarred(this._id);
},
// #4265: not an active member - so it is here through a team, organization
// or e-mail domain - and not the archive.
canHideBoard() {
const userId = Meteor.userId();
if (!userId || this.archived) return false;
return !(this.members || []).some(m => m && m.userId === userId && m.isActive !== false);
},
// When this board was archived, in the reader's own date format. Shown only
// in the Archive, where it is what one old board is told from another by.
//
@@ -1151,7 +1215,7 @@ Template.boardList.helpers({
extraClass: 'js-open-archived-board' },
// The Home row is also a drop target of its own - dropping a board on it
// makes that board the one that opens after login - so it carries a
// second class like the Archive does. docs/Features/Board/Home.md
// second class like the Archive does. docs/Features/All-Boards/Home.md
home: { icon: 'fa-home', labelKey: 'home', extraClass: 'js-home-menu' },
};
return menuSectionOrder(hasStarredBoards()).map(type => ({
@@ -1169,7 +1233,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/Features/Page/All-Boards.md
// thing. docs/Features/All-Boards/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
@@ -1190,14 +1254,14 @@ Template.boardList.helpers({
// The bookmarks, drawn as tiles beside the starred boards. Only in Starred:
// that is the list of places you keep, and a bookmark is one of them.
// docs/Features/Board/Starred.md
// docs/Features/Pages/Starred.md
starredPages() {
const user = ReactiveCache.getCurrentUser();
return user && user.starredPages ? user.starredPages() : [];
},
// Is the Android-launcher Remove bar showing? Only in Home, and only while a
// board from Home is actually in the air. docs/Features/Board/Home.md
// board from Home is actually in the air. docs/Features/All-Boards/Home.md
showsHomeRemoveTarget() {
return Template.instance().selectedMenu.get() === 'home'
&& draggingFromHome.get();
@@ -1319,7 +1383,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/Features/Page/Workspaces.md
// away from where the slot was drawn. docs/Features/All-Boards/Workspaces.md
const WORKSPACE_DROP_CLASSES = {
[DROP_BEFORE]: 'drop-before',
[DROP_INSIDE]: 'drop-inside',
@@ -1377,7 +1441,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/Features/Page/Workspaces.md
// docs/Features/All-Boards/Workspaces.md
workspaceHasChildren() {
return hasChildren(Template.currentData());
},
@@ -1506,6 +1570,76 @@ function persistBoardOrderFromDom(evt, tpl) {
}
Template.boardList.events({
// #802: an organization or team tab: its boards, and its members.
'click .js-select-group'(evt, tpl) {
evt.preventDefault();
const { kind, id } = evt.currentTarget.dataset;
const current = tpl.selectedGroup.get();
if (current && current.kind === kind && current.id === id) {
tpl.selectedGroup.set(null);
tpl.boardFacet.set(null);
return;
}
const { myGroups } = require('/models/lib/groupMembers');
const group = myGroups(ReactiveCache.getCurrentUser()).find(g => g.kind === kind && g.id === id);
tpl.selectedGroup.set(group || { kind, id, name: '' });
tpl.boardFacet.set({ type: kind, value: id });
tpl.boardsPageVar.set(1);
tpl.loadGroupMembers();
},
async 'click .js-add-group-member-button, submit .js-add-group-member'(evt, tpl) {
evt.preventDefault();
const group = tpl.selectedGroup.get();
const username = String(tpl.$('.js-group-member-username').val() || '').trim();
if (!group || !username) return;
try {
await Meteor.callAsync('addGroupMember', group.kind, group.id, username);
tpl.$('.js-group-member-username').val('');
tpl.loadGroupMembers();
} catch (error) {
alert(errorMessage(error));
}
},
async 'click .js-remove-group-member'(evt, tpl) {
evt.preventDefault();
const group = tpl.selectedGroup.get();
if (!group) return;
await Meteor.callAsync('removeGroupMember', group.kind, group.id, evt.currentTarget.dataset.userId);
tpl.loadGroupMembers();
},
// #4791: pick a group of the facet bar, again to clear it.
'click .js-board-facet'(evt, tpl) {
evt.preventDefault();
const { type, value } = evt.currentTarget.dataset;
const current = tpl.boardFacet.get();
tpl.boardFacet.set(current && current.type === type && current.value === value ? null : { type, value });
tpl.boardsPageVar.set(1);
},
'click .js-board-facet-clear'(evt, tpl) {
evt.preventDefault();
tpl.boardFacet.set(null);
tpl.boardsPageVar.set(1);
},
async 'click .js-apply-board-label-button, submit .js-apply-board-label'(evt, tpl) {
evt.preventDefault();
const label = String(tpl.$('.js-board-label-name').val() || '');
const text = String(tpl.$('.js-board-label-text').val() || '');
try {
await Meteor.callAsync('applyBoardLabel', label, text);
tpl.$('.js-board-label-name').val('');
tpl.$('.js-board-label-text').val('');
tpl.loadBoardFacets();
} catch (error) {
alert(errorMessage(error));
}
},
async 'click .js-remove-board-label'(evt, tpl) {
evt.preventDefault();
evt.stopPropagation();
await Meteor.callAsync('removeBoardLabel', evt.currentTarget.dataset.value);
if (tpl.boardFacet.get()?.type === 'label') tpl.boardFacet.set(null);
tpl.loadBoardFacets();
},
// Boards in Archive no longer NAVIGATES anywhere. It is a section of this
// page, drawn beside the left menu by the generic `js-select-menu` click
// below - selecting it from a menu row and then losing the menu is a menu
@@ -1612,6 +1746,18 @@ Template.boardList.events({
tpl.boardsPageVar.set(adjacentPage(total, tpl.boardsPageVar.get(), 1,
BOARDS_PER_PAGE));
},
// #4265: leave a board that came through a team: it is hidden for this user.
'click .js-hide-board'(evt, tpl) {
evt.preventDefault();
evt.stopPropagation();
const boardId = this._id;
if (!boardId) return;
Meteor.call('quitBoard', boardId, (err) => {
if (err) return;
const paged = tpl.pagedBoardsVar.get();
tpl.pagedBoardsVar.set({ ids: (paged.ids || []).filter(id => id !== boardId), total: Math.max(0, (paged.total || 1) - 1) });
});
},
'click .js-star-board'(evt) {
evt.preventDefault();
evt.stopPropagation();
@@ -1863,7 +2009,7 @@ Template.boardList.events({
if (confirm(TAPi18n.__('archive-board-confirm'))) {
const boardId = this._id;
Meteor.call('archiveBoard', boardId, (err) => {
if (err) alert(err?.reason || err?.message || 'Failed to archive board');
if (err) alert(errorMessage(err));
});
evt.preventDefault();
}
@@ -1999,7 +2145,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/Features/Page/Workspaces.md
// docs/Features/All-Boards/Workspaces.md
if (typeof dt.clearData === 'function') dt.clearData();
dt.setData('application/x-workspace-id', workspaceId);
@@ -2115,7 +2261,7 @@ Template.boardList.events({
// A board picked up in Home may only be dropped on the remove target. NOT
// calling preventDefault() is what REFUSES a drop in HTML5 drag and drop,
// 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
// drop landing and quietly doing nothing. docs/Features/All-Boards/Home.md
if (isDragFromHome(evt)) return;
const menuType = evt.currentTarget.getAttribute('data-type');
// Remaining accepts board drags generally. Starred additionally accepts a
@@ -2184,7 +2330,7 @@ Template.boardList.events({
boardIds.forEach((boardId) => {
Meteor.call('archiveBoard', boardId, (err) => {
if (err) alert(err?.reason || err?.message || 'Failed to archive board');
if (err) alert(errorMessage(err));
});
});
// The dragged boards are gone from this page, so a selection of them is
@@ -2305,7 +2451,7 @@ Template.boardList.events({
boardIds.forEach((boardId) => {
Meteor.call('clearDefaultBoard', boardId, (err) => {
if (err) alert(err?.reason || err?.message || 'Failed to remove from Home');
if (err) alert(errorMessage(err));
});
});
if (isMultiBoard) BoardMultiSelection.reset();
@@ -2320,7 +2466,7 @@ Template.boardList.events({
// predictable: you drop a board on Home and that board is Home, whatever was
// there before. Taking a board off Home is the opposite gesture - dragging it
// out of the Home section - and clicking the row's Multi-Selection toggle
// still toggles. docs/Features/Board/Home.md
// still toggles. docs/Features/All-Boards/Home.md
'dragover .js-home-menu'(evt) {
if (isArchivedMultiBoardDrag(evt)) return;
evt.preventDefault();
@@ -2360,7 +2506,7 @@ Template.boardList.events({
// 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');
if (err) alert(errorMessage(err));
});
if (isMultiBoard) BoardMultiSelection.reset();
},
@@ -2417,7 +2563,7 @@ Template.boardList.events({
const board = ReactiveCache.getBoard(boardId);
if (board && board.archived) {
Meteor.call('restoreBoard', boardId, (err) => {
if (err) alert(err?.reason || err?.message || 'Failed to restore board');
if (err) alert(errorMessage(err));
});
}
// ...and out of whatever workspace it was in, which is the other half of
@@ -2574,7 +2720,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/Features/Page/Header.md
// that draws it follow it out of the sidebar. docs/Features/Header-Bar/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
@@ -0,0 +1,88 @@
/* #3194: Calendar Mode - Trello's calendar. */
.calendar-mode-view {
padding: 8px 12px;
overflow: auto;
}
.calendar-mode-toolbar {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 8px;
margin-bottom: 8px;
}
.calendar-mode-title {
margin: 0;
font-size: 1.3em;
}
.calendar-mode-buttons {
display: flex;
flex-wrap: wrap;
gap: 4px;
}
.calendar-mode-buttons button.is-active {
font-weight: bold;
box-shadow: inset 0 -2px 0 currentColor;
}
.calendar-mode-weekdays,
.calendar-mode-week {
display: grid;
grid-template-columns: repeat(7, minmax(0, 1fr));
}
.calendar-mode-weekday {
padding: 4px;
font-weight: bold;
text-align: center;
}
.calendar-mode-day {
min-height: 110px;
padding: 4px;
border: 1px solid rgba(0, 0, 0, 0.12);
background: rgba(255, 255, 255, 0.6);
min-width: 0;
}
.calendar-mode-day.is-outside {
opacity: 0.55;
}
.calendar-mode-day.is-today .calendar-mode-day-number {
font-weight: bold;
text-decoration: underline;
}
.calendar-mode-day-number {
font-size: 0.85em;
margin-bottom: 4px;
}
.calendar-mode-card {
display: block;
margin-bottom: 4px;
padding: 4px 6px;
border-radius: 4px;
background: #fff;
color: inherit;
text-decoration: none;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
}
.calendar-mode-card-labels {
display: flex;
flex-wrap: wrap;
gap: 2px;
margin-bottom: 2px;
}
.calendar-mode-card-labels .card-label {
font-size: 0.75em;
padding: 0 4px;
border-radius: 3px;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.calendar-mode-card-title {
white-space: normal;
overflow-wrap: anywhere;
}
@media screen and (max-width: 800px) {
.calendar-mode-day {
min-height: 70px;
}
}
@@ -0,0 +1,31 @@
//- #3194: Calendar Mode - Trello's calendar. A card on the day it is due, with
its labels and its whole title, the cards of a day in the board's order
(models/lib/calendarMode.js). A month or a week.
template(name="calendarModeView")
.calendar-mode-view.js-calendar-mode-view
if currentCard
+cardDetails(currentCard)
.calendar-mode-toolbar
h2.calendar-mode-title= rangeTitle
.calendar-mode-buttons
button.js-calendar-mode-today(type="button") {{_ 'today'}}
button.js-calendar-mode-prev(type="button" aria-label="{{_ 'previous'}}") {{_ 'previous'}}
button.js-calendar-mode-next(type="button" aria-label="{{_ 'next'}}") {{_ 'next'}}
button.js-calendar-mode-range(type="button" data-range="week" class="{{#if isRange 'week'}}is-active{{/if}}") {{_ 'week'}}
button.js-calendar-mode-range(type="button" data-range="month" class="{{#if isRange 'month'}}is-active{{/if}}") {{_ 'month'}}
.calendar-mode-grid(role="grid")
.calendar-mode-weekdays(role="row")
each weekdayNames
.calendar-mode-weekday(role="columnheader")= this
each weeks
.calendar-mode-week(role="row")
each this
.calendar-mode-day(role="gridcell" data-day="{{key}}" class="{{#unless inRange}}is-outside{{/unless}} {{#if isToday}}is-today{{/if}}")
.calendar-mode-day-number= dayOfMonth
each cardsOn key
a.calendar-mode-card.js-calendar-mode-card(href="{{cardUrl}}" data-card-id="{{_id}}" class="{{#if color}}card-label-{{color}}-border{{/if}}")
if cardLabels.length
.calendar-mode-card-labels
each cardLabels
span.card-label(class="card-label-{{color}}" title="{{name}}")= name
.calendar-mode-card-title= title
@@ -0,0 +1,97 @@
// #3194: Calendar Mode (see calendarModeView.jade, models/lib/calendarMode.js).
import { ReactiveVar } from 'meteor/reactive-var';
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
import { TAPi18n } from '/imports/i18n';
import { ReactiveCache } from '/imports/reactiveCache';
import { Utils } from '/client/lib/utils';
import { Filter } from '/client/lib/filter';
const { calendarModeDays, shiftAnchor, cardsByDueDay, RANGES } = require('/models/lib/calendarMode');
// The month or week a viewer chose is remembered in this browser only.
const RANGE_KEY = 'calendarModeRange';
const storedRange = () => {
try { const range = window.localStorage.getItem(RANGE_KEY); return RANGES.includes(range) ? range : 'month'; } catch (e) { return 'month'; }
};
const firstDayOfWeek = () => {
const user = ReactiveCache.getCurrentUser();
const day = user && typeof user.getStartDayOfWeek === 'function' ? user.getStartDayOfWeek() : 1;
return Number.isInteger(day) && day >= 0 && day <= 6 ? day : 1;
};
Template.calendarModeView.onCreated(function () {
this.anchor = new ReactiveVar(new Date());
this.range = new ReactiveVar(storedRange());
this.days = () => calendarModeDays({ anchor: this.anchor.get(), range: this.range.get(), firstDay: firstDayOfWeek() });
});
Template.calendarModeView.helpers({
isRange(range) {
return Template.instance().range.get() === range;
},
rangeTitle() {
const tpl = Template.instance();
const anchor = tpl.anchor.get();
const locale = TAPi18n.getLanguage();
try {
if (tpl.range.get() === 'week') {
const { weeks } = tpl.days();
const fmt = new Intl.DateTimeFormat(locale, { day: 'numeric', month: 'short', year: 'numeric' });
return `${fmt.format(weeks[0][0].date)} - ${fmt.format(weeks[0][6].date)}`;
}
return new Intl.DateTimeFormat(locale, { month: 'long', year: 'numeric' }).format(anchor);
} catch (e) {
return anchor.toDateString();
}
},
weekdayNames() {
const { weeks } = Template.instance().days();
const locale = TAPi18n.getLanguage();
return weeks[0].map(day => {
try { return new Intl.DateTimeFormat(locale, { weekday: 'short' }).format(day.date); } catch (e) { return ''; }
});
},
weeks() {
return Template.instance().days().weeks;
},
// The cards due on this day, in the board's order. Read once per render
// from the board's due cards in the shown range, with the board Filter.
cardsOn(key) {
const tpl = Template.instance();
const board = Utils.getCurrentBoard();
if (!board) return [];
const { start, end } = tpl.days();
const filter = Filter.isActive() ? Filter._getMongoSelector() : undefined;
const cards = board.cardsDueInBetween(start, end, filter);
const listSort = {};
ReactiveCache.getLists({ boardId: board._id }).forEach(list => { listSort[list._id] = list.sort; });
const byDay = cardsByDueDay(typeof cards.fetch === 'function' ? cards.fetch() : cards, listSort);
return byDay.get(key) || [];
},
cardUrl() {
const board = Utils.getCurrentBoard();
return board ? FlowRouter.path('card', { boardId: board._id, slug: board.slug, cardId: this._id }) : '#';
},
cardLabels() {
const board = Utils.getCurrentBoard();
const labels = (board && board.labels) || [];
return (this.labelIds || []).map(id => labels.find(label => label._id === id)).filter(Boolean);
},
});
Template.calendarModeView.events({
'click .js-calendar-mode-today'(event, tpl) { tpl.anchor.set(new Date()); },
'click .js-calendar-mode-prev'(event, tpl) { tpl.anchor.set(shiftAnchor(tpl.anchor.get(), tpl.range.get(), -1)); },
'click .js-calendar-mode-next'(event, tpl) { tpl.anchor.set(shiftAnchor(tpl.anchor.get(), tpl.range.get(), 1)); },
'click .js-calendar-mode-range'(event, tpl) {
const range = event.currentTarget.dataset.range;
if (!RANGES.includes(range)) return;
tpl.range.set(range);
try { window.localStorage.setItem(RANGE_KEY, range); } catch (e) { /* per-browser convenience only */ }
},
// A card opens in this tab, beside the calendar, as on the Calendar view.
'click .js-calendar-mode-card'(event) {
event.preventDefault();
FlowRouter.go(event.currentTarget.getAttribute('href'));
},
});
+16
View File
@@ -66,3 +66,19 @@
color: #111 !important;
opacity: 1;
}
/* #6751: FullCalendar already tints today's cell (.fc-day-today, a pale
--fc-today-bg-color); the day NUMBER is marked too, so today can be found at
a glance. The board theme's dark shade with white text - the same pair the
date picker's day buttons use (boardColors.css) - so it follows the board's
colour theme and keeps that contrast; the plain accent is the fallback. */
.calendar-view .fc .fc-daygrid-day.fc-day-today .fc-daygrid-day-number {
background: var(--board-theme-dark, var(--theme-accent, #005377));
border-radius: 999px;
color: #fff;
font-weight: bold;
margin: 2px;
min-width: 1.6em;
padding-inline: 4px;
text-align: center;
}
@@ -1,4 +1,4 @@
//- The 10 board report charts (docs/Features/Reports/charts.tsv), each a thin
//- The 10 board report charts (docs/Features/Board-Header/Board-Views/Charts/charts.tsv), each a thin
// wrapper around the shared boardChartView (charts/boardCharts.jade/.js):
// export buttons, the chart's title, a small CSS-bar/table visualization and
// the underlying data table - all driven from the `boardChartData` server
@@ -24,7 +24,7 @@ function loadFlowRenderer() {
// ReactiveVar holds the answer, helpers read it. Drawn with Chart.js (MIT,
// canvas-based), loaded with a dynamic import() so its code only reaches the
// browser when a chart view actually mounts, following
// docs/Features/Reports/charts.tsv for what each chartKey means.
// docs/Features/Board-Header/Board-Views/Charts/charts.tsv for what each chartKey means.
Template.boardChartView.onCreated(function() {
this.chartData = new ReactiveVar(null);
this.options = new ReactiveVar({});
+347
View File
@@ -0,0 +1,347 @@
/* Board View / Chat: Slack / Rocket.Chat's three panes in the board's own
theme. No colour of its own: the accent is the theme's (--theme-accent,
falling back to WeKan blue as paginationControls.css does), surfaces are
translucent over the board background, text inherits.
The container is laid out LEFT TO RIGHT in every language and the pane
order is decided in chatView.js (paneOrder), so a right-to-left language
gets the mirror image exactly once; the pane CONTENTS follow the page's own
direction. */
.chat-view {
direction: ltr;
display: flex;
align-items: stretch;
height: calc(100vh - 110px);
min-height: 320px;
overflow: hidden;
}
.chat-view[data-dir="rtl"] .chat-view-pane {
direction: rtl;
}
.chat-view-pane {
display: flex;
flex-direction: column;
min-width: 0;
overflow: hidden;
background: rgba(255, 255, 255, 0.85);
color: #172b4d;
}
.chat-view-tree,
.chat-view-chat {
flex-grow: 0;
flex-shrink: 0;
}
.chat-view-card {
flex: 1 1 auto;
}
.chat-view-pane-folded {
flex: 0 0 36px;
align-items: center;
padding-top: 8px;
}
.chat-view-pane-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
padding: 8px 12px;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
font-weight: bold;
}
.chat-view-pane-title {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* A folded pane keeps its double caret, above the pane's icon. */
.chat-view-pane-folded button {
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
}
/* Right to left the tree is on the right and the chat on the left, so every
double caret points the other way. */
.chat-view[data-dir="rtl"] .chat-view-fold-caret {
transform: scaleX(-1);
}
.chat-view-pane-header button,
.chat-view-pane-folded button {
margin: 0 !important;
padding: 4px 8px;
background: transparent;
color: inherit;
box-shadow: none;
}
/* The draggable borders. */
.chat-view-resizer {
flex: 0 0 6px;
cursor: col-resize;
background: rgba(0, 0, 0, 0.08);
touch-action: none;
}
.chat-view-resizer:hover,
.chat-view-resizer:focus {
background: var(--theme-accent, #01628c);
outline: none;
}
/* The tree. */
.chat-view-tree ul {
list-style: none;
margin: 0;
padding: 0;
}
.chat-view-workspaces {
overflow-y: auto;
padding: 6px 0;
}
.chat-view-node > ul,
.chat-view-collapse > ul {
padding-inline-start: 14px;
}
/* The caret before the icon: it points to where the text runs (right, or
left in a right-to-left language) when closed, and down when open. */
.chat-view-caret {
flex: 0 0 auto;
width: 0.8em;
text-align: center;
transition: transform 0.2s ease;
}
.chat-view-tree[dir="rtl"] .chat-view-caret {
transform: scaleX(-1);
}
.chat-view-node.is-open > .chat-view-row > .chat-view-caret {
transform: rotate(90deg);
}
.chat-view-tree[dir="rtl"] .chat-view-node.is-open > .chat-view-row > .chat-view-caret {
transform: rotate(90deg) scaleX(-1);
}
/* A card has no caret: its # holds the same place. */
.chat-view-channel > .chat-view-row > .chat-view-hash {
width: 0.8em;
text-align: center;
}
/* Opening and closing slide: the rows under a node grow from nothing to
their height (a 0fr to 1fr grid row) - and simply show or hide for a
member who asked the system for reduced motion. */
.chat-view-collapse {
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows 0.2s ease;
}
.chat-view-collapse > ul {
overflow: hidden;
min-height: 0;
visibility: hidden;
transition: visibility 0.2s;
}
.chat-view-node.is-open > .chat-view-collapse {
grid-template-rows: 1fr;
}
.chat-view-node.is-open > .chat-view-collapse > ul {
visibility: visible;
}
@media (prefers-reduced-motion: reduce) {
.chat-view-collapse,
.chat-view-collapse > ul,
.chat-view-caret {
transition: none;
}
}
/* Messages this member has not read, under that row. */
.chat-view-unread {
flex: 0 0 auto;
margin-inline-start: auto;
min-width: 1.6em;
padding: 0 6px;
border-radius: 9px;
background: var(--theme-accent, #01628c);
color: #fff;
font-size: 0.75em;
font-weight: bold;
line-height: 1.6;
text-align: center;
}
.chat-view-all-cards {
flex: 0 0 auto;
margin: 0;
padding: 0 4px;
border: 0;
background: none;
color: inherit;
opacity: 0.5;
cursor: pointer;
}
.chat-view-row:hover .chat-view-all-cards,
.chat-view-all-cards:focus-visible,
.chat-view-all-cards[aria-pressed="true"] {
opacity: 1;
}
.chat-view-row:focus-visible {
outline: 2px solid currentColor;
outline-offset: -2px;
}
.chat-view-row {
display: flex;
align-items: center;
gap: 8px;
padding: 3px 12px;
border-radius: 4px;
color: inherit;
text-decoration: none;
cursor: pointer;
}
.chat-view-row:hover {
background: rgba(0, 0, 0, 0.06);
}
.chat-view-row-title {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.chat-view-node-heading {
margin: 10px 0 2px;
padding-inline-start: 12px;
font-size: 0.8em;
text-transform: uppercase;
opacity: 0.7;
}
.chat-view-board.is-current > .chat-view-row {
font-weight: bold;
}
.chat-view-hash {
opacity: 0.6;
}
.chat-view-channel.is-selected > .chat-view-row {
background: var(--theme-accent, #01628c);
color: #fff;
}
/* The card: the same card details as everywhere, held in the pane instead
of floating beside a minicard. */
.chat-view-card-body {
flex: 1 1 auto;
overflow-y: auto;
}
.chat-view-card-body .card-details {
position: static !important;
inset: auto !important;
width: auto !important;
max-width: none !important;
height: auto !important;
max-height: none !important;
transform: none !important;
visibility: visible !important;
box-shadow: none !important;
margin: 0 !important;
}
.chat-view-empty {
padding: 16px;
}
/* The chat. */
.chat-view-messages {
flex: 1 1 auto;
overflow-y: auto;
padding: 8px 12px;
}
.chat-view-message {
display: flex;
align-items: flex-start;
gap: 8px;
padding: 6px 0;
}
.chat-view-message-body {
min-width: 0;
flex: 1 1 auto;
}
.chat-view-message-author {
font-weight: bold;
margin-inline-end: 8px;
}
.chat-view-message-time {
font-size: 0.8em;
}
.chat-view-message-text {
overflow-wrap: anywhere;
}
.chat-view-message.is-mine .chat-view-message-author {
color: var(--theme-accent, #01628c);
}
.chat-view-compose {
display: flex;
gap: 8px;
padding: 8px 12px;
border-top: 1px solid rgba(0, 0, 0, 0.1);
}
.chat-view-compose textarea {
flex: 1 1 auto;
min-height: 2.5em;
margin: 0;
resize: vertical;
}
.chat-view-compose button {
margin: 0 !important;
align-self: flex-end;
}
/* A dark theme: the panes darken with it. */
body.dark-mode .chat-view-pane,
.board-color-dark .chat-view-pane {
background: rgba(30, 30, 30, 0.88);
color: #eee;
}
/* Narrow windows: one pane at a time is all that fits; the tree and the chat
stack above and below the card. */
@media (max-width: 800px) {
.chat-view {
flex-direction: column;
height: auto;
}
.chat-view-pane {
flex-basis: auto !important;
}
.chat-view-resizer {
display: none;
}
}
+174
View File
@@ -0,0 +1,174 @@
//- Board View / Chat: the board as a Slack / Rocket.Chat workspace, in the
board's own theme. Design: docs/Features/Board-Header/Board-Views/Chat-View.md.
//-
//- tree Workspaces > boards > swimlanes > lists > cards. A card is a
//- channel; everything can be dragged to reorder it.
//- card the selected card, the same card details as everywhere else.
//- chat the card's comments as messages, newest at the bottom.
//-
//- The panes are laid out in screen order from paneOrder(): tree | card | chat
in a left-to-right language, mirrored in a right-to-left one. Each pane folds
to a thin bar, and the two borders between them are dragged to resize; both
are saved on the member's profile (setChatViewLayout).
template(name="chatView")
.chat-view.nodragscroll(data-dir="{{dir}}")
each panes
if $eq this 'tree'
+chatViewTreePane
else if $eq this 'card'
+chatViewCardPane
else
+chatViewChatPane
if resizerAfter this
.chat-view-resizer.js-chat-resize(data-pane="{{resizerAfter this}}" role="separator" aria-orientation="vertical" tabindex="0" title="{{_ 'chat-view-resize'}}")
template(name="chatViewTreePane")
if collapsed 'tree'
.chat-view-pane.chat-view-pane-folded
//- The double caret stays where it was, pointing the way the pane opens.
button.js-chat-unfold(data-pane="tree" title="{{_ 'chat-view-show-channels'}}" aria-label="{{_ 'chat-view-show-channels'}}")
i.fa.fa-angle-double-right.chat-view-fold-caret(aria-hidden="true")
i.fa.fa-sitemap(aria-hidden="true")
else
nav.chat-view-pane.chat-view-tree(style="flex-basis: {{width 'tree'}}px" aria-label="{{_ 'chat-view-channels'}}" dir="{{treeDir}}")
.chat-view-pane-header
span.chat-view-pane-title {{_ 'chat-view-channels'}}
button.js-chat-fold(data-pane="tree" title="{{_ 'chat-view-hide'}}")
i.fa.fa-angle-double-left.chat-view-fold-caret
//- Workspaces > boards > swimlanes > lists > cards that have a
conversation (server/chatView.js), each level folding open under its
caret; every board without one is under Other boards, until its first
message moves it up to its place.
with channelTree
ul.chat-view-workspaces(role="tree")
each workspaces
+chatViewWorkspaceNode
each unassigned
+chatViewBoardNode
if other.length
li.chat-view-node.chat-view-node-heading {{_ 'chat-view-other-boards'}}
each other
+chatViewBoardNode
//- A row: the caret before the icon (after it, mirrored, right to left),
the name as Markdown, and the messages this member has not read.
template(name="chatViewWorkspaceNode")
li.chat-view-node.chat-view-workspace.js-chat-draggable.js-chat-drop(draggable="true" data-kind="workspace" data-id="{{id}}" role="treeitem" aria-expanded="{{#if isOpen 'workspace' id}}true{{else}}false{{/if}}" class="{{#if isOpen 'workspace' id}}is-open{{/if}}")
.chat-view-row.js-chat-toggle(data-key="workspace:{{id}}" tabindex="0")
i.chat-view-caret.fa.fa-caret-right(aria-hidden="true")
i.fa(class="{{#if isOpen 'workspace' id}}fa-folder-open-o{{else}}fa-folder-o{{/if}}" aria-hidden="true")
span.chat-view-row-title
+inlineViewer
= name
+chatViewUnread(count=unread)
.chat-view-collapse
ul(role="group")
each children
+chatViewWorkspaceNode
each boards
+chatViewBoardNode
template(name="chatViewBoardNode")
li.chat-view-node.chat-view-board.js-chat-draggable(draggable="true" data-kind="board" data-id="{{_id}}" role="treeitem" aria-expanded="{{#if isOpen 'board' _id}}true{{else}}false{{/if}}" class="{{#if isOpen 'board' _id}}is-open{{/if}} {{#if isCurrentNode}}is-current{{/if}}")
//- Clicking a board folds or unfolds it here; it does not leave the Chat view.
.chat-view-row.js-chat-toggle(data-key="board:{{_id}}" data-board-id="{{_id}}" tabindex="0")
i.chat-view-caret.fa.fa-caret-right(aria-hidden="true")
i.fa.fa-columns(aria-hidden="true")
span.chat-view-row-title
+inlineViewer
= title
+chatViewUnread(count=unread)
if hasMessages
button.chat-view-all-cards.js-chat-all-cards(type="button" data-board-id="{{_id}}" title="{{#if full}}{{_ 'chat-view-message-cards'}}{{else}}{{_ 'chat-view-all-cards'}}{{/if}}" aria-pressed="{{#if full}}true{{else}}false{{/if}}")
i.fa(class="{{#if full}}fa-comments{{else}}fa-list-ul{{/if}}")
.chat-view-collapse
ul(role="group")
each swimlanes
li.chat-view-node.js-chat-drop(class="{{#if isCurrentNode}}js-chat-draggable{{/if}} {{#if isOpen 'swimlane' _id}}is-open{{/if}}" draggable="{{#if isCurrentNode}}true{{else}}false{{/if}}" data-kind="swimlane" data-id="{{_id}}" role="treeitem" aria-expanded="{{#if isOpen 'swimlane' _id}}true{{else}}false{{/if}}")
.chat-view-row.js-chat-toggle(data-key="swimlane:{{_id}}" tabindex="0")
i.chat-view-caret.fa.fa-caret-right(aria-hidden="true")
i.fa.fa-th-large(aria-hidden="true")
span.chat-view-row-title
+inlineViewer
= title
+chatViewUnread(count=unread)
.chat-view-collapse
ul(role="group")
each lists
li.chat-view-node.js-chat-drop(class="{{#if isCurrentNode}}js-chat-draggable{{/if}} {{#if isOpen 'list' _id}}is-open{{/if}}" draggable="{{#if isCurrentNode}}true{{else}}false{{/if}}" data-kind="list" data-id="{{_id}}" data-swimlane-id="{{swimlaneId}}" role="treeitem" aria-expanded="{{#if isOpen 'list' _id}}true{{else}}false{{/if}}")
.chat-view-row.js-chat-toggle(data-key="list:{{_id}}" tabindex="0")
i.chat-view-caret.fa.fa-caret-right(aria-hidden="true")
i.fa.fa-list(aria-hidden="true")
span.chat-view-row-title
+inlineViewer
= title
+chatViewUnread(count=unread)
.chat-view-collapse
ul(role="group")
each cards
li.chat-view-node.chat-view-channel.js-chat-drop(class="{{#if isCurrentNode}}js-chat-draggable{{/if}} {{#if isSelected _id}}is-selected{{/if}}" draggable="{{#if isCurrentNode}}true{{else}}false{{/if}}" data-kind="card" data-id="{{_id}}" role="treeitem")
.chat-view-row.js-chat-select-card(data-card-id="{{_id}}" data-board-id="{{boardId}}" tabindex="0")
span.chat-view-hash #
span.chat-view-row-title
+inlineViewer
= title
+chatViewUnread(count=unread)
template(name="chatViewUnread")
if count
span.chat-view-unread(title="{{_ 'chat-view-unread' count=count}}" aria-label="{{_ 'chat-view-unread' count=count}}") {{countText}}
template(name="chatViewCardPane")
if collapsed 'card'
.chat-view-pane.chat-view-pane-folded
button.js-chat-unfold(data-pane="card" title="{{_ 'chat-view-show-card'}}")
i.fa.fa-id-card-o
else
section.chat-view-pane.chat-view-card
.chat-view-pane-header
span.chat-view-pane-title {{_ 'card'}}
button.js-chat-fold(data-pane="card" title="{{_ 'chat-view-hide'}}")
i.fa.fa-compress
if selectedCard
.chat-view-card-body
+cardDetails(selectedCard)
else
p.quiet.chat-view-empty {{_ 'chat-view-pick-card'}}
template(name="chatViewChatPane")
if collapsed 'chat'
.chat-view-pane.chat-view-pane-folded
button.js-chat-unfold(data-pane="chat" title="{{_ 'chat-view-show-chat'}}" aria-label="{{_ 'chat-view-show-chat'}}")
i.fa.fa-angle-double-left.chat-view-fold-caret(aria-hidden="true")
i.fa.fa-comments(aria-hidden="true")
else
section.chat-view-pane.chat-view-chat(style="flex-basis: {{width 'chat'}}px")
.chat-view-pane-header
span.chat-view-pane-title
if selectedCard
| #
+inlineViewer
= selectedCard.title
else
| {{_ 'comments'}}
button.js-chat-fold(data-pane="chat" title="{{_ 'chat-view-hide'}}")
i.fa.fa-angle-double-right.chat-view-fold-caret
.chat-view-messages.js-chat-messages
each messages
.chat-view-message(class="{{#if mine}}is-mine{{/if}}")
+userAvatar(userId=userId noRemove=true)
.chat-view-message-body
.chat-view-message-meta
span.chat-view-message-author {{author}}
span.quiet.chat-view-message-time {{time}}
.chat-view-message-text
+viewer
= text
unless messages.length
if selectedCard
p.quiet.chat-view-empty {{_ 'chat-view-no-messages'}}
if canComment
form.chat-view-compose.js-chat-send
textarea.js-chat-input(rows="2" placeholder="{{_ 'chat-view-message-placeholder'}}" aria-label="{{_ 'chat-view-message-placeholder'}}")
button.primary(type="submit" title="{{_ 'chat-view-send'}}")
i.fa.fa-paper-plane
+443
View File
@@ -0,0 +1,443 @@
import { ReactiveVar } from 'meteor/reactive-var';
import { ReactiveCache } from '/imports/reactiveCache';
import CardComments from '/models/cardComments';
import Cards from '/models/cards';
import Swimlanes from '/models/swimlanes';
import { Utils } from '/client/lib/utils';
import { TAPi18n } from '/imports/i18n';
const {
DEFAULT_LAYOUT, cleanChatLayout, paneOrder, resizePane, buildChannelTree,
dropNeighbours, dropAllowed,
} = require('/models/lib/chatView');
const { dropPosition, moveWorkspace } = require('/models/lib/workspacesTree');
// Board View / Chat (chatView.jade). The layout is the member's
// (profile.chatViewLayout): read reactively from the user document, changed
// locally while a border is dragged, and saved once when the drag ends.
const selectedCardId = new ReactiveVar(null);
const dragLayout = new ReactiveVar(null);
// The channel tree (server/chatView.js): the summary of the member's
// conversations, the boards opened with all their cards, and which rows are
// open - kept in this browser, a per-viewer convenience.
const channelSummary = new ReactiveVar(null);
const fullBoards = new ReactiveVar({});
// The board the member is on shows all its cards unless they chose only
// the ones with messages; its cards come from the board's own subscription,
// so a card dragged to another list moves in the tree at once.
const messagesOnly = new ReactiveVar(new Set());
function currentBoardStructure() {
const boardId = Utils.getCurrentBoardId();
if (!boardId || messagesOnly.get().has(boardId)) return null;
const live = item => item && !item.archived;
return {
swimlanes: (ReactiveCache.getSwimlanes({ boardId, archived: false }) || []).filter(live)
.map(s => ({ _id: s._id, boardId, title: s.title || '', sort: s.sort })),
lists: (ReactiveCache.getLists({ boardId, archived: false }) || []).filter(live)
.map(l => ({ _id: l._id, boardId, title: l.title || '', sort: l.sort, swimlaneId: l.swimlaneId || '' })),
cards: (ReactiveCache.getCards({ boardId, archived: false }) || []).filter(live)
.map(c => ({ cardId: c._id, boardId, swimlaneId: c.swimlaneId, listId: c.listId, title: c.title || '', sort: c.sort })),
};
}
// { 'board:<id>': true, 'list:<id>': false, ... }: what the member opened
// or closed; every other row is as defaultOpen says.
const OPEN_KEY = 'wekan-chat-view-open';
const readOpen = () => {
try {
const stored = JSON.parse(window.localStorage.getItem(OPEN_KEY) || '{}');
return stored && typeof stored === 'object' && !Array.isArray(stored) ? stored : {};
} catch (e) { return {}; }
};
const openRows = new ReactiveVar(readOpen());
function setOpen(key, open) {
const next = { ...openRows.get(), [key]: !!open };
const keys = Object.keys(next);
for (const old of keys.slice(0, Math.max(0, keys.length - 500))) delete next[old];
openRows.set(next);
try { window.localStorage.setItem(OPEN_KEY, JSON.stringify(next)); } catch (e) { /* not kept */ }
}
let reloadTimer = null;
function loadChannels(delay = 0) {
clearTimeout(reloadTimer);
reloadTimer = setTimeout(() => Meteor.call('chatViewChannels', (error, summary) => {
if (!error) channelSummary.set(summary);
}), delay);
}
function loadFullBoard(boardId) {
Meteor.call('chatViewBoardChannels', boardId, (error, full) => {
if (!error) fullBoards.set({ ...fullBoards.get(), [boardId]: full });
});
}
// The rows open before the member opened or closed any: the board they are
// on and the workspaces above it.
function defaultOpen(key) {
const boardId = Utils.getCurrentBoardId();
if (key === `board:${boardId}`) return true;
// Under a board that is open, its swimlanes and lists are too.
if (key.startsWith('swimlane:') || key.startsWith('list:')) return true;
if (!key.startsWith('workspace:')) return false;
const user = ReactiveCache.getCurrentUser();
const profile = (user && user.profile) || {};
const assigned = profile.boardWorkspaceAssignments && profile.boardWorkspaceAssignments[boardId];
if (!assigned) return false;
const find = (nodes, id) => {
for (const node of nodes || []) {
if (node.id === id) return node;
const inner = find(node.children, id);
if (inner) return inner;
}
return null;
};
const holds = node => !!node && (node.id === assigned || (node.children || []).some(holds));
return holds(find(profile.boardWorkspacesTree, key.slice('workspace:'.length)));
}
const isOpen = (kind, id) => {
const key = `${kind}:${id}`;
const chosen = openRows.get();
return Object.prototype.hasOwnProperty.call(chosen, key) ? chosen[key] === true : defaultOpen(key);
};
// The language's direction, reactively: the panes re-order the moment the
// member switches to or from a right-to-left language.
function isRtl() {
return !!(TAPi18n.getLanguageDirection && TAPi18n.getLanguageDirection() === 'rtl');
}
// Blaze hands over jQuery's event when jQuery is there, the DOM's otherwise.
const nativeEvent = event => event.originalEvent || event;
function layout() {
const live = dragLayout.get();
if (live) return live;
const user = ReactiveCache.getCurrentUser();
return cleanChatLayout(user && user.profile && user.profile.chatViewLayout);
}
function saveLayout(next) {
const clean = cleanChatLayout(next);
Meteor.call('setChatViewLayout', clean, () => dragLayout.set(null));
}
// The card opened in the middle, from any board in the tree: its own data
// (popupCardData) and conversation (chatViewCardComments) are subscribed.
function selectedCard() {
const id = selectedCardId.get();
return (id && ReactiveCache.getCard(id)) || null;
}
Template.chatView.onCreated(function () {
// The member's boards, for the Workspaces tree; the open board's own
// swimlanes, lists and cards are already subscribed by the board page.
this.subscribe('boards');
this.autorun(() => {
const id = selectedCardId.get();
if (id) {
this.subscribe('popupCardData', id);
this.subscribe('chatViewCardComments', id);
}
});
});
Template.chatView.helpers({
dir() { return isRtl() ? 'rtl' : 'ltr'; },
// The panes in SCREEN order: a flex row with dir=rtl would reverse them by
// itself, so the container is always laid out left to right (see the css)
// and the order is decided here, once.
panes() { return paneOrder(isRtl()); },
// The border to the end of a pane resizes the tree or the chat - the side
// panes. The card pane between them takes what is left.
resizerAfter(pane) {
const order = paneOrder(isRtl());
const at = order.indexOf(pane);
if (at === order.length - 1) return null;
const next = order[at + 1];
const side = pane === 'card' ? next : pane;
const l = layout();
if (side === 'tree' && l.treeCollapsed) return null;
if (side === 'chat' && l.chatCollapsed) return null;
return side;
},
});
const paneHelpers = {
collapsed(pane) { return layout()[`${pane}Collapsed`] === true; },
width(pane) { return layout()[`${pane}Width`] || DEFAULT_LAYOUT[`${pane}Width`]; },
selectedCard,
};
Template.chatViewTreePane.onCreated(function () {
loadChannels();
// A message written on this board - here or anywhere - is a reason to look
// again: a board's first message moves it out of Other boards.
this.autorun(() => {
const boardId = Utils.getCurrentBoardId();
if (boardId) CardComments.find({ boardId }).count();
loadChannels(500);
});
this.refresh = Meteor.setInterval(() => loadChannels(), 60000);
});
Template.chatViewTreePane.onDestroyed(function () {
Meteor.clearInterval(this.refresh);
});
Template.chatViewTreePane.helpers({
...paneHelpers,
treeDir() { return isRtl() ? 'rtl' : 'ltr'; },
channelTree() {
const user = ReactiveCache.getCurrentUser();
const profile = (user && user.profile) || {};
const full = { ...fullBoards.get() };
const current = currentBoardStructure();
if (current) full[Utils.getCurrentBoardId()] = current;
return buildChannelTree(channelSummary.get(), profile.boardWorkspacesTree, profile.boardWorkspaceAssignments, full);
},
});
const treeHelpers = {
isOpen,
// On the board the member is on: only there can rows be dragged, since a
// drop moves things on the open board.
isCurrentNode() { return this.boardId === Utils.getCurrentBoardId(); },
isSelected(id) { return selectedCardId.get() === id; },
};
Template.chatViewWorkspaceNode.helpers(treeHelpers);
Template.chatViewBoardNode.helpers(treeHelpers);
Template.chatViewUnread.helpers({
countText() { return this.count > 99 ? '99+' : String(this.count); },
});
Template.chatViewCardPane.helpers(paneHelpers);
Template.chatViewChatPane.helpers({
...paneHelpers,
messages() {
const card = selectedCard();
if (!card) return [];
const me = Meteor.userId();
return CardComments.find({ cardId: card._id }, { sort: { createdAt: 1 } }).fetch().map(c => {
const user = ReactiveCache.getUser(c.userId);
return {
_id: c._id,
userId: c.userId,
mine: c.userId === me,
author: user ? ((user.profile && user.profile.fullname) || user.username) : '',
time: c.createdAt ? new Date(c.createdAt).toLocaleString() : '',
text: c.text,
};
});
},
canComment() {
// The comment capability on the CARD's board, as the server decides
// (read-only roles too).
const card = selectedCard();
return !!(card && Utils.currentUserCan('comment', ReactiveCache.getBoard(card.boardId)));
},
});
// --- Drag and drop ------------------------------------------------------
let dragged = null;
async function dropOn(target, position) {
const boardId = Utils.getCurrentBoardId();
const { kind, id } = dragged;
const targetKind = target.dataset.kind;
const targetId = target.dataset.id;
if (!dropAllowed(kind, targetKind) || id === targetId) return;
if (kind === 'workspace') {
const user = ReactiveCache.getCurrentUser();
const tree = (user.profile && user.profile.boardWorkspacesTree) || [];
const next = moveWorkspace(tree, id, targetId, position);
if (next !== tree) Meteor.call('setWorkspacesTree', next);
return;
}
if (kind === 'board') {
Meteor.call('assignBoardToWorkspace', id, targetId);
return;
}
if (kind === 'swimlane') {
const siblings = ReactiveCache.getSwimlanes({ boardId, archived: false }) || [];
const { prev, next } = dropNeighbours(siblings, id, targetId, position === 'before' ? 'before' : 'after');
Swimlanes.update(id, { $set: { sort: Utils.calculateIndexData(prev, next, 1).base } });
return;
}
if (kind === 'list') {
const targetList = ReactiveCache.getList(targetId);
const siblings = (ReactiveCache.getLists({ boardId, archived: false }) || [])
.filter(l => !l.swimlaneId || l.swimlaneId === target.dataset.swimlaneId);
const { prev, next } = dropNeighbours(siblings, id, targetId, position === 'before' ? 'before' : 'after');
const update = { sort: Utils.calculateIndexData(prev, next, 1).base };
if (targetList && targetList.swimlaneId) update.swimlaneId = targetList.swimlaneId;
await Meteor.callAsync('updateListSort', id, boardId, update);
return;
}
// A card: before/after a card, or onto a list (its end). Where it lands is
// read from the TREE it was dropped on - the list and swimlane the member
// sees - not from a cached copy of the target card, which can still hold
// the list it was in before a move a moment ago.
const card = Cards.findOne(id);
if (!card) return;
const listId = targetKind === 'list' ? targetId : target.closest('[data-kind="list"]').dataset.id;
const swimlaneId = target.closest('[data-kind="swimlane"]').dataset.id;
const siblings = Cards.find({ boardId, listId, swimlaneId, archived: false }).fetch();
const neighbours = targetKind === 'list'
? dropNeighbours(siblings, id, null, 'after')
: dropNeighbours(siblings, id, targetId, position === 'before' ? 'before' : 'after');
await card.move(boardId, swimlaneId, listId, Utils.calculateIndexData(neighbours.prev, neighbours.next, 1).base);
}
Template.chatView.events({
'click .js-chat-fold'(event) {
event.preventDefault();
saveLayout({ ...layout(), [`${event.currentTarget.dataset.pane}Collapsed`]: true });
},
'click .js-chat-unfold'(event) {
event.preventDefault();
saveLayout({ ...layout(), [`${event.currentTarget.dataset.pane}Collapsed`]: false });
},
// A card is a channel: it opens in the middle, with its conversation
// beside it - from any board, without leaving the board the member is on.
'click .js-chat-select-card'(event) {
const link = event.target.closest && event.target.closest('a[href]');
if (link && event.currentTarget.contains(link)) return;
event.preventDefault();
selectedCardId.set(event.currentTarget.dataset.cardId);
},
// A workspace, board, swimlane or list folds or unfolds; a board without
// a conversation shows all its cards when opened, to write the first one.
'click .js-chat-toggle'(event) {
const link = event.target.closest && event.target.closest('a[href], button');
if (link && event.currentTarget.contains(link)) return;
event.preventDefault();
const key = event.currentTarget.dataset.key;
const [kind, id] = [key.slice(0, key.indexOf(':')), key.slice(key.indexOf(':') + 1)];
const open = !isOpen(kind, id);
setOpen(key, open);
if (open && kind === 'board' && id !== Utils.getCurrentBoardId()) {
const summary = channelSummary.get();
const talking = summary && summary.channels.some(c => c.boardId === id);
if (!talking && !fullBoards.get()[id]) loadFullBoard(id);
}
},
'keydown .js-chat-toggle, keydown .js-chat-select-card'(event) {
if (event.key !== 'Enter' && event.key !== ' ') return;
event.preventDefault();
event.currentTarget.click();
},
'click .js-chat-all-cards'(event) {
event.preventDefault();
event.stopPropagation();
const boardId = event.currentTarget.dataset.boardId;
if (boardId === Utils.getCurrentBoardId()) {
const only = new Set(messagesOnly.get());
if (only.has(boardId)) only.delete(boardId); else only.add(boardId);
messagesOnly.set(only);
} else {
const full = { ...fullBoards.get() };
if (full[boardId]) { delete full[boardId]; fullBoards.set(full); } else loadFullBoard(boardId);
}
setOpen(`board:${boardId}`, true);
},
// Resize: the pointer is captured on the handle, the width follows it
// locally, and the layout is saved once on release.
'pointerdown .js-chat-resize'(event) {
const handle = event.currentTarget;
const pane = handle.dataset.pane;
const rtl = isRtl();
const start = event.clientX;
const base = layout();
handle.setPointerCapture(event.pointerId);
const move = e => dragLayout.set(resizePane(base, pane, e.clientX - start, rtl));
const up = e => {
handle.removeEventListener('pointermove', move);
handle.removeEventListener('pointerup', up);
saveLayout(resizePane(base, pane, e.clientX - start, rtl));
};
handle.addEventListener('pointermove', move);
handle.addEventListener('pointerup', up);
},
// The keyboard way to the same thing: arrows move the border 16px.
'keydown .js-chat-resize'(event) {
const step = { ArrowLeft: -16, ArrowRight: 16 }[event.key];
if (!step) return;
event.preventDefault();
saveLayout(resizePane(layout(), event.currentTarget.dataset.pane, step, isRtl()));
},
'dragstart .js-chat-draggable'(event) {
event.stopPropagation();
const node = event.currentTarget;
dragged = { kind: node.dataset.kind, id: node.dataset.id };
const dt = nativeEvent(event).dataTransfer;
if (dt) {
dt.effectAllowed = 'move';
dt.setData('text/plain', node.dataset.id);
}
},
'dragover .js-chat-drop'(event) {
if (!dragged || !dropAllowed(dragged.kind, event.currentTarget.dataset.kind)) return;
event.preventDefault();
event.stopPropagation();
},
async 'drop .js-chat-drop'(event) {
if (!dragged) return;
event.preventDefault();
event.stopPropagation();
const target = event.currentTarget;
const row = target.querySelector('.chat-view-row') || target;
const rect = row.getBoundingClientRect();
const position = dropPosition(nativeEvent(event).clientY - rect.top, rect.height);
try {
await dropOn(target, position);
} finally {
dragged = null;
}
},
'dragend .js-chat-draggable'() { dragged = null; },
'submit .js-chat-send'(event, tpl) {
event.preventDefault();
const input = tpl.$('.js-chat-input');
const text = String(input.val() || '').trim();
const card = selectedCard();
if (!text || !card) return;
CardComments.insert({ text, boardId: card.boardId, cardId: card._id, parentId: '' });
input.val('');
loadChannels(500);
},
// Enter sends, Shift+Enter is a new line - as in Slack and Rocket.Chat.
'keydown .js-chat-input'(event) {
if (event.key === 'Enter' && !event.shiftKey && !event.isComposing) {
event.preventDefault();
$(event.currentTarget).closest('form').trigger('submit');
}
},
});
// The open channel is read: a message arriving while it is open is seen, so
// its unread count does not grow.
Template.chatViewChatPane.onCreated(function () {
this.autorun(() => {
const id = selectedCardId.get();
if (!id) return;
CardComments.find({ cardId: id }).count();
Tracker.nonreactive(() => {
const user = ReactiveCache.getCurrentUser();
if (user && typeof user.setCardLastViewed === 'function') Promise.resolve(user.setCardLastViewed(id)).then(() => loadChannels(1000), () => {});
});
});
});
// Keep the newest message in view, as a chat does.
Template.chatViewChatPane.onRendered(function () {
this.autorun(() => {
Template.currentData();
selectedCardId.get();
CardComments.find({ cardId: selectedCardId.get() }).count();
Tracker.afterFlush(() => {
const box = this.find('.js-chat-messages');
if (box) box.scrollTop = box.scrollHeight;
});
});
});
@@ -0,0 +1,10 @@
/* #211: the Column sets view. */
.column-sets-view { padding: 0.5em; }
.column-sets-toolbar { display: flex; flex-wrap: wrap; gap: 0.5em; align-items: center; margin-bottom: 0.6em; }
.column-sets-toolbar form { display: inline-flex; gap: 0.3em; margin: 0; }
.column-sets-toolbar input, .column-sets-toolbar select { width: auto; margin: 0; }
.column-sets-columns { display: flex; gap: 0.6em; align-items: flex-start; overflow-x: auto; }
.column-sets-column { min-width: 14em; max-width: 18em; background: rgba(0, 0, 0, 0.05); border-radius: 4px; padding: 0.4em; }
.column-sets-column-header { font-weight: bold; margin-bottom: 0.4em; display: flex; gap: 0.4em; align-items: center; }
.column-sets-card { background: #fff; border-radius: 3px; padding: 0.35em 0.5em; margin-bottom: 0.35em; box-shadow: 0 1px 0 rgba(0, 0, 0, 0.15); cursor: grab; }
.column-sets-card .card-label { font-size: 0.75em; margin-inline-end: 0.2em; }
@@ -0,0 +1,44 @@
//- #211: the same cards in several named column sets (models/lib/columnSets.js).
Pick a set; drag a card to another column to change its place in that set
only. Each card shows its column in the other sets.
template(name="columnSetsView")
.column-sets-view
.column-sets-toolbar
if columnSets.length
select.js-column-set-select(aria-label="{{_ 'column-sets'}}")
each columnSets
option(value=_id selected=isCurrentSet) {{name}}
if isBoardAdmin
form.js-add-column-set
input.js-column-set-name(type="text" maxlength="60" placeholder="{{_ 'column-set-new'}}" aria-label="{{_ 'column-set-new'}}")
button.js-add-column-set-button(type="submit") {{_ 'add'}}
if currentSet
form.js-add-column
input.js-column-title(type="text" maxlength="60" placeholder="{{_ 'column-new'}}" aria-label="{{_ 'column-new'}}")
select.js-column-color(aria-label="{{_ 'column-color'}}")
each colors
option(value=this) {{this}}
button.js-add-column-button(type="submit") {{_ 'add'}}
a.js-remove-column-set(title="{{_ 'column-set-remove'}}")
i.fa.fa-trash
if currentSet
.column-sets-columns
each columns
.column-sets-column.js-column-sets-drop(data-column-id=_id class="{{#if color}}card-label-{{color}}{{/if}}")
.column-sets-column-header
if title
| {{title}}
else
span.quiet {{_ 'column-not-placed'}}
span.quiet {{cards.length}}
if title
if isBoardAdmin
a.js-remove-column(data-column-id=_id title="{{_ 'column-remove'}}")
i.fa.fa-times
each cards
.column-sets-card.js-column-sets-card(draggable="{{#if canWrite}}true{{/if}}" data-card-id=_id)
a.js-column-sets-open-card(data-card-id=_id)= title
each otherBadges
span.card-label(class="card-label-{{color}}" title="{{setName}}") {{title}}
else
p.quiet {{_ 'column-sets-empty'}}
+106
View File
@@ -0,0 +1,106 @@
import { ReactiveVar } from 'meteor/reactive-var';
import { Random } from 'meteor/random';
import { ReactiveCache } from '/imports/reactiveCache';
import { Utils } from '/client/lib/utils';
import Boards from '/models/boards';
import Cards from '/models/cards';
const cs = require('/models/lib/columnSets');
// #211: the Column sets board view.
Template.columnSetsView.onCreated(function () {
this.setId = new ReactiveVar(null);
});
function currentSet(tpl) {
const board = Utils.getCurrentBoard();
const all = cs.sets(board);
return all.find(s => s._id === tpl.setId.get()) || all[0] || null;
}
function saveSets(next) {
const board = Utils.getCurrentBoard();
if (board && next) Boards.update(board._id, { $set: { columnSets: next } });
}
Template.columnSetsView.helpers({
columnSets() { return cs.sets(Utils.getCurrentBoard()); },
isCurrentSet() { const set = currentSet(Template.instance()); return !!(set && set._id === this._id); },
currentSet() { return currentSet(Template.instance()); },
colors() { return cs.COLORS; },
isBoardAdmin() { return !!ReactiveCache.getCurrentUser()?.isBoardAdmin(); },
canWrite() { const board = Utils.getCurrentBoard(); return !!board && Utils.currentUserCan('write', board); },
columns() {
const tpl = Template.instance();
const set = currentSet(tpl);
const board = Utils.getCurrentBoard();
if (!set || !board) return [];
const cards = ReactiveCache.getCards({ boardId: board._id, archived: false, type: 'cardType-card' }, { sort: { sort: 1 } });
const all = cs.sets(board);
return cs.columnsWithCards(set, cards).map(column => ({ ...column,
cards: column.cards.map(card => ({ _id: card._id, title: card.title, otherBadges: cs.otherSetBadges(card, all, set._id) })) }));
},
});
Template.columnSetsView.events({
'change .js-column-set-select'(event, tpl) {
tpl.setId.set(event.currentTarget.value);
},
'click .js-add-column-set-button, submit .js-add-column-set'(event, tpl) {
event.preventDefault();
const id = Random.id();
const next = cs.addColumnSet(cs.sets(Utils.getCurrentBoard()), tpl.$('.js-column-set-name').val(), id);
if (!next) return;
saveSets(next);
tpl.setId.set(id);
tpl.$('.js-column-set-name').val('');
},
'click .js-add-column-button, submit .js-add-column'(event, tpl) {
event.preventDefault();
const set = currentSet(tpl);
if (!set) return;
const next = cs.addColumn(cs.sets(Utils.getCurrentBoard()), set._id, tpl.$('.js-column-title').val(),
tpl.$('.js-column-color').val(), Random.id());
if (!next) return;
saveSets(next);
tpl.$('.js-column-title').val('');
},
'click .js-remove-column'(event, tpl) {
event.preventDefault();
const set = currentSet(tpl);
if (set) saveSets(cs.removeColumn(cs.sets(Utils.getCurrentBoard()), set._id, event.currentTarget.dataset.columnId));
},
'click .js-remove-column-set'(event, tpl) {
event.preventDefault();
const set = currentSet(tpl);
if (set && confirm(`${set.name}?`)) saveSets(cs.removeColumnSet(cs.sets(Utils.getCurrentBoard()), set._id));
tpl.setId.set(null);
},
'dragstart .js-column-sets-card'(event) {
const dt = event.originalEvent && event.originalEvent.dataTransfer;
if (dt) { dt.setData('text/plain', event.currentTarget.dataset.cardId); dt.effectAllowed = 'move'; }
},
'dragover .js-column-sets-drop'(event) {
event.preventDefault();
},
'drop .js-column-sets-drop'(event, tpl) {
event.preventDefault();
const dt = event.originalEvent && event.originalEvent.dataTransfer;
const cardId = dt && dt.getData('text/plain');
const set = currentSet(tpl);
const modifier = cs.placeModifier(set, event.currentTarget.dataset.columnId || null);
if (cardId && modifier) Cards.update(cardId, modifier);
},
'click .js-column-sets-open-card'(event) {
event.preventDefault();
const cardId = event.currentTarget.dataset.cardId;
const board = Utils.getCurrentBoard();
Meteor.subscribe('popupCardData', cardId, {
onReady() {
Session.set('popupCardId', cardId);
if (board) Session.set('popupCardBoardId', board._id);
if (!Popup.isOpen()) Popup.open('cardDetails')(event);
},
});
},
});
+10 -3
View File
@@ -1,6 +1,9 @@
import { ReactiveCache } from '/imports/reactiveCache';
import { ReactiveVar } from 'meteor/reactive-var';
import { Utils } from '/client/lib/utils';
import { ensureContrast } from '/models/lib/contrastColor';
import { surfacesFor } from '/models/lib/uiFonts';
import { activeThemeName } from '/client/lib/activeTheme';
import {
DEPENDENCY_ICON_CHOICES,
DEPENDENCY_TYPES,
@@ -138,13 +141,17 @@ Template.dependencyOverlay.onCreated(function () {
const y2 = dependent.y + dependent.h / 2;
const dx = Math.max(40, Math.abs(x2 - x1) / 2);
const path = `M ${x1} ${y1} C ${x1 + dx} ${y1}, ${x2 - dx} ${y2}, ${x2} ${y2}`;
const markerId = markerIdForColor(dep.color);
// The colour is the member's free choice (a colour wheel), drawn over the
// board: made visible against the theme's surface - a line is a graphic
// that says something, 3:1 (WCAG 1.4.11). The stored colour is untouched.
const drawColor = ensureContrast(dep.color, surfacesFor(activeThemeName()), 3);
const markerId = markerIdForColor(drawColor);
if (meta.directed) {
markerColors.add(dep.color);
markerColors.add(drawColor);
}
lines.push({
path,
color: dep.color,
color: drawColor,
type: dep.type,
icon: dep.icon,
directed: meta.directed,
+180
View File
@@ -0,0 +1,180 @@
/* Board View / Email (#794): Gmail's layout - folders on the start side, the
message table beside them - in the board's own theme. No colour of its own:
the accent is the theme's (--theme-accent, falling back to WeKan blue, as
paginationControls.css does), and a label is the board label's own colour.
Logical properties, so a right-to-left language mirrors it. */
.email-view {
display: flex;
align-items: flex-start;
gap: 16px;
padding: 12px;
min-height: 100%;
box-sizing: border-box;
}
.email-view-sidebar {
flex: 0 0 220px;
max-width: 220px;
position: sticky;
top: 0;
}
.email-view-compose {
display: inline-flex !important;
align-items: center;
margin: 0 0 12px 0 !important;
padding: 10px 20px;
border-radius: 16px;
background: var(--theme-accent, #01628c) !important;
color: #fff !important;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
}
.email-view-folders {
list-style: none;
margin: 0;
padding: 0;
}
.email-view-folder a {
display: flex;
align-items: center;
gap: 10px;
padding: 6px 12px;
border-start-end-radius: 16px;
border-end-end-radius: 16px;
color: inherit;
text-decoration: none;
}
.email-view-folder a:hover {
background: rgba(0, 0, 0, 0.06);
}
.email-view-folder.is-active a {
background: color-mix(in srgb, var(--theme-accent, #01628c) 18%, transparent);
font-weight: bold;
}
.email-view-folder-name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.email-view-folder-heading {
margin: 12px 0 4px;
padding-inline-start: 12px;
font-size: 0.8em;
text-transform: uppercase;
opacity: 0.7;
}
.email-view-label-dot {
display: inline-block;
flex: 0 0 auto;
width: 12px;
height: 12px;
border-radius: 3px;
}
.email-view-main {
flex: 1 1 auto;
min-width: 0;
}
.email-view-hint {
margin: 0 0 8px;
}
.email-view-row {
cursor: pointer;
}
.email-view-row:hover {
box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.15);
}
/* Received and not yet sent by us: bold, the way Gmail marks what is new to
you. Sent mail reads plainly. */
.email-view-row:not(.is-outgoing) .email-view-from,
.email-view-row:not(.is-outgoing) .email-view-subject-title {
font-weight: bold;
}
.email-view-snippet {
opacity: 0.7;
/* Two lines of the rendered message, then an ellipsis. */
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
line-clamp: 2;
overflow: hidden;
}
/* Rendered Markdown in a row reads as the row's text: a heading or a
paragraph keeps the row's size and flows on one line with the rest. */
.email-view-subject-title .viewer :is(h1, h2, h3, h4, h5, h6, p),
.email-view-snippet .viewer :is(h1, h2, h3, h4, h5, h6, p, ul, ol, li, blockquote) {
display: inline;
margin: 0;
padding: 0;
font-size: inherit;
line-height: inherit;
}
.email-view-snippet .viewer :is(h1, h2, h3, h4, h5, h6, p, li)::after {
content: ' ';
}
.email-view-subject-title .viewer img,
.email-view-snippet .viewer img {
max-height: 1.4em;
vertical-align: middle;
}
.email-view-row-label {
margin-inline-start: 6px;
padding: 0 6px;
font-size: 0.8em;
vertical-align: middle;
}
.email-view-reply {
margin-inline-start: 8px;
opacity: 0;
color: inherit;
}
.email-view-row:hover .email-view-reply,
.email-view-reply:focus {
opacity: 0.8;
}
.email-view-date {
white-space: nowrap !important;
text-align: end;
}
.email-compose label {
display: block;
}
.email-compose input,
.email-compose textarea {
width: 100%;
box-sizing: border-box;
}
/* Narrow windows: the folders above the messages, like Gmail's mobile menu. */
@media (max-width: 800px) {
.email-view {
flex-direction: column;
}
.email-view-sidebar {
position: static;
flex: 0 0 auto;
max-width: none;
width: 100%;
}
}
+89
View File
@@ -0,0 +1,89 @@
//- Board View / Email (#794): the board as a Gmail-like mailbox, in the
board's own theme. Design: docs/Features/Board-Header/Board-Views/Email-View.md.
//-
//- Left: Compose, the folders (Inbox = the list email to card puts new cards
in, Sent, All mail), the board's lists as folders and its labels as Gmail
labels. Right: the shared table page (docs/Features/General/Table.md) - one
page of messages, newest first. A row opens the card popup, as a message
opens in Gmail; its comments are the conversation.
template(name="emailView")
.email-view
nav.email-view-sidebar(aria-label="{{_ 'board-view-email'}}")
button.primary.email-view-compose.js-email-compose(type="button")
i.fa.fa-pencil
| &nbsp;{{_ 'email-view-compose'}}
ul.email-view-folders
each systemFolders
li.email-view-folder(class="{{#if active}}is-active{{/if}}")
a.js-email-folder(href="#" data-folder="{{id}}")
i.fa(class="{{icon}}")
span.email-view-folder-name {{_ labelKey}}
if listFolders.length
li.email-view-folder-heading {{_ 'lists'}}
each listFolders
li.email-view-folder(class="{{#if active}}is-active{{/if}}")
a.js-email-folder(href="#" data-folder="{{id}}")
i.fa.fa-folder-o
span.email-view-folder-name
+inlineViewer
= title
if labelFolders.length
li.email-view-folder-heading {{_ 'labels'}}
each labelFolders
li.email-view-folder(class="{{#if active}}is-active{{/if}}")
a.js-email-folder(href="#" data-folder="{{id}}")
span.email-view-label-dot(class="{{labelColorClass color}}" style="{{labelColorStyle color}}")
span.email-view-folder-name
+inlineViewer
= name
.email-view-main
unless receiving
p.quiet.email-view-hint {{_ 'email-view-not-receiving'}}
+tablePage(tablePageData)
//- One message. Cells match the header: From, Subject, Date.
template(name="emailViewRow")
tr.email-view-row.js-email-open(data-card-id="{{_id}}" class="{{#if outgoing}}is-outgoing{{/if}}")
td.email-view-from
if outgoing
span.quiet {{_ 'email-view-to'}}&nbsp;
| {{sender}}
//- Markdown, like every card title and description in WeKan: the viewer
honours the Admin Panel's security settings for links and code.
td.email-view-subject
.email-view-subject-title
+viewer
= title
if snippet
.email-view-snippet
+viewer
= snippet
each labels
span.card-label.email-view-row-label(class="{{labelColorClass color}}" style="{{labelColorStyle color}}" title=name)
+inlineViewer
= name
a.email-view-reply.js-email-reply(href="#" title="{{_ 'email-view-reply'}}" data-card-id="{{_id}}")
i.fa.fa-reply
td.email-view-date {{date}}
//- Compose, or reply on a card. Sent From the member's own board address, so
the answer comes back to this board (server/emailView.js sendBoardEmail).
template(name="emailComposePopup")
form.email-compose.js-email-compose-form
if replyTitle
p.quiet {{_ 'email-view-replying-to'}} {{replyTitle}}
label
| {{_ 'email-view-to'}}
input.js-email-to(type="text" value="{{to}}" placeholder="name@example.com" autocomplete="email" required)
label
| {{_ 'email-view-cc'}}
input.js-email-cc(type="text" autocomplete="email")
label
| {{_ 'email-view-subject'}}
input.js-email-subject(type="text" value="{{subject}}" required)
textarea.js-email-text(rows="10")
if error
p.warning {{error}}
button.primary(type="submit" disabled="{{#if sending}}disabled{{/if}}")
i.fa.fa-paper-plane
| &nbsp;{{_ 'email-view-send'}}
+236
View File
@@ -0,0 +1,236 @@
import { ReactiveVar } from 'meteor/reactive-var';
import { ReactiveCache } from '/imports/reactiveCache';
import { TAPi18n } from '/imports/i18n';
import Cards from '/models/cards';
import { Utils } from '/client/lib/utils';
import { reportPageDocs } from '/client/lib/reportPages';
import { reportPageKey } from '/models/lib/reportPageIndex';
import { buildHeader, docsByIds, pageInfo } from '/models/lib/tablePage';
const { EMAIL_VIEW_PAGE_ID, emailViewPageArgs, parseFolder, previewMarkdown } = require('/models/lib/emailView');
// Board View / Email (emailView.jade, server/emailView.js). The folder, the
// page and the search are this view's state; the rows are the page the server
// named (the page index), never a re-slice of the board's cards in minimongo.
const COLUMNS = [
{ labelKey: 'email-view-from' },
{ labelKey: 'email-view-subject' },
{ labelKey: 'email-view-date', nowrap: true },
];
const SYSTEM_FOLDERS = [
{ id: 'inbox', icon: 'fa-inbox', labelKey: 'email-view-inbox' },
{ id: 'sent', icon: 'fa-paper-plane', labelKey: 'email-view-sent' },
{ id: 'all', icon: 'fa-envelope-o', labelKey: 'email-view-all' },
];
function formatDate(date) {
if (!date) return '';
const d = new Date(date);
const now = new Date();
// Today: the time; this year: day and month; older: the whole date.
if (d.toDateString() === now.toDateString()) return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
if (d.getFullYear() === now.getFullYear()) return d.toLocaleDateString([], { day: 'numeric', month: 'short' });
return d.toLocaleDateString();
}
Template.emailView.onCreated(function () {
this.folder = new ReactiveVar('inbox');
this.page = new ReactiveVar(1);
this.searchTerm = new ReactiveVar('');
this.total = new ReactiveVar(0);
this.autorun(() => {
const boardId = Utils.getCurrentBoardId();
if (!boardId) return;
const options = { folder: this.folder.get(), page: this.page.get(), search: this.searchTerm.get() };
// New mail arrives as a new card on the board: the board's own
// subscription moves this count, and the page and total follow it.
Cards.find({ boardId }).count();
this.subscribe('emailViewPage', boardId, options);
Meteor.call('emailViewCount', boardId, options, (error, result) => {
if (!error && result) this.total.set(result.total);
});
});
});
function setFolder(tpl, folder) {
tpl.folder.set(folder);
tpl.page.set(1);
}
Template.emailView.helpers({
systemFolders() {
const current = Template.instance().folder.get();
return SYSTEM_FOLDERS.map(f => ({ ...f, active: f.id === current }));
},
listFolders() {
const board = Utils.getCurrentBoard();
const current = Template.instance().folder.get();
if (!board) return [];
return (ReactiveCache.getLists({ boardId: board._id, archived: false }, { sort: { sort: 1 } }) || [])
.map(l => ({ id: `list:${l._id}`, title: l.title, active: `list:${l._id}` === current }));
},
labelFolders() {
const board = Utils.getCurrentBoard();
const current = Template.instance().folder.get();
return ((board && board.labels) || [])
.map(l => ({ id: `label:${l._id}`, name: l.name || l.color, color: l.color, active: `label:${l._id}` === current }));
},
// Email to card off for this board: the Inbox only ever has what is put in
// the list by hand, and compose would be refused - say where to turn it on.
receiving() {
const board = Utils.getCurrentBoard();
return !!(board && board.emailToCard && board.emailToCard.enabled);
},
tablePageData() {
const tpl = Template.instance();
const info = pageInfo(tpl.total.get(), tpl.page.get());
const board = Utils.getCurrentBoard();
const labels = (board && board.labels) || [];
// The index of THIS request only: a previous search's index can still be
// in minimongo while its subscription stops.
const key = reportPageKey(EMAIL_VIEW_PAGE_ID, emailViewPageArgs(Utils.getCurrentBoardId(),
{ folder: tpl.folder.get(), page: tpl.page.get(), search: tpl.searchTerm.get() }));
const cards = reportPageDocs(Cards, key, docsByIds);
const docs = cards.map(card => {
const email = card.email || {};
const outgoing = email.direction === 'out';
const author = !email.from && card.userId ? ReactiveCache.getUser(card.userId) : null;
const sender = outgoing
? (email.to || []).join(', ')
: (email.from && (email.from.name || email.from.address))
|| (author && (author.profile && author.profile.fullname || author.username)) || '';
return {
_id: card._id,
title: card.title,
snippet: previewMarkdown(card.description),
outgoing,
sender,
date: formatDate(card.receivedAt || card.createdAt),
labels: (card.labelIds || []).map(id => labels.find(l => l._id === id)).filter(Boolean),
};
});
return {
header: buildHeader(COLUMNS),
rowTemplate: 'emailViewRow',
docs,
rowCount: docs.length,
total: tpl.total.get(),
searchTerm: tpl.searchTerm.get(),
page: info.page,
totalPages: info.totalPages,
hasPrev: info.hasPrev,
hasNext: info.hasNext,
emptyKey: 'email-view-empty',
};
},
});
Template.emailView.events({
'click .js-email-folder'(event, tpl) {
event.preventDefault();
setFolder(tpl, parseFolder(event.currentTarget.dataset.folder).kind === 'inbox'
? 'inbox' : event.currentTarget.dataset.folder);
},
'keydown .js-table-page-search'(event, tpl) {
if (event.key !== 'Enter') return;
event.preventDefault();
tpl.searchTerm.set(event.currentTarget.value || '');
tpl.page.set(1);
},
'click .js-table-page-prev'(event, tpl) {
event.preventDefault();
tpl.page.set(Math.max(1, tpl.page.get() - 1));
},
'click .js-table-page-next'(event, tpl) {
event.preventDefault();
const info = pageInfo(tpl.total.get(), tpl.page.get());
if (info.hasNext) tpl.page.set(tpl.page.get() + 1);
},
// A message opens as the card popup - its comments are the conversation.
'click .js-email-open'(event) {
if ($(event.target).closest('.js-email-reply').length) return;
const cardId = event.currentTarget.dataset.cardId;
const boardId = Utils.getCurrentBoardId();
if (!cardId) return;
event.preventDefault();
Meteor.subscribe('popupCardData', cardId, {
onReady() {
Session.set('popupCardId', cardId);
Session.set('popupCardBoardId', boardId);
if (!Popup.isOpen()) Popup.open('cardDetails')(event);
},
});
},
'click .js-email-compose'(event) {
Popup.open('emailCompose').call({}, event);
},
'click .js-email-reply'(event) {
event.preventDefault();
event.stopPropagation();
const card = ReactiveCache.getCard(event.currentTarget.dataset.cardId);
if (!card) return;
Popup.open('emailCompose').call({ currentData: () => ({ cardId: card._id }) }, event);
},
});
// --- Compose ---------------------------------------------------------
function replyCard() {
const data = Template.currentData() || {};
return data.cardId ? ReactiveCache.getCard(data.cardId) : null;
}
Template.emailComposePopup.onCreated(function () {
this.error = new ReactiveVar('');
this.sending = new ReactiveVar(false);
this.cardId = (Template.currentData() || {}).cardId || null;
});
Template.emailComposePopup.helpers({
error() { return Template.instance().error.get(); },
sending() { return Template.instance().sending.get(); },
replyTitle() {
const card = replyCard();
return card ? card.title : '';
},
// A reply goes back to whoever wrote, under "Re: ".
to() {
const card = replyCard();
const email = card && card.email;
if (!email) return '';
return email.direction === 'out' ? (email.to || []).join(', ') : ((email.from && email.from.address) || '');
},
subject() {
const card = replyCard();
if (!card) return '';
const subject = (card.email && card.email.subject) || card.title || '';
return /^re:/i.test(subject) ? subject : `Re: ${subject}`;
},
});
Template.emailComposePopup.events({
'submit .js-email-compose-form'(event, tpl) {
event.preventDefault();
const split = value => String(value || '').split(/[,;]/).map(s => s.trim()).filter(Boolean);
const input = {
to: split(tpl.$('.js-email-to').val()),
cc: split(tpl.$('.js-email-cc').val()),
subject: String(tpl.$('.js-email-subject').val() || ''),
text: String(tpl.$('.js-email-text').val() || ''),
};
if (tpl.cardId) input.cardId = tpl.cardId;
tpl.sending.set(true);
tpl.error.set('');
Meteor.call('sendBoardEmail', Utils.getCurrentBoardId(), input, error => {
tpl.sending.set(false);
if (error) {
tpl.error.set(TAPi18n.__(String(error.error || error.reason || 'email-view-send-failed')));
return;
}
Popup.back();
});
},
});
+57 -4
View File
@@ -17,6 +17,7 @@ import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
import { Meteor } from 'meteor/meteor';
import { Session } from 'meteor/session';
import { ReactiveCache } from '/imports/reactiveCache';
import { Utils } from '/client/lib/utils';
import { TAPi18n } from '/imports/i18n';
import { Accounts } from 'meteor/accounts-base';
import { BOARD_EXPORT_FIELDS } from '/models/lib/exportFields';
@@ -234,17 +235,43 @@ const EXPORT_FORMAT_GROUPS = [
path: 'exportZip',
ext: 'zip',
},
// Straight into Trello through its API (server/trelloApiExport.js), not
// a file: an action that opens its own popup.
{ key: 'trello-api', icon: 'fa-trello', label: 'Trello', action: 'js-export-to-trello', scopes: BOARD_ONLY },
{ key: 'kanboard', icon: 'fa-upload', label: 'Kanboard', path: 'export/kanboard', ext: 'json', scopes: BOARD_ONLY },
{ key: 'markdown', icon: 'fa-file-text-o', label: 'Markdown', path: 'export/markdown', ext: 'md', scopes: BOARD_ONLY },
{ key: 'leo', icon: 'fa-sitemap', label: 'Leo', path: 'export/leo', ext: 'leo', scopes: BOARD_ONLY },
{ key: 'opml', icon: 'fa-indent', label: 'OPML', path: 'export/opml', ext: 'opml', scopes: BOARD_ONLY },
{ key: 'orgmode', icon: 'fa-list-alt', label: 'Org mode', path: 'export/orgmode', ext: 'org', scopes: BOARD_ONLY },
{ key: 'todotxt', icon: 'fa-list-ul', label: 'todo.txt', path: 'export/todotxt', ext: 'txt', scopes: BOARD_ONLY },
{ key: 'taskwarrior', icon: 'fa-terminal', label: 'Taskwarrior', path: 'export/taskwarrior', ext: 'json', scopes: BOARD_ONLY },
{ key: 'focalboard', icon: 'fa-th-large', label: 'Focalboard', path: 'export/focalboard', ext: 'jsonl', scopes: BOARD_ONLY },
{ key: 'todoist', icon: 'fa-check-square-o', label: 'Todoist', path: 'export/todoist', ext: 'csv', scopes: BOARD_ONLY },
{ key: 'planner', icon: 'fa-th-list', label: 'Microsoft Planner', path: 'export/planner', ext: 'xlsx', scopes: BOARD_ONLY },
{ key: 'meistertask', icon: 'fa-tasks', label: 'MeisterTask', path: 'export/meistertask', ext: 'csv', scopes: BOARD_ONLY },
{ key: 'obsidian', icon: 'fa-diamond', label: 'Obsidian Kanban', path: 'export/obsidian', ext: 'md', scopes: BOARD_ONLY },
{ key: 'linear', icon: 'fa-bars', label: 'Linear', path: 'export/linear', ext: 'csv', scopes: BOARD_ONLY },
{ key: 'ticktick', icon: 'fa-check-circle-o', label: 'TickTick', path: 'export/ticktick', ext: 'csv', scopes: BOARD_ONLY },
{ key: 'clickup', icon: 'fa-check-square', label: 'ClickUp', path: 'export/clickup', ext: 'csv', scopes: BOARD_ONLY },
{ key: 'nullboard', icon: 'fa-sticky-note-o', label: 'Nullboard', path: 'export/nullboard', ext: 'nbx', scopes: BOARD_ONLY },
{ key: 'kanri', icon: 'fa-columns', label: 'Kanri', path: 'export/kanri', ext: 'json', scopes: BOARD_ONLY },
{ key: 'pivotal', icon: 'fa-flag', label: 'Pivotal Tracker', path: 'export/pivotal', ext: 'csv', scopes: BOARD_ONLY },
{ key: 'redmine', icon: 'fa-bug', label: 'Redmine', path: 'export/redmine', ext: 'csv', scopes: BOARD_ONLY },
{ key: 'tasksorg', icon: 'fa-check-circle-o', label: 'Tasks.org', path: 'export/tasksorg', ext: 'json', scopes: BOARD_ONLY },
{ key: 'monday', icon: 'fa-table', label: 'monday.com', path: 'export/monday', ext: 'xlsx', scopes: BOARD_ONLY },
{ key: 'superproductivity', icon: 'fa-check-circle-o', label: 'Super Productivity', path: 'export/superproductivity', ext: 'json', scopes: BOARD_ONLY },
{ key: 'vikunja', icon: 'fa-file-archive-o', label: 'Vikunja', path: 'export/vikunja', ext: 'zip', scopes: BOARD_ONLY },
{ key: 'wrike', icon: 'fa-table', label: 'Wrike', path: 'export/wrike', ext: 'xlsx', scopes: BOARD_ONLY },
{ key: 'wrikeworkflow', icon: 'fa-sitemap', label: 'Wrike workflow', path: 'export/wrikeworkflow', ext: 'json', scopes: BOARD_ONLY },
{ key: 'teamwork', icon: 'fa-table', label: 'Teamwork.com', path: 'export/teamwork', ext: 'xlsx', scopes: BOARD_ONLY },
{ key: 'businessmap', icon: 'fa-table', label: 'Businessmap (Kanbanize)', path: 'export/businessmap', ext: 'xlsx', scopes: BOARD_ONLY },
{ key: 'quire', icon: 'fa-sitemap', label: 'Quire', path: 'export/quire', ext: 'csv', scopes: BOARD_ONLY },
{ key: 'notion', icon: 'fa-table', label: 'Notion', path: 'export/notion', ext: 'csv', scopes: BOARD_ONLY },
...[
['trello', 'Trello'], ['jira', 'Jira'], ['deck', 'NextCloud Deck'],
['openproject', 'OpenProject'], ['github', 'GitHub'], ['gitlab', 'GitLab'],
['gitea', 'Gitea'], ['forgejo', 'Forgejo'], ['asana', 'Asana'],
['zenkit', 'Zenkit'],
['zenkit', 'Zenkit'], ['taiga', 'Taiga'],
].map(([format, label]) => ({
key: format,
icon: 'fa-upload',
@@ -297,10 +324,12 @@ function resolvedFormatGroups() {
// 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.
//
// It asks the write capability, as the server does when the import runs. It used
// to test `!user.isWorker && ...` - the helper functions themselves, which are
// always truthy - so the Import row was offered to nobody.
function canImportIntoBoard() {
const user = ReactiveCache.getCurrentUser();
return Boolean(user && !user.isWorker && !user.isCommentOnly
&& !user.isReadOnly && !user.isReadAssignedOnly);
return Boolean(ReactiveCache.getCurrentUser() && Utils.currentUserCan('write'));
}
Template.registerHelper('canImportIntoBoard', canImportIntoBoard);
@@ -438,11 +467,35 @@ Template.exportScopeBody.events({
},
});
// "Export all boards" (server/routes/exportAllBoards.js): the formats of this
// menu that can be written for many boards at once - WeKan JSON, CSV/TSV,
// Excel (one workbook, a sheet per board) and every tool format - with the
// menu's own labels. PDF, HTML, iCalendar and the dependency graph are
// documents of one board and are not among them.
function massExportFormats() {
const out = [{ key: 'wekan', label: 'JSON' }];
EXPORT_FORMAT_GROUPS.forEach(group => group.entries.forEach(entry => {
if (entry.key === 'excel') out.push({ key: 'excel', label: 'Excel' });
else if (['csv', 'scsv', 'tsv'].includes(entry.key)) out.push({ key: entry.key, label: entry.key === 'tsv' ? 'TSV' : `CSV ${entry.label}` });
else if (/^export\/[a-z]+$/.test(entry.path || '') && entry.path !== 'export/csv') out.push({ key: entry.path.slice(7), label: entry.label });
}));
return out;
}
// The download of "Export all boards" in `format`; with `boardIds`, only those.
function massExportUrl(format, boardIds) {
const params = new URLSearchParams({ authToken: Accounts._storedLoginToken() || '' });
if (Array.isArray(boardIds) && boardIds.length) params.set('boardIds', boardIds.join(','));
return `/api/export-all-boards/${format}?${params.toString()}`;
}
// `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,
massExportFormats,
massExportUrl,
exportUrl as exportUrlFor,
};
+1 -1
View File
@@ -42,4 +42,4 @@ template(name="mapImageUpload")
input.js-map-upload-input.hide(type="file" accept="image/png,image/jpeg,image/gif,image/webp")
button.primary.js-map-upload-button(type="button" disabled=uploading) {{_ 'map-view-upload'}}
if error
p.warning {{error}}
p.warning {{_ error}}
+7 -2
View File
@@ -130,12 +130,17 @@ Template.mapImageUpload.events({
});
uploader.on('end', err => {
tpl.uploading.set(false);
if (err) tpl.error.set(err.reason || err.message || 'upload-failed');
// The template translates the key; the reason is for the console.
if (err) {
if (process.env.DEBUG === 'true') console.warn('map image upload failed:', err);
tpl.error.set('upload-failed');
}
});
uploader.start();
} catch (error) {
tpl.uploading.set(false);
tpl.error.set((error && (error.reason || error.message)) || 'upload-failed');
if (process.env.DEBUG === 'true') console.warn('map image upload failed:', error);
tpl.error.set('upload-failed');
} finally {
input.value = '';
}
@@ -6,6 +6,7 @@ import { TAPi18n } from '/imports/i18n';
import { ReactiveVar } from 'meteor/reactive-var';
import { toFullCalendarFirstDay } from '/client/lib/calendarFirstDay';
import { weekNumberByFirstDay } from '/models/lib/weekStart';
import { calendarBaseOptions } from '/models/lib/calendarBaseOptions';
const { notHelperBoardTitle } = require('/models/lib/helperBoards');
// #2469: "Multi Board Calendar" - reuses #4223 Bigboard's exact
@@ -51,22 +52,21 @@ Template.multiboardCalendarView.helpers({
return !Template.instance().subsReady.get();
},
multiboardCalendarOptions() {
const t = (key, fallback) => {
const translated = TAPi18n.__(key);
return translated && translated !== key ? translated : fallback;
};
const currentUser = ReactiveCache.getCurrentUser();
const firstDay = toFullCalendarFirstDay(
currentUser ? currentUser.getStartDayOfWeek() : 1,
);
const displayOptions = calendarDateDisplayOptions('multiboard-calendar-view');
return {
id: 'multiboard-calendar-view',
...displayOptions,
initialView: displayOptions.initialView || 'dayGridMonth',
firstDay,
...calendarBaseOptions({
id: 'multiboard-calendar-view',
displayOptions: calendarDateDisplayOptions('multiboard-calendar-view'),
firstDay,
weekNumberOf: weekNumberByFirstDay,
translate: key => TAPi18n.__(key),
locale: TAPi18n.getLanguage(),
rtl: TAPi18n.isRTL(),
}),
// #2469 scope: cross-board drag-to-reschedule is out of scope for
// this pass (a card's board isn't necessarily the one currently
// open, and moving it needs more care than a single setDue call) -
@@ -74,39 +74,7 @@ Template.multiboardCalendarView.helpers({
// behaviour unchanged, and is unaffected by this view.
editable: false,
selectable: false,
weekNumbers: true,
weekNumberCalculation: date => weekNumberByFirstDay(date, firstDay),
eventTimeFormat: {
hour: 'numeric',
minute: '2-digit',
meridiem: 'short',
},
slotLabelFormat: {
hour: 'numeric',
minute: '2-digit',
meridiem: 'short',
},
headerToolbar: {
left: 'title',
center: '',
right:
'today prev,next timeGridDay,listDay timeGridWeek,listWeek dayGridMonth,listMonth',
},
buttonIcons: false,
buttonText: {
prev: t('previous', 'Previous'),
next: t('next', 'Next'),
today: t('today', 'Today'),
day: t('day', 'Day'),
week: t('week', 'Week'),
month: t('month', 'Month'),
list: t('list', 'List'),
},
height: 'auto',
navLinks: false,
nowIndicator: true,
locale: TAPi18n.getLanguage(),
direction: TAPi18n.isRTL() ? 'rtl' : 'ltr',
events(fetchInfo, callback) {
const filterSelector = Filter.isActive()
? Filter._getMongoSelector()
@@ -1,60 +0,0 @@
template(name="originalPositionsView")
.original-positions-view
.original-positions-header
button.btn.btn-sm.btn-outline-secondary.js-toggle-original-positions
i.fa.fa-history
if isShowingOriginalPositions
| Hide Original Positions
else
| Show Original Positions
if isShowingOriginalPositions
button.btn.btn-sm.btn-outline-primary.js-refresh-history
i.fa.fa-refresh
| Refresh
if isShowingOriginalPositions
.original-positions-content
if isLoading
.original-positions-loading
i.fa.fa-spinner.fa-spin
| Loading original positions...
else
.original-positions-filters
.btn-group.btn-group-sm(role="group")
button.btn.js-filter-type(type="button" class="{{#if isFilterType 'all'}}btn-primary{{else}}btn-outline-secondary{{/if}}" data-filter-type="all")
| All
button.btn.js-filter-type(type="button" class="{{#if isFilterType 'swimlane'}}btn-primary{{else}}btn-outline-secondary{{/if}}" data-filter-type="swimlane")
i.fa.fa-bars
| Swimlanes
button.btn.js-filter-type(type="button" class="{{#if isFilterType 'list'}}btn-primary{{else}}btn-outline-secondary{{/if}}" data-filter-type="list")
i.fa.fa-columns
| Lists
button.btn.js-filter-type(type="button" class="{{#if isFilterType 'card'}}btn-primary{{else}}btn-outline-secondary{{/if}}" data-filter-type="card")
i.fa.fa-sticky-note
| Cards
.original-positions-list
each getFilteredHistory
.original-position-item
.original-position-item-header
i.fa(class="{{getEntityTypeIcon entityType}}")
span.entity-type {{getEntityTypeLabel entityType}}
span.entity-name
+viewer
| {{getEntityDisplayName this}}
span.entity-id ({{entityId}})
.original-position-item-details
.original-position-description
| {{getEntityOriginalPositionDescription this}}
if originalTitle
.original-title
strong Original title:
+viewer
= originalTitle
.original-position-date
small.text-muted Created: {{formatDate createdAt}}
else
.no-original-positions
i.fa.fa-info-circle
| No original position data available for this board.
@@ -1,135 +0,0 @@
import { formatDateForDisplay } from '/client/lib/dateDisplay';
import { ReactiveVar } from 'meteor/reactive-var';
import { Meteor } from 'meteor/meteor';
/**
* Component to display original positions for all entities on a board
*/
Template.originalPositionsView.onCreated(function () {
this.showOriginalPositions = new ReactiveVar(false);
this.boardHistory = new ReactiveVar([]);
this.isLoading = new ReactiveVar(false);
this.filterType = new ReactiveVar('all'); // 'all', 'swimlane', 'list', 'card'
const tpl = this;
this.loadBoardHistory = function () {
const boardId = Session.get('currentBoard');
if (!boardId) return;
tpl.isLoading.set(true);
Meteor.call('positionHistory.getBoardHistory', boardId, (error, result) => {
tpl.isLoading.set(false);
if (error) {
console.error('Error loading board history:', error);
tpl.boardHistory.set([]);
} else {
tpl.boardHistory.set(result);
}
});
};
});
Template.originalPositionsView.onRendered(function () {
this.loadBoardHistory();
});
Template.originalPositionsView.helpers({
isShowingOriginalPositions() {
return Template.instance().showOriginalPositions.get();
},
isLoading() {
return Template.instance().isLoading.get();
},
getBoardHistory() {
return Template.instance().boardHistory.get();
},
getFilteredHistory() {
const tpl = Template.instance();
const history = tpl.boardHistory.get();
const filterType = tpl.filterType.get();
if (filterType === 'all') {
return history;
}
return history.filter(item => item.entityType === filterType);
},
isFilterType(type) {
return Template.instance().filterType.get() === type;
},
getEntityDisplayName(entity) {
const position = entity.originalPosition || {};
return position.title || `Entity ${entity.entityId}`;
},
getEntityOriginalPositionDescription(entity) {
const position = entity.originalPosition || {};
let description = `Position: ${position.sort || 0}`;
if (entity.entityType === 'list' && entity.originalSwimlaneId) {
description += ` in swimlane ${entity.originalSwimlaneId}`;
} else if (entity.entityType === 'card') {
if (entity.originalSwimlaneId) {
description += ` in swimlane ${entity.originalSwimlaneId}`;
}
if (entity.originalListId) {
description += ` in list ${entity.originalListId}`;
}
}
return description;
},
getEntityTypeIcon(entityType) {
switch (entityType) {
case 'swimlane':
return 'fa-bars';
case 'list':
return 'fa-columns';
case 'card':
return 'fa-sticky-note';
default:
return 'fa-question';
}
},
getEntityTypeLabel(entityType) {
switch (entityType) {
case 'swimlane':
return 'Swimlane';
case 'list':
return 'List';
case 'card':
return 'Card';
default:
return 'Unknown';
}
},
formatDate(date) {
return formatDateForDisplay(date, true, value => value.toLocaleString());
},
});
Template.originalPositionsView.events({
'click .js-toggle-original-positions'(evt, tpl) {
tpl.showOriginalPositions.set(!tpl.showOriginalPositions.get());
},
'click .js-refresh-history'(evt, tpl) {
tpl.loadBoardHistory();
},
'click .js-filter-type'(evt, tpl) {
const type = evt.currentTarget.dataset.filterType;
tpl.filterType.set(type);
},
});
+3 -3
View File
@@ -1,5 +1,5 @@
/* /public — the Public Boards table.
* Design: docs/Features/Page/Public.md, which is docs/Features/Page/Table.md.
* Design: docs/Features/All-Boards/Public.md, which is docs/Features/General/Table-Page.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
@@ -23,9 +23,9 @@
* so the row keeps the table's white background: with the light text below that
* is white on white, and only the row's emoji was visible. The All Boards tile
* has the same problem and solves it the same way — `.board-list-item` falls
* back to a grey (#999) so its white text always has something to sit on. */
* back to a grey (#767676) so its white text always has something to sit on. */
.public-board-row {
background-color: #999;
background-color: #767676;
background-size: cover;
background-position: center;
cursor: pointer;
+2 -2
View File
@@ -1,6 +1,6 @@
//- /public — the boards anybody may open.
//- Design: docs/Features/Page/Public.md, which is the Table page design
//- (docs/Features/Page/Table.md).
//- Design: docs/Features/All-Boards/Public.md, which is the Table page design
//- (docs/Features/General/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
+7 -3
View File
@@ -2,6 +2,7 @@ import { ReactiveVar } from 'meteor/reactive-var';
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
import Boards from '/models/boards';
import { ReportPages } from '/client/lib/reportPages';
import { reportPageKey } from '/models/lib/reportPageIndex';
import {
buildHeader,
docsByIds,
@@ -10,7 +11,7 @@ import {
} from '/models/lib/tablePage';
// /public — the boards anybody may open, as a table page.
// Design: docs/Features/Page/Public.md (which is docs/Features/Page/Table.md).
// Design: docs/Features/All-Boards/Public.md (which is docs/Features/General/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
@@ -62,7 +63,10 @@ Template.publicBoards.helpers({
// a plain find(): every board this user has opened is also in minimongo, and
// reading the collection would draw those too — the pager would say "1 / 3"
// beside rows from all three pages.
const index = ReportPages.findOne(PAGE_ID);
// The index of THIS request (models/lib/reportPageIndex.js reportPageKey),
// built from the same arguments the autorun subscribed with.
const index = ReportPages.findOne(reportPageKey(PAGE_ID,
[tpl.searchTerm.get() || '', TABLE_PAGE_ROWS_PER_PAGE, (tpl.page.get() - 1) * TABLE_PAGE_ROWS_PER_PAGE]));
const ids = (index && index.ids) || [];
const boards = ids.length
? docsByIds(ids, Boards.find({ _id: { $in: ids } }).fetch())
@@ -88,7 +92,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/Features/Page/Table.md).
// Panel panes supply none (docs/Features/General/Table.md).
header: buildHeader(COLUMNS),
// A row template, not `rows`: see COLUMNS above.
rowTemplate: 'publicBoardRow',
@@ -26,9 +26,11 @@ template(name="scrumMetadata")
each editors
label {{label}}
if options
select(name=field)
select(name=field multiple=multiple)
each options
option(value=value selected=selected) {{label}}
if multiple
small.scrum-multiple-help {{_ 'scrum-releases-select-help'}}
else
if number
input(type="number" name=field value=value min="0" step="any")
+40 -12
View File
@@ -5,12 +5,15 @@ import { ReactiveVar } from 'meteor/reactive-var';
import { Session } from 'meteor/session';
import { Tracker } from 'meteor/tracker';
import { ReactiveCache } from '/imports/reactiveCache';
const { cardReleaseIds, unknownScrumNameIds } = require('/models/lib/scrum');
// A card's Release is a list (`releaseIds`, models/lib/scrum.js
// cardReleaseIds - its legacy `releaseId` read too); a swimlane keeps one.
const definitions = {
card: [['Sprint', 'sprintId', 'scrum-sprint'], ['PastSprints', 'pastSprintIds', 'scrum-past-sprints'], ['Release', 'releaseId', 'scrum-release'], ['IssueType', 'issueType', 'scrum-issue-type'], ['AcceptanceCriteria', 'acceptanceCriteria', 'scrum-acceptance-criteria'], ['BacklogRank', 'backlogRank', 'scrum-backlog-rank']],
card: [['Sprint', 'sprintId', 'scrum-sprint'], ['PastSprints', 'pastSprintIds', 'scrum-past-sprints'], ['Release', 'releaseIds', 'scrum-release'], ['IssueType', 'issueType', 'scrum-issue-type'], ['AcceptanceCriteria', 'acceptanceCriteria', 'scrum-acceptance-criteria'], ['BacklogRank', 'backlogRank', 'scrum-backlog-rank']],
// The issue type is not a Scrum minicard field any more: the minicard shows
// it as a badge with an icon (models/lib/issueTypeIcon.js), turned on and off
// by its own Board Settings / Card row.
minicard: [['Sprint', 'sprintId', 'scrum-sprint'], ['PastSprints', 'pastSprintIds', 'scrum-past-sprints'], ['Release', 'releaseId', 'scrum-release'], ['AcceptanceCriteria', 'acceptanceCriteria', 'scrum-acceptance-criteria'], ['BacklogRank', 'backlogRank', 'scrum-backlog-rank']],
minicard: [['Sprint', 'sprintId', 'scrum-sprint'], ['PastSprints', 'pastSprintIds', 'scrum-past-sprints'], ['Release', 'releaseIds', 'scrum-release'], ['AcceptanceCriteria', 'acceptanceCriteria', 'scrum-acceptance-criteria'], ['BacklogRank', 'backlogRank', 'scrum-backlog-rank']],
list: [['Category', 'category', 'scrum-list-category']],
swimlane: [['Sprint', 'sprintId', 'scrum-sprint'], ['Release', 'releaseId', 'scrum-release'], ['Purpose', 'purpose', 'scrum-swimlane-purpose']],
};
@@ -24,10 +27,16 @@ Meteor.startup(() => Tracker.autorun(() => {
Meteor.userId(); Session.get('currentBoard');
invalidateScrumNames();
}));
function loadNames(board) {
// `missing`: sprint or release IDs a shown record names that the cached names
// do not have (models/lib/scrum.js unknownScrumNameIds). Saving a sprint or a
// release - from the Scrum view, another browser, another user or the API -
// does not change the board's scrumRevision, so the key alone would keep a
// stale list; an unknown ID asks again, at most once per 30 s per set.
function loadNames(board, missing = []) {
const key = JSON.stringify([board._id, Meteor.userId(), board.members, board.scrumRevision]);
if (Date.now() - (requestTimes.get(key) || 0) < 30000) return;
requestTimes.set(key, Date.now());
const throttleKey = missing.length ? `${key}|${missing.join(',')}` : key;
if (Date.now() - (requestTimes.get(throttleKey) || 0) < 30000) return;
requestTimes.set(throttleKey, Date.now());
const requestedGeneration = generation;
Meteor.call('scrum.getBoardData', board._id, (error, result) => {
if (requestedGeneration !== generation || error) return;
@@ -43,8 +52,14 @@ const boardFor = context => ReactiveCache.getBoard(context?.record?.boardId || S
// `field` (a definition suffix, 'Sprint') draws one field alone: on a card and
// a minicard each Scrum field sits where the board's Board Settings / Card
// order puts it (models/lib/cardFieldOrder.js SCRUM_FIELD_KEYS).
// Read through the board as that side sees it (models/lib/cardShownFields.js
// boardForSide): a Scrum section hidden whole on Show on Card or Show on
// Minicard in Card Settings hides these fields there too. The settings above
// (scrumFieldSettings) read the board itself.
function visibleFields(context) {
const board = boardFor(context);
const { boardForSide } = require('/models/lib/cardShownFields');
const side = context?.kind === 'card' || context?.kind === 'minicard' ? context.kind : null;
const board = side ? boardForSide(boardFor(context), side) : boardFor(context);
return (definitions[context?.kind] || []).filter(([suffix]) => (!context.field || context.field === suffix) &&
board?.scrum?.visibility?.[`${context.kind}${suffix}`] === true);
}
@@ -73,7 +88,10 @@ Template.scrumMetadata.onCreated(function () {
this.autorun(() => {
names.get();
const context = Template.currentData(); const board = boardFor(context);
if (board && visibleFields(context).length) loadNames(board);
const shown = visibleFields(context);
if (!board || !shown.length) return;
const state = safeNames(context);
loadNames(board, state ? unknownScrumNameIds(context.record?.scrum, shown.map(([, field]) => field), state) : []);
});
});
function safeNames(context) {
@@ -84,9 +102,15 @@ function safeNames(context) {
function fieldRows(context, editing = false) {
const state = safeNames(context); const metadata = context.record?.scrum || {};
return visibleFields(context).filter(([, field]) => !editing || field !== 'pastSprintIds').map(([, field, label]) => {
const raw = metadata[field]; let value = raw ?? '';
let options;
if (['sprintId', 'releaseId', 'pastSprintIds'].includes(field)) {
const raw = field === 'releaseIds' ? cardReleaseIds(metadata) : metadata[field]; let value = raw ?? '';
let options; let multiple = false;
if (field === 'releaseIds') {
// Several releases: a multiple select, every release of the board.
const records = state?.releases;
value = raw.map(id => records?.find(row => row._id === id)?.name || '').filter(Boolean).join(', ');
options = (records || []).map(row => ({ value: row._id, label: row.name, selected: raw.includes(row._id) }));
multiple = true;
} else if (['sprintId', 'releaseId', 'pastSprintIds'].includes(field)) {
const records = field === 'releaseId' ? state?.releases : state?.sprints;
value = field === 'pastSprintIds' ? (raw || []).map(id => records?.find(row => row._id === id)?.name || '').filter(Boolean).join(', ') : records?.find(row => row._id === raw)?.name || '';
options = [{ value: '', label: '—', selected: !raw }, ...(records || []).filter(row => field === 'releaseId' || ['planned', 'active'].includes(row.state) || row._id === raw).map(row => ({ value: row._id, label: row.name, selected: row._id === raw }))];
@@ -94,7 +118,7 @@ function fieldRows(context, editing = false) {
value = raw ? t(`scrum-category-${raw}`) : '';
options = ['backlog', 'todo', 'doing', 'done'].map(category => ({ value: category, label: t(`scrum-category-${category}`), selected: raw === category }));
}
return { field, label: t(label), value, options, number: field === 'backlogRank' };
return { field, label: t(label), value, options, multiple, number: field === 'backlogRank' };
});
}
Template.scrumMetadata.helpers({
@@ -112,8 +136,12 @@ Template.scrumMetadata.events({
async 'submit .js-scrum-metadata'(event, tpl) {
event.preventDefault(); event.stopPropagation();
const context = Template.currentData(); const record = context.record;
const metadata = Object.fromEntries(new FormData(event.currentTarget));
const form = new FormData(event.currentTarget);
const metadata = Object.fromEntries(form);
for (const key of ['sprintId', 'releaseId']) if (key in metadata) metadata[key] = metadata[key] || null;
// A multiple select sends one entry per chosen release, none when it is
// cleared: read them all, by the select being there rather than by a value.
if (event.currentTarget.querySelector('select[name="releaseIds"]')) metadata.releaseIds = form.getAll('releaseIds').filter(Boolean);
if ('backlogRank' in metadata) metadata.backlogRank = metadata.backlogRank === '' ? null : Number(metadata.backlogRank);
const method = { card: 'Card', list: 'List', swimlane: 'Swimlane' }[context.kind];
if (!method || tpl.busy.get()) return;
@@ -58,3 +58,8 @@
background: #3cb500;
transition: width 0.3s ease;
}
/* A card's releases: a multiple select (several releases per card). Some
global form rules give selects one row's height; show a few rows. */
.scrum-view select[multiple], .scrum-metadata select[multiple] { min-height: 5.5em; height: auto; }
.scrum-multiple-help { display: block; opacity: .8; max-width: 32em; }
+30 -7
View File
@@ -22,7 +22,7 @@ template(name="scrumView")
details.scrum-import-report
summary {{_ 'import'}}
each data.importLosses
p {{_ 'scrum-import-reference-omitted' reference=sourceId}}
p {{lossText}}
if data.partial
p(role="status" class="scrum-partial-report") {{_ 'scrum-partial-report'}}
if historyJob
@@ -59,7 +59,7 @@ template(name="scrumView")
select(name="estimateSource")
each estimateSources
option(value=value selected=selected) {{label}}
label {{_ 'custom-field'}}
label {{_ 'custom-fields'}}
select(name="estimateCustomFieldId")
option(value="") —
each estimateFields
@@ -71,6 +71,27 @@ template(name="scrumView")
each completionPolicies
option(value=value selected=selected) {{label}}
button.primary(type="submit" disabled=busy) {{_ 'save'}}
details.scrum-transfer-import
summary {{_ 'scrum-import-into-board'}}
p {{_ 'scrum-import-into-board-hint'}}
input.js-scrum-transfer-file(type="file" accept=".json,application/json")
button.js-scrum-transfer-preview(type="button" disabled=busy) {{_ 'scrum-import-preview'}}
//- `this.`: scrumView has `releases` and `cards` helpers, and a
helper wins over the data context - the preview's own counts
would read as empty (tests/scrumViewHelpers.test.cjs).
with transferPreview
.scrum-transfer-preview(role="status")
if this.changed
p.js-scrum-transfer-done {{_ 'scrum-import-into-board-done'}}
p {{_ 'scrum-import-preview-sprints' created=this.sprints.created.length matched=this.sprints.matched.length skipped=this.sprints.skipped.length}}
p {{_ 'scrum-import-preview-releases' created=this.releases.created.length matched=this.releases.matched.length skipped=this.releases.skipped.length}}
p {{_ 'scrum-import-preview-cards' updated=this.cards.updated unchanged=this.cards.unchanged unmatched=this.cards.unmatched}}
if nothingToDo
p.js-scrum-transfer-nothing {{_ 'scrum-import-preview-nothing'}}
each this.losses
p.scrum-transfer-loss {{lossText}}
if canApply
button.primary.js-scrum-transfer-apply(type="button" disabled=busy) {{_ 'import'}}
if isVelocity
+scrumReportTable(rows=velocity velocity=true)
else
@@ -120,7 +141,7 @@ template(name="scrumView")
textarea(name="goal") {{selectedSprint.goal}}
label {{_ 'start'}}
input(type="date" name="plannedStart" value=sprintStart)
label {{_ 'end'}}
label {{_ 'card-end'}}
input(type="date" name="plannedEnd" value=sprintEnd)
label {{_ 'scrum-capacity'}}
input(type="number" name="capacity" min="0" step="any" value=selectedSprint.capacity)
@@ -133,6 +154,8 @@ template(name="scrumView")
each releases
article
h4 {{name}} — {{stateLabel}}
p.scrum-release-scope(data-release-id=_id) {{_ 'scrum-release-scope'}}: {{scopeLabel}}
p.scrum-release-done(data-release-id=_id) {{_ 'scrum-completed'}}: {{doneLabel}}
p {{goal}}
p {{startLabel}} — {{endLabel}}
if releasedAt
@@ -151,7 +174,7 @@ template(name="scrumView")
textarea(name="goal") {{selectedRelease.goal}}
label {{_ 'start'}}
input(type="date" name="plannedStart" value=releaseStart)
label {{_ 'end'}}
label {{_ 'card-end'}}
input(type="date" name="plannedEnd" value=releaseEnd)
label {{_ 'status'}}
select(name="state")
@@ -207,10 +230,10 @@ template(name="scrumView")
label {{_ 'scrum-backlog-rank'}}
input(type="number" name="backlogRank" min="0" step="any" value=rankValue)
label {{_ 'scrum-release'}}
select(name="releaseId")
option(value="") —
select(name="releaseIds" multiple)
each cardReleaseOptions
option(value=_id selected=selected) {{name}}
small.scrum-multiple-help {{_ 'scrum-releases-select-help'}}
label {{_ 'scrum-issue-type'}}
textarea(name="issueType" rows="1") {{scrum.issueType}}
label {{_ 'scrum-acceptance-criteria'}}
@@ -218,7 +241,7 @@ template(name="scrumView")
button.primary(type="submit" disabled=busy) {{_ 'save'}}
else
tr
td(colspan="6") {{_ 'no-cards'}}
td(colspan="6") {{_ 'no-cards-found'}}
if hiddenCards
button.js-scrum-show-more(type="button") {{showMoreLabel}}
if isSprints
+72 -6
View File
@@ -11,9 +11,15 @@ import { TAPi18n } from '/imports/i18n';
import { Utils } from '/client/lib/utils';
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
import { ReactiveCache } from '/imports/reactiveCache';
const { DEFAULT_SCRUM_SETTINGS, getCardEstimate } = require('/models/lib/scrum');
const { velocityReports, reportChartGroups } = require('/models/lib/scrumReports');
const { DEFAULT_SCRUM_SETTINGS, getCardEstimate, cardReleaseIds } = require('/models/lib/scrum');
const { velocityReports, reportChartGroups, releaseReports } = require('/models/lib/scrumReports');
const { compareScrumCards } = require('/models/lib/scrumCardOrder');
const { scrumTransferFileFields } = require('/models/lib/scrumTransferMerge');
// What an import could not place, in words: the reasons an import into this
// board adds (models/lib/scrumTransferMerge.js), else the general one.
const LOSS_KEYS = { 'card-not-matched': 'scrum-import-card-not-matched', 'card-ambiguous': 'scrum-import-card-ambiguous',
'card-on-another-board': 'scrum-import-card-on-another-board', 'record-ambiguous': 'scrum-import-record-ambiguous',
'record-not-imported': 'scrum-import-record-not-imported', 'sprint-finished': 'scrum-import-sprint-finished' };
const current = () => Template.instance();
const data = () => current().dataState.get();
const selectedSprint = tpl => tpl.dataState.get()?.sprints.find(s => s._id === tpl.sprintId.get());
@@ -86,11 +92,34 @@ async function mutate(tpl, method, ...args) {
return false;
} finally { if (!tpl.stopped) tpl.busy.set(false); }
}
const transferChanges = preview => !!(preview.sprints.created.length || preview.releases.created.length ||
preview.events.created || preview.cards.updated || preview.dailyObservations);
// A dry run first, then the import itself, of the file chosen.
async function importTransfer(tpl, dryRun) {
if (tpl.busy.get()) return;
if (!tpl.transferFile) { tpl.error.set(t('scrum-import-choose-file')); return; }
tpl.busy.set(true); tpl.error.set('');
try {
const result = await Meteor.callAsync('scrum.importIntoBoard', Session.get('currentBoard'), tpl.transferFile, { dryRun });
if (tpl.stopped) return;
tpl.transferPreview.set(result);
// A quiet refresh: a loud one swaps the whole view for "Loading", which
// drew this panel again closed, so the import's own result - "imported"
// and what it could not place - was never seen, and the chosen file was
// gone for a second Preview (tests/scrumViewHelpers.test.cjs).
if (!dryRun) { invalidateScrumNames(); await refresh(tpl, { quiet: true }); }
} catch (error) {
if (!tpl.stopped) tpl.error.set(error.reason || error.message);
} finally { if (!tpl.stopped) tpl.busy.set(false); }
}
Template.scrumView.onCreated(function () {
this.dataState = new ReactiveVar(null); this.loading = new ReactiveVar(true);
this.error = new ReactiveVar(''); this.busy = new ReactiveVar(false);
this.sprintId = new ReactiveVar(''); this.request = 0; this.stopped = false;
this.releaseId = new ReactiveVar(''); this.eventId = new ReactiveVar('');
// A Scrum transfer imported into this board: the file's fields, and what
// the server said a dry run, or the import, does.
this.transferFile = null; this.transferPreview = new ReactiveVar(null);
// Rows shown: each carries its own form, so a large board renders a page at
// a time.
this.cardLimit = new ReactiveVar(CARD_PAGE);
@@ -98,6 +127,7 @@ Template.scrumView.onCreated(function () {
Session.get('currentBoard'); Meteor.userId();
this.dataState.set(null); this.sprintId.set('');
this.releaseId.set(''); this.eventId.set('');
this.transferFile = null; this.transferPreview.set(null);
void refresh(this);
});
});
@@ -162,7 +192,17 @@ Template.scrumView.helpers({
sprintOpen: () => ['planned', 'active'].includes(selectedSprint(current())?.state),
sprintStart: () => dateValue(selectedSprint(current())?.plannedStart),
sprintEnd: () => dateValue(selectedSprint(current())?.plannedEnd),
releases: () => (data()?.releases || []).map(r => ({ ...r, stateLabel: stateLabel(r.state), startLabel: dateValue(r.plannedStart), endLabel: dateValue(r.plannedEnd), releasedLabel: r.releasedAt ? new Date(r.releasedAt).toLocaleString() : '' })),
// Each release with the cards in it: a card with several releases counts in
// each (models/lib/scrumReports.js releaseReports), only the cards this
// reader may see.
releases() {
const result = data(); if (!result) return [];
const totals = new Map(releaseReports(result.releases, result.cards, { ...DEFAULT_SCRUM_SETTINGS, ...result.settings }, result.lists)
.map(report => [report.releaseId, report]));
const label = value => t('scrum-total', { count: value.count, estimate: value.estimate, unknown: value.unknown });
return result.releases.map(r => ({ ...r, stateLabel: stateLabel(r.state), startLabel: dateValue(r.plannedStart), endLabel: dateValue(r.plannedEnd), releasedLabel: r.releasedAt ? new Date(r.releasedAt).toLocaleString() : '',
scopeLabel: label(totals.get(r._id).scope), doneLabel: label(totals.get(r._id).done) }));
},
selectedRelease: () => selectedRelease(current()),
releaseOptions: () => (data()?.releases || []).map(r => ({ ...r, selected: r._id === current().releaseId.get() })),
releaseStates: () => ['planned', 'released', 'cancelled'].map(value => ({ value, label: t(`scrum-state-${value}`), selected: value === (selectedRelease(current())?.state || 'planned') })),
@@ -192,8 +232,8 @@ Template.scrumView.helpers({
rankLabel: card.scrum?.backlogRank ?? '—',
rankValue: card.scrum?.backlogRank ?? '',
sprintName: result.sprints.find(s => s._id === card.scrum?.sprintId)?.name || t('scrum-product-backlog'),
releaseName: result.releases.find(release => release._id === card.scrum?.releaseId)?.name || '—',
cardReleaseOptions: result.releases.map(release => ({ ...release, selected: release._id === card.scrum?.releaseId })),
releaseName: cardReleaseIds(card.scrum).map(id => result.releases.find(release => release._id === id)?.name).filter(Boolean).join(', ') || '—',
cardReleaseOptions: result.releases.map(release => ({ ...release, selected: cardReleaseIds(card.scrum).includes(release._id) })),
cardUrl: FlowRouter.path('card', { boardId: board._id, slug: board.slug, cardId: card._id }),
assignmentOptions: result.sprints.filter(s => ['planned', 'active'].includes(s.state)).map(s => ({ ...s, selected: s._id === card.scrum?.sprintId })) };
});
@@ -205,6 +245,21 @@ Template.scrumView.helpers({
})),
})),
eventKinds: () => ['planning', 'daily', 'review', 'retrospective'].map(value => ({ value, label: t(`scrum-event-${value}`), selected: value === (selectedEvent(current())?.kind || 'planning') })),
// The import report and the import-into-board panel are drawn by THIS
// template (scrumView.jade), so their helpers belong here: registered on
// scrumReportTable they were never found, the preview never rendered and
// the import report showed its summary with every loss line empty.
// tests/scrumViewHelpers.test.cjs pins it.
transferPreview: () => current().transferPreview.get(),
nothingToDo() {
const preview = current().transferPreview.get();
return !!preview && preview.dryRun && !transferChanges(preview);
},
canApply() {
const preview = current().transferPreview.get();
return !!preview && preview.dryRun && transferChanges(preview);
},
lossText() { return t(LOSS_KEYS[this.reason] || 'scrum-import-reference-omitted', { reference: this.sourceId }); },
});
Template.scrumReportTable.onCreated(function () { this.metric = new ReactiveVar('count'); });
Template.scrumReportTable.events({
@@ -232,6 +287,15 @@ Template.scrumView.events({
'change .js-scrum-sprint'(event, tpl) {
tpl.sprintId.set(event.currentTarget.value); tpl.eventId.set(''); tpl.cardLimit.set(CARD_PAGE);
},
async 'change .js-scrum-transfer-file'(event, tpl) {
tpl.transferFile = null; tpl.transferPreview.set(null); tpl.error.set('');
const file = event.currentTarget.files?.[0];
if (!file) return;
try { tpl.transferFile = scrumTransferFileFields(JSON.parse(await file.text())); }
catch (error) { tpl.error.set(error instanceof SyntaxError ? t('scrum-import-invalid-file') : error.message); }
},
async 'click .js-scrum-transfer-preview'(event, tpl) { event.preventDefault(); await importTransfer(tpl, true); },
async 'click .js-scrum-transfer-apply'(event, tpl) { event.preventDefault(); await importTransfer(tpl, false); },
async 'click .js-scrum-resume-import'(event, tpl) { event.preventDefault(); await mutate(tpl, 'scrum.resumeImport'); },
async 'click .js-scrum-discard-import'(event, tpl) { event.preventDefault(); await mutate(tpl, 'scrum.discardImport'); },
'click .js-scrum-show-more'(event, tpl) { event.preventDefault(); tpl.cardLimit.set(tpl.cardLimit.get() + CARD_PAGE); },
@@ -274,7 +338,9 @@ Template.scrumView.events({
async 'submit .js-scrum-card'(event, tpl) {
event.preventDefault(); const values = fields(event.currentTarget);
const card = tpl.dataState.get()?.cards.find(c => c._id === event.currentTarget.dataset.cardId);
if (card) await mutate(tpl, 'scrum.updateCard', card._id, { sprintId: nullable(values.sprintId), releaseId: nullable(values.releaseId), backlogRank: values.backlogRank === '' ? null : Number(values.backlogRank), issueType: values.issueType, acceptanceCriteria: values.acceptanceCriteria }, card.scrumRevision || 0);
// Every chosen release (none clears them), as the list.
const releaseIds = new FormData(event.currentTarget).getAll('releaseIds').filter(Boolean);
if (card) await mutate(tpl, 'scrum.updateCard', card._id, { sprintId: nullable(values.sprintId), releaseIds, backlogRank: values.backlogRank === '' ? null : Number(values.backlogRank), issueType: values.issueType, acceptanceCriteria: values.acceptanceCriteria }, card.scrumRevision || 0);
},
async 'submit .js-scrum-release'(event, tpl) {
event.preventDefault(); const form = event.currentTarget; const values = fields(form);
+17
View File
@@ -83,11 +83,28 @@
color: #333;
}
/* The table, not the board, scrolls sideways when its columns are wider than
the window. At 800px and below the table scrolls itself (display: block);
just above that - an iPad or an unfolded phone held upright, 834-928px - it
did not, the board canvas around it is overflow-x: hidden, and Members, Labels
and the date columns were cut off with no way to reach them.
tests/playwright/specs/viewport-matrix.e2e.js checks every column is reachable. */
.table-view-wrapper {
max-width: 100%;
overflow-x: auto;
}
.table-view-controls .js-table-view-toggle-card-title-wrap,
.table-view-controls .js-table-view-toggle-swimlane-groups {
box-sizing: border-box;
width: 34px;
height: 34px;
/* The global `button` rule (forms.css) pads every button 35px on each side
and paints its text white. On these white toggles that made the icon
white on white - two empty squares - and the padding stretched the 34px
box to 72px. tests/playwright/specs/viewport-matrix.e2e.js checks both. */
padding: 0;
color: #333;
line-height: 1;
border: 1px solid #bbb;
background: #fff;
+15 -3
View File
@@ -1,6 +1,6 @@
template(name="tableView")
if currentBoard
.wrapper
.wrapper.table-view-wrapper
.table-view-controls
.table-view-search-box
input.js-table-view-search(type="text" placeholder="{{_ 'search'}}" aria-label="{{_ 'search'}}")
@@ -57,11 +57,12 @@ template(name="tableView")
each row in rows
if row.isGroupHeader
tr.table-view-swimlane-group
th(colspan="11" scope="rowgroup")
th(colspan=columnCount scope="rowgroup")
+viewer
= row.swimlaneTitle
else
tr
//- #864: a row is dragged onto another to move its card there.
tr.js-table-view-row(draggable="{{#if canWrite}}true{{/if}}" data-card-id=row.card._id)
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
@@ -131,3 +132,14 @@ template(name="tableView")
if canModifyCard
a.card-label.add-label.js-end-date(title="{{_ 'card-end'}}")
i.fa.fa-plus
//- #864: add a card from the table: its title, list and swimlane.
if canWrite
form.table-view-add-card.js-table-view-add-card
input.js-table-view-add-card-title(type="text" placeholder="{{_ 'add-card'}}" aria-label="{{_ 'add-card'}}" required)
select.js-table-view-add-card-list(aria-label="{{_ 'list'}}")
each boardLists
option(value=_id) {{title}}
select.js-table-view-add-card-swimlane(aria-label="{{_ 'swimlane'}}")
each boardSwimlanes
option(value=_id) {{title}}
button.primary.js-table-view-add-card-button(type="submit") {{_ 'add-card'}}
+77
View File
@@ -3,6 +3,7 @@ import { Random } from 'meteor/random';
import { isLazyCards } from '/client/lib/lazyCards';
import { ReactiveCache } from '/imports/reactiveCache';
import { Utils } from '/client/lib/utils';
import { TAPi18n } from '/imports/i18n';
import { Filter } from '/client/lib/filter';
import { tableViewCardsSelector, tableCardWithDates } from '/models/lib/tableViewFilter';
import {
@@ -163,6 +164,20 @@ Template.tableView.helpers({
return Utils.getCurrentBoard();
},
// #864: dragging rows and adding cards need the right to write.
canWrite() {
const board = Utils.getCurrentBoard();
return !!board && Utils.currentUserCan('write', board);
},
boardLists() {
const board = Utils.getCurrentBoard();
return board ? ReactiveCache.getLists({ boardId: board._id, archived: false }, { sort: { sort: 1 } }) : [];
},
boardSwimlanes() {
const board = Utils.getCurrentBoard();
return board ? ReactiveCache.getSwimlanes({ boardId: board._id, archived: false }, { sort: { sort: 1 } }) : [];
},
rows() {
const tpl = Template.instance();
const all = tpl.filteredRows.get();
@@ -225,9 +240,71 @@ Template.tableView.helpers({
const board = Utils.getCurrentBoard();
return !!board && (board.allowsEndDate || board.allowsEndDateOnMinicard);
},
// A swimlane header row spans the whole table: Edit, Card, List and Swimlane,
// then - except on a small screen - Assignees, Members, Labels and each date
// column the board shows. A fixed 11 left it one short with all four dates
// and too wide on a phone.
columnCount() {
if (Utils.isMiniScreen()) return 4;
const board = Utils.getCurrentBoard();
const dates = !board ? 0 : ['Received', 'Start', 'Due', 'End']
.filter(kind => board[`allows${kind}Date`] || board[`allows${kind}DateOnMinicard`]).length;
return 7 + dates;
},
});
Template.tableView.events({
// #864: drag a row onto another: its card goes just before that card, in
// that card's list and swimlane (models/lib/tableViewDrop.js), through
// the same card.move the board uses.
'dragstart .js-table-view-row'(event) {
const dt = event.originalEvent && event.originalEvent.dataTransfer;
if (dt) {
dt.setData('text/plain', event.currentTarget.dataset.cardId);
dt.effectAllowed = 'move';
}
},
'dragover .js-table-view-row'(event) {
event.preventDefault();
},
async 'drop .js-table-view-row'(event) {
event.preventDefault();
const dt = event.originalEvent && event.originalEvent.dataTransfer;
const draggedId = dt && dt.getData('text/plain');
const dragged = draggedId && ReactiveCache.getCard(draggedId);
const target = ReactiveCache.getCard(event.currentTarget.dataset.cardId);
if (!dragged || !target) return;
const { tableViewDropTarget } = require('/models/lib/tableViewDrop');
const to = tableViewDropTarget(dragged, target, ReactiveCache.getCards({ listId: target.listId,
swimlaneId: target.swimlaneId, archived: false }));
if (!to) return;
try {
await dragged.move(to.boardId, to.swimlaneId, to.listId, to.sort);
} catch (error) {
console.error('Table view move failed:', error);
}
},
async 'submit .js-table-view-add-card'(event, tpl) {
event.preventDefault();
const board = Utils.getCurrentBoard();
const input = tpl.$('.js-table-view-add-card-title');
const title = String(input.val() || '').trim();
if (!board || !title) return;
try {
await Meteor.callAsync('tableViewAddCard', { boardId: board._id, title,
listId: String(tpl.$('.js-table-view-add-card-list').val() || ''),
swimlaneId: String(tpl.$('.js-table-view-add-card-swimlane').val() || '') });
input.val('');
} catch (error) {
// tableViewAddCard refuses with an error code; show it in the reader's
// language rather than the code or an English sentence.
const key = error?.error === 'not-found' ? 'error-list-doesNotExist' : 'error-notAllowed';
alert(TAPi18n.__(key));
}
},
'click .js-table-view-search-button'(event, tpl) {
event.preventDefault();
tpl.searchQuery.set(tpl.$('.js-table-view-search').val() || '');
+3 -2
View File
@@ -46,8 +46,9 @@ template(name="timelineView")
p.timeline-card-due
| {{_ 'due-date'}}: {{this.formattedDueAt}}
if selectedTimestamp
button.timeline-card-restore.js-restore-card-timeline.primary(type="button")
| {{_ 'board-view-timeline-restore'}}
if canRestoreCard this.cardId
button.timeline-card-restore.js-restore-card-timeline.primary(type="button")
| {{_ 'board-view-timeline-restore'}}
template(name="cardRestoreToTimelinePopup")
p {{_ 'board-view-timeline-restore-confirm'}}
+13 -1
View File
@@ -3,6 +3,7 @@ import { ReactiveCache } from '/imports/reactiveCache';
import { ReactiveVar } from 'meteor/reactive-var';
import { Template } from 'meteor/templating';
import { Utils } from '/client/lib/utils';
import { TAPi18n } from '/imports/i18n';
import { reconstructBoardStateAt } from '/models/lib/boardTimeline';
// Board View / Timeline (Step 2 of the maintainer's spec, Step 1 wiring):
@@ -91,6 +92,13 @@ function boardActivities() {
}
Template.timelineView.helpers({
// Restoring writes the card through its own setters, so it is offered only
// to those who may change that card now - the same rule as everywhere else
// (role, list exceptions, assigned-only).
canRestoreCard(cardId) {
const card = ReactiveCache.getCard(cardId);
return !!card && Utils.canModifyCard(card);
},
selectedTimestamp() {
return Template.instance().selectedTimestamp.get();
},
@@ -259,7 +267,11 @@ Template.timelineView.events({
'click .js-restore-card-timeline': Popup.afterConfirm('cardRestoreToTimeline', function () {
const cardContext = this;
if (cardContext && cardContext.cardId) {
restoreCardFromTimeline(cardContext.cardId, cardContext.fields);
// A refused write used to vanish: nothing awaited the restore.
restoreCardFromTimeline(cardContext.cardId, cardContext.fields).catch(error => {
if (process.env.DEBUG === 'true') console.warn('timeline restore failed:', error);
alert(TAPi18n.__('error-notAllowed'));
});
}
}),
});
@@ -0,0 +1,515 @@
/* Board View / Version Control: GitHub's and Gitea's repository layout - a
header, tabs, a bordered box of issues - in the board's own theme. No colour
of its own: the accent is the theme's (--theme-accent, falling back to
WeKan blue, as emailView.css does), a label is the board label's own colour,
and the boxes are drawn with currentColor, so a dark theme stays dark.
Logical properties, so a right-to-left language mirrors it. */
.vcs-view {
padding: 12px 16px;
min-height: 100%;
box-sizing: border-box;
max-width: 1280px;
}
.vcs-header {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 8px;
}
.vcs-repo {
display: flex;
align-items: center;
gap: 8px;
margin: 0;
font-size: 1.3em;
}
.vcs-repo-name {
font-weight: bold;
color: var(--theme-accent, #01628c);
}
.vcs-badge {
font-size: 0.65em;
font-weight: normal;
padding: 1px 8px;
border: 1px solid currentColor;
border-radius: 2em;
opacity: 0.75;
}
.vcs-header-actions,
.vcs-import {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
}
.vcs-import-link,
.vcs-button {
display: inline-flex;
align-items: center;
padding: 4px 10px;
border: 1px solid color-mix(in srgb, currentColor 25%, transparent);
border-radius: 6px;
color: inherit;
text-decoration: none;
}
.vcs-import-link:hover,
.vcs-button:hover {
background: color-mix(in srgb, currentColor 8%, transparent);
}
.vcs-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin: 12px 0 16px;
border-bottom: 1px solid color-mix(in srgb, currentColor 20%, transparent);
}
.vcs-tab {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 8px 12px;
border-bottom: 2px solid transparent;
color: inherit;
text-decoration: none;
}
.vcs-tab.is-active {
border-bottom-color: var(--theme-accent, #01628c);
font-weight: bold;
}
.vcs-counter {
min-width: 1.5em;
padding: 0 6px;
border-radius: 2em;
font-size: 0.85em;
text-align: center;
background: color-mix(in srgb, currentColor 12%, transparent);
}
.vcs-toolbar {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 12px;
}
.vcs-search {
flex: 1 1 280px;
margin: 0 !important;
}
.vcs-new,
.vcs-view button.primary {
background: var(--theme-accent, #01628c) !important;
color: #fff !important;
}
.vcs-box {
border: 1px solid color-mix(in srgb, currentColor 20%, transparent);
border-radius: 6px;
overflow: hidden;
}
.vcs-box-header {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 16px;
padding: 10px 16px;
background: color-mix(in srgb, currentColor 5%, transparent);
border-bottom: 1px solid color-mix(in srgb, currentColor 20%, transparent);
}
.vcs-state {
color: inherit;
text-decoration: none;
opacity: 0.7;
}
.vcs-state.is-active {
font-weight: bold;
opacity: 1;
}
.vcs-sort {
margin: 0 0 0 auto !important;
margin-inline-start: auto !important;
width: auto !important;
}
.vcs-issues,
.vcs-milestones,
.vcs-labels {
list-style: none;
margin: 0;
padding: 0;
}
.vcs-issue,
.vcs-milestone,
.vcs-label-row {
display: flex;
align-items: flex-start;
gap: 10px;
padding: 10px 16px;
border-top: 1px solid color-mix(in srgb, currentColor 12%, transparent);
}
.vcs-issue:first-child,
.vcs-milestone:first-child,
.vcs-label-row:first-child {
border-top: 0;
}
.vcs-issue:hover {
background: color-mix(in srgb, currentColor 4%, transparent);
}
.vcs-issue-icon {
margin-top: 3px;
}
/* Open and closed are told apart by the icon as well as the colour. */
.vcs-issue-icon.is-open,
.vcs-state-badge.is-open {
color: #1a7f37;
}
.vcs-issue-icon.is-closed,
.vcs-state-badge.is-closed {
color: #8250df;
}
.vcs-issue-main,
.vcs-milestone-main {
flex: 1 1 auto;
min-width: 0;
}
.vcs-issue-title,
.vcs-milestone-title {
font-weight: bold;
color: inherit;
text-decoration: none;
margin-inline-end: 6px;
}
.vcs-issue-title:hover,
.vcs-milestone-title:hover {
color: var(--theme-accent, #01628c);
text-decoration: underline;
}
.vcs-label {
display: inline-block;
margin: 2px 4px 2px 0;
text-decoration: none;
}
.vcs-issue-meta {
font-size: 0.85em;
margin-top: 2px;
}
.vcs-issue-milestone {
margin-inline-start: 8px;
color: inherit;
}
.vcs-issue-side {
display: flex;
align-items: center;
gap: 6px;
flex: 0 0 auto;
}
.vcs-issue-side .member {
width: 22px;
height: 22px;
}
.vcs-empty {
padding: 24px 16px;
text-align: center;
}
.vcs-milestone-progress {
flex: 0 0 40%;
}
.vcs-progress {
height: 8px;
border-radius: 4px;
overflow: hidden;
background: color-mix(in srgb, currentColor 12%, transparent);
}
.vcs-progress-bar {
height: 100%;
background: #1a7f37;
}
.vcs-overdue {
color: #cf222e;
}
.vcs-files {
width: 100%;
border-collapse: collapse;
}
.vcs-files td {
padding: 8px 16px;
border-top: 1px solid color-mix(in srgb, currentColor 12%, transparent);
}
/* One issue */
.vcs-back {
color: inherit;
}
.vcs-issue-heading {
margin: 8px 0 4px;
font-size: 1.6em;
font-weight: normal;
}
.vcs-issue-status {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
padding-bottom: 12px;
border-bottom: 1px solid color-mix(in srgb, currentColor 20%, transparent);
}
.vcs-state-badge {
padding: 4px 12px;
border: 1px solid currentColor;
border-radius: 2em;
font-weight: bold;
}
.vcs-issue-layout {
display: flex;
flex-wrap: wrap;
gap: 24px;
margin-top: 16px;
}
.vcs-timeline {
flex: 1 1 520px;
min-width: 0;
}
.vcs-sidebar {
flex: 0 1 240px;
}
.vcs-side-block {
padding-bottom: 12px;
margin-bottom: 12px;
border-bottom: 1px solid color-mix(in srgb, currentColor 12%, transparent);
}
.vcs-side-block h3 {
margin: 0 0 6px;
font-size: 0.9em;
opacity: 0.8;
}
.vcs-comment {
margin-bottom: 16px;
border: 1px solid color-mix(in srgb, currentColor 20%, transparent);
border-radius: 6px;
}
.vcs-comment-header {
display: flex;
align-items: center;
gap: 6px;
padding: 6px 12px;
background: color-mix(in srgb, currentColor 5%, transparent);
border-bottom: 1px solid color-mix(in srgb, currentColor 20%, transparent);
}
.vcs-comment-header .member {
width: 22px;
height: 22px;
}
.vcs-comment-body {
padding: 8px 12px;
}
.vcs-comment-form textarea,
.vcs-new-issue textarea,
.vcs-new-issue input[type="text"] {
width: 100%;
box-sizing: border-box;
}
.vcs-comment-actions {
display: flex;
justify-content: flex-end;
gap: 8px;
}
.vcs-new-issue {
max-width: 900px;
}
.vcs-new-labels {
border: 0;
padding: 0;
margin: 8px 0;
}
.vcs-new-label {
display: inline-flex;
align-items: center;
gap: 4px;
margin-inline-end: 12px;
}
@media (max-width: 640px) {
.vcs-milestone {
flex-wrap: wrap;
}
.vcs-milestone-progress {
flex-basis: 100%;
}
.vcs-sidebar {
flex-basis: 100%;
}
}
.vcs-synced {
align-self: center;
margin-inline-end: auto;
}
/* Releases and the wiki, as GitHub draws them. */
.vcs-releases {
list-style: none;
margin: 0;
padding: 0;
display: grid;
gap: 16px;
}
.vcs-wiki {
display: flex;
flex-wrap: wrap;
gap: 24px;
align-items: flex-start;
}
.vcs-wiki-main {
flex: 1 1 560px;
min-width: 0;
}
.vcs-wiki-side {
flex: 0 1 260px;
display: grid;
gap: 16px;
}
.vcs-wiki-tools {
display: flex;
gap: 16px;
}
.vcs-wiki-tools a {
color: inherit;
}
.vcs-wiki-footer {
margin-top: 24px;
padding-top: 12px;
border-top: 1px solid color-mix(in srgb, currentColor 20%, transparent);
}
.vcs-wiki-pages {
list-style: none;
margin: 0;
padding: 0;
}
.vcs-wiki-pages li a {
display: block;
padding: 6px 16px;
color: inherit;
text-decoration: none;
border-top: 1px solid color-mix(in srgb, currentColor 12%, transparent);
}
.vcs-wiki-pages li.is-active a {
font-weight: bold;
color: var(--theme-accent, #01628c);
}
.vcs-wiki-sidebar {
padding: 8px 16px;
}
/* Titles, labels and milestones that open or filter are spans with
role=link (a rendered title can hold a link of its own). */
.vcs-view [role="link"] {
cursor: pointer;
}
.vcs-view [role="link"]:focus-visible {
outline: 2px solid currentColor;
outline-offset: 2px;
}
/* Attachments: the controls of an opened card's attachments. */
.vcs-file-view {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 0;
border: 0;
background: none;
color: inherit;
font: inherit;
text-align: start;
cursor: pointer;
}
.vcs-file-view:hover,
.vcs-file-view:focus-visible {
text-decoration: underline;
}
.vcs-file-thumbnail {
width: 32px;
height: 32px;
object-fit: cover;
border-radius: 3px;
}
.vcs-file-actions {
white-space: nowrap;
}
.vcs-file-actions a {
margin-inline-end: 8px;
color: inherit;
cursor: pointer;
}
@@ -0,0 +1,381 @@
//- Board View / Version Control: the board as a GitHub / Gitea repository's
issue tracker, in the board's own theme. Design:
docs/Features/Board-Header/Board-Views/Version-Control-View.md. A card is an issue, the board's
labels are its labels, a label with a due date is a milestone, the comments
are the conversation, the attachments are the files and the board's change
history is the history, with its undo.
template(name="versionControlView")
.vcs-view
header.vcs-header
h2.vcs-repo
i.fa.fa-code-fork
span.vcs-repo-name
+inlineViewer
= boardTitle
if isPublic
span.vcs-badge {{_ 'public'}}
.vcs-header-actions
if canWrite
.vcs-import
span.quiet {{_ 'import'}}:
each importSources
a.vcs-import-link(href="{{pathFor 'import' source=key}}") {{name}}
if canWrite
each syncedLists
button.vcs-button.js-vcs-sync(type="button" data-list-id="{{_id}}" disabled="{{#if busy}}disabled{{/if}}" title="{{source}} {{project}}{{#if error}} - {{error}}{{/if}}")
i.fa.fa-refresh(class="{{#if busy}}fa-spin{{/if}}")
| &nbsp;{{_ 'vcs-sync-now' source}}:
+inlineViewer
= title
a.vcs-button.js-vcs-export(href="#")
i.fa.fa-download
| &nbsp;{{_ 'export-board'}}
nav.vcs-tabs(role="tablist" aria-label="{{_ 'board-view-version-control'}}")
each tabs
a.vcs-tab.js-vcs-tab(href="#" role="tab" data-tab="{{id}}" aria-selected="{{#if active}}true{{else}}false{{/if}}" class="{{#if active}}is-active{{/if}}")
i.fa(class="{{icon}}")
span {{_ labelKey}}
if count
span.vcs-counter {{count}}
if isTab 'issues'
if creating
+versionControlNewIssue
else if selectedIssue
+versionControlIssue(selectedIssue)
else
.vcs-toolbar
input.vcs-search.js-vcs-search(type="search" value="{{query}}" aria-label="{{_ 'search'}}" placeholder="is:open label:bug milestone:v1 author:@me")
a.vcs-button.js-vcs-tab(href="#" data-tab="labels")
i.fa.fa-tag
| &nbsp;{{_ 'labels'}}
a.vcs-button.js-vcs-tab(href="#" data-tab="milestones")
i.fa.fa-map-signs
| &nbsp;{{_ 'vcs-milestones'}}
if canWrite
button.primary.vcs-new.js-vcs-new(type="button") {{_ 'vcs-new-issue'}}
.vcs-box
.vcs-box-header
a.vcs-state.js-vcs-state(href="#" data-state="open" class="{{#if stateIs 'open'}}is-active{{/if}}")
i.fa.fa-dot-circle-o
| &nbsp;{{counts.open}} {{_ 'vcs-open'}}
a.vcs-state.js-vcs-state(href="#" data-state="closed" class="{{#if stateIs 'closed'}}is-active{{/if}}")
i.fa.fa-check
| &nbsp;{{counts.closed}} {{_ 'vcs-closed'}}
select.vcs-sort.js-vcs-sort(aria-label="{{_ 'sort'}}")
each sorts
option(value="{{id}}" selected="{{#if active}}selected{{/if}}") {{_ labelKey}}
ul.vcs-issues
each issues
li.vcs-issue
i.fa.vcs-issue-icon(class="{{#if closed}}fa-check-circle is-closed{{else}}fa-dot-circle-o is-open{{/if}}" aria-hidden="true")
.vcs-issue-main
//- Rendered titles and labels can hold links, and a link
inside a link is not one: the clickable ones are spans.
span.vcs-issue-title.js-vcs-open-issue(role="link" tabindex="0" data-card-id="{{_id}}")
+inlineViewer
= title
each labels
span.card-label.vcs-label.js-vcs-label(role="link" tabindex="0" data-name="{{name}}" class="{{labelColorClass color}}" style="{{labelColorStyle color}}")
+inlineViewer
= name
.vcs-issue-meta.quiet
if number
| {{numberText}}&nbsp;
| {{_ 'vcs-opened-by' date author}}
each milestones
span.vcs-issue-milestone.js-vcs-milestone(role="link" tabindex="0" data-title="{{title}}")
i.fa.fa-map-signs
| &nbsp;
+inlineViewer
= title
.vcs-issue-side
each assignees
+userAvatar(userId=this noRemove=true)
if comments
span.vcs-issue-comments(title="{{_ 'comments'}}")
i.fa.fa-comment-o
| &nbsp;{{comments}}
else
li.vcs-empty.quiet {{_ 'vcs-no-issues'}}
if isTab 'milestones'
.vcs-box
ul.vcs-milestones
each milestones
li.vcs-milestone
.vcs-milestone-main
span.vcs-milestone-title.js-vcs-milestone(role="link" tabindex="0" data-title="{{title}}")
+inlineViewer
= title
.quiet(class="{{#if overdue}}vcs-overdue{{/if}}")
if dueAt
i.fa.fa-calendar
| &nbsp;{{_ 'vcs-due' due}}
else
| {{_ 'vcs-no-due-date'}}
.vcs-milestone-progress
.vcs-progress(role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="{{percent}}")
.vcs-progress-bar(style="width: {{percent}}%")
.quiet {{percent}}% · {{open}} {{_ 'vcs-open'}} · {{closed}} {{_ 'vcs-closed'}}
else
li.vcs-empty.quiet {{_ 'vcs-no-milestones'}}
if isTab 'labels'
.vcs-box
ul.vcs-labels
each labelRows
li.vcs-label-row
span.card-label.vcs-label.js-vcs-label(role="link" tabindex="0" data-name="{{name}}" class="{{labelColorClass color}}" style="{{labelColorStyle color}}")
if name
+inlineViewer
= name
else
| {{color}}
span.quiet {{_ 'vcs-open-issues' open}}
else
li.vcs-empty.quiet {{_ 'vcs-no-labels'}}
if isTab 'releases'
ul.vcs-releases
each releases
li.vcs-box.vcs-release
.vcs-comment-header
i.fa.fa-tag
strong
+inlineViewer
= title
span.quiet {{date}}
.vcs-comment-body
+viewer
= body
if files.length
h4 {{_ 'attachments'}}
each files
p
i.fa.fa-file-archive-o
|
a(href="{{link}}" target="_blank" rel="noopener noreferrer") {{name}}
else
li.vcs-empty.quiet {{_ 'vcs-no-releases'}}
if isTab 'wiki'
if wikiPage
.vcs-wiki
.vcs-wiki-main
with wikiPage
h2.vcs-issue-heading
+inlineViewer
= title
p.vcs-wiki-tools
a.js-vcs-wiki-history(href="#")
i.fa.fa-history
| {{_ 'history'}}
if canWrite
a.js-vcs-wiki-edit(href="#")
i.fa.fa-pencil
| {{_ 'edit'}}
if wikiHistory
//- The page's history: each commit's edit, and Restore brings
an older text back - the card's own history table.
with wikiPage
+historyTable(scope="card" scopeId=_id)
else
.vcs-wiki-body
with wikiPage
+viewer
= text
if wikiFooter
.vcs-wiki-footer
+viewer
= wikiFooter
aside.vcs-wiki-side
.vcs-box
.vcs-box-header
strong {{_ 'vcs-wiki-pages'}}
ul.vcs-wiki-pages
each wikiPages
li(class="{{#if active}}is-active{{/if}}")
span.js-vcs-wiki-page(role="link" tabindex="0" data-card-id="{{_id}}")
+inlineViewer
= title
if wikiSidebar
.vcs-box.vcs-wiki-sidebar
.vcs-wiki-body
+viewer
= wikiSidebar
else
p.vcs-empty.quiet {{_ 'vcs-no-wiki'}}
if isTab 'files'
.vcs-box
table.vcs-files
tbody
each files
//- The controls of an opened card's attachments
(cards/attachments.jade attachmentGallery): a click on the
thumbnail or the name views it in the same viewer, walking the
tab's files; then download, rename, delete and the cover /
background menu, for the same members.
tr.vcs-file(data-attachment-id="{{_id}}")
td.vcs-file-name
if canPreview
button.vcs-file-view.js-vcs-file-view(type="button" data-attachment-id="{{_id}}" title="{{_ 'preview'}}" aria-label="{{_ 'preview'}}: {{cleanFilename name}}")
if isImage
img.vcs-file-thumbnail(src="{{attachmentPreviewUrl attachment}}" alt="" loading="lazy")
else
i.fa.fa-file-o
| &nbsp;{{cleanFilename name}}
else
i.fa.fa-file-o
| &nbsp;
a(href="{{link}}" target="_blank" rel="noopener noreferrer") {{name}}
td.quiet {{size}}
td.vcs-file-actions
if canPreview
a.js-vcs-file-view(data-attachment-id="{{_id}}" title="{{_ 'preview'}}")
i.fa.fa-search-plus
a.js-download(href="{{link}}?download=true" download="{{downloadFilename name}}" title="{{_ 'download'}}")
i.fa.fa-arrow-down
if currentUser.isBoardMember
unless currentUser.isCommentOnly
unless currentUser.isWorker
a.js-vcs-file-rename(data-attachment-id="{{_id}}" title="{{_ 'rename'}}")
i.fa.fa-pencil-square-o
a.js-vcs-file-delete(data-attachment-id="{{_id}}" title="{{_ 'delete'}}")
i.fa.fa-trash
a.js-vcs-file-menu(data-attachment-id="{{_id}}" title="{{_ 'attachmentActionsPopup-title'}}")
i.fa.fa-bars
td
span.js-vcs-open-issue(role="link" tabindex="0" data-card-id="{{cardId}}")
if number
| {{numberText}}&nbsp;
+inlineViewer
= issueTitle
td.quiet {{date}}
else
tr
td.vcs-empty.quiet {{_ 'vcs-no-files'}}
if isTab 'history'
//- The board's change history, as git log: who changed what, when -
and Restore undoes it, through the same history every board has.
+historyTable(scope="board" scopeId=boardId)
//- One issue: the conversation on the left, labels, people, milestone and
files on the right - GitHub's issue page.
template(name="versionControlIssue")
.vcs-issue-page
a.vcs-back.js-vcs-back(href="#")
i.fa.fa-arrow-left
| &nbsp;{{_ 'vcs-issues'}}
h2.vcs-issue-heading
+inlineViewer
= title
| &nbsp;
if number
span.quiet {{numberText}}
.vcs-issue-status
span.vcs-state-badge(class="{{#if closed}}is-closed{{else}}is-open{{/if}}")
i.fa(class="{{#if closed}}fa-check-circle{{else}}fa-dot-circle-o{{/if}}")
| &nbsp;{{#if closed}}{{_ 'vcs-closed'}}{{else}}{{_ 'vcs-open'}}{{/if}}
span.quiet {{_ 'vcs-opened-by' date author}} · {{_ 'vcs-comment-count' comments}}
.vcs-issue-layout
.vcs-timeline
.vcs-comment
.vcs-comment-header
strong {{author}}
span.quiet &nbsp;{{date}}
.vcs-comment-body
if body
+viewer
= body
else
p.quiet {{_ 'vcs-no-description'}}
each conversation
.vcs-comment
.vcs-comment-header
+userAvatar(userId=userId noRemove=true)
strong {{author}}
span.quiet &nbsp;{{time}}
.vcs-comment-body
+viewer
= text
if canComment
form.vcs-comment-form.js-vcs-comment
textarea.js-vcs-comment-text(rows="4" aria-label="{{_ 'vcs-leave-comment'}}" placeholder="{{_ 'vcs-leave-comment'}}")
.vcs-comment-actions
if synced
span.quiet.vcs-synced
i.fa.fa-refresh
| &nbsp;{{_ 'vcs-synced-from' syncSource}}
else if canWrite
if closed
button.js-vcs-set-state(type="button" data-state="open")
i.fa.fa-dot-circle-o
| &nbsp;{{_ 'vcs-reopen'}}
else
button.js-vcs-set-state(type="button" data-state="closed")
i.fa.fa-check-circle
| &nbsp;{{_ 'vcs-close'}}
button.primary(type="submit") {{_ 'comment'}}
aside.vcs-sidebar
.vcs-side-block
h3 {{_ 'assignee'}}
each assignees
+userAvatar(userId=this noRemove=true)
else
p.quiet {{_ 'vcs-none'}}
.vcs-side-block
h3 {{_ 'labels'}}
each labels
span.card-label.vcs-label(class="{{labelColorClass color}}" style="{{labelColorStyle color}}")
+inlineViewer
= name
else
p.quiet {{_ 'vcs-none'}}
.vcs-side-block
h3 {{_ 'vcs-milestones'}}
each milestones
p
i.fa.fa-map-signs
| &nbsp;
+inlineViewer
= title
else
p.quiet {{_ 'vcs-none'}}
.vcs-side-block
h3 {{_ 'attachments'}}
each issueFiles
p
a(href="{{link}}" target="_blank" rel="noopener noreferrer") {{name}}
else
p.quiet {{_ 'vcs-none'}}
//- Everything else - editing labels, people, dates, uploading files to
the Admin Panel's default storage - is the card's own popup.
button.js-vcs-open-card(type="button" data-card-id="{{_id}}")
i.fa.fa-id-card-o
| &nbsp;{{_ 'vcs-open-card'}}
template(name="versionControlNewIssue")
form.vcs-new-issue.js-vcs-create
h2 {{_ 'vcs-new-issue'}}
input.js-vcs-new-title(type="text" required aria-label="{{_ 'title'}}" placeholder="{{_ 'title'}}" autofocus)
textarea.js-vcs-new-body(rows="10" aria-label="{{_ 'description'}}" placeholder="{{_ 'description'}}")
if boardLabels.length
fieldset.vcs-new-labels
legend {{_ 'labels'}}
each boardLabels
label.vcs-new-label
input.js-vcs-new-label(type="checkbox" value="{{_id}}")
span.card-label(class="{{labelColorClass color}}" style="{{labelColorStyle color}}")
if name
+inlineViewer
= name
else
| {{color}}
if error
p.warning {{error}}
.vcs-comment-actions
button.js-vcs-cancel(type="button") {{_ 'cancel'}}
button.primary(type="submit") {{_ 'vcs-submit-issue'}}
Loaded 100 of 4604 files, more files were not shown because too many files have changed in this diff. Show more