15 Commits
Author SHA1 Message Date
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
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 d1b262d29a Lazy-load browser export code 2026-09-06 18:35:33 +03:00
Lauri Ojansivu 9df568c0c2 Board bar: a long board title moves ALL the buttons to the second row.
On iPad landscape with a long board title, the controls were split across both
rows: the title kept the left group (edit, visibility, watch, star, sort) beside
it and only the right group (Filter / Search / view / dependencies /
multi-selection / hamburger) wrapped underneath.

That is what separate flex items do. The title, the left group, the centre group
and the right group were four children of the #header-main-bar flex row, so the
row filled with as many of them as fit and broke wherever it ran out - between the
left and the right group. The three button groups now live inside ONE wrapper,
.board-header-btns-group, so the row has just two items: the title and the
buttons. Either the buttons fit beside the title or the whole set moves down
together, under the title, which is what the report asked for.

A short title is unaffected: the wrapper's flex-basis is auto, so it measures as
its content width and only moves down when it genuinely does not fit - title and
buttons still share one line exactly as before. The wrapper keeps flex-wrap: wrap
so the groups can still break among themselves at widths where even the buttons
alone do not fit. The h1 gains min-width: 0 / max-width: 100% / overflow-wrap so a
title longer than the whole bar wraps inside the title instead of overflowing the
viewport (a flex item cannot shrink below min-content without min-width: 0).

The template change is a re-indent: every content line is unchanged, only its
indentation, plus the new wrapper and its comment.

exportHTML.js removed '#header-main-bar .board-header-btns' when exporting a board
to HTML; it now removes the wrapper as well, so the export is not left with an
empty div where the buttons were.

Tests: tests/boardHeaderOneLine.test.cjs gains checks that all three groups are
nested INSIDE the wrapper (by indentation, since sibling groups are exactly the
bug), that the title stays a sibling of the wrapper so the row can break between
them, that the wrapper's flex-basis stays auto (with `flex: 1 1 0` it would
measure as zero width, never move down, and the split would come back), that the
h1 can shrink and wrap, and that the export still strips the whole button area.

Thanks to xet7 !
2026-07-25 00:51:14 +03:00
Lauri Ojansivu 2d32e34621 Fix ExportBleed: stored XSS in HTML board exports via a card-title second parse.
A board member could store entity-encoded markup in a card title. It stays inert
on the live board (Blaze escapes it; the +viewer sanitizer neutralizes handlers
and keeps the entity payload as text), but the exported index.html embedded a
click handler that read the card title/body via .textContent — which DECODES HTML
entities — and then concatenated those values into content.innerHTML. That SECOND
parse revived the tag (e.g. `<img src=x onerror=...>`) and executed it when a
recipient clicked the card in the export, disclosing all data in that document
(including cards added after the attacker's board membership was removed).

The modal is now built with DOM nodes and the card title/body are assigned through
textContent, never innerHTML, so they are inserted as inert text.

Regression: tests/exportHTMLXss.test.cjs (title/body reach only textContent; no
titleText/allText flows into any innerHTML; a textContent assignment does not
revive an entity-decoded <img>).

(CWE-79 stored XSS, GitHub Security Advisory GHSA-8r5p-4q9j-f5jx.)

Thanks to koyokr and xet7 !
2026-07-22 18:04:40 +03:00
xet7 ae9be62ef3 Export board to HTML .zip: stream to disk and include every card
The client HTML export cloned the live DOM and built the whole .zip as an
in-memory blob. Two problems on large boards: infinite scroll keeps only ~10
cards per list in the DOM, so most cards were missing from the export; and the
entire archive was buffered in browser RAM.

Before snapshotting, every list's cardlimit is now lifted so the whole board
renders (then we wait for Blaze to flush). The zip is written with JSZip's
generateInternalStream piped straight to the chosen file via the File System
Access API, chunk by chunk with backpressure, so the archive is never held
whole in memory; browsers without the API fall back to the previous blob
download.
2026-07-10 22:57:40 +03:00
Lauri Ojansivu ce3e51b147 Fix Impersonate User. Show/Edit Avatar at Admin Panel/People/People.
Thanks to ahlgrimma, DnT1mmer, robbagithub, norrig and xet7 !

Fixes #6226,
fixes #6101,
fixes #3327,
fixes #4748
2026-04-07 09:52:41 +03:00
Harry Adel ddd873c7a0 Fix client runtime regressions after Meteor 3 migration 2026-03-24 01:51:20 +02:00
Lewis Cowles 4d6e6a85e0 feat: download webfonts as well
Prior to this I was manually saving the fonts, and I don't remember documenting either.

Now there should be no documentation needed.
2023-12-30 06:41:10 +00:00
Lewis Cowles fe887b7b5e fix: export HTML currently exports JS popups 2023-12-30 06:26:53 +00:00
Martin Filser a59dfa8fc0 Card Details Popup, every "Sub-Popup" must use .back() instead of .close()
- .close() closes the popup. Until now it was ok, but as the Card
  Details are opened in a popup too, .back() must be used to get the
  same behaviour back
- .back() is also .close() if there is no "popup layer" left
2021-10-25 12:53:04 +02:00
Tomasz Borychowski d887e553ce fix(scrollbar): remove custom scrollbar 2020-08-28 11:46:51 +01:00
Giacomo Vespignani c9a28db3ab Added an API to get the cards for a specific custom field value 2020-05-27 11:16:35 +02:00
Lauri Ojansivu 05349a5deb Prettifier fixes. 2020-05-25 18:04:32 +03:00
Lewis Cowles 5ef83ab236 Export Board to Zip file
* Extracts Card covers
* Labels
* Re-works some CSS & HTML
* Produces deployable assets (minus WebFonts)
2020-05-18 11:06:18 +01:00