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 !
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 !
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 !
Add September feature instructions, preserve guide forwarding links, and document backup coverage with descriptive screenshots from #6683.
Thanks to markusst1982 and xet7 !
GET /api/boards/:boardId/boardViewSettings answers which entries of the
Board View menu a board offers, which one it opens in, separately for a
public and a private board, and the menu order - in the normalised shape the
menu renders with: every view once with both showOnPublic and showOnPrivate
explicit, the two defaults resolved (missing or unknown reads as Swimlanes),
boardViewOrder made whole, and `keys` listing the known views. Board access.
PUT /api/boards/:boardId/boardViewSettings takes any subset of
boardViewSettings, defaultPublicBoardView, defaultPrivateBoardView and
boardViewOrder, through the same pure modifiers the popup's clicks apply
(showBoardViewModifier, defaultBoardViewModifier, normalizeBoardViewOrder),
now composed by boardViewSettingsRequest() in models/lib/boardViewSettings.js:
a default is always shown on its side, hiding a side's current default is a
400 (set another default first - or in the same request, since defaults are
applied before the show flags), an unknown view key anywhere in the body is a
400 and nothing of that request is written, and the order is normalised so the
menu lists every view exactly once. Board admin (or site admin), the same
check as cardFieldOrder. Both answer boardViewSettingsSnapshot().
The existing GET/PUT /api/boards/:boardId/cardSettings routes get the JSDoc
@operation blocks the OpenAPI generator reads (get_board_card_settings,
update_board_card_settings), so the hand-written copy in
openapi/extra_paths.yml - which now duplicated the operationId - is removed.
public/api/wekan.yml and wekan.html are regenerated with the release
workflow's own commands: 158 operations. docs/API/REST-API.md documents both
endpoints with curl examples, and docs/Features/Board/Board-View-Settings.md
links to them.
tests/restApiNewFeatureRoutes.test.cjs pins the four routes to their method,
path, @operation and auth check, that the PUT writes only the helper's $set,
the snapshot's shape, the request helper's positive cases (setting a default
shows it, one request may move the default and hide the old one, the order is
normalised, the input board is not mutated) and negatives (unknown view key in
the order, the settings or a default; hiding a side's default; malformed
bodies; a partly-invalid body applies nothing), and that the generated and the
committed spec carry the four operations exactly once with the old
edit_board_card_settings gone.
Thanks to xet7 !
Every feature of the recent releases that had a UI but no endpoint, on the
same server-side code the UI uses:
- DELETE /api/boards/:boardId/attachments/:attachmentId soft-deletes,
POST .../attachments/:attachmentId/restore restores, and
GET /api/boards/:boardId/attachments/deleted lists what is restorable -
all through the attachments.softDelete / attachments.restore methods the
card and the card history use, run as the request's user. No hard delete.
- GET /api/admin/problems (the Problems status overview plus the new-problem
count per stream), GET /api/admin/problems/:stream (one page, with
limit/skip/search) and POST /api/admin/problems/:stream/acknowledge,
through the admin-only eventLog* methods the Admin Panel pages call.
- GET /api/admin/oauth-providers, PUT /api/admin/oauth-providers/:providerKey
and PUT /api/admin/passwordless, through saveOauthProviderSettings,
savePasswordlessSettings and getOauthProviderConfigSources. A provider's
secret is reported only as { source, hasValue }, never its value.
- GET/PUT /api/boards/:boardId/cardFieldOrder, normalised with the same
applyCardFieldOrder() the card renders with.
- PUT /api/boards/:boardId/rules/:ruleId accepts `enabled` to pause and
resume a rule, and GET reports it.
The OpenAPI generator is fixed on the way: server/models/boards.js has had a
bare `catch {` since v11.67, the esprima parser cannot read that, and a parse
failure was silently skipped - so public/api/wekan.yml has shipped WITHOUT
the whole Boards API since then. It now downlevels `catch {` and
`for await (`, warns when a file cannot be parsed, and no longer emits an
empty sub-schema for a primitive array-element marker such as
`wipLimitGroups.$.listIds.$`, which made the spec unparseable YAML.
public/api/wekan.yml and wekan.html are regenerated with the release
workflow's own commands: 156 operations, up from 122.
tests/restApiNewFeatureRoutes.test.cjs pins every new route to its method,
path, @operation block and authentication check, that the OAuth endpoints
never mention a secret outside the input whitelist, the generator's two
fixes, that the generated and the committed spec carry the Boards API and the
new operations, and - as the negative sweep - that no route in models/ or
server/models/ lacks an authentication check.
Thanks to xet7 !
Sandstorm username races.
- #2345: the Card Title Filter is now SAVED by every board trigger
builder (a trigger doc missing the field could never satisfy the
matcher's $in - those rules fired for nothing), SHOWN in the rule
details, and ENFORCED for archive/restore triggers by resolving the
title from the card when the activity lacks it; legacy field-less
triggers keep matching; the filter popup prefills its current value.
- #2674: rules created via POST/PUT /api/boards/:boardId/rules never
fired - missing matching fields are now normalized to '*' wildcards
and trigger/action types validated; the addMember/removeMember rule
actions no longer crash on unresolvable usernames or member-less
cards and their writes are awaited. New docs/API/Rules.md (endpoints,
matching fields, wildcard semantics, the exact two-rule
add-on-move-to/remove-on-move-from workflow), linked from REST-API.md;
api.py help now lists the rule commands with the same example.
- #574: Sandstorm unique-username generation probes case-insensitively
(anchored, regex-escaped) and retries the next number when a
concurrent insert wins the unique-index race, instead of aborting the
login hook with E11000.
Tests: tests/rulesCardTitleFilter.test.cjs (12),
tests/rulesApiTriggerNormalize.test.cjs (19),
tests/sandstormUsername.test.cjs (11) - positive and negative cases.
Thanks to InfoSec812, sfahrenholz, mitar and xet7.
Many docs still used flat GitHub-wiki page-name links like [Backup](Backup),
[OAuth2](OAuth2) or [FAQ](FAQ) that only resolved on the old wiki; in the docs/
hierarchy they were broken. Rewrite them to correct relative paths of the
existing pages (e.g. ](Backup) -> ](Backup.md), ](OAuth2) -> ](../Login/OAuth2.md),
](Sandstorm) -> ](../Platforms/FOSS/Sandstorm/Sandstorm.md)).
Mapping: page name -> docs/<...>/<Name>.md by filename, with explicit handling
for renamed pages (Caddy/Nginx-Webserver-Config, Wekan-Markdown, the Sandstorm
grain export page, Uberspace) and the REST-API-<X> family -> docs/API/<X>.md.
Ambiguous names resolved by preferring a same-directory file or the primary page.
Anchors and markdown titles preserved. 280 links across the docs tree.
Out of scope (left as-is): source-code references (models/*.js etc.) and
[Link](url) placeholders, which are not docs pages.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add REST endpoints for board domain sharing and global-admin settings, document
exporting large boards without base64 attachments, and document template
drag-to-share drop-target gating plus Propagate Members To Boards behavior.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>