- #3453: publish the FULL subtask ancestor chain, not only direct
parents - the cross-board full-path label vanished after refresh.
- #3748 (defect half): skip unresolvable custom-field definitions on
linked cards - phantom empty rows and a template TypeError on every
card details render. The mirroring itself is by design: linked cards
are pointers; label/custom-field ids are board-scoped; name-based
inheritance is the copy feature, not the link feature.
- #3199: group each archived card with its own Restore/Delete links and
add a clear separator below the group (RTL-safe, theme-neutral).
- #3843: comment-editor uploads and the attachments popup now share one
null-safe meta builder, and tests pin that comment attachments list in
the card's Attachments section.
Closes#3748: mirroring is by design (see above); the only actionable
defect is fixed in this commit.
Closes#3378: already fixed - notification_cleanup moved off SyncedCron
to a guarded Meteor.setInterval singleton, and quave:synced-cron 2.4.0
handles the E11000 history collision that percolate 1.5.x missed.
Closes#3276: already fixed by d3625db75 - the spent-time badge helpers
now read the live Cards document reactively, so the overtime color flips
immediately; verified behaviorally against the real component.
Closes#3843: behavior verified present and now regression-locked.
Thanks to MPeti1, peterbecich, fxkr, jghaanstra and xet7.
Switching directly from one board to another using the favorites bar reused
the same boardBody template instance (currentBoard went A -> B, never null),
so boardBody.onRendered never fired again and the one-shot
Utils.setBackgroundImage() call never re-ran. The previous board's inline
background:url(...) on .board-wrapper stayed on screen. Switching via the
"all boards" list worked only because it destroys and re-creates boardBody.
Two-part fix:
- boardBody.onRendered now calls Utils.setBackgroundImage() inside a reactive
this.autorun(), so it recomputes on every board switch (and background edit).
It performs no DB writes, so there is no autorun write loop.
- setBackgroundImage() now clears any stale inline background when the new
board has no image, instead of only ever setting one. The decision is a
Meteor-free helper models/lib/boardBackground.js (computeBoardBackground),
unit tested (positive + negative) in tests/boardBackground.test.cjs. Board
color keeps being applied reactively via the .board-wrapper colorClass.
Thanks to dasarne and xet7.
Two root causes behind "responsive views broken" / "icons too small to tap",
found via a Playwright iPhone-profile screenshot loop:
1. No viewport meta. The server-rendered <head> (customHeadRender.js) had no
<meta name="viewport">, so mobile browsers used a default ~980px virtual
width and scaled down: tiny UI, window.innerWidth=980 on a 390px phone, and
@media (max-width:800px) never matched. Add width=device-width (user zoom
left enabled for accessibility).
2. profile.mobileMode defaulted to false. The schema set mobileMode=false on
every user, so getMobileMode() always returned it and the auto-detection was
dead — every user stuck in desktop-mode even on phones. Make it optional (no
default). Rewrite auto-detection to use matchMedia width/pointer queries
instead of fragile UA sniffing (same class of bug as Meteor #12421), and add
a matchMedia listener that re-applies mobile-mode on resize/orientation when
the user has no explicit preference.
Verified on an iPhone 13 profile: innerWidth 390, body gets mobile-mode
automatically, header renders as large tappable icons.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The board view menu (Swimlanes, Lists, Calendar, Gantt) gains a new "Table" entry
below Gantt. It shows every card of the current board in a table — Card, List,
Swimlane, Members, Labels and Due Date — using the same styling as the My Cards
table view (the `.my-cards-board-table` CSS classes). Whereas My Cards' table spans
all of the user's cards across boards, this is the per-board equivalent showing all
of the current board's cards. The view is stored like the others as the user's
`board-view-table` boardView. It also has a search box and previous/next pagination
styled like the Admin Panel / People page, and Excel-like column sorting: click the
Card, List, Swimlane or Due Date header to sort by it, click again to reverse, with
an arrow showing the active column and direction. Search, sort and pagination run
client-side over the board's already-loaded cards.
Thanks to xet7 and Claude.
Builds the view toggle that the previously-dead globalSearchViewChange handler
hinted at (it had been an incomplete copy of the due-cards toggle, with no
button/popup/state). Now a real feature:
- globalSearchHeaderBar gets a view-change button (My cards / All cards) and a
globalSearchViewChangePopup, mirroring the due-cards pattern.
- Utils.globalSearchView()/setGlobalSearchView(): reactive (Session) + persisted
(localStorage), default "all". Reactive so the search re-runs immediately on
toggle (no page reload, unlike due cards).
- globalSearch re-runs reactively on the view + query; when "My cards", the
current user's username is injected as a `user:` predicate, reusing the
existing server selector ($or members/assignees) — no new server filtering.
- i18n (en) for the new strings + popup title.
Build-verified (meteor run compiles and starts). Logic reuses the already-tested
`user:` operator; not yet covered by an e2e spec.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Attachments now import. Trello attachment URLs require OAuth to download, so
WeKan could not fetch them. The Trello import page now has two separate file
fields: a **.json** field for a single Trello board, and a
**.zip** field for a package produced by the
[Trello Card Attachments Downloader](https://github.com/wekan/trello-attachments-downloader).
The .zip may contain one *or more* board .json files together with each
board's attachments in a board-name subfolder. The .zip is uploaded over HTTP
and processed entirely on the server (the attachment bytes never travel over
the realtime WebSocket, which previously overflowed the connection and made
the page flicker): WeKan detects every board .json, matches each board to its
attachment subfolder by name, streams each file straight into the Default
file storage, imports all boards, then opens All Boards. The upload is guarded
against abuse — it rejects oversized uploads, zip bombs (too many files or too
much uncompressed data, per-file size caps) and unsafe entry paths (absolute
paths or `..` traversal), and never overwrites existing files (stored under
generated ids). The importer also reads attachments from `card.attachments`
(newer Trello exports), not only from the action log, and uses the correct
Meteor-Files insert API (the previous calls were no-ops on Meteor 3). Single
board imports (the .json file or the pasted JSON, after the map-members step)
now run through the same server-side HTTP path rather than the realtime
`importBoard` method, so a heavy import can no longer drop the WebSocket and
get retried in an endless loop (the page flickering with "Invalid frame
header" / repeated "logged out" errors). When importing only a single board
.json and you have saved Trello API key and token, WeKan uses them on the
server to download that board's attachments, background image, member
avatars and card stickers from Trello (a .json export often omits stickers;
avatars only fill in mapped users who have none — an existing avatar is never
overwritten); each download is best-effort and a failure never aborts the
import. After a single-board import the new board's data is subscribed and
loaded before opening it, so it shows its swimlanes, lists and cards
immediately instead of needing a browser page reload (the HTTP import, unlike
the old realtime method, does not push the written documents to the client by
itself).
- Import Trello-only card features: stickers (shown as Font Awesome icons on
minicards and the card detail), card cover (color or attachment), card
location (name, address, coordinates with an OpenStreetMap link), and the
due-date "complete" checkbox shown on the minicard. Trello's two named
sticker packs are renamed and highlighted (the icon keeps its normal colour)
so they stay distinct: the "taco" pack becomes "mascot" with an underlined
icon, and the "pete" pack becomes "computer" with a ring around the icon,
e.g. "pete-ghost" imports as "computer ghost" with a Snapchat-ghost icon.
Several named stickers that used to share an icon now get their own Font
Awesome 4.7 icon (mascot active => heartbeat, pixel => qrcode, proto =>
flask, embarrassed => meh-o, clean => bath, computer shipped => truck).
- Live Trello API import: paste your Trello API key and token on the Trello
import page to list all your Trello workspaces and boards, then import all
or selected boards together with their attachments (downloaded
server-side). Imported boards are placed under a personal workspace named
after their Trello workspace, created by name if it does not exist, under an
optional parent workspace you choose. The API client respects Trello's rate
limits: it spaces out requests, honours `Retry-After` on HTTP 429, retries
transient 5xx and network errors with capped exponential backoff, and
reports a clear error on an invalid key/token (401). After a successful API
import you are taken to the All Boards page where the new boards appear.
- The live API import runs as a background job on the server: you can navigate
away and come back to the Trello import page to watch progress, see
per-board results, and read the full error log. Errors are shown in a
selectable text box with a one-click "Copy to clipboard" button (and you can
still select and copy just part of the text) so they are easy to share when
fixing issues. If the import stops on a fatal API error (invalid token, or
rate limit still failing after retries) you can fix the cause and Resume
from where it left off, and a server restart leaves the job paused for
resuming rather than lost. You can also Cancel the import, or Cancel and
delete the boards already imported, to start the whole process over cleanly.
- You can optionally save your Trello API key and token with the Save and
Delete buttons on the import page. When saved, they are stored only on the
server, never sent back to the browser, and are reused automatically when
you list workspaces or import (so you do not have to paste them again); a
"saved" indicator is shown, Save can overwrite them with new values, and
Delete clears them from the database. If you do not save them, they are kept
only in server memory for the running import and re-supplied when you resume.
- Larger Trello JSON exports can be selected as a file instead of pasted.
- Keep imported usernames for later mapping. Imported members that match an
existing WeKan user (by username, or by a previously recorded imported
username) are mapped automatically, and the imported username is remembered
on that user so future imports map too. Members with no matching WeKan user
are no longer lost: their usernames are kept on the board, so an admin can
map them to real users later in Admin Panel / People (Imported Usernames),
without having to map everyone up front at import time.
Thanks to xet7 and Claude.
- Board background images can now be stored in WeKan. A new `backgrounds` file
storage directory is created alongside `attachments` and `avatars` (using the
current default storage backend), and Board menu → Board backgrounds lets a
board admin upload background images, set one as the active board background,
download, and delete them. Board export now includes the board's background
images and re-imports them, and a Trello board's background image is
downloaded and stored on import (so it keeps working even if the original
Trello URL later changes). Thanks to xet7 and Claude.
- Stickers can be added to and removed from cards directly in WeKan, chosen from
a set of Font Awesome icons similar to Trello's stickers (previously stickers
only arrived via Trello import). The picker also includes every "mascot"
(underlined) and "computer" (ringed) highlighted sticker that a Trello import
can produce — generated from the same icon mapping the importer uses — so any
imported sticker can also be added by hand. Stickers show on the minicard and
in the card detail. Thanks to xet7 and Claude.
- The Trello-style "complete" checkbox (mark a card complete/incomplete,
independent of the due date) is shown as an animated green checkbox to the
left of the card title, both on the minicard and in the opened card, with
"Mark as complete" / "Mark as incomplete" tooltips. It uses the same animated
checkbox style as Admin Panel / Settings / Announcements and is vertically
centered with the title text, and the two stay in sync. The minicard and the
opened-card checkboxes use the same checked style (the board theme's colour, or
green when no theme is set), so they always look identical. Subtask checkboxes
and the card-detail custom-field checkbox now use the same animated checkbox as
checklist items (they previously used static square icons), so every checkbox
on a card animates consistently. Thanks to xet7 and Claude.
- Cards can now have multiple locations, similar to multiple members. The card
detail shows a Location section (after Labels and Stickers) listing each
location with its name, address and an OpenStreetMap link, with an "Add
location" button to add more and a button to edit or remove each one. A single
location imported from Trello keeps working and is shown in the same list. The
Add/Edit location popup can also detect a location from any map link (Google
Maps, OpenStreetMap, Bing Maps, Apple Maps, or generic `?q=`/`?ll=` links):
paste the link, press "Detect", and the latitude, longitude and (when present)
the place name/address are filled in automatically. The popup also has an
"Open map links at" setting (OpenStreetMap by default, or Google/Bing/Apple
Maps) saved to the user profile, controlling which map service the location
"Open in map" links use. Thanks to xet7 and Claude.
- The opened card now docks to the top of the window, overlaying the global and
board header bars, instead of opening downward from the clicked minicard, and
it can be dragged all the way to the top without its top hiding behind those
bars. (At 100% zoom the board wrapper no longer sets a `transform: scale(1)`,
which had made it the containing block for the fixed card and trapped it below
the headers.) Thanks to xet7 and Claude.
Fixes#5727,
fixes#2457,
fixes#5818,
fixes#755
- Fixed one-finger drag-to-scroll not working in the All Boards view and the
Calendar view on Fairphone 4 Firefox and Ubuntu Touch Morph. These browsers
do not emit the synthetic mouse events the `@wekanteam/dragscroll` library
relies on, so scrolling only worked in the Swimlanes/Lists views. Added a new
native touch-scrolling helper `client/lib/dragscrollTouch.js` that scrolls the
nearest `.dragscroll` container with a one-finger drag (iOS is skipped, since
native momentum scrolling already works there), and added the `dragscroll`
class to the All Boards list and the Calendar view.
- Fixed all text, buttons, the All Boards tables and the top bars (header and
the second bar) rendering about 2x too large on Fairphone 4. `getMobileMode()`
defaulted to mobile mode only for iPhone, so every other phone fell back to
desktop mode, whose large desktop sizing looks oversized on a small phone
screen. The default is now mobile mode for any phone-sized touch device
(iPhone, Android/Mobile browsers, Ubuntu Touch, or a coarse-pointer touch
screen with viewport width ≤ 800px); desktop browsers stay in desktop mode and
the user's own Mobile/Desktop toggle still takes priority.
- Fixed not being able to reorder swimlanes and lists by dragging their drag
handle on touch devices. The new touch-scroll helper was hijacking touches
that started on a drag handle; `.handle` / `.ui-sortable-handle` elements are
now excluded from touch-scrolling so dragging the handle reorders as expected.
- Fixed a card duplicating into two cards when it was dragged by its title-row
drag handle while its Card Details panel was open. The card sortable in
`client/components/lists/list.js` was re-initialized on every list re-render
without first destroying the previous instance, so two `stop` handlers fired
per drag. It now destroys any existing sortable before re-initializing, the
same guard already used for swimlanes.
Attachment storage:
- Added configurable attachment storage backends in the admin panel: choose the
default save storage and store attachments on the local filesystem, GridFS, or
cloud object storage — S3-compatible (AWS S3, MinIO, Cloudflare R2, Backblaze
B2, Wasabi, DigitalOcean Spaces), Azure Blob Storage, or Google Cloud Storage
— via the `@tweedegolf/storage-abstraction` adapters (new
`models/lib/cloudStorage.js`). Includes a "Test connection" action and
English translations for the new settings.
- Added a server-side bulk attachment move (new `server/attachmentBulkMove.js`
and `models/attachmentBulkMoveStatus.js`) that moves all attachments from one
storage backend to another as a background job whose progress survives the
admin navigating away from or closing the page.
Thanks to xet7.
Add findWhere, where, uniqBy, groupBy, indexBy, debounce, and once
helper functions in imports/lib/collectionHelpers.js. These provide
null-safe replacements for underscore methods that have no clean
native one-liner equivalent. Expose them on the Utils global for
client-side usage.