Commit Graph
6 Commits
Author SHA1 Message Date
Lauri Ojansivu 96c7faa872 Adaptive card loading: auto-lazy for big boards, no admin toggle (#6480)
Card loading is no longer an admin-configurable toggle. WeKan decides
per board by size ('auto', the default): a board over the threshold
(default 500 cards, CARDS_LOADING_LAZY_THRESHOLD) loads only visible
cards via windows; smaller boards keep loading all cards (unchanged,
fully featured). The server computes the mode per board and publishes it
(boardCardsLoadingMode -> boardCardLoadingModes) so client rendering
agrees; isLazyCards is now per-board.

Lazy windows now publish comments/attachments/checklists/checklistItems
with ONE cursor per window instead of one per card (same N+1 fix as
eager mode), and an open card subscribes to its own live children
(openCardData) so it is complete even when outside/just-added-to a
window. Operators can still force CARDS_LOADING=all|lazy|auto.

Removed the Admin/Features card-loading dropdown; pure decision logic in
models/lib/cardsLoading.js (resolveCardsLoadingMode now defaults to
'auto', plus effectiveBoardCardsMode + threshold) with updated unit
tests. New i18n key cards-loading-auto across locales.
2026-07-20 13:17:28 +03:00
Lauri Ojansivu f1cc8daebf Admin Panel / Features / Notifications: GDPR activity/notification controls (#5820)
New Notifications category with three GDPR toggles (all default off), enforced
server-side:

- Disable all activities: activity-feed entries are neither recorded
  (Activities.before.insert returns false to cancel) nor shown (the activities
  publication returns nothing, hiding existing and new entries).
- Disable all notifications: the Activities.after.insert dispatch hook returns
  early, so no watch notifications are sent; activity recording is unaffected.
- Disable watch: the watch() method rejects any watch-level change, and the board
  header watch button is hidden.

A synchronously-readable server cache (models/lib/featureFlags.js, kept current by
a Settings observer) lets the sync before.insert hook read the flags without
awaiting. Adds settings disableActivities, disableNotifications, disableWatch,
published in the public setting, plus global Blaze helpers activitiesDisabled /
watchDisabled for UI hiding.
2026-07-14 14:00:49 +03:00
Lauri Ojansivu 06fdede782 Admin Panel / Features / Security: import/export privacy controls
Six new optional toggles (all default off) governing how boards and user data
cross the WeKan boundary, enforced server-side so they cannot be bypassed from
the client:

- Disable all import / Disable all export: master switches that turn off every
  import/export feature. importBoard and cloneBoard call assertImportEnabled;
  every Exporter build()/buildStream() call assertExportEnabled. The import and
  export menu options are hidden in the UI via global importDisabled/exportDisabled
  Blaze helpers.
- Disable import avatars / Disable export avatars: never carry avatars into/out of
  WeKan. Import is gated at the single localizeAvatarFromBuffer choke point (covers
  WeKan JSON import, LDAP, OIDC/OAuth2 login sync and board-open localization) plus
  Trello inlineMemberAvatars; export skips the base64 avatar embed + avatarUrl in
  both Exporter paths.
- Anonymize import users / Anonymize export users: replace username/fullname/initials
  with counter placeholders (user1, user2, ...), drop avatars, and rewrite @username
  mentions + requestedBy/assignedBy in card/comment text. The placeholder word "user"
  is translated to the current user language. Export covers both build() and the
  streaming buildStream() (which does an id-only pre-scan so mentions streamed before
  the users array resolve to matching labels); import scrubs the incoming board in
  WekanCreator before any placeholder user or card is created.

New shared server helper models/lib/importExportSecurity.js holds the settings
read, guards, anonymization map builder and text rewriter. Adds settings
disableAllImport, disableAllExport, disableImportAvatars, disableExportAvatars,
anonymizeImportUsers, anonymizeExportUsers, published in the public setting.
2026-07-14 13:53:11 +03:00
Lauri Ojansivu 38ce497887 Admin Panel / Features / Security: "Always show all code as plain text"
New optional toggle under Admin Panel / Features / Security. When enabled,
rich text is never rendered as markdown or HTML -- the entire source is shown
as escaped plain text in every rich text field (board and card titles,
descriptions, comments, checklists, etc.), so hidden content is always
revealed: HTML comments (<!-- -->), the target URL inside a markdown link,
JavaScript and any other code. All code is always visible, not clickable, and
not running.

This extends the existing invisiblebleed protection (which already showed raw
source for description-less markdown links) to all content, and complements
the "Render links as plain text" toggle.

Implementation:
- New global setting alwaysShowCodeAsText (models/settings.js), published in
  the public setting publication, default off.
- Admin Panel / Features / Security gains a second materialCheckBox toggle.
- The markdown helper has the raw text, so the decision lives there. Because
  the wekan-markdown package cannot import app code, the setting is bridged via
  a ReactiveVar on the exported Markdown object; the rich text viewer
  (editor.js) keeps it in sync with the setting reactively, and the markdown
  helper reads it and renders the fully HTML-escaped source in a <pre> when on.
  Full escaping (not DOMPurify stripping) ensures tags like <script> are shown
  as text rather than removed. The description-less-link branch now uses the
  same full escaping so it too reveals all source.
2026-07-14 13:17:33 +03:00
Lauri Ojansivu f68c91d102 Admin Panel / Features: optional "Render links as plain text" toggle
New optional security feature under Admin Panel / Features / Security. When
enabled, all links -- both markdown links like [label](url) and raw HTML
<a href> tags -- are always shown as plain, non-clickable text in every rich
text field (board and card titles, descriptions, comments, checklists, etc.),
so a link can never be clicked and cannot present misleading anchor text.

This is a hardening option on top of the existing XSS sanitization (which
already strips javascript:/data: schemes, event-handler attributes and
dangerous tags); it addresses #6453, where a board title rendered as a
clickable link. Default off, so links stay clickable.

Implementation:
- New global setting renderLinksAsPlainText (models/settings.js), published in
  the public setting publication.
- Admin Panel / Features gains a Security category with a materialCheckBox
  toggle that saves the setting immediately.
- The shared DOMPurify sanitizer takes a { stripLinks } option that forbids the
  <a> tag while keeping its inner text (KEEP_CONTENT), so links collapse to
  plain text. The rich text viewer (mentions helper in editor.js) passes
  stripLinks based on the setting, gated reactively so toggling re-renders.
2026-07-14 12:51:44 +03:00
xet7 09805a8a91 Lazy card loading (client) + Admin Panel / Features section
Client side of CARDS_LOADING=lazy. In lazy mode each list's cardsWithLimit
subscribes to boardCardsWindow for just the cards it renders (growing with the
infinite-scroll limit) and to boardListCardCount for the total; showSpinner uses
that server count to decide whether more cards remain, since minimongo now holds
only the loaded window. Default 'all' mode is unchanged (helpers short-circuit on
isLazyCards()).

Adds Admin Panel / Features — a new admin section (route /admin-features, nav
entry, adminFeatures template) intended as the home for optional / performance /
future tier-gated capabilities. First control: "Card loading" (all | lazy), which
writes the cardsLoading Setting; it mirrors, and is overridden on startup by, the
CARDS_LOADING env var. Includes an experimental-note (counters/WIP limits and the
Calendar/Table/Gantt views reflect only loaded cards; reload boards after change)
and the i18n keys.
2026-07-10 22:57:40 +03:00