21 Commits
Author SHA1 Message Date
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 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 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 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 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 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 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 953d8f7c13 Organize feature guides by menu and document backup alternatives
Add September feature instructions, preserve guide forwarding links, and document backup coverage with descriptive screenshots from #6683.

Thanks to markusst1982 and xet7 !
2026-09-23 18:34:37 +03:00
Lauri Ojansivu 70202a849b Add checklist deadlines and expose checklist item dates in REST
Add optional checklist dueAt with the shared selected-calendar picker and badge. Return checklist and item deadlines from checklist and single-card GETs. Accept validated timezone-qualified ISO deadlines in checklist/item POST and PUT; null clears, omission preserves. Keep existing write permissions and ownership checks. Node regression and Chromium save/clear test pass.

Thanks to rmb82 and xet7 !

Fixes #6693,
2026-09-15 00:54:45 +03:00
Lauri Ojansivu c64d3454ac Add REST API for Board Settings / Board View, and OpenAPI blocks for the card settings routes.
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 !
2026-09-11 21:18:35 +03:00
Lauri Ojansivu bbcd98d9d8 Add REST API for attachment soft delete and restore, Admin Panel Problems, OAuth login provider and passwordless settings, card field order and pausing a rule.
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 !
2026-09-11 21:18:35 +03:00
Lauri Ojansivu 744df41544 Document what each board role may and may not do, as one table, read from the code.
There was no comparison of the roles anywhere. docs/Features/Members/Members.md
listed three of them - Admin, Normal, Comment only - in one line each, and there
are NINE: board admin, normal, no comments, comment only, worker, read only, and
an assigned-only variant of normal, comment-only and read-only. docs/API/Role.md
shows how to set each flag over the API without saying what any of them does.

docs/Features/Members/Roles.md is the table: for every role, which cards it sees,
whether it may comment, create or edit cards, move cards, edit lists and
swimlanes, and change the board's settings and members. It also says WHERE each
column comes from, because the answer is only two helpers -
allowIsBoardMemberWithWriteAccess() and allowIsBoardMemberCommentOnly() in
server/lib/utils.js - plus isBoardAdmin() and, for visibility, the assigned-only
scope in the card publications. The table is what the SERVER allows, because the
server is the authority and the UI can only hide buttons.

VALIDATED FROM THE CODE, and three things do not match their own names. They are
recorded as gaps, not fixed here, because each needs a decision about which side
is wrong:

  * "Comment only, assigned" (isCommentAssignedOnly) has FULL WRITE ACCESS.
    Nothing outside the card publications ever reads that flag - it is not in
    allowIsBoardMemberWithWriteAccess(), where isCommentOnly is - so the member
    may create and edit cards, lists and checklists. The role is in practice
    "Normal, but only sees the cards assigned to me", which is what
    isNormalAssignedOnly already means.

  * "No comments" (isNoComments) cannot write ANYTHING. That helper excludes it,
    so the role blocks editing as well as commenting - while the schema describes
    it as "is the member not allowed to make comments" and the UI's
    canModifyCard() does not exclude it, so the member is offered the edit
    affordances and the server refuses the write. The name and the schema comment
    both point at the helper being the wrong side.

  * The write helper does not exempt board admins. Every has*() helper on the
    board requires isAdmin: false, so a flag on an admin is ignored, but that one
    reads the raw flags. Not reachable from the Web UI, which writes all eight
    flags at once and never leaves two set - but reachable over the REST API,
    which takes them individually.

A fourth section lists where the UI is more generous than the server, i.e. the
buttons that do nothing: canModifyCard() does not exclude isNoComments, and
canModifyBoard() excludes neither isNoComments nor isWorker. None of these is a
security hole - the server rules hold - but a button that silently fails reads as
a bug to whoever clicks it.

tests/boardRoles.test.cjs keeps the page honest rather than trusting it, which is
the point: a permissions table that quietly goes stale is worse than none, because
it is what an admin decides who to trust with. It parses the table and checks that
every role memberRole() can return has a row and every row names a flag it really
reads, that the "create / edit" and "comment" columns match the flag lists in the
two server helpers, that the "which cards they see" column matches the
assigned-only scope, that only the board-admin row may change settings, that each
gap the table marks is still real AND still explained - so fixing one has to
update the page with it - and that the page is linked from both Members.md and
the API role page.

Thanks to xet7 !
2026-07-31 04:09:41 +03:00
Lauri Ojansivu 25d33cec36 Fix #2345 #2674 #574: rules card title filter, rules REST API, api docs;
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.
2026-07-17 03:36:50 +03:00
Lauri OjansivuandClaude Opus 4.8 7b66884ba3 docs: fix bare wiki-style links to relative paths of existing docs pages
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>
2026-06-21 21:03:27 +03:00
Lauri OjansivuandClaude Opus 4.8 b912ddb6f5 docs: convert internal wiki/repo links from https URLs to relative paths
The GitHub wiki is disabled and its content was migrated into docs/, so every
https://github.com/wekan/wekan/wiki/... link now redirects to the repo home and
is broken. Rewrite all internal links in docs/ to relative paths:

- wiki/<Page> -> relative path to the corresponding docs/ file (renamed pages
  mapped explicitly: Caddy-Webserver-Config->Webserver/Caddy.md, Wekan-Markdown
  ->Features/Markdown/Markdown.md, REST-API-Role->API/Role.md, etc.)
- wiki home -> docs/README.md
- blob|tree|raw/main|master/<path> -> relative path (anchors and #Lxx line
  ranges preserved; bare URLs wrapped as [name](path) so they stay clickable)

Kept absolute (intentionally not flattened to the current ref):
- ref-pinned links (edge, devel, version tags, commit hashes)
- links whose target no longer exists in the tree

wiki/issues -> the GitHub issues tracker URL (external). 436 links across 113
files; verified no wiki links remain and no broken relative links introduced.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 20:43:36 +03:00
Lauri OjansivuandClaude Opus 4.8 84678d3492 Docs: document board-domains & GlobalAdmin REST APIs, big-board export, and template drag-to-share / propagate-members (#5850).
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>
2026-06-19 11:37:44 +03:00
Lauri Ojansivu f6d8befb56 Moved website to separate repo. Moved docs2 back to docs.
Thanks to xet7 !
2026-04-28 11:59:16 +03:00
Lauri Ojansivu 1c0d8e9964 Moved docs to docs2, and wekan.fi to docs, to be able publish with GitHub Pages.
Thanks to xet7 !
2026-04-16 14:36:10 +03:00
Lauri Ojansivu 89480b61ce wekan.github.io to wekan.fi, and blog.wekan.team to wekan.fi/blog 2025-06-28 14:08:57 +03:00
Lauri Ojansivu f54cbac370 Updated docs 2025-06-13 23:05:01 +03:00
Lauri Ojansivu ce89ff4833 Reorganized Docs. In Progress. 2024-06-27 11:08:43 +03:00