Compare commits

..
743 Commits
Author SHA1 Message Date
Lauri Ojansivu 8d3b53f51d v8.08 2025-10-21 11:01:40 +03:00
Lauri Ojansivu d73e006935 Updated ChangeLog. 2025-10-21 10:48:45 +03:00
Lauri Ojansivu 9536e60bd1 Fix opening board migration of Shared Lists to Per-Swimlane lists to use ReactiveCache correctly without errors.
Thanks to xet7 !

Fixes #5960
2025-10-21 10:46:37 +03:00
Lauri Ojansivu 678ca978a3 Merge branch 'main' of github.com:wekan/wekan 2025-10-20 19:13:10 +03:00
Lauri Ojansivu 39420877fd Updated ChangeLog. 2025-10-20 19:12:07 +03:00
Lauri Ojansivu 6ea03cfba3 Revert moving mongodb raw database files.
Thanks to xet7 !
2025-10-20 18:08:52 +03:00
Lauri Ojansivu 9214b56aea v8.07 2025-10-20 17:43:18 +03:00
Lauri Ojansivu 699b4c464f Updated translations. 2025-10-20 17:30:42 +03:00
Lauri Ojansivu 9fa54a3148 Updated ChangeLog. 2025-10-20 17:29:29 +03:00
Lauri Ojansivu f2019b1059 If Snap Candidate MongoDB raw database files were at SNAP_COMMON/wekan, migrate them back to SNAP_COMMON.
Thanks to xet7 !
2025-10-20 17:26:53 +03:00
Lauri Ojansivu 714bbd0fb0 Updated ChangeLog. 2025-10-20 17:10:30 +03:00
Lauri Ojansivu 80777b4663 When opening board, add missing lists.
Thanks to xet7 !

Fixes #5926
2025-10-20 17:06:42 +03:00
Lauri Ojansivu 9473c1fe41 Updated ChangeLog. 2025-10-20 16:50:35 +03:00
Lauri Ojansivu 98f141d62f Fix Snap Candidate WeKan 8.00-8.06 commit ae01ea5 database directory from /var/snap/wekan/common/wekan back to 8.07 /var/snap/wekan/common.
Thanks to xet7 !
2025-10-20 16:42:28 +03:00
Lauri Ojansivu 85dd213b14 Updated translations. 2025-10-20 06:21:02 +03:00
Lauri Ojansivu 3cf00911f7 v8.06 2025-10-20 03:29:24 +03:00
Lauri Ojansivu bddaad8346 Updated ChangeLog. 2025-10-20 03:13:32 +03:00
Lauri Ojansivu 5df4efd7ba Have all iPhone use mobile view by default, while still having possibility to use mobile/desktop switch button for desktop mode.
Thanks to xet7 !
2025-10-20 03:11:02 +03:00
Lauri Ojansivu 59df6aad05 Updated ChangeLog. 2025-10-20 03:00:21 +03:00
Lauri Ojansivu c4af4d03ac Some mobile view fixes.
Thanks to xet7 !
2025-10-20 02:58:30 +03:00
Lauri Ojansivu 62679819d9 Updated ChangeLog. 2025-10-20 02:32:08 +03:00
Lauri Ojansivu 46d46e313c Fix Bug Member settings drops to the second line and overlaps when many boards are starred as favourites.
Thanks to xet7 !

Fixes #5943
2025-10-20 02:30:03 +03:00
Lauri Ojansivu 27e9d3ce47 Updated ChangeLog. 2025-10-20 01:59:21 +03:00
Lauri Ojansivu b6b0c5fe6d Fix Bug: Scale of Minicard icons is linked to horizontal screensize.
Thanks to xet7 !

Fixes #5947
2025-10-20 01:56:54 +03:00
Lauri Ojansivu 87b934a955 Updated translations. 2025-10-20 01:52:37 +03:00
Lauri Ojansivu 8cc6e9b812 Updated translations. 2025-10-20 01:45:00 +03:00
Lauri Ojansivu b7da17ff31 Updated ChangeLog. 2025-10-20 01:39:29 +03:00
Lauri Ojansivu 2dd3916f7e Added Date Format setting to Opened Card.
Thanks to xet7 !

Fixes #2011,
fixes #1176
2025-10-20 01:36:44 +03:00
Lauri Ojansivu 516552cce6 Updated ChangeLog. 2025-10-20 01:23:43 +03:00
Lauri Ojansivu 2d44881619 Fix card popup to use HTML date, not anymore JQuery date.
Thanks to xet7 !
2025-10-20 01:21:54 +03:00
Lauri Ojansivu 0acbf30b03 Fix migrations.
Thanks to xet7 !
2025-10-20 01:20:28 +03:00
Lauri Ojansivu e61f6b1c89 Updated ChangeLog. 2025-10-20 01:03:34 +03:00
Lauri Ojansivu 973a49526f Fix Broken Hyperlinks in Markdown to HTML conversion.
Thanks to xet7 !

Fixes #5932
2025-10-20 01:01:55 +03:00
Lauri Ojansivu e1902d58c1 Updated ChangeLog. 2025-10-20 00:35:33 +03:00
Lauri Ojansivu 1e53125499 Fix opened card attachments button text to be at tooltip, not at opened card.
Thanks to xet7 !
2025-10-20 00:33:02 +03:00
Lauri Ojansivu 91fb7d9e70 Updated ChangeLog. 2025-10-20 00:29:56 +03:00
Lauri Ojansivu eb6b42c4c9 Fix syntax error at migrations.
Thanks to xet7 !
2025-10-20 00:28:19 +03:00
Lauri Ojansivu 679d210667 Updated ChangeLog. 2025-10-20 00:24:46 +03:00
Lauri Ojansivu 1e6252de7f When opening board, migrate from Shared Lists to Per-Swimlane Lists.
Thanks to xet7 !

Fixes #5952
2025-10-20 00:22:26 +03:00
Lauri Ojansivu 48b645ee1e Updated ChangeLog. 2025-10-19 23:48:15 +03:00
Lauri Ojansivu 951d2e4937 Legacy Lists button at one board view to restore missing lists/cards.
Thanks to xet7 !

Fixes #5952
2025-10-19 23:40:02 +03:00
Lauri Ojansivu 1658883b78 Updated ChangeLog. 2025-10-19 23:19:54 +03:00
Lauri Ojansivu 3514335247 At Public Board, drag resize list width and swimlane height. For logged in users, fix adding labels.
Thanks to xet7 !

Fixes #5922
2025-10-19 23:15:55 +03:00
Lauri Ojansivu 55bec31a3f Fix drag drop lists. Part 2.
Thanks to xet7 !

Fixes #5951
2025-10-19 22:22:21 +03:00
Lauri Ojansivu 0d36abee4e Updated ChangeLog. 2025-10-19 21:47:51 +03:00
Lauri Ojansivu caa6e615ff Removed extra pipe characters.
Thanks to xet7 !
2025-10-19 21:46:14 +03:00
Lauri Ojansivu cd3576b995 Updated ChangeLog. 2025-10-19 21:41:44 +03:00
Lauri Ojansivu 324f3f7794 Fix drag drop lists. Part 1.
Thanks to xet7 !

Related #5951
2025-10-19 21:38:55 +03:00
Lauri Ojansivu 3257110673 Updated ChangeLog. 2025-10-19 20:07:39 +03:00
Lauri Ojansivu 66b444e2b0 Fix unable to see My Due Cards.
Thanks to xet7 !

Fixes #5948
2025-10-19 20:05:36 +03:00
Lauri Ojansivu 23860b1ee8 Updated ChangeLog. 2025-10-19 18:58:36 +03:00
Lauri Ojansivu 101048339b Fix Due dates to be color coded and have icons. Part 2.
Thanks to xet7 !

Fixes #5950
2025-10-19 18:55:44 +03:00
Lauri Ojansivu dc78e3b7a0 Updated ChangeLog. 2025-10-19 18:45:32 +03:00
Lauri Ojansivu d965faa317 Fix Due dates to be color coded and have icons.
Thanks to xet7 !

Fixes #5950
2025-10-19 18:42:37 +03:00
Lauri Ojansivu 5d2bfab0f5 Updated ChangeLog. 2025-10-19 18:12:25 +03:00
Lauri Ojansivu 841a6eaf8c Merge branch 'helioguardabaxo-master' 2025-10-19 18:11:05 +03:00
Lauri Ojansivu db59bb4aa4 Merge branch 'master' of github.com:helioguardabaxo/wekan into helioguardabaxo-master 2025-10-19 18:01:30 +03:00
helioguardabaxo 61f7099106 Fix stared, archive and clone icons 2025-10-19 09:24:18 -03:00
Lauri Ojansivu ef828bdd38 Updated translations. 2025-10-19 14:19:51 +03:00
Lauri Ojansivu 1134b45b05 Updated ChangeLog. 2025-10-19 11:07:49 +03:00
Lauri Ojansivu b06daff4c7 Fix add and drag drop attachments to minicards and card.
Thanks to xet7 !

Fixes #5946,
fixes #5436,
fixes #2936,
fixes #1926,
fixes #300,
fixes #142
2025-10-19 10:57:36 +03:00
Lauri Ojansivu cea414b589 Updated translations. 2025-10-17 21:55:49 +03:00
Lauri Ojansivu b8942b728f v8.05 2025-10-17 17:53:00 +03:00
Lauri Ojansivu 8e6eabd9e8 Updated transations. 2025-10-17 17:38:54 +03:00
Lauri Ojansivu 290dd6c4d1 Updated ChangeLog. 2025-10-17 07:56:49 +03:00
Lauri Ojansivu 088bc16072 Font Awesome to Unicode icons. Part 4.
Thanks to xet7 !
2025-10-17 07:55:04 +03:00
Lauri Ojansivu daad2fbd71 Updated ChangeLog. 2025-10-17 07:10:07 +03:00
Lauri Ojansivu 3af94c2a90 Font Awesome to Unicode icons. Part 3.
Thanks to xet7 !
2025-10-17 07:08:01 +03:00
Lauri Ojansivu a3ca76d3c4 Updated ChangeLog. 2025-10-17 06:09:42 +03:00
Lauri Ojansivu 62ede48196 Removed not needed visible text from mobile desktop switch button.
Thanks to xet7 !
2025-10-17 06:07:24 +03:00
Lauri Ojansivu 390a86a7a7 Updated ChangeLog. 2025-10-17 06:02:46 +03:00
Lauri Ojansivu 09631d6b0c Resize height of swimlane by dragging. Font Awesome to Unicode icons.
Thanks to xet7 !
2025-10-17 05:58:53 +03:00
Lauri Ojansivu 2947238a02 Convert Font Awesome to Unicode Icons. Part 1. In Progress.
Thanks to xet7 !
2025-10-17 02:19:43 +03:00
Lauri Ojansivu a7af4b4809 Updated ChangeLog. 2025-10-17 00:27:30 +03:00
Lauri Ojansivu cb6afe67a7 Replaced moment.js with Javascript date.
Thanks to xet7 !
2025-10-17 00:26:11 +03:00
Lauri Ojansivu 8c5b43295d Updated ChangeLog. 2025-10-16 23:21:47 +03:00
Lauri Ojansivu 79b94824ef Changed wekan-boostrap-datepicker to HTML datepicker.
Thanks to xet7 !
2025-10-16 23:19:26 +03:00
Lauri Ojansivu 33e4b046e8 Updated ChangeLog. 2025-10-16 22:25:20 +03:00
Lauri Ojansivu 386aea7c78 Popup fixes. Part 2.
Thanks to xet7 !
2025-10-16 22:24:11 +03:00
Lauri Ojansivu 4a7bccd983 Updated ChangeLog. 2025-10-16 21:46:35 +03:00
Lauri Ojansivu 1f0cae9e76 Updated ChangeLog. 2025-10-16 21:42:41 +03:00
Lauri Ojansivu 87ae085e6d Fix Edit avatar UI issue.
Thanks to xet7 !

Fixes #5940
2025-10-16 21:40:49 +03:00
Lauri Ojansivu 640ac2330f Updated ChangeLog. 2025-10-16 20:24:46 +03:00
Lauri Ojansivu 2543df9425 Show original positions of swimlanes, lists and cards.
Thanks to xet7 !

Fixes #5939
2025-10-16 20:23:05 +03:00
Lauri Ojansivu 915ab47a72 v8.04 2025-10-16 17:59:24 +03:00
Lauri Ojansivu 09ff287da2 Updated translations. 2025-10-16 17:52:03 +03:00
Lauri Ojansivu 2896180f80 Updated ChangeLog. 2025-10-16 17:50:52 +03:00
Lauri Ojansivu 4283b5b0e3 Disable not working minio and s3 support temporarily.
Thanks to xet7 !
2025-10-16 17:49:39 +03:00
Lauri Ojansivu bbbd3abf06 Try to fix Broken Hyperlinks in Markdown to HTML conversion.
Thanks to xet7 !

Fixes #5932
2025-10-16 17:47:59 +03:00
Lauri Ojansivu dd88483ec7 Removed extra npm packages.
Thanks to xet7 !
2025-10-15 23:26:33 +03:00
Lauri Ojansivu 00ddec7575 Fix popups positioning. Part 2.
Thanks to xet7 !
2025-10-15 23:19:07 +03:00
Lauri Ojansivu ab0ebab240 Updated ChangeLog. 2025-10-15 07:57:18 +03:00
Lauri Ojansivu 79e83e33ec Use only MongoDB 7 at Snap.
Thanks to xet7 !
2025-10-15 07:56:11 +03:00
Lauri Ojansivu aa402d652d Updated ChangeLog. 2025-10-15 07:49:05 +03:00
Lauri Ojansivu 690481c138 Remove using fork with MongoDB at Snap.
Thanks to xet7 !
2025-10-15 07:47:57 +03:00
Lauri Ojansivu 881125aa98 Updated ChangeLog. 2025-10-15 07:46:54 +03:00
Lauri Ojansivu 77eea4d494 Fix popups positioning.
Thanks to xet7 !

Fixes #5924
2025-10-15 07:44:46 +03:00
Lauri Ojansivu b26e16abb8 Updated ChangeLog. 2025-10-15 07:39:18 +03:00
Lauri Ojansivu f08c7702ee Fix wide screen.
Thanks to xet7 !

Fixes #5915
2025-10-15 07:38:14 +03:00
Lauri Ojansivu a4399c7ef4 Updated translations. 2025-10-15 07:25:33 +03:00
Lauri Ojansivu d6e50ed9a0 Updated ChangeLog. 2025-10-15 07:21:15 +03:00
Lauri Ojansivu 6b848b318d Make sure that all cards are visible.
Thanks to xet7 !

Related #5915
2025-10-15 07:15:46 +03:00
Lauri Ojansivu 70ce70cf0e Try to fix Snap to not fork mongodb.
Thanks to xet7 !
2025-10-14 14:29:42 +03:00
Lauri Ojansivu 5a79bc5ee3 v8.03 2025-10-14 13:51:47 +03:00
Lauri Ojansivu 5792a86959 Fix Snap MongoDB to not fork at systemd, so it stays running.
Thanks to xet7 !
2025-10-14 13:45:51 +03:00
Lauri Ojansivu 37c5436087 v8.02 2025-10-14 11:58:14 +03:00
Lauri Ojansivu 6592102e8f v8.02 2025-10-14 11:56:11 +03:00
Lauri Ojansivu 06a5a8f70d Try to fix Docker secrets to be optional.
Thanks to xet7 !

Fixes #5920
2025-10-14 11:54:09 +03:00
Lauri Ojansivu ef54ebada6 Updated ChangeLog. 2025-10-14 11:25:36 +03:00
Lauri Ojansivu d4f13de1d9 Try to fix swimlane hamburger menu popup positioning. In progress.
Thanks to xet7 !
2025-10-14 11:24:22 +03:00
Lauri Ojansivu 4fcedde529 Updated ChangeLog. 2025-10-14 11:04:40 +03:00
Lauri Ojansivu a4518bbefc Fix drag drop reorder swimlanes.
Thanks to xet7 !
2025-10-14 11:03:36 +03:00
Lauri Ojansivu 95f771aa26 Updated ChangeLog. 2025-10-14 10:47:00 +03:00
Lauri Ojansivu da98942cce Updated mobile Bookmarks/Starred boards. Part 1. In Progress.
Thanks to xet7 !
2025-10-14 10:43:39 +03:00
Lauri Ojansivu f3efaf59e1 Updated translations. 2025-10-14 09:39:19 +03:00
Lauri Ojansivu d64032f2a3 Updated ChangeLog. 2025-10-14 09:37:49 +03:00
Lauri Ojansivu abad8cc4d5 Change list width by dragging between lists.
Thanks to xet7 !
2025-10-14 09:36:11 +03:00
Lauri Ojansivu 0d9536e2f9 Updated ChangeLog. 2025-10-14 08:26:46 +03:00
Lauri Ojansivu 67b078b805 Accessibility improvements.
Thanks to xet7 !
2025-10-14 08:25:39 +03:00
Lauri Ojansivu 6f02eeae53 Updated ChangeLog. 2025-10-14 07:24:46 +03:00
Lauri Ojansivu 5bc03b23ea Updated dependencies.
Thanks to developers of dependencies !
2025-10-14 07:23:13 +03:00
Lauri Ojansivu 3d4acd8c8f Updated translations. 2025-10-14 07:21:41 +03:00
Lauri Ojansivu 448bec8181 Updated ChangeLog. 2025-10-14 01:54:28 +03:00
Lauri Ojansivu 0a34ee1b64 Removed not needed console log message.
Thanks to xet7 !
2025-10-14 01:52:58 +03:00
Lauri Ojansivu 34e8e4d4c3 Updated translations. 2025-10-14 01:38:38 +03:00
Lauri Ojansivu 32627c03f4 Updated ChangeLog. 2025-10-14 01:32:43 +03:00
Lauri Ojansivu 63c314ca18 Fixed migrations.
Thanks to xet7 !
2025-10-14 01:30:59 +03:00
Lauri Ojansivu e8453783da Updated translations. 2025-10-13 23:48:30 +03:00
Lauri Ojansivu 96522ec3a3 Updated translations. 2025-10-13 23:38:17 +03:00
Lauri Ojansivu 17dedab391 Updated translations. 2025-10-13 23:37:02 +03:00
Lauri Ojansivu 283d2ee09c Updated translations. 2025-10-13 23:33:27 +03:00
Lauri Ojansivu 289ff0127e Updated translations. 2025-10-13 23:23:32 +03:00
Lauri Ojansivu 931d7217b1 Updated translations. 2025-10-13 23:04:24 +03:00
Lauri Ojansivu 3149a3927e Updated translations. 2025-10-13 22:25:41 +03:00
Lauri Ojansivu c57bced7b1 Updated translations. 2025-10-13 22:21:23 +03:00
Lauri Ojansivu 8d794a59dc Updated ChangeLog. 2025-10-13 22:19:56 +03:00
Lauri Ojansivu 7bb1e24bda Fixed Admin Panel Settings menus Attachments and Cron.
Thanks to xet7 !
2025-10-13 22:17:32 +03:00
Lauri Ojansivu e0013b9b63 Fix Admin Panel Settings menu to show options correctly. Part 1.
Thanks to xet7 !
2025-10-13 20:51:29 +03:00
Lauri Ojansivu 7d81aab900 Updated ChangeLog. 2025-10-13 20:35:49 +03:00
Lauri Ojansivu cc99da5357 Fixed Error in migrate-lists-to-per-swimlane migration.
Thanks to xet7 !

Fixes #5918
2025-10-13 20:34:23 +03:00
Lauri Ojansivu 9bd21e1d1b Updated ChangeLog. 2025-10-12 05:38:08 +03:00
Lauri Ojansivu 2148aeea42 Change Admin Panel "Attachment Settings" and "Cron Settings" options to be tabs, not submenu. Part 3.
Thanks to xet7 !
2025-10-12 05:36:51 +03:00
Lauri Ojansivu 6a7a5505f9 Updated ChangeLog. 2025-10-12 05:26:46 +03:00
Lauri Ojansivu 5a6faafa30 Change Admin Panel "Attachment Settings" and "Cron Settings" options to be tabs, not submenu. Part 2.
Thanks to xet7 !
2025-10-12 05:25:44 +03:00
Lauri Ojansivu e2f3dad779 Updated ChangeLog. 2025-10-12 04:52:45 +03:00
Lauri Ojansivu ae2aa1f5cd Change Admin Panel "Attachment Settings" and "Cron Settings" options to be tabs, not submenu.
Thanks to xet7 !
2025-10-12 04:50:17 +03:00
Lauri Ojansivu 0e3a17d922 Updated ChangeLog. 2025-10-12 04:40:27 +03:00
Lauri Ojansivu 033919a270 Fix Admin Panel menus "Attachment Settings" and "Cron Settings" and make them translateable.
Thanks to xet7 !
2025-10-12 04:39:04 +03:00
Lauri Ojansivu f5d40a0a12 Updated ChangeLog. 2025-10-12 04:22:35 +03:00
Lauri Ojansivu 0fd781e80a Fix opening sidebar.
Thanks to xet7 !
2025-10-12 04:21:38 +03:00
Lauri Ojansivu a8f6170fdf Updated ChangeLog. 2025-10-12 03:49:52 +03:00
Lauri Ojansivu bd8c565415 Fixes to make board showing correctly.
Thanks to xet7 !
2025-10-12 03:48:21 +03:00
Lauri Ojansivu ffb02fe0ec Updated translations. 2025-10-11 20:39:08 +03:00
Lauri Ojansivu 114520302c Updated ChangeLog. 2025-10-11 20:35:58 +03:00
Lauri Ojansivu 317138ab72 If there is no cron jobs running, run migrations for boards that have not been opened yet.
Thanks to xet7 !
2025-10-11 20:33:31 +03:00
Lauri Ojansivu a990109f43 Updated ChangeLog. 2025-10-11 19:43:20 +03:00
Lauri Ojansivu da68b01502 Added Cron Manager to Admin Panel for long running jobs, like running migrations when opening board, copying or moving boards swimlanes lists cards etc.
Thanks to xet7 !
2025-10-11 19:41:09 +03:00
Lauri Ojansivu e90bc744d9 Updated ChangeLog. 2025-10-11 19:26:07 +03:00
Lauri Ojansivu 2b5c56484a Run database migrations when opening board. Not when updating WeKan.
Thanks to xet7 !
2025-10-11 19:23:47 +03:00
Lauri Ojansivu ea30612013 v8.01 2025-10-11 12:32:38 +03:00
Lauri Ojansivu 4445612bb8 Updated translations. 2025-10-11 12:30:09 +03:00
Lauri Ojansivu da56393078 Updated ChangeLog. 2025-10-11 12:15:25 +03:00
Lauri Ojansivu a86ff1e8d0 Fixed attachments and minicard related bugs that prevented WeKan starting.
Thanks to xet7 !
2025-10-11 12:13:11 +03:00
Lauri Ojansivu 7fce5650b8 Updated ChangeLog. 2025-10-11 11:55:41 +03:00
Lauri Ojansivu b949357e66 Snap MongoDB Server 3-8 support. Part 1.
Thanks to xet7 !
2025-10-11 11:54:14 +03:00
Lauri Ojansivu 8e71b68dbf Updated ChangeLog. 2025-10-11 11:39:39 +03:00
Lauri Ojansivu b77eed221f Fix count of Orgs Teams People at Admin Panel.
Thanks to xet7 !
2025-10-11 11:38:28 +03:00
Lauri Ojansivu 1fa91474d0 Updated ChangeLog. 2025-10-11 11:34:45 +03:00
Lauri Ojansivu fc32a89292 Fixed per-card and per-board settings of showing checkist at minicard.
Thanks to xet7 !
2025-10-11 11:31:57 +03:00
Lauri Ojansivu ea24010298 Updated ChangeLog. 2025-10-11 11:08:27 +03:00
Lauri Ojansivu ae1f80a52c Added attachments API and admin panel attachment management for file storage backends settings. Fixed drag drop upload attachments from file manager to minicard or opened card.
Thanks to xet7 !
2025-10-11 11:05:46 +03:00
Lauri Ojansivu 2f95431c9b Updated ChangeLog. 2025-10-11 10:50:05 +03:00
Lauri Ojansivu d59683eff1 Fixed attachments migrations at Admin Panel to not use too much CPU while migrating attachments.
Thanks to xet7 !
2025-10-11 10:48:12 +03:00
Lauri Ojansivu de77776cd0 Updated ChangeLog. 2025-10-11 10:33:52 +03:00
Lauri Ojansivu 74ccfea570 Add support for MongoDB 3-8, detecting which one is in use.
Thanks to xet7 !
2025-10-11 10:32:20 +03:00
Lauri Ojansivu 1f5a549589 Updated ChangeLog. 2025-10-11 10:17:41 +03:00
Lauri Ojansivu 3ccdc2e307 Made possible to start WeKan immediately without running any database migrations.
Thanks to xet7 !
2025-10-11 10:15:08 +03:00
Lauri Ojansivu 688b725807 v8.00 2025-10-11 07:50:28 +03:00
Lauri Ojansivu ae01ea576c Snap: Migrate MongoDB from 3 to 7 only when "snap set wekan migrate-mongodb='true'". Not automatically.
Thanks to xet7 !
2025-10-11 07:42:51 +03:00
Lauri Ojansivu cd0cd64849 v7.99 2025-10-11 06:13:27 +03:00
Lauri Ojansivu aab671398c Improve automatic Snap upgrades.
Thanks to xet7 !
2025-10-11 05:52:41 +03:00
Lauri Ojansivu d64aeb25d5 Updated ChangeLog. 2025-10-11 04:57:24 +03:00
Lauri Ojansivu aa4fa83127 Updated ChangeLog. 2025-10-11 04:50:23 +03:00
Lauri Ojansivu 81c3dc1d95 Security Fix JVN#15385465: CWE-79 XSS, that affected WeKan 7.94.
Thanks to Sho Sugiyama and xet7 !
2025-10-11 04:47:17 +03:00
Lauri Ojansivu 746eecf3d8 Updated ChangeLog. 2025-10-11 04:25:26 +03:00
Lauri Ojansivu cd948fb576 Added missing metadata fields to snapcraft.yaml .
Thanks to xet7 !
2025-10-11 04:20:28 +03:00
Lauri Ojansivu 4ec4e19e63 Try to fix Snap automatic upgrade.
Thanks to xet7 !
2025-10-11 04:02:02 +03:00
Lauri Ojansivu d88d197de9 Updated release scripts of snapcraft pack command syntax.
Thanks to xet7 !
2025-10-11 03:11:32 +03:00
Lauri Ojansivu 2a24918a9c Snap: Removed double mongo3 that's already at migratemongo.
Thanks to xet7 !
2025-10-11 03:09:33 +03:00
Lauri Ojansivu d2a85b41e4 v7.98 2025-10-11 02:07:44 +03:00
Lauri Ojansivu 9bcd991a61 Updated ChangeLog. 2025-10-11 02:02:10 +03:00
Lauri Ojansivu 1a7bd65e59 Fixed showing translations always, regardsless of is ROOT_URL set correctly or not.
Thanks to xet7 !
2025-10-11 01:57:08 +03:00
Lauri Ojansivu f8ee929cf7 Updated ChangeLog. 2025-10-11 01:46:01 +03:00
Lauri Ojansivu 734165f3c7 Fix sizes of drag handles at desktop mode.
Thanks to xet7 !
2025-10-11 01:44:38 +03:00
Lauri Ojansivu 8f9ef1bde8 Updated ChangeLog. 2025-10-11 01:24:31 +03:00
Lauri Ojansivu 2119c6ab0c Fix DOMPurify paths. Part 4.
Thanks to xet7 !
2025-10-11 01:23:18 +03:00
Lauri Ojansivu a85479f73c Updated ChangeLog. 2025-10-11 01:09:58 +03:00
Lauri Ojansivu 21ba0a9606 Fix DOMPurify paths. Part 3.
Thanks to xet7 !
2025-10-11 01:08:39 +03:00
Lauri Ojansivu 82ba0f9593 Updated ChangeLog. 2025-10-11 01:03:22 +03:00
Lauri Ojansivu 7769124401 Fix DOMPurify paths. Part 2.
Thanks to xet7 !
2025-10-11 00:58:00 +03:00
Lauri Ojansivu d9c978e7fc Merge branch 'main' of github.com:wekan/wekan 2025-10-11 00:50:14 +03:00
Lauri Ojansivu 90899f0928 Fix DOMPurify paths.
Thanks to xet7 !
2025-10-11 00:49:43 +03:00
Lauri Ojansivu f0421da517 Updated translations. 2025-10-11 00:41:06 +03:00
Lauri Ojansivu 573d4bf2cb Updated ChangeLog. 2025-10-11 00:31:46 +03:00
Lauri Ojansivu f1e1fd3593 Add Snap automatic upgrades. Part 2.
Thanks to xet7 !
2025-10-11 00:29:46 +03:00
Lauri Ojansivu c387c5bc34 Updated ChangeLog. 2025-10-11 00:27:04 +03:00
Lauri Ojansivu 0549bc0b0c Add Snap automatic upgrades.
Thanks to xet7 !
2025-10-11 00:25:16 +03:00
Lauri Ojansivu c2a3e11324 Updated ChangeLog. 2025-10-10 23:50:49 +03:00
Lauri Ojansivu 107e2ac900 Add support for Docker/Compose Secrets for passwords to Docker/Snap/Bundle platforms.
Thanks to Roemer and xet7 !

Fixes #5724
2025-10-10 23:46:48 +03:00
Lauri Ojansivu 3b60bdea14 Updated ChangeLog. 2025-10-10 23:22:59 +03:00
Lauri Ojansivu f6591d7820 Security Fix usd-2022-0041: CWE-284 Improper Access Control.
Thanks to Christian Pöschl of usd AG and xet7 !
2025-10-10 23:19:58 +03:00
Lauri Ojansivu 6bbd622066 Updated ChangeLog. 2025-10-10 23:16:26 +03:00
Lauri Ojansivu ee79cab7b2 Security Fix JVN#86586539: Stored XSS.
Thanks to Ryoya Koyama of Mitsui Bussan Secure Directions, Inc and xet7.
2025-10-10 23:14:06 +03:00
Lauri Ojansivu a0b94065c5 Updated ChangeLog. 2025-10-10 23:09:28 +03:00
Lauri Ojansivu e1fa607f87 Security Fix JVN#74210258: Stored XSS.
Thanks to Ryoya Koyama of Mitsui Bussan Secure Directions, Inc and xet7 !
2025-10-10 23:06:06 +03:00
Lauri Ojansivu 2e91a359f5 Updated ChangeLog. 2025-10-10 23:02:37 +03:00
Lauri Ojansivu 9720e703fd Security Fix JVN#14269684: Broken access control.
Thanks to Ryoya Koyama of Mitsui Bussan Secure Directions, Inc and xet7 !
2025-10-10 22:59:20 +03:00
Lauri Ojansivu f88898d5b8 Updated ChangeLog. 2025-10-10 22:22:43 +03:00
Lauri Ojansivu 30c1597b65 Security Fix FG-VD-22-078: Prevent SVG Billion Laughs Attack.
Thanks to Nguyen Thanh Nguyen of Fortinet's FortiGuard Labs and xet7 !
2025-10-10 22:16:47 +03:00
Lauri Ojansivu 5bc5171220 Updated ChangeLog. 2025-10-10 22:12:25 +03:00
Lauri Ojansivu d0f118e7af Security Fix: Computational Resource Abuse in Export endpoints.
Thanks to Anynymous Security Researcher and xet7 !
2025-10-10 22:09:27 +03:00
Lauri Ojansivu c481443667 Updated ChangeLog. 2025-10-10 22:04:30 +03:00
Lauri Ojansivu b87cff1289 Security Fix: IDOR CWE-639 that affected WeKan 7.80-7.93.
Thanks to apitech.fr and xet7 !
2025-10-10 21:59:04 +03:00
Lauri Ojansivu 0c080830bc Updated ChangeLog. 2025-10-10 21:48:27 +03:00
Lauri Ojansivu cdd7d69c66 Drag any files from file manager to minicard or opened card. Part 2.
Thanks to xet7 !

Fixes #2936
2025-10-10 21:46:07 +03:00
Lauri Ojansivu c1cbcdcc72 Updated ChangeLog. 2025-10-10 21:21:12 +03:00
Lauri Ojansivu 1c84b19f24 Show console.log 'Legacy attachments route loaded' only when environment variable DEBUG=true.
Thanks to xet7 !
2025-10-10 21:19:00 +03:00
Lauri Ojansivu 2c73dae019 Merge branch 'main' of github.com:wekan/wekan 2025-10-10 21:16:29 +03:00
Lauri Ojansivu d285850a04 Updated ChangeLog. 2025-10-10 21:15:55 +03:00
Lauri Ojansivu 719ef87efc Make possible for lists to have different names at different swimlanes. Make possible to drag list from one swimlane to another swimlane.
Thanks to xet7 !
2025-10-10 21:14:44 +03:00
Lauri Ojansivu 039b9abf5e Updated translations 2025-10-10 20:37:22 +03:00
Lauri Ojansivu 39daf56811 Updated ChangeLog. 2025-10-10 19:12:09 +03:00
Lauri Ojansivu a8de2f224f Use attachments from old CollectionFS database structure, when not yet migrated to Meteor-Files/ostrio-files, without needing to migrate database structure.
Thanks to xet7 !
2025-10-10 19:07:04 +03:00
Lauri Ojansivu dda013844c Updated ChangeLog. 2025-10-10 18:55:13 +03:00
Lauri Ojansivu 3e9481c5bd Drag any files from file manager to minicard or opened card.
Thanks to xet7 !

Fixes #2936
2025-10-10 18:52:30 +03:00
Lauri Ojansivu 85ac03a892 Updated ChangeLog. 2025-10-09 05:54:04 +03:00
Lauri Ojansivu 752699d1c2 Mobile one board per row. Board zoom size percent. Board toggle mobile/desktop mode. In Progress.
Thanks to xet7 !

Related #5902
2025-10-09 05:48:41 +03:00
Lauri Ojansivu 339ca581ab Merge branch 'main' of github.com:wekan/wekan 2025-10-09 02:07:00 +03:00
Lauri Ojansivu 20e9cf1144 Text and icons from fixed sizes to scaleable sizes. Thanks to xet7. 2025-10-09 02:06:26 +03:00
Lauri Ojansivu 9737884c4b Text and icon from fixed sized to scaleable sizes. Thanks to xet7. 2025-10-09 02:06:16 +03:00
Lauri Ojansivu 2f5670e830 v7.97 2025-10-08 23:57:08 +03:00
Lauri Ojansivu 108b18cebf Updated ChangeLog. 2025-10-08 23:48:26 +03:00
Lauri Ojansivu 814ac22a0d Updated ChangeLog. 2025-10-08 23:43:55 +03:00
Lauri Ojansivu 3814a218c2 Removed white box that appeared when clicking something.
Thanks to xet7 !

Fixes #5899
2025-10-08 23:38:44 +03:00
Lauri Ojansivu 3fda90612d Some mobile fixes.
Thanks to xet7 !

Fixes #5899
2025-10-08 23:32:13 +03:00
Lauri Ojansivu 05762aa50c Updated ChangeLog. 2025-10-08 09:37:51 +03:00
Lauri Ojansivu 9a31371de0 Fixed translation of "Change Language" at login page.
Thanks to xet7 !
2025-10-08 09:36:39 +03:00
Lauri Ojansivu b9b7143185 Updated ChangeLog. 2025-10-08 09:21:26 +03:00
Lauri Ojansivu 972721bdb5 Merge branch 'seve12-main' 2025-10-08 09:17:53 +03:00
Lauri Ojansivu 3a48982916 v7.96 2025-10-08 08:44:09 +03:00
Lauri Ojansivu 6b3a6f9f7d Updated ChangeLog. 2025-10-08 08:44:09 +03:00
Lauri Ojansivu 48fddecb62 Updated to MongoDB 7.0.25 at Snap Candidate.
Thanks to MongoDB developers !
2025-10-08 08:44:09 +03:00
Lauri Ojansivu 7f0d1cc5b3 Updated ChangeLog. 2025-10-08 08:44:09 +03:00
Lauri Ojansivu 70e82be0b4 Hide extra keyboard shortcuts toggle. Thanks to xet7. 2025-10-08 08:44:09 +03:00
Lauri Ojansivu 5fc0809844 Updated ChangeLog. 2025-10-08 08:44:09 +03:00
Lauri Ojansivu c79990bcd9 Added missing screenshots from wekan.github.io history to wekan/docs/Features/ . Thanks to xet7 2025-10-08 08:44:09 +03:00
dependabot[bot] cd9e7412ea Bump docker/login-action from 3.5.0 to 3.6.0
Bumps [docker/login-action](https://github.com/docker/login-action) from 3.5.0 to 3.6.0.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/184bdaa0721073962dff0199f1fb9940f07167d1...5e57cd118135c172c3672efd75eb46360885c0ef)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-version: 3.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-08 08:44:09 +03:00
Lauri Ojansivu 3e472606b5 Fixed links at docs. Thanks to xet7 2025-10-08 08:44:08 +03:00
Lauri Ojansivu 2966bc3204 Updated translations 2025-10-08 08:44:08 +03:00
Lauri Ojansivu 5dc6093eec Updated email address 2025-10-08 08:44:08 +03:00
Lauri Ojansivu 86fc60f5ba Updated ChangeLog. 2025-10-08 08:44:08 +03:00
Lauri Ojansivu 8189a3f9ab Fixed find.sh script errors about unmatched single quotes and binary files that are now ignored.
Thanks to xet7 !
2025-10-08 08:44:08 +03:00
Lauri Ojansivu 76a65e2533 Updated ChangeLog. 2025-10-08 08:44:08 +03:00
Jean-Sébastien Guay ad2914934e Fix links in docker-compose.yml to point to doc in repo 2025-10-08 08:44:08 +03:00
Lauri Ojansivu 2769ba2874 Updated ChangeLog. 2025-10-08 08:44:08 +03:00
Lauri Ojansivu 294f1e178e Updated ChangeLog. 2025-10-08 08:44:08 +03:00
Lauri Ojansivu c994d34bb8 Updated to MongoDB 7.0.24 at Snap Candidate.
Thanks to MongoDB developers !
2025-10-08 08:44:08 +03:00
Lauri Ojansivu 42b5dd0751 Updated ChangeLog. 2025-10-08 08:44:08 +03:00
Lauri Ojansivu f884d6d471 Added missing mobile drag drop image to docs from wekan.github.io git history.
Thanks to xet7 !
2025-10-08 08:44:08 +03:00
Lauri Ojansivu abe915a8d6 Updated ChangeLog. 2025-10-08 08:44:08 +03:00
Lauri Ojansivu a2e8de2fd8 Updated release script for new command of uploading snap package to Snap Store.
Thanks to xet7 !
2025-10-08 08:44:08 +03:00
Lauri Ojansivu e34ca6a8a5 v7.95 2025-10-08 08:44:08 +03:00
Lauri Ojansivu 596c9de603 Updated translations 2025-10-08 08:44:08 +03:00
Lauri Ojansivu 4170361a1c Updated ChangeLog. 2025-10-08 08:44:08 +03:00
Lauri Ojansivu 978a36ea77 Updated dependencies.
Thanks to developers of dependencies !
2025-10-08 08:44:08 +03:00
Lauri Ojansivu 8745ef7de8 Reorganized docs 2025-10-08 08:44:08 +03:00
Lauri Ojansivu 2ce929b7b3 Updated ChangeLog. 2025-10-08 08:44:08 +03:00
Lauri Ojansivu 2dcdfa2862 Added PGP public key for sending encrypted email.
Thanks to xet7 !
2025-10-08 08:44:08 +03:00
Lauri Ojansivu b18daac9c9 Updated ChangeLog. 2025-10-08 08:44:08 +03:00
sowwos 606041a00d docs(windows): clarify WRITABLE_PATH/attachments + LAN bind instructions (nits) 2025-10-08 08:44:08 +03:00
Lauri Ojansivu 4ed5952a81 Updates 2025-10-08 08:44:08 +03:00
Lauri Ojansivu 37bfce2eb7 Added missing image to WeKan Multiverse page.
Thanks to xet7 !
2025-10-08 08:44:08 +03:00
Lauri Ojansivu 59541699ce Updated ChangeLog. 2025-10-08 08:44:08 +03:00
Lauri Ojansivu af73c16e37 More info about ToroDB, FerretDB, Turso, TigerBeetle, PostgreSQL, SQLite etc.
Thanks to xet7 !
2025-10-08 08:44:07 +03:00
Lauri Ojansivu 673b09b84b Updated ChangeLog. 2025-10-08 08:44:07 +03:00
Lauri Ojansivu 09f8edfead Fixed image link at docs.
Thanks to xet7 !
2025-10-08 08:44:07 +03:00
Lauri Ojansivu e1ebdeca4f Updated ChangeLog. 2025-10-08 08:44:07 +03:00
Lauri Ojansivu 31769aa4af Updated upgrade link.
Thanks to xet7 !
2025-10-08 08:44:07 +03:00
seve12 b32ad86ce7 fixed issue 5887 2025-10-07 22:23:19 +03:00
Lauri Ojansivu 7350473a65 v7.96 2025-10-07 01:14:42 +03:00
Lauri Ojansivu 1c87e337d6 Updated ChangeLog. 2025-10-07 01:06:21 +03:00
Lauri Ojansivu dec038e7ed Updated to MongoDB 7.0.25 at Snap Candidate.
Thanks to MongoDB developers !
2025-10-07 01:05:01 +03:00
Lauri Ojansivu ebe6f7813e Updated ChangeLog. 2025-10-07 00:45:05 +03:00
Lauri Ojansivu 706dbfacac Merge branch 'seve12-main' 2025-10-07 00:36:50 +03:00
Lauri Ojansivu 62ad588aa9 Hide extra keyboard shortcuts toggle. Thanks to xet7. 2025-10-07 00:35:17 +03:00
Lauri Ojansivu 3c776e2ab6 Merge branch 'main' of github.com:seve12/wekan into seve12-main 2025-10-07 00:19:46 +03:00
seve12 ab4e2d1ed6 fixed issue #5885 2025-10-06 16:34:49 +03:00
Lauri Ojansivu 5e90c4a142 Updated ChangeLog. 2025-10-02 20:15:35 +03:00
Lauri Ojansivu 9a7fa25ca3 Added missing screenshots from wekan.github.io history to wekan/docs/Features/ . Thanks to xet7 2025-10-02 20:05:38 +03:00
Lauri Ojansivu 65cb0641f3 Merge branch 'main' of github.com:wekan/wekan 2025-10-02 19:57:56 +03:00
Lauri Ojansivu 6f2b891214 Fixed links at docs. Thanks to xet7 2025-10-02 19:57:28 +03:00
Lauri Ojansivu 44d7ab83ec Merge pull request #5890 from wekan/dependabot/github_actions/docker/login-action-3.6.0
Bump docker/login-action from 3.5.0 to 3.6.0
2025-10-02 00:06:19 +03:00
dependabot[bot] b328cc3f6a Bump docker/login-action from 3.5.0 to 3.6.0
Bumps [docker/login-action](https://github.com/docker/login-action) from 3.5.0 to 3.6.0.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/184bdaa0721073962dff0199f1fb9940f07167d1...5e57cd118135c172c3672efd75eb46360885c0ef)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-version: 3.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-29 23:33:41 +00:00
seve12 836226a8d3 RICHER_CARD_COMMENT_EDITOR=true has no effect (no rich text toolbar shown) fixed! 2025-09-29 15:56:23 +03:00
Lauri Ojansivu 2083f6630a Updated translations 2025-09-28 00:42:45 +03:00
Lauri Ojansivu 25e1e9028f Updated email address 2025-09-28 00:38:04 +03:00
seve12 a5651c686f fix 22.9 Unable to download large PDF files attached to card issue fixed 2025-09-23 13:40:14 +03:00
Lauri Ojansivu 93f0e0b125 Updated ChangeLog. 2025-09-21 15:01:17 +03:00
Lauri Ojansivu 730bb6d166 Fixed find.sh script errors about unmatched single quotes and binary files that are now ignored.
Thanks to xet7 !
2025-09-21 14:59:00 +03:00
Lauri Ojansivu 855bac59bf Updated ChangeLog. 2025-09-19 23:46:32 +03:00
Lauri Ojansivu 870900b92a Merge pull request #5882 from Skylark13/main
Fix links in docker-compose.yml to point to doc in repo
2025-09-19 23:45:09 +03:00
Jean-Sébastien Guay a1d5802d36 Fix links in docker-compose.yml to point to doc in repo 2025-09-19 15:26:53 -04:00
Lauri Ojansivu 6e16fa3992 Updated ChangeLog. 2025-09-19 16:35:59 +03:00
Lauri Ojansivu ee3b49ce59 Updated ChangeLog. 2025-09-19 03:08:40 +03:00
Lauri Ojansivu 3253dd5010 Updated to MongoDB 7.0.24 at Snap Candidate.
Thanks to MongoDB developers !
2025-09-19 03:06:06 +03:00
Lauri Ojansivu a10d8e0438 Updated ChangeLog. 2025-09-19 01:56:18 +03:00
Lauri Ojansivu 5151814e98 Added missing mobile drag drop image to docs from wekan.github.io git history.
Thanks to xet7 !
2025-09-19 01:54:01 +03:00
Lauri Ojansivu 2e2a0f01f4 Updated ChangeLog. 2025-09-18 23:11:33 +03:00
Lauri Ojansivu dec9b82da0 Updated release script for new command of uploading snap package to Snap Store.
Thanks to xet7 !
2025-09-18 23:09:03 +03:00
Lauri Ojansivu 33f4db5871 v7.95 2025-09-18 21:59:50 +03:00
Lauri Ojansivu 9a086c0397 Updated translations 2025-09-18 21:43:38 +03:00
Lauri Ojansivu 013ddb8978 Updated ChangeLog. 2025-09-18 21:34:15 +03:00
Lauri Ojansivu c4161c5ce6 Updated dependencies.
Thanks to developers of dependencies !
2025-09-18 21:29:36 +03:00
seve12 392b8dd5b2 fix 17.9.2025 2025-09-17 22:34:46 +03:00
seve12 d19eb801f3 fixes 16.9.2025 2025-09-16 23:05:35 +03:00
seve12 ed416ca555 fixes 25.9 2025-09-15 19:31:18 +03:00
seve12 106009316f more fixes 2025-09-10 17:17:51 +03:00
seve12 cb10ad3a20 Fixed 9.2.4.6, 9.1.3.5 and 11.7 2025-09-10 13:04:37 +03:00
Lauri Ojansivu 2fad0099e4 Reorganized docs 2025-08-27 16:11:15 +03:00
Lauri Ojansivu 454416b0bd Updated ChangeLog. 2025-08-26 15:15:44 +03:00
Lauri Ojansivu dd80b7308b Added PGP public key for sending encrypted email.
Thanks to xet7 !
2025-08-26 15:14:19 +03:00
Lauri Ojansivu fe5c5f1bdc Updated ChangeLog. 2025-08-21 13:11:51 +03:00
Lauri Ojansivu 79074deb34 Merge pull request #5872 from sowwos/docs/windows-offline-clarify
This adds two actionable Windows notes to docs/Platforms/Propietary/Windows/Offline.md:  - Explain that WRITABLE_PATH and ATTACHMENTS_STORE_PATH must be set before start to avoid the attachments error   (“The 'path' argument must be of type string. Received undefined” from models/attachments.js). - Show how to expose the dev server on LAN on Windows (BIND_IP=0.0.0.0 + ROOT_URL=http://<LAN-IP>:4000).  Tested on Windows 11 with Meteor dev server. Follow-up to #5871 (closed as wrong file).
2025-08-21 13:07:59 +03:00
sowwos db0294c95e docs(windows): clarify WRITABLE_PATH/attachments + LAN bind instructions (nits) 2025-08-21 07:19:13 +03:00
Lauri Ojansivu 8f0fa88eac Updates 2025-08-20 03:20:49 +03:00
Lauri Ojansivu 946f7045e9 Added missing image to WeKan Multiverse page.
Thanks to xet7 !
2025-08-20 03:19:26 +03:00
Lauri Ojansivu dda8c90d44 Updated ChangeLog. 2025-08-20 02:26:20 +03:00
Lauri Ojansivu 701485d76a More info about ToroDB, FerretDB, Turso, TigerBeetle, PostgreSQL, SQLite etc.
Thanks to xet7 !
2025-08-20 02:24:38 +03:00
Lauri Ojansivu b25198b87e Updated ChangeLog. 2025-08-20 00:58:58 +03:00
Lauri Ojansivu ad7e3774c2 Fixed image link at docs.
Thanks to xet7 !
2025-08-20 00:57:40 +03:00
Lauri Ojansivu ee67dea96c Updated ChangeLog. 2025-08-20 00:43:26 +03:00
Lauri Ojansivu b3d3bf1593 Updated upgrade link.
Thanks to xet7 !
2025-08-20 00:41:22 +03:00
Lauri Ojansivu 53726fed24 Updated WeKan version to Dockerfile.
Thanks to xet7 !
2025-08-18 19:01:19 +03:00
Lauri Ojansivu 7d2ca2c811 v7.94 2025-08-18 18:54:35 +03:00
Lauri Ojansivu c0d10a0ec3 Updated ChangeLog. 2025-08-18 18:35:09 +03:00
Lauri Ojansivu 8e01e3c762 Upgraded to MongoDB 7.0.23 at Snap Candidate.
Thanks to MongoDB developers !
2025-08-18 18:30:21 +03:00
Lauri Ojansivu 04be599893 Updates 2025-08-18 18:15:51 +03:00
Lauri Ojansivu f6cb7fddf1 Updated ChangeLog. 2025-08-13 05:34:28 +03:00
Lauri Ojansivu a662283b7d Updates to design docs. 2025-08-13 05:32:26 +03:00
Lauri Ojansivu f34490fe28 Reorganized Design Docs 2025-08-13 05:30:35 +03:00
Lauri Ojansivu 1a98c86a37 Updated ChangeLog. 2025-08-12 16:55:06 +03:00
Lauri Ojansivu 97b5931a4f Updated dependencies.
Thanks to developers of dependencies !
2025-08-12 16:53:34 +03:00
Lauri Ojansivu 2a4dd9b693 Updated ChangeLog. 2025-08-12 16:04:41 +03:00
Lauri Ojansivu ecf31c3675 Updated MongoDB to 7.0.22.
Thanks to MongoDB developers !
2025-08-12 16:03:28 +03:00
Lauri Ojansivu 9b0d0a2b7c Updated ChangeLog. 2025-08-12 15:59:18 +03:00
Lauri Ojansivu e2ffca2e39 Merge pull request #5865 from wekan/dependabot/github_actions/actions/download-artifact-5
Bump actions/download-artifact from 4 to 5
2025-08-12 15:58:29 +03:00
Lauri Ojansivu a8435879cf Updated translations 2025-08-12 15:56:52 +03:00
Lauri Ojansivu e16695585a Updated ChangeLog. 2025-08-12 15:03:27 +03:00
Lauri Ojansivu a7bcfc6db7 Added docs for Windows SSL/TLS. Part 6.
Thanks to xet7 !

Related #5866
2025-08-12 15:01:56 +03:00
Lauri Ojansivu 21bed98d69 Updated ChangeLog. 2025-08-12 15:00:23 +03:00
Lauri Ojansivu 044793cc44 Added docs for Windows SSL/TLS. Part 5.
Thanks to xet7 !

Related #5866
2025-08-12 14:59:20 +03:00
Lauri Ojansivu 083a7050b9 Updated ChangeLog. 2025-08-12 14:55:13 +03:00
Lauri Ojansivu cc78e19740 Added docs for Windows SSL/TLS. Part 4.
Thanks to xet7 !

Related #5866
2025-08-12 14:53:09 +03:00
Lauri Ojansivu ee01a07b0a Added docs for Windows SSL/TLS. Part 3.
Thanks to xet7 !

Related #5866
2025-08-12 14:44:34 +03:00
Lauri Ojansivu ce528b1826 Added docs for Windows SSL/TLS. Part 2.
Thanks to xet7 !

Related #5866
2025-08-12 14:40:33 +03:00
Lauri Ojansivu 92c0543614 Added docs for Windows SSL/TLS.
Thanks to xet7 !

Related #5866
2025-08-12 14:33:55 +03:00
dependabot[bot] b6638f4a29 Bump actions/download-artifact from 4 to 5
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4 to 5.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-12 07:34:46 +00:00
Lauri Ojansivu 96be56a620 Merge pull request #5864 from wekan/dependabot/github_actions/actions/checkout-5
Bump actions/checkout from 4 to 5
2025-08-12 10:33:51 +03:00
dependabot[bot] 2e555e420a Bump actions/checkout from 4 to 5
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-12 03:02:57 +00:00
Lauri Ojansivu a849951c53 Update LXD docs 2025-08-09 23:03:09 +03:00
Lauri Ojansivu 435cb04717 Update PWA docs 2025-08-09 21:14:30 +03:00
Lauri Ojansivu 25a375261e Move docs PWA to Browsers directory 2025-08-09 21:09:27 +03:00
Lauri Ojansivu 1a1a44616f Revert accessibility changes temporarily.
Thanks to xet7 !

Related https://github.com/wekan/wekan/pull/5857
2025-08-09 14:23:40 +03:00
Lauri Ojansivu e6347a305c Updated ChangeLog. 2025-08-09 12:00:39 +03:00
Lauri Ojansivu b137108ae1 Fixed uppercase K of WeKan.
Thanks to xet7 !

Related https://github.com/wekan/wekan/pull/5857
2025-08-09 11:55:50 +03:00
Lauri Ojansivu 4ce7ff7cef Merge pull request #5857 from seve12/main
Accessibility:
- Added product name to page titles, settings and global search.
- More accessible header and layout templates, modal dialogs, DOM structure, color contrast.
2025-08-09 11:33:50 +03:00
seve12 184415a026 Enhance color contrast and accessibility for various UI elements 2025-08-09 04:29:30 +03:00
Lauri Ojansivu 3977adcb8f Updated translations 2025-08-08 12:38:15 +03:00
Lauri Ojansivu 1c47f98f59 Feature: Added brute force login protection settings to Admin Panel/People/Locked Users. Part 2.
Thanks to xet7 !
2025-08-08 12:36:17 +03:00
seve12 9f0c37352e Enhance accessibility for modal dialogs and improve DOM structure 2025-08-07 23:59:34 +03:00
seve12 7a20fa4805 Merge branch 'main' of https://github.com/seve12/wekan 2025-08-06 19:29:25 +03:00
seve12 6a713d9e8d Refactor header and layout templates for improved accessibility and consistency 2025-08-06 19:18:01 +03:00
Lauri Ojansivu 51b0544752 Updated translations. 2025-08-06 08:17:02 +03:00
seve12 1022a7aa3a Update settingBody.js 2025-08-05 20:23:52 +03:00
seve12 c048678844 Update router.js 2025-08-05 20:22:09 +03:00
seve12 f9fb02457c Merge branch 'main' of https://github.com/seve12/wekan 2025-08-05 20:07:50 +03:00
seve12 ce419ce64e Merge pull request #1 from seve12/severi
Improves accessibility by disabling custom Tab handling
2025-08-05 20:05:39 +03:00
severi12345 ecb6ff57fc Update page titles to include product name in settings and global search 2025-08-05 19:59:33 +03:00
Lauri Ojansivu 663866562f Updated ChangeLog. 2025-08-05 12:57:00 +03:00
Lauri Ojansivu dfdd0b6cbb Added info about Sandstorm Radicale Calendar and Contacts.
Thanks to xet7 !
2025-08-05 12:55:29 +03:00
Lauri Ojansivu c81ca82822 Updated translations. 2025-08-05 12:02:36 +03:00
Lauri Ojansivu 66b067eb49 Updated translations. 2025-08-05 11:26:23 +03:00
Lauri Ojansivu 4e9f2dc46a Updated ChangeLog. 2025-08-05 11:24:34 +03:00
Lauri Ojansivu f02d8b61b5 Merge pull request #5856 from wekan/dependabot/github_actions/docker/login-action-3.5.0
Bump docker/login-action from 3.4.0 to 3.5.0
2025-08-05 11:18:16 +03:00
Lauri Ojansivu 088de1877c Merge pull request #5855 from wekan/dependabot/github_actions/docker/metadata-action-5.8.0
Bump docker/metadata-action from 5.7.0 to 5.8.0
2025-08-05 11:17:59 +03:00
dependabot[bot] 4611abffed Bump docker/login-action from 3.4.0 to 3.5.0
Bumps [docker/login-action](https://github.com/docker/login-action) from 3.4.0 to 3.5.0.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/74a5d142397b4f367a81961eba4e8cd7edddf772...184bdaa0721073962dff0199f1fb9940f07167d1)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-version: 3.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-05 03:24:07 +00:00
dependabot[bot] f206f2f45c Bump docker/metadata-action from 5.7.0 to 5.8.0
Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 5.7.0 to 5.8.0.
- [Release notes](https://github.com/docker/metadata-action/releases)
- [Commits](https://github.com/docker/metadata-action/compare/902fa8ec7d6ecbf8d84d538b9b233a880e428804...c1e51972afc2121e065aed6d45c65596fe445f3f)

---
updated-dependencies:
- dependency-name: docker/metadata-action
  dependency-version: 5.8.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-05 03:17:28 +00:00
Lauri Ojansivu 9c5f5757be Updated translations. 2025-08-05 00:50:29 +03:00
Lauri Ojansivu ec2e0f6fed Updated ChangeLog. 2025-08-05 00:33:46 +03:00
Lauri Ojansivu ae0d059b6f Feature: Added brute force login protection settings to Admin Panel/People/Locked Users.
Added filtering of Admin Panel/People/People: All Users/Locked Users Only/Active/Not Active.
Added visual indicators: red lock icon for locked users, green check for active users, and red X for inactive users.
Added "Unlock All" button to quickly unlock all brute force locked users.
Added ability to toggle user active status directly from the People page.
Moved lockout settings from environment variables to database so admins can configure the lockout thresholds directly in the UI.

Thanks to xet7.
2025-08-05 00:31:43 +03:00
Lauri Ojansivu 0132b801b2 Updated translations. 2025-08-04 21:34:33 +03:00
Lauri Ojansivu 06ca564ec2 Updated ChangeLog. 2025-08-04 21:26:51 +03:00
Lauri Ojansivu d83ce5e633 Feature: Accessibility page at /accessibility. Settings at Admin Panel. When enabled, link at right sidebar.
Thanks to xet7 !
2025-08-04 21:22:14 +03:00
Lauri Ojansivu 5287319fde Updated translations. 2025-08-04 19:53:31 +03:00
Lauri Ojansivu c75a23e693 Updated ChangeLog. 2025-08-04 19:49:59 +03:00
Lauri Ojansivu 180bf930c1 Updated translations. 2025-08-04 19:46:24 +03:00
Lauri Ojansivu c16e99ba1e Added FerretDB/PostgreSQL to changelog. 2025-08-04 19:21:16 +03:00
Lauri Ojansivu b9887fc917 Fixed link at OpenShift docs.
Thanks to xet7 !

Related #5854
2025-08-04 19:17:17 +03:00
Lauri Ojansivu 96b36bd8ef Updated ChangeLog. 2025-08-04 18:24:27 +03:00
Lauri Ojansivu 0e32f666eb Comment out not used Tab code at accessibility related changes.
Thanks to xet7 !

Related https://github.com/wekan/wekan/pull/5853
2025-08-04 18:17:24 +03:00
Lauri Ojansivu 1341fc6a98 Merge pull request #5853 from seve12/severi
Improves accessibility by disabling custom Tab handling
2025-08-04 18:12:46 +03:00
seve12 81c9e96bf3 Improves accessibility by disabling custom Tab handling
Comments out custom Tab key behavior to allow default browser focus movement, enhancing accessibility for keyboard users and assistive technologies.
2025-08-04 17:45:53 +03:00
Lauri Ojansivu 20f91ef893 Updated ChangeLog. 2025-08-01 02:29:49 +03:00
Lauri Ojansivu 1187254573 Updated security email address.
Thanks to xet7 !
2025-08-01 02:28:15 +03:00
Lauri Ojansivu f22fb4f4a9 Updated translations 2025-07-28 23:28:36 +03:00
Lauri Ojansivu a14762e005 Updated ChangeLog. 2025-07-19 20:16:31 +03:00
Lauri Ojansivu 9a91214043 Update CHANGELOG.md 2025-07-19 05:14:56 +03:00
Lauri Ojansivu 30795a46cf Update CHANGELOG.md 2025-07-19 05:10:55 +03:00
Lauri Ojansivu b0865c8d06 Update CHANGELOG.md 2025-07-19 05:03:37 +03:00
Lauri Ojansivu c2aabbc6db Update CHANGELOG.md 2025-07-19 05:02:01 +03:00
Lauri Ojansivu 4aad3dafd4 Update CHANGELOG.md 2025-07-19 04:58:25 +03:00
Lauri Ojansivu 62269a9902 Update CHANGELOG.md 2025-07-19 04:54:59 +03:00
Lauri Ojansivu 4bc2fd4a7f Update CHANGELOG.md 2025-07-19 04:52:48 +03:00
Lauri Ojansivu 0e43a08689 Update CHANGELOG.md platforms info at top. Thanks to xet7! 2025-07-19 04:51:15 +03:00
Lauri Ojansivu 77b1416413 Updated ChangeLog. 2025-07-19 01:29:59 +03:00
Lauri Ojansivu 200545822c Fixes for building at Mac M1 Air.
Thanks to xet7 !
2025-07-18 22:31:17 +03:00
Lauri Ojansivu ac33f2164f Removed not needed files. 2025-07-18 19:04:25 +03:00
Lauri Ojansivu e82ca8a1f4 Updated ChangeLog. 2025-07-18 15:19:57 +03:00
Lauri Ojansivu f894f9ef6d Fixed building Docker image. Pushed new Docker image to Docker Hub, Quay and GitHub.
Thanks to xet7 !

Fixes #5835
2025-07-18 15:17:36 +03:00
Lauri Ojansivu ccfd8ef566 Updated ChangeLog. 2025-07-18 08:55:51 +03:00
Lauri Ojansivu 6c6b66c802 Updated release website script.
Thanks to xet7 !
2025-07-18 08:53:41 +03:00
Lauri Ojansivu 2cb2e7cb10 v7.93 2025-07-18 08:30:28 +03:00
Lauri Ojansivu 8744f47a0e Updated ChangeLog. 2025-07-17 01:25:27 +03:00
Lauri Ojansivu b742b25f96 Merge pull request #5832 from Atry/edit_list
Add REST API endpoint to edit a list with various properties
2025-07-17 01:21:06 +03:00
Yang, Bo 6d7c20f397 Add API endpoint to edit a list with various properties 2025-07-16 22:09:58 +00:00
Lauri Ojansivu f3d384c1dd Updated ChangeLog. 2025-07-17 00:52:34 +03:00
Lauri Ojansivu 69ce79a7b9 Fixed Snap local build script.
Thanks to xet7 !
2025-07-17 00:50:18 +03:00
Lauri Ojansivu 1cd191f76b Updated ChangeLog. 2025-07-16 01:10:59 +03:00
Lauri Ojansivu afc3c29341 Merge pull request #5829 from Atry/get_card_by_id
Avoid calling `ReactiveCache.getCard` twice
2025-07-16 01:08:27 +03:00
Lauri Ojansivu fedb3040d1 Updated ChangeLog. 2025-07-16 00:59:16 +03:00
Yang, Bo 0a969c59d3 Avoid calling ReactiveCache.getCard twice 2025-07-15 14:58:21 -07:00
Lauri Ojansivu d9fb9f49fd Merge pull request #5828 from Atry/get-card-by-id
Add a REST API endpoint to retrieve a card by ID
2025-07-16 00:53:42 +03:00
Yang, Bo fc77d0d201 Add REST API endpoint to retrieve a card by ID 2025-07-15 21:40:43 +00:00
Lauri Ojansivu 56700d1d06 Updated ChangeLog. 2025-07-10 05:22:38 +03:00
Lauri Ojansivu 84259c5116 Merge pull request #5826 from Atry/rest-api-custom-field-return-type
fix: correct return type in API documentation for custom fields
2025-07-09 19:14:13 +03:00
Yang, Bo 6cc746df8b fix: correct return type in API documentation for custom fields 2025-07-09 15:43:20 +00:00
Lauri Ojansivu b3ad06692f Updated ChangeLog. 2025-07-08 14:18:54 +03:00
Lauri Ojansivu bb0814bb11 Update Python.md 2025-07-08 14:16:49 +03:00
Lauri Ojansivu 722faf8aaa Updated ChangeLog. 2025-07-07 23:06:17 +03:00
Lauri Ojansivu 159324858b Fixed release script of building Snap locally at Ubuntu.
Thanks to xet7 !
2025-07-07 23:03:36 +03:00
Lauri Ojansivu f73a370719 Updated ChangeLog. 2025-07-07 22:42:12 +03:00
Lauri Ojansivu dfc658b910 Added release script to switch between using kvm, snapcraft.io/multipass, waydroid and virtualbox.
Thanks to xet7 !
2025-07-07 22:41:15 +03:00
Lauri Ojansivu 0a52f470b7 Updated ChangeLog. 2025-07-07 22:19:44 +03:00
Lauri Ojansivu e4822d8bc4 Updated issue template about pull requests for fixes to docs.
Thanks to xet7 !
2025-07-07 22:17:57 +03:00
Lauri Ojansivu 9d1cab3ac2 Updated ChangeLog. 2025-07-07 22:15:18 +03:00
Lauri Ojansivu 6d16ec238a Updated image links at Azure docs 2025-07-07 20:29:48 +03:00
Lauri Ojansivu e0284f225b Updated Keycloak link 2025-07-07 20:25:59 +03:00
Lauri Ojansivu a5acd0801a Updated Azure link 2025-07-07 20:24:34 +03:00
Lauri Ojansivu d59337ae47 Updated docs 2025-07-07 19:44:24 +03:00
Lauri Ojansivu 2f27a3f839 Updated docs about migrating manually. Part 2. 2025-07-07 19:27:54 +03:00
Lauri Ojansivu 450b33f98a Updated docs about migrating manually 2025-07-07 19:26:43 +03:00
Lauri Ojansivu 9adf5027fa Updated docs about importing from Asana 2025-07-07 19:21:27 +03:00
Lauri Ojansivu 1610b84845 Updated docs about importing from Trello. Part 3. 2025-07-07 19:18:29 +03:00
Lauri Ojansivu 40b2cf88fc Updated docs about importing from Trello. Part 2. 2025-07-07 19:17:41 +03:00
Lauri Ojansivu 55ef6c0c56 Updated docs about importing from Trello 2025-07-07 19:15:51 +03:00
Lauri Ojansivu a9c67ac200 Fix docs link about CSV. Part 2. 2025-07-07 19:09:57 +03:00
Lauri Ojansivu 3892be47ac Fix docs link about CSV 2025-07-07 19:07:48 +03:00
Lauri Ojansivu a2654b2e29 Removed not working link from docs 2025-07-07 19:05:49 +03:00
Lauri Ojansivu a052cc8fa5 Updated docs about Kubernetes 2025-07-07 19:04:05 +03:00
Lauri Ojansivu 5d09100cb6 Update info about hashing 2025-07-07 19:00:10 +03:00
Lauri Ojansivu 76769d1b85 Fixed not working link 2025-07-07 18:56:34 +03:00
Lauri Ojansivu 2cf57250e8 Fix link to image at docs 2025-07-07 18:54:56 +03:00
Lauri Ojansivu 5d0a0c931f Removed old link Test Edge from docs index. 2025-07-07 18:50:56 +03:00
Lauri Ojansivu fa1adfec76 Formatting 2025-07-07 18:43:26 +03:00
Lauri Ojansivu 9271b07383 Fix link to image about PWA app icon install.
Thanks to stevenroose and xet7 !

Fixes #5824
2025-07-07 18:40:52 +03:00
Lauri Ojansivu 4fe955650a Added most often used links 2025-07-07 18:36:39 +03:00
Lauri Ojansivu 6b401000b9 Updated Snap platforms setting. 2025-07-05 20:11:13 +03:00
Lauri Ojansivu aa681cde88 Updated building Snap 2025-07-05 16:59:33 +03:00
Lauri Ojansivu 0921c6ea53 Updated ChangeLog. 2025-07-05 16:09:33 +03:00
Lauri Ojansivu 2df73bedee Updated rebuild-wekan.sh install deps for macOS. Part 2. 2025-07-05 15:30:12 +03:00
Lauri Ojansivu 938fe49b2c Update rebuild-wekan.sh install deps for macOS 2025-07-05 15:27:11 +03:00
Lauri Ojansivu 7e2e649a8e Updated ChangeLog. 2025-07-05 14:16:02 +03:00
Lauri Ojansivu 6e11aa27f8 Reorganized docs to fix building WeKan 2025-07-04 04:08:41 +03:00
Lauri Ojansivu 57ee624398 Updated issue template. 2025-07-04 03:18:44 +03:00
Lauri Ojansivu 3cba95b60a Updated issue template. 2025-07-01 19:08:43 +03:00
Lauri Ojansivu bd796ce642 Updated links at readme 2025-06-28 14:22:01 +03:00
Lauri Ojansivu 3a8473e632 Fix image link 2025-06-28 14:10:41 +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 c5e5bb613e Moved images from wekan.github.io to docs 2025-06-28 11:50:55 +03:00
Lauri Ojansivu b52d1ca6c4 wekan.team to wekan.fi 2025-06-28 10:59:38 +03:00
Lauri Ojansivu 790bafbb09 wekan.team to wekan.fi 2025-06-27 21:37:17 +03:00
Lauri Ojansivu c256ce6968 Update Browser-compatibility-matrix.md about Servo 2025-06-26 20:29:48 +03:00
Lauri Ojansivu 85465e0dfb Updated ChangeLog. 2025-06-26 05:45:11 +03:00
Lauri Ojansivu b7409c1d0b Removed old broken snap files.
Thanks to xet7 !
2025-06-26 05:43:30 +03:00
Lauri Ojansivu 4060d33bbc Updated ChangeLog. 2025-06-26 05:42:34 +03:00
Lauri Ojansivu 6526d99123 Upgraded Snap Candidate to have base core24, MongoDB 7 and Caddy 2.
Thanks to xet7 !
2025-06-26 05:39:37 +03:00
Lauri Ojansivu 1b44c848ad Updated ChangeLog. 2025-06-26 03:47:57 +03:00
Lauri Ojansivu e6980be061 Updated dependencies.
Thanks to developers of dependencies !
2025-06-26 03:46:39 +03:00
Lauri Ojansivu 81c77650b4 Updated translations. 2025-06-26 03:08:38 +03:00
Lauri Ojansivu 791e37dd9c Updated ChangeLog. 2025-06-26 03:01:59 +03:00
Lauri Ojansivu f926448bbb Added docs about using MongoDB at unsupported CPUs with Qemu.
Thanks to xet7 !

Related #4321,
related #5814
2025-06-26 03:00:04 +03:00
Lauri Ojansivu 7128f067d0 Updated ChangeLog. 2025-06-26 02:48:12 +03:00
Lauri Ojansivu 59ab6c1cd8 Snap Candidate: If CPU does not support AVX, use Qemu that supports AVX, so that MongoDB 6 works. Part 3.
Thanks to xet7 !

Fixes #4321
2025-06-26 02:41:11 +03:00
Lauri Ojansivu 89105b6e72 Updated ChangeLog. 2025-06-26 01:20:41 +03:00
Lauri Ojansivu 51e43f1c25 Specify meteor version, otherwise the latest meteor version is not compatible with node14. Part 2.
Thanks to xet7 !
2025-06-26 01:19:06 +03:00
Lauri Ojansivu 9b24d644a4 Merge pull request #5815 from Atry/board-rest-api-doc
feat: enhance API documentation and add board-related endpoints
2025-06-26 01:09:42 +03:00
Lauri Ojansivu 2549e0e4c8 Merge pull request #5816 from dassio/main
specify meteor version, otherwise the lastes meteor version is not compatible with node14
2025-06-26 01:08:43 +03:00
xiangbin.li d974fa638c specify meteor version, otherwise the lastes version is not compatiable with node_14 2025-06-24 03:36:20 +00:00
Yang, Bo f399b5119f feat: enhance API documentation and add board-related endpoints
Previously, boards.js contains a `?.` operator, which is not supported by esprima, therefore boards.js is skipped.

This PR fixes the API generation for borads.js by removing `?.`.

See https://github.com/jquery/esprima/issues/2112
2025-06-23 20:01:38 +00:00
Lauri Ojansivu 12d3fabdb7 Updates 2025-06-18 17:12:53 +03:00
Lauri Ojansivu d5727f5b15 Updated github issue template 2025-06-18 17:08:49 +03:00
Lauri Ojansivu 1908d04fd4 Updates 2025-06-18 17:04:59 +03:00
Lauri Ojansivu c7f8c828cb Updates 2025-06-18 17:01:38 +03:00
Lauri Ojansivu 054d0913fd Updates 2025-06-18 16:56:37 +03:00
Lauri Ojansivu ca7dc69c85 Fix links and add more info 2025-06-18 16:53:29 +03:00
Lauri Ojansivu b2ff51d99c Updates 2025-06-18 16:48:33 +03:00
Lauri Ojansivu abd4b6bd87 Updates 2025-06-18 16:45:04 +03:00
Lauri Ojansivu b92d07c9a6 Updated translations 2025-06-18 11:30:37 +03:00
Lauri Ojansivu c6204417a3 Updated translations 2025-06-18 11:27:23 +03:00
Lauri Ojansivu 3a23401443 Updated ChangeLog. 2025-06-18 11:07:15 +03:00
Lauri Ojansivu 12e6e46924 Update Browser-compatibility-matrix.md 2025-06-17 00:19:33 +03:00
Lauri Ojansivu be1b959519 Update Browser-compatibility-matrix.md 2025-06-17 00:18:04 +03:00
Lauri Ojansivu 7841c9e801 Update Browser-compatibility-matrix.md 2025-06-17 00:15:23 +03:00
Lauri Ojansivu bc486e2a7d Updated ChangeLog. 2025-06-16 14:58:15 +03:00
Lauri Ojansivu d01885e2ad Updated ChangeLog. 2025-06-16 14:47:50 +03:00
Lauri Ojansivu 358ffe0601 Merge pull request #5805 from walster001/main
CleanDark Theme Fixes
2025-06-16 14:45:19 +03:00
walster001 8d0bf1b758 Fix invisible scrollbar on CleanDark cards
Found that scrollbars were blending in with the card background largely due to transparency on the main board.
2025-06-16 00:39:10 +09:30
walster001 c7961cf2f4 Fix spacing 2025-06-16 00:22:20 +09:30
walster001 2ac374e578 Fix font colour contrast CleanDark
Fixes the visibility of card titles when card is opened for CleanDark theme as was inheriting black title from CleanLight theme. 
Title now is white to match and heavily contrast theme.
2025-06-16 00:18:50 +09:30
Lauri Ojansivu ad3306e19e Update Browser-compatibility-matrix.md 2025-06-14 22:37:10 +03:00
Lauri Ojansivu f54cbac370 Updated docs 2025-06-13 23:05:01 +03:00
Lauri Ojansivu 4bfc0140be Updated docs links 2025-06-13 22:36:59 +03:00
Lauri Ojansivu cc7459b983 Updated docs: Fixed links 2025-06-13 22:15:36 +03:00
Lauri Ojansivu 11415cd517 Updated Docs: Updated deep dive link 2025-06-13 21:45:38 +03:00
Lauri Ojansivu 9aa028f32d Updates 2025-06-13 21:39:46 +03:00
Lauri Ojansivu 2a415cbd26 Updated docs: Moved content from Home.md to README.md 2025-06-13 21:38:12 +03:00
Lauri Ojansivu 40a52da2d3 Updated docs: Removed footer 2025-06-13 21:34:45 +03:00
Lauri Ojansivu 31e043c1d7 Updated docs 2025-06-13 21:32:36 +03:00
Lauri Ojansivu 985afafed0 Snap Candidate back to MongoDB 6.0.24 2025-06-12 21:22:16 +03:00
Lauri Ojansivu 3a4d20ecb2 Updates 2025-06-12 20:28:52 +03:00
Lauri Ojansivu 35b524f179 Updated ChangeLog. 2025-06-12 20:15:19 +03:00
Lauri Ojansivu d2f17ef140 Upgrade to MongoDB 7.0.21 2025-06-12 20:13:49 +03:00
Lauri Ojansivu d99864ca95 v7.92 2025-06-12 19:30:59 +03:00
Lauri Ojansivu e6e1191f8a Updated ChangeLog. 2025-06-12 15:50:06 +03:00
Lauri Ojansivu 1f10c20874 Updated dependencies.
Thanks to developers of dependencies !
2025-06-12 15:48:55 +03:00
Lauri Ojansivu c699f7a940 Updated ChangeLog. 2025-06-12 15:47:50 +03:00
Lauri Ojansivu 7115c292c7 Updated dependencies.
Thanks to developers of dependencies !
2025-06-12 15:46:44 +03:00
Lauri Ojansivu 7744b14b48 Updated ChangeLog. 2025-06-12 15:44:50 +03:00
Lauri Ojansivu 6fed9e73d8 Merge pull request #5801 from Atry/fix-login-openapi
fix: properly document `/users/login`
2025-06-12 15:43:09 +03:00
Yang, Bo e14a3f9c2d fix: properly document /users/login 2025-06-12 02:18:19 +00:00
Lauri Ojansivu 7bf75b1ff7 Updated ChangeLog. 2025-06-11 19:54:05 +03:00
Lauri Ojansivu 6905e24199 Updated to MongoDB 6.0.24 at Snap Candidate.
Thanks to MongoDB developers!
2025-06-11 19:52:13 +03:00
Lauri Ojansivu 9eb25c09da Updated ChangeLog. 2025-06-06 03:58:51 +03:00
Lauri Ojansivu c97f5ffca3 Updated ChangeLog. 2025-06-03 01:11:26 +03:00
Lauri Ojansivu eb19168b5f Merge pull request #5795 from wekan/dependabot/github_actions/docker/build-push-action-6.18.0
Bump docker/build-push-action from 6.17.0 to 6.18.0
2025-06-03 01:09:31 +03:00
dependabot[bot] e421b68766 Bump docker/build-push-action from 6.17.0 to 6.18.0
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.17.0 to 6.18.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/1dc73863535b631f98b2378be8619f83b136f4a0...263435318d21b8e681c14492fe198d362a7d2c83)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-version: 6.18.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-02 20:16:16 +00:00
Lauri Ojansivu 6b86625c56 Updated ChangeLog. 2025-06-02 19:15:46 +03:00
Lauri Ojansivu 254b6dd5c7 Merge pull request #5793 from walster001/main
Allow selection of card text via webkit/user-select attributes
2025-06-02 19:13:18 +03:00
walster001 7b545d12c7 Allow selection of card text via webkit/user-select attributes
Fixes open issue around webkit/user-select attributes. Modifies values to allow "text" values to be selected in cards and on the board.
2025-06-02 22:03:16 +09:30
Lauri Ojansivu 01ed20b400 v7.91 2025-05-25 01:05:10 +03:00
Lauri Ojansivu fad930833a Updated translations. 2025-05-25 00:31:15 +03:00
Lauri Ojansivu 9192fbee44 Updated ChangeLog. 2025-05-25 00:30:11 +03:00
Lauri Ojansivu aaa5f9885d Reverted https://github.com/wekan/wekan/pull/5774 about due date changes.
Thanks to xet7 !

Fixes #5782
2025-05-25 00:22:47 +03:00
Lauri Ojansivu 1979b1692d Reverted date format changes.
Thanks to xet7 !
2025-05-25 00:11:09 +03:00
valhalla-creator fc31eeca88 Update cardDate.js 2025-05-22 21:20:20 +01:00
valhalla-creator 6d4de76a9a Update datepicker.js 2025-05-22 21:19:39 +01:00
valhalla-creator 9245ba4a5b Update utils.js 2025-05-22 21:18:52 +01:00
valhalla-creator 973a788c21 Update datepicker.js 2025-05-22 21:13:22 +01:00
valhalla-creator 0d5df8d3cf Update cardDate.js 2025-05-22 21:12:44 +01:00
valhalla-creator b67a236657 Update utils.js 2025-05-22 21:11:28 +01:00
Lauri Ojansivu 170617d457 Merge pull request #5780 from wekan/dependabot/github_actions/docker/build-push-action-6.17.0
Bump docker/build-push-action from 6.16.0 to 6.17.0
2025-05-19 23:12:48 +03:00
dependabot[bot] cc2f359b6c Bump docker/build-push-action from 6.16.0 to 6.17.0
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.16.0 to 6.17.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/14487ce63c7a62a4a324b0bfb37086795e31c6c1...1dc73863535b631f98b2378be8619f83b136f4a0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-version: 6.17.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-19 20:08:52 +00:00
Lauri Ojansivu c0d44a8686 v7.90 2025-05-14 03:05:46 +03:00
Lauri Ojansivu a1700f38b1 Updated ChangeLog. 2025-05-14 01:14:08 +03:00
Lauri Ojansivu b82ba63532 Fix Lines in multiline markdown code block to not anymore have darker background.
Thanks to mueller-ma and xet7 !

Fixes #5689
2025-05-14 01:11:54 +03:00
Lauri Ojansivu dbd1324a90 Updated ChangeLog. 2025-05-14 00:15:37 +03:00
Lauri Ojansivu e52158b729 Changed start.bat default ROOT_URL=http://localhost , although it only works for local user.
Thanks to xet7 !
2025-05-14 00:13:52 +03:00
Lauri Ojansivu 68a717cf47 Updated ChangeLog. 2025-05-13 23:48:45 +03:00
Lauri Ojansivu c0a9780f80 Added missing quotes to Fix Due Date Problem in Non-English Numbers.
Thanks to xet7 !

Fixes https://github.com/wekan/wekan/pull/5774,
fixes #5752
2025-05-13 23:43:08 +03:00
Lauri Ojansivu 0520129a40 Updated ChangeLog. 2025-05-13 23:36:32 +03:00
Lauri Ojansivu 6b36e126ab Merge pull request #5774 from valhalla-creator/bugfix/fix-due-date
Bugfix/fix due date
2025-05-13 23:31:33 +03:00
valhalla-creator 3cb4662363 Update datepicker.js 2025-05-13 21:26:34 +01:00
valhalla-creator cef14f301f Update utils.js 2025-05-13 21:21:48 +01:00
valhalla-creator eca4a0c440 Update cardDate.js 2025-05-13 21:19:59 +01:00
Lauri Ojansivu cea04b2ada Updated ChangeLog. 2025-05-13 23:18:13 +03:00
Lauri Ojansivu b7d9af9ae7 v7.89 2025-05-13 22:45:44 +03:00
Lauri Ojansivu 0046b3c802 Merge pull request #5772 from oussama-madimagh/fix/css-compatibility
Fix: Add vendor prefixes for user-select and text-size-adjust for better browser compatibility
2025-05-13 22:37:32 +03:00
oussama-madimagh 25384c8ac3 Fix: Add vendor prefixes for user-select and text-size-adjust for better browser compatibility 2025-05-13 20:33:29 +01:00
Lauri Ojansivu 2eee72da8a Updated ChangeLog. 2025-05-13 22:00:50 +03:00
Lauri Ojansivu d65c422e3f Merge pull request #5769 from Adamsss001/feature/email-i18n
Add email notifications language localization feature
2025-05-13 21:50:53 +03:00
Omar Abid 2ab9bd3172 Add email notifications language localization feature 2025-05-13 19:45:08 +01:00
Lauri Ojansivu a0b5c2e88c Updated ChangeLog. 2025-05-13 19:42:41 +03:00
Lauri Ojansivu 0bd4108661 Upgraded to MongoDB 6.0.23 at Snap Candidate.
Thanks to MongoDB developers !
2025-05-13 19:37:42 +03:00
Lauri Ojansivu 7d1a1475ba Add missing code of fix insertion order of list.
Thanks to xet7 !

Fixes https://github.com/wekan/wekan/pull/5767,
fixes #5732
2025-05-13 18:54:14 +03:00
Lauri Ojansivu 6418263165 Merge branch 'correct-list-position' of github.com:valhalla-creator/wekan 2025-05-13 18:30:59 +03:00
Lauri Ojansivu b32c835cf9 Updated license 2025-05-13 17:00:54 +03:00
valhalla-creator a927685bc6 Update swimlanes.js 2025-05-12 22:33:42 +01:00
Lauri Ojansivu 6827d40e84 Updated ChangeLog. 2025-05-12 22:26:33 +03:00
Lauri Ojansivu 9aa12b0eb6 Updated Caddy docs.
Thanks to xet7 !
2025-05-12 22:25:18 +03:00
Lauri Ojansivu 8ca1a36d78 Updated ChangeLog. 2025-05-12 21:57:43 +03:00
Lauri Ojansivu 394f2eae69 Updated Browser Compatibility Matrix.
Thanks to xet7 !
2025-05-12 21:56:11 +03:00
Lauri Ojansivu 7588459c3e Updated translations. 2025-05-12 21:43:02 +03:00
Lauri Ojansivu 2c33f45a6a Updated translations. 2025-04-29 05:47:20 +03:00
Lauri Ojansivu 43cf0b66ac Merge pull request #5754 from wekan/dependabot/github_actions/docker/build-push-action-6.16.0
Bump docker/build-push-action from 6.15.0 to 6.16.0
2025-04-29 04:59:27 +03:00
dependabot[bot] c05f6fcff2 Bump docker/build-push-action from 6.15.0 to 6.16.0
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.15.0 to 6.16.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/471d1dc4e07e5cdedd4c2171150001c434f0b7a4...14487ce63c7a62a4a324b0bfb37086795e31c6c1)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-version: 6.16.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-28 21:07:25 +00:00
Lauri Ojansivu a4eb32158b Updated ChangeLog. 2025-04-25 23:33:40 +03:00
Lauri Ojansivu 072ced9d0e Added script to build snap locally and push to snapcraft.io candidate and edge.
Thanks to xet7 !
2025-04-25 23:30:57 +03:00
Lauri Ojansivu c5fefc1414 v7.88 2025-04-25 22:12:37 +03:00
Lauri Ojansivu c825895ceb Reverted translation fix that sometimes did not work.
Thanks to xet7 !

Fixes #5751,
related https://github.com/wekan/wekan/pull/5748
2025-04-25 22:06:06 +03:00
Lauri Ojansivu 9488972ed8 Merge newest changes. 2025-04-25 21:07:22 +03:00
Lauri Ojansivu 1f4bed38f7 Merge pull request #5750 from walster001/main
Fix more contrasting issues
2025-04-25 21:02:50 +03:00
Lauri Ojansivu 747112957a v7.87 2025-04-25 20:59:20 +03:00
Lauri Ojansivu 18228096f9 Updated ChangeLog. 2025-04-25 20:56:07 +03:00
Lauri Ojansivu d0ea5bf500 Fix typos.
Thanks to xet7 !
2025-04-25 20:54:02 +03:00
walster001 74fe91fa3a Fix more contrasting issues
- Fix contrast issue with CleanDark & CleanLight buttons on theme selection menu.
2025-04-26 01:40:15 +09:30
Lauri Ojansivu d4e3075fcc v7.86 2025-04-25 19:00:08 +03:00
Lauri Ojansivu 413ae5f20e Updated translations. 2025-04-25 18:38:13 +03:00
Lauri Ojansivu e23f86f542 Updated ChangeLog. 2025-04-25 18:35:12 +03:00
Lauri Ojansivu b3c47dbe84 Merge pull request #5749 from walster001/main
Fix contrast issue checkbox
2025-04-25 18:32:28 +03:00
Lauri Ojansivu b10c2b9a15 Updated ChangeLog. 2025-04-25 18:31:35 +03:00
Lauri Ojansivu bb6910acd1 Merge branch 'nourguidara-fix-i18n-init-timing' 2025-04-25 18:29:43 +03:00
Lauri Ojansivu 76b000820e Updated ChangeLog. 2025-04-25 18:28:23 +03:00
Lauri Ojansivu 50b874629c Merge branch 'Rayene123-add-toggle-watch-icon-on-card' 2025-04-25 18:25:44 +03:00
Lauri Ojansivu 4d145ab4ad Added translations to questions about are you sure.
Thanks to xet7 !

Related https://github.com/wekan/wekan/pull/5745
2025-04-25 18:24:40 +03:00
Lauri Ojansivu 4d67a4641f Merge branch 'fix-i18n-init-timing' of github.com:nourguidara/wekan into nourguidara-fix-i18n-init-timing 2025-04-25 17:54:41 +03:00
Lauri Ojansivu bc2eab6f9d Merge branch 'add-toggle-watch-icon-on-card' of github.com:Rayene123/wekan into Rayene123-add-toggle-watch-icon-on-card 2025-04-25 17:49:14 +03:00
walster001 3ccce75863 Fix contrast issue checkbox
- Fixed contrasting issue with tick check on custom fields, also fixed the ugly green tick coming up on the checklists in cards by utilisation of the !important tag to override it with the checkbox colour for custom fields also.
2025-04-25 20:15:34 +09:30
nour guidara b390b266e2 bug fix in imports\i18n\index.js 2025-04-25 11:10:46 +01:00
Lauri Ojansivu 561af8926c Merge branch 'main' of github.com:wekan/wekan 2025-04-24 22:20:06 +03:00
Lauri Ojansivu 5717dc2cf4 Updated ChangeLog. 2025-04-24 22:19:45 +03:00
Lauri Ojansivu d0e64ad6d2 Merge pull request #5746 from nourguidara/nourguidara-patch-1
Improve impersonate user documentation in docs/Admin/Impersonate-user.md
2025-04-24 22:18:07 +03:00
Lauri Ojansivu 8e1a735ec7 Updates 2025-04-24 22:17:47 +03:00
nour guidara 1301d67295 Update Impersonate-user.md
Restored direct link to the original pull request (#3280) as requested
2025-04-24 20:14:41 +01:00
nour guidara 7c239f5218 Update Impersonate-user.md 2025-04-24 19:45:29 +01:00
Rayene123 61235c953c added a watch icon button when clicked toggle if a card is watched or not 2025-04-24 11:53:08 +01:00
Lauri Ojansivu ea65a0308d Merge pull request #5741 from Firas-Git/documentation/login-and-signup
Documentation/login and signup
2025-04-21 22:57:08 +03:00
Firas Saidi f840b07c02 documentation/login-and-signup 2025-04-21 20:50:23 +01:00
Lauri Ojansivu 8af719d39e v7.85 2025-04-21 16:30:58 +03:00
Lauri Ojansivu c1a4250bd2 Updated to MongoDB 6.0.22.
Thanks to MongoDB developers !
2025-04-21 16:28:41 +03:00
Lauri Ojansivu b4b442f8a8 Updated translations. 2025-04-21 16:13:56 +03:00
Lauri Ojansivu b7e76fcfa1 Updated ChangeLog. 2025-04-21 16:10:24 +03:00
Lauri Ojansivu 01950cc796 Fix board sidebar menu doesn't open in mobile.
Thanks to mimZD and xet7 !

Fixes #5736
2025-04-21 16:07:43 +03:00
Firas Saidi 14f2022e21 fix-5434 2025-04-10 11:16:38 +01:00
Lauri Ojansivu 23bac73559 Updated translations. 2025-04-05 17:39:29 +03:00
Lauri Ojansivu 2de9b94b01 Updated ChangeLog. 2025-03-31 21:50:47 +03:00
Lauri Ojansivu 05d1736f5f Update Browser-compatibility-matrix.md 2025-03-31 21:45:22 +03:00
Lauri Ojansivu db5346fc5c Update Browser-compatibility-matrix.md 2025-03-31 21:38:06 +03:00
Lauri Ojansivu f09f5bd737 Updated translations. 2025-03-29 21:35:45 +02:00
Lauri Ojansivu d96a1a5c9e v7.84 2025-03-23 17:33:57 +02:00
Lauri Ojansivu ad3cc9c087 Updated translations. 2025-03-22 21:39:15 +02:00
Lauri Ojansivu d0c7bf65a4 Updated ChangeLog. 2025-03-22 21:30:53 +02:00
Lauri Ojansivu bb6ac70f63 Updated dependencies.
Thanks to developers of dependencies !
2025-03-22 21:26:41 +02:00
Lauri Ojansivu c38b9da7d6 Merge pull request #5718 from wekan/dependabot/github_actions/docker/login-action-3.4.0
Bump docker/login-action from 3.3.0 to 3.4.0
2025-03-18 01:37:59 +02:00
dependabot[bot] 19153ca489 Bump docker/login-action from 3.3.0 to 3.4.0
Bumps [docker/login-action](https://github.com/docker/login-action) from 3.3.0 to 3.4.0.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/9780b0c442fbb1117ed29e0efdff1e18412f7567...74a5d142397b4f367a81961eba4e8cd7edddf772)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-17 21:00:44 +00:00
Lauri Ojansivu 8e7a5e8cb5 Updated translations. 2025-03-15 22:31:43 +02:00
Lauri Ojansivu 1407059937 Updated ChangeLog. 2025-03-15 19:33:49 +02:00
Lauri Ojansivu a2911bc9c3 Fixed building OpenAPI docs.
Thanks to xet7 !

Fixes #5512
2025-03-15 19:32:16 +02:00
Lauri Ojansivu f99b735746 Updated ChangeLog. 2025-03-11 04:23:55 +02:00
Lauri Ojansivu 4510ddda15 Fix typo at Shortcuts.
Thanks to xet7 !
2025-03-11 04:19:45 +02:00
Lauri Ojansivu 028ec46f46 Updated ChangeLog. 2025-03-09 20:05:08 +02:00
Lauri Ojansivu c0e4e01deb Added developer docs about Login code.
Thanks to xet7 !

Fixes #5714
2025-03-09 20:03:42 +02:00
Lauri Ojansivu cfcf682134 Updated ChangeLog. 2025-03-09 19:08:20 +02:00
Lauri Ojansivu 27bd9817d7 Merge pull request #5712 from Rayene123/Time-on-Minicard-for-Start/End/Due-Date
Time on minicard for start/end/due date
2025-03-09 19:03:54 +02:00
Firas Saidi dc389a68ab bug_fixing 2025-03-09 16:23:37 +01:00
Rayene123 6d982cdfd9 formatted the date when storing it 2025-03-09 14:15:17 +00:00
Rayene123 827ee615c1 added the start , due , end hour 2025-03-09 14:12:20 +00:00
Lauri Ojansivu f5f0ba721e v7.83 2025-03-08 21:32:37 +02:00
Lauri Ojansivu 1344f85986 Updated ChangeLog. 2025-03-08 21:14:12 +02:00
Lauri Ojansivu e7462ada12 Reverted Docker changes of WeKan v7.68 to get WeKan Docker version working.
Thanks to xet7 !

Fixes #5697
2025-03-08 21:09:09 +02:00
Lauri Ojansivu 11199b6225 Updated ChangeLog. 2025-03-08 19:58:05 +02:00
Lauri Ojansivu 345aece0f2 Updated ChangeLog. 2025-03-08 19:55:49 +02:00
Lauri Ojansivu 912c84422b Merge branch 'Firas-Git-CopyCard_FixError' 2025-03-08 19:52:42 +02:00
Lauri Ojansivu b402676079 Removed siteurl from manifest to fix browser console error about not matching URL.
Thanks to xet7 !
2025-03-08 19:51:23 +02:00
Lauri Ojansivu 908a5fc60d Comment out error message of non-existing Custom Field.
Thanks to xet7 !

Related #5684
2025-03-08 19:50:00 +02:00
Lauri Ojansivu 38e57d3635 Merge branch 'CopyCard_FixError' of github.com:Firas-Git/wekan into Firas-Git-CopyCard_FixError 2025-03-08 19:46:19 +02:00
Firas Saidi 14167b19b9 cards file 2025-03-08 18:02:03 +01:00
Lauri Ojansivu fc548b426d Updated translations. 2025-03-08 17:45:02 +02:00
Zegden Rayen 094f13934c added the confirm pop up before cloning or archiving a board 2025-03-08 15:38:22 +00:00
Lauri Ojansivu 71acd9603e Updated ChangeLog. 2025-03-06 01:52:46 +02:00
Lauri Ojansivu 341f655ab7 Merge pull request #5704 from wekan/dependabot/github_actions/docker/build-push-action-6.15.0
Bump docker/build-push-action from 6.14.0 to 6.15.0
2025-03-05 19:33:04 +02:00
Lauri Ojansivu 0c5323c106 Merge pull request #5703 from wekan/dependabot/github_actions/docker/metadata-action-5.7.0
Bump docker/metadata-action from 5.6.1 to 5.7.0
2025-03-05 19:30:27 +02:00
dependabot[bot] 7f871fdf30 Bump docker/build-push-action from 6.14.0 to 6.15.0
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.14.0 to 6.15.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/0adf9959216b96bec444f325f1e493d4aa344497...471d1dc4e07e5cdedd4c2171150001c434f0b7a4)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-03 20:41:23 +00:00
dependabot[bot] 6347b40a02 Bump docker/metadata-action from 5.6.1 to 5.7.0
Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 5.6.1 to 5.7.0.
- [Release notes](https://github.com/docker/metadata-action/releases)
- [Commits](https://github.com/docker/metadata-action/compare/369eb591f429131d6889c46b94e711f089e6ca96...902fa8ec7d6ecbf8d84d538b9b233a880e428804)

---
updated-dependencies:
- dependency-name: docker/metadata-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-03 20:41:20 +00:00
Lauri Ojansivu bce84a432a Updated ChangeLog. 2025-03-02 12:03:22 +02:00
Lauri Ojansivu b6fc4deb63 Merge pull request #5702 from adam-fu/main
Adding Development information to readme
2025-03-02 12:00:54 +02:00
adam-fu 6b48f9e259 Adding Development information to readme 2025-03-02 06:43:29 +01:00
Lauri Ojansivu 44b7666426 Merge branch 'main' of github.com:wekan/wekan 2025-02-25 14:33:34 +02:00
Lauri Ojansivu ef70ed76a2 Updated changelog 2025-02-25 14:33:06 +02:00
Lauri Ojansivu a1a1b3d1ee Merge pull request #5696 from wekan/dependabot/github_actions/docker/build-push-action-6.14.0
Bump docker/build-push-action from 6.13.0 to 6.14.0
2025-02-24 23:21:55 +02:00
dependabot[bot] 0326757399 Bump docker/build-push-action from 6.13.0 to 6.14.0
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.13.0 to 6.14.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/ca877d9245402d1537745e0e356eab47c3520991...0adf9959216b96bec444f325f1e493d4aa344497)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-24 21:18:37 +00:00
Lauri Ojansivu edbc8ed92b v7.82 2025-02-24 20:21:33 +02:00
Lauri Ojansivu 07e9ec0617 Updated ChangeLog. 2025-02-24 15:12:07 +02:00
Lauri Ojansivu 18d0fa4327 Fixed building WeKan. Updated dompurify. Forked Meteor 2.14 version of meteor-node-stubs to update elliptic.
Thanks to delopers of depedencies and xet7 !

Fixes #5694
2025-02-24 14:56:54 +02:00
Lauri Ojansivu 666ee84033 Updated dependencies.
Thanks to developes of dependencies "
2025-02-23 15:55:20 +02:00
Lauri Ojansivu 120642f47d Updated ChangeLog. 2025-02-21 21:22:14 +02:00
Lauri Ojansivu 148b81262d Updated requirements at docs.
Thanks to mueller-ma !

Fixes #5692
2025-02-21 21:12:28 +02:00
Lauri Ojansivu c41467f76e Merge pull request #5691 from wekan/dependabot/npm_and_yarn/dompurify-3.2.4
Bump dompurify from 2.5.6 to 3.2.4
2025-02-15 15:47:49 +02:00
dependabot[bot] c83e83b8b1 Bump dompurify from 2.5.6 to 3.2.4
Bumps [dompurify](https://github.com/cure53/DOMPurify) from 2.5.6 to 3.2.4.
- [Release notes](https://github.com/cure53/DOMPurify/releases)
- [Commits](https://github.com/cure53/DOMPurify/compare/2.5.6...3.2.4)

---
updated-dependencies:
- dependency-name: dompurify
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-15 08:58:49 +00:00
Lauri Ojansivu 563a508e26 Added missing ) character.
Thanks to xet7 !
2025-02-11 22:12:57 +02:00
Lauri Ojansivu 1d8347cc23 Merge pull request #5682 from salleman33/devel
Update metrics.js
2025-02-11 17:28:33 +02:00
salleman 874abf2c41 Update metrics.js
Add possibility to use a token in place of ipaddress to access metrics route
2025-02-11 12:06:23 +01:00
Lauri Ojansivu 3e18f820ea v7.81 2025-02-07 16:13:52 +02:00
Lauri Ojansivu b571f1c953 Upgraded to MongoDB 6.0.20 at Snap Candidate.
Thanks to MongoDB developers!
2025-02-07 16:11:48 +02:00
Lauri Ojansivu fd69c7ceb8 Updated ChangeLog. 2025-02-05 04:41:42 +02:00
Lauri Ojansivu d15faa1890 Updated ChangeLog. 2025-01-29 14:15:31 +02:00
Lauri Ojansivu 0c7e12c5e7 Added Meteor 3.1 learning course to docs.
Thanks to producer of learning course !
2025-01-29 14:13:21 +02:00
Lauri Ojansivu 36a3077853 Fix misspelling of hours at env variable setting LDAP_BACKGROUND_SYNC_INTERVAL.
Thanks to hubermam !

Fixes #5665
2025-01-29 14:09:31 +02:00
Lauri Ojansivu 51bc23dbd9 Updated ChangeLog. 2025-01-29 13:40:34 +02:00
Lauri Ojansivu 0b1e0bd395 Fixed env variable METRICS_ACCEPTED_IP_ADDRESS to be same as at docs.
Thanks to salleman33 !

Fixes #5671
2025-01-29 13:38:40 +02:00
Lauri Ojansivu 151aafd114 Updated ChangeLog. 2025-01-29 10:23:12 +02:00
Lauri Ojansivu 16c8a65a1c Updated ChangeLog. 2025-01-29 10:22:08 +02:00
Lauri Ojansivu f3133e9cd8 Merge pull request #5670 from wekan/dependabot/github_actions/docker/build-push-action-6.13.0
Bump docker/build-push-action from 6.12.0 to 6.13.0
2025-01-29 10:14:55 +02:00
dependabot[bot] de84aa7674 Bump docker/build-push-action from 6.12.0 to 6.13.0
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.12.0 to 6.13.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/67a2d409c0a876cbe6b11854e3e25193efe4e62d...ca877d9245402d1537745e0e356eab47c3520991)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-27 20:17:17 +00:00
Lauri Ojansivu 1bdbfe86bb Merge pull request #5663 from wekan/dependabot/github_actions/helm/chart-releaser-action-1.7.0
Bump helm/chart-releaser-action from 1.6.0 to 1.7.0
2025-01-21 22:44:23 +02:00
Lauri Ojansivu 397b9f439e Merge pull request #5662 from wekan/dependabot/github_actions/docker/build-push-action-6.12.0
Bump docker/build-push-action from 6.11.0 to 6.12.0
2025-01-21 22:44:12 +02:00
dependabot[bot] 261e0aee0f Bump helm/chart-releaser-action from 1.6.0 to 1.7.0
Bumps [helm/chart-releaser-action](https://github.com/helm/chart-releaser-action) from 1.6.0 to 1.7.0.
- [Release notes](https://github.com/helm/chart-releaser-action/releases)
- [Commits](https://github.com/helm/chart-releaser-action/compare/v1.6.0...v1.7.0)

---
updated-dependencies:
- dependency-name: helm/chart-releaser-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-20 20:02:15 +00:00
dependabot[bot] a6d9bde658 Bump docker/build-push-action from 6.11.0 to 6.12.0
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.11.0 to 6.12.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/b32b51a8eda65d6793cd0494a773d4f6bcef32dc...67a2d409c0a876cbe6b11854e3e25193efe4e62d)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-20 20:02:13 +00:00
Lauri Ojansivu 8d0a07e1a8 Updated translations. 2025-01-19 23:06:23 +02:00
Lauri Ojansivu 659615e6f2 Merge pull request #5658 from wekan/dependabot/github_actions/docker/build-push-action-6.11.0
Bump docker/build-push-action from 6.10.0 to 6.11.0
2025-01-15 12:18:49 +02:00
dependabot[bot] 9f0b82c0d5 Bump docker/build-push-action from 6.10.0 to 6.11.0
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.10.0 to 6.11.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/48aba3b46d1b1fec4febb7c5d0c644b249a11355...b32b51a8eda65d6793cd0494a773d4f6bcef32dc)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-13 20:55:32 +00:00
Lauri Ojansivu 9a23dcc991 v7.80 2025-01-12 23:48:05 +02:00
Lauri Ojansivu 980d345305 Updated ChangeLog. 2025-01-12 23:19:51 +02:00
Lauri Ojansivu 41f76ee974 Updated translations. 2025-01-11 20:53:31 +02:00
Lauri Ojansivu 83a60e6303 Merge pull request #5655 from NadavTasher/bugfix/fix-assignment-of-removed-members
Fix assignee toggling keyboard shortcut to only toggle current board members
2025-01-03 17:32:58 +02:00
Nadav Tasher 7b23c85117 Fix assignee toggling keyboard shortcut to only toggle current board members 2025-01-03 17:15:40 +02:00
Lauri Ojansivu ff3900d100 Merge pull request #5654 from NadavTasher/bugfix/mouse-over-card-reactive-cache 2025-01-03 16:12:17 +02:00
Nadav Tasher e5e4b8ebfb Fix card issues with ReactiveCache when using keyboard shortcuts
When using ReactiveCache.getCard, a buggy instance of Card is returned.
The bug experienced is that .assignees and .members are not always up-to-date.

This can be reproduced by trying to hover over cards and triggering a keyboard
shortcut that interacts with a card.
2025-01-03 12:08:08 +02:00
Lauri Ojansivu 9516b75d65 Updated translations. 2025-01-02 19:39:08 +02:00
Lauri Ojansivu 6b1a92001a v7.79 2025-01-02 13:08:53 +02:00
Lauri Ojansivu e4e1fdb187 Updated ChangeLog. 2025-01-02 12:59:37 +02:00
Lauri Ojansivu 58ad80218a Updated translations. 2025-01-02 12:40:14 +02:00
Lauri Ojansivu 3322d3b33d Merge pull request #5652 from NadavTasher/feature/toggle-week-of-year-display
Feature - Add toggle for week-of-year in date displays (ISO 8601)
2025-01-02 12:07:23 +02:00
Lauri Ojansivu bd9d0c2d1b Updated translations. 2025-01-02 06:11:10 +02:00
Lauri Ojansivu 02b99e0555 Merge pull request #5653 from NadavTasher/feature/assign-members-by-numbers
Feature - Assign members using keyboard shortcut `Ctrl+Alt+[1-9]`
2025-01-02 06:01:32 +02:00
Nadav Tasher f803190dd2 Change shortcut help message 2025-01-01 22:54:51 +02:00
Nadav Tasher deb3a8389a Fix member index bug in keyboard shortcut 2025-01-01 22:26:59 +02:00
Nadav Tasher e7d02c12eb Added translation for new assigning keyboard shortcut 2025-01-01 22:23:21 +02:00
Nadav Tasher 169eaa180b Added support for assigning members to cards using Ctrl+Alt+Num 2025-01-01 22:22:57 +02:00
Nadav Tasher 08cde06ce5 Support show week of year toggle in cardCustomFields 2025-01-01 21:13:55 +02:00
Nadav Tasher 4607bfe454 Support week of year toggle in cardDate 2025-01-01 21:13:30 +02:00
Nadav Tasher de0eece27c Add show week of year translation 2025-01-01 21:13:03 +02:00
Nadav Tasher 25ad901fb3 Add show week of year toggle to sidebar 2025-01-01 21:12:38 +02:00
Nadav Tasher 99e579f47d Add week of year toggle state in user model 2025-01-01 21:12:04 +02:00
767 changed files with 79015 additions and 18620 deletions

No files matched your search

-257
View File
@@ -1,257 +0,0 @@
name: wekan
version: 0
version-script: git describe --tags | cut -c 2-
summary: The open-source kanban
description: |
Wekan is an open-source and collaborative kanban board application.
Whether you’re maintaining a personal todo list, planning your holidays with some friends, or working in a team on your next revolutionary idea, Kanban boards are an unbeatable tool to keep your things organized. They give you a visual overview of the current state of your project, and make you productive by allowing you to focus on the few items that matter the most.
Depending on target environment, some configuration settings might need to be adjusted.
For full list of configuration options call:
$ wekan.help
confinement: strict
grade: stable
architectures:
- amd64
plugs:
mongodb-plug:
interface: content
target: $SNAP_DATA/shared
hooks:
configure:
plugs:
- network
- network-bind
slots:
mongodb-slot:
interface: content
write:
- $SNAP_DATA/share
apps:
wekan:
command: wekan-control
daemon: simple
plugs: [network, network-bind]
mongodb:
command: mongodb-control
daemon: simple
plugs: [network, network-bind]
caddy:
command: caddy-control
daemon: simple
plugs: [network, network-bind]
help:
command: wekan-help
database-backup:
command: mongodb-backup
plugs: [network, network-bind]
database-list-backups:
command: ls -al $SNAP_COMMON/db-backups/
database-restore:
command: mongodb-restore
plugs: [network, network-bind]
parts:
mongodb:
source: https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1604-4.2.6.tgz
plugin: dump
stage-packages: [libssl1.0.0, libcurl3]
filesets:
mongo:
- usr
- bin
- lib
stage:
- $mongo
prime:
- $mongo
wekan:
source: .
plugin: nodejs
node-engine: 14.21.3
node-packages:
- node-gyp
- node-pre-gyp
- fibers
build-packages:
- ca-certificates
- apt-utils
- python
- python3
- g++
- capnproto
- curl
- libcurl3
- execstack
- nodejs
- npm
stage-packages:
- libfontconfig1
override-build: |
echo "Cleaning environment first"
rm -rf ~/.meteor ~/.npm /usr/local/lib/node_modules
# Create the OpenAPI specification
rm -rf .build
## Use Meteor 1.8.x on Snap
#rm -rf .meteor
#mv .snap-meteor-1.8/.meteor .
#mv .snap-meteor-1.8/package.json .
#mv .snap-meteor-1.8/package-lock.json .
## Meteor 1.9.x has changes to Buffer() => Buffer.alloc(), so reverting those
#mv .snap-meteor-1.8/cfs_access-point.txt fix-download-unicode/
#mv .snap-meteor-1.8/export.js models/
#mv .snap-meteor-1.8/wekanCreator.js models/
#mv .snap-meteor-1.8/ldap.js packages/wekan-ldap/server/ldap.js
#mv .snap-meteor-1.8/oidc_server.js packages/wekan-oidc/oidc_server.js
rm -rf .snap-meteor-1.8
#mkdir -p .build/python
#cd .build/python
#git clone --depth 1 -b master https://github.com/Kronuz/esprima-python
#cd esprima-python
#python3 setup.py install
#cd ../../..
#mkdir -p ./public/api
#python3 ./openapi/generate_openapi.py --release $(git describe --tags --abbrev=0) > ./public/api/wekan.yml
# we temporary need api2html and mkdirp
#npm install -g api2html@0.3.0
#npm install -g mkdirp
#api2html -c ./public/logo-header.png -o ./public/api/wekan.html ./public/api/wekan.yml
#npm uninstall -g mkdirp
#npm uninstall -g api2html
# Node Fibers 100% CPU usage issue:
# https://github.com/wekan/wekan-mongodb/issues/2#issuecomment-381453161
# https://github.com/meteor/meteor/issues/9796#issuecomment-381676326
# https://github.com/sandstorm-io/sandstorm/blob/0f1fec013fe7208ed0fd97eb88b31b77e3c61f42/shell/server/00-startup.js#L99-L129
# Also see beginning of wekan/server/authentication.js
# import Fiber from "fibers";
# Fiber.poolSize = 1e9;
# OLD: Download node version 8.12.0 prerelease build => Official node 8.12.0 has been released
# Description at https://releases.wekan.team/node.txt
##echo "375bd8db50b9c692c0bbba6e96d4114cd29bee3770f901c1ff2249d1038f1348 node" >> node-SHASUMS256.txt.asc
##curl https://releases.wekan.team/node -o node
# Verify Fibers patched node authenticity
##echo "Fibers 100% CPU issue patched node authenticity:"
##grep node node-SHASUMS256.txt.asc | shasum -a 256 -c -
##rm -f node-SHASUMS256.txt.asc
##chmod +x node
##mv node `which node`
# DOES NOT WORK: paxctl fix.
# Removed from build-packages: - paxctl
#echo "Applying paxctl fix for alpine linux: https://github.com/wekan/wekan/issues/1303"
#paxctl -mC `which node`
#echo "Installing npm"
#curl -L https://www.npmjs.com/install.sh | sh
echo "Installing meteor"
curl https://install.meteor.com/ -o install_meteor.sh
#sed -i "s|RELEASE=.*|RELEASE=\"1.8.1-beta.0\"|g" install_meteor.sh
chmod +x install_meteor.sh
sh install_meteor.sh
rm install_meteor.sh
# REPOS BELOW ARE INCLUDED TO WEKAN REPO
#if [ ! -d "packages" ]; then
# mkdir packages
#fi
#if [ ! -d "packages/kadira-flow-router" ]; then
# cd packages
# git clone --depth 1 -b master https://github.com/wekan/flow-router.git kadira-flow-router
# cd ..
#fi
#if [ ! -d "packages/meteor-useraccounts-core" ]; then
# cd packages
# git clone --depth 1 -b master https://github.com/meteor-useraccounts/core.git meteor-useraccounts-core
# sed -i 's/api\.versionsFrom/\/\/api.versionsFrom/' meteor-useraccounts-core/package.js
# cd ..
#fi
#if [ ! -d "packages/meteor-accounts-cas" ]; then
# cd packages
# git clone --depth 1 -b master https://github.com/wekan/meteor-accounts-cas.git meteor-accounts-cas
# cd ..
#fi
#if [ ! -d "packages/wekan-ldap" ]; then
# cd packages
# git clone --depth 1 -b master https://github.com/wekan/wekan-ldap.git
# cd ..
#fi
#if [ ! -d "packages/wekan-scrollbar" ]; then
# cd packages
# git clone --depth 1 -b master https://github.com/wekan/wekan-scrollbar.git
# cd ..
#fi
#if [ ! -d "packages/wekan_accounts-oidc" ]; then
# cd packages
# git clone --depth 1 -b master https://github.com/wekan/meteor-accounts-oidc.git
# mv meteor-accounts-oidc/packages/switch_accounts-oidc wekan-accounts-oidc
# mv meteor-accounts-oidc/packages/switch_oidc wekan-oidc
# rm -rf meteor-accounts-oidc
# cd ..
#fi
#if [ ! -d "packages/markdown" ]; then
# cd packages
# git clone --depth 1 -b master --recurse-submodules https://github.com/wekan/markdown.git
# cd ..
#fi
rm -rf .build
meteor add standard-minifier-js --allow-superuser
meteor npm install --allow-superuser
meteor npm install --allow-superuser --save babel-runtime
meteor build .build --directory --allow-superuser
cp -f fix-download-unicode/cfs_access-point.txt .build/bundle/programs/server/packages/cfs_access-point.js
#Removed binary version of bcrypt because of security vulnerability that is not fixed yet.
#https://github.com/wekan/wekan/commit/4b2010213907c61b0e0482ab55abb06f6a668eac
#https://github.com/wekan/wekan/commit/7eeabf14be3c63fae2226e561ef8a0c1390c8d3c
#cd .build/bundle/programs/server/npm/node_modules/meteor/npm-bcrypt
#rm -rf node_modules/bcrypt
#meteor npm install --save bcrypt
# Change from npm-bcrypt directory back to .build/bundle/programs/server directory.
#cd ../../../../
# Change to directory .build/bundle/programs/server
cd .build/bundle/programs/server
npm install
npm install --allow-superuser --save babel-runtime
#meteor npm install --save bcrypt
# Change back to Wekan source directory
cd ../../../..
cp -r .build/bundle/* $SNAPCRAFT_PART_INSTALL/
cp .build/bundle/.node_version.txt $SNAPCRAFT_PART_INSTALL/
rm -f $SNAPCRAFT_PART_INSTALL/lib/node_modules/wekan
rm -f $SNAPCRAFT_PART_INSTALL/programs/server/npm/node_modules/meteor/rajit_bootstrap3-datepicker/lib/bootstrap-datepicker/node_modules/phantomjs-prebuilt/lib/phantom/bin/phantomjs
rm -f $SNAPCRAFT_PART_INSTALL/programs/server/npm/node_modules/tar/lib/.mkdir.js.swp
rm -f $SNAPCRAFT_PART_INSTALL/lib/node_modules/node-pre-gyp/node_modules/tar/lib/.mkdir.js.swp
rm -f $SNAPCRAFT_PART_INSTALL/lib/node_modules/node-gyp/node_modules/tar/lib/.mkdir.js.swp
# Meteor 1.8.x additional .swp remove
rm -f $SNAPCRAFT_PART_INSTALL/programs/server/node_modules/node-pre-gyp/node_modules/tar/lib/.mkdir.js.swp
organize:
README: README.wekan
prime:
- -lib/node_modules/node-pre-gyp/node_modules/tar/lib/.unpack.js.swp
helpers:
source: snap-src
plugin: dump
caddy:
plugin: dump
source: https://caddyserver.com/download/linux/amd64?license=personal&telemetry=off
source-type: tar
organize:
caddy: bin/caddy
CHANGES.txt: CADDY_CHANGES.txt
EULA.txt: CADDY_EULA.txt
LICENSES.txt: CADDY_LICENSES.txt
README.txt: CADDY_README.txt
stage:
- -init
-198
View File
@@ -1,198 +0,0 @@
#!/bin/bash
echo "Note: If you use other locale than en_US.UTF-8 , you need to additionally install en_US.UTF-8"
echo " with 'sudo dpkg-reconfigure locales' , so that MongoDB works correctly."
echo " You can still use any other locale as your main locale."
#Below script installs newest node 8.x for Debian/Ubuntu/Mint.
#NODE_VERSION=12.21.0
#X64NODE="https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.gz"
function pause(){
read -p "$*"
}
function cprec(){
if [[ -d "$1" ]]; then
if [[ ! -d "$2" ]]; then
sudo mkdir -p "$2"
fi
for i in $(ls -A "$1"); do
cprec "$1/$i" "$2/$i"
done
else
sudo cp "$1" "$2"
fi
}
# sudo npm doesn't work right, so this is a workaround
function npm_call(){
TMPDIR="/tmp/tmp_npm_prefix"
if [[ -d "$TMPDIR" ]]; then
rm -rf $TMPDIR
fi
mkdir $TMPDIR
NPM_PREFIX="$(npm config get prefix)"
npm config set prefix $TMPDIR
npm "$@"
npm config set prefix "$NPM_PREFIX"
echo "Moving files to $NPM_PREFIX"
for i in $(ls -A $TMPDIR); do
cprec "$TMPDIR/$i" "$NPM_PREFIX/$i"
done
rm -rf $TMPDIR
}
#function wekan_repo_check(){
## UNCOMMENTING, IT'S NOT REQUIRED THAT /HOME/USERNAME IS /HOME/WEKAN
# git_remotes="$(git remote show 2>/dev/null)"
# res=""
# for i in $git_remotes; do
# res="$(git remote get-url $i | sed 's/.*wekan\/wekan.*/wekan\/wekan/')"
# if [[ "$res" == "wekan/wekan" ]]; then
# break
# fi
# done
#
# if [[ "$res" != "wekan/wekan" ]]; then
# echo "$PWD is not a wekan repository"
# exit;
# fi
#}
echo
PS3='Please enter your choice: '
options=("Install Wekan dependencies" "Build Wekan" "Run Meteor for dev on http://localhost:4000" "Run Meteor for dev on http://CURRENT-IP-ADDRESS:4000" "Run Meteor for dev on http://CUSTOM-IP-ADDRESS:PORT" "Quit")
select opt in "${options[@]}"
do
case $opt in
"Install Wekan dependencies")
if [[ "$OSTYPE" == "linux-gnu" ]]; then
echo "Linux";
# Debian, Ubuntu, Mint
sudo apt-get install -y build-essential gcc g++ make git curl wget
# npm nodejs
#sudo npm -g install npm
curl -0 -L https://npmjs.org/install.sh | sudo sh
sudo chown -R $(id -u):$(id -g) $HOME/.npm
sudo npm -g install n
sudo n 12.21.0
#curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
#sudo apt-get install -y nodejs
elif [[ "$OSTYPE" == "darwin"* ]]; then
echo "macOS";
pause '1) Install XCode 2) Install Node 8.x from https://nodejs.org/en/ 3) Press [Enter] key to continue.'
elif [[ "$OSTYPE" == "cygwin" ]]; then
# POSIX compatibility layer and Linux environment emulation for Windows
echo "TODO: Add Cygwin";
exit;
elif [[ "$OSTYPE" == "msys" ]]; then
# Lightweight shell and GNU utilities compiled for Windows (part of MinGW)
echo "TODO: Add msys on Windows";
exit;
elif [[ "$OSTYPE" == "win32" ]]; then
# I'm not sure this can happen.
echo "TODO: Add Windows";
exit;
elif [[ "$OSTYPE" == "freebsd"* ]]; then
echo "TODO: Add FreeBSD";
exit;
else
echo "Unknown"
echo ${OSTYPE}
exit;
fi
## Latest npm with Meteor 1.8.x
npm_call -g install npm
npm_call -g install node-gyp
# Latest fibers for Meteor 1.8.x
sudo mkdir -p /usr/local/lib/node_modules/fibers/.node-gyp
npm_call -g install fibers
# Install Meteor, if it's not yet installed
curl https://install.meteor.com | bash
sudo chown -R $(id -u):$(id -g) $HOME/.npm $HOME/.meteor
break
;;
"Build Wekan")
echo "Building Wekan."
#wekan_repo_check
# REPOS BELOW ARE INCLUDED TO WEKAN REPO
#rm -rf packages/kadira-flow-router packages/meteor-useraccounts-core packages/meteor-accounts-cas packages/wekan-ldap packages/wekan-ldap packages/wekan-scrfollbar packages/meteor-accounts-oidc packages/markdown
#mkdir packages
#cd packages
#git clone --depth 1 -b master https://github.com/wekan/flow-router.git kadira-flow-router
#git clone --depth 1 -b master https://github.com/meteor-useraccounts/core.git meteor-useraccounts-core
#git clone --depth 1 -b master https://github.com/wekan/meteor-accounts-cas.git
#git clone --depth 1 -b master https://github.com/wekan/wekan-ldap.git
#git clone --depth 1 -b master https://github.com/wekan/wekan-scrollbar.git
#git clone --depth 1 -b master https://github.com/wekan/meteor-accounts-oidc.git
#git clone --depth 1 -b master --recurse-submodules https://github.com/wekan/markdown.git
#mv meteor-accounts-oidc/packages/switch_accounts-oidc wekan_accounts-oidc
#mv meteor-accounts-oidc/packages/switch_oidc wekan_oidc
#rm -rf meteor-accounts-oidc
#if [[ "$OSTYPE" == "darwin"* ]]; then
# echo "sed at macOS";
# sed -i '' 's/api\.versionsFrom/\/\/api.versionsFrom/' ~/repos/wekan/packages/meteor-useraccounts-core/package.js
#else
# echo "sed at ${OSTYPE}"
# sed -i 's/api\.versionsFrom/\/\/api.versionsFrom/' ~/repos/wekan/packages/meteor-useraccounts-core/package.js
#fi
#cd ..
sudo chown -R $(id -u):$(id -g) $HOME/.npm $HOME/.meteor
rm -rf node_modules .meteor/local
npm install
rm -rf .build
meteor build .build --directory
cp -f fix-download-unicode/cfs_access-point.txt .build/bundle/programs/server/packages/cfs_access-point.js
# Remove legacy webbroser bundle, so that Wekan works also at Android Firefox, iOS Safari, etc.
rm -rf .build/bundle/programs/web.browser.legacy
#Removed binary version of bcrypt because of security vulnerability that is not fixed yet.
#https://github.com/wekan/wekan/commit/4b2010213907c61b0e0482ab55abb06f6a668eac
#https://github.com/wekan/wekan/commit/7eeabf14be3c63fae2226e561ef8a0c1390c8d3c
#cd ~/repos/wekan/.build/bundle/programs/server/npm/node_modules/meteor/npm-bcrypt
#rm -rf node_modules/bcrypt
#meteor npm install bcrypt
cd .build/bundle/programs/server
rm -rf node_modules
npm install
#meteor npm install bcrypt
cd ../../../..
echo Done.
break
;;
"Run Meteor for dev on http://localhost:4000")
WITH_API=true RICHER_CARD_COMMENT_EDITOR=false ROOT_URL=http://localhost:4000 meteor run --exclude-archs web.browser.legacy,web.cordova --port 4000
break
;;
"Run Meteor for dev on http://CURRENT-IP-ADDRESS:4000")
IPADDRESS=$(ip a | grep 'noprefixroute' | grep 'inet ' | cut -d: -f2 | awk '{ print $2}' | cut -d '/' -f 1)
echo "Your IP address is $IPADDRESS"
WITH_API=true RICHER_CARD_COMMENT_EDITOR=false ROOT_URL=http://$IPADDRESS:4000 meteor run --exclude-archs web.browser.legacy,web.cordova --port 4000
break
;;
"Run Meteor for dev on http://CUSTOM-IP-ADDRESS:PORT")
ip address
echo "From above list, what is your IP address?"
read IPADDRESS
echo "On what port you would like to run Wekan?"
read PORT
echo "ROOT_URL=http://$IPADDRESS:$PORT"
WITH_API=true RICHER_CARD_COMMENT_EDITOR=false ROOT_URL=http://$IPADDRESS:$PORT meteor run --exclude-archs web.browser.legacy,web.cordova --port $PORT
break
;;
"Quit")
break
;;
*) echo invalid option;;
esac
done
-155
View File
@@ -1,155 +0,0 @@
name: wekan
version: git
summary: The open-source kanban
description: |
Wekan is an open-source and collaborative kanban board application.
Whether you’re maintaining a personal todo list, planning your holidays with some friends, or working in a team on your next revolutionary idea, Kanban boards are an unbeatable tool to keep your things organized. They give you a visual overview of the current state of your project, and make you productive by allowing you to focus on the few items that matter the most.
Depending on target environment, some configuration settings might need to be adjusted.
For full list of configuration options call:
$ wekan.help
confinement: strict
grade: stable
base: core18
architectures:
- amd64
plugs:
mongodb-plug:
interface: content
target: $SNAP_DATA/shared
hooks:
configure:
plugs:
- network
- network-bind
slots:
mongodb-slot:
interface: content
write:
- $SNAP_DATA/share
apps:
wekan:
command: wekan-control
daemon: simple
plugs: [network, network-bind]
mongodb:
command: mongodb-control
daemon: simple
plugs: [network, network-bind]
caddy:
command: caddy-control
daemon: simple
plugs: [network, network-bind]
help:
command: wekan-help
database-backup:
command: mongodb-backup
plugs: [network, network-bind]
database-list-backups:
command: ls -al $SNAP_COMMON/db-backups/
database-restore:
command: mongodb-restore
plugs: [network, network-bind]
parts:
mongodb:
source: https://repo.mongodb.org/apt/ubuntu/dists/xenial/mongodb-org/4.2/multiverse/binary-amd64/mongodb-org-server_4.2.2_amd64.deb
#https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-4.0.14.tgz
#https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1604-3.2.22.tgz
plugin: dump
stage-packages: [libssl1.0.0, libcurl3]
filesets:
mongo:
- usr
- bin
- lib
stage:
- $mongo
prime:
- $mongo
wekan:
source: .
plugin: nodejs
node-engine: 14.19.0
node-packages:
- node-gyp
- node-pre-gyp
- fibers
build-packages:
- ca-certificates
- apt-utils
- build-essential
- python
- python3
- g++
- capnproto
- curl
- libcurl3
- execstack
- nodejs
- npm
stage-packages:
- libfontconfig1
override-build: |
echo "Cleaning environment first"
rm -rf ~/.meteor ~/.npm /usr/local/lib/node_modules
rm -rf .build
echo "Installing meteor"
curl https://install.meteor.com/ -o install_meteor.sh
chmod +x install_meteor.sh
sh install_meteor.sh
rm install_meteor.sh
rm -rf .build
meteor add standard-minifier-js --allow-superuser
meteor npm install --allow-superuser
meteor npm install --allow-superuser --save babel-runtime
meteor build .build --directory --allow-superuser
cp -f fix-download-unicode/cfs_access-point.txt .build/bundle/programs/server/packages/cfs_access-point.js
cd .build/bundle/programs/server
npm install
npm install --allow-superuser --save babel-runtime
# Change back to Wekan source directory
cd ../../../..
cp -r .build/bundle/* $SNAPCRAFT_PART_INSTALL/
cp .build/bundle/.node_version.txt $SNAPCRAFT_PART_INSTALL/
rm -f $SNAPCRAFT_PART_INSTALL/lib/node_modules/wekan
rm -f $SNAPCRAFT_PART_INSTALL/programs/server/npm/node_modules/meteor/rajit_bootstrap3-datepicker/lib/bootstrap-datepicker/node_modules/phantomjs-prebuilt/lib/phantom/bin/phantomjs
rm -f $SNAPCRAFT_PART_INSTALL/programs/server/npm/node_modules/tar/lib/.mkdir.js.swp
rm -f $SNAPCRAFT_PART_INSTALL/lib/node_modules/node-pre-gyp/node_modules/tar/lib/.mkdir.js.swp
rm -f $SNAPCRAFT_PART_INSTALL/lib/node_modules/node-gyp/node_modules/tar/lib/.mkdir.js.swp
rm -f $SNAPCRAFT_PART_INSTALL/programs/server/node_modules/node-pre-gyp/node_modules/tar/lib/.mkdir.js.swp
organize:
README: README.wekan
prime:
- -lib/node_modules/node-pre-gyp/node_modules/tar/lib/.unpack.js.swp
helpers:
source: snap-src
plugin: dump
caddy:
plugin: dump
source: https://caddyserver.com/download/linux/amd64?license=personal&telemetry=off
source-type: tar
organize:
caddy: bin/caddy
CHANGES.txt: CADDY_CHANGES.txt
EULA.txt: CADDY_EULA.txt
LICENSES.txt: CADDY_LICENSES.txt
README.txt: CADDY_README.txt
stage:
- -init
-183
View File
@@ -1,183 +0,0 @@
name: wekan
version: '6.21'
base: core20
summary: Open Source kanban
description: |
WeKan ® is an Open Source and collaborative kanban board application.
Whether you’re maintaining a personal todo list, planning your holidays with some friends, or working in a team on your next revolutionary idea, Kanban boards are an unbeatable tool to keep your things organized. They give you a visual overview of the current state of your project, and make you productive by allowing you to focus on the few items that matter the most.
Depending on target environment, some configuration settings might need to be adjusted.
For full list of configuration options call:
$ wekan.help
confinement: strict
grade: stable
architectures:
- build-on: amd64
run-on: amd64
- build-on: arm64
run-on: arm64
- build-on: ppc64el
run-on: ppc64el
- build-on: s390x
run-on: s390x
plugs:
mongodb-plug:
interface: content
target: $SNAP_DATA/shared
hooks:
configure:
plugs:
- network
- network-bind
slots:
mongodb-slot:
interface: content
write:
- $SNAP_DATA/share
apps:
wekan:
command: wekan-control
daemon: simple
plugs: [network, network-bind, mount-observe, system-observe, bluetooth-control]
restart-condition: on-failure
mongodb:
command: mongodb-control
daemon: simple
plugs: [network, network-bind, mount-observe, system-observe, bluetooth-control]
restart-condition: on-failure
caddy:
command: caddy-control
daemon: simple
plugs: [network, network-bind]
help:
command: wekan-help
database-backup:
command: mongodb-backup
plugs: [network, network-bind, mount-observe, system-observe, bluetooth-control]
database-list-backups:
command: ls -al $SNAP_COMMON/db-backups/
database-restore:
command: mongodb-restore
plugs: [network, network-bind, mount-observe, system-observe, bluetooth-control]
parts:
mongodb:
plugin: dump
source:
- on amd64: https://repo.mongodb.org/apt/ubuntu/dists/focal/mongodb-org/4.4/multiverse/binary-amd64/mongodb-org-server_4.4.13_amd64.deb
- on arm64: https://repo.mongodb.org/apt/ubuntu/dists/focal/mongodb-org/4.4/multiverse/binary-arm64/mongodb-org-server_4.4.13_arm64.deb
- on ppc64el: https://repo.mongodb.org/apt/ubuntu/dists/focal/mongodb-org/4.4/multiverse/binary-ppc64el/mongodb-org-server_4.4.13_ppc64el.deb
- on s390x: https://repo.mongodb.org/apt/ubuntu/dists/focal/mongodb-org/4.4/multiverse/binary-s390x/mongodb-org-server_4.4.13_s390x.deb
stage-packages:
- libssl1.1
- libcurl3-dev
- libcurl4-openssl-dev
filesets:
mongo:
- usr
- bin
- lib
stage:
- $mongo
prime:
- $mongo
wekan:
#plugin: npm
plugin: dump
source:
# Fixed URLs to some allowed GitHub releases URL.
# Non-GitHub build server file urls are not allowed at 2022-03-02 and later.
- on amd64: https://github.com/wekan/wekan/releases/download/v6.20/wekan-6.20-amd64.zip
- on arm64: https://github.com/wekan/wekan/releases/download/v6.20/wekan-6.20-arm64.zip
- on ppc64el: https://github.com/wekan/wekan/releases/download/v6.20/wekan-6.20-ppc64el.zip
- on s390x: https://github.com/wekan/wekan/releases/download/v6.20/wekan-6.20-s390x.zip
# npm-node-version: 14.19.1
# node-packages:
# - node-gyp
# - node-pre-gyp
# - fibers
# build-packages:
# - npm
# - build-essential
# - ca-certificates
# - apt-utils
# - python
# - python3
# - g++
# - capnproto
# - curl
# - execstack
# - nodejs
# - npm
# - p7zip-full
# stage-packages:
# - libfontconfig1
override-build: |
cp -r bundle/* $SNAPCRAFT_PART_INSTALL/
cp bundle/.node_version.txt $SNAPCRAFT_PART_INSTALL/
rm -f $SNAPCRAFT_PART_INSTALL/lib/node_modules/wekan
snapcraftctl build
organize:
README: README.wekan
prime:
- -lib/node_modules/node-pre-gyp/node_modules/tar/lib/.unpack.js.swp
- -lib/node_modules/weka*
helpers:
source: snap-src
plugin: dump
caddy:
plugin: dump
## Caddy v1 is not developed anymore. TODO: Sometime migrate to Caddy v2.
## https://caddy.community/t/caddyfile-v1-adapter/9129
## https://github.com/caddyserver/caddy/tree/v1
#source: https://caddyserver.com/download/linux/amd64?license=personal&telemetry=off
#source-type: tar
# Using last working binary that was downloaded from above URL to Wekan Snap,
# and .txt files from https://github.com/caddyserver/caddy/tree/v1/dist
source: https://wekan.github.io/caddy-v1-linux-amd64.7z
source-type: 7z
organize:
caddy: bin/caddy
CHANGES.txt: CADDY_CHANGES.txt
EULA.txt: CADDY_EULA.txt
LICENSES.txt: CADDY_LICENSES.txt
README.txt: CADDY_README.txt
stage:
- -init
caddy2:
plugin: dump
source:
# Fixed URLs to some allowed GitHub releases URL.
# Non-GitHub build server file urls are not allowed at 2022-03-02 and later.
- on amd64: https://github.com/wekan/wekan/releases/download/v6.20/caddy-v2-amd64.zip
- on arm64: https://github.com/wekan/wekan/releases/download/v6.20/caddy-v2-arm64.zip
- on ppc64el: https://github.com/wekan/wekan/releases/download/v6.20/caddy-v2-ppc64el.zip
- on s390x: https://github.com/wekan/wekan/releases/download/v6.20/caddy-v2-s390x.zip
source-type: zip
organize:
caddy: bin/caddy
CHANGES.txt: CADDY_CHANGES.txt
EULA.txt: CADDY_EULA.txt
LICENSES.txt: CADDY_LICENSES.txt
README.txt: CADDY_README.txt
stage:
- -init
+1 -1
View File
@@ -1,3 +1,3 @@
# These are supported funding model platforms
custom: ['https://wekan.team/commercial-support/']
custom: ['https://wekan.fi/commercial-support/']
+4 -3
View File
@@ -1,5 +1,9 @@
## Issue
UPGRADE: https://wekan.fi/upgrade/
Pull requests welcome to fix any broken links at docs directory, and organizing docs/Features and their screenshots to subdirectories of each feature.
Please report these issues elsewhere:
- SECURITY ISSUES, PGP EMAIL: https://github.com/wekan/wekan/blob/main/SECURITY.md
@@ -7,9 +11,6 @@ Please report these issues elsewhere:
If WeKan Snap is slow, try this: https://github.com/wekan/wekan/wiki/Cron
**[PLEASE UPGRADE](https://github.com/wekan/wekan/wiki/Backup)** to the newest
WeKan ® before reporting an issue, if possible.
Please search existing Open and Closed issues, most questions have already been answered.
If you can not login for any reason: https://github.com/wekan/wekan/wiki/Forgot-Password
+1 -1
View File
@@ -9,6 +9,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: 'Checkout Repository'
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: 'Dependency Review'
uses: actions/dependency-review-action@v4
+4 -4
View File
@@ -32,13 +32,13 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
# Login against a Docker registry except on PR
# https://github.com/docker/login-action
- name: Log into registry ${{ env.REGISTRY }}
if: github.event_name != 'pull_request'
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
@@ -48,14 +48,14 @@ jobs:
# https://github.com/docker/metadata-action
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@369eb591f429131d6889c46b94e711f089e6ca96
uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
# Build and push Docker image with Buildx (don't push on PR)
# https://github.com/docker/build-push-action
- name: Build and push Docker image
uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
+1 -1
View File
@@ -15,6 +15,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Build the Docker image
run: docker build . --file Dockerfile --tag wekan:$(date +%s)
+2 -2
View File
@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0
@@ -25,6 +25,6 @@ jobs:
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
- name: Run chart-releaser
uses: helm/chart-releaser-action@v1.6.0
uses: helm/chart-releaser-action@v1.7.0
env:
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
+6 -6
View File
@@ -18,7 +18,7 @@ jobs:
# runs-on: ubuntu-latest
# steps:
# - name: checkout
# uses: actions/checkout@v4
# uses: actions/checkout@v5
#
# - name: setup node
# uses: actions/setup-node@v1
@@ -42,7 +42,7 @@ jobs:
# needs: [lintcode]
# steps:
# - name: checkout
# uses: actions/checkout@v4
# uses: actions/checkout@v5
#
# - name: setup node
# uses: actions/setup-node@v1
@@ -65,7 +65,7 @@ jobs:
# needs: [lintcode,lintstyle]
# steps:
# - name: checkout
# uses: actions/checkout@v4
# uses: actions/checkout@v5
#
# - name: setup node
# uses: actions/setup-node@v1
@@ -90,7 +90,7 @@ jobs:
# CHECKOUTS
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
# CACHING
- name: Install Meteor
@@ -147,10 +147,10 @@ jobs:
needs: [tests]
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Download coverage
uses: actions/download-artifact@v4
uses: actions/download-artifact@v5
with:
name: coverage-folder
path: .coverage/
+3 -3
View File
@@ -52,8 +52,8 @@ ongoworks:speakingurl
raix:handlebar-helpers
http@2.0.0! # force new http package
# Datepicker
wekan-bootstrap-datepicker
# Datepicker (disabled - using native HTML inputs)
# wekan-bootstrap-datepicker
# UI components
ostrio:i18n
@@ -93,4 +93,4 @@ ejson@1.1.3
logging@1.3.3
wekan-fullcalendar
momentjs:moment@2.29.3
wekan-fontawesome
# wekan-fontawesome
-2
View File
@@ -155,8 +155,6 @@ wekan-accounts-cas@0.1.0
wekan-accounts-lockout@1.0.0
wekan-accounts-oidc@1.0.10
wekan-accounts-sandstorm@0.8.0
wekan-bootstrap-datepicker@1.10.0
wekan-fontawesome@6.4.2
wekan-fullcalendar@3.10.5
wekan-ldap@0.0.2
wekan-markdown@1.0.9
+12 -11
View File
@@ -1,12 +1,13 @@
{
"editor.tabSize": 2,
"editor.rulers": [80],
"files.trimTrailingWhitespace": true,
"files.insertFinalNewline": true,
"files.exclude": {
"**/.git": true,
"**/.DS_Store": true,
"node_modules": true,
".meteor/local": true
}
}
"terminal.integrated.env.linux": {
"TERM": "xterm-256color"
},
"terminal.integrated.env.osx": {
"TERM": "xterm-256color"
},
"terminal.integrated.env.windows": {
"TERM": "xterm-256color"
},
"terminal.integrated.shell.linux": "/bin/bash",
"terminal.integrated.shellArgs.linux": ["-l"]
}
+851 -10
View File
@@ -1,19 +1,860 @@
[Mac ChangeLog](https://github.com/wekan/wekan/wiki/Mac)
Newest WeKan at these amd64 platforms:
Newest WeKan at these platforms:
- Windows and Linux bundle
- Snap Candidate
- Docker
- Kubernetes
- [bundle zip at releases](https://github.com/wekan/wekan/releases), docs at
- [Linux amd64, arm64, s390x](https://github.com/wekan/wekan/blob/main/docs/Platforms/FOSS/RaspberryPi/Raspberry-Pi.md)
- [Windows amd64, works also at Windows arm64](https://github.com/wekan/wekan/blob/main/docs/Platforms/Propietary/Windows/Offline.md)
- [Mac amd64, works also with Rosetta2 at Apple Silicon](https://github.com/wekan/wekan/blob/main/docs/Platforms/Propietary/Mac.md)
- https://wekan.fi/install/
- Snap Candidate amd64
- Docker amd64
- Kubernetes Docker amd64
- Bitnami MongoDB Docker images do not exist anymore. [MongoDump/MongoRestore to groundhog2k MongoDB images](https://github.com/wekan/charts/issues/45)
Fixing other platforms In Progress.
- Install info at Server part of webpage https://wekan.github.io
- Newest Node.js is at https://github.com/wekan/node-v14-esm/releases/tag/v14.21.4
- MongoDB 6.x
- Node.js 14.x at https://github.com/wekan/node-v14-esm/releases/tag/v14.21.4 and https://nodejs.org/dist/latest-v14.x/
- MongoDB 6.x and 7.x, or FerretDB/PostgreSQL https://blog.ferretdb.io/building-project-management-stack-wekan-ferretdb/
[How to upgrade WeKan](https://github.com/wekan/wekan/issues/4585)
[Upgrade WeKan](https://wekan.fi/upgrade/)
WeKan 8.00-8.06 had wrong raw database directory setting /var/snap/wekan/common/wekan and some cards were not visible.
Those are fixed at WeKan 8.07 where database directory is back to /var/snap/wekan/common and all cards are visible.
# v8.08 2025-10-21 WeKan ® release
This release fixes the following bugs:
- [Fix opening board migration of Shared Lists to Per-Swimlane lists to use ReactiveCache correctly without errors](https://github.com/wekan/wekan/commit/9536e60bd1c77c8a22e89d2eb2968e11da3a28cd).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.07 2025-10-20 WeKan ® release
This release fixes the following bugs:
- [Fix Snap Candidate WeKan 8.00-8.06 commit ae01ea5 database directory from /var/snap/wekan/common/wekan back to 8.07 /var/snap/wekan/common](https://github.com/wekan/wekan/commit/98f141d62f3b6d4371d024c72eae6688d0f4e516).
Thanks to xet7.
- [When opening board, add missing lists](https://github.com/wekan/wekan/commit/80777b46638ed15b8194105751499ada4b066d19).
Thanks to xet7.
- [If Snap Candidate MongoDB raw database files were at SNAP_COMMON/wekan, migrate them back to SNAP_COMMON](https://github.com/wekan/wekan/commit/f2019b1059c8d6f4cd9a46c3db7e004c4928cebb).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.06 2025-10-20 WeKan ® release
This release adds the following new features:
- [At Public Board, drag resize list width and swimlane height. For logged in users, fix adding labels](https://github.com/wekan/wekan/commit/351433524708e9a7ccb4795d9ca31a78904943ea).
Thanks to xet7.
- [When opening board, migrate from Shared Lists to Per-Swimlane Lists](https://github.com/wekan/wekan/commit/1e6252de7f26f3af14a99fb63b5dac27ba0576f3).
Thanks to xet7.
- [Added Date Format setting to Opened Card](https://github.com/wekan/wekan/commit/2dd3916f7ee3df10bd88643cf2c796cb166b3044).
Thanks to xet7.
and fixes the following bugs:
- [Fix add and drag drop attachments to minicards and card](https://github.com/wekan/wekan/commit/b06daff4c7e63453643459f7d8798fde97e3200c).
Thanks to xet7.
- [Fix starred, archive and clone icons](https://github.com/wekan/wekan/pull/5953).
Thanks to helioguardabaxo.
- Fix Due dates to be color coded and have unicode icons.
[Part 1](https://github.com/wekan/wekan/commit/d965faa3174dc81636106e6f81435b2750b0625f),
[Part 2](https://github.com/wekan/wekan/commit/101048339bdd1e45f876aeb1aa5ec32ceda28139).
Thanks to xet7.
- [Fix unable to see My Due Cards](https://github.com/wekan/wekan/commit/66b444e2b0c9b2ed5f98cd1ff0cd9222b2d0c624).
Thanks to xet7.
- Fix drag drop lists.
[Part 1](https://github.com/wekan/wekan/commit/324f3f7794aace800022a24deb5fd5fb36ebd384),
[Part 2](https://github.com/wekan/wekan/commit/ff516ec696ef499f11b04b30053eeb9d3f96d8d1).
Thanks to xet7.
- [Removed extra pipe characters](https://github.com/wekan/wekan/commit/caa6e615ff3c3681bf2b470a625eb39c6009b825).
Thanks to xet7.
- [Fix syntax error at migrations](https://github.com/wekan/wekan/commit/eb6b42c4c9f99894fd93e62c9b3fceda3429c96c).
Thanks to xet7.
- [Fix opened card attachments button text to be at tooltip, not at opened card](https://github.com/wekan/wekan/commit/1e53125499ef563ca3c65f786ac3525e5f50274c).
Thanks to xet7.
- [Fix Broken Hyperlinks in Markdown to HTML conversion](https://github.com/wekan/wekan/commit/973a49526fdf22c143468d3d9db64269b1defa7d).
Thanks to xet7.
- [Fix migrations](https://github.com/wekan/wekan/commit/0acbf30b0346f49c0ee8f5161fb00b4eca8e1a0c).
Thanks to xet7.
- [Fix card popup to use HTML date, not anymore JQuery date](https://github.com/wekan/wekan/commit/2d44881619d78e8ef4c5060d17e9035f5babd778).
Thanks to xet7.
- [Fix Bug: Scale of Minicard icons is linked to horizontal screensize](https://github.com/wekan/wekan/commit/b6b0c5fe6d7dbd37926c662f96f2e3653cabd867).
Thanks to xet7.
- [Fix Bug Member settings drops to the second line and overlaps when many boards are starred as favourites](https://github.com/wekan/wekan/commit/46d46e313cbb8d9c3e4a976ec27b5141c266050f).
Thanks to xet7.
- [Some mobile view fixes](https://github.com/wekan/wekan/commit/c4af4d03acc02f3e54e91f2a65bce2f88742b1a6).
Thanks to xet7.
- [Have all iPhone use mobile view by default, while still having possibility to use mobile/desktop switch button for desktop mode](https://github.com/wekan/wekan/commit/5df4efd7ba06e618e454f068df05885306283bb1).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.05 2025-10-17 WeKan ® release
This release fixes the following bugs:
- [Show original positions of swimlanes, lists and cards](https://github.com/wekan/wekan/commit/2543df94252c2789fb484ae52b9a6ff298252ceb).
Thanks to xet7.
- Fix popups issues at Edit Avatar, Archive card confirm, etc.
[Part 1](https://github.com/wekan/wekan/commit/87ae085e6d0a56a2083eec819cf7d795d3e51e1a),
[Part 2](https://github.com/wekan/wekan/commit/386aea7c788d6eaf9d486ead4d81453401adf390).
Thanks to xet7.
- [Changed wekan-boostrap-datepicker to HTML datepicker](https://github.com/wekan/wekan/commit/79b94824efedaa9e256de931fd26398eb2838d6a).
Thanks to xet7.
- [Replaced moment.js with Javascript date](https://github.com/wekan/wekan/commit/cb6afe67a7363af89663ba17392dc5f90a15f703).
Thanks to xet7.
- [Convert Font Awesome to Unicode Icons. Part 1. In Progress](https://github.com/wekan/wekan/commit/2947238a021b6952b56e828d49a8c0094520d89a).
Thanks to xet7.
- [Resize height of swimlane by dragging. Font Awesome to Unicode icons](https://github.com/wekan/wekan/commit/09631d6b0c1b8e3bbc3bf45d4bb65449b46f1288).
Thanks to xet7.
- [Removed not needed visible text from mobile desktop switch button](https://github.com/wekan/wekan/commit/62ede481966107405460f6d5b90f292c98bae254).
Thanks to xet7.
- Font Awesome to Unicode icons.
[Part 3](https://github.com/wekan/wekan/commit/3af94c2a9059a399b9f9946c387caff892ace2f9).
[Part 4](https://github.com/wekan/wekan/commit/088bc16072ea0dd02aa2dec6a2e3e9aed00a3cc9).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.04 2025-10-16 WeKan ® release
This release fixes the following bugs:
- [Make sure that all cards are visible](https://github.com/wekan/wekan/commit/6b848b318d62afe9772218febdb09c7426774f60).
Thanks to xet7.
- [Fix wide screen](https://github.com/wekan/wekan/commit/f08c7702eecf23588f7bc023beefb453edd704c6).
Thanks to xet7.
- Fix popups positioning.
[Part 1](https://github.com/wekan/wekan/commit/77eea4d494e5db8e2c0e59732bcea73aa163bc13),
[Part 1](https://github.com/wekan/wekan/commit/00ddec75754bbbccc6fb9b3096495b9609246480).
Thanks to xet7.
- [Remove using fork with MongoDB at Snap](https://github.com/wekan/wekan/commit/690481c138f9629054180310dd172295c7f6d34e).
Thanks to xet7.
- [Use only MongoDB 7 at Snap](https://github.com/wekan/wekan/commit/79e83e33ec1dcec4eea81d5fb4a9f7381c176a12).
Thanks to xet7.
- [Removed extra npm packages](https://github.com/wekan/wekan/commit/dd88483ec7526eee4a97bac5f09e03985be5d923).
Thanks to xet7.
- [Try to fix Broken Hyperlinks in Markdown to HTML conversion](https://github.com/wekan/wekan/commit/bbbd3abf06e45a3fa57c4aa987d87f1873eb11d6).
Thanks to xet7.
- [Disable not working minio and s3 support temporarily](https://github.com/wekan/wekan/commit/4283b5b0e330930fff1fa2bb73c355a4ffb4cda0).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.03 2025-10-14 WeKan ® release
This release fixes the following bugs:
- [Fix Snap MongoDB to not fork at systemd, so it stays running](https://github.com/wekan/wekan/commit/5792a869594b4c79a93db414b95a13d60013193b).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.02 2025-10-14 WeKan ® release
This release adds the following new features:
- [Run database migrations when opening board. Not when upgrading WeKan](https://github.com/wekan/wekan/commit/2b5c56484a4dd559f062ef892fd5248a903b2a10).
Thanks to xet7.
- [Added Cron Manager to Admin Panel for long running jobs, like running migrations when opening board, copying or moving boards swimlanes lists cards etc](https://github.com/wekan/wekan/commit/da68b01502afc9d5d9ea1267bee9fc98bb08b611).
Thanks to xet7.
- [If there is no cron jobs running, run migrations for boards that have not been opened yet](https://github.com/wekan/wekan/commit/317138ab7209a41715336ea8251df45f11a6d173).
Thanks to xet7.
- [Accessibility improvements](https://github.com/wekan/wekan/commit/67b078b8056ec9851caaf6ef855719de1e6d966d).
Thanks to xet7.
- [Change list width by dragging between lists](https://github.com/wekan/wekan/commit/abad8cc4d5dded0f5e1a80892a3b29aa71404a5c).
Thanks to xet7.
and adds the following updates:
- [Updated dependencies](https://github.com/wekan/wekan/commit/5bc03b23ea34816d8e1135cbe9ed5f18a2573854).
Thanks to developers of dependencies.
and fixes the following bugs:
- [Fixes to make board showing correctly](https://github.com/wekan/wekan/commit/bd8c565415998c9aaded821988d591105258b378).
Thanks to xet7.
- [Fix opening sidebar](https://github.com/wekan/wekan/commit/0fd781e80aaf841c26ce59caffc579b9c391330f).
Thanks to xet7.
- [Fix Admin Panel menus "Attachment Settings" and "Cron Settings" and make them translateable](https://github.com/wekan/wekan/commit/033919a2702fa6959b8f8c87f076d3f255ace6ba).
Thanks to xet7.
- Change Admin Panel "Attachment Settings" and "Cron Settings" options to be tabs, not submenu.
[Part 1](https://github.com/wekan/wekan/commit/ae2aa1f5cd2511e80e12a91426eb91bb968dff98),
[Part 2](https://github.com/wekan/wekan/commit/5a6faafa30fefcd5dd0af7cc52b847a54d538065),
[Part 3](https://github.com/wekan/wekan/commit/2148aeea42f69fa367bf8c451d7f1c3a63b52880).
Thanks to xet7.
- [Fixed Error in migrate-lists-to-per-swimlane migration](https://github.com/wekan/wekan/commit/cc99da5357fb1fc00e3b5aece20c57917f88301b).
Thanks to xet7.
- Fix Admin Panel Settings menu to show Attachments and Cron options correctly.
[Part 1](https://github.com/wekan/wekan/e0013b9b631eb16861b1cfdb25386bf8e9099b4e),
[Part 2](https://github.com/wekan/wekan/7bb1e24bda2ed9db0bad0fafcf256680c2c05e8a).
- [Fixed migrations](https://github.com/wekan/wekan/commit/63c314ca185aeda650c01b4a67fcde1067320d22).
Thanks to xet7.
- [Removed not needed console log message](https://github.com/wekan/wekan/commit/0a34ee1b6437dcfd65e31d9bbc9f3ccfa5718ba9).
Thanks to xet7.
- [Updated mobile Bookmarks/Starred boards. Part 1. In Progress](https://github.com/wekan/wekan/commit/da98942cce37363d6062695d3c4cf7e2df796cac).
Thanks to xet7.
- [Fix drag drop reorder swimlanes](https://github.com/wekan/wekan/commit/a4518bbefc99be74f7ccfdbb9fdf902007ca90f3).
Thanks to xet7.
- [Try to fix swimlane hamburger menu popup positioning. In progress](https://github.com/wekan/wekan/commit/d4f13de1d978b271d05e1d67d40e3c1c14761578).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.01 2025-10-11 WeKan ® release
This release adds the following new features:
- [Made possible to start WeKan immediately without running any database migrations](https://github.com/wekan/wekan/commit/3ccdc2e3070978a79bc68941375122504318677d).
Thanks to xet7.
- [Add support for MongoDB 3-8 client, detecting which one is in use](https://github.com/wekan/wekan/commit/74ccfea5703b08b2166593d1bb8e1d981970cec0).
Thanks to xet7.
- [Add support for Snap MongoDB Server 3-8 support. Part 1](https://github.com/wekan/wekan/commit/b949357e665d97604cbecb04a9211319ac4e13c2).
Thanks to xet7.
- [Added attachments API and admin panel attachment management for file storage backends settings. Fixed drag drop upload attachments from file manager to minicard or opened card](https://github.com/wekan/wekan/commit/ae1f80a52cde09689dddb1209708630b4949b7ee).
Thanks to xet7.
and fixes the following bugs:
- [Fixed attachments migrations at Admin Panel to not use too much CPU while migrating attachments](https://github.com/wekan/wekan/commit/d59683eff1267ff87a6aef9ae36c7aebbe10eaa1).
Thanks to xet7.
- [Fixed per-card and per-board settings of showing checkist at minicard](https://github.com/wekan/wekan/commit/fc32a89292e4d314bd3868739f6e01bc2bc17018).
Thanks to xet7.
- [Fix count of Orgs Teams People at Admin Panel](https://github.com/wekan/wekan/commit/b77eed221f9e5fc657234a6de3e11c27f4de3d38).
Thanks to xet7.
- [Fixed attachments and minicard related bugs that prevented WeKan starting](https://github.com/wekan/wekan/commit/a86ff1e8d03d330a7ea0702cf3677859ed552509).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.00 2025-10-11 WeKan ® release
This release adds the following new features:
- [Snap: Migrate MongoDB from 3 to 7 only when "snap set wekan migrate-mongodb='true'". Not automatically](https://github.com/wekan/wekan/commit/ae01ea576c3073acbc181a5b19008ddaea4700e1).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v7.99 2025-10-11 WeKan ® release
This release fixed the following bugs:
- [Improve automatic Snap upgrades](https://github.com/wekan/wekan/commit/aab671398c7ee3d7ea4934c6c9c977ad630fa74f).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v7.98 2025-10-11 WeKan ® release
This release adds the following CRITICAL SECURITY FIXES:
- [Security Fix: IDOR CWE-639 that affected WeKan 7.80-7.93](https://github.com/wekan/wekan/commit/b87cff1289d2c98ef84d69e171f112ecedb4d843).
Thanks to Romain Korpas at apitech.fr and xet7.
- [Security Fix: Computational Resource Abuse in Export endpoints](https://github.com/wekan/wekan/commit/d0f118e7af0b2ede517d6d051226c38fa8e557b6).
Thanks to Anynymous Security Researcher and xet7.
- [Security Fix FG-VD-22-078: Prevent SVG Billion Laughs Attack](https://github.com/wekan/wekan/commit/30c1597b658b0ef50fd2efc56786e8b0f08ac72c).
Thanks to Nguyen Thanh Nguyen of Fortinet's FortiGuard Labs and xet7.
- [Security Fix usd-2022-0041: CWE-284 Improper Access Control](https://github.com/wekan/wekan/commit/f6591d7820e01075cba93612a5fdbf692fbe49dc).
Thanks to Christian Pöschl of usd AG and xet7.
- [Security Fix JVN#14269684: Broken access control](https://github.com/wekan/wekan/commit/9720e703fd9432bf0e1bfea2358f8c7ea078f1b1).
Thanks to Ryoya Koyama of Mitsui Bussan Secure Directions, Inc and xet7.
- [Security Fix JVN#74210258: Stored XSS](https://github.com/wekan/wekan/commit/e1fa607f87d821accb846f2deef1f388003848d1).
Thanks to Ryoya Koyama of Mitsui Bussan Secure Directions, Inc and xet7.
- [Security Fix JVN#86586539: Stored XSS](https://github.com/wekan/wekan/commit/ee79cab7b27f73fab62a00ec49add73fd6f7bcaa).
Thanks to Ryoya Koyama of Mitsui Bussan Secure Directions, Inc and xet7.
- [Security Fix JVN#15385465: CWE-79 XSS, that affected WeKan 7.94](https://github.com/wekan/wekan/commit/81c3dc1d956cd7040655940f6569653d7b98fa9a).
Thanks to Sho Sugiyama and xet7.
- Security Fix JVN#80785288: CWE-79 XSS, that affected WeKan 3.94 and v3.95. This was already previously fixed.
Thanks to xet7.
and adds the following new features:
- [Mobile one board per row. Board zoom size percent. Board toggle mobile/desktop mode. In Progress](https://github.com/wekan/wekan/commit/752699d1c2fb8ea9ff0f3ec9ae0b2b776443d826).
Thanks to xet7.
- Drag any files from file manager to minicard or opened card.
[Part 1](https://github.com/wekan/wekan/commit/3e9481c5bd2c02ba501bd0a6ef1d1e6ce82bb1d9),
[Part 2](https://github.com/wekan/wekan/commit/cdd7d69c660d0b6ac06b7b75d4f59985b8a9322a).
Thanks to xet7.
- [Use attachments from old CollectionFS database structure, when not yet migrated to Meteor-Files/ostrio-files, without needing to migrate database structure](https://github.com/wekan/wekan/commit/a8de2f224f61d0e5c7061fefb1cdd4f45f3bb020).
Thanks to xet7.
- [Show console.log 'Legacy attachments route loaded' only when environment variable DEBUG=true](https://github.com/wekan/wekan/commit/1c84b19f246ebe6d3cad4f56726013147756d9c3).
Thanks to xet7.
- [Make possible for lists to have different names at different swimlanes. Make possible to drag list from one swimlane to another swimlane](https://github.com/wekan/wekan/commit/719ef87efceacfe91461a8eeca7cf74d11f4cc0a).
Thanks to xet7.
- [Add support for Docker/Compose Secrets for passwords to Docker/Snap/Bundle platforms](https://github.com/wekan/wekan/commit/107e2ac90043138ea8aa09aadd92f4af355289c0).
Thanks to Roemer and xet7.
- Add Snap automatic upgrades.
[Part 1](https://github.com/wekan/wekan/commit/0549bc0b0c7dd4a5db53d645662dd4362ef4d1f3),
[Part 2](https://github.com/wekan/wekan/commit/f1e1fd359364dbe6366501ac4f6a150d73509f17).
Thanks to xet7.
and fixes the following bugs:
- Fix DOMPurify paths.
[Part 1](https://github.com/wekan/wekan/commit/90899f0928274cbc30ed3d6df0227664efcd2584),
[Part 2](https://github.com/wekan/wekan/commit/77691244018f3a1e44c28973b40a363bf77f99b2),
[Part 3](https://github.com/wekan/wekan/commit/21ba0a96066a495e885fcf186f026cf1c7ddf45d),
[Part 4](https://github.com/wekan/wekan/commit/2119c6ab0c2d76c8a9b22fa712b35b38345fc040).
Thanks to xet7.
- [Fix sizes of drag handles at desktop mode](https://github.com/wekan/wekan/commit/734165f3c78e9ba9643826da62f78844273f18f6).
Thanks to xet7.
- [Fixed showing translations always, regardsless of is ROOT_URL set correctly or not](https://github.com/wekan/wekan/commit/1a7bd65e596a4a1030e2686a787186b6d1c61abe).
Thanks to xet7.
- [Snap: Removed double mongo3 that's already at migratemongo](https://github.com/wekan/wekan/commit/2a24918a9c31e541f4d6424b77cfd76a8d9f2b06).
Thanks to xet7.
- [Updated release scripts of snapcraft pack command syntax](https://github.com/wekan/wekan/commit/d88d197de964fd71e61ea6c932521f1e5abe7630).
Thanks to xet7.
- [Try to fix Snap automatic upgrade](https://github.com/wekan/wekan/commit/4ec4e19e637b86b05b11afd076fae09d3698b8de).
Thanks to xet7.
- [Added missing metadata fields to snapcraft.yaml](https://github.com/wekan/wekan/commit/cd948fb5765efdf5a7d792180a25ffa26bab2730).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v7.97 2025-10-08 WeKan ® release
This release fixes the following bugs:
- [Fixed Collapse button not there when using clearlight board theme](https://github.com/wekan/wekan/pull/5900).
Thanks to seve12.
- [Fixed translation of "Change Language" at login page](https://github.com/wekan/wekan/commit/9a31371de0e155d3144769f45a1fa216872935eb).
Thanks to xet7.
- [Some mobile and tablet fixes. Show drag handles at touch screens only. No toggle of drag handles anymore](https://github.com/wekan/wekan/commit/3fda90612d88fc5a8f1e17ae5c399745de06aada).
Thanks to xet7.
- [Removed white box that appeared when clicking something](https://github.com/wekan/wekan/commit/3814a218c2dab3ed9e8908c3a2de996ffebf6d9d).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v7.96 2025-10-07 WeKan ® release
This release adds the following updates:
- [Updated release script for new command of uploading snap package to Snap Store](https://github.com/wekan/wekan/commit/dec9b82da0d700aafd7e2b2063b5d5b07dd383c3).
Thanks to xet7.
- [Updated to MongoDB 7.0.25 at Snap Candidate](https://github.com/wekan/wekan/commit/dec038e7edce11e2ce59de974aa870d7266af33b).
Thanks to MongoDB developers.
and fixes the following bugs:
- [Added missing mobile drag drop image to docs from wekan.github.io git history](https://github.com/wekan/wekan/commit/5151814e98599c22fff98dc7c42fbf634004ab20).
Thanks to xet7.
- [Fix links in docker-compose.yml to point to doc in repo](https://github.com/wekan/wekan/pull/5882).
Thanks to Skylark13.
- [Fixed find.sh script errors about unmatched single quotes and binary files that are now ignored](https://github.com/wekan/wekan/commit/730bb6d166baa5530cc75b4c94ae46c8077884b0).
Thanks to xet7.
- [Fixed links at docs](https://github.com/wekan/wekan/commit/6f2b89121402df8ecba88295ad09f5c4179ab0ed).
Thanks to xet7.
- [Added missing screenshots from wekan.github.io history to wekan/docs/Features/](https://github.com/wekan/wekan/commit/9a7fa25ca33d4267e193f0f9bb97b09c490e44fe).
Thanks to xet7.
- [Some accessibility, saving, downloading and editor fixes](https://github.com/wekan/wekan/pull/5895).
Thanks to seve12.
- [Hide extra keyboard shortcuts toggle](https://github.com/wekan/wekan/commit/62ad588aa982cd4305a8817cf21f0818234f4354).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v7.95 2025-09-18 WeKan ® release
This release adds the following updates:
- [Updated upgrade link at repo readme](https://github.com/wekan/wekan/commit/b3d3bf159376d56048361a385f0b295667e2319c).
Thanks to xet7.
- [Fixed image link at docs](https://github.com/wekan/wekan/commit/ad7e3774c2b29265a8fbefdde7b9b83822af6def).
Thanks to xet7.
- [More info about ToroDB, FerretDB, Turso, TigerBeetle, PostgreSQL, SQLite etc](https://github.com/wekan/wekan/commit/701485d76aded5ee4fa17967d94918132a821af6).
Thanks to xet7.
- [Added missing image to WeKan Multiverse page](https://github.com/wekan/wekan/commit/946f7045e92e26bbe8d182634844ee5eebf73564).
Thanks to xet7.
- [Added more info to Windows Offline docs about WRITABLE_PATH and ATTACHMENTS_STORE_PATH](https://github.com/wekan/wekan/pull/5872).
Thanks to sowwos.
- [Added PGP public key for sending encrypted email](https://github.com/wekan/wekan/commit/dd80b7308bb6c66113c8e6009c442a44a29e477a).
Thanks to xet7.
- [Updated dependencies](https://github.com/wekan/wekan/commit/c4161c5ce69a35c76364b13bcc456905f6335162).
Thanks to developers of dependencies.
- [Replace bitnami mongodb Helm chart by groundhog2k one](https://github.com/wekan/charts/pull/44).
Thanks to ariep.
Thanks to above GitHub users for their contributions and translators for their translations.
# v7.94 2025-08-18 WeKan ® release
This release adds the following new features:
- [Feature: Accessibility page at /accessibility. Settings at Admin Panel. When enabled, link at right sidebar](https://github.com/wekan/wekan/commit/d83ce5e6335c175f2f4a384f6090c18c7f860555).
Thanks to xet7.
- [Feature: Added brute force login protection settings to Admin Panel/People/Locked Users](https://github.com/wekan/wekan/commit/ae0d059b6feb38a438ca21aa154c63b410641763).
Added filtering of Admin Panel/People/People: All Users/Locked Users Only/Active/Not Active.
Added visual indicators: red lock icon for locked users, green check for active users, and red X for inactive users.
Added "Unlock All" button to quickly unlock all brute force locked users.
Added ability to toggle user active status directly from the People page.
Moved lockout settings from environment variables to database so admins can configure the lockout thresholds directly in the UI.
Thanks to xet7.
- Added docs for Windows SSL/TLS.
[Part 1](https://github.com/wekan/wekan/commit/92c0543614fb92cf8217d8cc24a367296f3ade37),
[Part 2](https://github.com/wekan/wekan/commit/ce528b1826255ebf3ac9533dd8ed91bec8bbe8b1),
[Part 3](https://github.com/wekan/wekan/commit/ee01a07b0ac7c6b06054b3d7bfac8c38161adf19),
[Part 4](https://github.com/wekan/wekan/commit/cc78e19740ae1bf42f2bbb17f801fe2d4dac135d),
[Part 5](https://github.com/wekan/wekan/commit/044793cc4473bebc8eefa64a0df9e9a20115d049),
[Part 6](https://github.com/wekan/wekan/commit/a7bcfc6db7cc78892ed20cdf0d03f84094853687).
Thanks to xet7.
and adds the following updates:
- Update CHANGELOG.md platforms info at top.
[Part 1](https://github.com/wekan/wekan/commit/0e43a08689c0d47c816ce781e93650f0c37efe28),
[Part 2](https://github.com/wekan/wekan/commit/4bc2fd4a7fc166041a5c483aad73bf8079c3ae45),
[Part 3](https://github.com/wekan/wekan/commit/62269a99024744c789ac931921c96d5edae9d79e),
[Part 4](https://github.com/wekan/wekan/commit/4aad3dafd4530cc7779072fc9a550d8732f32558),
[Part 5](https://github.com/wekan/wekan/commit/c2aabbc6db9ffbb73d9f06edb73a15a9f4838df4),
[Part 6](https://github.com/wekan/wekan/commit/b0865c8d06a31d07b5b5df719871c298e1cf4ad8),
[Part 7](https://github.com/wekan/wekan/commit/30795a46cfb5f37837206a6de653913bfb631595),
[Part 8](https://github.com/wekan/wekan/commit/9a91214043d93307074cf33e54d40cb43c14ad62).
- [Updated security email address](https://github.com/wekan/wekan/commit/11872545734db82229e873b48f122d826457fec3).
Thanks to xet7.
- Updated GitHub Docker Actions.
[Part 1](https://github.com/wekan/wekan/pull/5855),
[Part 2](https://github.com/wekan/wekan/pull/5856),
[Part 3](https://github.com/wekan/wekan/pull/5864),
[Part 4](https://github.com/wekan/wekan/pull/5865).
Thanks to Dependabot.
- [Added info about Sandstorm Radicale Calendar and Contacts](https://github.com/wekan/wekan/commit/dfdd0b6cbbbb60bf6dba7a61f34dc52e74f98b41).
Thanks to xet7.
- [Updated dependencies](https://github.com/wekan/wekan/commit/97b5931a4f717541631dde55f2b834d037e1d82d).
Thanks to developers of dependencies.
- Reorganized Design Docs.
[Part 1](https://github.com/wekan/wekan/commit/f34490fe2820352c8303f0bac47ad7158592e8f0),
[Part 2](https://github.com/wekan/wekan/commit/a662283b7de91c0ef63195691e06b5cbc6cdff6c).
Thanks to xet7.
- [Added related link to design docs](https://github.com/wekan/wekan/commit/a662283b7de91c0ef63195691e06b5cbc6cdff6c).
Thanks to xet7.
- [Upgraded to MongoDB 7.0.23 at Snap Candidate](https://github.com/wekan/wekan/commit/8e01e3c762748269c2e60f9f349dac30f30971d7).
Thanks to MongoDB developers.
and fixes the following bugs:
- [Fixes for building at macOS](https://github.com/wekan/wekan/commit/200545822c5c778521f76525938fe777ed9666f0).
Thanks to xet7.
- [Improves accessibility by disabling custom Tab handling](https://github.com/wekan/wekan/pull/5853).
Thanks to seve12, co-maintainer of WeKan.
- [Comment out not used Tab code at accessibility related changes](https://github.com/wekan/wekan/commit/0e32f666eb30cc254177119907e6eae3c09ce0ca).
Thanks to xet7.
- [Fixed link at OpenShift docs](https://github.com/wekan/wekan/commit/b9887fc91758edb31259a8df7770a1d992bc9251).
Thanks to xet7.
- [Added FerretDB/PostgreSQL to changelog](https://github.com/wekan/wekan/commit/c16e99ba1ec7bc25a4e0449ea463ea8f4b02da11).
Thanks to xet7.
- [Accessibility](https://github.com/wekan/wekan/pull/5857):
Added product name to page titles, settings and global search.
More accessible header and layout templates, modal dialogs, DOM structure, color contrast.
Thanks to seve12.
- [Fixed uppercase K of WeKan](https://github.com/wekan/wekan/commit/b137108ae15a9c5685cca5f45cfa9480ec7d3510).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v7.93 2025-07-18 WeKan ® release
This release adds the following new features:
- [Enhance API documentation and add board-related endpoints](https://github.com/wekan/wekan/pull/5815).
Thanks to Atry.
- Snap Candidate: If CPU does not support AVX, use Qemu that supports AVX, so that MongoDB 6 works.
[Part 1](https://github.com/wekan/migratemongo/commit/e7ca69584f021c4de16062f7074f177cca7ab25c),
[Part 2](https://github.com/wekan/migratemongo/commit/e34efeeced9e293ff6ea12bf36486eab3ce3a3f5),
[Part 3](https://github.com/wekan/wekan/commit/59ab6c1cd8ca3d71616201b02de6536b15b2303d).
Thanks to xet7.
- [Added docs about using MongoDB at unsupported CPUs with Qemu](https://github.com/wekan/wekan/commit/f926448bbbbd570ad1e6a1015c755b12c6b229c0).
Thanks to xet7.
- [Helm Chart: Add podAnnotations support](https://github.com/wekan/charts/pull/42).
Thanks to adalinesimonian.
- [Added release script to switch between using kvm, snapcraft.io/multipass, waydroid and virtualbox](https://github.com/wekan/wekan/commit/dfc658b910f038fa6d6ae2e06c190eb8e6205a78).
Thanks to xet7.
- [Add a REST API endpoint to retrieve a card by ID](https://github.com/wekan/wekan/pull/5828).
Thanks to Atry.
- [Avoid calling ReactiveCache.getCard twice](https://github.com/wekan/wekan/pull/5829).
Thanks to Atry.
- [Add REST API endpoint to edit a list with various properties](https://github.com/wekan/wekan/pull/5832).
Thanks to Atry.
and adds the following major updates to links of WeKan websites, blog and docs:
- These changes are because most people at Finland expect Finnish company to be at .fi domain.
- It did take too much time at local computer shop to explain about domain being .team
- Most content have been moved to wekan.fi . For app stores https://wekan.fi/app/ there is still wekan.team used for app.
- Many wikis and images from wekan.github.io have been moved to wekan repo docs directory
- Pull requests welcome to fix any broken links at docs directory, and organizing docs/Features and their screenshots to subdirectories of each feature.
- [Updated issue template about pull requests for fixes to docs](https://github.com/wekan/wekan/commit/e4822d8bc4dd0c200b63983013625d2c3677e9f9).
Thanks to xet7.
- wekan.team to wekan.fi.
[Part 1](https://github.com/wekan/wekan/commit/790bafbb096a606cdb34a1d942478ae53174ff1c),
[Part 2](https://github.com/wekan/wekan/commit/b52d1ca6c47fbeeb3c5f93111809a1863478b833).
Thanks to xet7.
- [Moved images from wekan.github.io to repo wekan/docs](https://github.com/wekan/wekan/commit/c5e5bb613e9d0e8652bce24a1cecb620dd8e7cd4).
Thanks to xet7.
- [wekan.github.io to wekan.fi, and blog.wekan.team to wekan.fi/blog](https://github.com/wekan/wekan/commit/89480b61ce98d5a344108ad7c2ce9b3577fd3d7e>
Thanks to xet7.
- Update Browser-compatibility-matrix.md about
[Servo](https://github.com/wekan/wekan/commit/c256ce69688e47675245bf68f13d3025b2a7d7ab),
[uWolf](https://github.com/wekan/wekan/commit/ad3306e19e422b9b8ce70a13e862bc107fec1eba),
[TUI webbrowsers](https://github.com/wekan/wekan/commit/7841c9e801c737aab07e9bd68f8d84f9e4becc3e),
[Chawan](https://github.com/wekan/wekan/commit/be1b9595196e2072ec2259de9b81bc3b823722eb) and
[Elinks](https://github.com/wekan/wekan/commit/12e6e469249c9392181df20f51bcc684e2656960).
Thanks to xet7.
- Updated links, after moving many wikis to wekan repo docs directory:
[Part 1: Updated docs](https://github.com/wekan/wekan/commit/31e043c1d7c0d4031054234241f6a1b7479f2134),
[Part 2: Removed footer](https://github.com/wekan/wekan/commit/40a52da2d3fd472847b9bfd2696f3f9a72021b52),
[Part 3: Moved content from Home.md to README.md](https://github.com/wekan/wekan/commit/2a415cbd2661bd210fdd18626f1e606f73334bd2),
[Part 4: Updated links at readme](https://github.com/wekan/wekan/commit/9aa028f32dc7c186d55ada422b73a7e331de2cbb),
[Part 5: Updated deep dive link](https://github.com/wekan/wekan/commit/11415cd51781bde20375c6d98fccde17227136fd),
[Part 6: Fixed links](https://github.com/wekan/wekan/commit/cc7459b98395f15fd2c97992f74c279a93f8956a),
[Part 7: Updated links](https://github.com/wekan/wekan/commit/4bfc0140bef8504ba24b237a9c5c525a5bf3fb7d),
[Part 8: Reorganized docs file locations](https://github.com/wekan/wekan/commit/f54cbac37008fc99f19fb20930d2fa5ecb535a69),
[Part 9: Fix Discord image link and WeKan API link](https://github.com/wekan/wekan/commit/3a8473e6326bff2f17ada1bb48f0c7f38b356721),
[Part 10: Fix links about docs that were moved from wiki to readme](https://github.com/wekan/wekan/commit/bd796ce64248dc58ff4a192ca58796096dbef019),
[Part 11: Updated building Snap](https://github.com/wekan/wekan/commit/aa681cde88d17ee0fd957233c52297e9ab0047ce),
[Part 12: Updated Snap platforms setting](https://github.com/wekan/wekan/commit/6b401000b91dc46b810dfc27964246891b7cd68c),
[Part 13: Fix link to image about PWA app icon install. Thanks to stevenroose and xet7](https://github.com/wekan/wekan/commit/9271b07383c3544bd1368f95e79a7d4d637d5922),
[Part 14: Formatting](https://github.com/wekan/wekan/commit/fa1adfec768ad1d34f3fbd45e6332c6c0db87b05),
[Part 15: Removed old link Test Edge from docs index](https://github.com/wekan/wekan/commit/5d0a0c931f3d4ea2e65fcc412f1eeaa6cc2107d5),
[Part 16: Fix link to image at docs](https://github.com/wekan/wekan/commit/2cf57250e8bc6f6f220f0a0f8dc85a5196f80138),
[Part 17: Fixed not working link](https://github.com/wekan/wekan/commit/76769d1b855032ef17aeae3c82558cd301e2da0d),
[Part 18: Added most often used links](https://github.com/wekan/wekan/commit/4fe955650a11d77a06a319a2e707ae97d77b471f),
[Part 19: Update info about hashing](https://github.com/wekan/wekan/commit/5d09100cb6cf0cc686112587327c8cb993a559e4),
[Part 20: Updated docs about Kubernetes](https://github.com/wekan/wekan/commit/a052cc8fa5bffad652cf585cf5cab84e13afa442),
[Part 21: Removed not working link from docs](https://github.com/wekan/wekan/commit/a2654b2e2950974cf25e6acaa36a6d5a4285564c),
[Part 22: Fix docs link about CSV. Part 1](https://github.com/wekan/wekan/commit/3892be47ac0fc0ff3bdafcf44ce818eea47fe6b0),
[Part 23: Fix docs link about CSV. Part 2](https://github.com/wekan/wekan/commit/a9c67ac2006592ac575a5cb68fe575d8ac3f4d6a),
[Part 24: Updated docs about importing from Trello. Part 1](https://github.com/wekan/wekan/commit/55ef6c0c56b3b383edeb2984bcdfbf2ab7e74e6d),
[Part 25: Updated docs about importing from Trello. Part 2](https://github.com/wekan/wekan/commit/40b2cf88fc7c8a94fd9ad8690ca4ca68add68d5f),
[Part 26: Updated docs about importing from Trello. Part 3](https://github.com/wekan/wekan/commit/1610b84845f332eabc8e204a917ca5daa284d24d),
[Part 27: Updated docs about importing from Asana](https://github.com/wekan/wekan/commit/9adf5027fafbb9f44507dcc703a38de9b6215287),
[Part 28: Updated docs about migrating manually. Part 1](https://github.com/wekan/wekan/commit/450b33f98a8527423e6c5cee98bb5020a77e113f),
[Part 29: Updated docs about migrating manually. Part 2](https://github.com/wekan/wekan/commit/2f27a3f839a26a773b3a2cf8e7f93d3930b01b1a),
[Part 30: Updated docs](https://github.com/wekan/wekan/commit/d59337ae47876396c2571aa803bd57236abfc328),
[Part 31: Updated Azure link](https://github.com/wekan/wekan/commit/a5acd0801aa55b49243bdc1c001c226546537cb3),
[Part 32: Updated Keycloak link](https://github.com/wekan/wekan/commit/e0284f225b6567eb90ac0224fd348cf8e4e87229),
[Part 33: Updated image links at Azure docs](https://github.com/wekan/wekan/commit/6d16ec238a00f456494c84e08ea13eb166e42f6e),
[Part 34: Updated Python docs](https://github.com/wekan/wekan/commit/bb0814bb11bf00e73c3c48c9954792c6ca09e3be).
Thanks to xet7.
and adds the following updates:
- [Updated dependencies](https://github.com/wekan/wekan/commit/e6980be061a6ab54d592cd86f1079a7672a3e081).
Thanks to developers of dependencies.
- [Upgraded Snap Candidate to have base core24, MongoDB 7 and Caddy 2](https://github.com/wekan/wekan/commit/6526d99123b047fe4dfcf8f60a23f65d1261324c).
Thanks to xet7.
- [Removed old broken not used snap configuration files](https://github.com/wekan/wekan/commit/b7409c1d0b574b77f0fa95f7289d815671c1e224).
Thanks to xet7.
- [Updated release website script](https://github.com/wekan/wekan/commit/6c6b66c802e88e8dcc7d9215b9caad4c269861c5).
Thanks to xet7.
and fixes the following bugs:
- [CleanDark Theme Fixes](https://github.com/wekan/wekan/pull/5805).
Thanks to walster001.
- Specify meteor version, otherwise the latest meteor version is not compatible with node14.
[Part 1](https://github.com/wekan/wekan/pull/5816),
[Part 2](https://github.com/wekan/wekan/commit/51e43f1c254abee7ee12e8fdab8c52695e858fa2).
Thanks to dassio and xet7.
- [Reorganized docs to fix building WeKan](https://github.com/wekan/wekan/commit/6e11aa27f896e41c57980b45b59dc8248259421f).
Thanks to xet7.
- Fixed rebuild-wekan.sh install deps for macOS.
[Part 1](https://github.com/wekan/wekan/commit/938fe49b2c0dd523e3402b514b13ba5b605dac2b),
[Part 2](https://github.com/wekan/wekan/commit/2df73bedeebf6e6da2e4003bd236668e185f6701).
Thanks to xet7.
- [Fixed release script of building Snap locally at Ubuntu](https://github.com/wekan/wekan/commit/159324858bb2a72761082265558329eee0c0863d).
Thanks to xet7.
- [Fix: Correct return type in API documentation for Custom Fields](https://github.com/wekan/wekan/pull/5826).
Thanks to Atry.
- [Fixed Snap local build script](https://github.com/wekan/wekan/commit/69ce79a7b9e86b60482d42455701b58ae9926c69).
Thanks to xet7.
- [Fixed building Docker image. Pushed new Docker image to Docker Hub, Quay and GitHub](https://github.com/wekan/wekan/commit/f894f9ef6d8bad47260d0b648f18c36a5b208517).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v7.92 2025-06-12 WeKan ® release
This release adds the following updates:
- [Updated Docker build push action](https://github.com/wekan/wekan/pull/5795).
Thanks to dependabot.
- [Updated to MongoDB 6.0.24 at Snap Candidate](https://github.com/wekan/wekan/commit/6905e24199186274f37d8bbdfac313f957fdd54f).
Thanks to MongoDB developers.
- Updated dependencies.
[Part 1](https://github.com/wekan/wekan/commit/7115c292c7066d8d00778fa738bb11c2db467895),
[Part 2](https://github.com/wekan/wekan/commit/1f10c208744ec45e062db08a179b6e9a3b1bcd2e).
Thanks to developers of dependencies.
- [Upgrade to MongoDB 7.0.21](https://github.com/wekan/wekan/commit/d2f17ef1402335e4b3bd9de8864c68f97714febf).
Thanks to MongoDB developers.
- [Snap Candidate back to MongoDB 6.0.24, because 7.0.21 did not work](https://github.com/wekan/wekan/commit/985afafed0eb608fb95d4e54f846836b1fb0e800).
Thanks to xet7.
and fixes the following bugs:
- [Allow selection of card text via webkit/user-select attributes](https://github.com/wekan/wekan/pull/5793).
Thanks to walster001.
- [Helm Chart: Fix: quote host names](https://github.com/wekan/charts/pull/41).
Thanks to Atry.
- [Fix: properly document /users/login API](https://github.com/wekan/wekan/pull/5801).
Thanks to Atry.
Thanks to above GitHub users for their contributions and translators for their translations.
# v7.91 2025-05-25 WeKan ® release
This release adds the following updates:
- [Updated Docker build push action](https://github.com/wekan/wekan/pull/5780).
Thanks to dependabot.
and fixes the following bugs:
- Reverted due date fixes.
[Part 1](https://github.com/wekan/wekan/commit/1979b1692dcaf6fd909cabfb894635f1e6a66fa8),
[Part 2](https://github.com/wekan/wekan/commit/aaa5f9885d37a369606e61a759af37f444ec606d).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v7.90 2025-05-14 WeKan ® release
This release fixes the following bugs:
- [Fix Due Date Problem in Non-English Numbers](https://github.com/wekan/wekan/pull/5774).
Thanks to valhalla-creator.
- [Added missing quotes to Fix Due Date Problem in Non-English Numbers](https://github.com/wekan/wekan/commit/c0a9780f803ed445a93a274d13504a8f245c1885).
Thanks to xet7.
- [Changed start.bat default ROOT_URL=http://localhost , although it only works for local user](https://github.com/wekan/wekan/commit/e52158b729c8ba39a55fe52e38fd6b134b42548e).
Thanks to xet7.
- [Fix Lines in multiline markdown code block to not anymore have darker background](https://github.com/wekan/wekan/commit/b82ba63532b32ec3a0c860a380648cef6739db0f).
Thanks to mueller-ma and xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v7.89 2025-05-13 WeKan ® release
Note: Docker image is only at ghcr.io (GitHub) and Docker Hub (wekanteam/wekan).
Quay.io is at read only mode, they are fixing something, so it's not possible to
push Docker image yet to quay.io .
This release adds the following new features:
- [Add email notifications language localization feature](https://github.com/wekan/wekan/pull/5769).
Thanks to Adamsss001.
and adds the following updates:
- [Added script to build snap locally and push to snapcraft.io candidate and edge](https://github.com/wekan/wekan/commit/072ced9d0e53dc6c8964e9b37fc7d1036962ca19).
Thanks to xet7.
- [Updated Browser Compatibility Matrix by adding Servo](https://github.com/wekan/wekan/commit/394f2eae69542067fde9e2511ffcef165d170d2d).
Thanks to xet7.
- [Updated Caddy docs](https://github.com/wekan/wekan/commit/9aa12b0eb62962bd107fe1aa87cdf5bad5a5905f).
Thanks to xet7.
- [Upgraded to MongoDB 6.0.23 at Snap Candidate](https://github.com/wekan/wekan/commit/0bd410866196bc2faa76b9c26c12f42617321a03).
Thanks to MongoDB developers.
and fixes the following bugs:
- [Fix list insertion order and prevent runtime error in "Add After" feature](https://github.com/wekan/wekan/pull/5767).
Thanks to valhalla-creator.
- [Add missing code of fix insertion order of list](https://github.com/wekan/wekan/commit/7d1a1475baefb3fd20da3df835c349a62b425041).
Thanks to xet7.
- [Fix: Add CSS vendor prefixes for user-select and text-size-adjust for better browser compatibility](https://github.com/wekan/wekan/pull/5772).
Thanks to oussama-madimagh.
Thanks to above GitHub users for their contributions and translators for their translations.
# v7.88 2025-04-25 WeKan ® release
This release fixes the following bugs:
- [Reverted translation fix that sometimes did not work](https://github.com/wekan/wekan/commit/c825895cebd24355d076741512c9aa16844393d9).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v7.87 2025-04-25 WeKan ® release
Note: This release does not work, there is bugs. Use newest release.
This release fixes the following bugs:
- [Fix typos](https://github.com/wekan/wekan/commit/d0ea5bf50067fd76359330986edbad8dd1fbcdcc).
Thanks to xet7.
- [Fix more contrasting issues](https://github.com/wekan/wekan/pull/5750).
Thanks to walster001.
Thanks to above GitHub users for their contributions and translators for their translations.
# v7.86 2025-04-25 WeKan ® release
Note: This release does not work, there is typos. Use newest release.
This release adds the following updates:
- [Improve impersonate user documentation in docs/Admin/Impersonate-user.md](https://github.com/wekan/wekan/pull/5746).
Thanks to nourguidara.
and fixes the following bugs:
- [Add toggle watch icon on card. Add questions are you sure to duplicate board and archive board](https://github.com/wekan/wekan/pull/5745).
Thanks to Rayene123.
- [Fix translations not working](https://github.com/wekan/wekan/pull/5748).
Thanks to nourguidara.
- [Fix contrast issue checkbox at Exodark theme](https://github.com/wekan/wekan/pull/5749).
Thanks to walster001.
Thanks to above GitHub users for their contributions and translators for their translations.
# v7.85 2025-04-21 WeKan ® release
This release adds the following updates:
- Updated Browser compatibility matrix, adding Iris browser at RISC OS Direct, and fixing links.
[Part1](https://github.com/wekan/wekan/commit/db5346fc5c7407160f381c0fcf4a87204206ed55),
[Part2](https://github.com/wekan/wekan/commit/05d1736f5f21e93e83b2e25029c6cab6c5106398).
Thanks to xet7.
- [Updated to MongoDB 6.0.22](https://github.com/wekan/wekan/commit/c1a4250bd2e26be5549704234a3cfb5306120352).
Thanks to MongoDB developers.
and fixes the following bugs:
- [Fix board sidebar menu doesn't open in mobile](https://github.com/wekan/wekan/commit/01950cc796697fa201ac9e4376cb204d308e8181).
Thanks to mimZD and xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v7.84 2025-03-23 WeKan ® release
This release adds the following new features:
- [Time on minicard for Start/End/Due Date](https://github.com/wekan/wekan/pull/5712).
Thanks to Rayene123.
and adds the following updates:
- [Added developer docs about Login code](https://github.com/wekan/wekan/commit/c0e4e01deb936653df69b4fd21598ac27cd349a0).
Thanks to xet7.
- Updated dependencies.
[Part 1](https://github.com/wekan/wekan/pull/5718),
[Part 2](https://github.com/wekan/wekan/commit/bb6ac70f63b6f3568b7c943417c698615936a956).
Thanks to developers of dependencies.
and fixes the following bugs:
- [Fixed typo at Shortcuts, now became visible translations of assignees visibility 1-9 shortcuts](https://github.com/wekan/wekan/commit/4510ddda1507bc775b9523d3673adba48d7a8385).
Thanks to xet7.
- [Fixed building OpenAPI docs](https://github.com/wekan/wekan/commit/a2911bc9c3bd94ef583388931d2cd354cc8657e3).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v7.83 2025-03-08 WeKan ® release
This release adds the following updates:
- [Adding Development information to readme](https://github.com/wekan/wekan/pull/5702).
Thanks to adam-fu.
- Updated GitHub actions.
[Part 1](https://github.com/wekan/wekan/pull/5703),
[Part 2](https://github.com/wekan/wekan/pull/5704).
and fixes the following bugs:
- [Removed siteurl from manifest to fix browser console error about not matching URL](https://github.com/wekan/wekan/commit/b402676079517e463ed291285f69c04126a00975).
Thanks to xet7.
- [Exception while invoking method 'copyCard' TypeError: Cannot read property 'name' of undefined](https://github.com/wekan/wekan/pull/5711).
Thanks to Firas-Git.
- [Comment out error message of non-existing Custom Field](https://github.com/wekan/wekan/commit/908a5fc60d5d574a36d4bfd496d3a16fd267ae75).
Thanks to xet7.
- [Reverted Docker changes of WeKan v7.68 to get WeKan Docker version working](https://github.com/wekan/wekan/commit/e7462ada12ec8edfd08d6c3b3a97a034211d51eb).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v7.82 2025-02-24 WeKan ® release
v7.82 Docker version is broken: https://github.com/wekan/wekan/issues/5697 . Snap Candidate works.
This release adds the following new features:
- [Add possibility to use a token in place of ipaddress to access metrics route](https://github.com/wekan/wekan/pull/5682).
Thanks to salleman33.
and adds the following updates:
- [Updated dependencies](https://github.com/wekan/wekan/pull/5691).
Thanks to dependabot.
- [Updated requirements at docs](https://github.com/wekan/wekan/commit/148b81262d0d143460e881d645fefa6740aae40d).
Thanks to mueller-ma.
- [Updated dependencies](https://github.com/wekan/wekan/commit/666ee8403388f7d5e1a30cf0e53bc46a70bf1c40).
Thanks to developes of dependencies.
- [Fixed building WeKan. Updated dompurify. Forked Meteor 2.14 version of meteor-node-stubs to update elliptic](https://github.com/wekan/wekan/commit/18d0fa43275cd2955dd6416213e316ca08a62255).
Thanks to developers of depedencies and xet7.
and fixes the following bugs:
- [Added missing ) character](https://github.com/wekan/wekan/commit/563a508e269be87eb713e2888409525e1ba82001).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v7.81 2025-02-07 WeKan ® release
This release adds the following new features:
- [Helm Chart: Added secretManaged value for enabling or disabling the creation of secret by Helm](https://github.com/wekan/charts/pull/39).
Thanks to salleman33.
and adds the following updates:
- [Updated Docker Actions](https://github.com/wekan/wekan/pull/5670).
Thanks to dependabot.
- [Added Meteor 3.1 learning course to docs](https://github.com/wekan/wekan/commit/0c7e12c5e7f322bdbaaa61100e66153dd0b92e4d).
Thanks to producer of learning course.
- [Upgraded to MongoDB 6.0.20 at Snap Candidate](https://github.com/wekan/wekan/commit/b571f1c9530b899db75bf28a03c18277a9b77cb8).
Thanks to MongoDB developers.
and fixes the following bugs:
- [Fixed env variable METRICS_ACCEPTED_IP_ADDRESS to be same as at docs](https://github.com/wekan/wekan/commit/0b1e0bd39569175668c195b63dde91bf0e6f1b24).
Thanks to salleman33.
- [Fixed misspelling of hours at env variable setting LDAP_BACKGROUND_SYNC_INTERVAL](https://github.com/wekan/wekan/commit/36a307785369337a788499065f64175971878930).
Thanks to hubermam.
- [Helm Chart: Restore pod security context in deployment](https://github.com/wekan/charts/pull/40).
Thanks to adalinesimonian.
Thanks to above GitHub users for their contributions and translators for their translations.
# v7.80 2025-01-12 WeKan ® release
This release fixes the following bugs:
- [Fix card updating issues with ReactiveCache when using keyboard shortcuts](https://github.com/wekan/wekan/pull/5654).
Thanks to NadavTasher.
- [Fix assignee toggling keyboard shortcut to only toggle current board members](https://github.com/wekan/wekan/pull/5655).
Thanks to NadavTasher.
Thanks to above GitHub users for their contributions and translators for their translations.
# v7.79 2025-01-02 WeKan ® release
This release adds the following new features:
- [Add toggle for week-of-year in date displays (ISO 8601)](https://github.com/wekan/wekan/pull/5652).
Thanks to NadavTasher.
- [Assign members using keyboard shortcut Ctrl+Alt+(1-9)](https://github.com/wekan/wekan/pull/5653).
Thanks to NadavTasher.
Thanks to above GitHub users for their contributions and translators for their translations.
# v7.78 2024-12-31 WeKan ® release
@@ -1918,7 +2759,7 @@ This release adds the following new features:
- [Count of attachments on minicard. (Badges renamed to Count of attachments)](https://github.com/wekan/wekan/pull/4940).
Thanks to helioguardabaxo.
- [Renamed Badge to Count of attachments on minicard. Changed icons to Cover image on minicard](https://github.com/wekan/wekan/commit/825742359ef0738b110ed0904853c604bdbd1bac).
Thanks to xet7.
Thanks to xet7.
- [Added conditional to show after list field only if board has at least one list. Now after list shows the last list by default](https://github.com/wekan/wekan/pull/4941).
Thanks to helioguardabaxo.
- [Changed Add List to be at left. Before it was at right](https://github.com/wekan/wekan/commit/467835192fbcd9d4016674fa2ee406258cc106e7).
+1 -1
View File
@@ -11,7 +11,7 @@ For all code at WeKan GitHub Organization https://github.com/wekan
## Private reports
- Email support@wekan.team
- Email support@wekan.fi
- Security issues: [SECURITY.md](SECURITY.md)
- License violations
- Anything private, sensitive or negative
+1 -1
View File
@@ -11,7 +11,7 @@ Not paid:
Paid by customers of WeKan Team:
- Commercial Support at https://wekan.team/commercial-support/
- Commercial Support at https://wekan.fi/commercial-support/
- Support
- Private Chat
- Features
+154 -96
View File
@@ -1,76 +1,31 @@
FROM scratch AS downloads
# Download NodeJS bundle
ADD https://static.meteor.com/dev-bundle-node-os/v14.21.4/node-v14.21.4-linux-x64.tar.gz node.tar.gz
# Download Meteor bundle
ADD https://static.meteor.com/packages-bootstrap/2.16/meteor-bootstrap-os.linux.x86_64.tar.gz meteor.tar.gz
FROM ubuntu:24.04 AS build
# Install node from official archive
RUN --mount=type=bind,from=downloads,source=node.tar.gz,target=node.tar.gz \
tar xzf node.tar.gz --strip-components=1 --keep-old-files --no-same-owner
# Install meteor from official archive
RUN --mount=type=bind,from=downloads,source=meteor.tar.gz,target=meteor.tar.gz \
tar xzf meteor.tar.gz -C $HOME --no-same-owner
# Update the npm version
RUN npm install -g npm@6.14.17
# Install several build dependencies
RUN apt update && \
apt install --yes --no-install-suggests --no-install-recommends ssh git g++ curl ca-certificates && \
rm -r /var/lib/apt/lists
# Change to temporary working directory
WORKDIR /workdir
# Copy package.json and package-lock.json to install application dependencies
COPY package.json .
COPY package-lock.json .
# Install build dependencies
RUN $HOME/.meteor/meteor npm install --production
# Copy meteor application configurations
COPY .meteor .meteor
# Copy application sources
COPY packages packages
COPY imports imports
COPY config config
COPY models models
COPY public public
COPY server server
COPY client client
# Build the application
RUN $HOME/.meteor/meteor build --directory /build --allow-superuser
# Enter server bundle directory
WORKDIR /build/bundle/programs/server
# Install server dependencies
RUN $HOME/.meteor/meteor npm install --production
# Enter installation directory of fibers
WORKDIR /build/bundle/programs/server/node_modules/fibers
# Build fibers integrations
RUN node build.js
FROM ubuntu:24.04 AS wekan
# Add metadata to image
FROM ubuntu:24.04
LABEL maintainer="wekan"
LABEL org.opencontainers.image.ref.name="ubuntu"
LABEL org.opencontainers.image.version="24.04"
LABEL org.opencontainers.image.source="https://github.com/wekan/wekan"
# Set default environment variables
ENV WITH_API=true \
# 2022-04-25:
# - gyp does not yet work with Ubuntu 22.04 ubuntu:rolling,
# so changing to 21.10. https://github.com/wekan/wekan/issues/4488
# 2021-09-18:
# - Above Ubuntu base image copied from Docker Hub ubuntu:hirsute-20210825
# to Quay to avoid Docker Hub rate limits.
ARG DEBIAN_FRONTEND=noninteractive
ENV BUILD_DEPS="apt-utils gnupg gosu wget bzip2 g++ curl libarchive-tools build-essential git ca-certificates python3 unzip"
ENV \
DEBUG=false \
NODE_VERSION=v14.21.4 \
METEOR_RELEASE=METEOR@2.14 \
USE_EDGE=false \
METEOR_EDGE=1.5-beta.17 \
NPM_VERSION=6.14.17 \
FIBERS_VERSION=4.0.1 \
ARCHITECTURE=linux-x64 \
SRC_PATH=./ \
WITH_API=true \
RESULTS_PER_PAGE="" \
DEFAULT_BOARD_ID="" \
ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURES_BEFORE=3 \
@@ -111,6 +66,7 @@ ENV WITH_API=true \
OAUTH2_LOGIN_STYLE=redirect \
OAUTH2_CLIENT_ID="" \
OAUTH2_SECRET="" \
OAUTH2_SECRET_FILE="" \
OAUTH2_SERVER_URL="" \
OAUTH2_AUTH_ENDPOINT="" \
OAUTH2_USERINFO_ENDPOINT="" \
@@ -136,6 +92,7 @@ ENV WITH_API=true \
LDAP_AUTHENTIFICATION=false \
LDAP_AUTHENTIFICATION_USERDN="" \
LDAP_AUTHENTIFICATION_PASSWORD="" \
LDAP_AUTHENTIFICATION_PASSWORD_FILE="" \
LDAP_LOG_ENABLED=false \
LDAP_BACKGROUND_SYNC=false \
LDAP_BACKGROUND_SYNC_INTERVAL="" \
@@ -201,38 +158,139 @@ ENV WITH_API=true \
ORACLE_OIM_ENABLED=false \
WAIT_SPINNER="" \
WRITABLE_PATH=/data \
S3=""
S3="" \
MAIL_SERVICE_PASSWORD_FILE="" \
MONGO_PASSWORD_FILE="" \
S3_SECRET_FILE=""
# Install node and application sources from build stage
RUN --mount=type=bind,from=downloads,source=node.tar.gz,target=node.tar.gz \
--mount=type=bind,from=build,source=/build,target=/build \
# Install NodeJS
tar xzf node.tar.gz --strip-components=1 --keep-old-files --no-same-owner && \
# Add the wekan user
useradd --uid 999 --user-group --system wekan && \
# Copy wekan bundle
cp -r /build/bundle /wekan && \
# Remove the web.browser.legacy platform
rm -r /wekan/programs/web.browser.legacy && \
# Change ownership of wekan directory
chown wekan:wekan -R /wekan && \
# Create the data directory
mkdir /data && \
# Change ownership of data directory
chown wekan:root -R /data
# NODE_OPTIONS="--max_old_space_size=4096"
#---------------------------------------------
# == at docker-compose.yml: AUTOLOGIN WITH OIDC/OAUTH2 ====
# https://github.com/wekan/wekan/wiki/autologin
#- OIDC_REDIRECTION_ENABLED=true
#---------------------------------------------------------------------
# Copy the app to the image
#COPY ${SRC_PATH} /home/wekan/app
# Install OS
RUN <<EOR
set -o xtrace
# Add non-root user wekan
useradd --user-group --system --home-dir /home/wekan wekan
# OS dependencies
apt-get update --assume-yes
apt-get install --assume-yes --no-install-recommends ${BUILD_DEPS}
# Meteor installer doesn't work with the default tar binary, so using bsdtar while installing.
# https://github.com/coreos/bugs/issues/1095#issuecomment-350574389
cp $(which tar) $(which tar)~
ln -sf $(which bsdtar) $(which tar)
# Install NodeJS
cd /tmp
# Download nodejs
#wget "https://github.com/wekan/node-v14-esm/releases/download/${NODE_VERSION}/node-${NODE_VERSION}-${ARCHITECTURE}.tar.gz"
wget "https://github.com/wekan/node-v14-esm/releases/download/v14.21.4/node-v14.21.4-linux-x64.tar.gz"
#wget "https://github.com/wekan/node-v14-esm/releases/download/${NODE_VERSION}/SHASUMS256.txt"
wget "https://github.com/wekan/node-v14-esm/releases/download/v14.21.4/SHASUMS256.txt"
# Verify nodejs authenticity
#grep "node-${NODE_VERSION}-${ARCHITECTURE}.tar.gz" "SHASUMS256.txt" | shasum -a 256 -c -
grep "node-v14.21.4-linux-x64.tar.gz" "SHASUMS256.txt" | shasum -a 256 -c -
rm -f "SHASUMS256.txt"
# Install Node
#tar xzf "node-$NODE_VERSION-$ARCHITECTURE.tar.gz" -C /usr/local --strip-components=1 --no-same-owner
tar xzf "node-v14.21.4-linux-x64.tar.gz" -C /usr/local --strip-components=1 --no-same-owner
#rm "node-$NODE_VERSION-$ARCHITECTURE.tar.gz" "SHASUMS256.txt"
rm "node-v14.21.4-linux-x64.tar.gz" "SHASUMS256.txt"
ln -s "/usr/local/bin/node" "/usr/local/bin/nodejs"
#mkdir -p "/opt/nodejs/lib/node_modules/fibers/.node-gyp" "/root/.node-gyp/${NODE_VERSION} /home/wekan/.config"
#mkdir -p "/opt/nodejs/lib/node_modules/fibers/.node-gyp" "/root/.node-gyp/v14.21.4 /home/wekan/.config"
# Install node dependencies
#npm install -g npm@${NPM_VERSION} --production
npm install -g npm@$6.14.17 --production
chown --recursive wekan:wekan /home/wekan/.config
# Install Meteor
cd /home/wekan
chown --recursive wekan:wekan /home/wekan
echo "Starting meteor ${METEOR_RELEASE} installation... \n"
#gosu wekan:wekan curl https://install.meteor.com/ | /bin/sh
# Specify Meteor version 2.14 to be compatible: https://github.com/wekan/wekan/pull/5816/files
#gosu wekan:wekan npm -g install meteor@2.14 --unsafe-perm
#mv /root/.meteor /home/wekan/
#chown --recursive wekan:wekan /home/wekan/.meteor
#sed -i 's/api\.versionsFrom/\/\/api.versionsFrom/' /home/wekan/app/packages/meteor-useraccounts-core/package.js
#cd /home/wekan/.meteor
#gosu wekan:wekan /home/wekan/.meteor/meteor -- help
# Build app (Production)
#cd /home/wekan/app
mkdir -p /home/wekan/app
cd /home/wekan/app
#mkdir -p /home/wekan/.npm
#chown --recursive wekan:wekan /home/wekan/.npm
#chmod u+w *.json
#gosu wekan:wekan meteor npm install --production
#gosu wekan:wekan /home/wekan/.meteor/meteor build --directory /home/wekan/app_build
#cd /home/wekan/app_build/bundle/programs/server/
#chmod u+w *.json
#gosu wekan:wekan meteor npm install --production
#cd node_modules/fibers
#node build.js
#cd ../..
# Remove legacy webbroser bundle, so that Wekan works also at Android Firefox, iOS Safari, etc.
#rm -rf /home/wekan/app_build/bundle/programs/web.browser.legacy
#mv /home/wekan/app_build/bundle /build
wget "https://github.com/wekan/wekan/releases/download/v8.08/wekan-8.08-amd64.zip"
unzip wekan-8.08-amd64.zip
rm wekan-8.08-amd64.zip
mv /home/wekan/app/bundle /build
# Put back the original tar
mv $(which tar)~ $(which tar)
# Cleanup
apt-get remove --purge --assume-yes ${BUILD_DEPS}
#npm uninstall -g api2html
apt-get autoremove --assume-yes
apt-get clean --assume-yes
rm -Rf /tmp/*
rm -Rf /var/lib/apt/lists/*
rm -Rf /var/cache/apt
rm -Rf /var/lib/apt/lists
rm -Rf /home/wekan/app_build
rm -Rf /home/wekan/app
rm -Rf /home/wekan/.meteor
mkdir /data
chown wekan --recursive /data
EOR
# Change to the wekan user
USER wekan
# Set the port to listen on
ENV PORT=8080
EXPOSE ${PORT}
EXPOSE $PORT
# Define the stop signal for node
STOPSIGNAL SIGINT
STOPSIGNAL SIGKILL
WORKDIR /home/wekan/app
# Declare a volume on /data
VOLUME /data
# Set startup command
CMD ["node", "/wekan/main.js"]
#---------------------------------------------------------------------
# https://github.com/wekan/wekan/issues/3585#issuecomment-1021522132
# Add more Node heap:
# NODE_OPTIONS="--max_old_space_size=4096"
# Add more stack:
# bash -c "ulimit -s 65500; exec node --stack-size=65500 main.js"
#---------------------------------------------------------------------
#
# CMD ["node", "/build/main.js"]
# CMD ["bash", "-c", "ulimit -s 65500; exec node --stack-size=65500 /build/main.js"]
# CMD ["bash", "-c", "ulimit -s 65500; exec node --stack-size=65500 --max-old-space-size=8192 /build/main.js"]
CMD ["bash", "-c", "ulimit -s 65500; exec node /build/main.js"]
+6 -1
View File
@@ -53,7 +53,12 @@ ENV QEMU_ARCHITECTURE=aarch64 \
WITH_API=true \
PORT=8080 \
ROOT_URL=http://localhost \
MONGO_URL=mongodb://127.0.0.1:27017/wekan
MONGO_URL=mongodb://127.0.0.1:27017/wekan \
LDAP_AUTHENTIFICATION_PASSWORD_FILE="" \
OAUTH2_SECRET_FILE="" \
MAIL_SERVICE_PASSWORD_FILE="" \
MONGO_PASSWORD_FILE="" \
S3_SECRET_FILE=""
# Copy qemu-static to image
COPY --from=builder qemu-${QEMU_ARCHITECTURE}-static /usr/bin
+6 -1
View File
@@ -56,7 +56,12 @@ ENV QEMU_ARCHITECTURE=s390x \
WITH_API=true \
PORT=8080 \
ROOT_URL=http://localhost \
MONGO_URL=mongodb://127.0.0.1:27017/wekan
MONGO_URL=mongodb://127.0.0.1:27017/wekan \
LDAP_AUTHENTIFICATION_PASSWORD_FILE="" \
OAUTH2_SECRET_FILE="" \
MAIL_SERVICE_PASSWORD_FILE="" \
MONGO_PASSWORD_FILE="" \
S3_SECRET_FILE=""
# Copy qemu-static to image
COPY --from=builder qemu-${QEMU_ARCHITECTURE}-static /usr/bin
+1 -1
View File
@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2014-2024 The Wekan Team
Copyright (c) Lauri Ojansivu
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+23 -26
View File
@@ -1,10 +1,8 @@
[Gitpod Ready-to-Code](https://gitpod.io/#https://github.com/wekan/wekan)
# WeKan ® - Open Source kanban
## Downloads
https://wekan.github.io / Install WeKan ® Server
https://wekan.fi/install/
## Docker Containers
@@ -16,7 +14,7 @@ docker-compose.yml at https://github.com/wekan/wekan/blob/main/docker-compose.ym
## Standards
- [WeKan and Standard for Public Code](https://wekan.github.io/standard-for-public-code/) assessment was made at 2023-11.
- [WeKan and Standard for Public Code](https://wekan.fi/standard-for-public-code/) assessment was made at 2023-11.
Currently Wekan meets 8 out of 16 criteria out of the box.
Some others could be met with small changes.
@@ -27,21 +25,19 @@ docker-compose.yml at https://github.com/wekan/wekan/blob/main/docker-compose.ym
- [Open Hub](https://www.openhub.net/p/wekan)
- [OSS Insight](https://ossinsight.io/analyze/wekan/wekan)
## [Translate WeKan ® at Transifex](https://app.transifex.com/wekan/)
## Translate WeKan ® at Transifex
Translations to non-English languages are accepted only at [Transifex](https://app.transifex.com/wekan/wekan) using webbrowser.
New English strings of new features can be added as PRs to master branch file wekan/imports/i18n/data/en.i18n.json .
## [WeKan ® feature requests and bugs](https://github.com/wekan/wekan/issues)
## WeKan ® feature requests and bugs
Please add most of your questions as GitHub issue: [WeKan ® Feature Requests and Bugs](https://github.com/wekan/wekan/issues).
It's better than at chat where details get lost when chat scrolls up.
## Chat
## Discussions
[Discussions][discussions] - WeKan Community GitHub Discussions, that are not [Feature Requests and Bugs](https://github.com/wekan/wekan/issues).
[WeKan IRC FAQ](https://github.com/wekan/wekan/wiki/IRC-FAQ)
[IRC](https://github.com/wekan/wekan/blob/main/docs/FAQ/IRC-FAQ.md)
## Docker: Latest tag has newest release
@@ -51,8 +47,8 @@ See bottom of https://github.com/wekan/wekan/issues/3874
## FAQ
**NOTE**:
- Please read the [FAQ](https://github.com/wekan/wekan/wiki/FAQ) first
- Please don't feed the [trolls](https://github.com/wekan/wekan/wiki/FAQ#why-am-i-called-a-troll) and [spammers](https://github.com/wekan/wekan/wiki/FAQ#why-am-i-called-a-spammer) that are mentioned in the FAQ :)
- Please read the [FAQ](https://github.com/wekan/wekan/blob/main/docs/FAQ/FAQ.md) first
- Please don't feed the [trolls](https://github.com/wekan/wekan/blob/main/docs/FAQ/FAQ.md#why-am-i-called-a-troll) and [spammers](https://github.com/wekan/wekan/blob/main/docs/FAQ/FAQ.md#why-am-i-called-a-spammer) that are mentioned in the FAQ :)
## About WeKan ®
@@ -72,14 +68,11 @@ that by providing one-click installation on various platforms.
- WeKan ® largest user has 30k users using WeKan ® in their company.
- WeKan ® has been [translated](https://app.transifex.com/wekan/) to about 105 languages.
- [Features][features]: WeKan ® has real-time user interface.
- [Platforms][platforms]: WeKan ® supports many platforms.
- [Platforms](https://wekan.fi/install/): WeKan ® supports many platforms.
WeKan ® is critical part of new platforms Wekan is currently being integrated to.
## Requirements
- 64bit: Linux [Snap](https://github.com/wekan/wekan-snap/wiki/Install) or [Sandstorm](https://sandstorm.io) /
[Mac](https://github.com/wekan/wekan/wiki/Mac) / [Windows](https://github.com/wekan/wekan/wiki/Install-Wekan-from-source-on-Windows).
[More Platforms](https://github.com/wekan/wekan/wiki/Platforms), bundle for RasPi3 ARM and other CPUs where Node.js and MongoDB exists.
- 1 GB RAM minimum free for WeKan ®. Production server should have minimum total 4 GB RAM.
For thousands of users, for example with [Docker](https://github.com/wekan/wekan/blob/main/docker-compose.yml): 3 frontend servers,
each having 2 CPU and 2 wekan-app containers. One backend wekan-db server with many CPUs.
@@ -89,7 +82,7 @@ that by providing one-click installation on various platforms.
WeKan ® on Sandstorm is not usually affected by any Standalone WeKan ® (Snap/Docker/Source) security issues.
- [Reporting all new bugs immediately](https://github.com/wekan/wekan/issues).
New features and fixes are added to WeKan ® [many times a day](https://github.com/wekan/wekan/blob/main/CHANGELOG.md).
- [Backups](https://github.com/wekan/wekan/wiki/Backup) of WeKan ® database once a day miminum.
- [Backups](https://github.com/wekan/wekan/blob/main/docs/Backup/Backup.md) of WeKan ® database once a day miminum.
Bugs, updates, users deleting list or card, harddrive full, harddrive crash etc can eat your data. There is no undo yet.
Some bug can cause WeKan ® board to not load at all, requiring manual fixing of database content.
@@ -102,14 +95,22 @@ that by providing one-click installation on various platforms.
- There is many companies and individuals contributing code to WeKan ®, to add features and bugfixes
[many times a day](https://github.com/wekan/wekan/blob/main/CHANGELOG.md).
- [Please add Add new Feature Requests and Bug Reports immediately](https://github.com/wekan/wekan/issues).
- [Commercial Support](https://wekan.team/commercial-support/).
- [Commercial Support](https://wekan.fi/commercial-support/).
We also welcome sponsors for features and bugfixes.
By working directly with WeKan ® you get the benefit of active maintenance and new features added by growing WeKan ® developer community.
## Getting Started with Development
The default branch uses [Meteor 2 with Node.js 14](https://wekan.fi/install/).
To contribute, [create a fork](https://github.com/wekan/wekan/blob/main/docs/DeveloperDocs/Build-and-Create-Pull-Request.md#2-create-fork-of-httpsgithubcomwekanwekan-at-github-web-page) and run `./rebuild-wekan.sh` (or `./rebuild-wekan.bat` on Windows) as detailed [here](https://github.com/wekan/wekan/blob/main/docs/DeveloperDocs/Build-and-Create-Pull-Request.md#3-select-option-1-to-install-dependencies-and-then-enter). Once you're ready, please test your code and [submit a pull request (PR)](https://github.com/wekan/wekan/blob/main/docs/DeveloperDocs/Build-and-Create-Pull-Request.md#7-test).
Please refer to the [developer documentation](https://github.com/wekan/wekan/blob/main/docs/DeveloperDocs/Developer-Documentation.md) for more information.
## Screenshot
[More screenshots at Features page](https://github.com/wekan/wekan/wiki/Features)
[More screenshots at Features page](https://github.com/wekan/wekan/tree/main/docs/Features)
[![Screenshot of WeKan ®][screenshot_wekan]][roadmap_wekan]
@@ -118,16 +119,12 @@ By working directly with WeKan ® you get the benefit of active maintenance and
WeKan ® is released under the very permissive [MIT license](LICENSE), and made
with [Meteor](https://www.meteor.com).
[platforms]: https://github.com/wekan/wekan/wiki/Platforms
[dev_docs]: https://github.com/wekan/wekan/wiki/Developer-Documentation
[screenshot_wekan]: https://wekan.github.io/wekan-dark-mode.png
[dev_docs]: https://github.com/wekan/wekan/blob/main/docs/DeveloperDocs/Developer-Documentation.md
[screenshot_wekan]: https://wekan.fi/wekan-dark-mode.png
[features]: https://github.com/wekan/wekan/wiki/Features
[roadmap_wekan]: https://boards.wekan.team/b/D2SzJKZDS4Z48yeQH/wekan-open-source-kanban-board-with-mit-license
[wekan_issues]: https://github.com/wekan/wekan/issues
[wekan_issues]: https://github.com/wekan/wekan/issues
[docker_image]: https://hub.docker.com/r/wekanteam/wekan/
[wekan_wiki]: https://github.com/wekan/wekan/wiki
[translate_wekan]: https://app.transifex.com/wekan/
[translate_wekan]: https://app.transifex.com/wekan/wekan/
[open_source]: https://en.wikipedia.org/wiki/Open-source_software
[free_software]: https://en.wikipedia.org/wiki/Free_software
[discussions]: https://github.com/wekan/wekan/discussions
+113
View File
@@ -0,0 +1,113 @@
# 🔐 WeKan — Login System Overview
This document provides a detailed overview of WeKan’s **login and authentication system**, covering client-side UI, server-side logic, external authentication methods, and potential upgrade paths.
---
## 🖥️ Login Web UI
WeKan's login interface is implemented using a combination of:
- `layouts.jade` – Login HTML structure
- `layouts.js` – Login logic and interactivity
- `layouts.css` – Styling and layout
📁 Source: [`client/components/main`](https://github.com/wekan/wekan/tree/main/client/components/main)
---
## ⚙️ Server-Side Authentication
Server-side login functionality is handled in:
- [`server/authentication.js`](https://github.com/wekan/wekan/blob/main/server/authentication.js)
Other related configurations:
- 🔧 Account config: [`config/accounts.js`](https://github.com/wekan/wekan/blob/main/config/accounts.js)
- 📨 Sign-up invitations: [`models/settings.js#L275`](https://github.com/wekan/wekan/blob/main/models/settings.js#L275)
- 👤 User creation logic: [`models/users.js#L1339`](https://github.com/wekan/wekan/blob/main/models/users.js#L1339)
---
## 👥 Meteor User Accounts
WeKan utilizes Meteor’s `accounts` system. Relevant resources:
- 📚 Meteor 2.x Accounts Docs: [v2-docs.meteor.com/api/accounts](https://v2-docs.meteor.com/api/accounts)
- 🔍 Meteor Packages:
- [`packages`](https://github.com/wekan/wekan/blob/main/.meteor/packages)
- [`versions`](https://github.com/wekan/wekan/blob/main/.meteor/versions)
- 📦 Meteor 2.14 core packages: [Meteor 2.14 packages](https://github.com/meteor/meteor/tree/release/METEOR%402.14/packages)
---
## 🔐 External Authentication (OIDC, LDAP, etc.)
WeKan supports external authentication methods via internal packages.
📁 See [`packages/`](https://github.com/wekan/wekan/tree/main/packages) for:
- OpenID Connect (OIDC)
- LDAP
- OAuth and other integrations
---
## 📦 NPM & AtmosphereJS Dependencies
- 🔗 `package.json`: [Dependencies list](https://github.com/wekan/wekan/blob/main/package.json)
- 🧩 WekanTeam scoped NPM packages: [@wekanteam on npm](https://www.npmjs.com/search?q=%40wekanteam)
- ☁️ AtmosphereJS Meteor packages: [atmospherejs.com](https://atmospherejs.com)
---
## 🚧 Meteor Version & Upgrade Notes
- 📌 Current Version: **Meteor 2.14**
- [`.meteor/release`](https://github.com/wekan/wekan/blob/main/.meteor/release)
- 🔧 Maintained with only **critical fixes** until ~Summer 2025
- 🚀 Migration to **Meteor 3** or a new framework is under consideration
📘 Meteor 3 API: [docs.meteor.com/api/accounts](https://docs.meteor.com/api/accounts)
---
## 🧪 Prototypes & Examples
### 🐘 PHP Prototype Sign-Up
Used in experimental versions:
- Step 1: [`sign-up1.php`](https://github.com/wekan/php/blob/main/page/sign-up1.php)
- Step 2: [`sign-up2.php`](https://github.com/wekan/php/blob/main/page/sign-up2.php)
- Main entry: [`index.php#L72-L83`](https://github.com/wekan/php/blob/main/public/index.php#L72-L83)
---
### 🎨 WeKan Studio Prototype
Sign-up logic in the **WeKan Studio** version:
- [`signUp.fmt`](https://github.com/wekan/wekanstudio/blob/main/srv/templates/login/signUp.fmt)
---
## 📎 Future Considerations
- Upgrading to **Meteor 3.x**
- Refactoring frontend logic to fix translation rendering order
- Exploring **simplified authentication systems** in future prototypes
---
## 🔗 Project Links
- 🔧 Main Repo: [github.com/wekan/wekan](https://github.com/wekan/wekan)
- 🌐 Website: [wekan.github.io](https://wekan.github.io)
- 📚 Documentation: [Wekan Wiki](https://github.com/wekan/wekan/wiki)
---
---
+5 -4
View File
@@ -1,11 +1,12 @@
About money, see [CONTRIBUTING.md](CONTRIBUTING.md)
Security is very important to us. If you discover any issue regarding security, please disclose
the information responsibly by sending an email to security@wekan.team and not by
creating a GitHub issue. We will respond swiftly to fix verifiable security issues.
the information responsibly by sending an email from Protonmail to security@wekan.fi
that is Protomail email address, or by using this PGP key
[security-at-wekan.fi.asc](security-at-wekan.fi.asc) to security@wekan.fi
and not by creating a GitHub issue. We will respond swiftly to fix verifiable security issues.
We thank you with a place at our hall of fame page, that is
at https://wekan.github.io/hall-of-fame
We thank you with a place at our hall of fame page, that is at https://wekan.fi/hall-of-fame
## How should reports be formatted?
+1 -1
View File
@@ -1,5 +1,5 @@
appId: wekan-public/apps/77b94f60-dec9-0136-304e-16ff53095928
appVersion: "v7.78.0"
appVersion: "v8.08.0"
files:
userUploads:
- README.md
+276
View File
@@ -39,6 +39,12 @@ If *nix: chmod +x api.py => ./api.py users
python3 api.py addcustomfieldtoboard AUTHORID BOARDID NAME TYPE SETTINGS SHOWONCARD AUTOMATICALLYONCARD SHOWLABELONMINICARD SHOWSUMATTOPOFLIST # Add Custom Field to Board
python3 api.py editcustomfield BOARDID LISTID CARDID CUSTOMFIELDID NEWCUSTOMFIELDVALUE # Edit Custom Field
python3 api.py listattachments BOARDID # List attachments
python3 api.py uploadattachment BOARDID SWIMLANEID LISTID CARDID FILEPATH [STORAGE_BACKEND] # Upload attachment to card
python3 api.py downloadattachment ATTACHMENTID OUTPUTPATH # Download attachment to local file
python3 api.py attachmentinfo ATTACHMENTID # Get attachment information
python3 api.py listcardattachments BOARDID SWIMLANEID LISTID CARDID # List attachments for specific card
python3 api.py copymoveattachment ATTACHMENTID TARGETBOARDID TARGETSWIMLANEID TARGETLISTID TARGETCARDID [copy|move] # Copy or move attachment
python3 api.py deleteattachment ATTACHMENTID # Delete attachment
python3 api.py cardsbyswimlane SWIMLANEID LISTID # Retrieve cards list on a swimlane
python3 api.py getcard BOARDID LISTID CARDID # Get card info
python3 api.py addlabel BOARDID LISTID CARDID LABELID # Add label to a card
@@ -750,3 +756,273 @@ if arguments == 1:
data2 = body.text.replace('}',"}\n")
print(data2)
# ------- LIST OF PUBLIC BOARDS END -----------
# ------- NEW ATTACHMENT API ENDPOINTS START -----------
if sys.argv[1] == 'uploadattachment':
# ------- UPLOAD ATTACHMENT START -----------
if arguments < 5:
print("Usage: python3 api.py uploadattachment BOARDID SWIMLANEID LISTID CARDID FILEPATH [STORAGE_BACKEND]")
print("Storage backends: fs, gridfs, s3")
exit(1)
boardid = sys.argv[2]
swimlaneid = sys.argv[3]
listid = sys.argv[4]
cardid = sys.argv[5]
filepath = sys.argv[6]
storage_backend = sys.argv[7] if arguments > 6 else None
# Read file and convert to base64
try:
with open(filepath, 'rb') as f:
file_data = f.read()
import base64
base64_data = base64.b64encode(file_data).decode('utf-8')
except FileNotFoundError:
print(f"Error: File '{filepath}' not found")
exit(1)
except Exception as e:
print(f"Error reading file: {e}")
exit(1)
# Get file info
import os
filename = os.path.basename(filepath)
import mimetypes
file_type = mimetypes.guess_type(filepath)[0] or 'application/octet-stream'
# Prepare request data
upload_data = {
'boardId': boardid,
'swimlaneId': swimlaneid,
'listId': listid,
'cardId': cardid,
'fileData': base64_data,
'fileName': filename,
'fileType': file_type
}
if storage_backend:
upload_data['storageBackend'] = storage_backend
# Make API call
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey), 'Content-Type': 'application/json'}
upload_url = wekanurl + 'api/attachment/upload'
try:
response = requests.post(upload_url, headers=headers, json=upload_data)
response.raise_for_status()
result = response.json()
print(f"Upload successful!")
print(f"Attachment ID: {result.get('attachmentId')}")
print(f"File: {result.get('fileName')}")
print(f"Size: {result.get('fileSize')} bytes")
print(f"Storage: {result.get('storageBackend')}")
except requests.exceptions.RequestException as e:
print(f"Upload failed: {e}")
if hasattr(e, 'response') and e.response is not None:
print(f"Response: {e.response.text}")
# ------- UPLOAD ATTACHMENT END -----------
if sys.argv[1] == 'downloadattachment':
# ------- DOWNLOAD ATTACHMENT START -----------
if arguments < 3:
print("Usage: python3 api.py downloadattachment ATTACHMENTID OUTPUTPATH")
exit(1)
attachmentid = sys.argv[2]
outputpath = sys.argv[3]
# Make API call
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
download_url = wekanurl + f'api/attachment/download/{attachmentid}'
try:
response = requests.get(download_url, headers=headers)
response.raise_for_status()
result = response.json()
if result.get('success'):
# Decode base64 data and save to file
import base64
file_data = base64.b64decode(result.get('base64Data'))
with open(outputpath, 'wb') as f:
f.write(file_data)
print(f"Download successful!")
print(f"File saved to: {outputpath}")
print(f"Original filename: {result.get('fileName')}")
print(f"Size: {result.get('fileSize')} bytes")
print(f"Storage: {result.get('storageBackend')}")
else:
print(f"Download failed: {result.get('message', 'Unknown error')}")
except requests.exceptions.RequestException as e:
print(f"Download failed: {e}")
if hasattr(e, 'response') and e.response is not None:
print(f"Response: {e.response.text}")
# ------- DOWNLOAD ATTACHMENT END -----------
if sys.argv[1] == 'attachmentinfo':
# ------- ATTACHMENT INFO START -----------
if arguments < 2:
print("Usage: python3 api.py attachmentinfo ATTACHMENTID")
exit(1)
attachmentid = sys.argv[2]
# Make API call
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
info_url = wekanurl + f'api/attachment/info/{attachmentid}'
try:
response = requests.get(info_url, headers=headers)
response.raise_for_status()
result = response.json()
if result.get('success'):
print("=== ATTACHMENT INFO ===")
print(f"Attachment ID: {result.get('attachmentId')}")
print(f"File Name: {result.get('fileName')}")
print(f"File Size: {result.get('fileSize')} bytes")
print(f"File Type: {result.get('fileType')}")
print(f"Storage Backend: {result.get('storageBackend')}")
print(f"Board ID: {result.get('boardId')}")
print(f"Swimlane ID: {result.get('swimlaneId')}")
print(f"List ID: {result.get('listId')}")
print(f"Card ID: {result.get('cardId')}")
print(f"Created At: {result.get('createdAt')}")
print(f"Is Image: {result.get('isImage')}")
print(f"Versions: {len(result.get('versions', []))}")
else:
print(f"Failed to get attachment info: {result.get('message', 'Unknown error')}")
except requests.exceptions.RequestException as e:
print(f"Request failed: {e}")
if hasattr(e, 'response') and e.response is not None:
print(f"Response: {e.response.text}")
# ------- ATTACHMENT INFO END -----------
if sys.argv[1] == 'listcardattachments':
# ------- LIST CARD ATTACHMENTS START -----------
if arguments < 5:
print("Usage: python3 api.py listcardattachments BOARDID SWIMLANEID LISTID CARDID")
exit(1)
boardid = sys.argv[2]
swimlaneid = sys.argv[3]
listid = sys.argv[4]
cardid = sys.argv[5]
# Make API call
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
list_url = wekanurl + f'api/attachment/list/{boardid}/{swimlaneid}/{listid}/{cardid}'
try:
response = requests.get(list_url, headers=headers)
response.raise_for_status()
result = response.json()
if result.get('success'):
attachments = result.get('attachments', [])
print(f"=== CARD ATTACHMENTS ({len(attachments)}) ===")
for attachment in attachments:
print(f"ID: {attachment.get('attachmentId')}")
print(f"Name: {attachment.get('fileName')}")
print(f"Size: {attachment.get('fileSize')} bytes")
print(f"Type: {attachment.get('fileType')}")
print(f"Storage: {attachment.get('storageBackend')}")
print(f"Created: {attachment.get('createdAt')}")
print("---")
else:
print(f"Failed to list attachments: {result.get('message', 'Unknown error')}")
except requests.exceptions.RequestException as e:
print(f"Request failed: {e}")
if hasattr(e, 'response') and e.response is not None:
print(f"Response: {e.response.text}")
# ------- LIST CARD ATTACHMENTS END -----------
if sys.argv[1] == 'copymoveattachment':
# ------- COPY/MOVE ATTACHMENT START -----------
if arguments < 6:
print("Usage: python3 api.py copymoveattachment ATTACHMENTID TARGETBOARDID TARGETSWIMLANEID TARGETLISTID TARGETCARDID [copy|move]")
exit(1)
attachmentid = sys.argv[2]
targetboardid = sys.argv[3]
targetswimlaneid = sys.argv[4]
targetlistid = sys.argv[5]
targetcardid = sys.argv[6]
operation = sys.argv[7] if arguments > 6 else 'copy'
if operation not in ['copy', 'move']:
print("Operation must be 'copy' or 'move'")
exit(1)
# Prepare request data
request_data = {
'attachmentId': attachmentid,
'targetBoardId': targetboardid,
'targetSwimlaneId': targetswimlaneid,
'targetListId': targetlistid,
'targetCardId': targetcardid
}
# Make API call
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey), 'Content-Type': 'application/json'}
api_url = wekanurl + f'api/attachment/{operation}'
try:
response = requests.post(api_url, headers=headers, json=request_data)
response.raise_for_status()
result = response.json()
if result.get('success'):
print(f"{operation.capitalize()} successful!")
if operation == 'copy':
print(f"Source Attachment ID: {result.get('sourceAttachmentId')}")
print(f"New Attachment ID: {result.get('newAttachmentId')}")
else:
print(f"Attachment ID: {result.get('attachmentId')}")
print(f"Source Board: {result.get('sourceBoardId')}")
print(f"Target Board: {result.get('targetBoardId')}")
print(f"File: {result.get('fileName')}")
print(f"Size: {result.get('fileSize')} bytes")
else:
print(f"{operation.capitalize()} failed: {result.get('message', 'Unknown error')}")
except requests.exceptions.RequestException as e:
print(f"{operation.capitalize()} failed: {e}")
if hasattr(e, 'response') and e.response is not None:
print(f"Response: {e.response.text}")
# ------- COPY/MOVE ATTACHMENT END -----------
if sys.argv[1] == 'deleteattachment':
# ------- DELETE ATTACHMENT START -----------
if arguments < 2:
print("Usage: python3 api.py deleteattachment ATTACHMENTID")
exit(1)
attachmentid = sys.argv[2]
# Make API call
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
delete_url = wekanurl + f'api/attachment/delete/{attachmentid}'
try:
response = requests.delete(delete_url, headers=headers)
response.raise_for_status()
result = response.json()
if result.get('success'):
print("Delete successful!")
print(f"Attachment ID: {result.get('attachmentId')}")
print(f"File: {result.get('fileName')}")
else:
print(f"Delete failed: {result.get('message', 'Unknown error')}")
except requests.exceptions.RequestException as e:
print(f"Delete failed: {e}")
if hasattr(e, 'response') and e.response is not None:
print(f"Response: {e.response.text}")
# ------- DELETE ATTACHMENT END -----------
# ------- NEW ATTACHMENT API ENDPOINTS END -----------
+11
View File
@@ -4,3 +4,14 @@ if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/pwa-service-worker.js');
});
}
// Import board converter for on-demand conversion
import '/client/lib/boardConverter';
import '/client/components/boardConversionProgress';
// Import migration manager and progress UI
import '/client/lib/migrationManager';
import '/client/components/migrationProgress';
// Import cron settings
import '/client/components/settings/cronSettings';
+14 -14
View File
@@ -1,16 +1,16 @@
.activity-title {
margin: 0 0.5em 0.8em;
margin: 0 0.7vw 1vh;
display: flex;
justify-content: space-between;
}
.reactions-popup .add-comment-reaction {
display: inline-block;
cursor: pointer;
border-radius: 5px;
font-size: 22px;
border-radius: 0.7vw;
font-size: clamp(18px, 4vw, 22px);
text-align: center;
line-height: 30px;
width: 40px;
line-height: 1.3;
width: 5vw;
}
.reactions-popup .add-comment-reaction:hover {
background-color: #b0c4de;
@@ -19,13 +19,13 @@
clear: both;
}
.activities .activity {
margin: 0.5px 0;
padding: 6px 0;
margin: 0.1vh 0;
padding: 0.8vh 0;
display: flex;
}
.activities .activity .member {
width: 32px;
height: 32px;
width: 4vw;
height: 4vw;
}
.activities .activity .activity-member {
font-weight: 700;
@@ -36,18 +36,18 @@
flex: 1;
align-self: center;
margin: 0;
margin-left: 3px;
margin-left: 0.4vw;
overflow: hidden;
word-break: break-word;
}
.activities .activity .activity-desc .activity-comment {
display: block;
border-radius: 3px;
border-radius: 0.4vw;
background: #fff;
text-decoration: none;
box-shadow: 0 1px 2px rgba(0,0,0,0.2);
margin-top: 5px;
padding: 5px;
box-shadow: 0 0.2vh 0.3vh rgba(0,0,0,0.2);
margin-top: 0.7vh;
padding: 0.7vh 0.7vw;
}
.activities .activity .activity-desc .activity-checklist {
display: block;
+8 -11
View File
@@ -1,5 +1,6 @@
import { ReactiveCache } from '/imports/reactiveCache';
import DOMPurify from 'dompurify';
import { sanitizeHTML, sanitizeText } from '/imports/lib/secureDOMPurify';
import { TAPi18n } from '/imports/i18n';
const activitiesPerPage = 500;
@@ -216,15 +217,11 @@ BlazeComponent.extendComponent({
{
href: source.url,
},
DOMPurify.sanitize(source.system, {
ALLOW_UNKNOWN_PROTOCOLS: true,
}),
sanitizeHTML(source.system),
),
);
} else {
return DOMPurify.sanitize(source.system, {
ALLOW_UNKNOWN_PROTOCOLS: true,
});
return sanitizeHTML(source.system);
}
}
return null;
@@ -248,10 +245,10 @@ BlazeComponent.extendComponent({
href: `${attachment.link()}?download=true`,
target: '_blank',
},
DOMPurify.sanitize(attachment.name),
sanitizeText(attachment.name),
),
)) ||
DOMPurify.sanitize(this.currentData().activity.attachmentName)
sanitizeText(this.currentData().activity.attachmentName)
);
},
@@ -265,7 +262,7 @@ BlazeComponent.extendComponent({
Template.activity.helpers({
sanitize(value) {
return DOMPurify.sanitize(value, { ALLOW_UNKNOWN_PROTOCOLS: true });
return sanitizeHTML(value);
},
});
@@ -336,7 +333,7 @@ function createCardLink(card, board) {
href: card.originRelativeUrl(),
class: 'action-card',
},
DOMPurify.sanitize(text, { ALLOW_UNKNOWN_PROTOCOLS: true }),
sanitizeHTML(text),
),
)
);
@@ -353,7 +350,7 @@ function createBoardLink(board, list) {
href: board.originRelativeUrl(),
class: 'action-board',
},
DOMPurify.sanitize(text, { ALLOW_UNKNOWN_PROTOCOLS: true }),
sanitizeHTML(text),
),
)
);
@@ -0,0 +1,184 @@
/* Board Conversion Progress Styles */
.board-conversion-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.7);
z-index: 9999;
display: none;
align-items: center;
justify-content: center;
}
.board-conversion-overlay.active {
display: flex;
}
.board-conversion-modal {
background: white;
border-radius: 8px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
max-width: 500px;
width: 90%;
max-height: 80vh;
overflow: hidden;
animation: slideIn 0.3s ease-out;
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateY(-20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.board-conversion-header {
padding: 20px 24px 16px;
border-bottom: 1px solid #e0e0e0;
text-align: center;
}
.board-conversion-header h3 {
margin: 0 0 8px 0;
color: #333;
font-size: 20px;
font-weight: 500;
}
.board-conversion-header h3 i {
margin-right: 8px;
color: #2196F3;
}
.board-conversion-header p {
margin: 0;
color: #666;
font-size: 14px;
}
.board-conversion-content {
padding: 24px;
}
.conversion-progress {
margin-bottom: 20px;
}
.progress-bar {
width: 100%;
height: 8px;
background-color: #e0e0e0;
border-radius: 4px;
overflow: hidden;
margin-bottom: 8px;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #2196F3, #21CBF3);
border-radius: 4px;
transition: width 0.3s ease;
position: relative;
}
.progress-fill::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(
90deg,
transparent,
rgba(255, 255, 255, 0.3),
transparent
);
animation: shimmer 2s infinite;
}
@keyframes shimmer {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(100%);
}
}
.progress-text {
text-align: center;
font-weight: 600;
color: #2196F3;
font-size: 16px;
}
.conversion-status {
text-align: center;
margin-bottom: 16px;
color: #333;
font-size: 16px;
}
.conversion-status i {
margin-right: 8px;
color: #2196F3;
}
.conversion-time {
text-align: center;
color: #666;
font-size: 14px;
background-color: #f5f5f5;
padding: 8px 12px;
border-radius: 4px;
margin-bottom: 16px;
}
.conversion-time i {
margin-right: 6px;
color: #FF9800;
}
.board-conversion-footer {
padding: 16px 24px 20px;
border-top: 1px solid #e0e0e0;
background-color: #f9f9f9;
}
.conversion-info {
text-align: center;
color: #666;
font-size: 13px;
line-height: 1.4;
}
.conversion-info i {
margin-right: 6px;
color: #2196F3;
}
/* Responsive design */
@media (max-width: 600px) {
.board-conversion-modal {
width: 95%;
margin: 20px;
}
.board-conversion-header,
.board-conversion-content,
.board-conversion-footer {
padding-left: 16px;
padding-right: 16px;
}
.board-conversion-header h3 {
font-size: 18px;
}
}
@@ -0,0 +1,27 @@
template(name="boardConversionProgress")
.board-conversion-overlay(class="{{#if isConverting}}active{{/if}}")
.board-conversion-modal
.board-conversion-header
h3
| ⚙️
| {{_ 'converting-board'}}
p {{_ 'converting-board-description'}}
.board-conversion-content
.conversion-progress
.progress-bar
.progress-fill(style="width: {{conversionProgress}}%")
.progress-text {{conversionProgress}}%
.conversion-status
| ⚙️
| {{conversionStatus}}
.conversion-time(style="{{#unless conversionEstimatedTime}}display: none;{{/unless}}")
| ⏰
| {{_ 'estimated-time-remaining'}}: {{conversionEstimatedTime}}
.board-conversion-footer
.conversion-info
| ℹ️
| {{_ 'conversion-info-text'}}
@@ -0,0 +1,37 @@
import { Template } from 'meteor/templating';
import { ReactiveVar } from 'meteor/reactive-var';
import {
boardConverter,
isConverting,
conversionProgress,
conversionStatus,
conversionEstimatedTime
} from '/client/lib/boardConverter';
Template.boardConversionProgress.helpers({
isConverting() {
return isConverting.get();
},
conversionProgress() {
return conversionProgress.get();
},
conversionStatus() {
return conversionStatus.get();
},
conversionEstimatedTime() {
return conversionEstimatedTime.get();
}
});
Template.boardConversionProgress.onCreated(function() {
// Subscribe to conversion state changes
this.autorun(() => {
isConverting.get();
conversionProgress.get();
conversionStatus.get();
conversionEstimatedTime.get();
});
});
+287 -2
View File
@@ -6,6 +6,27 @@
bottom: 0;
overflow-x: hidden;
overflow-y: hidden;
width: 100%;
min-width: 100%;
}
/* When zoom is 50% or lower, ensure full width like content */
.board-wrapper[style*="transform: scale(0.5)"] {
width: 100% !important;
max-width: 100% !important;
margin: 0 !important;
}
.board-wrapper[style*="transform: scale(0.4)"] {
width: 100% !important;
max-width: 100% !important;
margin: 0 !important;
}
.board-wrapper[style*="transform: scale(0.3)"] {
width: 100% !important;
max-width: 100% !important;
margin: 0 !important;
}
.board-wrapper .board-canvas {
position: absolute;
@@ -15,6 +36,219 @@
bottom: 0;
transition: margin 0.1s;
overflow-y: auto;
width: 100%;
min-width: 100%;
}
/* Ensure horizontal scrollbar is visible for high zoom levels */
.board-wrapper .board-canvas[style*="overflow-x: auto"] {
overflow-x: auto !important;
overflow-y: auto !important;
}
/* Style the scrollbar for better visibility */
.board-wrapper .board-canvas::-webkit-scrollbar {
height: 12px;
width: 12px;
}
.board-wrapper .board-canvas::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 6px;
}
.board-wrapper .board-canvas::-webkit-scrollbar-thumb {
background: #888;
border-radius: 6px;
}
.board-wrapper .board-canvas::-webkit-scrollbar-thumb:hover {
background: #555;
}
/* Ensure content area can scroll horizontally for high zoom levels */
#content[style*="overflow-x: auto"] {
overflow-x: auto !important;
overflow-y: auto !important;
}
/* Style content area scrollbar */
#content::-webkit-scrollbar {
height: 12px;
width: 12px;
}
#content::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 6px;
}
#content::-webkit-scrollbar-thumb {
background: #888;
border-radius: 6px;
}
#content::-webkit-scrollbar-thumb:hover {
background: #555;
}
/* Ensure scrollbars are always visible for high zoom levels */
#content[style*="overflow-x: auto"] {
position: relative;
}
#content[style*="overflow-x: auto"]::-webkit-scrollbar {
height: 12px;
width: 12px;
}
/* Force vertical scrollbar to always be visible */
#content[style*="overflow-y: auto"] {
overflow-y: scroll !important;
}
/* Mobile - make all text 2x bigger inside #content by default (icons stay same size) */
@media screen and (max-width: 800px),
screen and (max-device-width: 800px),
screen and (-webkit-min-device-pixel-ratio: 2) and (max-width: 800px),
screen and (max-width: 800px) and (orientation: portrait),
screen and (max-width: 800px) and (orientation: landscape) {
#content {
font-size: 2em !important; /* 2x bigger base font size for content area */
}
/* Make all text elements 2x bigger */
#content h1, #content h2, #content h3, #content h4, #content h5, #content h6,
#content p, #content span, #content div, #content a, #content button,
#content .minicard, #content .list-header-name, #content .board-header-btn,
#content .card-title, #content .card-details, #content .card-description,
#content .swimlane-header, #content .list-title, #content .card-text,
#content .member, #content .member-name, #content .member-initials,
#content .checklist-item, #content .checklist-title, #content .comment,
#content .activity, #content .activity-text, #content .activity-time,
#content .board-title, #content .board-description, #content .list-name,
#content .card-text, #content .card-title, #content .card-description,
#content .swimlane-title, #content .swimlane-description,
#content .board-header-title, #content .board-header-description,
#content .card-detail-title, #content .card-detail-description,
#content .list-header-title, #content .list-header-description,
#content .swimlane-header-title, #content .swimlane-header-description,
#content .minicard-title, #content .minicard-description,
#content .card-comment, #content .card-comment-text,
#content .checklist-item-text, #content .checklist-item-title,
#content .activity-item, #content .activity-item-text,
#content .board-member, #content .board-member-name,
#content .team-member, #content .team-member-name,
#content .org-member, #content .org-member-name,
#content .template-member, #content .template-member-name,
#content .user-name, #content .user-email, #content .user-role,
#content .setting-title, #content .setting-description,
#content .popup-title, #content .popup-description,
#content .modal-title, #content .modal-description,
#content .notification-title, #content .notification-text,
#content .announcement-title, #content .announcement-text,
#content .offline-warning-title, #content .offline-warning-text,
#content .error-title, #content .error-text,
#content .success-title, #content .success-text,
#content .info-title, #content .info-text,
#content .warning-title, #content .warning-text {
font-size: 1em !important; /* Use inherited 2x scaling */
}
/* Keep icons the same size (don't scale them) */
#content .fa, #content .icon, #content i {
font-size: 1em !important; /* Keep original icon size */
}
/* Reset specific icon sizes to prevent double scaling */
#content .fa-home, #content .fa-bars, #content .fa-search,
#content .fa-bell, #content .fa-user, #content .fa-cog,
#content .fa-plus, #content .fa-minus, #content .fa-edit,
#content .fa-trash, #content .fa-save, #content .fa-cancel,
#content .fa-arrow-left, #content .fa-arrow-right,
#content .fa-arrow-up, #content .fa-arrow-down,
#content .fa-check, #content .fa-times, #content .fa-close,
#content .fa-star, #content .fa-heart, #content .fa-thumbs-up,
#content .fa-thumbs-down, #content .fa-comment, #content .fa-reply,
#content .fa-share, #content .fa-download, #content .fa-upload,
#content .fa-copy, #content .fa-paste, #content .fa-cut,
#content .fa-undo, #content .fa-redo, #content .fa-refresh,
#content .fa-sync, #content .fa-spinner, #content .fa-loading,
#content .fa-info, #content .fa-question, #content .fa-exclamation,
#content .fa-warning, #content .fa-error, #content .fa-success,
#content .fa-check-circle, #content .fa-times-circle,
#content .fa-exclamation-circle, #content .fa-question-circle,
#content .fa-info-circle, #content .fa-warning-circle,
#content .fa-error-circle, #content .fa-success-circle {
font-size: 1em !important; /* Keep original icon size */
}
}
/* Fallback for iPhone devices using JavaScript detection */
.iphone-device #content {
font-size: 2em !important; /* 2x bigger base font size for content area */
}
.iphone-device #content h1, .iphone-device #content h2, .iphone-device #content h3, .iphone-device #content h4, .iphone-device #content h5, .iphone-device #content h6,
.iphone-device #content p, .iphone-device #content span, .iphone-device #content div, .iphone-device #content a, .iphone-device #content button,
.iphone-device #content .minicard, .iphone-device #content .list-header-name, .iphone-device #content .board-header-btn,
.iphone-device #content .card-title, .iphone-device #content .card-details, .iphone-device #content .card-description,
.iphone-device #content .swimlane-header, .iphone-device #content .list-title, .iphone-device #content .card-text,
.iphone-device #content .member, .iphone-device #content .member-name, .iphone-device #content .member-initials,
.iphone-device #content .checklist-item, .iphone-device #content .checklist-title, .iphone-device #content .comment,
.iphone-device #content .activity, .iphone-device #content .activity-text, .iphone-device #content .activity-time,
.iphone-device #content .board-title, .iphone-device #content .board-description, .iphone-device #content .list-name,
.iphone-device #content .card-text, .iphone-device #content .card-title, .iphone-device #content .card-description,
.iphone-device #content .swimlane-title, .iphone-device #content .swimlane-description,
.iphone-device #content .board-header-title, .iphone-device #content .board-header-description,
.iphone-device #content .card-detail-title, .iphone-device #content .card-detail-description,
.iphone-device #content .list-header-title, .iphone-device #content .list-header-description,
.iphone-device #content .swimlane-header-title, .iphone-device #content .swimlane-header-description,
.iphone-device #content .minicard-title, .iphone-device #content .minicard-description,
.iphone-device #content .card-comment, .iphone-device #content .card-comment-text,
.iphone-device #content .checklist-item-text, .iphone-device #content .checklist-item-title,
.iphone-device #content .activity-item, .iphone-device #content .activity-item-text,
.iphone-device #content .board-member, .iphone-device #content .board-member-name,
.iphone-device #content .team-member, .iphone-device #content .team-member-name,
.iphone-device #content .org-member, .iphone-device #content .org-member-name,
.iphone-device #content .template-member, .iphone-device #content .template-member-name,
.iphone-device #content .user-name, .iphone-device #content .user-email, .iphone-device #content .user-role,
.iphone-device #content .setting-title, .iphone-device #content .setting-description,
.iphone-device #content .popup-title, .iphone-device #content .popup-description,
.iphone-device #content .modal-title, .iphone-device #content .modal-description,
.iphone-device #content .notification-title, .iphone-device #content .notification-text,
.iphone-device #content .announcement-title, .iphone-device #content .announcement-text,
.iphone-device #content .offline-warning-title, .iphone-device #content .offline-warning-text,
.iphone-device #content .error-title, .iphone-device #content .error-text,
.iphone-device #content .success-title, .iphone-device #content .success-text,
.iphone-device #content .info-title, .iphone-device #content .info-text,
.iphone-device #content .warning-title, .iphone-device #content .warning-text {
font-size: 1em !important; /* Use inherited 2x scaling */
}
/* Keep icons the same size for iPhone devices */
.iphone-device #content .fa, .iphone-device #content .icon, .iphone-device #content i {
font-size: 1em !important; /* Keep original icon size */
}
/* Ensure scrollbars are positioned correctly */
#content[style*="overflow-x: auto"]::-webkit-scrollbar:vertical {
width: 12px;
}
#content[style*="overflow-x: auto"]::-webkit-scrollbar:horizontal {
height: 12px;
}
/* Force both scrollbars to always be visible for high zoom levels */
#content[style*="overflow-x: auto"][style*="overflow-y: auto"] {
overflow-x: scroll !important;
overflow-y: scroll !important;
}
/* Ensure the right scrollbar is always visible on the inner right edge */
#content[style*="overflow-y: auto"] {
scrollbar-gutter: stable;
}
.board-wrapper .board-canvas .board-overlay {
position: fixed;
@@ -33,15 +267,59 @@
.board-wrapper .board-canvas.is-dragging-active .minicard-wrapper.is-checked {
display: none;
}
@media screen and (max-width: 800px) {
/* Mobile view styles - applied when isMiniScreen is true (iPhone, etc.) */
.board-wrapper.mobile-view {
width: 100% !important;
min-width: 100% !important;
left: 0 !important;
right: 0 !important;
}
.board-wrapper.mobile-view .board-canvas {
width: 100% !important;
min-width: 100% !important;
left: 0 !important;
right: 0 !important;
}
.board-wrapper.mobile-view .board-canvas.mobile-view .swimlane {
border-bottom: 1px solid #ccc;
display: flex;
flex-direction: column;
margin: 0;
padding: 0;
overflow-x: hidden;
overflow-y: auto;
width: 100%;
min-width: 100%;
}
@media screen and (max-width: 800px),
screen and (max-device-width: 932px) and (-webkit-min-device-pixel-ratio: 3) {
.board-wrapper {
width: 100% !important;
min-width: 100% !important;
left: 0 !important;
right: 0 !important;
}
.board-wrapper .board-canvas {
width: 100% !important;
min-width: 100% !important;
left: 0 !important;
right: 0 !important;
}
.board-wrapper .board-canvas .swimlane {
border-bottom: 1px solid #ccc;
display: flex;
flex-direction: column;
margin: 0;
padding: 0 0px 0px 0;
padding: 0;
overflow-x: hidden;
overflow-y: auto;
width: 100%;
min-width: 100%;
}
}
.calendar-event-green {
@@ -219,3 +497,10 @@
font-size: 25px;
cursor: pointer;
}
/* Global file drag over state for board canvas */
.board-canvas.file-drag-over {
background-color: rgba(0, 123, 255, 0.05) !important;
border: 2px dashed #007bff !important;
transition: all 0.2s ease;
}
+21 -7
View File
@@ -1,5 +1,10 @@
template(name="board")
if isBoardReady.get
if isMigrating.get
+migrationProgress
else if isConverting.get
+boardConversionProgress
else if isBoardReady.get
if currentBoard
if onlyShowCurrentCard
+cardDetails(currentCard)
@@ -16,13 +21,18 @@ template(name="boardBody")
if notDisplayThisBoard
| {{_ 'tableVisibilityMode-allowPrivateOnly'}}
else
.board-wrapper(class=currentBoard.colorClass)
// Debug information (remove in production)
if debugBoardState
.debug-info(style="position: fixed; top: 0; left: 0; background: rgba(0,0,0,0.8); color: white; padding: 10px; z-index: 9999; font-size: 12px;")
| Board: {{currentBoard.title}} | View: {{boardView}} | HasSwimlanes: {{hasSwimlanes}} | Swimlanes: {{currentBoard.swimlanes.length}}
.board-wrapper(class=currentBoard.colorClass class="{{#if isMiniScreen}}mobile-view{{/if}}")
.board-canvas.js-swimlanes(
class="{{#if hasSwimlanes}}dragscroll{{/if}}"
class="{{#if Sidebar.isOpen}}is-sibling-sidebar-open{{/if}}"
class="{{#if MultiSelection.isActive}}is-multiselection-active{{/if}}"
class="{{#if draggingActive.get}}is-dragging-active{{/if}}"
class="{{#unless isVerticalScrollbars}}no-scrollbars{{/unless}}")
class="{{#unless isVerticalScrollbars}}no-scrollbars{{/unless}}"
class="{{#if isMiniScreen}}mobile-view{{/if}}")
if showOverlay.get
.board-overlay
if currentBoard.isTemplatesBoard
@@ -33,15 +43,19 @@ template(name="boardBody")
each currentBoard.swimlanes
+swimlane(this)
else
a.js-empty-board-add-swimlane(title="{{_ 'add-swimlane'}}")
h1.big-message.quiet
| {{_ 'add-swimlane'}} +
// Fallback: If no swimlanes exist, show lists instead of empty message
+listsGroup(currentBoard)
else if isViewLists
+listsGroup(currentBoard)
else if isViewCalendar
+calendarView
else
+listsGroup(currentBoard)
// Default view - show swimlanes if they exist, otherwise show lists
if hasSwimlanes
each currentBoard.swimlanes
+swimlane(this)
else
+listsGroup(currentBoard)
+sidebar
template(name="calendarView")
+770 -57
View File
@@ -1,6 +1,11 @@
import { ReactiveCache } from '/imports/reactiveCache';
import { TAPi18n } from '/imports/i18n';
import dragscroll from '@wekanteam/dragscroll';
import { boardConverter } from '/client/lib/boardConverter';
import { migrationManager } from '/client/lib/migrationManager';
import { attachmentMigrationManager } from '/client/lib/attachmentMigrationManager';
import Swimlanes from '/models/swimlanes';
import Lists from '/models/lists';
const subManager = new SubsManager();
const { calculateIndex } = Utils;
@@ -9,6 +14,9 @@ const swimlaneWhileSortingHeight = 150;
BlazeComponent.extendComponent({
onCreated() {
this.isBoardReady = new ReactiveVar(false);
this.isConverting = new ReactiveVar(false);
this.isMigrating = new ReactiveVar(false);
this._swimlaneCreated = new Set(); // Track boards where we've created swimlanes
// The pattern we use to manually handle data loading is described here:
// https://kadira.io/academy/meteor-routing-guide/content/subscriptions-and-data-management/using-subs-manager
@@ -17,22 +25,330 @@ BlazeComponent.extendComponent({
this.autorun(() => {
const currentBoardId = Session.get('currentBoard');
if (!currentBoardId) return;
const handle = subManager.subscribe('board', currentBoardId, false);
Tracker.nonreactive(() => {
Tracker.autorun(() => {
this.isBoardReady.set(handle.ready());
if (handle.ready()) {
// Ensure default swimlane exists (only once per board)
this.ensureDefaultSwimlane(currentBoardId);
// Check if board needs conversion
this.checkAndConvertBoard(currentBoardId);
} else {
this.isBoardReady.set(false);
}
});
});
});
},
ensureDefaultSwimlane(boardId) {
// Only create swimlane once per board
if (this._swimlaneCreated.has(boardId)) {
return;
}
try {
const board = ReactiveCache.getBoard(boardId);
if (!board) return;
const swimlanes = board.swimlanes();
if (swimlanes.length === 0) {
const swimlaneId = Swimlanes.insert({
title: 'Default',
boardId: boardId,
});
this._swimlaneCreated.add(boardId);
} else {
this._swimlaneCreated.add(boardId);
}
} catch (error) {
console.error('Error creating default swimlane:', error);
}
},
async checkAndConvertBoard(boardId) {
try {
const board = ReactiveCache.getBoard(boardId);
if (!board) {
this.isBoardReady.set(true);
return;
}
// Check if board needs migration based on migration version
const needsMigration = !board.migrationVersion || board.migrationVersion < 1;
if (needsMigration) {
// Start background migration for old boards
this.isMigrating.set(true);
await this.startBackgroundMigration(boardId);
this.isMigrating.set(false);
}
// Check if board needs conversion (for old structure)
if (boardConverter.isBoardConverted(boardId)) {
if (process.env.DEBUG === 'true') {
console.log(`Board ${boardId} has already been converted, skipping conversion`);
}
this.isBoardReady.set(true);
} else {
const needsConversion = boardConverter.needsConversion(boardId);
if (needsConversion) {
this.isConverting.set(true);
const success = await boardConverter.convertBoard(boardId);
this.isConverting.set(false);
if (success) {
this.isBoardReady.set(true);
} else {
console.error('Board conversion failed, setting ready to true anyway');
this.isBoardReady.set(true); // Still show board even if conversion failed
}
} else {
this.isBoardReady.set(true);
}
}
// Convert shared lists to per-swimlane lists if needed
await this.convertSharedListsToPerSwimlane(boardId);
// Fix missing lists migration (for cards with wrong listId references)
await this.fixMissingLists(boardId);
// Start attachment migration in background if needed
this.startAttachmentMigrationIfNeeded(boardId);
} catch (error) {
console.error('Error during board conversion check:', error);
this.isConverting.set(false);
this.isMigrating.set(false);
this.isBoardReady.set(true); // Show board even if conversion check failed
}
},
async startBackgroundMigration(boardId) {
try {
// Start background migration using the cron system
Meteor.call('boardMigration.startBoardMigration', boardId, (error, result) => {
if (error) {
console.error('Failed to start background migration:', error);
} else {
if (process.env.DEBUG === 'true') {
console.log('Background migration started for board:', boardId);
}
}
});
} catch (error) {
console.error('Error starting background migration:', error);
}
},
async convertSharedListsToPerSwimlane(boardId) {
try {
const board = ReactiveCache.getBoard(boardId);
if (!board) return;
// Check if board has already been processed for shared lists conversion
if (board.hasSharedListsConverted) {
if (process.env.DEBUG === 'true') {
console.log(`Board ${boardId} has already been processed for shared lists conversion`);
}
return;
}
// Get all lists for this board
const allLists = board.lists();
const swimlanes = board.swimlanes();
if (swimlanes.length === 0) {
if (process.env.DEBUG === 'true') {
console.log(`Board ${boardId} has no swimlanes, skipping shared lists conversion`);
}
return;
}
// Find shared lists (lists with empty swimlaneId or null swimlaneId)
const sharedLists = allLists.filter(list => !list.swimlaneId || list.swimlaneId === '');
if (sharedLists.length === 0) {
if (process.env.DEBUG === 'true') {
console.log(`Board ${boardId} has no shared lists to convert`);
}
// Mark as processed even if no shared lists
Meteor.call('boards.update', boardId, { $set: { hasSharedListsConverted: true } });
return;
}
if (process.env.DEBUG === 'true') {
console.log(`Converting ${sharedLists.length} shared lists to per-swimlane lists for board ${boardId}`);
}
// Convert each shared list to per-swimlane lists
for (const sharedList of sharedLists) {
// Create a copy of the list for each swimlane
for (const swimlane of swimlanes) {
// Check if this list already exists in this swimlane
const existingList = Lists.findOne({
boardId: boardId,
swimlaneId: swimlane._id,
title: sharedList.title
});
if (!existingList) {
// Create a new list in this swimlane
const newListData = {
title: sharedList.title,
boardId: boardId,
swimlaneId: swimlane._id,
sort: sharedList.sort || 0,
archived: sharedList.archived || false,
createdAt: new Date(),
modifiedAt: new Date()
};
// Copy other properties if they exist
if (sharedList.color) newListData.color = sharedList.color;
if (sharedList.wipLimit) newListData.wipLimit = sharedList.wipLimit;
if (sharedList.wipLimitEnabled) newListData.wipLimitEnabled = sharedList.wipLimitEnabled;
if (sharedList.wipLimitSoft) newListData.wipLimitSoft = sharedList.wipLimitSoft;
Lists.insert(newListData);
}
}
// Archive or remove the original shared list
Lists.update(sharedList._id, {
$set: {
archived: true,
modifiedAt: new Date()
}
});
}
// Mark board as processed
Meteor.call('boards.update', boardId, { $set: { hasSharedListsConverted: true } });
if (process.env.DEBUG === 'true') {
console.log(`Successfully converted ${sharedLists.length} shared lists to per-swimlane lists for board ${boardId}`);
}
} catch (error) {
console.error('Error converting shared lists to per-swimlane:', error);
}
},
async fixMissingLists(boardId) {
try {
const board = ReactiveCache.getBoard(boardId);
if (!board) return;
// Check if board has already been processed for missing lists fix
if (board.fixMissingListsCompleted) {
if (process.env.DEBUG === 'true') {
console.log(`Board ${boardId} has already been processed for missing lists fix`);
}
return;
}
// Check if migration is needed
const needsMigration = await new Promise((resolve, reject) => {
Meteor.call('fixMissingListsMigration.needsMigration', boardId, (error, result) => {
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
if (!needsMigration) {
if (process.env.DEBUG === 'true') {
console.log(`Board ${boardId} does not need missing lists fix`);
}
return;
}
if (process.env.DEBUG === 'true') {
console.log(`Starting fix missing lists migration for board ${boardId}`);
}
// Execute the migration
const result = await new Promise((resolve, reject) => {
Meteor.call('fixMissingListsMigration.execute', boardId, (error, result) => {
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
if (result && result.success) {
console.log(`Successfully fixed missing lists for board ${boardId}: created ${result.createdLists} lists, updated ${result.updatedCards} cards`);
}
} catch (error) {
console.error('Error fixing missing lists:', error);
}
},
async startAttachmentMigrationIfNeeded(boardId) {
try {
// Check if board has already been migrated
if (attachmentMigrationManager.isBoardMigrated(boardId)) {
if (process.env.DEBUG === 'true') {
console.log(`Board ${boardId} has already been migrated, skipping`);
}
return;
}
// Check if there are unconverted attachments
const unconvertedAttachments = attachmentMigrationManager.getUnconvertedAttachments(boardId);
if (unconvertedAttachments.length > 0) {
if (process.env.DEBUG === 'true') {
console.log(`Starting attachment migration for ${unconvertedAttachments.length} attachments in board ${boardId}`);
}
await attachmentMigrationManager.startAttachmentMigration(boardId);
} else {
// No attachments to migrate, mark board as migrated
// This will be handled by the migration manager itself
if (process.env.DEBUG === 'true') {
console.log(`Board ${boardId} has no attachments to migrate`);
}
}
} catch (error) {
console.error('Error starting attachment migration:', error);
}
},
onlyShowCurrentCard() {
return Utils.isMiniScreen() && Utils.getCurrentCardId(true);
const isMiniScreen = Utils.isMiniScreen();
const currentCardId = Utils.getCurrentCardId(true);
return isMiniScreen && currentCardId;
},
goHome() {
FlowRouter.go('home');
},
isConverting() {
return this.isConverting.get();
},
isMigrating() {
return this.isMigrating.get();
},
isBoardReady() {
return this.isBoardReady.get();
},
currentBoard() {
return Utils.getCurrentBoard();
},
}).register('board');
BlazeComponent.extendComponent({
@@ -46,36 +362,227 @@ BlazeComponent.extendComponent({
// fix swimlanes sort field if there are null values
const currentBoardData = Utils.getCurrentBoard();
const nullSortSwimlanes = currentBoardData.nullSortSwimlanes();
if (nullSortSwimlanes.length > 0) {
const swimlanes = currentBoardData.swimlanes();
let count = 0;
swimlanes.forEach(s => {
Swimlanes.update(s._id, {
$set: {
sort: count,
},
if (currentBoardData && Swimlanes) {
const nullSortSwimlanes = currentBoardData.nullSortSwimlanes();
if (nullSortSwimlanes.length > 0) {
const swimlanes = currentBoardData.swimlanes();
let count = 0;
swimlanes.forEach(s => {
Swimlanes.update(s._id, {
$set: {
sort: count,
},
});
count += 1;
});
count += 1;
});
}
}
// fix lists sort field if there are null values
const nullSortLists = currentBoardData.nullSortLists();
if (nullSortLists.length > 0) {
const lists = currentBoardData.lists();
let count = 0;
lists.forEach(l => {
Lists.update(l._id, {
$set: {
sort: count,
},
if (currentBoardData && Lists) {
const nullSortLists = currentBoardData.nullSortLists();
if (nullSortLists.length > 0) {
const lists = currentBoardData.lists();
let count = 0;
lists.forEach(l => {
Lists.update(l._id, {
$set: {
sort: count,
},
});
count += 1;
});
count += 1;
});
}
}
},
onRendered() {
// Initialize user settings (zoom and mobile mode)
Utils.initializeUserSettings();
// Detect iPhone devices and add class for better CSS targeting
const isIPhone = /iPhone|iPod/.test(navigator.userAgent);
if (isIPhone) {
document.body.classList.add('iphone-device');
}
// Accessibility: Focus management for popups and menus
function focusFirstInteractive(container) {
if (!container) return;
// Find first focusable element
const focusable = container.querySelectorAll('button, [role="button"], a[href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
for (let i = 0; i < focusable.length; i++) {
if (!focusable[i].disabled && focusable[i].offsetParent !== null) {
focusable[i].focus();
break;
}
}
}
// Observe for new popups/menus and set focus (but exclude swimlane content)
const popupObserver = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
mutation.addedNodes.forEach(function(node) {
if (node.nodeType === 1 &&
(node.classList.contains('popup') || node.classList.contains('modal') || node.classList.contains('menu')) &&
!node.closest('.js-swimlanes') &&
!node.closest('.swimlane') &&
!node.closest('.list') &&
!node.closest('.minicard')) {
setTimeout(function() { focusFirstInteractive(node); }, 10);
}
});
});
});
popupObserver.observe(document.body, { childList: true, subtree: true });
// Remove tabindex from non-interactive elements (e.g., user abbreviations, labels)
document.querySelectorAll('.user-abbreviation, .user-label, .card-header-label, .edit-label, .private-label').forEach(function(el) {
if (el.hasAttribute('tabindex')) {
el.removeAttribute('tabindex');
}
});
/*
// Add a toggle button for keyboard shortcuts accessibility
if (!document.getElementById('wekan-shortcuts-toggle')) {
const toggleContainer = document.createElement('div');
toggleContainer.id = 'wekan-shortcuts-toggle';
toggleContainer.style.position = 'fixed';
toggleContainer.style.top = '10px';
toggleContainer.style.right = '10px';
toggleContainer.style.zIndex = '1000';
toggleContainer.style.background = '#fff';
toggleContainer.style.border = '2px solid #005fcc';
toggleContainer.style.borderRadius = '6px';
toggleContainer.style.padding = '8px 12px';
toggleContainer.style.boxShadow = '0 2px 8px rgba(0,0,0,0.1)';
toggleContainer.style.fontSize = '16px';
toggleContainer.style.color = '#005fcc';
toggleContainer.setAttribute('role', 'region');
toggleContainer.setAttribute('aria-label', 'Keyboard Shortcuts Settings');
toggleContainer.innerHTML = `
<label for="shortcuts-toggle-checkbox" style="cursor:pointer;">
<input type="checkbox" id="shortcuts-toggle-checkbox" ${window.wekanShortcutsEnabled ? 'checked' : ''} style="margin-right:8px;" />
Enable keyboard shortcuts
</label>
`;
document.body.appendChild(toggleContainer);
const checkbox = document.getElementById('shortcuts-toggle-checkbox');
checkbox.addEventListener('change', function(e) {
window.toggleWekanShortcuts(e.target.checked);
});
}
*/
// Ensure toggle-buttons, color choices, reactions, renaming, and calendar controls are focusable and have ARIA roles
document.querySelectorAll('.js-toggle').forEach(function(el) {
el.setAttribute('tabindex', '0');
el.setAttribute('role', 'button');
// Short, descriptive label for favorite/star toggle
if (el.classList.contains('js-favorite-toggle')) {
el.setAttribute('aria-label', TAPi18n.__('favorite-toggle-label'));
} else {
el.setAttribute('aria-label', 'Toggle');
}
});
document.querySelectorAll('.js-color-choice').forEach(function(el) {
el.setAttribute('tabindex', '0');
el.setAttribute('role', 'button');
el.setAttribute('aria-label', 'Choose color');
});
document.querySelectorAll('.js-reaction').forEach(function(el) {
el.setAttribute('tabindex', '0');
el.setAttribute('role', 'button');
el.setAttribute('aria-label', 'React');
});
document.querySelectorAll('.js-rename-swimlane').forEach(function(el) {
el.setAttribute('tabindex', '0');
el.setAttribute('role', 'button');
el.setAttribute('aria-label', 'Rename swimlane');
});
document.querySelectorAll('.js-rename-list').forEach(function(el) {
el.setAttribute('tabindex', '0');
el.setAttribute('role', 'button');
el.setAttribute('aria-label', 'Rename list');
});
document.querySelectorAll('.fc-button').forEach(function(el) {
el.setAttribute('tabindex', '0');
el.setAttribute('role', 'button');
});
// Set the language attribute on the <html> element for accessibility
document.documentElement.lang = TAPi18n.getLanguage();
// Ensure the accessible name for the board view switcher matches the visible label "Swimlanes"
// This fixes WCAG 2.5.3: Label in Name
const swimlanesSwitcher = this.$('.js-board-view-swimlanes');
if (swimlanesSwitcher.length) {
swimlanesSwitcher.attr('aria-label', swimlanesSwitcher.text().trim() || 'Swimlanes');
}
// Add a highly visible focus indicator and improve contrast for interactive elements
if (!document.getElementById('wekan-accessible-focus-style')) {
const style = document.createElement('style');
style.id = 'wekan-accessible-focus-style';
style.innerHTML = `
/* Focus indicator */
button:focus, [role="button"]:focus, a:focus, input:focus, select:focus, textarea:focus, .dropdown-menu:focus, .js-board-view-swimlanes:focus, .js-add-card:focus {
outline: 3px solid #005fcc !important;
outline-offset: 2px !important;
}
/* Input borders */
input, textarea, select {
border: 2px solid #222 !important;
}
/* Plus icon for adding a new card */
.js-add-card {
color: #005fcc !important; /* dark blue for contrast */
cursor: pointer;
outline: none;
}
.js-add-card[tabindex] {
outline: none;
}
/* Hamburger menu */
.fa-bars, .icon-hamburger {
color: #222 !important;
}
/* Grey icons in card detail header */
.card-detail-header .fa, .card-detail-header .icon {
color: #444 !important;
}
/* Grey operating elements in card detail */
.card-detail .fa, .card-detail .icon {
color: #444 !important;
}
/* Blue bar in checklists */
.checklist-progress-bar {
background-color: #005fcc !important;
}
/* Green checkmark in checklists */
.checklist .fa-check {
color: #007a33 !important;
}
/* X-Button and arrow button in menus */
.close, .fa-arrow-left, .icon-arrow-left {
color: #005fcc !important;
}
/* Cross icon to move boards */
.js-move-board {
color: #005fcc !important;
}
/* Current date background */
.current-date {
background-color: #005fcc !important;
color: #fff !important;
}
`;
document.head.appendChild(style);
}
// Ensure plus/add elements are focusable and have ARIA roles
document.querySelectorAll('.js-add-card').forEach(function(el) {
el.setAttribute('tabindex', '0');
el.setAttribute('role', 'button');
el.setAttribute('aria-label', 'Add new card');
});
const boardComponent = this;
const $swimlanesDom = boardComponent.$('.js-swimlanes');
@@ -198,22 +705,20 @@ BlazeComponent.extendComponent({
// Always reset dragscroll on view switch
dragscroll.reset();
if (Utils.isTouchScreenOrShowDesktopDragHandles()) {
$swimlanesDom.sortable({
handle: '.js-swimlane-header-handle',
});
} else {
$swimlanesDom.sortable({
handle: '.swimlane-header',
});
}
if ($swimlanesDom.data('uiSortable') || $swimlanesDom.data('sortable')) {
if (Utils.isTouchScreenOrShowDesktopDragHandles()) {
$swimlanesDom.sortable('option', 'handle', '.js-swimlane-header-handle');
} else {
$swimlanesDom.sortable('option', 'handle', '.swimlane-header');
}
// Disable drag-dropping if the current user is not a board member
$swimlanesDom.sortable(
'option',
'disabled',
!ReactiveCache.getCurrentUser()?.isBoardAdmin(),
);
// Disable drag-dropping if the current user is not a board member
$swimlanesDom.sortable(
'option',
'disabled',
!ReactiveCache.getCurrentUser()?.isBoardAdmin(),
);
}
});
// If there is no data in the board (ie, no lists) we autofocus the list
@@ -230,51 +735,122 @@ BlazeComponent.extendComponent({
notDisplayThisBoard() {
let allowPrivateVisibilityOnly = TableVisibilityModeSettings.findOne('tableVisibilityMode-allowPrivateOnly');
let currentBoard = Utils.getCurrentBoard();
if (allowPrivateVisibilityOnly !== undefined && allowPrivateVisibilityOnly.booleanValue && currentBoard.permission == 'public') {
return true;
}
return false;
return allowPrivateVisibilityOnly !== undefined && allowPrivateVisibilityOnly.booleanValue && currentBoard && currentBoard.permission == 'public';
},
isViewSwimlanes() {
const currentUser = ReactiveCache.getCurrentUser();
let boardView;
if (currentUser) {
return (currentUser.profile || {}).boardView === 'board-view-swimlanes';
boardView = (currentUser.profile || {}).boardView;
} else {
return (
window.localStorage.getItem('boardView') === 'board-view-swimlanes'
);
boardView = window.localStorage.getItem('boardView');
}
},
hasSwimlanes() {
return Utils.getCurrentBoard().swimlanes().length > 0;
// If no board view is set, default to swimlanes
if (!boardView) {
boardView = 'board-view-swimlanes';
}
return boardView === 'board-view-swimlanes';
},
isViewLists() {
const currentUser = ReactiveCache.getCurrentUser();
let boardView;
if (currentUser) {
return (currentUser.profile || {}).boardView === 'board-view-lists';
boardView = (currentUser.profile || {}).boardView;
} else {
return window.localStorage.getItem('boardView') === 'board-view-lists';
boardView = window.localStorage.getItem('boardView');
}
return boardView === 'board-view-lists';
},
isViewCalendar() {
const currentUser = ReactiveCache.getCurrentUser();
let boardView;
if (currentUser) {
return (currentUser.profile || {}).boardView === 'board-view-cal';
boardView = (currentUser.profile || {}).boardView;
} else {
return window.localStorage.getItem('boardView') === 'board-view-cal';
boardView = window.localStorage.getItem('boardView');
}
return boardView === 'board-view-cal';
},
hasSwimlanes() {
const currentBoard = Utils.getCurrentBoard();
if (!currentBoard) {
if (process.env.DEBUG === 'true') {
console.log('hasSwimlanes: No current board');
}
return false;
}
try {
const swimlanes = currentBoard.swimlanes();
const hasSwimlanes = swimlanes && swimlanes.length > 0;
if (process.env.DEBUG === 'true') {
console.log('hasSwimlanes: Board has', swimlanes ? swimlanes.length : 0, 'swimlanes');
}
return hasSwimlanes;
} catch (error) {
console.error('hasSwimlanes: Error getting swimlanes:', error);
return false;
}
},
isVerticalScrollbars() {
const user = ReactiveCache.getCurrentUser();
return user && user.isVerticalScrollbars();
},
boardView() {
return Utils.boardView();
},
debugBoardState() {
// Enable debug mode by setting ?debug=1 in URL
const urlParams = new URLSearchParams(window.location.search);
return urlParams.get('debug') === '1';
},
debugBoardStateData() {
const currentBoard = Utils.getCurrentBoard();
const currentBoardId = Session.get('currentBoard');
const isBoardReady = this.isBoardReady.get();
const isConverting = this.isConverting.get();
const isMigrating = this.isMigrating.get();
const boardView = Utils.boardView();
if (process.env.DEBUG === 'true') {
console.log('=== BOARD DEBUG STATE ===');
console.log('currentBoardId:', currentBoardId);
console.log('currentBoard:', !!currentBoard, currentBoard ? currentBoard.title : 'none');
console.log('isBoardReady:', isBoardReady);
console.log('isConverting:', isConverting);
console.log('isMigrating:', isMigrating);
console.log('boardView:', boardView);
console.log('========================');
}
return {
currentBoardId,
hasCurrentBoard: !!currentBoard,
currentBoardTitle: currentBoard ? currentBoard.title : 'none',
isBoardReady,
isConverting,
isMigrating,
boardView
};
},
openNewListForm() {
if (this.isViewSwimlanes()) {
// The form had been removed in 416b17062e57f215206e93a85b02ef9eb1ab4902
@@ -298,6 +874,31 @@ BlazeComponent.extendComponent({
}
},
'click .js-empty-board-add-swimlane': Popup.open('swimlaneAdd'),
// Global drag and drop file upload handlers for better visual feedback
'dragover .board-canvas'(event) {
const dataTransfer = event.originalEvent.dataTransfer;
if (dataTransfer && dataTransfer.types && dataTransfer.types.includes('Files')) {
event.preventDefault();
// Add visual indicator that files can be dropped
$('.board-canvas').addClass('file-drag-over');
}
},
'dragleave .board-canvas'(event) {
const dataTransfer = event.originalEvent.dataTransfer;
if (dataTransfer && dataTransfer.types && dataTransfer.types.includes('Files')) {
// Only remove class if we're leaving the board canvas entirely
if (!event.currentTarget.contains(event.relatedTarget)) {
$('.board-canvas').removeClass('file-drag-over');
}
}
},
'drop .board-canvas'(event) {
const dataTransfer = event.originalEvent.dataTransfer;
if (dataTransfer && dataTransfer.types && dataTransfer.types.includes('Files')) {
event.preventDefault();
$('.board-canvas').removeClass('file-drag-over');
}
},
},
];
},
@@ -326,8 +927,109 @@ BlazeComponent.extendComponent({
},
}).register('boardBody');
// Accessibility: Allow users to enable/disable keyboard shortcuts
window.wekanShortcutsEnabled = true;
window.toggleWekanShortcuts = function(enabled) {
window.wekanShortcutsEnabled = !!enabled;
};
// Example: Wrap your character key shortcut handler like this
document.addEventListener('keydown', function(e) {
// Example: "W" key shortcut (replace with your actual shortcut logic)
if (!window.wekanShortcutsEnabled) return;
if (e.key === 'w' || e.key === 'W') {
// ...existing shortcut logic...
// e.g. open swimlanes view, etc.
}
});
// Keyboard accessibility for card actions (favorite, archive, duplicate, etc.)
document.addEventListener('keydown', function(e) {
if (!window.wekanShortcutsEnabled) return;
// Only proceed if focus is on a card action element
const active = document.activeElement;
if (active && active.classList.contains('js-card-action')) {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
active.click();
}
// Move card up/down with arrow keys
if (e.key === 'ArrowUp') {
e.preventDefault();
if (active.dataset.cardId) {
Meteor.call('moveCardUp', active.dataset.cardId);
}
}
if (e.key === 'ArrowDown') {
e.preventDefault();
if (active.dataset.cardId) {
Meteor.call('moveCardDown', active.dataset.cardId);
}
}
}
// Make plus/add elements keyboard accessible
if (active && active.classList.contains('js-add-card')) {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
active.click();
}
}
// Keyboard move for cards (alternative to drag & drop)
if (active && active.classList.contains('js-move-card')) {
if (e.key === 'ArrowUp') {
e.preventDefault();
if (active.dataset.cardId) {
Meteor.call('moveCardUp', active.dataset.cardId);
}
}
if (e.key === 'ArrowDown') {
e.preventDefault();
if (active.dataset.cardId) {
Meteor.call('moveCardDown', active.dataset.cardId);
}
}
}
// Ensure move card buttons are focusable and have ARIA roles
document.querySelectorAll('.js-move-card').forEach(function(el) {
el.setAttribute('tabindex', '0');
el.setAttribute('role', 'button');
el.setAttribute('aria-label', 'Move card');
});
// Make toggle-buttons, color choices, reactions, and X-buttons keyboard accessible
if (active && (active.classList.contains('js-toggle') || active.classList.contains('js-color-choice') || active.classList.contains('js-reaction') || active.classList.contains('close'))) {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
active.click();
}
}
// Prevent scripts from removing focus when received
if (active) {
active.addEventListener('focus', function(e) {
// Do not remove focus
// No-op: This prevents F55 failure
}, { once: true });
}
// Make swimlane/list renaming keyboard accessible
if (active && (active.classList.contains('js-rename-swimlane') || active.classList.contains('js-rename-list'))) {
if (e.key === 'Enter') {
e.preventDefault();
active.click();
}
}
// Calendar navigation buttons
if (active && active.classList.contains('fc-button')) {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
active.click();
}
}
});
BlazeComponent.extendComponent({
onRendered() {
// Set the language attribute on the <html> element for accessibility
document.documentElement.lang = TAPi18n.getLanguage();
this.autorun(function () {
$('#calendar-view').fullCalendar('refetchEvents');
});
@@ -341,11 +1043,19 @@ BlazeComponent.extendComponent({
timezone: 'local',
weekNumbers: true,
header: {
left: 'title today prev,next',
left: 'title today prev,next',
center:
'agendaDay,listDay,timelineDay agendaWeek,listWeek,timelineWeek month,listMonth',
right: '',
},
buttonText: {
prev: TAPi18n.__('calendar-previous-month-label'), // e.g. "Previous month"
next: TAPi18n.__('calendar-next-month-label'), // e.g. "Next month"
},
ariaLabel: {
prev: TAPi18n.__('calendar-previous-month-label'),
next: TAPi18n.__('calendar-next-month-label'),
},
// height: 'parent', nope, doesn't work as the parent might be small
height: 'auto',
/* TODO: lists as resources: https://fullcalendar.io/docs/vertical-resource-view */
@@ -476,6 +1186,9 @@ BlazeComponent.extendComponent({
document.body.appendChild(modalElement);
const openModal = function() {
modalElement.style.display = 'flex';
// Set focus to the input field for better keyboard accessibility
const input = modalElement.querySelector('#card-title-input');
if (input) input.focus();
};
const closeModal = function() {
modalElement.style.display = 'none';
+37 -23
View File
@@ -1,4 +1,4 @@
/* ===============
/* ===============
THEME - NEPHRITIS
=================*/
@@ -99,7 +99,7 @@ THEME - NEPHRITIS
/* === END NEPHRITIS THEME === */
/* ===============
/* ===============
THEME - Pomegranate
=================*/
@@ -200,7 +200,7 @@ THEME - Pomegranate
/* === END Pomegranate THEME === */
/* ===============
/* ===============
THEME - Belize
=================*/
@@ -301,7 +301,7 @@ THEME - Belize
/* === END Belize THEME === */
/* ===============
/* ===============
THEME - Wisteria
=================*/
@@ -402,7 +402,7 @@ THEME - Wisteria
/* === END Wisteria THEME === */
/* ===============
/* ===============
THEME - Midnight
=================*/
@@ -503,7 +503,7 @@ THEME - Midnight
/* === END Midnight THEME === */
/* ===============
/* ===============
THEME - Pumpkin
=================*/
@@ -604,7 +604,7 @@ THEME - Pumpkin
/* === END Pumpkin THEME === */
/* ===============
/* ===============
THEME - Moderate Pink
=================*/
@@ -705,7 +705,7 @@ THEME - Moderate Pink
/* === END Moderatepink THEME === */
/* ===============
/* ===============
THEME - Strong Cyan
=================*/
@@ -806,7 +806,7 @@ THEME - Strong Cyan
/* === END Strongcyan THEME === */
/* ===============
/* ===============
THEME - Lime Green
=================*/
@@ -907,7 +907,7 @@ THEME - Lime Green
/* === END Limegreen THEME === */
/* ===============
/* ===============
THEME - Dark
=================*/
@@ -1111,7 +1111,7 @@ THEME - Dark
/* === END Dark THEME === */
/* ===============
/* ===============
THEME - Relax
=================*/
@@ -1241,7 +1241,7 @@ THEME - Relax
/* === END Relax THEME === */
/* ===============
/* ===============
THEME - Corteza
=================*/
@@ -1342,7 +1342,7 @@ THEME - Corteza
/* === END Corteza THEME === */
/* ===============
/* ===============
THEME - Clear Blue
=================*/
@@ -1609,7 +1609,7 @@ THEME - Clear Blue
/* === END Clearblue THEME === */
/* ===============
/* ===============
THEME - Natural
=================*/
@@ -1719,7 +1719,7 @@ THEME - Natural
/* === END Natural THEME === */
/* ===============
/* ===============
THEME - Modern
=================*/
@@ -2006,7 +2006,7 @@ THEME - Modern
/* === END Modern THEME === */
/* ===============
/* ===============
THEME - Modern Dark
=================*/
@@ -2496,7 +2496,7 @@ THEME - Modern Dark
/* === END ModernDark THEME === */
/* ===============
/* ===============
THEME - Exodark
=================*/
@@ -2544,8 +2544,8 @@ THEME - Exodark
background: #1b1b1b;
}
.board-color-exodark .materialCheckBox.is-checked {
border-bottom: 2px solid #222;
border-right: 2px solid #222;
border-bottom: 2px solid #dbdbdb!important;/*Fix contrast of checkbox*/
border-right: 2px solid #dbdbdb!important;
}
.board-color-exodark .is-multiselection-active .multi-selection-checkbox.is-checked + .minicard {
background: #e9e9e9;
@@ -2744,7 +2744,7 @@ THEME - Exodark
/* === END Exodark THEME === */
/* ===============
/* ===============
THEME - Clean Dark
=================*/
@@ -2811,6 +2811,7 @@ THEME - Clean Dark
.board-color-cleandark .card-details {
background: #23232B;
scrollbar-color: #ffffff #2e2e39;
border-radius: 20px;
box-shadow: none;
}
@@ -3179,10 +3180,17 @@ THEME - Clean Dark
margin-top: 8px;
}
/* ===============
.board-color-cleandark.background-box {
background-color:#23232B;
} /*Fixes contrast issues with background box in theme selection list*/
/* ===============
THEME - Clean Light
=================*/
/* Please note Clean Light theme elements also contain references to some cleandark theme elements so if unable to find code you're looking for under CleanDark it might be here. This should probably be cleaned up*/
.board-color-cleanlight.background-box {
background-color:#e0e0e0;
color:#010101!important;
} /*Fixes issues with text colour/background box being similar no contrast */
.board-color-cleanlight {
background: #E0E0E0;
@@ -3314,7 +3322,13 @@ THEME - Clean Light
.board-color-cleanlight .list .list-header .list-header-menu .js-collapse,
.board-color-cleandark .list .list-header .list-header-menu .js-collapse {
display: none;
/* Make collapse button visible in Clean Light / Clean Dark themes.
Previously this was hidden which caused the missing Collapse button
when using these themes. Use inline-block so it lines up with other
header controls. */
display: inline-block;
vertical-align: middle;
color: inherit;
}
.board-color-cleanlight .list-header-add,
@@ -3485,7 +3499,7 @@ THEME - Clean Light
.board-color-cleandark .card-details-header {
background: #2E2E39 !important;
color: rgba(255, 255, 255, 1);
color: #FFF !important;
}
.board-color-cleanlight .card-details-header .card-details-title,
+923 -7
View File
@@ -1,5 +1,5 @@
.integration-form {
padding: 5px;
padding: 0.7vh 0.7vw;
border-bottom: 1px solid #ccc;
}
.flex,
@@ -12,12 +12,928 @@
display: flex;
}
.option {
-webkit-border-radius: 3px;
border-radius: 3px;
-webkit-border-radius: 0.4vw;
border-radius: 0.4vw;
background: #fff;
text-decoration: none;
-webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.2);
box-shadow: 0 1px 2px rgba(0,0,0,0.2);
margin-top: 5px;
padding: 5px;
-webkit-box-shadow: 0 0.2vh 0.3vh rgba(0,0,0,0.2);
box-shadow: 0 0.2vh 0.3vh rgba(0,0,0,0.2);
margin-top: 0.7vh;
padding: 0.7vh 0.7vw;
}
/* Zoom and Mobile Mode Controls */
.board-header-btns.center {
display: flex;
align-items: center;
justify-content: center;
flex: 1;
}
.zoom-controls {
display: flex;
align-items: center;
gap: 0.5vw;
background: rgba(255, 255, 255, 0.9);
padding: 0.5vh 1vw;
border-radius: 0.5vw;
box-shadow: 0 0.2vh 0.5vh rgba(0,0,0,0.1);
}
.zoom-controls .board-header-btn {
padding: 0.5vh 0.8vw !important;
border-radius: 0.3vw !important;
background: #fff !important;
border: 1px solid #000 !important;
transition: all 0.2s ease !important;
color: #000 !important;
height: auto !important;
line-height: normal !important;
margin: 0 !important;
float: none !important;
overflow: visible !important;
}
.zoom-controls .board-header-btn i {
color: #000 !important;
float: none !important;
display: inline !important;
line-height: normal !important;
margin: 0 !important;
}
.zoom-controls .board-header-btn:hover {
background: #000 !important;
border-color: #000 !important;
color: #fff !important;
}
.zoom-controls .board-header-btn:hover i {
color: #fff !important;
}
.zoom-controls .board-header-btn.is-active {
background: #0079bf;
color: white;
border-color: #005a8a;
}
.zoom-controls .board-header-btn.is-active i {
color: white;
}
.zoom-level {
font-weight: bold;
color: #333;
min-width: 3vw;
text-align: center;
font-size: clamp(12px, 2vw, 14px);
cursor: pointer;
padding: 0.3vh 0.5vw;
border-radius: 0.3vw;
transition: all 0.2s ease;
}
.zoom-level:hover {
background: #f0f0f0;
color: #000;
}
/* Mobile Mode Styles */
.mobile-mode .board-wrapper {
width: 100%;
height: 100%;
}
.mobile-mode .board-canvas {
height: 100%;
}
.mobile-mode .minicard {
font-size: clamp(16px, 4vw, 20px);
padding: 1.2vh 1.5vw 0.5vh;
min-height: 3vh;
}
.mobile-mode .list-header-name {
font-size: clamp(18px, 4.5vw, 24px);
}
.mobile-mode .board-header-btn {
padding: 1vh 1.5vw;
font-size: clamp(14px, 3.5vw, 18px);
}
.mobile-mode .zoom-controls {
padding: 1vh 1.5vw;
gap: 1vw;
}
.mobile-mode .zoom-controls .board-header-btn {
padding: 1vh 1.5vw !important;
font-size: clamp(14px, 3.5vw, 18px) !important;
background: #fff !important;
border: 1px solid #000 !important;
color: #000 !important;
height: auto !important;
line-height: normal !important;
margin: 0 !important;
float: none !important;
overflow: visible !important;
}
.mobile-mode .zoom-controls .board-header-btn i {
color: #000 !important;
float: none !important;
display: inline !important;
line-height: normal !important;
margin: 0 !important;
}
.mobile-mode .zoom-controls .board-header-btn:hover {
background: #000 !important;
border-color: #000 !important;
color: #fff !important;
}
.mobile-mode .zoom-controls .board-header-btn:hover i {
color: #fff !important;
}
.mobile-mode .zoom-level {
font-size: clamp(14px, 3.5vw, 18px);
min-width: 4vw;
}
/* Comprehensive Mobile Mode Styles - Works on all screen sizes */
.mobile-mode .board-wrapper {
width: 100% !important;
height: 100% !important;
transform: none !important;
transform-origin: initial !important;
max-width: 100% !important;
}
.mobile-mode .board-canvas {
height: 100% !important;
overflow-x: hidden !important;
overflow-y: auto !important;
width: 100% !important;
max-width: 100% !important;
}
.mobile-mode .swimlane {
width: 100% !important;
min-width: 100% !important;
max-width: 100% !important;
margin-bottom: 2rem !important;
display: block !important;
float: none !important;
}
.mobile-mode .swimlane-header {
width: 100% !important;
min-width: 100% !important;
max-width: 100% !important;
font-size: clamp(18px, 2.5vw, 32px) !important;
padding: 1rem !important;
margin-bottom: 1rem !important;
display: block !important;
}
.mobile-mode .list {
width: 100% !important;
min-width: 100% !important;
max-width: 100% !important;
display: block !important;
float: none !important;
margin-bottom: 2rem !important;
border-left: none !important;
border-bottom: 2px solid #ccc !important;
clear: both !important;
}
.mobile-mode .list-header {
width: 100% !important;
min-width: 100% !important;
max-width: 100% !important;
padding: 1rem !important;
font-size: clamp(18px, 2.5vw, 32px) !important;
display: grid !important;
grid-template-columns: 30px 1fr auto auto !important;
gap: 10px !important;
align-items: center !important;
position: relative !important;
}
.mobile-mode .list-header .list-header-name {
font-size: clamp(18px, 2.5vw, 32px) !important;
font-weight: bold !important;
grid-row: 1 !important;
grid-column: 2 !important;
align-self: end !important;
display: block !important;
visibility: visible !important;
opacity: 1 !important;
}
.mobile-mode .list-header .cardCount {
font-size: clamp(14px, 2vw, 24px) !important;
grid-row: 2 !important;
grid-column: 2 !important;
align-self: start !important;
display: block !important;
visibility: visible !important;
opacity: 1 !important;
}
.mobile-mode .list-header .list-header-menu-icon {
position: static !important;
right: auto !important;
top: auto !important;
transform: none !important;
grid-row: 1/3 !important;
grid-column: 3 !important;
padding: 14px !important;
font-size: clamp(24px, 3vw, 48px) !important;
text-align: center !important;
display: block !important;
visibility: visible !important;
opacity: 1 !important;
}
.mobile-mode .list-header .list-header-handle {
position: static !important;
right: auto !important;
top: auto !important;
transform: none !important;
grid-row: 1/3 !important;
grid-column: 4 !important;
padding: 14px !important;
font-size: clamp(28px, 3.5vw, 56px) !important;
text-align: center !important;
display: block !important;
visibility: visible !important;
opacity: 1 !important;
}
.mobile-mode .list-body {
width: 100% !important;
min-width: 100% !important;
max-width: 100% !important;
padding: 1rem !important;
display: block !important;
}
.mobile-mode .minicard {
width: 100% !important;
min-width: 100% !important;
max-width: 100% !important;
font-size: clamp(16px, 2vw, 24px) !important;
padding: 1.2vh 1.5vw 0.5vh !important;
min-height: 3vh !important;
margin-bottom: 0.5rem !important;
display: block !important;
float: none !important;
}
.mobile-mode .minicard .minicard-title {
font-size: clamp(16px, 2vw, 24px) !important;
font-weight: bold !important;
}
.mobile-mode .minicard .minicard-members {
font-size: clamp(12px, 1.5vw, 18px) !important;
}
.mobile-mode .minicard .minicard-lists {
font-size: clamp(12px, 1.5vw, 18px) !important;
}
/* Desktop Mode Styles */
.desktop-mode .board-wrapper {
width: auto !important;
height: auto !important;
}
.desktop-mode .swimlane {
width: auto !important;
min-width: auto !important;
}
.desktop-mode .list {
width: auto !important;
min-width: auto !important;
display: flex !important;
float: left !important;
margin-bottom: 0 !important;
border-left: 1px solid #ccc !important;
border-bottom: none !important;
}
.desktop-mode .list-header {
width: auto !important;
min-width: auto !important;
padding: 2.5vh 1.5vw 0.5vh !important;
font-size: clamp(14px, 3vw, 18px) !important;
display: block !important;
}
.desktop-mode .list-header .list-header-name {
font-size: clamp(14px, 3vw, 18px) !important;
display: inline !important;
grid-row: auto !important;
grid-column: auto !important;
align-self: auto !important;
}
.desktop-mode .list-header .cardCount {
font-size: 12px !important;
grid-row: auto !important;
grid-column: auto !important;
align-self: auto !important;
}
.desktop-mode .list-header .list-header-menu-icon {
position: absolute !important;
right: 60px !important;
top: 50% !important;
transform: translateY(-50%) !important;
grid-row: auto !important;
grid-column: auto !important;
padding: 14px !important;
font-size: 40px !important;
}
.desktop-mode .list-header .list-header-handle {
position: absolute !important;
right: 10px !important;
top: 50% !important;
transform: translateY(-50%) !important;
grid-row: auto !important;
grid-column: auto !important;
padding: 7px !important;
font-size: clamp(16px, 3vw, 20px) !important;
}
.desktop-mode .list-body {
width: auto !important;
min-width: auto !important;
padding: 5px 11px !important;
}
.desktop-mode .minicard {
width: auto !important;
min-width: auto !important;
font-size: clamp(12px, 2.5vw, 16px) !important;
padding: 0.5vh 0.8vw !important;
min-height: auto !important;
margin-bottom: 9px !important;
}
.desktop-mode .minicard .minicard-title {
font-size: clamp(12px, 2.5vw, 16px) !important;
}
.desktop-mode .minicard .minicard-members {
font-size: 10px !important;
}
.desktop-mode .minicard .minicard-lists {
font-size: 10px !important;
}
/* Additional Mobile Mode Styles for Other Elements - Works on all screen sizes */
.mobile-mode .swimlane-header .swimlane-title {
font-size: clamp(18px, 2.5vw, 32px) !important;
font-weight: bold !important;
display: block !important;
visibility: visible !important;
opacity: 1 !important;
}
.mobile-mode .swimlane-header .swimlane-description {
font-size: clamp(14px, 2vw, 24px) !important;
display: block !important;
visibility: visible !important;
opacity: 1 !important;
}
.mobile-mode .board-header {
font-size: clamp(18px, 2.5vw, 32px) !important;
padding: 1rem !important;
width: 100% !important;
max-width: 100% !important;
}
.mobile-mode .board-header .board-header-title {
font-size: clamp(18px, 2.5vw, 32px) !important;
font-weight: bold !important;
display: block !important;
visibility: visible !important;
opacity: 1 !important;
}
.mobile-mode .board-header .board-header-description {
font-size: clamp(14px, 2vw, 24px) !important;
display: block !important;
visibility: visible !important;
opacity: 1 !important;
}
.mobile-mode .board-header .board-header-btn {
font-size: clamp(14px, 2vw, 24px) !important;
padding: 1vh 1.5vw !important;
display: inline-block !important;
visibility: visible !important;
opacity: 1 !important;
}
.mobile-mode .board-header .board-header-btn i {
font-size: clamp(14px, 2vw, 24px) !important;
display: inline !important;
visibility: visible !important;
opacity: 1 !important;
}
/* Force mobile mode visibility on all screen sizes */
.mobile-mode .list-header .fa-angle-right,
.mobile-mode .list-header .fa-arrows {
display: block !important;
visibility: visible !important;
opacity: 1 !important;
position: static !important;
right: auto !important;
top: auto !important;
transform: none !important;
}
.mobile-mode .list-header .fa-angle-right {
grid-row: 1/3 !important;
grid-column: 3 !important;
padding: 14px !important;
font-size: clamp(24px, 3vw, 48px) !important;
text-align: center !important;
}
.mobile-mode .list-header .fa-arrows {
grid-row: 1/3 !important;
grid-column: 4 !important;
padding: 14px !important;
font-size: clamp(28px, 3.5vw, 56px) !important;
text-align: center !important;
}
/* Override any media queries that might hide elements in mobile mode */
.mobile-mode * {
max-width: none !important;
}
.mobile-mode .list,
.mobile-mode .swimlane,
.mobile-mode .board-wrapper,
.mobile-mode .board-canvas {
max-width: 100% !important;
width: 100% !important;
min-width: 100% !important;
}
/* Force mobile mode list styling on all screen sizes - override desktop CSS */
.mobile-mode .board-canvas {
display: block !important;
flex-direction: column !important;
flex-wrap: nowrap !important;
align-items: stretch !important;
justify-content: flex-start !important;
width: 100% !important;
max-width: 100% !important;
min-width: 100% !important;
overflow-x: hidden !important;
overflow-y: auto !important;
}
.mobile-mode .swimlane {
display: block !important;
width: 100% !important;
max-width: 100% !important;
min-width: 100% !important;
margin: 0 0 2rem 0 !important;
padding: 0 !important;
float: none !important;
clear: both !important;
}
.mobile-mode .swimlane .swimlane-header {
display: block !important;
width: 100% !important;
max-width: 100% !important;
min-width: 100% !important;
margin: 0 0 1rem 0 !important;
padding: 1rem !important;
font-size: clamp(18px, 2.5vw, 32px) !important;
font-weight: bold !important;
border-bottom: 2px solid #ccc !important;
}
.mobile-mode .swimlane .lists {
display: block !important;
width: 100% !important;
max-width: 100% !important;
min-width: 100% !important;
margin: 0 !important;
padding: 0 !important;
flex-direction: column !important;
flex-wrap: nowrap !important;
align-items: stretch !important;
justify-content: flex-start !important;
}
.mobile-mode .list {
display: block !important;
width: 100% !important;
max-width: 100% !important;
min-width: 100% !important;
margin: 0 0 2rem 0 !important;
padding: 0 !important;
float: none !important;
clear: both !important;
border-left: none !important;
border-right: none !important;
border-top: none !important;
border-bottom: 2px solid #ccc !important;
flex: none !important;
flex-basis: auto !important;
flex-grow: 0 !important;
flex-shrink: 0 !important;
position: static !important;
left: auto !important;
right: auto !important;
top: auto !important;
bottom: auto !important;
transform: none !important;
}
.mobile-mode .list:first-child {
margin-left: 0 !important;
margin-top: 0 !important;
}
.mobile-mode .list:last-child {
margin-right: 0 !important;
margin-bottom: 0 !important;
}
.mobile-mode .list.ui-sortable-helper {
display: block !important;
width: 100% !important;
max-width: 100% !important;
min-width: 100% !important;
height: auto !important;
min-height: 60px !important;
margin: 0 0 2rem 0 !important;
padding: 0 !important;
float: none !important;
clear: both !important;
border-left: none !important;
border-right: none !important;
border-top: none !important;
border-bottom: 2px solid #ccc !important;
flex: none !important;
}
.mobile-mode .list.placeholder {
display: block !important;
width: 100% !important;
max-width: 100% !important;
min-width: 100% !important;
height: auto !important;
min-height: 60px !important;
margin: 0 0 2rem 0 !important;
padding: 0 !important;
float: none !important;
clear: both !important;
border-left: none !important;
border-right: none !important;
border-top: none !important;
border-bottom: 2px solid #ccc !important;
flex: none !important;
}
/* Override any existing responsive CSS that might interfere with mobile mode */
.mobile-mode .board-canvas .swimlane .lists {
display: block !important;
width: 100% !important;
max-width: 100% !important;
min-width: 100% !important;
margin: 0 !important;
padding: 0 !important;
flex-direction: column !important;
flex-wrap: nowrap !important;
align-items: stretch !important;
justify-content: flex-start !important;
overflow: visible !important;
}
.mobile-mode .board-canvas .swimlane .lists .list {
display: block !important;
width: 100% !important;
max-width: 100% !important;
min-width: 100% !important;
margin: 0 0 2rem 0 !important;
padding: 0 !important;
float: none !important;
clear: both !important;
border-left: none !important;
border-right: none !important;
border-top: none !important;
border-bottom: 2px solid #ccc !important;
flex: none !important;
flex-basis: auto !important;
flex-grow: 0 !important;
flex-shrink: 0 !important;
position: static !important;
left: auto !important;
right: auto !important;
top: auto !important;
bottom: auto !important;
transform: none !important;
}
/* Force mobile mode to override any media query styles */
@media screen and (min-width: 801px) {
.mobile-mode .board-canvas {
display: block !important;
flex-direction: column !important;
flex-wrap: nowrap !important;
align-items: stretch !important;
justify-content: flex-start !important;
width: 100% !important;
max-width: 100% !important;
min-width: 100% !important;
overflow-x: hidden !important;
overflow-y: auto !important;
}
.mobile-mode .swimlane {
display: block !important;
width: 100% !important;
max-width: 100% !important;
min-width: 100% !important;
margin: 0 0 2rem 0 !important;
padding: 0 !important;
float: none !important;
clear: both !important;
}
.mobile-mode .swimlane .lists {
display: block !important;
width: 100% !important;
max-width: 100% !important;
min-width: 100% !important;
margin: 0 !important;
padding: 0 !important;
flex-direction: column !important;
flex-wrap: nowrap !important;
align-items: stretch !important;
justify-content: flex-start !important;
}
.mobile-mode .list {
display: block !important;
width: 100% !important;
max-width: 100% !important;
min-width: 100% !important;
margin: 0 0 2rem 0 !important;
padding: 0 !important;
float: none !important;
clear: both !important;
border-left: none !important;
border-right: none !important;
border-top: none !important;
border-bottom: 2px solid #ccc !important;
flex: none !important;
flex-basis: auto !important;
flex-grow: 0 !important;
flex-shrink: 0 !important;
position: static !important;
left: auto !important;
right: auto !important;
top: auto !important;
bottom: auto !important;
transform: none !important;
}
}
/* Hide desktop-only elements in mobile mode (like mobile media queries do) */
.mobile-mode .board-header-btn i.fa + span {
display: none !important;
}
.mobile-mode .board-header-btn span {
display: none !important;
}
.mobile-mode .board-header-btn .fa + span {
display: none !important;
}
.mobile-mode .board-header-btn .fa + .board-header-btn-text {
display: none !important;
}
.mobile-mode .board-header-btn .fa + .board-header-btn-label {
display: none !important;
}
/* Show only icons in mobile mode */
.mobile-mode .board-header-btn {
width: auto !important;
min-width: auto !important;
padding: 8px !important;
text-align: center !important;
}
.mobile-mode .board-header-btn i {
display: inline-block !important;
margin: 0 !important;
}
/* Hide desktop-specific elements that shouldn't show in mobile mode */
.mobile-mode .desktop-only,
.mobile-mode .board-header .desktop-only {
display: none !important;
}
.mobile-mode .board-header .board-header-btn.desktop-only {
display: none !important;
}
/* Hide desktop-specific board header buttons in mobile mode */
.mobile-mode .board-header-btns.left {
display: none !important;
}
.mobile-mode .board-header-btns.center {
display: none !important;
}
/* Show only the right section buttons in mobile mode, but hide text labels */
.mobile-mode .board-header-btns.right {
display: block !important;
}
.mobile-mode .board-header-btns.right .board-header-btn span {
display: none !important;
}
.mobile-mode .board-header-btns.right .board-header-btn .fa + span {
display: none !important;
}
.mobile-mode .board-header-btns.right .board-header-btn .fa + .board-header-btn-text {
display: none !important;
}
.mobile-mode .board-header-btns.right .board-header-btn .fa + .board-header-btn-label {
display: none !important;
}
/* Hide specific desktop-only buttons that shouldn't show in mobile mode */
.mobile-mode .board-header-btn.js-star-board span,
.mobile-mode .board-header-btn.js-change-visibility span,
.mobile-mode .board-header-btn.js-watch-board span,
.mobile-mode .board-header-btn.js-sort-cards span {
display: none !important;
}
/* Show only icons for mobile mode buttons */
.mobile-mode .board-header-btns.right .board-header-btn {
width: auto !important;
min-width: auto !important;
padding: 8px !important;
text-align: center !important;
margin: 0 2px !important;
}
.mobile-mode .board-header-btns.right .board-header-btn i {
display: inline-block !important;
margin: 0 !important;
}
/* Ensure mobile mode looks like small screen mobile view */
.mobile-mode .board-header {
height: 40px !important;
}
.mobile-mode .board-header .board-header-btns {
margin-top: 0px !important;
}
.mobile-mode .board-header .board-header-btn {
height: 32px !important;
line-height: 32px !important;
font-size: 15px !important;
}
.mobile-mode .board-header .board-header-btn i.fa {
line-height: 32px !important;
}
/* Copy mobile media query styles to mobile mode for consistent appearance */
.mobile-mode .board-header {
height: 40px !important;
padding: 0 !important;
margin: 0 !important;
}
.mobile-mode .board-header .board-header-btns {
margin-top: 0px !important;
height: 40px !important;
display: flex !important;
align-items: center !important;
justify-content: flex-end !important;
}
.mobile-mode .board-header .board-header-btn {
height: 32px !important;
line-height: 32px !important;
font-size: 15px !important;
margin: 0 2px !important;
padding: 4px 8px !important;
border-radius: 4px !important;
background: rgba(255, 255, 255, 0.1) !important;
border: 1px solid rgba(255, 255, 255, 0.2) !important;
color: #fff !important;
text-decoration: none !important;
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
min-width: 32px !important;
width: auto !important;
}
.mobile-mode .board-header .board-header-btn:hover {
background: rgba(255, 255, 255, 0.2) !important;
border-color: rgba(255, 255, 255, 0.3) !important;
}
.mobile-mode .board-header .board-header-btn i.fa {
line-height: 32px !important;
font-size: 15px !important;
margin: 0 !important;
padding: 0 !important;
}
.mobile-mode .board-header .board-header-btn i.fa + span {
display: none !important;
}
.mobile-mode .board-header .board-header-btn span {
display: none !important;
}
/* Hide the board title in mobile mode to match mobile view */
.mobile-mode .header-board-menu {
display: none !important;
}
/* Ensure the board header takes full width in mobile mode */
.mobile-mode .board-header {
width: 100% !important;
max-width: 100% !important;
display: flex !important;
align-items: center !important;
justify-content: space-between !important;
padding: 0 10px !important;
}
/* Additional Desktop Mode Styles for Other Elements */
.desktop-mode .swimlane-header .swimlane-title {
font-size: clamp(14px, 3vw, 18px) !important;
}
.desktop-mode .swimlane-header .swimlane-description {
font-size: 12px !important;
}
.desktop-mode .board-header {
font-size: clamp(14px, 3vw, 18px) !important;
padding: 2.5vh 1.5vw 0.5vh !important;
}
.desktop-mode .board-header .board-header-title {
font-size: clamp(14px, 3vw, 18px) !important;
}
.desktop-mode .board-header .board-header-description {
font-size: 12px !important;
}
.desktop-mode .board-header .board-header-btn {
font-size: clamp(12px, 2.5vw, 16px) !important;
padding: 0.5vh 0.8vw !important;
}
.desktop-mode .board-header .board-header-btn i {
font-size: clamp(12px, 2.5vw, 16px) !important;
}
+122 -95
View File
@@ -14,91 +14,85 @@ template(name="boardHeaderBar")
with currentBoard
if currentUser.isBoardAdmin
a.board-header-btn(class="{{#if currentUser.isBoardAdmin}}js-edit-board-title{{else}}is-disabled{{/if}}" title="{{_ 'edit'}}" value=title)
i.fa.fa-pencil-square-o
| ✏️
a.board-header-btn.js-star-board(class="{{#if isStarred}}is-active{{/if}}"
title="{{#if isStarred}}{{_ 'click-to-unstar'}}{{else}}{{_ 'click-to-star'}}{{/if}} {{_ 'starred-boards-description'}}")
i.fa(class="fa-star{{#unless isStarred}}-o{{/unless}}")
if showStarCounter
span
= currentBoard.stars
a.board-header-btn.js-star-board(class="{{#if isStarred}}is-active{{/if}}"
title="{{#if isStarred}}{{_ 'star-board-short-unstar'}}{{else}}{{_ 'star-board-short-star'}}{{/if}}" aria-label="{{#if isStarred}}{{_ 'star-board-short-unstar'}}{{else}}{{_ 'star-board-short-star'}}{{/if}}")
| {{#if isStarred}}⭐{{else}}☆{{/if}}
if showStarCounter
span
= currentBoard.stars
a.board-header-btn(
class="{{#if currentUser.isBoardAdmin}}js-change-visibility{{else}}is-disabled{{/if}}"
title="{{_ currentBoard.permission}}")
i.fa(class="{{#if currentBoard.isPublic}}fa-globe{{else}}fa-lock{{/if}}")
span {{_ currentBoard.permission}}
a.board-header-btn(
class="{{#if currentUser.isBoardAdmin}}js-change-visibility{{else}}is-disabled{{/if}}"
title="{{_ currentBoard.permission}}")
| {{#if currentBoard.isPublic}}🌐{{else}}🔒{{/if}}
span {{_ currentBoard.permission}}
a.board-header-btn.js-watch-board(
title="{{_ watchLevel }}")
if $eq watchLevel "watching"
i.fa.fa-eye
if $eq watchLevel "tracking"
i.fa.fa-bell
if $eq watchLevel "muted"
i.fa.fa-bell-slash
span {{_ watchLevel}}
a.board-header-btn(title="{{_ 'sort-cards'}}" class="{{#if isSortActive }}emphasis{{else}} js-sort-cards {{/if}}")
i.fa.fa-sort
span {{#if isSortActive }}{{_ 'sort-is-on'}}{{else}}{{_ 'sort-cards'}}{{/if}}
a.board-header-btn.js-watch-board(
title="{{_ watchLevel }}")
if $eq watchLevel "watching"
| 👁️
if $eq watchLevel "tracking"
| 🔔
if $eq watchLevel "muted"
| 🔕
span {{_ watchLevel}}
a.board-header-btn(title="{{_ 'sort-cards'}}" class="{{#if isSortActive }}emphasis{{else}} js-sort-cards {{/if}}")
| {{sortCardsIcon}}
span {{#if isSortActive }}{{_ 'sort-is-on'}}{{else}}{{_ 'sort-cards'}}{{/if}}
if isSortActive
a.board-header-btn-close.js-sort-reset(title="{{_ 'remove-sort'}}")
i.fa.fa-times-thin
| ❌
else
a.board-header-btn.js-log-in(
title="{{_ 'log-in'}}")
i.fa.fa-sign-in
| 🚪
span {{_ 'log-in'}}
.board-header-btns.center
.board-header-btns.right
if currentBoard
if isMiniScreen
if currentUser
with currentBoard
a.board-header-btn(class="{{#if currentUser.isBoardAdmin}}js-edit-board-title{{else}}is-disabled{{/if}}" title="{{_ 'edit'}}" value=title)
i.fa.fa-pencil-square-o
| ✏️
a.board-header-btn.js-star-board(class="{{#if isStarred}}is-active{{/if}}"
title="{{#if isStarred}}{{_ 'click-to-unstar'}}{{else}}{{_ 'click-to-star'}}{{/if}} {{_ 'starred-boards-description'}}")
i.fa(class="fa-star{{#unless isStarred}}-o{{/unless}}")
if showStarCounter
span
= currentBoard.stars
a.board-header-btn.js-star-board(class="{{#if isStarred}}is-active{{/if}}"
title="{{#if isStarred}}{{_ 'click-to-unstar'}}{{else}}{{_ 'click-to-star'}}{{/if}} {{_ 'starred-boards-description'}}")
| {{#if isStarred}}⭐{{else}}☆{{/if}}
a.board-header-btn(
class="{{#if currentUser.isBoardAdmin}}js-change-visibility{{else}}is-disabled{{/if}}"
title="{{_ currentBoard.permission}}")
i.fa(class="{{#if currentBoard.isPublic}}fa-globe{{else}}fa-lock{{/if}}")
span {{_ currentBoard.permission}}
a.board-header-btn(
class="{{#if currentUser.isBoardAdmin}}js-change-visibility{{else}}is-disabled{{/if}}"
title="{{_ currentBoard.permission}}")
| {{#if currentBoard.isPublic}}🌐{{else}}🔒{{/if}}
a.board-header-btn.js-watch-board(
title="{{_ watchLevel }}")
if $eq watchLevel "watching"
i.fa.fa-eye
if $eq watchLevel "tracking"
i.fa.fa-bell
if $eq watchLevel "muted"
i.fa.fa-bell-slash
span {{_ watchLevel}}
a.board-header-btn(title="{{_ 'sort-cards'}}" class="{{#if isSortActive }}emphasis{{else}} js-sort-cards {{/if}}")
i.fa.fa-sort
span {{#if isSortActive }}{{_ 'sort-is-on'}}{{else}}{{_ 'sort-cards'}}{{/if}}
a.board-header-btn.js-watch-board(
title="{{_ watchLevel }}")
if $eq watchLevel "watching"
| 👁️
if $eq watchLevel "tracking"
| 🔔
if $eq watchLevel "muted"
| 🔕
a.board-header-btn(title="{{_ 'sort-cards'}}" class="{{#if isSortActive }}emphasis{{else}} js-sort-cards {{/if}}")
| {{sortCardsIcon}}
if isSortActive
a.board-header-btn-close.js-sort-reset(title="{{_ 'remove-sort'}}")
i.fa.fa-times-thin
| ❌
else
a.board-header-btn.js-log-in(
title="{{_ 'log-in'}}")
i.fa.fa-sign-in
span {{_ 'log-in'}}
| 🚪
if isSandstorm
if currentUser
a.board-header-btn.js-open-archived-board
i.fa.fa-archive
span {{_ 'archives'}}
| 📦
//if showSort
// a.board-header-btn.js-open-sort-view(title="{{_ 'sort-desc'}}")
@@ -108,60 +102,56 @@ template(name="boardHeaderBar")
a.board-header-btn.js-open-filter-view(
title="{{#if Filter.isActive}}{{_ 'filter-on-desc'}}{{else}}{{_ 'filter'}}{{/if}}"
class="{{#if Filter.isActive}}emphasis{{/if}}")
i.fa.fa-filter
span {{#if Filter.isActive}}{{_ 'filter-on'}}{{else}}{{_ 'filter'}}{{/if}}
| 🔽
if Filter.isActive
a.board-header-btn-close.js-filter-reset(title="{{_ 'filter-clear'}}")
i.fa.fa-times-thin
| ❌
a.board-header-btn.js-open-search-view(title="{{_ 'search'}}")
i.fa.fa-search
span {{_ 'search'}}
| 🔍
unless currentBoard.isTemplatesBoard
a.board-header-btn.js-toggle-board-view(
title="{{_ 'board-view'}}")
i.fa.fa-caret-down
| ▼
if $eq boardView 'board-view-swimlanes'
i.fa.fa-th-large
| 🏊
if $eq boardView 'board-view-lists'
i.fa.fa-trello
| 📋
if $eq boardView 'board-view-cal'
i.fa.fa-calendar
span {{#if boardView}}{{_ boardView}}{{else}}{{_ 'board-view-swimlanes'}}{{/if}}
| 📅
if canModifyBoard
a.board-header-btn.js-multiselection-activate(
title="{{#if MultiSelection.isActive}}{{_ 'multi-selection-on'}}{{else}}{{_ 'multi-selection'}}{{/if}}"
class="{{#if MultiSelection.isActive}}emphasis{{/if}}")
i.fa.fa-check-square-o
span {{#if MultiSelection.isActive}}{{_ 'multi-selection-on'}}{{else}}{{_ 'multi-selection'}}{{/if}}
if MultiSelection.isActive
a.board-header-btn-close.js-multiselection-reset(title="{{_ 'filter-clear'}}")
i.fa.fa-times-thin
| ☑️
if MultiSelection.isActive
a.board-header-btn-close.js-multiselection-reset(title="{{_ 'filter-clear'}}")
| ❌
.separator
a.board-header-btn.js-toggle-sidebar(title="{{_ 'sidebar-open'}} {{_ 'or'}} {{_ 'sidebar-close'}}")
i.fa.fa-navicon
| ☰
template(name="boardVisibilityList")
ul.pop-over-list
li
with "private"
a.js-select-visibility
i.fa.fa-lock.colorful
| 🔒
| {{_ 'private'}}
if visibilityCheck
i.fa.fa-check
| ✅
span.sub-name {{_ 'private-desc'}}
if notAllowPrivateVisibilityOnly
li
with "public"
a.js-select-visibility
i.fa.fa-globe.colorful
| 🌐
| {{_ 'public'}}
if visibilityCheck
i.fa.fa-check
| ✅
span.sub-name {{_ 'public-desc'}}
template(name="boardChangeVisibilityPopup")
@@ -172,26 +162,26 @@ template(name="boardChangeWatchPopup")
li
with "watching"
a.js-select-watch
i.fa.fa-eye.colorful
| 👁️
| {{_ 'watching'}}
if watchCheck
i.fa.fa-check
| ✅
span.sub-name {{_ 'watching-info'}}
li
with "tracking"
a.js-select-watch
i.fa.fa-bell.colorful
| 🔔
| {{_ 'tracking'}}
if watchCheck
i.fa.fa-check
| ✅
span.sub-name {{_ 'tracking-info'}}
li
with "muted"
a.js-select-watch
i.fa.fa-bell-slash.colorful
| 🔕
| {{_ 'muted'}}
if watchCheck
i.fa.fa-check
| ✅
span.sub-name {{_ 'muted-info'}}
template(name="boardChangeViewPopup")
@@ -199,24 +189,24 @@ template(name="boardChangeViewPopup")
li
with "board-view-swimlanes"
a.js-open-swimlanes-view
i.fa.fa-th-large.colorful
| 🏊
| {{_ 'board-view-swimlanes'}}
if $eq Utils.boardView "board-view-swimlanes"
i.fa.fa-check
| ✅
li
with "board-view-lists"
a.js-open-lists-view
i.fa.fa-trello.colorful
| 📋
| {{_ 'board-view-lists'}}
if $eq Utils.boardView "board-view-lists"
i.fa.fa-check
| ✅
li
with "board-view-cal"
a.js-open-cal-view
i.fa.fa-calendar.colorful
| 📅
| {{_ 'board-view-cal'}}
if $eq Utils.boardView "board-view-cal"
i.fa.fa-check
| ✅
template(name="createBoard")
form
@@ -228,11 +218,40 @@ template(name="createBoard")
else
p.quiet
if $eq visibility.get 'public'
span.fa.fa-globe.colorful
span 🌐
= " "
| {{{_ 'board-public-info'}}}
else
span.fa.fa-lock.colorful
span 🔒
= " "
| {{{_ 'board-private-info'}}}
a.js-change-visibility {{_ 'change'}}.
a.flex.js-toggle-add-template-container
.materialCheckBox#add-template-container
span {{_ 'add-template-container'}}
input.primary.wide(type="submit" value="{{_ 'create'}}")
span.quiet
| {{_ 'or'}}
a.js-import-board {{_ 'import'}}
span.quiet
| /
a.js-board-template {{_ 'template'}}
template(name="createBoardPopup")
form
label
| {{_ 'title'}}
input.js-new-board-title(type="text" placeholder="{{_ 'bucket-example'}}" autofocus required)
if visibilityMenuIsOpen.get
+boardVisibilityList
else
p.quiet
if $eq visibility.get 'public'
span 🌐
= " "
| {{{_ 'board-public-info'}}}
else
span 🔒
= " "
| {{{_ 'board-private-info'}}}
a.js-change-visibility {{_ 'change'}}.
@@ -256,10 +275,10 @@ template(name="createBoard")
// li
// a.js-sort-by(name="{{value.name}}")
// if $eq sortby value.name
// i(class="fa {{Direction}}")
// | {{#if $eq Direction "fa-arrow-up"}}⬆️{{else}}⬇️{{/if}}
// | {{_ value.label }}{{_ value.shortLabel}}
// if $eq sortby value.name
// i(class="fa fa-check")
// | ✅
template(name="boardChangeTitlePopup")
form
@@ -279,14 +298,22 @@ template(name="boardCreateRulePopup")
template(name="cardsSortPopup")
ul.pop-over-list
li
a.js-sort-due {{_ 'due-date'}}
a.js-sort-due
| 📅
| {{_ 'due-date'}}
hr
li
a.js-sort-title {{_ 'title-alphabetically'}}
a.js-sort-title
| 🔤
| {{_ 'title-alphabetically'}}
hr
li
a.js-sort-created-desc {{_ 'created-at-newest-first'}}
a.js-sort-created-desc
| ⬇️
| {{_ 'created-at-newest-first'}}
hr
li
a.js-sort-created-asc {{_ 'created-at-oldest-first'}}
a.js-sort-created-asc
| ⬆️
| {{_ 'created-at-oldest-first'}}
+53 -4
View File
@@ -33,6 +33,8 @@ BlazeComponent.extendComponent({
return currentBoard && currentBoard.getWatchLevel(Meteor.userId());
},
isStarred() {
const boardId = Session.get('currentBoard');
const user = ReactiveCache.getCurrentUser();
@@ -80,10 +82,29 @@ BlazeComponent.extendComponent({
},
'click .js-toggle-board-view': Popup.open('boardChangeView'),
'click .js-toggle-sidebar'() {
Sidebar.toggle();
console.log('Hamburger menu clicked');
// Use the same approach as keyboard shortcuts
if (typeof Sidebar !== 'undefined' && Sidebar && typeof Sidebar.toggle === 'function') {
console.log('Using Sidebar.toggle()');
Sidebar.toggle();
} else {
console.warn('Sidebar not available, trying alternative approach');
// Try to trigger the sidebar through the global Blaze helper
if (typeof Blaze !== 'undefined' && Blaze._globalHelpers && Blaze._globalHelpers.Sidebar) {
const sidebar = Blaze._globalHelpers.Sidebar();
if (sidebar && typeof sidebar.toggle === 'function') {
console.log('Using Blaze helper Sidebar.toggle()');
sidebar.toggle();
}
}
}
},
'click .js-open-filter-view'() {
Sidebar.setView('filter');
if (Sidebar) {
Sidebar.setView('filter');
} else {
console.warn('Sidebar not available for setView');
}
},
'click .js-sort-cards': Popup.open('cardsSort'),
/*
@@ -100,14 +121,22 @@ BlazeComponent.extendComponent({
*/
'click .js-filter-reset'(event) {
event.stopPropagation();
Sidebar.setView();
if (Sidebar) {
Sidebar.setView();
} else {
console.warn('Sidebar not available for setView');
}
Filter.reset();
},
'click .js-sort-reset'() {
Session.set('sortBy', '');
},
'click .js-open-search-view'() {
Sidebar.setView('search');
if (Sidebar) {
Sidebar.setView('search');
} else {
console.warn('Sidebar not available for setView');
}
},
'click .js-multiselection-activate'() {
const currentCard = Utils.getCurrentCardId();
@@ -126,6 +155,7 @@ BlazeComponent.extendComponent({
},
];
},
}).register('boardHeaderBar');
Template.boardHeaderBar.helpers({
@@ -135,6 +165,23 @@ Template.boardHeaderBar.helpers({
isSortActive() {
return Session.get('sortBy') ? true : false;
},
sortCardsIcon() {
const sortBy = Session.get('sortBy');
if (!sortBy) {
return '🃏'; // Card icon when nothing is selected
}
// Determine which sort option is active based on sortBy object
if (sortBy.dueAt) {
return '📅'; // Due date icon
} else if (sortBy.title) {
return '🔤'; // Alphabet icon
} else if (sortBy.createdAt) {
return sortBy.createdAt === 1 ? '⬆️' : '⬇️'; // Up/down arrow based on direction
}
return '🃏'; // Default card icon
},
});
Template.boardChangeViewPopup.events({
@@ -201,6 +248,7 @@ const CreateBoard = BlazeComponent.extendComponent({
title: title,
permission: 'private',
type: 'template-container',
migrationVersion: 1, // Latest version - no migration needed
}),
);
@@ -244,6 +292,7 @@ const CreateBoard = BlazeComponent.extendComponent({
Boards.insert({
title,
permission: visibility,
migrationVersion: 1, // Latest version - no migration needed
}),
);
+595 -21
View File
@@ -1,5 +1,86 @@
@import url("../../../css/reset.css") print, screen;
/* Board List Header with Zoom Controls */
.board-list-header {
display: flex;
justify-content: center;
margin: 1vh 0 2vh 0;
padding: 1vh 0;
}
.zoom-controls {
display: flex;
align-items: center;
gap: 0.5vw;
background: rgba(255, 255, 255, 0.9);
padding: 0.5vh 1vw;
border-radius: 0.5vw;
box-shadow: 0 0.2vh 0.5vh rgba(0,0,0,0.1);
}
.zoom-controls .board-header-btn {
padding: 0.5vh 0.8vw !important;
border-radius: 0.3vw !important;
background: #fff !important;
border: 1px solid #000 !important;
transition: all 0.2s ease !important;
text-decoration: none !important;
color: #000 !important;
display: flex !important;
align-items: center !important;
gap: 0.3vw !important;
height: auto !important;
line-height: normal !important;
margin: 0 !important;
float: none !important;
overflow: visible !important;
}
.zoom-controls .board-header-btn i {
color: #000 !important;
float: none !important;
display: inline !important;
line-height: normal !important;
margin: 0 !important;
}
.zoom-controls .board-header-btn:hover {
background: #000 !important;
border-color: #000 !important;
color: #fff !important;
}
.zoom-controls .board-header-btn:hover i {
color: #fff !important;
}
.zoom-controls .board-header-btn.is-active {
background: #0079bf;
color: white;
border-color: #005a8a;
}
.zoom-controls .board-header-btn.is-active i {
color: white;
}
.zoom-level {
font-weight: bold;
color: #333;
min-width: 3vw;
text-align: center;
font-size: clamp(12px, 2vw, 14px);
cursor: pointer;
padding: 0.3vh 0.5vw;
border-radius: 0.3vw;
transition: all 0.2s ease;
}
.zoom-level:hover {
background: #f0f0f0;
color: #000;
}
.board-list {
margin: 0 8px;
}
@@ -22,8 +103,8 @@
transform: rotate(4deg);
display: block !important;
}
.board-list li.starred .fa-star,
.board-list li.starred .fa-star-o {
.board-list li.starred .is-star-active,
.board-list li.starred .is-not-star-active {
opacity: 1;
}
.board-list .board-list-item {
@@ -73,8 +154,8 @@
.board-list .js-add-board :hover {
background-color: #939393;
}
.board-list .fa-star,
.board-list .fa-star-o {
.board-list .is-star-active,
.board-list .is-not-star-active {
bottom: 0;
font-size: 14px;
height: 18px;
@@ -131,30 +212,30 @@
transition-duration: 0.15s;
transition-property: color, font-size, background;
}
.board-list li:hover a:hover .fa-star,
.board-list li:hover a:hover .is-star-active,
.board-list li:hover a:hover .fa-clone,
.board-list li:hover a:hover .fa-archive,
.board-list li:hover a:hover .fa-star-o {
.board-list li:hover a:hover .is-not-star-active {
color: #fff;
}
.board-list li:hover a .fa-star,
.board-list li:hover a .is-star-active,
.board-list li:hover a .fa-clone,
.board-list li:hover a .fa-archive,
.board-list li:hover a .fa-star-o {
.board-list li:hover a .is-not-star-active {
color: #fff;
opacity: 0.75;
}
.board-list li:hover a .fa-star:hover,
.board-list li:hover a .is-star-active:hover,
.board-list li:hover a .fa-clone:hover,
.board-list li:hover a .fa-archive:hover,
.board-list li:hover a .fa-star-o:hover {
.board-list li:hover a .is-not-star-active:hover {
font-size: 18px;
opacity: 1;
}
.board-list li:hover a .fa-star.is-star-active,
.board-list li:hover a .fa-clone.is-star-active,
.board-list li:hover a .fa-archive.is-star-active,
.board-list li:hover a .fa-star-o.is-star-active {
.board-list li:hover a .is-star-active,
.board-list li:hover a .fa-clone,
.board-list li:hover a .fa-archive,
.board-list li:hover a .is-not-star-active {
opacity: 1;
}
.board-backgrounds-list .board-background-select {
@@ -192,17 +273,144 @@
font-size: 25px;
color: #fff;
}
@media screen and (max-width: 800px) {
/* Mobile view styles - applied when isMiniScreen is true (iPhone, etc.) */
.board-list.mobile-view {
height: calc(100vh - 120px);
overflow-y: scroll !important;
overflow-x: hidden;
padding: 0 1rem;
margin: 0;
scrollbar-width: auto !important;
scrollbar-color: #888 #f1f1f1;
}
.board-list.mobile-view li {
width: 100%;
float: none;
display: block;
margin-bottom: 1rem;
padding-right: 50px; /* Space for drag handle */
}
.board-list.mobile-view .board-list-item {
overflow: visible;
height: 8rem;
width: 100%;
margin: 0;
padding-right: 50px; /* Ensure content doesn't overlap with drag handle */
}
.board-list.mobile-view .board-list-item .details {
padding-right: 50px; /* Extra space for drag handle */
width: 100%;
box-sizing: border-box;
}
.board-list.mobile-view .board-list-item-sub-name {
position: relative;
top: -100px;
left: -100px;
}
.board-list.mobile-view .board-handle {
position: absolute;
padding: 7px;
top: 50%;
transform: translateY(-50%);
right: 10px;
font-size: 24px;
color: #fff;
background: rgba(0,0,0,0.3);
border-radius: 50%;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
transition: background-color 0.2s ease;
}
.board-list.mobile-view .board-handle:hover {
background: rgba(255, 255, 0, 0.8) !important; /* Yellow hover */
}
/* Force scrollbar to be visible on mobile view */
.board-list.mobile-view::-webkit-scrollbar {
width: 12px !important;
display: block !important;
visibility: visible !important;
}
.board-list.mobile-view::-webkit-scrollbar-track {
background: #f1f1f1 !important;
border-radius: 6px !important;
display: block !important;
visibility: visible !important;
}
.board-list.mobile-view::-webkit-scrollbar-thumb {
background: #888 !important;
border-radius: 6px !important;
display: block !important;
visibility: visible !important;
}
.board-list.mobile-view::-webkit-scrollbar-thumb:hover {
background: #555 !important;
}
/* Force mobile view to have scrollable content */
.board-list.mobile-view {
min-height: 100vh; /* Force content to be tall enough to scroll */
}
/* Hide archive and clone board buttons in mobile view */
.board-list.mobile-view .js-archive-board,
.board-list.mobile-view .js-clone-board {
display: none !important;
}
/* Change board drag handle to up-down arrow in mobile view */
.board-list.mobile-view .board-handle.fa-arrows::before {
content: "↕️" !important;
font-family: inherit !important;
}
.board-list.mobile-view::after {
content: '';
display: block;
height: 100px;
}
@media screen and (max-width: 800px),
screen and (max-device-width: 800px),
screen and (-webkit-min-device-pixel-ratio: 2) and (max-width: 800px),
screen and (max-width: 800px) and (orientation: portrait),
screen and (max-width: 800px) and (orientation: landscape),
screen and (max-device-width: 932px) and (-webkit-min-device-pixel-ratio: 3) {
.board-list {
height: 100%;
overflow: scroll;
overflow-y: auto;
overflow-x: hidden;
padding: 0 1rem;
margin: 0;
}
.board-list li {
width: 50%;
width: 100%;
float: none;
display: block;
margin-bottom: 1rem;
padding-right: 50px; /* Space for drag handle */
}
.board-list .board-list-item {
overflow: hidden;
overflow: visible;
height: 8rem;
width: 100%;
margin: 0;
padding-right: 50px; /* Ensure content doesn't overlap with drag handle */
}
.board-list .board-list-item .details {
padding-right: 50px; /* Extra space for drag handle */
width: 100%;
box-sizing: border-box;
}
.board-list .board-list-item-sub-name {
position: relative;
@@ -216,19 +424,297 @@
transform: translateY(-50%);
right: 10px;
font-size: 24px;
color: #fff;
background: rgba(0,0,0,0.3);
border-radius: 50%;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
transition: background-color 0.2s ease;
}
.board-list .board-handle:hover {
background: rgba(255, 255, 0, 0.8) !important; /* Yellow hover */
}
}
/* Very small screens - ensure one board per row */
@media screen and (max-width: 360px) {
li {
width: 100%;
.board-list li {
width: 100% !important;
float: none !important;
display: block !important;
}
.board-handle {
.board-list .board-handle {
position: absolute;
padding: 7px;
top: 50%;
transform: translateY(-50%);
right: 10px;
font-size: 24px;
color: #fff;
background: rgba(0,0,0,0.3);
border-radius: 50%;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
}
}
/* Mobile - make all text and icons 2x bigger above #content on All Boards page */
@media screen and (max-width: 800px),
screen and (max-device-width: 800px),
screen and (-webkit-min-device-pixel-ratio: 2) and (max-width: 800px),
screen and (max-width: 800px) and (orientation: portrait),
screen and (max-width: 800px) and (orientation: landscape),
screen and (max-device-width: 932px) and (-webkit-min-device-pixel-ratio: 3) {
.wrapper {
font-size: 2em !important; /* 2x bigger base font size for All Boards page */
}
.wrapper * {
font-size: inherit !important; /* Inherit the 2x scaling */
}
.wrapper .fa, .wrapper .icon {
font-size: 2em !important; /* 2x bigger icons */
}
.board-list-header {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.board-list-header h1 {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.AllBoardTeamsOrgs {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.AllBoardTeamsOrgs select {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.AllBoardTeamsOrgs input {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.AllBoardTeamsOrgs .fa {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.board-list {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.board-list .board-list-item {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.board-list .board-list-item-name {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.board-list .board-list-item-desc {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.board-list .minicard-members {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.board-list .minicard-lists {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.board-list .fa {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.board-list .board-handle {
font-size: 1em !important; /* Use inherited 2x scaling */
}
}
/* Fallback for iPhone devices using JavaScript detection - All Boards page */
.iphone-device .wrapper {
font-size: 2em !important; /* 2x bigger base font size for All Boards page */
}
.iphone-device .wrapper * {
font-size: inherit !important; /* Inherit the 2x scaling */
}
.iphone-device .wrapper .fa, .iphone-device .wrapper .icon {
font-size: 2em !important; /* 2x bigger icons */
}
.iphone-device .board-list-header {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.iphone-device .board-list-header h1 {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.iphone-device .AllBoardTeamsOrgs {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.iphone-device .AllBoardTeamsOrgs select {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.iphone-device .AllBoardTeamsOrgs input {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.iphone-device .AllBoardTeamsOrgs .fa {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.iphone-device .board-list {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.iphone-device .board-list .board-list-item {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.iphone-device .board-list .board-list-item-name {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.iphone-device .board-list .board-list-item-desc {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.iphone-device .board-list .minicard-members {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.iphone-device .board-list .minicard-lists {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.iphone-device .board-list .fa {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.iphone-device .board-list .board-handle {
font-size: 1em !important; /* Use inherited 2x scaling */
}
/* iPhone 12 Mini and very small screens - make everything much larger */
@media screen and (max-width: 400px) and (max-height: 900px) {
.board-list {
height: calc(100vh - 120px) !important;
overflow-y: scroll !important;
overflow-x: hidden !important;
-webkit-overflow-scrolling: touch;
padding: 0 0.5rem;
}
.board-list li {
width: 100% !important;
float: none !important;
display: block !important;
margin-bottom: 1.5rem !important;
}
.board-list .board-list-item {
height: 12rem !important; /* Much taller */
width: 100% !important;
margin: 0 !important;
padding: 1rem !important; /* More padding */
font-size: 18px !important; /* Much larger text */
line-height: 1.4 !important;
}
.board-list .board-list-item .board-list-item-name {
font-size: 20px !important; /* Larger board names */
font-weight: bold !important;
margin-bottom: 0.5rem !important;
}
.board-list .board-list-item .board-list-item-desc {
font-size: 16px !important; /* Larger descriptions */
line-height: 1.3 !important;
}
.board-list .board-list-item .minicard-members {
font-size: 14px !important; /* Larger member avatars */
}
.board-list .board-list-item .minicard-lists {
font-size: 14px !important; /* Larger list counters */
}
.board-list .board-handle {
position: absolute;
padding: 10px !important;
top: 50%;
transform: translateY(-50%);
right: 15px !important;
font-size: 28px !important; /* Much larger drag handle */
color: #fff;
background: rgba(0,0,0,0.4) !important;
border-radius: 50%;
width: 50px !important; /* Larger handle */
height: 50px !important;
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
transition: background-color 0.2s ease;
}
.board-list .board-handle:hover {
background: rgba(255, 255, 0, 0.8) !important; /* Yellow hover */
}
/* Force scrollbar to be visible and larger */
.board-list::-webkit-scrollbar {
width: 16px !important; /* Much wider scrollbar */
display: block !important;
visibility: visible !important;
}
.board-list::-webkit-scrollbar-track {
background: #f1f1f1 !important;
border-radius: 8px !important;
display: block !important;
visibility: visible !important;
}
.board-list::-webkit-scrollbar-thumb {
background: #666 !important; /* Darker for better visibility */
border-radius: 8px !important;
display: block !important;
visibility: visible !important;
min-height: 50px !important; /* Minimum thumb size */
}
.board-list::-webkit-scrollbar-thumb:hover {
background: #333 !important;
}
/* Ensure scrollbar is always visible */
.board-list {
scrollbar-gutter: stable;
scrollbar-width: auto !important;
min-height: 100vh !important;
}
.board-list::after {
content: '';
display: block;
height: 200px !important; /* More space to ensure scrolling */
}
}
.AllBoardTeamsOrgs {
@@ -279,3 +765,91 @@
padding-right: 6px;
text-align: center;
}
/* Fix multiple scrollbars issue on mobile */
@media screen and (max-width: 800px),
screen and (max-device-width: 800px),
screen and (-webkit-min-device-pixel-ratio: 2) and (max-width: 800px),
screen and (max-width: 800px) and (orientation: portrait),
screen and (max-width: 800px) and (orientation: landscape),
screen and (max-device-width: 932px) and (-webkit-min-device-pixel-ratio: 3) {
.wrapper {
overflow: hidden;
height: 100vh;
}
.board-list {
overflow-y: scroll !important;
overflow-x: hidden !important;
-webkit-overflow-scrolling: touch;
scrollbar-width: thin;
scrollbar-color: #888 #f1f1f1;
height: calc(100vh - 120px); /* Ensure there's content to scroll */
}
/* Force scrollbar to always be visible */
.board-list::-webkit-scrollbar {
width: 12px !important;
display: block !important;
visibility: visible !important;
}
.board-list::-webkit-scrollbar-track {
background: #f1f1f1 !important;
border-radius: 6px !important;
display: block !important;
visibility: visible !important;
}
.board-list::-webkit-scrollbar-thumb {
background: #888 !important;
border-radius: 6px !important;
display: block !important;
visibility: visible !important;
}
.board-list::-webkit-scrollbar-thumb:hover {
background: #555 !important;
}
/* Ensure scrollbar is always visible */
.board-list::-webkit-scrollbar-corner {
background: #f1f1f1 !important;
}
/* Force scrollbar to be visible */
.board-list {
scrollbar-gutter: stable;
scrollbar-width: auto !important;
min-height: 100vh; /* Force content to be tall enough to scroll */
}
/* Ensure there's always content to scroll */
.board-list::after {
content: '';
display: block;
height: 100px;
}
/* Ensure only one scrollbar is visible */
body {
overflow: hidden;
}
#content {
overflow: hidden;
}
/* Hide archive and clone board buttons in mobile view */
.board-list .js-archive-board,
.board-list .js-clone-board {
display: none !important;
}
/* Change board drag handle to up-down arrow in mobile view */
.board-list .board-handle.fa-arrows::before {
content: "↕️" !important;
font-family: inherit !important;
}
}
+36 -31
View File
@@ -1,5 +1,7 @@
template(name="boardList")
.wrapper
.board-list-header
ul.AllBoardTeamsOrgs
li.AllBoardTeams
if userHasTeams
@@ -29,7 +31,7 @@ template(name="boardList")
input#filterBtn(type="button" value="{{_ 'filter'}}")
input#resetBtn(type="button" value="{{_ 'filter-clear'}}")
ul.board-list.clearfix.js-boards
ul.board-list.clearfix.js-boards(class="{{#if isMiniScreen}}mobile-view{{/if}}")
li.js-add-board
a.board-list-item.label(title="{{_ 'add-board'}}")
| {{_ 'add-board'}}
@@ -62,11 +64,9 @@ template(name="boardList")
i.fa.js-has-spenttime-cards(
class="fa-circle{{#if hasOvertimeCards}} has-overtime-card-active{{else}} no-overtime-card-active{{/if}}"
title="{{#if hasOvertimeCards}}{{_ 'has-overtime-cards'}}{{else}}{{_ 'has-spenttime-cards'}}{{/if}}")
if isTouchScreenOrShowDesktopDragHandles
i.fa.board-handle(
class="fa-arrows"
title="{{_ 'drag-board'}}")
else
i.fa.board-handle(
class="fa-arrows"
title="{{_ 'drag-board'}}")
if isSandstorm
i.fa.js-clone-board(
class="fa-clone"
@@ -106,9 +106,10 @@ template(name="boardList")
each list in boardLists _id
.item
| {{ list }}
i.fa.js-star-board(
class="fa-star{{#if isStarred}} is-star-active{{else}}-o{{/if}}"
a.js-star-board(
class="{{#if isStarred}}is-star-active{{else}}is-not-star-active{{/if}}"
title="{{_ 'star-board-title'}}")
| {{#if isStarred}}⭐{{else}}☆{{/if}}
p.board-list-item-desc
+viewer
= description
@@ -116,32 +117,36 @@ template(name="boardList")
i.fa.js-has-spenttime-cards(
class="fa-circle{{#if hasOvertimeCards}} has-overtime-card-active{{else}} no-overtime-card-active{{/if}}"
title="{{#if hasOvertimeCards}}{{_ 'has-overtime-cards'}}{{else}}{{_ 'has-spenttime-cards'}}{{/if}}")
if isTouchScreenOrShowDesktopDragHandles
i.fa.board-handle(
class="fa-arrows"
title="{{_ 'drag-board'}}")
else
i.fa.board-handle(
class="fa-arrows"
title="{{_ 'drag-board'}}")
if isSandstorm
i.fa.js-clone-board(
class="fa-clone"
title="{{_ 'duplicate-board'}}")
i.fa.js-archive-board(
class="fa-archive"
title="{{_ 'archive-board'}}")
a.js-clone-board(
class="fa-clone"
title="{{_ 'duplicate-board'}}")
| 📋
a.js-archive-board(
class="fa-archive"
title="{{_ 'archive-board'}}")
| 📦
else if isAdministrable
i.fa.js-clone-board(
class="fa-clone"
title="{{_ 'duplicate-board'}}")
i.fa.js-archive-board(
class="fa-archive"
title="{{_ 'archive-board'}}")
a.js-clone-board(
class="fa-clone"
title="{{_ 'duplicate-board'}}")
| 📋
a.js-archive-board(
class="fa-archive"
title="{{_ 'archive-board'}}")
| 📦
else if currentUser.isAdmin
i.fa.js-clone-board(
class="fa-clone"
title="{{_ 'duplicate-board'}}")
i.fa.js-archive-board(
class="fa-archive"
title="{{_ 'archive-board'}}")
a.js-clone-board(
class="fa-clone"
title="{{_ 'duplicate-board'}}")
| 📋
a.js-archive-board(
class="fa-archive"
title="{{_ 'archive-board'}}")
| 📦
template(name="boardListHeaderBar")
h1 {{_ title }}
+35 -26
View File
@@ -22,6 +22,9 @@ Template.boardListHeaderBar.events({
},
});
Template.boardList.events({
});
Template.boardListHeaderBar.helpers({
title() {
//if (FlowRouter.getRouteName() === 'template-container') {
@@ -239,34 +242,40 @@ BlazeComponent.extendComponent({
evt.preventDefault();
},
'click .js-clone-board'(evt) {
let title = getSlug(ReactiveCache.getBoard(this.currentData()._id).title) || 'cloned-board';
Meteor.call(
'copyBoard',
this.currentData()._id,
{
sort: ReactiveCache.getBoards({ archived: false }).length,
type: 'board',
title: ReactiveCache.getBoard(this.currentData()._id).title,
},
(err, res) => {
if (err) {
console.error(err);
} else {
Session.set('fromBoard', null);
subManager.subscribe('board', res, false);
FlowRouter.go('board', {
id: res,
slug: title,
});
}
},
);
evt.preventDefault();
if (confirm(TAPi18n.__('duplicate-board-confirm'))) {
let title =
getSlug(ReactiveCache.getBoard(this.currentData()._id).title) ||
'cloned-board';
Meteor.call(
'copyBoard',
this.currentData()._id,
{
sort: ReactiveCache.getBoards({ archived: false }).length,
type: 'board',
title: ReactiveCache.getBoard(this.currentData()._id).title,
},
(err, res) => {
if (err) {
console.error(err);
} else {
Session.set('fromBoard', null);
subManager.subscribe('board', res, false);
FlowRouter.go('board', {
id: res,
slug: title,
});
}
},
);
evt.preventDefault();
}
},
'click .js-archive-board'(evt) {
const boardId = this.currentData()._id;
Meteor.call('archiveBoard', boardId);
evt.preventDefault();
if (confirm(TAPi18n.__('archive-board-confirm'))) {
const boardId = this.currentData()._id;
Meteor.call('archiveBoard', boardId);
evt.preventDefault();
}
},
'click .js-accept-invite'() {
const boardId = this.currentData()._id;
@@ -0,0 +1,263 @@
/* Original Positions View Styles */
.original-positions-view {
margin: 10px 0;
padding: 15px;
background-color: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 6px;
}
.original-positions-header {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 15px;
}
.original-positions-header .btn {
display: flex;
align-items: center;
gap: 5px;
}
.original-positions-content {
background-color: white;
border: 1px solid #dee2e6;
border-radius: 4px;
padding: 15px;
}
.original-positions-loading {
text-align: center;
padding: 20px;
color: #6c757d;
font-style: italic;
}
.original-positions-loading i {
margin-right: 8px;
}
.original-positions-filters {
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 1px solid #dee2e6;
}
.original-positions-filters .btn-group {
display: flex;
flex-wrap: wrap;
gap: 5px;
}
.original-positions-filters .btn {
display: flex;
align-items: center;
gap: 5px;
white-space: nowrap;
}
.original-positions-list {
max-height: 400px;
overflow-y: auto;
}
.original-position-item {
background-color: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 4px;
margin-bottom: 10px;
padding: 12px;
transition: all 0.2s ease;
}
.original-position-item:hover {
background-color: #e9ecef;
border-color: #ced4da;
}
.original-position-item:last-child {
margin-bottom: 0;
}
.original-position-item-header {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 8px;
font-weight: 600;
color: #495057;
}
.original-position-item-header i {
color: #6c757d;
width: 16px;
text-align: center;
}
.entity-type {
background-color: #007bff;
color: white;
padding: 2px 6px;
border-radius: 3px;
font-size: 11px;
font-weight: 500;
text-transform: uppercase;
}
.entity-name {
color: #212529;
font-weight: 600;
}
.entity-id {
color: #6c757d;
font-size: 11px;
font-family: monospace;
}
.original-position-item-details {
margin-left: 24px;
}
.original-position-description {
color: #495057;
margin-bottom: 6px;
font-size: 13px;
}
.original-title {
color: #6c757d;
font-size: 12px;
margin-bottom: 6px;
padding: 4px 6px;
background-color: #e9ecef;
border-radius: 3px;
}
.original-title strong {
color: #495057;
}
.original-position-date {
color: #6c757d;
font-size: 11px;
}
.no-original-positions {
text-align: center;
padding: 40px 20px;
color: #6c757d;
font-style: italic;
}
.no-original-positions i {
font-size: 24px;
margin-bottom: 10px;
display: block;
color: #adb5bd;
}
/* Responsive adjustments */
@media (max-width: 768px) {
.original-positions-view {
margin: 5px 0;
padding: 10px;
}
.original-positions-header {
flex-direction: column;
align-items: stretch;
gap: 8px;
}
.original-positions-header .btn {
justify-content: center;
}
.original-positions-filters .btn-group {
justify-content: center;
}
.original-position-item-header {
flex-wrap: wrap;
gap: 6px;
}
.entity-name {
flex: 1;
min-width: 0;
word-break: break-word;
}
.original-position-item-details {
margin-left: 0;
margin-top: 8px;
}
}
/* Dark theme support */
@media (prefers-color-scheme: dark) {
.original-positions-view {
background-color: #2d3748;
border-color: #4a5568;
color: #e2e8f0;
}
.original-positions-content {
background-color: #1a202c;
border-color: #4a5568;
}
.original-position-item {
background-color: #2d3748;
border-color: #4a5568;
color: #e2e8f0;
}
.original-position-item:hover {
background-color: #4a5568;
border-color: #718096;
}
.original-position-item-header {
color: #e2e8f0;
}
.original-position-item-header i {
color: #a0aec0;
}
.entity-name {
color: #e2e8f0;
}
.entity-id {
color: #a0aec0;
}
.original-position-description {
color: #e2e8f0;
}
.original-title {
background-color: #4a5568;
color: #a0aec0;
}
.original-title strong {
color: #e2e8f0;
}
.original-position-date {
color: #a0aec0;
}
.no-original-positions {
color: #a0aec0;
}
.no-original-positions i {
color: #718096;
}
}
@@ -0,0 +1,82 @@
<template name="originalPositionsView">
<div class="original-positions-view">
<div class="original-positions-header">
<button class="btn btn-sm btn-outline-secondary" onclick="{{toggleOriginalPositions}}">
<i class="fa fa-history"></i>
{{#if isShowingOriginalPositions}}Hide{{else}}Show{{/if}} Original Positions
</button>
{{#if isShowingOriginalPositions}}
<button class="btn btn-sm btn-outline-primary" onclick="{{refreshHistory}}">
<i class="fa fa-refresh"></i> Refresh
</button>
{{/if}}
</div>
{{#if isShowingOriginalPositions}}
<div class="original-positions-content">
{{#if isLoading}}
<div class="original-positions-loading">
<i class="fa fa-spinner fa-spin"></i> Loading original positions...
</div>
{{else}}
<div class="original-positions-filters">
<div class="btn-group btn-group-sm" role="group">
<button type="button"
class="btn {{#if isFilterType 'all'}}btn-primary{{else}}btn-outline-secondary{{/if}}"
onclick="{{setFilterType 'all'}}">
All
</button>
<button type="button"
class="btn {{#if isFilterType 'swimlane'}}btn-primary{{else}}btn-outline-secondary{{/if}}"
onclick="{{setFilterType 'swimlane'}}">
<i class="fa fa-bars"></i> Swimlanes
</button>
<button type="button"
class="btn {{#if isFilterType 'list'}}btn-primary{{else}}btn-outline-secondary{{/if}}"
onclick="{{setFilterType 'list'}}">
<i class="fa fa-columns"></i> Lists
</button>
<button type="button"
class="btn {{#if isFilterType 'card'}}btn-primary{{else}}btn-outline-secondary{{/if}}"
onclick="{{setFilterType 'card'}}">
<i class="fa fa-sticky-note"></i> Cards
</button>
</div>
</div>
<div class="original-positions-list">
{{#each getFilteredHistory}}
<div class="original-position-item">
<div class="original-position-item-header">
<i class="fa {{getEntityTypeIcon entityType}}"></i>
<span class="entity-type">{{getEntityTypeLabel entityType}}</span>
<span class="entity-name">{{getEntityDisplayName this}}</span>
<span class="entity-id">({{entityId}})</span>
</div>
<div class="original-position-item-details">
<div class="original-position-description">
{{getEntityOriginalPositionDescription this}}
</div>
{{#if originalTitle}}
<div class="original-title">
<strong>Original title:</strong> {{originalTitle}}
</div>
{{/if}}
<div class="original-position-date">
<small class="text-muted">Created: {{formatDate createdAt}}</small>
</div>
</div>
</div>
{{else}}
<div class="no-original-positions">
<i class="fa fa-info-circle"></i>
No original position data available for this board.
</div>
{{/each}}
</div>
{{/if}}
</div>
{{/if}}
</div>
</template>
@@ -0,0 +1,148 @@
import { BlazeComponent } from 'meteor/peerlibrary:blaze-components';
import { ReactiveVar } from 'meteor/reactive-var';
import { Meteor } from 'meteor/meteor';
import { Template } from 'meteor/templating';
import './originalPositionsView.html';
/**
* Component to display original positions for all entities on a board
*/
class OriginalPositionsViewComponent extends BlazeComponent {
onCreated() {
super.onCreated();
this.showOriginalPositions = new ReactiveVar(false);
this.boardHistory = new ReactiveVar([]);
this.isLoading = new ReactiveVar(false);
this.filterType = new ReactiveVar('all'); // 'all', 'swimlane', 'list', 'card'
}
onRendered() {
super.onRendered();
this.loadBoardHistory();
}
loadBoardHistory() {
const boardId = Session.get('currentBoard');
if (!boardId) return;
this.isLoading.set(true);
Meteor.call('positionHistory.getBoardHistory', boardId, (error, result) => {
this.isLoading.set(false);
if (error) {
console.error('Error loading board history:', error);
this.boardHistory.set([]);
} else {
this.boardHistory.set(result);
}
});
}
toggleOriginalPositions() {
this.showOriginalPositions.set(!this.showOriginalPositions.get());
}
isShowingOriginalPositions() {
return this.showOriginalPositions.get();
}
isLoading() {
return this.isLoading.get();
}
getBoardHistory() {
return this.boardHistory.get();
}
getFilteredHistory() {
const history = this.getBoardHistory();
const filterType = this.filterType.get();
if (filterType === 'all') {
return history;
}
return history.filter(item => item.entityType === filterType);
}
getSwimlanesHistory() {
return this.getBoardHistory().filter(item => item.entityType === 'swimlane');
}
getListsHistory() {
return this.getBoardHistory().filter(item => item.entityType === 'list');
}
getCardsHistory() {
return this.getBoardHistory().filter(item => item.entityType === 'card');
}
setFilterType(type) {
this.filterType.set(type);
}
getFilterType() {
return this.filterType.get();
}
getEntityDisplayName(entity) {
const position = entity.originalPosition || {};
return position.title || `Entity ${entity.entityId}`;
}
getEntityOriginalPositionDescription(entity) {
const position = entity.originalPosition || {};
let description = `Position: ${position.sort || 0}`;
if (entity.entityType === 'list' && entity.originalSwimlaneId) {
description += ` in swimlane ${entity.originalSwimlaneId}`;
} else if (entity.entityType === 'card') {
if (entity.originalSwimlaneId) {
description += ` in swimlane ${entity.originalSwimlaneId}`;
}
if (entity.originalListId) {
description += ` in list ${entity.originalListId}`;
}
}
return description;
}
getEntityTypeIcon(entityType) {
switch (entityType) {
case 'swimlane':
return 'fa-bars';
case 'list':
return 'fa-columns';
case 'card':
return 'fa-sticky-note';
default:
return 'fa-question';
}
}
getEntityTypeLabel(entityType) {
switch (entityType) {
case 'swimlane':
return 'Swimlane';
case 'list':
return 'List';
case 'card':
return 'Card';
default:
return 'Unknown';
}
}
formatDate(date) {
return new Date(date).toLocaleString();
}
refreshHistory() {
this.loadBoardHistory();
}
}
OriginalPositionsViewComponent.register('originalPositionsView');
export default OriginalPositionsViewComponent;
+169
View File
@@ -106,6 +106,142 @@
color: white;
cursor: pointer;
font-size: 4em;
}
/* Upload progress indicators for drag-and-drop uploads */
.minicard-upload-progress,
.card-details-upload-progress {
background: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 4px;
padding: 12px;
margin: 8px 0;
font-size: 14px;
}
.upload-progress-header {
display: flex;
align-items: center;
margin-bottom: 8px;
font-weight: bold;
color: #495057;
}
.upload-progress-header i {
margin-right: 8px;
color: #007bff;
}
.upload-progress-item {
display: flex;
flex-direction: column;
margin-bottom: 8px;
padding: 8px;
background: white;
border-radius: 3px;
border: 1px solid #dee2e6;
}
.upload-progress-item.upload-error {
border-color: #dc3545;
background: #f8d7da;
}
.upload-progress-filename {
font-weight: 500;
margin-bottom: 4px;
color: #495057;
word-break: break-all;
}
.upload-progress-bar {
width: 100%;
height: 6px;
background: #e9ecef;
border-radius: 3px;
overflow: hidden;
margin-bottom: 4px;
}
.upload-progress-fill {
height: 100%;
background: linear-gradient(90deg, #007bff, #0056b3);
transition: width 0.3s ease;
border-radius: 3px;
}
.upload-progress-item.upload-error .upload-progress-fill {
background: #dc3545;
}
.upload-progress-error,
.upload-progress-success {
display: flex;
align-items: center;
font-size: 12px;
font-weight: 500;
}
.upload-progress-error {
color: #dc3545;
}
.upload-progress-success {
color: #28a745;
}
.upload-progress-error i,
.upload-progress-success i {
margin-right: 4px;
}
/* Minicard specific styles */
.minicard-upload-progress {
margin: 4px 0;
padding: 8px;
font-size: 12px;
}
.minicard-upload-progress .upload-progress-item {
padding: 6px;
margin-bottom: 6px;
}
.minicard-upload-progress .upload-progress-filename {
font-size: 11px;
}
/* Card details specific styles */
.card-details-upload-progress {
margin: 12px 0;
padding: 16px;
}
.card-details-upload-progress .upload-progress-header {
font-size: 16px;
margin-bottom: 12px;
}
.card-details-upload-progress .upload-progress-item {
padding: 12px;
margin-bottom: 10px;
}
.card-details-upload-progress .upload-progress-filename {
font-size: 14px;
}
/* Drag over state for minicards */
.minicard.is-dragging-over {
border: 2px dashed #007bff !important;
background: rgba(0, 123, 255, 0.1) !important;
}
/* Drag over state for card details */
.js-card-details.is-dragging-over {
border: 2px dashed #007bff !important;
background: rgba(0, 123, 255, 0.05) !important;
}
top: 0;
right: 8px;
position: absolute;
@@ -200,3 +336,36 @@
margin-top: 10px;
}
}
/* Attachment migration styles */
.attachment-item.migrating {
position: relative;
opacity: 0.7;
}
.attachment-migration-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.9);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 10;
border-radius: 4px;
}
.migration-spinner {
font-size: 24px;
color: #007cba;
margin-bottom: 8px;
}
.migration-text {
font-size: 12px;
color: #666;
text-align: center;
}
+25 -18
View File
@@ -34,10 +34,10 @@ template(name="attachmentViewer")
#viewer-overlay.hidden
#viewer-top-bar
span#attachment-name
a#viewer-close.fa.fa-times-thin
a#viewer-close ❌
#viewer-container
i.fa.fa-chevron-left.attachment-arrow#prev-attachment
| ◀️
#viewer-content
img#image-viewer.hidden
video#video-viewer.hidden(controls="true")
@@ -45,7 +45,7 @@ template(name="attachmentViewer")
object#pdf-viewer.hidden(type="application/pdf")
span.pdf-preview-error {{_ 'preview-pdf-not-supported' }}
object#txt-viewer.hidden(type="text/plain")
i.fa.fa-chevron-right.attachment-arrow#next-attachment
| ▶️
template(name="attachmentGallery")
@@ -53,11 +53,11 @@ template(name="attachmentGallery")
if canModifyCard
a.attachment-item.add-attachment.js-add-attachment
i.fa.fa-plus.icon
| ➕
each attachments
.attachment-item
.attachment-item(class="{{#if isAttachmentMigrating _id}}migrating{{/if}}")
.attachment-thumbnail-container.open-preview(data-attachment-id="{{_id}}" data-card-id="{{ meta.cardId }}")
if link
if(isImage)
@@ -86,25 +86,32 @@ template(name="attachmentGallery")
= name
span.file-size ({{fileSize size}})
.attachment-actions
a.js-download(href="{{link}}?download=true", download="{{name}}")
i.fa.fa-download.icon(title="{{_ 'download'}}")
a.js-download(href="{{link}}?download=true", download="{{name}}", title="{{_ 'download'}}")
| ⬇️
if currentUser.isBoardMember
unless currentUser.isCommentOnly
unless currentUser.isWorker
a.js-rename
i.fa.fa-pencil-square-o.icon(title="{{_ 'rename'}}")
a.js-confirm-delete
i.fa.fa-trash.icon(title="{{_ 'delete'}}")
a.fa.fa-navicon.icon.js-open-attachment-menu(data-attachment-link="{{link}}" title="{{_ 'attachmentActionsPopup-title'}}")
a.js-rename(title="{{_ 'rename'}}")
| ✏️
a.js-confirm-delete(title="{{_ 'delete'}}")
| 🗑️
a.js-open-attachment-menu(data-attachment-link="{{link}}", title="{{_ 'attachmentActionsPopup-title'}}")
| ☰
// Migration spinner overlay
if isAttachmentMigrating _id
.attachment-migration-overlay
.migration-spinner
| ⚙️
.migration-text {{_ 'migrating-attachment'}}
template(name="attachmentActionsPopup")
ul.pop-over-list
li
if isImage
a(class="{{#if isCover}}js-remove-cover{{else}}js-add-cover{{/if}}")
i.fa.fa-book
i.fa.fa-picture-o
| 📖
| 🖼️
if isCover
| {{_ 'remove-cover'}}
else
@@ -112,7 +119,7 @@ template(name="attachmentActionsPopup")
if currentUser.isBoardAdmin
if isImage
a(class="{{#if isBackgroundImage}}js-remove-background-image{{else}}js-add-background-image{{/if}}")
i.fa.fa-picture-o
| 🖼️
if isBackgroundImage
| {{_ 'remove-background-image'}}
else
@@ -120,19 +127,19 @@ template(name="attachmentActionsPopup")
if $neq versions.original.storage "fs"
a.js-move-storage-fs
i.fa.fa-arrow-right
| ▶️
| {{_ 'attachment-move-storage-fs'}}
if $neq versions.original.storage "gridfs"
if versions.original.storage
a.js-move-storage-gridfs
i.fa.fa-arrow-right
| ▶️
| {{_ 'attachment-move-storage-gridfs'}}
if $neq versions.original.storage "s3"
if versions.original.storage
a.js-move-storage-s3
i.fa.fa-arrow-right
| ▶️
| {{_ 'attachment-move-storage-s3'}}
template(name="attachmentRenamePopup")
+124 -37
View File
@@ -1,6 +1,9 @@
import { ReactiveCache } from '/imports/reactiveCache';
import { ObjectID } from 'bson';
import DOMPurify from 'dompurify';
import { sanitizeHTML, sanitizeText } from '/imports/lib/secureDOMPurify';
import uploadProgressManager from '../../lib/uploadProgressManager';
import { attachmentMigrationManager } from '/client/lib/attachmentMigrationManager';
const filesize = require('filesize');
const prettyMilliseconds = require('pretty-ms');
@@ -268,7 +271,7 @@ Template.attachmentGallery.helpers({
return ret;
},
sanitize(value) {
return DOMPurify.sanitize(value);
return sanitizeHTML(value);
},
});
@@ -296,44 +299,13 @@ Template.cardAttachmentsPopup.events({
const files = event.currentTarget.files;
if (files) {
let uploads = [];
for (const file of files) {
const fileId = new ObjectID().toString();
let fileName = DOMPurify.sanitize(file.name);
const uploaders = handleFileUpload(card, files);
// If sanitized filename is not same as original filename,
// it could be XSS that is already fixed with sanitize,
// or just normal mistake, so it is not a problem.
// That is why here is no warning.
if (fileName !== file.name) {
// If filename is empty, only in that case add some filename
if (fileName.length === 0) {
fileName = 'Empty-filename-after-sanitize.txt';
}
}
const config = {
file: file,
fileId: fileId,
fileName: fileName,
meta: Utils.getCommonAttachmentMetaFrom(card),
chunkSize: 'dynamic',
};
config.meta.fileId = fileId;
const uploader = Attachments.insert(
config,
false,
);
uploaders.forEach(uploader => {
uploader.on('start', function() {
uploads.push(this);
templateInstance.uploads.set(uploads);
});
uploader.on('uploaded', (error, fileRef) => {
if (!error) {
if (fileRef.isImage) {
card.setCover(fileRef._id);
}
}
});
uploader.on('end', (error, fileRef) => {
uploads = uploads.filter(_upload => _upload.config.fileId != fileRef._id);
templateInstance.uploads.set(uploads);
@@ -341,8 +313,7 @@ Template.cardAttachmentsPopup.events({
Popup.back();
}
});
uploader.start();
}
});
}
},
'click .js-computer-upload'(event, templateInstance) {
@@ -356,6 +327,105 @@ const MAX_IMAGE_PIXEL = Utils.MAX_IMAGE_PIXEL;
const COMPRESS_RATIO = Utils.IMAGE_COMPRESS_RATIO;
let pastedResults = null;
// Shared upload logic for drag-and-drop functionality
export function handleFileUpload(card, files) {
if (!files || files.length === 0) {
return [];
}
// Check if board allows attachments
const board = card.board();
if (!board || !board.allowsAttachments) {
if (process.env.DEBUG === 'true') {
console.warn('Attachments not allowed on this board');
}
return [];
}
// Check if user can modify the card
if (!Utils.canModifyCard()) {
if (process.env.DEBUG === 'true') {
console.warn('User does not have permission to modify this card');
}
return [];
}
const uploads = [];
for (const file of files) {
// Basic file validation
if (!file || !file.name) {
if (process.env.DEBUG === 'true') {
console.warn('Invalid file object');
}
continue;
}
const fileId = new ObjectID().toString();
let fileName = sanitizeText(file.name);
// If sanitized filename is not same as original filename,
// it could be XSS that is already fixed with sanitize,
// or just normal mistake, so it is not a problem.
// That is why here is no warning.
if (fileName !== file.name) {
// If filename is empty, only in that case add some filename
if (fileName.length === 0) {
fileName = 'Empty-filename-after-sanitize.txt';
}
}
const config = {
file: file,
fileId: fileId,
fileName: fileName,
meta: Utils.getCommonAttachmentMetaFrom(card),
chunkSize: 'dynamic',
};
config.meta.fileId = fileId;
try {
const uploader = Attachments.insert(
config,
false,
);
// Add to progress manager for tracking
const uploadId = uploadProgressManager.addUpload(card._id, uploader, file);
uploader.on('uploaded', (error, fileRef) => {
if (!error) {
if (fileRef.isImage) {
card.setCover(fileRef._id);
if (process.env.DEBUG === 'true') {
console.log(`Set cover image for card ${card._id}: ${fileRef.name}`);
}
}
} else {
if (process.env.DEBUG === 'true') {
console.error('Upload error:', error);
}
}
});
uploader.on('error', (error) => {
if (process.env.DEBUG === 'true') {
console.error('Upload error:', error);
}
});
uploads.push(uploader);
uploader.start();
} catch (error) {
if (process.env.DEBUG === 'true') {
console.error('Failed to create uploader:', error);
}
}
}
return uploads;
}
Template.previewClipboardImagePopup.onRendered(() => {
// we can paste image from clipboard
const handle = results => {
@@ -498,7 +568,7 @@ BlazeComponent.extendComponent({
const name = this.$('.js-edit-attachment-name')[0]
.value
.trim() + this.data().extensionWithDot;
if (name === DOMPurify.sanitize(name)) {
if (name === sanitizeText(name)) {
Meteor.call('renameAttachment', this.data()._id, name);
}
Popup.back();
@@ -507,3 +577,20 @@ BlazeComponent.extendComponent({
]
}
}).register('attachmentRenamePopup');
// Template helpers for attachment migration status
Template.registerHelper('attachmentMigrationStatus', function(attachmentId) {
return attachmentMigrationManager.getAttachmentMigrationStatus(attachmentId);
});
Template.registerHelper('isAttachmentMigrating', function(attachmentId) {
return attachmentMigrationManager.isAttachmentBeingMigrated(attachmentId);
});
Template.registerHelper('attachmentMigrationProgress', function() {
return attachmentMigrationManager.attachmentMigrationProgress.get();
});
Template.registerHelper('attachmentMigrationStatusText', function() {
return attachmentMigrationManager.attachmentMigrationStatus.get();
});
+14 -12
View File
@@ -6,10 +6,10 @@ template(name="cardCustomFieldsPopup")
span.full-name
= name
if hasCustomField
i.fa.fa-check
| ✅
hr
a.quiet-button.full.js-settings
i.fa.fa-cog
| ⚙️
span {{_ 'settings'}}
template(name="cardCustomField")
@@ -22,7 +22,7 @@ template(name="cardCustomField-text")
= value
.edit-controls.clearfix
button.primary(type="submit") {{_ 'save'}}
a.fa.fa-times-thin.js-close-inlined-form
a.js-close-inlined-form
else
a.js-open-inlined-form
if value
@@ -41,7 +41,7 @@ template(name="cardCustomField-number")
input(type="number" value=data.value)
.edit-controls.clearfix
button.primary(type="submit") {{_ 'save'}}
a.fa.fa-times-thin.js-close-inlined-form
a.js-close-inlined-form
else
a.js-open-inlined-form
if value
@@ -66,7 +66,7 @@ template(name="cardCustomField-currency")
input(type="text" value=data.value autofocus)
.edit-controls.clearfix
button.primary(type="submit") {{_ 'save'}}
a.fa.fa-times-thin.js-close-inlined-form
a.js-close-inlined-form
else
a.js-open-inlined-form
if value
@@ -79,13 +79,14 @@ template(name="cardCustomField-currency")
template(name="cardCustomField-date")
if canModifyCard
a.js-edit-date(title="{{showTitle}} {{_ 'predicate-week'}} {{showWeek}}" class="{{classes}}")
a.js-edit-date(title="{{showTitle}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}")
if value
div.card-date
time(datetime="{{showISODate}}")
| {{showDate}}
b
| {{showWeek}}
if showWeekOfYear
b
| {{showWeek}}
else
| {{_ 'edit'}}
else
@@ -93,8 +94,9 @@ template(name="cardCustomField-date")
div.card-date
time(datetime="{{showISODate}}")
| {{showDate}}
b
| {{showWeek}}
if showWeekOfYear
b
| {{showWeek}}
template(name="cardCustomField-dropdown")
if canModifyCard
@@ -111,7 +113,7 @@ template(name="cardCustomField-dropdown")
= name
.edit-controls.clearfix
button.primary(type="submit") {{_ 'save'}}
a.fa.fa-times-thin.js-close-inlined-form
a.js-close-inlined-form
else
a.js-open-inlined-form
if value
@@ -132,7 +134,7 @@ template(name="cardCustomField-stringtemplate")
input.js-card-customfield-stringtemplate-item.last(type="text" value="" placeholder="{{_ 'custom-field-stringtemplate-item-placeholder'}}" autofocus)
.edit-controls.clearfix
button.primary(type="submit") {{_ 'save'}}
a.fa.fa-times-thin.js-close-inlined-form
a.js-close-inlined-form
else
a.js-open-inlined-form
if value
+42 -15
View File
@@ -1,6 +1,27 @@
import moment from 'moment/min/moment-with-locales';
import { TAPi18n } from '/imports/i18n';
import { DatePicker } from '/client/lib/datepicker';
import { ReactiveCache } from '/imports/reactiveCache';
import {
formatDateTime,
formatDate,
formatDateByUserPreference,
formatTime,
getISOWeek,
isValidDate,
isBefore,
isAfter,
isSame,
add,
subtract,
startOf,
endOf,
format,
parseDate,
now,
createDate,
fromNow,
calendar
} from '/imports/lib/dateUtils';
import Cards from '/models/cards';
import { CustomFieldStringTemplate } from '/client/lib/customFields'
@@ -134,27 +155,33 @@ CardCustomField.register('cardCustomField');
super.onCreated();
const self = this;
self.date = ReactiveVar();
self.now = ReactiveVar(moment());
self.now = ReactiveVar(now());
window.setInterval(() => {
self.now.set(moment());
self.now.set(now());
}, 60000);
self.autorun(() => {
self.date.set(moment(self.data().value));
self.date.set(new Date(self.data().value));
});
}
showWeek() {
return this.date.get().week().toString();
return getISOWeek(this.date.get()).toString();
}
showWeekOfYear() {
const user = ReactiveCache.getCurrentUser();
if (!user) {
// For non-logged-in users, week of year is not shown
return false;
}
return user.isShowWeekOfYear();
}
showDate() {
// this will start working once mquandalle:moment
// is updated to at least moment.js 2.10.5
// until then, the date is displayed in the "L" format
return this.date.get().calendar(null, {
sameElse: 'llll',
});
const currentUser = ReactiveCache.getCurrentUser();
const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
return formatDateByUserPreference(this.date.get(), dateFormat, true);
}
showISODate() {
@@ -163,8 +190,8 @@ CardCustomField.register('cardCustomField');
classes() {
if (
this.date.get().isBefore(this.now.get(), 'minute') &&
this.now.get().isBefore(this.data().value)
isBefore(this.date.get(), this.now.get(), 'minute') &&
isBefore(this.now.get(), this.data().value, 'minute')
) {
return 'current';
}
@@ -172,7 +199,7 @@ CardCustomField.register('cardCustomField');
}
showTitle() {
return `${TAPi18n.__('card-start-on')} ${this.date.get().format('LLLL')}`;
return `${TAPi18n.__('card-start-on')} ${this.date.get().toLocaleString()}`;
}
events() {
@@ -191,7 +218,7 @@ CardCustomField.register('cardCustomField');
const self = this;
self.card = Utils.getCurrentCard();
self.customFieldId = this.data()._id;
this.data().value && this.date.set(moment(this.data().value));
this.data().value && this.date.set(new Date(this.data().value));
}
_storeDate(date) {
+50 -6
View File
@@ -42,23 +42,67 @@
.card-date.long-overdue.is-active {
background-color: #fd3e24;
}
/* Date type specific colors */
.card-date.received-date {
background-color: #dbdbdb; /* Light grey for received */
}
.card-date.received-date:hover,
.card-date.received-date.is-active {
background-color: #b3b3b3;
}
.card-date.start-date {
background-color: #90ee90; /* Light green for start */
color: #000; /* Black text for start */
}
.card-date.start-date:hover,
.card-date.start-date.is-active {
background-color: #7dd87d;
}
.card-date.due-date {
background-color: #ffd700; /* Yellow for due */
color: #000; /* Black text for due */
}
.card-date.due-date:hover,
.card-date.due-date.is-active {
background-color: #e6c200;
}
.card-date.end-date {
background-color: #ffb3b3; /* Light red for end */
color: #000; /* Black text for end */
}
.card-date.end-date:hover,
.card-date.end-date.is-active {
background-color: #ff9999;
}
.card-date.end-date time::before {
content: "\f253";
content: "🏁"; /* Finish flag - represents end/completion */
}
.card-date.due-date time::before {
content: "\f090";
content: "⏰"; /* Alarm clock - represents due/deadline */
}
.card-date.start-date time::before {
content: "\f251";
content: "🚀"; /* Rocket - represents start/launch */
}
.card-date.received-date time::before {
content: "\f08b";
content: "📥"; /* Inbox tray - represents received/incoming */
}
/* Generic date badge and custom field date */
.card-date:not(.received-date):not(.start-date):not(.due-date):not(.end-date) time::before {
/*content: "📅"; // Calendar - represents generic date */
}
.card-date time::before {
font: normal normal normal 14px/1 FontAwesome;
font-size: inherit;
-webkit-font-smoothing: antialiased;
margin-right: 0.3em;
display: inline-block;
}
.customfield-date {
display: block;
+119 -8
View File
@@ -1,20 +1,131 @@
template(name="dateBadge")
if canModifyCard
a.js-edit-date.card-date(title="{{showTitle}} {{_ 'predicate-week'}} {{showWeek}}" class="{{classes}}")
a.js-edit-date.card-date(title="{{showTitle}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}")
time(datetime="{{showISODate}}")
| {{showDate}}
b
| {{showWeek}}
if showWeekOfYear
b
| {{showWeek}}
else
a.card-date(title="{{showTitle}} {{_ 'predicate-week'}} {{showWeek}}" class="{{classes}}")
a.card-date(title="{{showTitle}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}")
time(datetime="{{showISODate}}")
| {{showDate}}
if showWeekOfYear
b
| {{showWeek}}
template(name="dateCustomField")
a(title="{{showTitle}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}")
time(datetime="{{showISODate}}")
| {{showDate}}
if showWeekOfYear
b
| {{showWeek}}
template(name="dateCustomField")
a(title="{{showTitle}} {{_ 'predicate-week'}} {{showWeek}}" class="{{classes}}")
template(name="minicardReceivedDate")
if canModifyCard
a.js-edit-date.card-date.received-date(title="{{_ 'card-received'}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}")
time(datetime="{{showISODate}}")
| {{showDate}}
if showWeekOfYear
b
| {{showWeek}}
else
a.card-date.received-date(title="{{_ 'card-received'}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}")
time(datetime="{{showISODate}}")
| {{showDate}}
if showWeekOfYear
b
| {{showWeek}}
template(name="minicardStartDate")
if canModifyCard
a.js-edit-date.card-date.start-date(title="{{_ 'card-start'}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}")
time(datetime="{{showISODate}}")
| {{showDate}}
if showWeekOfYear
b
| {{showWeek}}
else
a.card-date.start-date(title="{{_ 'card-start'}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}")
time(datetime="{{showISODate}}")
| {{showDate}}
if showWeekOfYear
b
| {{showWeek}}
template(name="minicardDueDate")
if canModifyCard
a.js-edit-date.card-date.due-date(title="{{_ 'card-due'}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}")
time(datetime="{{showISODate}}")
| {{showDate}}
if showWeekOfYear
b
| {{showWeek}}
else
a.card-date.due-date(title="{{_ 'card-due'}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}")
time(datetime="{{showISODate}}")
| {{showDate}}
if showWeekOfYear
b
| {{showWeek}}
template(name="minicardEndDate")
if canModifyCard
a.js-edit-date.card-date.end-date(title="{{_ 'card-end'}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}")
time(datetime="{{showISODate}}")
| {{showDate}}
if showWeekOfYear
b
| {{showWeek}}
else
a.card-date.end-date(title="{{_ 'card-end'}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}")
time(datetime="{{showISODate}}")
| {{showDate}}
if showWeekOfYear
b
| {{showWeek}}
template(name="minicardCustomFieldDate")
a(title="{{_ 'date'}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}")
time(datetime="{{showISODate}}")
| {{showDate}}
b
| {{showWeek}}
if showWeekOfYear
b
| {{showWeek}}
template(name="editCardReceivedDatePopup")
form.edit-card-received-date
.datepicker
.clear-date
a.js-clear-date {{_ 'clear'}}
.datepicker-actions
button.primary.wide.left(type="submit") {{_ 'save'}}
button.js-delete-date.negate.wide.right {{_ 'delete'}}
template(name="editCardStartDatePopup")
form.edit-card-start-date
.datepicker
.clear-date
a.js-clear-date {{_ 'clear'}}
.datepicker-actions
button.primary.wide.left(type="submit") {{_ 'save'}}
button.js-delete-date.negate.wide.right {{_ 'delete'}}
template(name="editCardDueDatePopup")
form.edit-card-due-date
.datepicker
.clear-date
a.js-clear-date {{_ 'clear'}}
.datepicker-actions
button.primary.wide.left(type="submit") {{_ 'save'}}
button.js-delete-date.negate.wide.right {{_ 'delete'}}
template(name="editCardEndDatePopup")
form.edit-card-end-date
.datepicker
.clear-date
a.js-clear-date {{_ 'clear'}}
.datepicker-actions
button.primary.wide.left(type="submit") {{_ 'save'}}
button.js-delete-date.negate.wide.right {{_ 'delete'}}
+139 -68
View File
@@ -1,17 +1,38 @@
import moment from 'moment/min/moment-with-locales';
import { TAPi18n } from '/imports/i18n';
import { DatePicker } from '/client/lib/datepicker';
import {
formatDateTime,
formatDate,
formatDateByUserPreference,
formatTime,
getISOWeek,
isValidDate,
isBefore,
isAfter,
isSame,
add,
subtract,
startOf,
endOf,
format,
parseDate,
now,
createDate,
fromNow,
calendar,
diff
} from '/imports/lib/dateUtils';
// editCardReceivedDatePopup
(class extends DatePicker {
onCreated() {
super.onCreated(moment().format('YYYY-MM-DD HH:mm'));
super.onCreated(formatDateTime(now()));
this.data().getReceived() &&
this.date.set(moment(this.data().getReceived()));
this.date.set(new Date(this.data().getReceived()));
}
_storeDate(date) {
this.card.setReceived(date);
this.card.setReceived(formatDateTime(date));
}
_deleteDate() {
@@ -22,22 +43,17 @@ import { DatePicker } from '/client/lib/datepicker';
// editCardStartDatePopup
(class extends DatePicker {
onCreated() {
super.onCreated(moment().format('YYYY-MM-DD HH:mm'));
this.data().getStart() && this.date.set(moment(this.data().getStart()));
super.onCreated(formatDateTime(now()));
this.data().getStart() && this.date.set(new Date(this.data().getStart()));
}
onRendered() {
super.onRendered();
if (moment.isDate(this.card.getReceived())) {
this.$('.js-datepicker').datepicker(
'setStartDate',
this.card.getReceived(),
);
}
// DatePicker base class handles initialization with native HTML inputs
}
_storeDate(date) {
this.card.setStart(date);
this.card.setStart(formatDateTime(date));
}
_deleteDate() {
@@ -49,18 +65,16 @@ import { DatePicker } from '/client/lib/datepicker';
(class extends DatePicker {
onCreated() {
super.onCreated('1970-01-01 17:00:00');
this.data().getDue() && this.date.set(moment(this.data().getDue()));
this.data().getDue() && this.date.set(new Date(this.data().getDue()));
}
onRendered() {
super.onRendered();
if (moment.isDate(this.card.getStart())) {
this.$('.js-datepicker').datepicker('setStartDate', this.card.getStart());
}
// DatePicker base class handles initialization with native HTML inputs
}
_storeDate(date) {
this.card.setDue(date);
this.card.setDue(formatDateTime(date));
}
_deleteDate() {
@@ -71,19 +85,17 @@ import { DatePicker } from '/client/lib/datepicker';
// editCardEndDatePopup
(class extends DatePicker {
onCreated() {
super.onCreated(moment().format('YYYY-MM-DD HH:mm'));
this.data().getEnd() && this.date.set(moment(this.data().getEnd()));
super.onCreated(formatDateTime(now()));
this.data().getEnd() && this.date.set(new Date(this.data().getEnd()));
}
onRendered() {
super.onRendered();
if (moment.isDate(this.card.getStart())) {
this.$('.js-datepicker').datepicker('setStartDate', this.card.getStart());
}
// DatePicker base class handles initialization with native HTML inputs
}
_storeDate(date) {
this.card.setEnd(date);
this.card.setEnd(formatDateTime(date));
}
_deleteDate() {
@@ -100,23 +112,29 @@ const CardDate = BlazeComponent.extendComponent({
onCreated() {
const self = this;
self.date = ReactiveVar();
self.now = ReactiveVar(moment());
self.now = ReactiveVar(now());
window.setInterval(() => {
self.now.set(moment());
self.now.set(now());
}, 60000);
},
showWeek() {
return this.date.get().week().toString();
return getISOWeek(this.date.get()).toString();
},
showWeekOfYear() {
const user = ReactiveCache.getCurrentUser();
if (!user) {
// For non-logged-in users, week of year is not shown
return false;
}
return user.isShowWeekOfYear();
},
showDate() {
// this will start working once mquandalle:moment
// is updated to at least moment.js 2.10.5
// until then, the date is displayed in the "L" format
return this.date.get().calendar(null, {
sameElse: 'llll',
});
const currentUser = ReactiveCache.getCurrentUser();
const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
return formatDateByUserPreference(this.date.get(), dateFormat, true);
},
showISODate() {
@@ -129,7 +147,7 @@ class CardReceivedDate extends CardDate {
super.onCreated();
const self = this;
self.autorun(() => {
self.date.set(moment(self.data().getReceived()));
self.date.set(new Date(self.data().getReceived()));
});
}
@@ -141,9 +159,9 @@ class CardReceivedDate extends CardDate {
const theDate = this.date.get();
// if dueAt, endAt and startAt exist & are > receivedAt, receivedAt doesn't need to be flagged
if (
(startAt && theDate.isAfter(startAt)) ||
(endAt && theDate.isAfter(endAt)) ||
(dueAt && theDate.isAfter(dueAt))
(startAt && isAfter(theDate, startAt)) ||
(endAt && isAfter(theDate, endAt)) ||
(dueAt && isAfter(theDate, dueAt))
)
classes += 'long-overdue';
else classes += 'current';
@@ -151,9 +169,10 @@ class CardReceivedDate extends CardDate {
}
showTitle() {
return `${TAPi18n.__('card-received-on')} ${this.date
.get()
.format('LLLL')}`;
const currentUser = ReactiveCache.getCurrentUser();
const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
const formattedDate = formatDateByUserPreference(this.date.get(), dateFormat, true);
return `${TAPi18n.__('card-received-on')} ${formattedDate}`;
}
events() {
@@ -169,7 +188,7 @@ class CardStartDate extends CardDate {
super.onCreated();
const self = this;
self.autorun(() => {
self.date.set(moment(self.data().getStart()));
self.date.set(new Date(self.data().getStart()));
});
}
@@ -180,15 +199,18 @@ class CardStartDate extends CardDate {
const theDate = this.date.get();
const now = this.now.get();
// if dueAt or endAt exist & are > startAt, startAt doesn't need to be flagged
if ((endAt && theDate.isAfter(endAt)) || (dueAt && theDate.isAfter(dueAt)))
if ((endAt && isAfter(theDate, endAt)) || (dueAt && isAfter(theDate, dueAt)))
classes += 'long-overdue';
else if (theDate.isAfter(now)) classes += '';
else if (isAfter(theDate, now)) classes += '';
else classes += 'current';
return classes;
}
showTitle() {
return `${TAPi18n.__('card-start-on')} ${this.date.get().format('LLLL')}`;
const currentUser = ReactiveCache.getCurrentUser();
const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
const formattedDate = formatDateByUserPreference(this.date.get(), dateFormat, true);
return `${TAPi18n.__('card-start-on')} ${formattedDate}`;
}
events() {
@@ -204,7 +226,7 @@ class CardDueDate extends CardDate {
super.onCreated();
const self = this;
self.autorun(() => {
self.date.set(moment(self.data().getDue()));
self.date.set(new Date(self.data().getDue()));
});
}
@@ -214,17 +236,20 @@ class CardDueDate extends CardDate {
const theDate = this.date.get();
const now = this.now.get();
// if the due date is after the end date, green - done early
if (endAt && theDate.isAfter(endAt)) classes += 'current';
if (endAt && isAfter(theDate, endAt)) classes += 'current';
// if there is an end date, don't need to flag the due date
else if (endAt) classes += '';
else if (now.diff(theDate, 'days') >= 2) classes += 'long-overdue';
else if (now.diff(theDate, 'minute') >= 0) classes += 'due';
else if (now.diff(theDate, 'days') >= -1) classes += 'almost-due';
else if (diff(now, theDate, 'days') >= 2) classes += 'long-overdue';
else if (diff(now, theDate, 'minute') >= 0) classes += 'due';
else if (diff(now, theDate, 'days') >= -1) classes += 'almost-due';
return classes;
}
showTitle() {
return `${TAPi18n.__('card-due-on')} ${this.date.get().format('LLLL')}`;
const currentUser = ReactiveCache.getCurrentUser();
const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
const formattedDate = formatDateByUserPreference(this.date.get(), dateFormat, true);
return `${TAPi18n.__('card-due-on')} ${formattedDate}`;
}
events() {
@@ -240,7 +265,7 @@ class CardEndDate extends CardDate {
super.onCreated();
const self = this;
self.autorun(() => {
self.date.set(moment(self.data().getEnd()));
self.date.set(new Date(self.data().getEnd()));
});
}
@@ -249,13 +274,13 @@ class CardEndDate extends CardDate {
const dueAt = this.data().getDue();
const theDate = this.date.get();
if (!dueAt) classes += '';
else if (theDate.isBefore(dueAt)) classes += 'current';
else if (theDate.isAfter(dueAt)) classes += 'due';
else if (isBefore(theDate, dueAt)) classes += 'current';
else if (isAfter(theDate, dueAt)) classes += 'due';
return classes;
}
showTitle() {
return `${TAPi18n.__('card-end-on')} ${this.date.get().format('LLLL')}`;
return `${TAPi18n.__('card-end-on')} ${format(this.date.get(), 'LLLL')}`;
}
events() {
@@ -275,12 +300,21 @@ class CardCustomFieldDate extends CardDate {
super.onCreated();
const self = this;
self.autorun(() => {
self.date.set(moment(self.data().value));
self.date.set(new Date(self.data().value));
});
}
showWeek() {
return this.date.get().week().toString();
return getISOWeek(this.date.get()).toString();
}
showWeekOfYear() {
const user = ReactiveCache.getCurrentUser();
if (!user) {
// For non-logged-in users, week of year is not shown
return false;
}
return user.isShowWeekOfYear();
}
showDate() {
@@ -293,7 +327,10 @@ class CardCustomFieldDate extends CardDate {
}
showTitle() {
return `${this.date.get().format('LLLL')}`;
const currentUser = ReactiveCache.getCurrentUser();
const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
const formattedDate = formatDateByUserPreference(this.date.get(), dateFormat, true);
return `${formattedDate}`;
}
classes() {
@@ -307,32 +344,62 @@ class CardCustomFieldDate extends CardDate {
CardCustomFieldDate.register('cardCustomFieldDate');
(class extends CardReceivedDate {
template() {
return 'minicardReceivedDate';
}
showDate() {
return this.date.get().format('L');
const currentUser = ReactiveCache.getCurrentUser();
const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
return formatDateByUserPreference(this.date.get(), dateFormat, true);
}
}.register('minicardReceivedDate'));
(class extends CardStartDate {
template() {
return 'minicardStartDate';
}
showDate() {
return this.date.get().format('L');
const currentUser = ReactiveCache.getCurrentUser();
const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
return formatDateByUserPreference(this.date.get(), dateFormat, true);
}
}.register('minicardStartDate'));
(class extends CardDueDate {
template() {
return 'minicardDueDate';
}
showDate() {
return this.date.get().format('L');
const currentUser = ReactiveCache.getCurrentUser();
const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
return formatDateByUserPreference(this.date.get(), dateFormat, true);
}
}.register('minicardDueDate'));
(class extends CardEndDate {
template() {
return 'minicardEndDate';
}
showDate() {
return this.date.get().format('L');
const currentUser = ReactiveCache.getCurrentUser();
const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
return formatDateByUserPreference(this.date.get(), dateFormat, true);
}
}.register('minicardEndDate'));
(class extends CardCustomFieldDate {
template() {
return 'minicardCustomFieldDate';
}
showDate() {
return this.date.get().format('L');
const currentUser = ReactiveCache.getCurrentUser();
const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
return formatDateByUserPreference(this.date.get(), dateFormat, true);
}
}.register('minicardCustomFieldDate'));
@@ -341,7 +408,7 @@ class VoteEndDate extends CardDate {
super.onCreated();
const self = this;
self.autorun(() => {
self.date.set(moment(self.data().getVoteEnd()));
self.date.set(new Date(self.data().getVoteEnd()));
});
}
classes() {
@@ -349,10 +416,12 @@ class VoteEndDate extends CardDate {
return classes;
}
showDate() {
return this.date.get().format('L LT');
const currentUser = ReactiveCache.getCurrentUser();
const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
return formatDateByUserPreference(this.date.get(), dateFormat, true);
}
showTitle() {
return `${TAPi18n.__('card-end-on')} ${this.date.get().format('LLLL')}`;
return `${TAPi18n.__('card-end-on')} ${this.date.get().toLocaleString()}`;
}
events() {
@@ -368,7 +437,7 @@ class PokerEndDate extends CardDate {
super.onCreated();
const self = this;
self.autorun(() => {
self.date.set(moment(self.data().getPokerEnd()));
self.date.set(new Date(self.data().getPokerEnd()));
});
}
classes() {
@@ -376,10 +445,12 @@ class PokerEndDate extends CardDate {
return classes;
}
showDate() {
return this.date.get().format('l LT');
const currentUser = ReactiveCache.getCurrentUser();
const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
return formatDateByUserPreference(this.date.get(), dateFormat, true);
}
showTitle() {
return `${TAPi18n.__('card-end-on')} ${this.date.get().format('LLLL')}`;
return `${TAPi18n.__('card-end-on')} ${format(this.date.get(), 'LLLL')}`;
}
events() {
+42 -6
View File
@@ -1,3 +1,31 @@
/* Date Format Selector */
.card-details-item-date-format {
margin-bottom: 10px;
}
.card-details-item-date-format .card-details-item-title {
font-size: 14px;
font-weight: bold;
margin-bottom: 5px;
color: #333;
}
.card-details-item-date-format .js-date-format-selector {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
background-color: #fff;
font-size: 14px;
cursor: pointer;
}
.card-details-item-date-format .js-date-format-selector:focus {
outline: none;
border-color: #007cba;
box-shadow: 0 0 0 2px rgba(0, 124, 186, 0.2);
}
.assignee {
border-radius: 3px;
display: block;
@@ -70,23 +98,23 @@
}
.copied-tooltip {
display: none;
padding: 0px 10px;
padding: 0 1.3vw;
background-color: rgba(0,0,0,0.875);
color: #fff;
border-radius: 5px;
border-radius: 0.7vw;
}
.card-details {
padding: 0;
flex-shrink: 0;
flex-basis: 600px;
flex-basis: min(600px, 80vw);
will-change: flex-basis;
overflow-y: scroll;
overflow-x: hidden;
background: #f7f7f7;
border-radius: bottom 3px;
border-radius: bottom 0.4vw;
z-index: 30;
animation: flexGrowIn 0.1s;
box-shadow: 0 0 7px 0 #b3b3b3;
box-shadow: 0 0 0.9vh 0 #b3b3b3;
transition: flex-basis 0.1s;
box-sizing: border-box;
}
@@ -95,7 +123,7 @@
}
.card-details .card-details-canvas {
width: auto;
padding: 0 20px;
padding: 0 2.5vw;
}
.card-details .card-details-header {
margin: 0 -20px 5px;
@@ -596,3 +624,11 @@ input[type="submit"].attachment-add-link-submit {
overflow: hidden;
background-color: #cecece;
}
/* Drag and drop file upload visual feedback */
.js-card-details.is-dragging-over {
border: 2px dashed #0079bf;
background-color: #e3f2fd !important;
transform: scale(1.01);
transition: all 0.2s ease;
}
+129 -84
View File
@@ -12,15 +12,19 @@ template(name="cardDetails")
else
unless isMiniScreen
unless isPopup
a.fa.fa-times-thin.close-card-details.js-close-card-details(title="{{_ 'close-card'}}")
a.close-card-details.js-close-card-details(title="{{_ 'close-card'}}")
| ❌
if canModifyCard
if cardMaximized
a.fa.fa-window-minimize.minimize-card-details.js-minimize-card-details(title="{{_ 'minimize-card'}}")
a.minimize-card-details.js-minimize-card-details(title="{{_ 'minimize-card'}}")
| 🔽
else
a.fa.fa-window-maximize.maximize-card-details.js-maximize-card-details(title="{{_ 'maximize-card'}}")
a.maximize-card-details.js-maximize-card-details(title="{{_ 'maximize-card'}}")
| 🔼
if canModifyCard
a.fa.fa-navicon.card-details-menu.js-open-card-details-menu(title="{{_ 'cardDetailsActionsPopup-title'}}")
a.fa.fa-link.card-copy-button.js-copy-link(
a.card-details-menu.js-open-card-details-menu(title="{{_ 'cardDetailsActionsPopup-title'}}")
| ☰
a.card-copy-button.js-copy-link(
id="cardURL_copy"
class="fa-link"
title="{{_ 'copy-card-link-to-clipboard'}}"
@@ -29,10 +33,12 @@ template(name="cardDetails")
span.copied-tooltip {{_ 'copied'}}
else
unless isPopup
a.fa.fa-times-thin.close-card-details.js-close-card-details(title="{{_ 'close-card'}}")
a.close-card-details.js-close-card-details(title="{{_ 'close-card'}}")
| ❌
if canModifyCard
a.fa.fa-navicon.card-details-menu-mobile-web.js-open-card-details-menu(title="{{_ 'cardDetailsActionsPopup-title'}}")
a.fa.fa-link.card-copy-mobile-button.js-copy-link(
a.card-details-menu-mobile-web.js-open-card-details-menu(title="{{_ 'cardDetailsActionsPopup-title'}}")
| ☰
a.card-copy-mobile-button.js-copy-link(
id="cardURL_copy"
class="fa-link"
title="{{_ 'copy-card-link-to-clipboard'}}"
@@ -47,7 +53,8 @@ template(name="cardDetails")
| ##{getCardNumber}
= getTitle
if isWatching
i.card-details-watch.fa.fa-eye
i.card-details-watch
| 👁️
.card-details-path
each parentList
| &nbsp; &gt; &nbsp;
@@ -65,13 +72,33 @@ template(name="cardDetails")
else
p.warning {{_ 'card-archived'}}
// Upload progress indicator for drag-and-drop uploads
if hasActiveUploads
.card-details-upload-progress
.upload-progress-header
| 📤
span {{_ 'uploading-files'}} ({{uploadCount}})
each uploads
.upload-progress-item(class="{{#if $eq status 'error'}}upload-error{{/if}}")
.upload-progress-filename {{file.name}}
.upload-progress-bar
.upload-progress-fill(style="width: {{progress}}%")
if $eq status 'error'
.upload-progress-error
| ⚠️
span {{_ 'upload-failed'}}
else if $eq status 'completed'
.upload-progress-success
| ✅
span {{_ 'upload-completed'}}
.card-details-left
.card-details-items
if currentBoard.allowsLabels
.card-details-item.card-details-item-labels
h3.card-details-item-title
i.fa.fa-tags
| 🏷️
| {{_ 'labels'}}
a(class="{{#if canModifyCard}}js-add-labels{{else}}is-disabled{{/if}}" title="{{_ 'card-labels-title'}}")
each labels
@@ -81,15 +108,25 @@ template(name="cardDetails")
if canModifyCard
unless currentUser.isWorker
a.card-label.add-label.js-add-labels(title="{{_ 'card-labels-title'}}")
i.fa.fa-plus
| ➕
if currentBoard.hasAnyAllowsDate
hr
.card-details-item.card-details-item-date-format
h3.card-details-item-title
| 📅
| {{_ 'date-format'}}
.card-details-item-content
select.js-date-format-selector
option(value="YYYY-MM-DD" selected="{{#if isDateFormat 'YYYY-MM-DD'}}selected{{/if}}") {{_ 'date-format-yyyy-mm-dd'}}
option(value="DD-MM-YYYY" selected="{{#if isDateFormat 'DD-MM-YYYY'}}selected{{/if}}") {{_ 'date-format-dd-mm-yyyy'}}
option(value="MM-DD-YYYY" selected="{{#if isDateFormat 'MM-DD-YYYY'}}selected{{/if}}") {{_ 'date-format-mm-dd-yyyy'}}
if currentBoard.allowsReceivedDate
.card-details-item.card-details-item-received
h3.card-details-item-title
i.fa.fa-sign-out
| 📥
| {{_ 'card-received'}}
if getReceived
+cardReceivedDate
@@ -97,12 +134,12 @@ template(name="cardDetails")
if canModifyCard
unless currentUser.isWorker
a.card-label.add-label.js-received-date
i.fa.fa-plus
| ➕
if currentBoard.allowsStartDate
.card-details-item.card-details-item-start
h3.card-details-item-title
i.fa.fa-hourglass-start
| 🚀
| {{_ 'card-start'}}
if getStart
+cardStartDate
@@ -110,12 +147,12 @@ template(name="cardDetails")
if canModifyCard
unless currentUser.isWorker
a.card-label.add-label.js-start-date
i.fa.fa-plus
| ➕
if currentBoard.allowsDueDate
.card-details-item.card-details-item-due
h3.card-details-item-title
i.fa.fa-sign-in
| ⏰
| {{_ 'card-due'}}
if getDue
+cardDueDate
@@ -123,12 +160,12 @@ template(name="cardDetails")
if canModifyCard
unless currentUser.isWorker
a.card-label.add-label.js-due-date
i.fa.fa-plus
| ➕
if currentBoard.allowsEndDate
.card-details-item.card-details-item-end
h3.card-details-item-title
i.fa.fa-hourglass-end
| 🏁
| {{_ 'card-end'}}
if getEnd
+cardEndDate
@@ -136,7 +173,7 @@ template(name="cardDetails")
if canModifyCard
unless currentUser.isWorker
a.card-label.add-label.js-end-date
i.fa.fa-plus
| ➕
if currentBoard.hasAnyAllowsUser
hr
@@ -144,7 +181,7 @@ template(name="cardDetails")
if currentBoard.allowsCreator
.card-details-item.card-details-item-creator
h3.card-details-item-title
i.fa.fa-user
| 👤
| {{_ 'creator'}}
+userAvatar(userId=userId noRemove=true)
@@ -154,7 +191,7 @@ template(name="cardDetails")
if currentBoard.allowsMembers
.card-details-item.card-details-item-members
h3.card-details-item-title
i.fa.fa-users
| 👤s
| {{_ 'members'}}
each userId in getMembers
+userAvatar(userId=userId cardId=_id)
@@ -162,30 +199,30 @@ template(name="cardDetails")
if canModifyCard
unless currentUser.isWorker
a.member.add-member.card-details-item-add-button.js-add-members(title="{{_ 'card-members-title'}}")
i.fa.fa-plus
| ➕
//if assigneeSelected
if currentBoard.allowsAssignee
.card-details-item.card-details-item-assignees
h3.card-details-item-title
i.fa.fa-user
| 👤
| {{_ 'assignee'}}
each userId in getAssignees
+userAvatar(userId=userId cardId=_id assignee=true)
| {{! XXX Hack to hide syntaxic coloration /// }}
if canModifyCard
a.assignee.add-assignee.card-details-item-add-button.js-add-assignees(title="{{_ 'assignee'}}")
i.fa.fa-plus
| ➕
if currentUser.isWorker
unless assigneeSelected
a.assignee.add-assignee.card-details-item-add-button.js-add-assignees(title="{{_ 'assignee'}}")
i.fa.fa-plus
| ➕
//.card-details-items
if currentBoard.allowsRequestedBy
.card-details-item.card-details-item-name
h3.card-details-item-title
i.fa.fa-shopping-cart
| 🛒
| {{_ 'requested-by'}}
if canModifyCard
unless currentUser.isWorker
@@ -205,7 +242,7 @@ template(name="cardDetails")
if currentBoard.allowsAssignedBy
.card-details-item.card-details-item-name
h3.card-details-item-title
i.fa.fa-user-plus
| 👤-plus
| {{_ 'assigned-by'}}
if canModifyCard
unless currentUser.isWorker
@@ -228,7 +265,7 @@ template(name="cardDetails")
if currentBoard.allowsCardSortingByNumber
.card-details-item.card-details-sort-order
h3.card-details-item-title
i.fa.fa-sort
| 🔢
| {{_ 'sort'}}
if canModifyCard
+inlinedForm(classNames="js-card-details-sort")
@@ -241,7 +278,7 @@ template(name="cardDetails")
if currentBoard.allowsShowLists
.card-details-item.card-details-show-lists
h3.card-details-item-title
i.fa.fa-list
| 📋
| {{_ 'list'}}
select.js-select-card-details-lists(disabled="{{#unless canModifyCard}}disabled{{/unless}}")
each currentBoard.lists
@@ -267,7 +304,7 @@ template(name="cardDetails")
hr
.card-details-item.card-details-item-customfield
h3.card-details-item-title
i.fa.fa-list-alt
| 📋-alt
= definition.name
+cardCustomField
@@ -278,14 +315,14 @@ template(name="cardDetails")
else
input.toggle-switch(type="checkbox" id="toggleCustomFieldsGridButton")
label.toggle-label(for="toggleCustomFieldsGridButton")
a.fa.fa-plus.js-custom-fields.card-details-item.custom-fields(title="{{_ 'custom-fields'}}")
a.js-custom-fields.card-details-item.custom-fields(title="{{_ 'custom-fields'}}")
if getVoteQuestion
hr
.vote-title
div.flex
h3
i.fa.fa-thumbs-up
| 👍
| {{_ 'vote-question'}}
if getVoteEnd
+voteEndDate
@@ -303,11 +340,11 @@ template(name="cardDetails")
if showVotingButtons
button.card-details-green.js-vote.js-vote-positive(class="{{#if voteState}}voted{{/if}}")
if voteState
i.fa.fa-thumbs-up
| 👍
| {{_ 'vote-for-it'}}
button.card-details-red.js-vote.js-vote-negative(class="{{#if $eq voteState false}}voted{{/if}}")
if $eq voteState false
i.fa.fa-thumbs-down
| 👎
| {{_ 'vote-against'}}
if getPokerQuestion
@@ -315,7 +352,7 @@ template(name="cardDetails")
.poker-title
div.flex
h3
i.fa.fa-thumbs-up
| 👍
| {{_ 'poker-question'}}
if getPokerEnd
+pokerEndDate
@@ -330,52 +367,52 @@ template(name="cardDetails")
.poker-card
span.inner.js-poker.js-poker-vote-one(class="{{#if $eq pokerState 'one'}}poker-voted{{/if}}") {{_ 'poker-one'}}
if $eq pokerState "one"
i.fa.fa-check
| ✅
.poker-deck
.poker-card
span.inner.js-poker.js-poker-vote-two(class="{{#if $eq pokerState 'two'}}poker-voted{{/if}}") {{_ 'poker-two'}}
if $eq pokerState "two"
i.fa.fa-check
| ✅
.poker-deck
.poker-card
span.inner.js-poker.js-poker-vote-three(class="{{#if $eq pokerState 'three'}}poker-voted{{/if}}") {{_ 'poker-three'}}
if $eq pokerState "three"
i.fa.fa-check
| ✅
.poker-deck
.poker-card
span.inner.js-poker.js-poker-vote-five(class="{{#if $eq pokerState 'five'}}poker-voted{{/if}}") {{_ 'poker-five'}}
if $eq pokerState "five"
i.fa.fa-check
| ✅
.poker-deck
.poker-card
span.inner.js-poker.js-poker-vote-eight(class="{{#if $eq pokerState 'eight'}}poker-voted{{/if}}") {{_ 'poker-eight'}}
if $eq pokerState "eight"
i.fa.fa-check
| ✅
.poker-deck
.poker-card
span.inner.js-poker.js-poker-vote-thirteen(class="{{#if $eq pokerState 'thirteen'}}poker-voted{{/if}}") {{_ 'poker-thirteen'}}
if $eq pokerState "thirteen"
i.fa.fa-check
| ✅
.poker-deck
.poker-card
span.inner.js-poker.js-poker-vote-twenty(class="{{#if $eq pokerState 'twenty'}}poker-voted{{/if}}") {{_ 'poker-twenty'}}
if $eq pokerState "twenty"
i.fa.fa-check
| ✅
.poker-deck
.poker-card
span.inner.js-poker.js-poker-vote-forty(class="{{#if $eq pokerState 'forty'}}poker-voted{{/if}}") {{_ 'poker-forty'}}
if $eq pokerState "forty"
i.fa.fa-check
| ✅
.poker-deck
.poker-card
span.inner.js-poker.js-poker-vote-one-hundred(class="{{#if $eq pokerState 'oneHundred'}}poker-voted{{/if}}") {{_ 'poker-oneHundred'}}
if $eq pokerState "oneHundred"
i.fa.fa-check
| ✅
.poker-deck
.poker-card
span.inner.js-poker.js-poker-vote-unsure(class="{{#if $eq pokerState 'unsure'}}poker-voted{{/if}}") {{_ 'poker-unsure'}}
if $eq pokerState "unsure"
i.fa.fa-check
| ✅
if currentUser.isBoardAdmin
button.card-details-blue.js-poker-finish(class="{{#if $eq voteState false}}poker-voted{{/if}}") {{_ 'poker-finish'}}
@@ -505,7 +542,7 @@ template(name="cardDetails")
button.card-details-red.js-poker-replay(class="{{#if $eq voteState false}}voted{{/if}}") {{_ 'poker-replay'}}
div.estimation-add
button.js-poker-estimation
i.fa.fa-plus
| ➕
| {{_ 'set-estimation'}}
input(type=text,autofocus value=getPokerEstimation,id="pokerEstimation")
@@ -515,18 +552,18 @@ template(name="cardDetails")
if currentBoard.allowsDescriptionTitle
hr
h3.card-details-item-title
i.fa.fa-align-left
| 📝
| {{_ 'description'}}
if currentBoard.allowsDescriptionText
+inlinedCardDescription(classNames="card-description js-card-description")
+descriptionForm
.edit-controls.clearfix
button.primary(type="submit") {{_ 'save'}}
a.fa.fa-times-thin.js-close-inlined-form
a.js-close-inlined-form
else
if currentBoard.allowsDescriptionText
a.js-open-inlined-form(title="{{_ 'edit'}}" value=title)
i.fa.fa-pencil-square-o
| ✏️
a.js-open-inlined-form(title="{{_ 'edit'}}" value=title)
if getDescription
+viewer
@@ -556,7 +593,7 @@ template(name="cardDetails")
if currentBoard.allowsAttachments
hr
h3.card-details-item-title
i.fa.fa-paperclip
| 📎
| {{_ 'attachments'}}
if Meteor.settings.public.attachmentsUploadMaxSize
| {{_ 'max-upload-filesize'}} {{Meteor.settings.public.attachmentsUploadMaxSize}}
@@ -572,7 +609,7 @@ template(name="cardDetails")
unless currentUser.isNoComments
.comment-title
h3.card-details-item-title
i.fa.fa-comment-o
| 💬
| {{_ 'comments'}}
if currentBoard.allowsComments
@@ -587,7 +624,7 @@ template(name="cardDetails")
unless currentUser.isNoComments
.activity-title
h3.card-details-item-title
i.fa.fa-history
| 📜
| {{ _ 'activities'}}
if currentUser.isBoardMember
.material-toggle-switch(title="{{_ 'show-activities'}}")
@@ -607,41 +644,41 @@ template(name="cardDetails")
+activities(card=this mode="card")
template(name="editCardTitleForm")
a.fa.fa-copy(title="{{_ 'copy-text-to-clipboard'}}")
a(title="{{_ 'copy-text-to-clipboard'}}")
span.copied-tooltip {{_ 'copied'}}
textarea.js-edit-card-title(rows='1' autofocus dir="auto")
= getTitle
.edit-controls.clearfix
button.primary.confirm.js-submit-edit-card-title-form(type="submit") {{_ 'save'}}
a.fa.fa-times-thin.js-close-inlined-form
a.js-close-inlined-form
template(name="editCardRequesterForm")
input.js-edit-card-requester(type='text' autofocus value=getRequestedBy dir="auto")
.edit-controls.clearfix
button.primary.confirm.js-submit-edit-card-requester-form(type="submit") {{_ 'save'}}
a.fa.fa-times-thin.js-close-inlined-form
a.js-close-inlined-form
template(name="editCardAssignerForm")
input.js-edit-card-assigner(type='text' autofocus value=getAssignedBy dir="auto")
.edit-controls.clearfix
button.primary.confirm.js-submit-edit-card-assigner-form(type="submit") {{_ 'save'}}
a.fa.fa-times-thin.js-close-inlined-form
a.js-close-inlined-form
template(name="editCardSortOrderForm")
input.js-edit-card-sort(type='text' autofocus value=sort dir="auto")
.edit-controls.clearfix
button.primary.confirm.js-submit-edit-card-sort-form(type="submit") {{_ 'save'}}
a.fa.fa-times-thin.js-close-inlined-form
a.js-close-inlined-form
template(name="cardDetailsActionsPopup")
ul.pop-over-list
li
a.js-toggle-watch-card
if isWatching
i.fa.fa-eye
| 👁️
| {{_ 'unwatch'}}
else
i.fa.fa-eye-slash
| 👁️-slash
| {{_ 'watch'}}
hr
if canModifyCard
@@ -652,16 +689,16 @@ template(name="cardDetailsActionsPopup")
//li: a.js-attachments {{_ 'card-edit-attachments'}}
li
a.js-start-voting
i.fa.fa-thumbs-up
| 👍
| {{_ 'card-edit-voting'}}
li
a.js-start-planning-poker
i.fa.fa-thumbs-up
| 👍
| {{_ 'card-edit-planning-poker'}}
if currentUser.isBoardAdmin
li
a.js-custom-fields
i.fa.fa-list-alt
| 📋-alt
| {{_ 'card-edit-custom-fields'}}
//li: a.js-received-date {{_ 'editCardReceivedDatePopup-title'}}
//li: a.js-start-date {{_ 'editCardStartDatePopup-title'}}
@@ -669,67 +706,75 @@ template(name="cardDetailsActionsPopup")
//li: a.js-end-date {{_ 'editCardEndDatePopup-title'}}
li
a.js-spent-time
i.fa.fa-clock-o
| 🕐
| {{_ 'editCardSpentTimePopup-title'}}
li
a.js-set-card-color
i.fa.fa-paint-brush
| 🎨
| {{_ 'setCardColorPopup-title'}}
li
a.js-toggle-show-list-on-minicard
if showListOnMinicard
| 👁️
| {{_ 'hide-list-on-minicard'}}
else
| 👁️-slash
| {{_ 'show-list-on-minicard'}}
hr
ul.pop-over-list
li
a.js-export-card
i.fa.fa-share-alt
| 📤
| {{_ 'export-card'}}
hr
ul.pop-over-list
li
a.js-move-card-to-top
i.fa.fa-arrow-up
| ⬆️
| {{_ 'moveCardToTop-title'}}
li
a.js-move-card-to-bottom
i.fa.fa-arrow-down
| ⬇️
| {{_ 'moveCardToBottom-title'}}
hr
ul.pop-over-list
if currentUser.isBoardAdmin
li
a.js-move-card
i.fa.fa-arrow-right
| ➡️
| {{_ 'moveCardPopup-title'}}
unless currentUser.isWorker
li
a.js-copy-card
i.fa.fa-copy
| 📋
| {{_ 'copyCardPopup-title'}}
unless currentUser.isWorker
ul.pop-over-list
li
a.js-copy-checklist-cards
i.fa.fa-copy
i.fa.fa-copy
| 📋
| 📋
| {{_ 'copyManyCardsPopup-title'}}
unless archived
hr
ul.pop-over-list
li
a.js-archive
i.fa.fa-arrow-right
i.fa.fa-archive
| ➡️
| 📦
| {{_ 'archive-card'}}
hr
ul.pop-over-list
li
a.js-more
i.fa.fa-link
| 🔗
| {{_ 'cardMorePopup-title'}}
template(name="exportCardPopup")
ul.pop-over-list
li
a(href="{{exportUrlCardPDF}}",, download="{{exportFilenameCardPDF}}")
i.fa.fa-share-alt
| 📤
| {{_ 'export-card-pdf'}}
template(name="moveCardPopup")
@@ -784,7 +829,7 @@ template(name="cardMembersPopup")
= user.profile.fullname
| (<span class="username">{{ user.username }}</span>)
if isCardMember
i.fa.fa-check
| ✅
template(name="cardAssigneesPopup")
input.card-assignees-filter(type="text" placeholder="{{_ 'search'}}")
@@ -798,7 +843,7 @@ template(name="cardAssigneesPopup")
= user.profile.fullname
| (<span class="username">{{ user.username }}</span>)
if isCardAssignee
i.fa.fa-check
| ✅
if currentUser.isWorker
ul.pop-over-list.js-card-assignee-list
li.item(class="{{#if currentUser.isCardAssignee}}active{{/if}}")
@@ -808,7 +853,7 @@ template(name="cardAssigneesPopup")
= currentUser.profile.fullname
| (<span class="username">{{ currentUser.username }}</span>)
if currentUser.isCardAssignee
i.fa.fa-check
| ✅
template(name="cardAssigneePopup")
.board-assignee-menu
@@ -832,7 +877,7 @@ template(name="cardMorePopup")
span.clearfix
span {{_ 'link-card'}}
= ' '
i.fa.colorful(class="{{#if board.isPublic}}fa-globe{{else}}fa-lock{{/if}}")
| {{#if board.isPublic}}🌐{{else}}🔒{{/if}}
input.inline-input(type="text" id="cardURL" readonly value="{{ originRelativeUrl }}" autofocus="autofocus")
button.js-copy-card-link-to-clipboard(class="btn" id="clipboard") {{_ 'copy-card-link-to-clipboard'}}
.copied-tooltip {{_ 'copied'}}
@@ -874,7 +919,7 @@ template(name="setCardColorPopup")
unless $eq color 'white'
span.card-label.palette-color.js-palette-color(class="card-details-{{color}}")
if(isSelected color)
i.fa.fa-check
| ✅
button.primary.confirm.js-submit {{_ 'save'}}
button.js-remove-color.negate.wide.right {{_ 'unset-color'}}
@@ -908,12 +953,12 @@ template(name="cardStartVotingPopup")
.materialCheckBox#vote-public(name="vote-public" class="{{#if votePublic}}is-checked{{/if}}")
span {{_ 'vote-public'}}
.check-div.flex
i.fa.fa-hourglass-end
| ⏰
a.js-end-date
span
| {{_ 'card-end'}}
unless getVoteEnd
i.fa.fa-plus
| ➕
if getVoteEnd
+voteEndDate
@@ -954,12 +999,12 @@ template(name="cardStartPlanningPokerPopup")
.materialCheckBox#poker-allow-non-members(name="poker-allow-non-members" class="{{#if pokerAllowNonBoardMembers}}is-checked{{/if}}")
span {{_ 'allowNonBoardMembers'}}
.check-div.flex
i.fa.fa-hourglass-end
| ⏰
a.js-end-date
span
| {{_ 'card-end'}}
unless getPokerEnd
i.fa.fa-plus
| ➕
if getPokerEnd
+pokerEndDate
+157 -53
View File
@@ -1,7 +1,26 @@
import { ReactiveCache } from '/imports/reactiveCache';
import moment from 'moment/min/moment-with-locales';
import { TAPi18n } from '/imports/i18n';
import { DatePicker } from '/client/lib/datepicker';
import {
formatDateTime,
formatDate,
formatTime,
getISOWeek,
isValidDate,
isBefore,
isAfter,
isSame,
add,
subtract,
startOf,
endOf,
format,
parseDate,
now,
createDate,
fromNow,
calendar
} from '/imports/lib/dateUtils';
import Cards from '/models/cards';
import Boards from '/models/boards';
import Checklists from '/models/checklists';
@@ -12,6 +31,8 @@ import CardComments from '/models/cardComments';
import { ALLOWED_COLORS } from '/config/const';
import { UserAvatar } from '../users/userAvatar';
import { DialogWithBoardSwimlaneList } from '/client/lib/dialogWithBoardSwimlaneList';
import { handleFileUpload } from './attachments';
import uploadProgressManager from '../../lib/uploadProgressManager';
const subManager = new SubsManager();
const { calculateIndexData } = Utils;
@@ -285,6 +306,10 @@ BlazeComponent.extendComponent({
const $tooltip = this.$('.card-details-header .copied-tooltip');
Utils.showCopied(promise, $tooltip);
},
'change .js-date-format-selector'(event) {
const dateFormat = event.target.value;
Meteor.call('changeDateFormat', dateFormat);
},
'click .js-open-card-details-menu': Popup.open('cardDetailsActions'),
'submit .js-card-description'(event) {
event.preventDefault();
@@ -453,7 +478,7 @@ BlazeComponent.extendComponent({
'click .js-poker-finish'(e) {
if ($(e.target).hasClass('js-poker-finish')) {
e.preventDefault();
const now = moment().format('YYYY-MM-DD HH:mm');
const now = formatDateTime(new Date());
this.data().setPokerEnd(now);
}
},
@@ -481,6 +506,62 @@ BlazeComponent.extendComponent({
}
}
},
// Drag and drop file upload handlers
'dragover .js-card-details'(event) {
// Only prevent default for file drags to avoid interfering with other drag operations
const dataTransfer = event.originalEvent.dataTransfer;
if (dataTransfer && dataTransfer.types && dataTransfer.types.includes('Files')) {
event.preventDefault();
event.stopPropagation();
}
},
'dragenter .js-card-details'(event) {
const dataTransfer = event.originalEvent.dataTransfer;
if (dataTransfer && dataTransfer.types && dataTransfer.types.includes('Files')) {
event.preventDefault();
event.stopPropagation();
const card = this.data();
const board = card.board();
// Only allow drag-and-drop if user can modify card and board allows attachments
if (Utils.canModifyCard() && board && board.allowsAttachments) {
$(event.currentTarget).addClass('is-dragging-over');
}
}
},
'dragleave .js-card-details'(event) {
const dataTransfer = event.originalEvent.dataTransfer;
if (dataTransfer && dataTransfer.types && dataTransfer.types.includes('Files')) {
event.preventDefault();
event.stopPropagation();
$(event.currentTarget).removeClass('is-dragging-over');
}
},
'drop .js-card-details'(event) {
const dataTransfer = event.originalEvent.dataTransfer;
if (dataTransfer && dataTransfer.types && dataTransfer.types.includes('Files')) {
event.preventDefault();
event.stopPropagation();
$(event.currentTarget).removeClass('is-dragging-over');
const card = this.data();
const board = card.board();
// Check permissions
if (!Utils.canModifyCard() || !board || !board.allowsAttachments) {
return;
}
// Check if this is a file drop (not a checklist item reorder)
if (!dataTransfer.files || dataTransfer.files.length === 0) {
return;
}
const files = dataTransfer.files;
if (files && files.length > 0) {
handleFileUpload(card, files);
}
}
},
},
];
},
@@ -490,6 +571,21 @@ Template.cardDetails.helpers({
isPopup() {
let ret = !!Utils.getPopupCardId();
return ret;
},
isDateFormat(format) {
const currentUser = ReactiveCache.getCurrentUser();
if (!currentUser) return format === 'YYYY-MM-DD';
return currentUser.getDateFormat() === format;
},
// Upload progress helpers
hasActiveUploads() {
return uploadProgressManager.hasActiveUploads(this._id);
},
uploads() {
return uploadProgressManager.getUploadsForCard(this._id);
},
uploadCount() {
return uploadProgressManager.getUploadCountForCard(this._id);
}
});
Template.cardDetailsPopup.onDestroyed(() => {
@@ -592,6 +688,10 @@ Template.cardDetailsActionsPopup.helpers({
isBoardAdmin() {
return ReactiveCache.getCurrentUser().isBoardAdmin();
},
showListOnMinicard() {
return this.showListOnMinicard;
},
});
Template.cardDetailsActionsPopup.events({
@@ -637,6 +737,12 @@ Template.cardDetailsActionsPopup.events({
if (!err && ret) Popup.close();
});
},
'click .js-toggle-show-list-on-minicard'() {
const currentCard = this;
const newValue = !currentCard.showListOnMinicard;
Cards.update(currentCard._id, { $set: { showListOnMinicard: newValue } });
Popup.close();
},
});
BlazeComponent.extendComponent({
@@ -1031,8 +1137,8 @@ BlazeComponent.extendComponent({
// editVoteEndDatePopup
(class extends DatePicker {
onCreated() {
super.onCreated(moment().format('YYYY-MM-DD HH:mm'));
this.data().getVoteEnd() && this.date.set(moment(this.data().getVoteEnd()));
super.onCreated(formatDateTime(now()));
this.data().getVoteEnd() && this.date.set(new Date(this.data().getVoteEnd()));
}
events() {
return [
@@ -1043,12 +1149,12 @@ BlazeComponent.extendComponent({
// if no time was given, init with 12:00
const time =
evt.target.time.value ||
moment(new Date().setHours(12, 0, 0)).format('LT');
formatTime(new Date().setHours(12, 0, 0));
const dateString = `${evt.target.date.value} ${time}`;
/*
const newDate = moment(dateString, 'L LT', true);
const newDate = parseDate(dateString, ['L LT'], true);
if (newDate.isValid()) {
// if active vote - store it
if (this.currentData().getVoteQuestion()) {
@@ -1062,28 +1168,27 @@ BlazeComponent.extendComponent({
*/
// Try to parse different date formats of all languages.
// This code is same for vote and planning poker.
const usaDate = moment(dateString, 'L LT', true);
const euroAmDate = moment(dateString, 'DD.MM.YYYY LT', true);
const euro24hDate = moment(dateString, 'DD.MM.YYYY HH.mm', true);
const eurodotDate = moment(dateString, 'DD.MM.YYYY HH:mm', true);
const minusDate = moment(dateString, 'YYYY-MM-DD HH:mm', true);
const slashDate = moment(dateString, 'DD/MM/YYYY HH.mm', true);
const dotDate = moment(dateString, 'DD/MM/YYYY HH:mm', true);
const brezhonegDate = moment(dateString, 'DD/MM/YYYY h[e]mm A', true);
const hrvatskiDate = moment(dateString, 'DD. MM. YYYY H:mm', true);
const latviaDate = moment(dateString, 'YYYY.MM.DD. H:mm', true);
const nederlandsDate = moment(dateString, 'DD-MM-YYYY HH:mm', true);
// greekDate does not work: el Greek Ελληνικά ,
// it has date format DD/MM/YYYY h:mm MM like 20/06/2021 11:15 MM
// where MM is maybe some text like AM/PM ?
// Also some other languages that have non-ascii characters in dates
// do not work.
const greekDate = moment(dateString, 'DD/MM/YYYY h:mm A', true);
const macedonianDate = moment(dateString, 'D.MM.YYYY H:mm', true);
// Try to parse different date formats using native Date parsing
const formats = [
'YYYY-MM-DD HH:mm',
'MM/DD/YYYY HH:mm',
'DD.MM.YYYY HH:mm',
'DD/MM/YYYY HH:mm',
'DD-MM-YYYY HH:mm'
];
let parsedDate = null;
for (const format of formats) {
parsedDate = parseDate(dateString, [format], true);
if (parsedDate) break;
}
// Fallback to native Date parsing
if (!parsedDate) {
parsedDate = new Date(dateString);
}
if (usaDate.isValid()) {
if (isValidDate(parsedDate)) {
// if active poker - store it
if (this.currentData().getPokerQuestion()) {
this._storeDate(usaDate.toDate());
@@ -1262,9 +1367,9 @@ BlazeComponent.extendComponent({
// editPokerEndDatePopup
(class extends DatePicker {
onCreated() {
super.onCreated(moment().format('YYYY-MM-DD HH:mm'));
super.onCreated(formatDateTime(now()));
this.data().getPokerEnd() &&
this.date.set(moment(this.data().getPokerEnd()));
this.date.set(new Date(this.data().getPokerEnd()));
}
/*
@@ -1282,7 +1387,7 @@ BlazeComponent.extendComponent({
return moment.localeData().longDateFormat('LT');
}
const newDate = moment(dateString, dateformat() + ' ' + timeformat(), true);
const newDate = parseDate(dateString, [dateformat() + ' ' + timeformat()], true);
*/
events() {
@@ -1294,7 +1399,7 @@ BlazeComponent.extendComponent({
// if no time was given, init with 12:00
const time =
evt.target.time.value ||
moment(new Date().setHours(12, 0, 0)).format('LT');
formatTime(new Date().setHours(12, 0, 0));
const dateString = `${evt.target.date.value} ${time}`;
@@ -1305,7 +1410,7 @@ BlazeComponent.extendComponent({
Maybe client/components/lib/datepicker.jade could have hidden input field for
datepicker format that could be used to detect date format?
const newDate = moment(dateString, dateformat() + ' ' + timeformat(), true);
const newDate = parseDate(dateString, [dateformat() + ' ' + timeformat()], true);
if (newDate.isValid()) {
// if active poker - store it
@@ -1318,28 +1423,27 @@ BlazeComponent.extendComponent({
}
*/
// Try to parse different date formats of all languages.
// This code is same for vote and planning poker.
const usaDate = moment(dateString, 'L LT', true);
const euroAmDate = moment(dateString, 'DD.MM.YYYY LT', true);
const euro24hDate = moment(dateString, 'DD.MM.YYYY HH.mm', true);
const eurodotDate = moment(dateString, 'DD.MM.YYYY HH:mm', true);
const minusDate = moment(dateString, 'YYYY-MM-DD HH:mm', true);
const slashDate = moment(dateString, 'DD/MM/YYYY HH.mm', true);
const dotDate = moment(dateString, 'DD/MM/YYYY HH:mm', true);
const brezhonegDate = moment(dateString, 'DD/MM/YYYY h[e]mm A', true);
const hrvatskiDate = moment(dateString, 'DD. MM. YYYY H:mm', true);
const latviaDate = moment(dateString, 'YYYY.MM.DD. H:mm', true);
const nederlandsDate = moment(dateString, 'DD-MM-YYYY HH:mm', true);
// greekDate does not work: el Greek Ελληνικά ,
// it has date format DD/MM/YYYY h:mm MM like 20/06/2021 11:15 MM
// where MM is maybe some text like AM/PM ?
// Also some other languages that have non-ascii characters in dates
// do not work.
const greekDate = moment(dateString, 'DD/MM/YYYY h:mm A', true);
const macedonianDate = moment(dateString, 'D.MM.YYYY H:mm', true);
// Try to parse different date formats using native Date parsing
const formats = [
'YYYY-MM-DD HH:mm',
'MM/DD/YYYY HH:mm',
'DD.MM.YYYY HH:mm',
'DD/MM/YYYY HH:mm',
'DD-MM-YYYY HH:mm'
];
let parsedDate = null;
for (const format of formats) {
parsedDate = parseDate(dateString, [format], true);
if (parsedDate) break;
}
// Fallback to native Date parsing
if (!parsedDate) {
parsedDate = new Date(dateString);
}
if (usaDate.isValid()) {
if (isValidDate(parsedDate)) {
// if active poker - store it
if (this.currentData().getPokerQuestion()) {
this._storeDate(usaDate.toDate());
+4 -4
View File
@@ -15,8 +15,8 @@ template(name="editCardSpentTime")
template(name="timeBadge")
if canModifyCard
a.js-edit-time.card-time(title="{{showTitle}}" class="{{#if getIsOvertime}}card-label-red{{else}}card-label-green{{/if}}")
| {{showTime}}
a.js-edit-time.card-time(title="{{_ 'time'}}" class="{{#if getIsOvertime}}card-label-red{{else}}card-label-green{{/if}}")
| ⏱️ {{showTime}}
else
a.card-time(title="{{showTitle}}" class="{{#if getIsOvertime}}card-label-red{{else}}card-label-green{{/if}}")
| {{showTime}}
a.card-time(title="{{_ 'time'}}" class="{{#if getIsOvertime}}card-label-red{{else}}card-label-green{{/if}}")
| ⏱️ {{showTime}}
+17 -18
View File
@@ -1,14 +1,14 @@
template(name="checklists")
.checklists-title
h3.card-details-item-title
i.fa.fa-check
| ✅
| {{_ 'checklists'}}
if canModifyCard
+inlinedForm(autoclose=false classNames="js-add-checklist" cardId = cardId position="top")
+addChecklistItemForm
else
a.add-checklist-top.js-open-inlined-form(title="{{_ 'add-checklist'}}")
i.fa.fa-plus
| ➕
if currentUser.isBoardMember
.material-toggle-switch(title="{{_ 'hide-finished-checklist'}}")
//span.toggle-switch-title
@@ -28,7 +28,7 @@ template(name="checklists")
+addChecklistItemForm(checklist=checklist showNewlineBecomesNewChecklistItem=false)
else
a.add-checklist.js-open-inlined-form(title="{{_ 'add-checklist'}}")
i.fa.fa-plus
| ➕
template(name="checklistDetail")
.js-checklist.checklist.nodragscroll
@@ -38,7 +38,7 @@ template(name="checklistDetail")
.checklist-title
span
if canModifyCard
a.fa.fa-navicon.checklist-details-menu.js-open-checklist-details-menu(title="{{_ 'checklistActionsPopup-title'}}")
a.checklist-details-menu.js-open-checklist-details-menu(title="{{_ 'checklistActionsPopup-title'}}")
if canModifyCard
h4.title.js-open-inlined-form.is-editable
@@ -63,12 +63,12 @@ template(name="checklistDeletePopup")
button.js-confirm.negate.full(type="submit") {{_ 'delete'}}
template(name="addChecklistItemForm")
a.fa.fa-copy(title="{{_ 'copy-text-to-clipboard'}}")
a(title="{{_ 'copy-text-to-clipboard'}}")
span.copied-tooltip {{_ 'copied'}}
textarea.js-add-checklist-item(rows='1' autofocus)
.edit-controls.clearfix
button.primary.confirm.js-submit-add-checklist-item-form(type="submit") {{_ 'save'}}
a.fa.fa-times-thin.js-close-inlined-form(title="{{_ 'close-add-checklist-item'}}")
a.js-close-inlined-form(title="{{_ 'close-add-checklist-item'}}")
if showNewlineBecomesNewChecklistItem
.material-toggle-switch(title="{{_ 'newlineBecomesNewChecklistItem'}}")
input.toggle-switch(type="checkbox" id="toggleNewlineBecomesNewChecklistItem")
@@ -81,7 +81,7 @@ template(name="addChecklistItemForm")
| {{_ 'originOrder'}}
template(name="editChecklistItemForm")
a.fa.fa-copy(title="{{_ 'copy-text-to-clipboard'}}")
a(title="{{_ 'copy-text-to-clipboard'}}")
span.copied-tooltip {{_ 'copied'}}
textarea.js-edit-checklist-item(rows='1' autofocus dir="auto")
if $eq type 'item'
@@ -90,12 +90,12 @@ template(name="editChecklistItemForm")
= checklist.title
.edit-controls.clearfix
button.primary.confirm.js-submit-edit-checklist-item-form(type="submit") {{_ 'save'}}
a.fa.fa-times-thin.js-close-inlined-form(title="{{_ 'close-edit-checklist-item'}}")
a.js-close-inlined-form(title="{{_ 'close-edit-checklist-item'}}")
span(title=createdAt) {{ moment createdAt }}
if canModifyCard
a.js-delete-checklist-item {{_ "delete"}}...
a.js-convert-checklist-item-to-card
i.fa.fa-copy
| 📋
| {{_ 'convertChecklistItemToCardPopup-title'}}
template(name="checklistItems")
@@ -105,7 +105,7 @@ template(name="checklistItems")
+addChecklistItemForm(checklist=checklist showNewlineBecomesNewChecklistItem=true position="top")
else
a.add-checklist-item.js-open-inlined-form(title="{{_ 'add-checklist-item'}}")
i.fa.fa-plus
| ➕
.checklist-items.js-checklist-items
each item in checklist.items
+inlinedForm(classNames="js-edit-checklist-item" item = item checklist = checklist)
@@ -117,7 +117,7 @@ template(name="checklistItems")
+addChecklistItemForm(checklist=checklist showNewlineBecomesNewChecklistItem=true)
else
a.add-checklist-item.js-open-inlined-form(title="{{_ 'add-checklist-item'}}")
i.fa.fa-plus
| ➕
template(name='checklistItemDetail')
.js-checklist-item.checklist-item(class="{{#if item.isFinished }}is-checked{{#if checklist.hideCheckedChecklistItems}} invisible{{/if}}{{/if}}{{#if checklist.hideAllChecklistItems}} is-checked invisible{{/if}}"
@@ -125,8 +125,7 @@ template(name='checklistItemDetail')
if canModifyCard
.check-box-container
.check-box.materialCheckBox(class="{{#if item.isFinished }}is-checked{{/if}}")
if isTouchScreenOrShowDesktopDragHandles
span.fa.checklistitem-handle(class="fa-arrows" title="{{_ 'dragChecklistItem'}}")
span.fa.checklistitem-handle(class="fa-arrows" title="{{_ 'dragChecklistItem'}}")
.item-title.js-open-inlined-form.is-editable(class="{{#if item.isFinished }}is-checked{{/if}}")
+viewer
= item.title
@@ -140,16 +139,16 @@ template(name="checklistActionsPopup")
ul.pop-over-list
li
a.js-delete-checklist.delete-checklist
i.fa.fa-trash
| 🗑️
| {{_ "delete"}} ...
a.js-move-checklist.move-checklist
i.fa.fa-arrow-right
| ➡️
| {{_ "moveChecklist"}} ...
a.js-copy-checklist.copy-checklist
i.fa.fa-copy
| 📋
| {{_ "copyChecklist"}} ...
a.js-hide-checked-checklist-items
i.fa.fa-eye-slash
| 🙈
| {{_ "hideCheckedChecklistItems"}} ...
.material-toggle-switch(title="{{_ 'hide-checked-items'}}")
if checklist.hideCheckedChecklistItems
@@ -158,7 +157,7 @@ template(name="checklistActionsPopup")
input.toggle-switch(type="checkbox" id="toggleHideCheckedChecklistItems_{{checklist._id}}")
label.toggle-label(for="toggleHideCheckedChecklistItems_{{checklist._id}}")
a.js-hide-all-checklist-items
i.fa.fa-ban
| 🚫
| {{_ "hideAllChecklistItems"}} ...
.material-toggle-switch(title="{{_ 'hideAllChecklistItems'}}")
if checklist.hideAllChecklistItems
+1
View File
@@ -38,6 +38,7 @@
.palette-colors {
display: flex;
flex-wrap: wrap;
justify-content: flex-start; /* left-align color chips in wider popovers */
}
.palette-colors .palette-color {
flex-grow: 1;
+4 -3
View File
@@ -6,7 +6,7 @@ template(name="formLabel")
.palette-colors: each labels
span.card-label.palette-color.js-palette-color(class="card-label-{{color}}")
if(isSelected color)
i.fa.fa-check
| ✅
template(name="createLabelPopup")
form.create-label
@@ -28,7 +28,8 @@ template(name="cardLabelsPopup")
ul.edit-labels-pop-over
each board.labels
li.js-card-label-item
a.card-label-edit-button.fa.fa-pencil.js-edit-label
a.card-label-edit-button.js-edit-label
| ✏️
if isTouchScreenOrShowDesktopDragHandles
span.fa.label-handle(class="fa-arrows" title="{{_ 'dragLabel'}}")
span.card-label.card-label-selectable.js-select-label.card-label-wrapper(class="card-label-{{color}}"
@@ -36,5 +37,5 @@ template(name="cardLabelsPopup")
+viewer
= name
if(isLabelSelected ../_id)
i.card-label-selectable-icon.fa.fa-check
| ✅
a.quiet-button.full.js-add-label {{_ 'label-create'}}
+26 -4
View File
@@ -125,8 +125,19 @@ Template.createLabelPopup.events({
.$('#labelName')
.val()
.trim();
const color = Blaze.getData(templateInstance.find('.fa-check')).color;
board.addLabel(name, color);
// Find the selected color by looking for the palette color that contains the checkmark
let selectedColor = null;
templateInstance.$('.js-palette-color').each(function() {
if ($(this).text().includes('✅')) {
selectedColor = Blaze.getData(this).color;
return false; // break out of loop
}
});
if (selectedColor) {
board.addLabel(name, selectedColor);
}
Popup.back();
},
});
@@ -144,8 +155,19 @@ Template.editLabelPopup.events({
.$('#labelName')
.val()
.trim();
const color = Blaze.getData(templateInstance.find('.fa-check')).color;
board.editLabel(this._id, name, color);
// Find the selected color by looking for the palette color that contains the checkmark
let selectedColor = null;
templateInstance.$('.js-palette-color').each(function() {
if ($(this).text().includes('✅')) {
selectedColor = Blaze.getData(this).color;
return false; // break out of loop
}
});
if (selectedColor) {
board.editLabel(this._id, name, selectedColor);
}
Popup.back();
},
});
+157 -60
View File
@@ -3,11 +3,11 @@
position: relative;
display: flex;
align-items: center;
margin-bottom: 9px;
margin-bottom: 1.2vh;
}
.minicard-wrapper.placeholder {
background: #ccc;
border-radius: 9px;
border-radius: 1.2vw;
}
.minicard-wrapper.ui-sortable-helper {
cursor: grabbing;
@@ -16,28 +16,28 @@
}
.minicard-wrapper.ui-sortable-helper .and-n-other {
width: 100%;
height: 16px;
padding: 4px;
height: 2.5vh;
padding: 0.5vh;
background-color: #f2f2f2;
text-align: center;
border-radius: 3px;
border-radius: 0.4vw;
}
.minicard-wrapper.ui-sortable-helper .multi-selection-checkbox {
display: none;
}
.minicard-wrapper .multi-selection-checkbox + .minicard {
margin-left: 8px;
margin-left: 1vw;
}
@media only screen {
.minicard {
padding: 6px 8px 2px;
padding: 0.8vh 1vw 0.3vh;
position: relative;
flex: 1;
flex-wrap: wrap;
background-color: #fff;
min-height: 20px;
box-shadow: 0 1px 2px rgba(0,0,0,0.15);
border-radius: 2px;
min-height: 2.5vh;
box-shadow: 0 0.2vh 0.3vh rgba(0,0,0,0.15);
border-radius: 0.3vw;
color: #4d4d4d;
overflow: hidden;
transition: transform 0.2s, border-radius 0.2s;
@@ -45,14 +45,14 @@
}
.minicard-details-menu-with-handle {
float: right;
font-size: 18px;
padding-right: 30px;
padding-left: 5px;
font-size: clamp(14px, 3vw, 18px);
padding-right: 4vw;
padding-left: 0.7vw;
}
.minicard-details-menu {
float: right;
font-size: 18px;
padding-left: 5px;
font-size: clamp(14px, 3vw, 18px);
padding-left: 0.7vw;
}
@media print {
.minicard-details-menu,
@@ -63,7 +63,7 @@
.minicard.linked-board .linked-icon,
.minicard.linked-card .linked-icon {
display: inline-block;
margin-right: 11px;
margin-right: 1.5vw;
vertical-align: baseline;
font-size: 0.9em;
}
@@ -72,11 +72,11 @@
color: #937760;
}
.is-selected .minicard {
transform: translateX(11px);
transform: translateX(1.5vw);
border-bottom-right-radius: 0;
border-top-right-radius: 0;
z-index: 25;
box-shadow: -2px 1px 2px rgba(0,0,0,0.2);
box-shadow: -0.3vw 0.2vh 0.3vh rgba(0,0,0,0.2);
}
.minicard:hover:not(.minicard-composer),
.is-selected .minicard,
@@ -90,20 +90,20 @@
background-position: center;
background-repeat: no-repeat;
background-size: contain;
height: 145px;
height: 20vh;
user-select: none;
margin: 6px -8px 6px -8px;
border-radius: top 2px;
margin: 0.8vh -1vw 0.8vh -1vw;
border-radius: top 0.3vw;
}
.minicard .minicard-labels {
float: none;
}
.minicard .minicard-labels .minicard-label {
width: 11px;
height: 11px;
border-radius: 2px;
margin-right: 3px;
margin-bottom: 3px;
width: clamp(12px, 1.5vw, 16px);
height: clamp(12px, 1.5vw, 16px);
border-radius: 0.3vw;
margin-right: 0.4vw;
margin-bottom: 0.4vh;
}
.minicard .minicard-labels-no-text {
display: flex;
@@ -119,22 +119,22 @@
flex-grow: 1;
display: block;
word-wrap: break-word;
max-width: 100px;
margin-right: 4px;
max-width: 13vw;
margin-right: 0.5vw;
}
.minicard .minicard-custom-field-item-fullwidth {
flex-grow: 1;
display: block;
word-wrap: break-word;
max-width: 100%;
margin-right: 4px;
margin-right: 0.5vw;
}
.minicard .handle {
width: 20px;
height: 20px;
width: clamp(20px, 2.5vw, 28px);
height: clamp(20px, 2.5vw, 28px);
position: absolute;
right: 5px;
top: 5px;
right: 0.7vw;
top: 0.7vh;
display: none;
}
@media only screen {
@@ -143,13 +143,13 @@
}
}
.minicard .handle .fa-arrows {
font-size: 20px;
font-size: clamp(16px, 3vw, 20px);
color: #ccc;
}
.minicard .minicard-title .card-number {
color: #b3b3b3;
display: inline-block;
margin-right: 5px;
margin-right: 0.7vw;
}
@media only screen {
.minicard .minicard-title p:last-child {
@@ -166,11 +166,85 @@
flex-wrap: wrap;
}
.minicard .date {
margin-right: 3px;
margin-right: 0.4vw;
}
/* Unicode icons for minicard dates - matching cardDate.css */
.minicard .card-date.end-date time::before {
content: "🏁"; /* Finish flag - represents end/completion */
}
.minicard .card-date.due-date time::before {
content: "⏰"; /* Alarm clock - represents due/deadline */
}
.minicard .card-date.start-date time::before {
content: "🚀"; /* Rocket - represents start/launch */
}
.minicard .card-date.received-date time::before {
content: "📥"; /* Inbox tray - represents received/incoming */
}
.minicard .card-date time::before {
font-size: inherit;
margin-right: 0.3em;
display: inline-block;
}
/* Date type specific colors for minicards - matching cardDate.css */
.minicard .card-date.received-date {
background-color: #dbdbdb; /* Grey for received - same as base card-date */
}
.minicard .card-date.received-date:hover,
.minicard .card-date.received-date.is-active {
background-color: #b3b3b3;
}
.minicard .card-date.start-date {
background-color: #90ee90; /* Light green for start */
color: #000; /* Black text for start */
}
.minicard .card-date.start-date:hover,
.minicard .card-date.start-date.is-active {
background-color: #7dd87d;
}
.minicard .card-date.due-date {
background-color: #ffd700; /* Yellow for due */
color: #000; /* Black text for due */
}
.minicard .card-date.due-date:hover,
.minicard .card-date.due-date.is-active {
background-color: #e6c200;
}
.minicard .card-date.end-date {
background-color: #ffb3b3; /* Light red for end */
color: #000; /* Black text for end */
}
.minicard .card-date.end-date:hover,
.minicard .card-date.end-date.is-active {
background-color: #ff9999;
}
/* Font Awesome icons in minicard dates */
.minicard .card-date i.fa {
margin-right: 0.3vw;
font-size: 0.9em;
vertical-align: middle;
}
/* Font Awesome icons in minicard spent time */
.minicard .card-time i.fa {
margin-right: 0.3vw;
font-size: 0.9em;
vertical-align: middle;
}
.minicard .badges {
float: left;
margin-top: 7px;
margin-top: 1vh;
color: #808080;
}
.minicard .badges:empty {
@@ -178,14 +252,14 @@
}
.minicard .badges .badge {
float: left;
margin-right: 11px;
margin-bottom: 3px;
margin-right: 1.5vw;
margin-bottom: 0.4vh;
font-size: 0.9em;
}
.minicard .badges .badge.is-finished {
background: #3cb500;
padding: 0px 3px;
border-radius: 3px;
padding: 0 0.4vw;
border-radius: 0.4vw;
color: #fff;
}
.minicard .badges .badge:last-of-type {
@@ -201,41 +275,41 @@
}
.minicard .badges .badge .badge-text {
font-size: 0.9em;
padding-left: 2px;
line-height: 14px;
padding-left: 0.3vw;
line-height: 1.2;
}
.minicard .badges .badge .check-list-text {
padding-left: 0px;
line-height: 12px;
line-height: 1.1;
}
.minicard .minicard-members,
.minicard .minicard-assignees,
.minicard .minicard-creator {
float: right;
margin-left: 5px;
margin-bottom: 4px;
margin-left: 0.7vw;
margin-bottom: 0.5vh;
}
.minicard .minicard-members .member,
.minicard .minicard-assignees .member,
.minicard .minicard-creator .member {
float: right;
border-radius: 50%;
height: 28px;
width: 28px;
margin-bottom: 4px;
height: clamp(24px, 3.5vw, 32px);
width: clamp(24px, 3.5vw, 32px);
margin-bottom: 0.5vh;
}
.minicard .minicard-members .assignee,
.minicard .minicard-assignees .assignee,
.minicard .minicard-creator .assignee {
float: right;
border-radius: 50%;
height: 28px;
width: 28px;
height: clamp(24px, 3.5vw, 32px);
width: clamp(24px, 3.5vw, 32px);
}
.minicard .minicard-members + .badges,
.minicard .minicard-assignees + .badges,
.minicard .minicard-creator + .badges {
margin-top: 5px;
margin-top: 0.7vh;
}
.minicard .minicard-assignees {
border-bottom: 1px solid #f00;
@@ -248,13 +322,13 @@
display: none;
}
.minicard .minicard-description {
padding: 6px 0 0 8px;
padding: 0.8vh 0 0 1vw;
color: #000;
background-color: #eee;
width: 100%;
margin-bottom: 2px;
margin-left: -4px;
border-radius: 3px;
margin-bottom: 0.3vh;
margin-left: -0.5vw;
border-radius: 0.4vw;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
@@ -262,7 +336,7 @@
-webkit-box-orient: vertical;
}
.minicard.minicard-composer {
margin-bottom: 10px;
margin-bottom: 1.3vh;
}
.minicard.minicard-composer textarea.minicard-composer-textarea,
.minicard.minicard-composer textarea.minicard-composer-textarea:focus {
@@ -273,9 +347,9 @@
height: auto;
margin: 0;
padding: 0;
max-height: 162px;
min-height: 36px;
margin-bottom: 20px;
max-height: 22vh;
min-height: 5vh;
margin-bottom: 2.5vh;
overflow-y: auto;
}
.parent-prefix {
@@ -580,3 +654,26 @@
.text-green {
color: #008000;
}
/* Drag and drop file upload visual feedback */
.minicard.is-dragging-over {
border: 2px dashed #0079bf;
background-color: #e3f2fd !important;
transform: scale(1.02);
transition: all 0.2s ease;
}
/* List name display on minicard */
.minicard-list-name {
font-size: 0.75em;
color: #8c8c8c;
margin-top: 0.2vh;
display: flex;
align-items: center;
gap: 0.3vw;
}
.minicard-list-name i.fa {
font-size: 0.8em;
opacity: 0.7;
}
+67 -39
View File
@@ -4,19 +4,13 @@ template(name="minicard")
class="{{#if isLinkedBoard}}linked-board{{/if}}"
class="{{#if colorClass}}minicard-{{colorClass}}{{/if}}")
if canModifyCard
if isTouchScreenOrShowDesktopDragHandles
a.fa.fa-navicon.minicard-details-menu-with-handle.js-open-minicard-details-menu(title="{{_ 'cardDetailsActionsPopup-title'}}")
.handle
.fa.fa-arrows
else
a.fa.fa-navicon.minicard-details-menu.js-open-minicard-details-menu(title="{{_ 'cardDetailsActionsPopup-title'}}")
a.minicard-details-menu-with-handle.js-open-minicard-details-menu(title="{{_ 'cardDetailsActionsPopup-title'}}") ☰
.handle
| ↕️
.dates
if getReceived
unless getStart
unless getDue
unless getEnd
.date
+minicardReceivedDate
.date
+minicardReceivedDate
if getStart
.date
+minicardStartDate
@@ -31,6 +25,27 @@ template(name="minicard")
if cover
if currentBoard.allowsCoverAttachmentOnMinicard
.minicard-cover(style="background-image: url('{{cover.link 'original'}}?dummyReloadAfterSessionEstablished={{sess}}');")
// Upload progress indicator for drag-and-drop uploads
if hasActiveUploads
.minicard-upload-progress
.upload-progress-header
| 📤
span {{_ 'uploading-files'}} ({{uploadCount}})
each uploads
.upload-progress-item(class="{{#if $eq status 'error'}}upload-error{{/if}}")
.upload-progress-filename {{file.name}}
.upload-progress-bar
.upload-progress-fill(style="width: {{progress}}%")
if $eq status 'error'
.upload-progress-error
| ⚠️
span {{_ 'upload-failed'}}
else if $eq status 'completed'
.upload-progress-success
| ✅
span {{_ 'upload-completed'}}
.minicard-title
if $eq 'prefix-with-full-path' currentBoard.presentParentTask
.parent-prefix
@@ -40,23 +55,17 @@ template(name="minicard")
| {{ parentCardName }}
if isLinkedBoard
a.js-linked-link
span.linked-icon.fa.fa-folder
span.linked-icon | 📁
else if isLinkedCard
a.js-linked-link
span.linked-icon.fa.fa-id-card
span.linked-icon | 🃏
if getArchived
span.linked-icon.linked-archived.fa.fa-archive
span.linked-icon.linked-archived | 📦
+viewer
if currentBoard.allowsCardNumber
span.card-number
| ##{getCardNumber}
= getTitle
if $eq 'subtext-with-full-path' currentBoard.presentParentTask
.parent-subtext
| {{ parentString ' > ' }}
if $eq 'subtext-with-parent' currentBoard.presentParentTask
.parent-subtext
| {{ parentCardName }}
if labels
.minicard-labels(class="{{#if hiddenMinicardLabelText}}minicard-labels-no-text{{/if}}")
each labels
@@ -132,7 +141,7 @@ template(name="minicard")
if canModifyCard
if comments.length
.badge(title="{{_ 'card-comments-title' comments.length }}")
span.badge-icon.fa.fa-comment-o.badge-comment.badge-text
span.badge-icon.badge-comment.badge-text | 💬
= ' '
= comments.length
//span.badge-comment.badge-text
@@ -140,42 +149,52 @@ template(name="minicard")
if getDescription
unless currentBoard.allowsDescriptionTextOnMinicard
.badge.badge-state-image-only(title=getDescription)
span.badge-icon.fa.fa-align-left
span.badge-icon | 📝
if getVoteQuestion
.badge.badge-state-image-only(title=getVoteQuestion)
span.badge-icon.fa.fa-thumbs-up(class="{{#if voteState}}text-green{{/if}}")
span.badge-icon(class="{{#if voteState}}text-green{{/if}}") | 👍
span.badge-text {{ voteCountPositive }}
span.badge-icon.fa.fa-thumbs-down(class="{{#if $eq voteState false}}text-red{{/if}}")
span.badge-icon(class="{{#if $eq voteState false}}text-red{{/if}}") | 👎
span.badge-text {{ voteCountNegative }}
if getPokerQuestion
.badge.badge-state-image-only(title=getPokerQuestion)
span.badge-icon.fa.fa-check(class="{{#if pokerState}}text-green{{/if}}")
span.badge-icon(class="{{#if pokerState}}text-green{{/if}}") | ✅
if expiredPoker
span.badge-text {{ getPokerEstimation }}
if attachments.length
if currentBoard.allowsBadgeAttachmentOnMinicard
.badge
span.badge-icon.fa.fa-paperclip
span.badge-icon | 📎
span.badge-text= attachments.length
if checklists.length
.badge(class="{{#if checklistFinished}}is-finished{{/if}}")
span.badge-icon.fa.fa-check-square-o
span.badge-icon | ☑️
span.badge-text.check-list-text {{checklistFinishedCount}}/{{checklistItemCount}}
if allSubtasks.count
.badge
span.badge-icon.fa.fa-sitemap
span.badge-icon | 🌐
span.badge-text.check-list-text {{subtasksFinishedCount}}/{{allSubtasksCount}}
//{{subtasksFinishedCount}}/{{subtasksCount}} does not work because when a subtaks is archived, the count goes down
if currentBoard.allowsCardSortingByNumber
if currentBoard.allowsCardSortingByNumberOnMinicard
.badge
span.badge-icon.fa.fa-sort
span.badge-icon | 🔢
span.badge-text.check-list-sort {{ sort }}
if currentBoard.allowsDescriptionTextOnMinicard
if getDescription
.minicard-description
+viewer
| {{ getDescription }}
if shouldShowListOnMinicard
.minicard-list-name
| 📋
| {{ listName }}
if $eq 'subtext-with-full-path' currentBoard.presentParentTask
.parent-subtext
| {{ parentString ' > ' }}
if $eq 'subtext-with-parent' currentBoard.presentParentTask
.parent-subtext
| {{ parentCardName }}
template(name="editCardSortOrderPopup")
input.js-edit-card-sort-popup(type='text' autofocus value=sort dir="auto")
@@ -187,41 +206,50 @@ template(name="minicardDetailsActionsPopup")
if canModifyCard
li
a.js-move-card
i.fa.fa-arrow-right
| ➡️
| {{_ 'moveCardPopup-title'}}
li
a.js-copy-card
i.fa.fa-copy
| 📋
| {{_ 'copyCardPopup-title'}}
hr
li
a.js-archive
i.fa.fa-arrow-right
i.fa.fa-archive
| ➡️
| 📦
| {{_ 'archive-card'}}
hr
li
a.js-move-card-to-top
i.fa.fa-arrow-up
| ⬆️
| {{_ 'moveCardToTop-title'}}
li
a.js-move-card-to-bottom
i.fa.fa-arrow-down
| ⬇️
| {{_ 'moveCardToBottom-title'}}
hr
li
a.js-add-labels
i.fa.fa-tags
| 🏷️
| {{_ 'card-edit-labels'}}
li
a.js-due-date
i.fa.fa-sign-in
| 📥
| {{_ 'editCardDueDatePopup-title'}}
li
a.js-set-card-color
i.fa.fa-paint-brush
| 🎨
| {{_ 'setCardColorPopup-title'}}
li
a.js-link
i.fa.fa-link
| 🔗
| {{_ 'link-card'}}
li
a.js-toggle-watch-card
if isWatching
| 👁️
| {{_ 'unwatch'}}
else
| 👁️-slash
| {{_ 'watch'}}
+96 -1
View File
@@ -1,6 +1,8 @@
import { ReactiveCache } from '/imports/reactiveCache';
import { TAPi18n } from '/imports/i18n';
import { CustomFieldStringTemplate } from '/client/lib/customFields'
import { CustomFieldStringTemplate } from '/client/lib/customFields';
import { handleFileUpload } from './attachments';
import uploadProgressManager from '../../lib/uploadProgressManager';
// Template.cards.events({
// 'click .member': Popup.open('cardMember')
@@ -46,6 +48,10 @@ BlazeComponent.extendComponent({
}
return ret;
},
isWatching() {
const card = this.currentData();
return card.findWatcher(Meteor.userId());
},
showMembers() {
// cache "board" to reduce the mini-mongodb access
@@ -103,6 +109,62 @@ BlazeComponent.extendComponent({
'click span.badge-icon.fa.fa-sort, click span.badge-text.check-list-sort' : Popup.open("editCardSortOrder"),
'click .minicard-labels' : this.cardLabelsPopup,
'click .js-open-minicard-details-menu': Popup.open('minicardDetailsActions'),
// Drag and drop file upload handlers
'dragover .minicard'(event) {
// Only prevent default for file drags to avoid interfering with sortable
const dataTransfer = event.originalEvent.dataTransfer;
if (dataTransfer && dataTransfer.types && dataTransfer.types.includes('Files')) {
event.preventDefault();
event.stopPropagation();
}
},
'dragenter .minicard'(event) {
const dataTransfer = event.originalEvent.dataTransfer;
if (dataTransfer && dataTransfer.types && dataTransfer.types.includes('Files')) {
event.preventDefault();
event.stopPropagation();
const card = this.data();
const board = card.board();
// Only allow drag-and-drop if user can modify card and board allows attachments
if (Utils.canModifyCard() && board && board.allowsAttachments) {
$(event.currentTarget).addClass('is-dragging-over');
}
}
},
'dragleave .minicard'(event) {
const dataTransfer = event.originalEvent.dataTransfer;
if (dataTransfer && dataTransfer.types && dataTransfer.types.includes('Files')) {
event.preventDefault();
event.stopPropagation();
$(event.currentTarget).removeClass('is-dragging-over');
}
},
'drop .minicard'(event) {
const dataTransfer = event.originalEvent.dataTransfer;
if (dataTransfer && dataTransfer.types && dataTransfer.types.includes('Files')) {
event.preventDefault();
event.stopPropagation();
$(event.currentTarget).removeClass('is-dragging-over');
const card = this.data();
const board = card.board();
// Check permissions
if (!Utils.canModifyCard() || !board || !board.allowsAttachments) {
return;
}
// Check if this is a file drop (not a card reorder)
if (!dataTransfer.files || dataTransfer.files.length === 0) {
return;
}
const files = dataTransfer.files;
if (files && files.length > 0) {
handleFileUpload(card, files);
}
}
},
}
];
},
@@ -125,6 +187,32 @@ Template.minicard.helpers({
? Meteor.connection._lastSessionId
: null;
},
isWatching() {
return this.findWatcher(Meteor.userId());
},
// Upload progress helpers
hasActiveUploads() {
return uploadProgressManager.hasActiveUploads(this._id);
},
uploads() {
return uploadProgressManager.getUploadsForCard(this._id);
},
uploadCount() {
return uploadProgressManager.getUploadCountForCard(this._id);
},
listName() {
const list = this.list();
return list ? list.title : '';
},
shouldShowListOnMinicard() {
// Show list name if either:
// 1. Board-wide setting is enabled, OR
// 2. This specific card has the setting enabled
const currentBoard = this.currentBoard;
if (!currentBoard) return false;
return currentBoard.allowsShowListsOnMinicard || this.showListOnMinicard;
}
});
BlazeComponent.extendComponent({
@@ -178,4 +266,11 @@ Template.minicardDetailsActionsPopup.events({
this.archive();
Utils.goBoardId(this.boardId);
}),
'click .js-toggle-watch-card'() {
const currentCard = this;
const level = currentCard.findWatcher(Meteor.userId()) ? null : 'watching';
Meteor.call('watch', 'card', currentCard._id, level, (err, ret) => {
if (!err && ret) Popup.back();
});
},
});
+3 -3
View File
@@ -13,7 +13,7 @@ template(name="resultCard")
.broken-cards-null
| NULL
if getBoard.archived
i.fa.fa-archive
| 📦
li.result-card-context.result-card-context-separator
= ' '
| {{_ 'context-separator'}}
@@ -27,7 +27,7 @@ template(name="resultCard")
.broken-cards-null
| NULL
if getSwimlane.archived
i.fa.fa-archive
| 📦
li.result-card-context.result-card-context-separator
= ' '
| {{_ 'context-separator'}}
@@ -41,4 +41,4 @@ template(name="resultCard")
.broken-cards-null
| NULL
if getList.archived
i.fa.fa-archive
| 📦
+8 -8
View File
@@ -1,6 +1,6 @@
template(name="subtasks")
h3.card-details-item-title
i.fa.fa-sitemap
| 🌐
| {{_ 'subtasks'}}
if currentUser.isBoardAdmin
if toggleDeleteDialog.get
@@ -16,7 +16,7 @@ template(name="subtasks")
+addSubtaskItemForm
else
a.js-open-inlined-form(title="{{_ 'add-subtask'}}")
i.fa.fa-plus
| ➕
template(name="subtaskDetail")
.js-subtasks.subtask
@@ -26,7 +26,7 @@ template(name="subtaskDetail")
.subtask-title
span
if canModifyCard
a.fa.fa-navicon.subtask-details-menu.js-open-subtask-details-menu(title="{{_ 'subtaskActionsPopup-title'}}")
a.subtask-details-menu.js-open-subtask-details-menu(title="{{_ 'subtaskActionsPopup-title'}}")
if canModifyCard
h2.title.js-open-inlined-form.is-editable
+viewer
@@ -40,7 +40,7 @@ template(name="addSubtaskItemForm")
textarea.js-add-subtask-item(rows='1' autofocus dir="auto")
.edit-controls.clearfix
button.primary.confirm.js-submit-add-subtask-item-form(type="submit") {{_ 'save'}}
a.fa.fa-times-thin.js-close-inlined-form
a.js-close-inlined-form
template(name="editSubtaskItemForm")
textarea.js-edit-subtask-item(rows='1' autofocus dir="auto")
@@ -50,7 +50,7 @@ template(name="editSubtaskItemForm")
= subtask.title
.edit-controls.clearfix
button.primary.confirm.js-submit-edit-subtask-item-form(type="submit") {{_ 'save'}}
a.fa.fa-times-thin.js-close-inlined-form
a.js-close-inlined-form
span(title=createdAt) {{ moment createdAt }}
if canModifyCard
if currentUser.isBoardAdmin
@@ -68,7 +68,7 @@ template(name="subtasksItems")
+addSubtaskItemForm
else
a.add-subtask-item.js-open-inlined-form
i.fa.fa-plus
| ➕
| {{_ 'add-subtask-item'}}...
template(name='subtaskItemDetail')
@@ -92,10 +92,10 @@ template(name="subtaskActionsPopup")
ul.pop-over-list
li
a.js-view-subtask(title="{{ subtask.title }}")
i.fa.fa-eye
| 👁️
| {{_ "view-it"}}
if currentUser.isBoardAdmin
a.js-delete-subtask.delete-subtask
i.fa.fa-trash
| 🗑️
| {{_ "delete"}} ...
@@ -0,0 +1,123 @@
/* Original Position Component Styles */
.original-position-info {
margin: 5px 0;
padding: 8px;
border-radius: 4px;
font-size: 12px;
line-height: 1.4;
}
.original-position-loading {
color: #666;
font-style: italic;
}
.original-position-loading i {
margin-right: 5px;
}
.original-position-details {
background-color: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 3px;
padding: 6px 8px;
}
.original-position-moved {
color: #856404;
background-color: #fff3cd;
border: 1px solid #ffeaa7;
border-radius: 3px;
padding: 4px 6px;
margin-bottom: 4px;
}
.original-position-moved i {
color: #f39c12;
margin-right: 5px;
}
.original-position-unchanged {
color: #155724;
background-color: #d4edda;
border: 1px solid #c3e6cb;
border-radius: 3px;
padding: 4px 6px;
margin-bottom: 4px;
}
.original-position-unchanged i {
color: #28a745;
margin-right: 5px;
}
.original-position-text {
font-weight: 500;
}
.original-title {
color: #6c757d;
font-size: 11px;
margin-top: 4px;
padding-top: 4px;
border-top: 1px solid #e9ecef;
}
.original-title strong {
color: #495057;
}
/* Integration with existing Wekan styles */
.swimlane .original-position-info,
.list .original-position-info,
.card .original-position-info {
margin: 2px 0;
padding: 4px 6px;
}
/* Responsive adjustments */
@media (max-width: 768px) {
.original-position-info {
font-size: 11px;
padding: 6px;
}
.original-position-details {
padding: 4px 6px;
}
.original-position-moved,
.original-position-unchanged {
padding: 3px 5px;
}
}
/* Dark theme support */
@media (prefers-color-scheme: dark) {
.original-position-details {
background-color: #2d3748;
border-color: #4a5568;
color: #e2e8f0;
}
.original-position-moved {
background-color: #744210;
border-color: #b7791f;
color: #fbd38d;
}
.original-position-unchanged {
background-color: #22543d;
border-color: #38a169;
color: #9ae6b4;
}
.original-title {
color: #a0aec0;
border-color: #4a5568;
}
.original-title strong {
color: #e2e8f0;
}
}
@@ -0,0 +1,29 @@
<template name="originalPosition">
<div class="original-position-info">
{{#if isLoading}}
<div class="original-position-loading">
<i class="fa fa-spinner fa-spin"></i> Loading original position...
</div>
{{else if showOriginalPosition}}
<div class="original-position-details">
{{#if hasMovedFromOriginal}}
<div class="original-position-moved">
<i class="fa fa-info-circle"></i>
<span class="original-position-text">{{getOriginalPositionDescription}}</span>
</div>
{{else}}
<div class="original-position-unchanged">
<i class="fa fa-check-circle"></i>
<span class="original-position-text">In original position</span>
</div>
{{/if}}
{{#if getOriginalTitle}}
<div class="original-title">
<strong>Original title:</strong> {{getOriginalTitle}}
</div>
{{/if}}
</div>
{{/if}}
</div>
</template>
@@ -0,0 +1,98 @@
import { BlazeComponent } from 'meteor/peerlibrary:blaze-components';
import { ReactiveVar } from 'meteor/reactive-var';
import { Meteor } from 'meteor/meteor';
import { Template } from 'meteor/templating';
import './originalPosition.html';
/**
* Component to display original position information for swimlanes, lists, and cards
*/
class OriginalPositionComponent extends BlazeComponent {
onCreated() {
super.onCreated();
this.originalPosition = new ReactiveVar(null);
this.isLoading = new ReactiveVar(false);
this.hasMoved = new ReactiveVar(false);
this.autorun(() => {
const data = this.data();
if (data && data.entityId && data.entityType) {
this.loadOriginalPosition(data.entityId, data.entityType);
}
});
}
loadOriginalPosition(entityId, entityType) {
this.isLoading.set(true);
const methodName = `positionHistory.get${entityType.charAt(0).toUpperCase() + entityType.slice(1)}OriginalPosition`;
Meteor.call(methodName, entityId, (error, result) => {
this.isLoading.set(false);
if (error) {
console.error('Error loading original position:', error);
this.originalPosition.set(null);
} else {
this.originalPosition.set(result);
// Check if the entity has moved
const movedMethodName = `positionHistory.has${entityType.charAt(0).toUpperCase() + entityType.slice(1)}Moved`;
Meteor.call(movedMethodName, entityId, (movedError, movedResult) => {
if (!movedError) {
this.hasMoved.set(movedResult);
}
});
}
});
}
getOriginalPosition() {
return this.originalPosition.get();
}
isLoading() {
return this.isLoading.get();
}
hasMovedFromOriginal() {
return this.hasMoved.get();
}
getOriginalPositionDescription() {
const position = this.getOriginalPosition();
if (!position) return 'No original position data';
if (position.originalPosition) {
const entityType = this.data().entityType;
let description = `Original position: ${position.originalPosition.sort || 0}`;
if (entityType === 'list' && position.originalSwimlaneId) {
description += ` in swimlane ${position.originalSwimlaneId}`;
} else if (entityType === 'card') {
if (position.originalSwimlaneId) {
description += ` in swimlane ${position.originalSwimlaneId}`;
}
if (position.originalListId) {
description += ` in list ${position.originalListId}`;
}
}
return description;
}
return 'No original position data';
}
getOriginalTitle() {
const position = this.getOriginalPosition();
return position ? position.originalTitle : '';
}
showOriginalPosition() {
return this.getOriginalPosition() !== null;
}
}
OriginalPositionComponent.register('originalPosition');
export default OriginalPositionComponent;
+2 -3
View File
@@ -4,11 +4,10 @@ template(name="datepicker")
.fields
.left
label(for="date") {{_ 'date'}}
input.js-date-field#date(type="text" name="date" value=showDate placeholder=dateFormat autofocus)
input.js-date-field#date(type="date" name="date" value=showDate autofocus)
.right
label(for="time") {{_ 'time'}}
input.js-time-field#time(type="text" name="time" value=showTime placeholder=timeFormat)
.js-datepicker
input.js-time-field#time(type="time" name="time" value=showTime)
if error.get
.warning {{_ error.get}}
button.primary.wide.left.js-submit-date(type="submit") {{_ 'save'}}
+44 -39
View File
@@ -5,11 +5,11 @@ button {
box-sizing: border-box;
background-color: #ebebeb;
border: 1px solid #ccc;
border-radius: 3px;
border-radius: 0.4vw;
display: block;
margin-bottom: 12px;
min-height: 34px;
padding: 7px;
margin-bottom: 1.5vh;
min-height: 4.5vh;
padding: 1vh 1vw;
}
select.full,
textarea.full,
@@ -31,17 +31,18 @@ button:focus {
outline: 0;
}
input[type="file"] {
margin-bottom: 16px;
margin-bottom: 2vh;
}
input[type="radio"] {
-webkit-appearance: radio;
appearance: radio;
min-height: inherit;
}
input[type="text"],
input[type="password"],
input[type="email"] {
transition: background 85ms ease-in, border-color 85ms ease-in;
width: 250px;
width: min(250px, 30vw);
}
input[type="text"].inline-input,
input[type="password"].inline-input,
@@ -49,10 +50,10 @@ input[type="email"].inline-input {
background: none;
border: 0;
margin: 0;
padding: 2px;
padding: 0.3vh;
min-height: 0;
height: 18px;
width: 200px;
height: 2.5vh;
width: min(200px, 25vw);
}
input[type="text"].full-line,
input[type="password"].full-line,
@@ -96,30 +97,31 @@ input[type="email"]:disabled,
textarea:disabled {
background-color: #dcdcdc;
border-color: #bfbfbf;
color: #8c8c8c;
color: #222;
-webkit-touch-callout: none;
-webkit-user-select: none;
user-select: none;
}
select {
max-height: 300px;
width: 256px;
margin-bottom: 8px;
max-height: 40vh;
width: min(256px, 32vw);
margin-bottom: 1vh;
}
select.inline {
width: 100%;
}
option[disabled] {
color: #8c8c8c;
color: #222;
}
textarea {
height: 150px;
height: 20vh;
transition: background 85ms ease-in, border-color 85ms ease-in;
resize: vertical;
width: 100%;
}
textarea.editor {
resize: none;
padding-bottom: 22px;
padding-bottom: 3vh;
}
.button {
border-radius: 3px;
@@ -134,14 +136,14 @@ button {
cursor: pointer;
display: inline-block;
font-weight: 700;
line-height: 22px;
padding: 7px 20px;
line-height: 1.3;
padding: 1vh 2.5vw;
text-align: center;
}
input[type="submit"] .wide,
button .wide {
padding-left: 30px;
padding-right: 30px;
padding-left: 4vw;
padding-right: 4vw;
}
input[type="submit"]:hover,
button:hover,
@@ -222,8 +224,8 @@ button.disabled:active,
}
fieldset {
border: 1px solid #bfbfbf;
padding: 15px;
margin-bottom: 15px;
padding: 2vh 2vw;
margin-bottom: 2vh;
}
input[type="hidden"] {
display: none;
@@ -231,18 +233,18 @@ input[type="hidden"] {
.radio-div,
.check-div {
display: block;
margin: 0 0 4px 20px;
min-height: 20px;
margin: 0 0 0.5vh 2.5vw;
min-height: 2.5vh;
position: relative;
}
.radio-div input,
.check-div input {
left: -18px;
left: -2.5vw;
min-height: 0;
margin: 0;
padding: 0;
position: absolute;
top: 2px;
top: 0.3vh;
}
.radio-div label,
.check-div label {
@@ -251,23 +253,23 @@ input[type="hidden"] {
label {
display: block;
font-weight: 700;
margin-bottom: 4px;
margin-bottom: 0.5vh;
}
label.form-error {
color: #ba1212;
color: #d32f2f;
}
input::-webkit-input-placeholder,
textarea::-webkit-input-placeholder,
input::-moz-placeholder,
textarea::-moz-placeholder {
color: #8c8c8c;
color: #333 !important;
}
.edit-controls,
.add-controls {
display: flex;
align-items: center;
margin-top: 0px;
margin-bottom: 12px;
margin-bottom: 1.5vh;
}
@media print {
.add-controls {
@@ -279,13 +281,13 @@ textarea::-moz-placeholder {
.edit-controls input[type=button],
.add-controls input[type=button] {
float: left;
height: 32px;
height: 4.5vh;
margin-bottom: 0px;
}
.edit-controls .fa-times-thin,
.add-controls .fa-times-thin {
font-size: 26px;
margin: 3px 10px;
font-size: clamp(20px, 4vw, 26px);
margin: 0.5vh 1.5vw;
}
[type="checkbox"]:not(:checked),
[type="checkbox"]:checked {
@@ -315,6 +317,7 @@ textarea::-moz-placeholder {
border-left: 2px solid transparent;
transform: rotate(40deg);
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
transform-origin: 100% 100%;
}
.button-link {
@@ -322,6 +325,7 @@ textarea::-moz-placeholder {
background: linear-gradient(#fff, #f5f5f5);
border-radius: 3px;
box-sizing: border-box;
-webkit-user-select: none;
user-select: none;
border: 1px solid #e3e3e3;
border-bottom-color: #c2c2c2;
@@ -374,14 +378,14 @@ textarea::-moz-placeholder {
.button-link.setting.disabled {
background: #fff;
border-color: #e9e9e9;
color: #8c8c8c;
color: #222;
cursor: default;
}
.button-link.setting.disabled select {
display: none;
}
.button-link.setting.disabled:hover .label {
color: #8c8c8c;
color: #222;
}
.button-link.setting.disabled,
.button-link.setting.disabled:hover,
@@ -397,7 +401,7 @@ textarea::-moz-placeholder {
color: #a8a8a8;
}
.button-link.setting .label {
color: #8c8c8c;
color: #222;
display: block;
font-size: 12px;
line-height: 14px;
@@ -505,8 +509,9 @@ button.loud-text-button:hover {
.emphasis-button,
.quiet-button {
border-radius: 3px;
-webkit-user-select: none;
user-select: none;
color: #8c8c8c;
color: #222;
display: block;
margin: 2px 0;
padding: 6px 8px;
@@ -571,7 +576,7 @@ button.loud-text-button:hover {
top: 6px;
}
.big-link.none {
color: #8c8c8c;
color: #222;
text-decoration: none;
}
.big-link.none:hover {
@@ -601,7 +606,7 @@ button.loud-text-button:hover {
}
.show-more {
border-radius: 3px;
color: #8c8c8c;
color: #222;
display: block;
padding: 16px 8px 16px 40px;
margin: 8px 0;
+8 -8
View File
@@ -5,17 +5,17 @@
.map-members .mapping-list {
display: flex;
flex-wrap: wrap;
margin: 0 -4px;
margin: 0 -0.5vw;
}
.map-members .mapping-list .mapping-item {
max-width: 300px;
min-width: 200px;
padding: 6px;
margin: 5px;
max-width: min(300px, 40vw);
min-width: min(200px, 26vw);
padding: 0.8vh 0.8vw;
margin: 0.7vh;
flex: 1;
background: #fff;
border-radius: 3px;
box-shadow: 0 1px 2px rgba(0,0,0,0.15);
border-radius: 0.4vw;
box-shadow: 0 0.2vh 0.3vh rgba(0,0,0,0.15);
}
.map-members .mapping-list .mapping-item:hover {
background: #f2f2f2;
@@ -37,7 +37,7 @@
}
.map-members .mapping-list .wekan {
display: inline-block;
width: 35px;
width: 4.5vw;
}
.map-members .mapping-list .wekan .member {
float: none;
+735 -32
View File
@@ -8,8 +8,230 @@
padding: 0;
float: left;
}
/* List resize handle */
.list-resize-handle {
position: absolute;
top: 0;
right: -3px;
width: 6px;
height: 100%;
cursor: col-resize;
z-index: 10;
background: transparent;
transition: background-color 0.2s ease;
border-radius: 2px;
/* Ensure the handle is clickable */
pointer-events: auto;
}
.list-resize-handle:hover {
background: rgba(0, 123, 255, 0.4);
box-shadow: 0 0 4px rgba(0, 123, 255, 0.3);
}
.list-resize-handle:active {
background: rgba(0, 123, 255, 0.6);
box-shadow: 0 0 6px rgba(0, 123, 255, 0.4);
}
/* Show resize handle only on hover */
.list:hover .list-resize-handle {
background: rgba(0, 0, 0, 0.1);
}
.list:hover .list-resize-handle:hover {
background: rgba(0, 123, 255, 0.4);
box-shadow: 0 0 4px rgba(0, 123, 255, 0.3);
}
/* Add a subtle indicator line */
.list-resize-handle::before {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 2px;
height: 20px;
background: rgba(0, 0, 0, 0.2);
border-radius: 1px;
opacity: 0;
transition: opacity 0.2s ease;
}
.list-resize-handle:hover::before {
opacity: 1;
}
/* Disable resize handle for collapsed lists and mobile view */
.list.list-collapsed .list-resize-handle,
.list.mobile-view .list-resize-handle {
display: none;
}
/* Disable resize handle for auto-width lists */
.list.list-auto-width .list-resize-handle {
display: none;
}
/* Visual feedback during resize */
.list.list-resizing {
transition: none !important;
box-shadow: 0 0 10px rgba(0, 123, 255, 0.3);
/* Ensure the list maintains its new width during resize */
flex: none !important;
flex-basis: auto !important;
flex-grow: 0 !important;
flex-shrink: 0 !important;
/* Override any conflicting layout properties */
float: left !important;
display: block !important;
position: relative !important;
/* Force width to be respected */
width: var(--list-width, auto) !important;
min-width: var(--list-width, auto) !important;
max-width: var(--list-width, auto) !important;
/* Ensure the width is applied immediately */
overflow: visible !important;
}
body.list-resizing-active {
cursor: col-resize !important;
}
body.list-resizing-active * {
cursor: col-resize !important;
}
/* Ensure swimlane container doesn't interfere with list resizing */
.swimlane .list.list-resizing {
/* Override any swimlane flex properties */
flex: none !important;
flex-basis: auto !important;
flex-grow: 0 !important;
flex-shrink: 0 !important;
/* Ensure width is respected */
width: var(--list-width, auto) !important;
min-width: var(--list-width, auto) !important;
max-width: var(--list-width, auto) !important;
}
/* More aggressive override for any container that might interfere */
.js-swimlane .list.list-resizing,
.dragscroll .list.list-resizing,
[id^="swimlane-"] .list.list-resizing {
/* Force the width to be applied */
width: var(--list-width, auto) !important;
min-width: var(--list-width, auto) !important;
max-width: var(--list-width, auto) !important;
flex: none !important;
flex-basis: auto !important;
flex-grow: 0 !important;
flex-shrink: 0 !important;
float: left !important;
display: block !important;
}
/* Ensure the width persists after resize is complete */
.js-swimlane .list[style*="--list-width"],
.dragscroll .list[style*="--list-width"],
[id^="swimlane-"] .list[style*="--list-width"] {
/* Maintain the width after resize */
width: var(--list-width, auto) !important;
min-width: var(--list-width, auto) !important;
max-width: var(--list-width, auto) !important;
flex: none !important;
flex-basis: auto !important;
flex-grow: 0 !important;
flex-shrink: 0 !important;
float: left !important;
display: block !important;
}
/* Ensure consistent header height for all lists */
.list-header {
/* Maintain consistent height and padding for all lists */
min-height: 2.5vh !important;
height: auto !important;
padding: 2.5vh 1.5vw 0.5vh !important;
/* Make sure the background covers the full height */
background-color: #e4e4e4 !important;
border-bottom: 0.8vh solid #e4e4e4 !important;
/* Use original display for consistent button positioning */
display: block !important;
position: relative !important;
/* Prevent vertical expansion but allow normal height */
overflow: hidden !important;
}
/* Ensure title text doesn't cause height changes for all lists */
.list-header .list-header-name {
/* Prevent text wrapping to maintain consistent height */
white-space: nowrap !important;
/* Truncate text with ellipsis if too long */
text-overflow: ellipsis !important;
/* Ensure proper line height */
line-height: 1.2 !important;
/* Ensure it doesn't overflow */
overflow: hidden !important;
/* Add margin to prevent overlap with buttons */
margin-right: 120px !important;
}
/* Position drag handle at top-right corner for ALL lists */
.list-header .list-header-handle {
/* Position at top-right corner, aligned with title text top */
position: absolute !important;
top: 2.5vh !important;
right: 1.5vw !important;
/* Ensure it's above other elements */
z-index: 15 !important;
/* Remove margin since it's absolutely positioned */
margin-right: 0 !important;
/* Ensure proper display */
display: inline-block !important;
/* Ensure it's clickable and shows proper cursor */
cursor: move !important;
pointer-events: auto !important;
/* Add some padding for better clickability */
padding: 4px !important;
}
/* Ensure buttons maintain original positioning */
.js-swimlane .list[style*="--list-width"] .list-header .list-header-plus-top,
.js-swimlane .list[style*="--list-width"] .list-header .js-collapse,
.js-swimlane .list[style*="--list-width"] .list-header .js-open-list-menu,
.dragscroll .list[style*="--list-width"] .list-header .list-header-plus-top,
.dragscroll .list[style*="--list-width"] .list-header .js-collapse,
.dragscroll .list[style*="--list-width"] .list-header .js-open-list-menu,
[id^="swimlane-"] .list[style*="--list-width"] .list-header .list-header-plus-top,
[id^="swimlane-"] .list[style*="--list-width"] .list-header .js-collapse,
[id^="swimlane-"] .list[style*="--list-width"] .list-header .js-open-list-menu {
/* Use original positioning to maintain layout */
position: relative !important;
/* Maintain original spacing */
margin-right: 15px !important;
/* Ensure proper display */
display: inline-block !important;
}
/* Ensure watch icon and card count maintain original positioning */
.js-swimlane .list[style*="--list-width"] .list-header .list-header-watch-icon,
.dragscroll .list[style*="--list-width"] .list-header .list-header-watch-icon,
[id^="swimlane-"] .list[style*="--list-width"] .list-header .list-header-watch-icon,
.js-swimlane .list[style*="--list-width"] .list-header .cardCount,
.dragscroll .list[style*="--list-width"] .list-header .cardCount,
[id^="swimlane-"] .list[style*="--list-width"] .list-header .cardCount {
/* Use original positioning to maintain layout */
position: relative !important;
/* Maintain original spacing */
margin-right: 15px !important;
/* Ensure proper display */
display: inline-block !important;
}
[id^="swimlane-"] .list:first-child {
min-width: 20px;
min-width: 2.5vw;
}
.list.list-auto-width {
flex: 1;
@@ -33,11 +255,74 @@
background-color: rgba(0,0,0,0.2);
border-color: transparent;
box-shadow: none;
height: 100px;
height: 15vh;
}
.list.list-collapsed {
flex: none;
min-width: 60px;
max-width: 80px;
width: 60px;
min-height: 60vh;
height: 60vh;
overflow: visible;
position: relative;
}
.list.list-collapsed .list-header {
padding: 1vh 1.5vw 0.5vh;
min-height: 2.5vh !important;
height: auto !important;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
position: relative;
overflow: visible !important;
width: 100%;
max-width: 60px;
margin: 0 auto;
}
.list.list-collapsed .list-header .js-collapse {
margin: 0 auto 20px auto;
z-index: 10;
padding: 8px 12px;
font-size: 12px;
white-space: nowrap;
display: block;
width: fit-content;
}
.list.list-collapsed .list-header .list-rotated {
width: auto !important;
height: auto !important;
margin: 20px 0 0 0 !important;
position: relative !important;
overflow: visible !important;
}
.list.list-collapsed .list-header .list-rotated h2.list-header-name {
text-align: left;
overflow: visible;
white-space: nowrap;
display: block !important;
font-size: 12px;
line-height: 1.2;
color: #333;
background-color: rgba(255, 255, 255, 0.95);
border: 1px solid #ddd;
padding: 8px 4px;
border-radius: 4px;
margin: 0 auto;
width: 25vh;
height: 60vh;
position: absolute;
left: 50%;
top: 50%;
transform: translate(calc(-50% + 50px), -50%) rotate(0deg);
z-index: 10;
visibility: visible !important;
opacity: 1 !important;
pointer-events: none;
}
.list.list-composer .open-list-composer,
.list .list-composer .open-list-composer {
color: #8c8c8c;
@@ -45,24 +330,24 @@
.list.list-composer .list-name-input,
.list .list-composer .list-name-input {
background: #fff;
margin: -3px 0 8px;
margin: -0.4vh 0 1vh;
}
.list-header-add {
flex: 0 0 auto;
padding: 12px;
padding: 1.5vh 1.5vw;
position: relative;
min-height: 20px;
min-height: 2.5vh;
}
.list-header {
flex: 0 0 auto;
padding: 20px 12px 4px;
padding: 2.5vh 1.5vw 0.5vh;
position: relative;
min-height: 20px;
min-height: 2.5vh;
background-color: #e4e4e4;
border-bottom: 6px solid #e4e4e4;
border-bottom: 0.8vh solid #e4e4e4;
}
.list-header.list-header-card-count {
min-height: 35px;
min-height: 4.5vh;
height: auto;
}
.list-header.ui-sortable-handle {
@@ -73,21 +358,21 @@
}
.list-header .list-header-name {
display: inline;
font-size: 16px;
line-height: 17px;
font-size: clamp(14px, 3vw, 18px);
line-height: 1.2;
margin: 0;
font-weight: bold;
min-height: 9px;
min-width: 30px;
min-height: 1.2vh;
min-width: 4vw;
overflow: hidden;
text-overflow: ellipsis;
word-wrap: break-word;
}
.list-rotated {
width: 10px;
height: 250px;
margin-top: -90px;
margin-left: -110px;
width: 1.3vw;
height: 35vh;
margin-top: -12vh;
margin-left: -14vw;
margin-right: 0;
transform: rotate(90deg);
position: relative;
@@ -121,11 +406,152 @@
color: #a6a6a6;
margin-right: 15px;
}
.list-header .list-header-uncollapse-left {
.list-header .js-collapse {
color: #a6a6a6;
margin-right: 15px;
display: inline-block;
vertical-align: middle;
padding: 5px 8px;
border: 1px solid #ccc;
border-radius: 4px;
background-color: #f5f5f5;
cursor: pointer;
font-size: 14px;
}
.list-header .list-header-uncollapse-right {
color: #a6a6a6;
.list-header .js-collapse:hover {
background-color: #e0e0e0;
color: #333;
}
.list.list-collapsed .list-header .js-collapse {
display: inline-block !important;
visibility: visible !important;
opacity: 1 !important;
}
/* Responsive adjustments for collapsed lists */
@media (min-width: 768px) {
.list.list-collapsed {
min-width: 60px;
max-width: 80px;
width: 60px;
min-height: 60vh;
height: 60vh;
}
.list.list-collapsed .list-header {
max-width: 60px;
margin: 0 auto;
min-height: 2.5vh !important;
height: auto !important;
}
.list.list-collapsed .list-header .list-rotated {
width: auto !important;
height: auto !important;
margin: 20px 0 0 0 !important;
position: relative !important;
}
.list.list-collapsed .list-header .list-rotated h2.list-header-name {
width: 15vh;
font-size: 12px;
height: 30px;
line-height: 1.2;
padding: 8px 4px;
margin: 0 auto;
position: absolute;
left: 50%;
top: 50%;
transform: translate(calc(-50% + 50px), -50%) rotate(0deg);
text-align: left;
visibility: visible !important;
opacity: 1 !important;
display: block !important;
background-color: rgba(255, 255, 255, 0.95);
border: 1px solid #ddd;
color: #333;
z-index: 10;
}
.list.list-collapsed .list-header .js-collapse {
margin: 0 auto 20px auto;
}
}
@media (min-width: 1024px) {
.list.list-collapsed {
min-height: 60vh;
height: 60vh;
}
.list.list-collapsed .list-header {
min-height: 2.5vh !important;
height: auto !important;
}
.list.list-collapsed .list-header .list-rotated {
width: auto !important;
height: auto !important;
margin: 20px 0 0 0 !important;
position: relative !important;
}
.list.list-collapsed .list-header .list-rotated h2.list-header-name {
width: 15vh;
font-size: 12px;
height: 30px;
line-height: 1.2;
padding: 8px 4px;
margin: 0 auto;
position: absolute;
left: 50%;
top: 50%;
transform: translate(calc(-50% + 50px), -50%) rotate(0deg);
text-align: left;
visibility: visible !important;
opacity: 1 !important;
display: block !important;
background-color: rgba(255, 255, 255, 0.95);
border: 1px solid #ddd;
color: #333;
z-index: 10;
}
.list.list-collapsed .list-header .js-collapse {
margin: 0 auto 20px auto;
}
}
@media (min-width: 1200px) {
.list.list-collapsed {
min-height: 60vh;
height: 60vh;
}
.list.list-collapsed .list-header {
min-height: 2.5vh !important;
height: auto !important;
}
.list.list-collapsed .list-header .list-rotated {
width: auto !important;
height: auto !important;
margin: 20px 0 0 0 !important;
position: relative !important;
}
.list.list-collapsed .list-header .list-rotated h2.list-header-name {
width: 15vh;
font-size: 12px;
height: 30px;
line-height: 1.2;
padding: 8px 4px;
margin: 0 auto;
position: absolute;
left: 50%;
top: 50%;
transform: translate(calc(-50% + 50px), -50%) rotate(0deg);
text-align: left;
visibility: visible !important;
opacity: 1 !important;
display: block !important;
background-color: rgba(255, 255, 255, 0.95);
border: 1px solid #ddd;
color: #333;
z-index: 10;
}
.list.list-collapsed .list-header .js-collapse {
margin: 0 auto 20px auto;
}
}
.list-header .list-header-collapse {
color: #a6a6a6;
@@ -214,19 +640,147 @@
#js-list-width-edit .list-width-error {
display: none;
}
@media screen and (max-width: 800px) {
/* Mobile view styles - applied when isMiniScreen is true (iPhone, etc.) */
.mini-list.mobile-view {
flex: 0 0 60px;
height: auto;
width: 100%;
min-width: 100%;
border-left: 0px !important;
border-bottom: 1px solid #ccc;
}
.list.mobile-view {
display: contents;
flex-basis: auto;
width: 100%;
min-width: 100%;
border-left: 0px !important;
}
.list.mobile-view:first-child {
margin-left: 0px;
}
.list.mobile-view.ui-sortable-helper {
flex: 0 0 60px;
height: 60px;
width: 100%;
border-left: 0px !important;
border-bottom: 1px solid #ccc;
}
.list.mobile-view.ui-sortable-helper .list-header.ui-sortable-handle {
cursor: grabbing;
}
.list.mobile-view.placeholder {
flex: 0 0 60px;
height: 60px;
width: 100%;
border-left: 0px !important;
border-bottom: 1px solid #ccc;
}
.list.mobile-view .list-body {
padding: 15px 19px;
width: 100%;
min-width: 100%;
}
.list.mobile-view .list-header {
/*Updated padding values for mobile devices, this should fix text grouping issue*/
padding: 20px 0px 20px 0px;
border-bottom: 0px solid #e4e4e4;
min-height: 30px;
margin-top: 10px;
align-items: center;
width: 100%;
min-width: 100%;
/* Force grid layout for iPhone */
display: grid !important;
grid-template-columns: 30px 1fr auto auto !important;
gap: 10px !important;
}
.list.mobile-view .list-header .list-header-left-icon {
padding: 7px;
padding-right: 27px;
margin-top: 1px;
top: -7px;
left: -7px;
}
.list.mobile-view .list-header .list-header-menu-icon {
padding: 14px;
font-size: 40px !important;
text-align: center;
/* Force positioning for iPhone */
position: absolute !important;
right: 60px !important;
top: 50% !important;
transform: translateY(-50%) !important;
z-index: 10;
}
.list.mobile-view .list-header .list-header-handle {
padding: 14px;
font-size: 48px !important;
text-align: center;
/* Force positioning for iPhone */
position: absolute !important;
right: 10px !important;
top: 50% !important;
transform: translateY(-50%) !important;
z-index: 10;
}
.list.mobile-view .list-header .list-header-left-icon {
display: grid;
grid-row: 1/3;
grid-column: 1;
}
.list.mobile-view .list-header .list-header-name {
grid-row: 1;
grid-column: 2;
align-self: end;
font-size: 20px !important;
font-weight: bold;
line-height: 1.2;
padding-bottom: 2px;
}
.list.mobile-view .list-header .cardCount {
grid-row: 2;
grid-column: 2;
align-self: start;
font-size: 16px !important;
line-height: 1.2;
}
.list.mobile-view .list-header .list-header-menu {
grid-row: 1/3;
grid-column: 3;
}
.list.mobile-view .list-header .list-header-menu-icon {
grid-row: 1/3;
grid-column: 3;
}
.list.mobile-view .list-header .list-header-handle {
grid-row: 1/3;
grid-column: 4;
}
.list.mobile-view .list-header .inlined-form {
grid-row: 1/3;
grid-column: 1/4;
}
.list.mobile-view .list-header .edit-controls {
align-items: initial;
}
@media screen and (max-width: 800px),
screen and (max-device-width: 932px) and (-webkit-min-device-pixel-ratio: 3) {
.mini-list {
flex: 0 0 60px;
height: auto;
width: 100%;
border-left: 0px;
min-width: 100%;
border-left: 0px !important;
border-bottom: 1px solid #ccc;
}
.list {
display: contents;
flex-basis: auto;
width: 100%;
border-left: 0px;
min-width: 100%;
border-left: 0px !important;
}
.list:first-child {
margin-left: 0px;
@@ -235,7 +789,7 @@
flex: 0 0 60px;
height: 60px;
width: 100%;
border-left: 0px;
border-left: 0px !important;
border-bottom: 1px solid #ccc;
}
.list.ui-sortable-helper .list-header.ui-sortable-handle {
@@ -245,11 +799,13 @@
flex: 0 0 60px;
height: 60px;
width: 100%;
border-left: 0px;
border-left: 0px !important;
border-bottom: 1px solid #ccc;
}
.list-body {
padding: 15px 19px;
width: 100%;
min-width: 100%;
}
.list-header {
/*Updated padding values for mobile devices, this should fix text grouping issue*/
@@ -258,6 +814,8 @@
min-height: 30px;
margin-top: 10px;
align-items: center;
width: 100%;
min-width: 100%;
}
.list-header .list-header-left-icon {
padding: 7px;
@@ -267,24 +825,29 @@
left: -7px;
}
.list-header .list-header-menu-icon {
padding: 14px;
font-size: 40px;
text-align: center;
/* iOS Safari fallback positioning */
position: absolute;
padding: 7px;
right: 60px;
top: 50%;
transform: translateY(-50%);
right: 47px;
font-size: 20px;
}
.list-header .list-header-handle {
padding: 14px;
font-size: 48px;
text-align: center;
/* iOS Safari fallback positioning */
position: absolute;
padding: 7px;
right: 10px;
top: 50%;
transform: translateY(-50%);
right: 10px;
font-size: 24px;
}
.list-header {
display: grid;
grid-template-columns: 30px 5fr 1fr;
grid-template-columns: 30px 1fr auto auto;
gap: 10px;
}
.list-header .list-header-left-icon {
display: grid;
@@ -295,16 +858,30 @@
grid-row: 1;
grid-column: 2;
align-self: end;
font-size: 20px;
font-weight: bold;
line-height: 1.2;
padding-bottom: 2px;
}
.list-header .cardCount {
grid-row: 2;
grid-column: 2;
align-self: start;
font-size: 16px;
line-height: 1.2;
}
.list-header .list-header-menu {
grid-row: 1/3;
grid-column: 3;
}
.list-header .list-header-menu-icon {
grid-row: 1/3;
grid-column: 3;
}
.list-header .list-header-handle {
grid-row: 1/3;
grid-column: 4;
}
.list-header .inlined-form {
grid-row: 1/3;
grid-column: 1/4;
@@ -313,6 +890,132 @@
align-items: initial;
}
}
/* iPhone 12 Mini specific - fix icon positioning in stacked lists view */
@media screen and (device-width: 375px) and (device-height: 812px), /* iPhone 12 Mini exact */
screen and (max-width: 375px) and (max-height: 812px), /* iPhone 12 Mini viewport */
screen and (-webkit-min-device-pixel-ratio: 3) and (max-width: 375px) /* iPhone 12 Mini Retina */ {
.list.mobile-view .list-header {
/* Force grid layout for iPhone 12 Mini */
display: grid !important;
grid-template-columns: 30px 1fr auto auto !important;
gap: 10px !important;
align-items: center !important;
}
.list.mobile-view .list-header .list-header-menu-icon {
/* Remove absolute positioning for iPhone 12 Mini */
position: static !important;
right: auto !important;
top: auto !important;
transform: none !important;
/* Use grid positioning */
grid-row: 1/3 !important;
grid-column: 3 !important;
padding: 14px !important;
font-size: 40px !important;
text-align: center !important;
}
.list.mobile-view .list-header .list-header-handle {
/* Remove absolute positioning for iPhone 12 Mini */
position: static !important;
right: auto !important;
top: auto !important;
transform: none !important;
/* Use grid positioning */
grid-row: 1/3 !important;
grid-column: 4 !important;
padding: 14px !important;
font-size: 48px !important;
text-align: center !important;
}
.list.mobile-view .list-header .list-header-name {
grid-row: 1 !important;
grid-column: 2 !important;
align-self: end !important;
font-size: 20px !important;
font-weight: bold !important;
line-height: 1.2 !important;
padding-bottom: 2px !important;
}
.list.mobile-view .list-header .cardCount {
grid-row: 2 !important;
grid-column: 2 !important;
align-self: start !important;
font-size: 16px !important;
line-height: 1.2 !important;
}
.list.mobile-view .list-header .list-header-left-icon {
display: grid !important;
grid-row: 1/3 !important;
grid-column: 1 !important;
}
}
/* iPhone device JavaScript detection fallback - fix icon positioning */
.iphone-device .list.mobile-view .list-header {
/* Force grid layout for iPhone devices */
display: grid !important;
grid-template-columns: 30px 1fr auto auto !important;
gap: 10px !important;
align-items: center !important;
}
.iphone-device .list.mobile-view .list-header .list-header-menu-icon {
/* Remove absolute positioning for iPhone devices */
position: static !important;
right: auto !important;
top: auto !important;
transform: none !important;
/* Use grid positioning */
grid-row: 1/3 !important;
grid-column: 3 !important;
padding: 14px !important;
font-size: 40px !important;
text-align: center !important;
}
.iphone-device .list.mobile-view .list-header .list-header-handle {
/* Remove absolute positioning for iPhone devices */
position: static !important;
right: auto !important;
top: auto !important;
transform: none !important;
/* Use grid positioning */
grid-row: 1/3 !important;
grid-column: 4 !important;
padding: 14px !important;
font-size: 48px !important;
text-align: center !important;
}
.iphone-device .list.mobile-view .list-header .list-header-name {
grid-row: 1 !important;
grid-column: 2 !important;
align-self: end !important;
font-size: 20px !important;
font-weight: bold !important;
line-height: 1.2 !important;
padding-bottom: 2px !important;
}
.iphone-device .list.mobile-view .list-header .cardCount {
grid-row: 2 !important;
grid-column: 2 !important;
align-self: start !important;
font-size: 16px !important;
line-height: 1.2 !important;
}
.iphone-device .list.mobile-view .list-header .list-header-left-icon {
display: grid !important;
grid-row: 1/3 !important;
grid-column: 1 !important;
}
.link-board-wrapper {
display: flex;
align-items: baseline;
+3 -2
View File
@@ -1,10 +1,11 @@
template(name='list')
.list.js-list(id="js-list-{{_id}}"
style="{{#unless collapsed}}min-width:{{listWidth}}px;max-width:{{listConstraint}}px;{{/unless}}"
class="{{#if collapsed}}list-collapsed{{/if}} {{#if autoWidth}}list-auto-width{{/if}}")
class="{{#if collapsed}}list-collapsed{{/if}} {{#if autoWidth}}list-auto-width{{/if}} {{#if isMiniScreen}}mobile-view{{/if}}")
+listHeader
+listBody
.list-resize-handle.js-list-resize-handle.nodragscroll
template(name='miniList')
a.mini-list.js-select-list.js-list(id="js-list-{{_id}}")
a.mini-list.js-select-list.js-list(id="js-list-{{_id}}" class="{{#if isMiniScreen}}mobile-view{{/if}}")
+listHeader
+250 -12
View File
@@ -24,6 +24,9 @@ BlazeComponent.extendComponent({
onRendered() {
const boardComponent = this.parentComponent().parentComponent();
// Initialize list resize functionality immediately
this.initializeListResize();
const itemsSelector = '.js-minicard:not(.placeholder, .js-card-composer)';
const $cards = this.$('.js-minicards');
@@ -147,17 +150,13 @@ BlazeComponent.extendComponent({
});
this.autorun(() => {
if (Utils.isTouchScreenOrShowDesktopDragHandles()) {
$cards.sortable({
handle: '.handle',
});
} else {
$cards.sortable({
handle: '.minicard',
});
}
if ($cards.data('uiSortable') || $cards.data('sortable')) {
if (Utils.isTouchScreenOrShowDesktopDragHandles()) {
$cards.sortable('option', 'handle', '.handle');
} else {
$cards.sortable('option', 'handle', '.minicard');
}
$cards.sortable(
'option',
'disabled',
@@ -198,20 +197,259 @@ BlazeComponent.extendComponent({
listWidth() {
const user = ReactiveCache.getCurrentUser();
const list = Template.currentData();
return user.getListWidth(list.boardId, list._id);
if (!list) return 270; // Return default width if list is not available
if (user) {
// For logged-in users, get from user profile
return user.getListWidthFromStorage(list.boardId, list._id);
} else {
// For non-logged-in users, get from localStorage
try {
const stored = localStorage.getItem('wekan-list-widths');
if (stored) {
const widths = JSON.parse(stored);
if (widths[list.boardId] && widths[list.boardId][list._id]) {
return widths[list.boardId][list._id];
}
}
} catch (e) {
console.warn('Error reading list width from localStorage:', e);
}
return 270; // Return default width if not found
}
},
listConstraint() {
const user = ReactiveCache.getCurrentUser();
const list = Template.currentData();
return user.getListConstraint(list.boardId, list._id);
if (!list) return 550; // Return default constraint if list is not available
if (user) {
// For logged-in users, get from user profile
return user.getListConstraintFromStorage(list.boardId, list._id);
} else {
// For non-logged-in users, get from localStorage
try {
const stored = localStorage.getItem('wekan-list-constraints');
if (stored) {
const constraints = JSON.parse(stored);
if (constraints[list.boardId] && constraints[list.boardId][list._id]) {
return constraints[list.boardId][list._id];
}
}
} catch (e) {
console.warn('Error reading list constraint from localStorage:', e);
}
return 550; // Return default constraint if not found
}
},
autoWidth() {
const user = ReactiveCache.getCurrentUser();
const list = Template.currentData();
if (!user) {
// For non-logged-in users, auto-width is disabled
return false;
}
return user.isAutoWidth(list.boardId);
},
initializeListResize() {
// Check if we're still in a valid template context
if (!Template.currentData()) {
console.warn('No current template data available for list resize initialization');
return;
}
const list = Template.currentData();
const $list = this.$('.js-list');
const $resizeHandle = this.$('.js-list-resize-handle');
// Check if elements exist
if (!$list.length || !$resizeHandle.length) {
console.warn('List or resize handle not found, retrying in 100ms');
Meteor.setTimeout(() => {
if (!this.isDestroyed) {
this.initializeListResize();
}
}, 100);
return;
}
// Only enable resize for non-collapsed, non-auto-width lists
const isAutoWidth = this.autoWidth();
if (list.collapsed || isAutoWidth) {
$resizeHandle.hide();
return;
}
let isResizing = false;
let startX = 0;
let startWidth = 0;
let minWidth = 100; // Minimum width as defined in the existing code
let maxWidth = this.listConstraint() || 1000; // Use constraint as max width
let listConstraint = this.listConstraint(); // Store constraint value for use in event handlers
const component = this; // Store reference to component for use in event handlers
const startResize = (e) => {
isResizing = true;
startX = e.pageX || e.originalEvent.touches[0].pageX;
startWidth = $list.outerWidth();
// Add visual feedback
$list.addClass('list-resizing');
$('body').addClass('list-resizing-active');
// Prevent text selection during resize
$('body').css('user-select', 'none');
e.preventDefault();
e.stopPropagation();
};
const doResize = (e) => {
if (!isResizing) {
return;
}
const currentX = e.pageX || e.originalEvent.touches[0].pageX;
const deltaX = currentX - startX;
const newWidth = Math.max(minWidth, Math.min(maxWidth, startWidth + deltaX));
// Apply the new width immediately for real-time feedback
$list[0].style.setProperty('--list-width', `${newWidth}px`);
$list[0].style.setProperty('width', `${newWidth}px`);
$list[0].style.setProperty('min-width', `${newWidth}px`);
$list[0].style.setProperty('max-width', `${newWidth}px`);
$list[0].style.setProperty('flex', 'none');
$list[0].style.setProperty('flex-basis', 'auto');
$list[0].style.setProperty('flex-grow', '0');
$list[0].style.setProperty('flex-shrink', '0');
e.preventDefault();
e.stopPropagation();
};
const stopResize = (e) => {
if (!isResizing) return;
isResizing = false;
// Calculate final width
const currentX = e.pageX || e.originalEvent.touches[0].pageX;
const deltaX = currentX - startX;
const finalWidth = Math.max(minWidth, Math.min(maxWidth, startWidth + deltaX));
// Ensure the final width is applied
$list[0].style.setProperty('--list-width', `${finalWidth}px`);
$list[0].style.setProperty('width', `${finalWidth}px`);
$list[0].style.setProperty('min-width', `${finalWidth}px`);
$list[0].style.setProperty('max-width', `${finalWidth}px`);
$list[0].style.setProperty('flex', 'none');
$list[0].style.setProperty('flex-basis', 'auto');
$list[0].style.setProperty('flex-grow', '0');
$list[0].style.setProperty('flex-shrink', '0');
// Remove visual feedback but keep the width
$list.removeClass('list-resizing');
$('body').removeClass('list-resizing-active');
$('body').css('user-select', '');
// Keep the CSS custom property for persistent width
// The CSS custom property will remain on the element to maintain the width
// Save the new width using the existing system
const boardId = list.boardId;
const listId = list._id;
// Use the new storage method that handles both logged-in and non-logged-in users
if (process.env.DEBUG === 'true') {
}
const currentUser = ReactiveCache.getCurrentUser();
if (currentUser) {
// For logged-in users, use server method
Meteor.call('applyListWidthToStorage', boardId, listId, finalWidth, listConstraint, (error, result) => {
if (error) {
console.error('Error saving list width:', error);
} else {
if (process.env.DEBUG === 'true') {
}
}
});
} else {
// For non-logged-in users, save to localStorage directly
try {
// Save list width
const storedWidths = localStorage.getItem('wekan-list-widths');
let widths = storedWidths ? JSON.parse(storedWidths) : {};
if (!widths[boardId]) {
widths[boardId] = {};
}
widths[boardId][listId] = finalWidth;
localStorage.setItem('wekan-list-widths', JSON.stringify(widths));
// Save list constraint
const storedConstraints = localStorage.getItem('wekan-list-constraints');
let constraints = storedConstraints ? JSON.parse(storedConstraints) : {};
if (!constraints[boardId]) {
constraints[boardId] = {};
}
constraints[boardId][listId] = listConstraint;
localStorage.setItem('wekan-list-constraints', JSON.stringify(constraints));
if (process.env.DEBUG === 'true') {
}
} catch (e) {
console.warn('Error saving list width/constraint to localStorage:', e);
}
}
e.preventDefault();
};
// Mouse events
$resizeHandle.on('mousedown', startResize);
$(document).on('mousemove', doResize);
$(document).on('mouseup', stopResize);
// Touch events for mobile
$resizeHandle.on('touchstart', startResize, { passive: false });
$(document).on('touchmove', doResize, { passive: false });
$(document).on('touchend', stopResize, { passive: false });
// Prevent dragscroll interference
$resizeHandle.on('mousedown', (e) => {
e.stopPropagation();
});
// Reactively update resize handle visibility when auto-width changes
component.autorun(() => {
if (component.autoWidth()) {
$resizeHandle.hide();
} else {
$resizeHandle.show();
}
});
// Clean up on component destruction
component.onDestroyed(() => {
$(document).off('mousemove', doResize);
$(document).off('mouseup', stopResize);
$(document).off('touchmove', doResize);
$(document).off('touchend', stopResize);
});
},
}).register('list');
Template.miniList.events({
+2 -2
View File
@@ -32,7 +32,7 @@ template(name="listBody")
+addCardForm(listId=_id position="bottom")
else
a.open-minicard-composer.js-card-composer.js-open-inlined-form(title="{{_ 'add-card-to-bottom-of-list'}}")
i.fa.fa-plus
| ➕
template(name="spinnerList")
.sk-spinner.sk-spinner-list(
@@ -54,7 +54,7 @@ template(name="addCardForm")
.add-controls.clearfix
button.primary.confirm(type="submit") {{_ 'add'}}
a.fa.fa-times-thin.js-close-inlined-form
a.js-close-inlined-form | ❌
.add-controls.clearfix
unless currentBoard.isTemplatesBoard
unless currentBoard.isTemplateBoard
+17 -5
View File
@@ -314,7 +314,8 @@ BlazeComponent.extendComponent({
// Pressing Tab should open the form of the next column, and Maj+Tab go
// in the reverse order
} else if (evt.keyCode === 9) {
evt.preventDefault();
// Prevent custom focus movement on Tab key for accessibility
// evt.preventDefault();
const isReverse = evt.shiftKey;
const list = $(`#js-list-${this.data().listId}`);
const listSelector = '.js-list:not(.js-list-composer)';
@@ -413,13 +414,16 @@ BlazeComponent.extendComponent({
// or `Enter` to validation the auto-completion. We also need to stop the
// event propagation to prevent the card from submitting (on `Enter`) or
// going on the next column (on `Tab`).
/*
onKeydown(evt, commands) {
if (evt.keyCode === 9 || evt.keyCode === 13) {
evt.stopPropagation();
return commands.KEY_ENTER;
}
// Prevent custom focus movement on Tab key for accessibility
// if (evt.keyCode === 9 || evt.keyCode === 13) {
// evt.stopPropagation();
// return commands.KEY_ENTER;
//}
return null;
},
*/
},
);
},
@@ -468,6 +472,14 @@ BlazeComponent.extendComponent({
if (!this.selectedBoardId.get()) {
return [];
}
const board = ReactiveCache.getBoard(this.selectedBoardId.get());
if (!board) {
return [];
}
// Ensure default swimlane exists
board.getDefaultSwimline();
const swimlanes = ReactiveCache.getSwimlanes(
{
boardId: this.selectedBoardId.get()
+36 -33
View File
@@ -7,12 +7,10 @@ template(name="listHeader")
else
if isMiniScreen
if currentList
a.list-header-left-icon.fa.fa-angle-left.js-unselect-list
a.list-header-left-icon.js-unselect-list
| ◀️
else
if collapsed
a.js-collapse(title="{{_ 'uncollapse'}}")
i.fa.fa-arrow-left.list-header-uncollapse-left
i.fa.fa-arrow-right.list-header-uncollapse-right
if showCardsCountForList cards.length
br
span.cardCount {{cardsCount}}
@@ -29,6 +27,10 @@ template(name="listHeader")
if showCardsCountForList cards.length
span.cardCount {{cardsCount}} {{cardsCountForListIsOne cards.length}}
else
if collapsed
a.js-collapse(title="{{_ 'uncollapse'}}")
| ⬅️
| ➡️
div(class="{{#if collapsed}}list-rotated{{/if}}")
h2.list-header-name(
title="{{ moment modifiedAt 'LLL' }}"
@@ -45,94 +47,95 @@ template(name="listHeader")
if isMiniScreen
if currentList
if isWatching
i.list-header-watch-icon.fa.fa-eye
i.list-header-watch-icon | 👁️
div.list-header-menu
unless currentUser.isCommentOnly
if canSeeAddCard
a.js-add-card.fa.fa-plus.list-header-plus-top(title="{{_ 'add-card-to-top-of-list'}}")
a.fa.fa-navicon.js-open-list-menu(title="{{_ 'listActionPopup-title'}}")
a.js-add-card.list-header-plus-top(title="{{_ 'add-card-to-top-of-list'}}") ➕
a.js-open-list-menu(title="{{_ 'listActionPopup-title'}}") ☰
else
a.list-header-menu-icon.fa.fa-angle-right.js-select-list
a.list-header-handle.handle.fa.fa-arrows.js-list-handle
a.list-header-menu-icon.js-select-list ▶️
a.list-header-handle.handle.js-list-handle ↕️
else if currentUser.isBoardMember
if isWatching
i.list-header-watch-icon.fa.fa-eye
i.list-header-watch-icon | 👁️
unless collapsed
div.list-header-menu
unless currentUser.isCommentOnly
//if isBoardAdmin
// a.fa.js-list-star.list-header-plus-top(class="fa-star{{#unless starred}}-o{{/unless}}")
if canSeeAddCard
a.js-add-card.fa.fa-plus.list-header-plus-top(title="{{_ 'add-card-to-top-of-list'}}")
a.js-add-card.list-header-plus-top(title="{{_ 'add-card-to-top-of-list'}}") ➕
a.js-collapse(title="{{_ 'collapse'}}")
i.fa.fa-arrow-right.list-header-collapse-right
i.fa.fa-arrow-left.list-header-collapse-left
a.fa.fa-navicon.js-open-list-menu(title="{{_ 'listActionPopup-title'}}")
if currentUser.isBoardAdmin
if isTouchScreenOrShowDesktopDragHandles
a.list-header-handle.handle.fa.fa-arrows.js-list-handle
| ⬅️
| ➡️
a.js-open-list-menu(title="{{_ 'listActionPopup-title'}}") ☰
if currentUser.isBoardMember
unless currentUser.isCommentOnly
a.list-header-handle.handle.js-list-handle ↕️
template(name="editListTitleForm")
.list-composer
input.list-name-input.full-line(type="text" value=title autofocus)
.edit-controls.clearfix
button.primary.confirm(type="submit") {{_ 'save'}}
a.fa.fa-times-thin.js-close-inlined-form
a.js-close-inlined-form
| ❌
template(name="listActionPopup")
ul.pop-over-list
li
a.js-add-card.list-header-plus-bottom
i.fa.fa-plus
i.fa.fa-arrow-down
| ➕
| ⬇️
| {{_ 'add-card-to-bottom-of-list'}}
hr
ul.pop-over-list
li
a.js-set-list-width
i.fa.fa-arrows-h
| ↔️
| {{_ 'set-list-width'}}
ul.pop-over-list
li
a.js-toggle-watch-list
if isWatching
i.fa.fa-eye
| 👁️
| {{_ 'unwatch'}}
else
i.fa.fa-eye-slash
| 🙈
| {{_ 'watch'}}
unless currentUser.isCommentOnly
unless currentUser.isWorker
ul.pop-over-list
li
a.js-set-color-list
i.fa.fa-paint-brush
| 🎨
| {{_ 'set-color-list'}}
ul.pop-over-list
if cards.length
li
a.js-select-cards
i.fa.fa-check-square
| ☑️
| {{_ 'list-select-cards'}}
if currentUser.isBoardAdmin
ul.pop-over-list
li
a.js-set-wip-limit
i.fa.fa-ban
| 🚫
| {{#if isWipLimitEnabled }}{{_ 'edit-wip-limit'}}{{else}}{{_ 'setWipLimitPopup-title'}}{{/if}}
unless currentUser.isWorker
hr
ul.pop-over-list
li
a.js-close-list
i.fa.fa-arrow-right
i.fa.fa-archive
| ➡️
| 📦
| {{_ 'archive-list'}}
hr
ul.pop-over-list
li
a.js-more
i.fa.fa-link
| 🔗
| {{_ 'listMorePopup-title'}}
template(name="boardLists")
@@ -149,7 +152,7 @@ template(name="listMorePopup")
span.clearfix
span {{_ 'link-list'}}
= ' '
i.fa.colorful(class="{{#if board.isPublic}}fa-globe{{else}}fa-lock{{/if}}")
| {{#if board.isPublic}}🌐{{else}}🔒{{/if}}
input.inline-input(type="text" readonly value="{{ rootUrl }}")
| {{_ 'added'}}
span.date(title=list.createdAt) {{ moment createdAt 'LLL' }}
@@ -169,7 +172,7 @@ template(name="setWipLimitPopup")
ul.pop-over-list
li: a.js-enable-wip-limit {{_ 'enable-wip-limit'}}
if isWipLimitEnabled
i.fa.fa-check
| ✅
if isWipLimitEnabled
p
input.wip-limit-value(type="number" value="{{ wipLimitValue }}" min="1" max="99")
@@ -197,7 +200,7 @@ template(name="setListWidthPopup")
br
a.js-auto-width-board(
title="{{#if isAutoWidth}}{{_ 'click-to-disable-auto-width'}}{{else}}{{_ 'click-to-enable-auto-width'}}{{/if}}")
i.fa(class="fa-solid fa-{{#if isAutoWidth}}compress{{else}}expand{{/if}}")
| {{#if isAutoWidth}}🗜️{{else}}📏{{/if}}
span {{_ 'auto-list-width'}}
template(name="listWidthErrorPopup")
@@ -211,6 +214,6 @@ template(name="setListColorPopup")
// note: we use the swimlane palette to have more than just the border
span.card-label.palette-color.js-palette-color(class="card-details-{{color}}")
if(isSelected color)
i.fa.fa-check
| ✅
button.primary.confirm.js-submit {{_ 'save'}}
button.js-remove-color.negate.wide.right {{_ 'unset-color'}}
+43 -18
View File
@@ -1,27 +1,27 @@
.my-cards-board-wrapper {
border-radius: 0 0 4px 4px;
min-width: 400px;
margin-bottom: 2rem;
border-radius: 0 0 0.5vw 0.5vw;
min-width: min(400px, 52vw);
margin-bottom: 2.5vh;
margin-right: auto;
margin-left: auto;
border-width: 2px;
border-width: 0.3vw;
border-style: solid;
border-color: #a2a2a2;
}
.my-cards-board-title {
font-size: 1.4rem;
font-size: clamp(1.2rem, 3vw, 1.6rem);
font-weight: bold;
padding: 0.5rem;
padding: 0.7vh 0.7vw;
background-color: #808080;
color: #fff;
}
.my-cards-swimlane-title {
font-size: 1.1rem;
font-size: clamp(1rem, 2.5vw, 1.3rem);
font-weight: bold;
padding: 0.5rem;
padding-bottom: 0.4rem;
padding: 0.7vh 0.7vw;
padding-bottom: 0.5vh;
margin-top: 0;
margin-bottom: 0.5rem;
margin-bottom: 0.7vh;
text-align: center;
}
.swimlane-default-color {
@@ -29,23 +29,23 @@
}
.my-cards-list-title {
font-weight: bold;
font-size: 1.1rem;
font-size: clamp(1rem, 2.5vw, 1.3rem);
text-align: center;
margin-bottom: 0.7rem;
margin-bottom: 0.9vh;
}
.my-cards-list-wrapper {
margin: 1rem;
border-radius: 5px;
margin: 1.3vh 1.3vw;
border-radius: 0.7vw;
display: inline-grid;
min-width: 250px;
max-width: 350px;
min-width: min(250px, 32vw);
max-width: min(350px, 45vw);
}
.my-cards-card-wrapper {
margin-top: 0;
margin-bottom: 10px;
margin-bottom: 1.3vh;
}
.my-cards-dueat-list-wrapper {
max-width: 500px;
max-width: min(500px, 65vw);
margin-right: auto;
margin-left: auto;
}
@@ -72,3 +72,28 @@
border-radius: 5px;
margin-right: 5px;
}
/* Accessibility page styles */
.accessibility-page {
padding: 20px;
max-width: 800px;
margin: 0 auto;
}
.accessibility-page h2 {
font-size: 24px;
margin-bottom: 20px;
color: #4d4d4d;
}
.accessibility-page-content {
background-color: #fff;
padding: 20px;
border-radius: 3px;
box-shadow: 0 1px 2px rgba(0,0,0,0.15);
}
.accessibility-page-content p {
margin-bottom: 16px;
line-height: 1.6;
}
+12 -2
View File
@@ -1,8 +1,18 @@
template(name="accessibilityHeaderBar")
if currentUser
h1
| {{_ 'accessibility-title'}}
if isAccessibilityEnabled
= accessibilityTitle
else
| {{_ 'accessibility'}}
template(name="accessibility")
if currentUser
| {{_ 'accessibility-content'}}
.accessibility-page
if isAccessibilityEnabled
.accessibility-page-content
+viewer
| {{accessibilityContent}}
else
.accessibility-page-content
| {{_ 'accessibility-info-not-added-yet'}}
+27
View File
@@ -1,11 +1,38 @@
import { ReactiveCache } from '/imports/reactiveCache';
import { TAPi18n } from '/imports/i18n';
// Shared helpers for both accessibility templates
const accessibilityHelpers = {
accessibilityTitle() {
const setting = AccessibilitySettings.findOne({});
return setting && setting.title ? setting.title : TAPi18n.__('accessibility-title');
},
accessibilityContent() {
const setting = AccessibilitySettings.findOne({});
return setting && setting.body ? setting.body : TAPi18n.__('accessibility-content');
},
isAccessibilityEnabled() {
const setting = AccessibilitySettings.findOne({});
return setting && setting.enabled;
}
};
// Main accessibility page component
BlazeComponent.extendComponent({
onCreated() {
this.error = new ReactiveVar('');
this.loading = new ReactiveVar(false);
Meteor.subscribe('setting');
Meteor.subscribe('accessibilitySettings');
},
...accessibilityHelpers
}).register('accessibility');
// Header bar component
BlazeComponent.extendComponent({
onCreated() {
Meteor.subscribe('accessibilitySettings');
},
...accessibilityHelpers
}).register('accessibilityHeaderBar');
+29
View File
@@ -0,0 +1,29 @@
template(name="bookmarks")
.panel
h2 {{_ 'bookmarks'}}
if currentUser
if hasStarredBoards
ul
each starredBoards
li
a(href="{{pathFor 'board' id=_id slug=slug}}")= title
a.js-toggle-star(title="{{_ 'star-board-short-unstar'}}")
| ⭐
else
p {{_ 'no-starred-boards'}}
else
p {{_ 'please-sign-in'}}
// Desktop popup
template(name="bookmarksPopup")
ul.pop-over-list
if hasStarredBoards
each starredBoards
li
a(href="{{pathFor 'board' id=_id slug=slug}}")
| ⭐
| #{title}
a.js-toggle-star.right(title="{{_ 'star-board-short-unstar'}}")
| ⭐
else
li {{_ 'no-starred-boards'}}
+55
View File
@@ -0,0 +1,55 @@
Template.bookmarks.helpers({
hasStarredBoards() {
const user = ReactiveCache.getCurrentUser();
if (!user) return false;
const { starredBoards = [] } = user.profile || {};
return Array.isArray(starredBoards) && starredBoards.length > 0;
},
starredBoards() {
const user = ReactiveCache.getCurrentUser();
if (!user) return [];
const { starredBoards = [] } = user.profile || {};
if (!Array.isArray(starredBoards) || starredBoards.length === 0) return [];
return Boards.find({ _id: { $in: starredBoards } }, { sort: { sort: 1 } });
},
});
Template.bookmarks.events({
'click .js-toggle-star'(e) {
e.preventDefault();
const boardId = this._id;
const user = ReactiveCache.getCurrentUser();
if (user && boardId) {
user.toggleBoardStar(boardId);
}
},
});
Template.bookmarksPopup.helpers({
hasStarredBoards() {
const user = ReactiveCache.getCurrentUser();
if (!user) return false;
const { starredBoards = [] } = user.profile || {};
return Array.isArray(starredBoards) && starredBoards.length > 0;
},
starredBoards() {
const user = ReactiveCache.getCurrentUser();
if (!user) return [];
const { starredBoards = [] } = user.profile || {};
if (!Array.isArray(starredBoards) || starredBoards.length === 0) return [];
return Boards.find({ _id: { $in: starredBoards } }, { sort: { sort: 1 } });
},
});
Template.bookmarksPopup.events({
'click .js-toggle-star'(e) {
e.preventDefault();
const boardId = this._id;
const user = ReactiveCache.getCurrentUser();
if (user && boardId) {
user.toggleBoardStar(boardId);
}
},
});
+5 -5
View File
@@ -1,15 +1,15 @@
.broken-cards-card-wrapper {
margin-top: 0;
margin-bottom: 10px;
border-width: 3px !important;
margin-bottom: 1.3vh;
border-width: 0.4vw !important;
border-color: #808080 !important;
border-style: solid;
border-radius: 5px;
padding: 1.5rem;
border-radius: 0.7vw;
padding: 2vh 2vw;
background-color: #fff;
}
.broken-cards-wrapper {
max-width: 500px;
max-width: min(500px, 65vw);
margin-right: auto;
margin-left: auto;
}
+1 -1
View File
@@ -1,5 +1,5 @@
.due-cards-dueat-list-wrapper {
max-width: 500px;
max-width: min(500px, 65vw);
margin-right: auto;
margin-left: auto;
}
+9 -9
View File
@@ -1,23 +1,23 @@
template(name="dueCardsHeaderBar")
if currentUser
h1
i.fa.fa-calendar
| 📅
| {{_ 'dueCards-title'}}
.board-header-btns.left
a.board-header-btn.js-due-cards-view-change(title="{{_ 'dueCardsViewChange-title'}}")
i.fa.fa-caret-down
| ▼
if $eq dueCardsView 'me'
i.fa.fa-user
| 👤
| {{_ 'dueCardsViewChange-choice-me'}}
if $eq dueCardsView 'all'
i.fa.fa-users
| 👥
| {{_ 'dueCardsViewChange-choice-all'}}
template(name="dueCardsModalTitle")
if currentUser
h2
i.fa.fa-keyboard-o
| ⌨️
| {{_ 'dueCards-title'}}
template(name="dueCards")
@@ -40,18 +40,18 @@ template(name="dueCardsViewChangePopup")
li
with "dueCardsViewChange-choice-me"
a.js-due-cards-view-me
i.fa.fa-user.colorful
| 👤
| {{_ 'dueCardsViewChange-choice-me'}}
if $eq Utils.dueCardsView "me"
i.fa.fa-check
| ✅
hr
li
with "dueCardsViewChange-choice-all"
a.js-due-cards-view-all
i.fa.fa-users.colorful
| 👥
| {{_ 'dueCardsViewChange-choice-all'}}
span.sub-name
+viewer
| {{_ 'dueCardsViewChange-choice-all-description' }}
if $eq Utils.dueCardsView "all"
i.fa.fa-check
| ✅
+57 -8
View File
@@ -15,7 +15,7 @@ BlazeComponent.extendComponent({
dueCardsView() {
// eslint-disable-next-line no-console
// console.log('sort:', Utils.dueCardsView());
return Utils.dueCardsView();
return Utils && Utils.dueCardsView ? Utils.dueCardsView() : 'me';
},
events() {
@@ -38,12 +38,16 @@ BlazeComponent.extendComponent({
return [
{
'click .js-due-cards-view-me'() {
Utils.setDueCardsView('me');
if (Utils && Utils.setDueCardsView) {
Utils.setDueCardsView('me');
}
Popup.back();
},
'click .js-due-cards-view-all'() {
Utils.setDueCardsView('all');
if (Utils && Utils.setDueCardsView) {
Utils.setDueCardsView('all');
}
Popup.back();
},
},
@@ -54,7 +58,38 @@ BlazeComponent.extendComponent({
class DueCardsComponent extends CardSearchPagedComponent {
onCreated() {
super.onCreated();
// Add a small delay to ensure ReactiveCache is ready
this.searchRetryCount = 0;
this.maxRetries = 3;
// Use a timeout to ensure the search runs after the component is fully initialized
Meteor.setTimeout(() => {
this.performSearch();
}, 100);
}
performSearch() {
if (process.env.DEBUG === 'true') {
console.log('Performing due cards search, attempt:', this.searchRetryCount + 1);
}
// Check if user is authenticated
const currentUser = ReactiveCache.getCurrentUser();
if (!currentUser) {
if (process.env.DEBUG === 'true') {
console.log('User not authenticated, waiting...');
}
Meteor.setTimeout(() => {
this.performSearch();
}, 1000);
return;
}
if (process.env.DEBUG === 'true') {
console.log('User authenticated:', currentUser.username);
}
const queryParams = new QueryParams();
queryParams.addPredicate(OPERATOR_HAS, {
field: PREDICATE_DUE_AT,
@@ -66,17 +101,31 @@ class DueCardsComponent extends CardSearchPagedComponent {
order: ORDER_ASCENDING,
});
if (Utils.dueCardsView() !== 'all') {
queryParams.addPredicate(OPERATOR_USER, ReactiveCache.getCurrentUser().username);
}
// Note: User filtering is handled server-side based on board membership
// The OPERATOR_USER filter is too restrictive as it only shows cards where
// the user is assigned or a member of the card, not the board
// if (Utils && Utils.dueCardsView && Utils.dueCardsView() !== 'all') {
// const currentUser = ReactiveCache.getCurrentUser();
// if (currentUser && currentUser.username) {
// queryParams.addPredicate(OPERATOR_USER, currentUser.username);
// }
// }
// Debug: Log the query parameters
if (process.env.DEBUG === 'true') {
console.log('Due cards query params:', queryParams.params);
console.log('Due cards query text:', queryParams.text);
console.log('Due cards has predicates:', queryParams.getPredicates('has'));
console.log('Due cards sort predicates:', queryParams.getPredicates('sort'));
}
this.runGlobalSearch(queryParams);
}
dueCardsView() {
// eslint-disable-next-line no-console
//console.log('sort:', Utils.dueCardsView());
return Utils.dueCardsView();
return Utils && Utils.dueCardsView ? Utils.dueCardsView() : 'me';
}
sortByBoard() {
+4 -2
View File
@@ -1,6 +1,8 @@
template(name="editor")
a.fa.fa-brands.fa-markdown(title="{{_ 'convert-to-markdown'}}")
a.fa.fa-copy(title="{{_ 'copy-text-to-clipboard'}}")
a(title="{{_ 'convert-to-markdown'}}")
| 📝
a(title="{{_ 'copy-text-to-clipboard'}}")
| 📋
span.copied-tooltip {{_ 'copied'}}
textarea.editor(
dir="auto"
+7 -12
View File
@@ -79,7 +79,6 @@ BlazeComponent.extendComponent({
autosize($textarea);
$textarea.escapeableTextComplete(mentions);
};
/*
if (Meteor.settings.public.RICHER_CARD_COMMENT_EDITOR === true || Meteor.settings.public.RICHER_CARD_COMMENT_EDITOR === 'true') {
const isSmall = Utils.isMiniScreen();
const toolbar = isSmall
@@ -117,7 +116,7 @@ BlazeComponent.extendComponent({
].join('|');
const badPatterns = new RegExp(
`(?:${[
`<(${badTags})s*[^>][\\s\\S]*?<\\/\\1>`,
`<(${badTags})\\s*[^>][\\s\\S]*?<\\/\\1>`,
`<(${badTags})[^>]*?\\/>`,
].join('|')})`,
'gi',
@@ -128,9 +127,9 @@ BlazeComponent.extendComponent({
// remove attributes ' style="..."'
const badAttributes = new RegExp(
`(?:${[
'on\\S+=([\'"]?).*?\\1',
'href=([\'"]?)javascript:.*?\\2',
'style=([\'"]?).*?\\3',
'on\\S+=([\'\"]?).*?\\1',
'href=([\'\"]?)javascript:.*?\\2',
'style=([\'\"]?).*?\\3',
'target=\\S+',
].join('|')})`,
'gi',
@@ -300,7 +299,6 @@ BlazeComponent.extendComponent({
} else {
enableTextarea();
}
*/
enableTextarea();
},
events() {
@@ -327,6 +325,7 @@ BlazeComponent.extendComponent({
}).register('editor');
import DOMPurify from 'dompurify';
import { sanitizeHTML } from '/imports/lib/secureDOMPurify';
// Additional safeAttrValue function to allow for other specific protocols
// See https://github.com/leizongmin/js-xss/issues/52#issuecomment-241354114
@@ -373,9 +372,7 @@ Blaze.Template.registerHelper(
let content = Blaze.toHTML(view.templateContentBlock);
const currentBoard = Utils.getCurrentBoard();
if (!currentBoard)
return HTML.Raw(
DOMPurify.sanitize(content, { ALLOW_UNKNOWN_PROTOCOLS: true }),
);
return HTML.Raw(sanitizeHTML(content));
const knowedUsers = _.union(currentBoard.members.map(member => {
const u = ReactiveCache.getUser(member.userId);
if (u) {
@@ -419,9 +416,7 @@ Blaze.Template.registerHelper(
content = content.replace(fullMention, Blaze.toHTML(link));
}
return HTML.Raw(
DOMPurify.sanitize(content, { ALLOW_UNKNOWN_PROTOCOLS: true }),
);
return HTML.Raw(sanitizeHTML(content));
}),
);
+6 -4
View File
@@ -1,20 +1,21 @@
template(name="globalSearchHeaderBar")
if currentUser
h1
i.fa.fa-search
| 🔍
| {{_ 'globalSearch-title'}}
template(name="globalSearchModalTitle")
if currentUser
h2
i.fa.fa-keyboard-o
| ⌨️
| {{_ 'globalSearch-title'}}
template(name="resultsPaged")
if resultsHeading.get
h1
= resultsHeading.get
a.fa.fa-link(title="{{_ 'link-to-search' }}" href="{{ getSearchHref }}")
a(title="{{_ 'link-to-search' }}" href="{{ getSearchHref }}")
| 🔗
each card in results.get
+resultCard(card)
table.global-search-footer
@@ -41,7 +42,8 @@ template(name="globalSearch")
value="{{ query.get }}"
autofocus dir="auto"
)
a.js-new-search.fa.fa-eraser
a.js-new-search
| 🧹
if debug.get.show
h1 Debug
if debug.get.showSelector
+900 -9
View File
@@ -58,7 +58,7 @@
float: left;
overflow: hidden;
line-height: 28px;
margin: 0 2px;
margin: 0 12px;
}
#header #header-main-bar .board-header-btn i.fa {
float: left;
@@ -99,7 +99,37 @@
display: flex;
z-index: 1000;
padding: 10px 0px;
align-items: center;
flex-wrap: nowrap; /* Prevent wrapping to keep single row */
min-height: 28px;
overflow: hidden; /* Prevent content from overflowing */
}
#header-quick-access .home-icon {
display: flex;
align-items: center;
margin-right: 1rem;
flex-shrink: 0;
}
#header-quick-access .home-icon a {
display: flex;
align-items: center;
text-decoration: none;
color: #fff;
padding: 4px 8px;
border-radius: 4px;
transition: background-color 0.2s ease;
}
#header-quick-access .home-icon a:hover {
background-color: rgba(255, 255, 255, 0.1);
}
#header-quick-access .home-icon .fa-home {
font-size: 16px;
margin-right: 4px;
}
#header-quick-access .allBoards {
font-size: 14px;
padding: 4px 15px;
@@ -138,13 +168,39 @@
white-space: nowrap;
padding: 10px;
margin: -10px;
flex: 1; /* Take up available space */
min-width: 0; /* Allow shrinking below content size */
display: flex; /* Use flexbox for better control */
align-items: center;
scrollbar-width: thin; /* Firefox */
scrollbar-color: rgba(255, 255, 255, 0.3) transparent; /* Firefox */
}
/* Webkit scrollbar styling for better UX */
#header-quick-access ul.header-quick-access-list::-webkit-scrollbar {
height: 4px;
}
#header-quick-access ul.header-quick-access-list::-webkit-scrollbar-track {
background: transparent;
}
#header-quick-access ul.header-quick-access-list::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.3);
border-radius: 2px;
}
#header-quick-access ul.header-quick-access-list::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.5);
}
#header-quick-access ul.header-quick-access-list li {
display: inline;
display: inline-block; /* Keep inline-block for proper spacing */
width: auto;
color: #d9d9d9;
padding: 12px 0px;
margin: -10px 0px;
flex-shrink: 0; /* Prevent items from shrinking */
white-space: nowrap; /* Prevent text wrapping within items */
}
#header-quick-access ul.header-quick-access-list li a {
padding: 12px 10px;
@@ -191,11 +247,204 @@
margin: 0;
margin-top: 1px;
}
#header-quick-access #header-user-bar .header-user-bar-name,
#header-quick-access #header-help {
margin: 4px 8px 0 0;
float: left;
}
/* Zoom Controls in Header */
#header-quick-access .zoom-controls {
display: flex;
align-items: center;
gap: 0.5vw;
background: rgba(255, 255, 255, 0.9);
padding: 0.5vh 1vw;
border-radius: 0.5vw;
box-shadow: 0 0.2vh 0.5vh rgba(0,0,0,0.1);
margin: 0 1vw;
float: left;
}
#header-quick-access .zoom-controls .board-header-btn {
padding: 0.5vh 0.8vw !important;
border-radius: 0.3vw !important;
background: #fff !important;
border: 1px solid #000 !important;
transition: all 0.2s ease !important;
color: #000 !important;
height: auto !important;
line-height: normal !important;
margin: 0 !important;
float: none !important;
overflow: visible !important;
text-decoration: none !important;
display: flex !important;
align-items: center !important;
gap: 0.3vw !important;
}
#header-quick-access .zoom-controls .board-header-btn i {
color: #000 !important;
float: none !important;
display: inline !important;
line-height: normal !important;
margin: 0 !important;
}
#header-quick-access .zoom-controls .board-header-btn:hover {
background: #000 !important;
border-color: #000 !important;
color: #fff !important;
}
#header-quick-access .zoom-controls .board-header-btn:hover i {
color: #fff !important;
}
#header-quick-access .zoom-controls .zoom-level {
font-weight: bold;
color: #333;
min-width: 3vw;
text-align: center;
font-size: clamp(12px, 2vw, 14px);
cursor: pointer;
padding: 0.3vh 0.5vw;
border-radius: 0.3vw;
transition: all 0.2s ease;
position: relative;
display: flex;
align-items: center;
justify-content: center;
}
#header-quick-access .zoom-controls .zoom-level:hover {
background: #f0f0f0;
color: #000;
}
#header-quick-access .zoom-controls .zoom-display {
display: inline-block;
}
#header-quick-access .zoom-controls .zoom-input {
background: #fff;
color: #000;
border: 1px solid #ccc;
border-radius: 0.3vw;
padding: 0.3vh 0.5vw;
font-weight: bold;
text-align: center;
width: 100%;
min-width: 3vw;
font-size: clamp(12px, 2vw, 14px);
}
/* Make zoom input wider on all mobile screens */
@media screen and (max-width: 800px),
screen and (max-device-width: 932px) and (-webkit-min-device-pixel-ratio: 3) {
#header-quick-access .zoom-controls .zoom-input {
min-width: 50px !important; /* Wider on mobile */
width: 50px !important; /* Fixed width to show all numbers */
font-size: 14px !important; /* Slightly larger text */
}
}
#header-quick-access .zoom-controls .zoom-input:focus {
outline: 2px solid #005fcc;
outline-offset: 1px;
}
/* Mobile Mode Toggle in Header */
#header-quick-access .mobile-mode-toggle {
display: flex;
align-items: center;
margin: 0 1vw;
float: left;
}
#header-quick-access .mobile-mode-toggle .board-header-btn {
padding: 0.5vh 0.8vw !important;
border-radius: 0.3vw !important;
background: #fff !important;
border: 1px solid #000 !important;
transition: all 0.2s ease !important;
color: #000 !important;
height: auto !important;
line-height: normal !important;
margin: 0 !important;
float: none !important;
overflow: visible !important;
text-decoration: none !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
gap: 6px !important;
position: relative !important;
}
#header-quick-access .mobile-mode-toggle .board-header-btn i {
color: #666 !important;
float: none !important;
display: inline !important;
line-height: normal !important;
margin: 0 !important;
transition: all 0.2s ease !important;
font-size: clamp(14px, 2.8vw, 18px) !important;
}
#header-quick-access .mobile-mode-toggle .board-header-btn i.active {
color: #000 !important;
font-weight: bold !important;
transform: scale(1.1) !important;
}
#header-quick-access .mobile-mode-toggle .board-header-btn:hover {
background: #000 !important;
border-color: #000 !important;
color: #fff !important;
}
#header-quick-access .mobile-mode-toggle .board-header-btn:hover i {
color: #ccc !important;
}
#header-quick-access .mobile-mode-toggle .board-header-btn:hover i.active {
color: #fff !important;
}
#header-quick-access .mobile-mode-toggle .board-header-btn.mobile-active {
background: #fff !important;
border-color: #000 !important;
color: #000 !important;
}
#header-quick-access .mobile-mode-toggle .board-header-btn.mobile-active i.mobile-icon {
color: #000 !important;
font-weight: bold !important;
transform: scale(1.1) !important;
}
#header-quick-access .mobile-mode-toggle .board-header-btn.mobile-active i.desktop-icon {
color: #666 !important;
}
#header-quick-access .mobile-mode-toggle .board-header-btn.desktop-active {
background: #fff !important;
border-color: #000 !important;
color: #000 !important;
}
#header-quick-access .mobile-mode-toggle .board-header-btn.desktop-active i.mobile-icon {
color: #666 !important;
}
#header-quick-access .mobile-mode-toggle .board-header-btn.desktop-active i.desktop-icon {
color: #000 !important;
font-weight: bold !important;
transform: scale(1.1) !important;
}
#header-quick-access #header-user-bar .header-user-bar-name i.fa-chevron-down {
margin-right: 4px;
}
@@ -204,7 +453,8 @@
margin: 6px 5px 0;
width: 12px;
}
@media screen and (max-width: 800px) {
@media screen and (max-width: 800px),
screen and (max-device-width: 932px) and (-webkit-min-device-pixel-ratio: 3) {
#header #header-main-bar {
height: 40px;
}
@@ -226,7 +476,654 @@
transition: background-color 0.4s;
width: 100%;
z-index: 30;
flex-wrap: nowrap !important; /* Force single row on mobile */
overflow: hidden; /* Prevent content overflow */
}
/* Mobile home icon styling */
#header-quick-access .home-icon {
margin-right: 0.5rem;
}
#header-quick-access .home-icon .fa-home {
font-size: 16px;
margin-right: 4px;
}
#header-quick-access .home-icon a {
padding: 4px 8px;
font-size: 12px;
}
/* Ensure All Boards text is visible on mobile */
#header-quick-access .home-icon.allBoards {
display: flex;
align-items: center;
}
/* Adjust for very small screens */
@media screen and (max-width: 480px) {
#header-quick-access .home-icon a {
font-size: 11px;
padding: 3px 6px;
}
#header-quick-access .home-icon .fa-home {
font-size: 14px;
margin-right: 3px;
}
}
/* Mobile - make all text and icons 2x bigger above #content by default */
@media screen and (max-width: 800px),
screen and (max-device-width: 800px),
screen and (-webkit-min-device-pixel-ratio: 2) and (max-width: 800px),
screen and (max-width: 800px) and (orientation: portrait),
screen and (max-width: 800px) and (orientation: landscape) {
#header-quick-access {
height: 48px !important; /* Fixed height for mobile */
min-height: 48px !important; /* Minimum height for mobile */
flex-wrap: nowrap !important; /* Force single row */
align-items: center !important; /* Center align items */
padding: 8px 0px !important; /* Adjust padding for mobile */
overflow: hidden !important; /* Prevent content overflow */
}
#header-quick-access {
font-size: 2em !important; /* 2x bigger base font size */
}
#header-quick-access * {
font-size: inherit !important; /* Inherit the 2x scaling */
}
#header-quick-access .fa,
#header-quick-access .icon {
font-size: 2em !important; /* 2x bigger icons */
}
#header-quick-access .home-icon a {
font-size: 1em !important; /* Use inherited 2x scaling */
}
#header-quick-access .home-icon .fa-home {
font-size: 1em !important; /* Use inherited 2x scaling */
}
#header-quick-access .zoom-controls {
font-size: 1em !important; /* Use inherited 2x scaling */
}
#header-quick-access .zoom-controls .zoom-level {
font-size: 1em !important; /* Use inherited 2x scaling */
}
#header-quick-access .zoom-controls .zoom-input {
font-size: 1em !important; /* Use inherited 2x scaling */
}
#header-quick-access .mobile-mode-toggle .board-header-btn {
font-size: 1em !important; /* Use inherited 2x scaling */
}
#header-quick-access .mobile-mode-toggle .board-header-btn i {
font-size: 1em !important; /* Use inherited 2x scaling */
}
/* Mobile header wrapping and spacing */
#header-quick-access .home-icon {
flex-shrink: 0 !important;
margin-right: 0.5rem !important;
margin-bottom: 4px !important;
}
#header-quick-access .zoom-controls {
flex-shrink: 0 !important;
margin: 0 0.5rem !important;
margin-bottom: 4px !important;
}
#header-quick-access .mobile-mode-toggle {
flex-shrink: 0 !important;
margin: 0 0.5rem !important;
margin-bottom: 4px !important;
}
#header-quick-access #notifications {
flex-shrink: 0 !important;
margin: 0 0.5rem !important;
margin-bottom: 4px !important;
}
#header-quick-access #header-user-bar {
flex-shrink: 0 !important;
margin: 0 0.5rem !important;
margin-bottom: 4px !important;
}
#header-quick-access ul.header-quick-access-list {
flex-shrink: 0 !important;
margin: 0 0.5rem !important;
margin-bottom: 4px !important;
width: auto !important;
}
}
/* Mobile All Boards page - make logo row elements 2x bigger */
@media screen and (max-width: 800px),
screen and (max-device-width: 800px),
screen and (-webkit-min-device-pixel-ratio: 2) and (max-width: 800px),
screen and (max-width: 800px) and (orientation: portrait),
screen and (max-width: 800px) and (orientation: landscape) {
.wrapper ~ #header-quick-access,
body:not(.board-view) #header-quick-access {
font-size: 2em !important; /* 2x bigger base font size for logo row */
}
/* iPhone 12 Mini specific - 3x bigger for All Boards page */
@media screen and (device-width: 375px) and (device-height: 812px), /* iPhone 12 Mini exact */
screen and (max-width: 375px) and (max-height: 812px), /* iPhone 12 Mini viewport */
screen and (-webkit-min-device-pixel-ratio: 3) and (max-width: 375px) /* iPhone 12 Mini Retina */ {
.wrapper ~ #header-quick-access,
body:not(.board-view) #header-quick-access {
font-size: 3em !important; /* 3x bigger base font size for iPhone 12 Mini All Boards page */
}
}
.wrapper ~ #header-quick-access *,
body:not(.board-view) #header-quick-access * {
font-size: inherit !important; /* Inherit the 2x scaling */
}
.wrapper ~ #header-quick-access .fa,
.wrapper ~ #header-quick-access .icon,
body:not(.board-view) #header-quick-access .fa,
body:not(.board-view) #header-quick-access .icon {
font-size: 2em !important; /* 2x bigger icons in logo row */
}
.wrapper ~ #header-quick-access .home-icon a,
body:not(.board-view) #header-quick-access .home-icon a {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.wrapper ~ #header-quick-access .home-icon .fa-home,
body:not(.board-view) #header-quick-access .home-icon .fa-home {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.wrapper ~ #header-quick-access .zoom-controls,
body:not(.board-view) #header-quick-access .zoom-controls {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.wrapper ~ #header-quick-access .zoom-controls .zoom-level,
body:not(.board-view) #header-quick-access .zoom-controls .zoom-level {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.wrapper ~ #header-quick-access .zoom-controls .zoom-input,
body:not(.board-view) #header-quick-access .zoom-controls .zoom-input {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.wrapper ~ #header-quick-access .mobile-mode-toggle .board-header-btn,
body:not(.board-view) #header-quick-access .mobile-mode-toggle .board-header-btn {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.wrapper ~ #header-quick-access .mobile-mode-toggle .board-header-btn i,
body:not(.board-view) #header-quick-access .mobile-mode-toggle .board-header-btn i {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.wrapper ~ #header-quick-access #notifications,
body:not(.board-view) #header-quick-access #notifications {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.wrapper ~ #header-quick-access #notifications .fa,
body:not(.board-view) #header-quick-access #notifications .fa {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.wrapper ~ #header-quick-access #header-user-bar,
body:not(.board-view) #header-quick-access #header-user-bar {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.wrapper ~ #header-quick-access #header-user-bar .fa,
body:not(.board-view) #header-quick-access #header-user-bar .fa {
font-size: 1em !important; /* Use inherited 2x scaling */
}
}
/* iPhone 12 Mini specific - make header elements 3x bigger */
@media screen and (device-width: 375px) and (device-height: 812px), /* iPhone 12 Mini exact */
screen and (max-width: 375px) and (max-height: 812px), /* iPhone 12 Mini viewport */
screen and (-webkit-min-device-pixel-ratio: 3) and (max-width: 375px), /* iPhone 12 Mini Retina */
screen and (max-width: 375px) and (orientation: portrait), /* iPhone 12 Mini portrait */
screen and (max-width: 375px) and (orientation: landscape) /* iPhone 12 Mini landscape */ {
#header-quick-access {
font-size: 3em !important; /* 3x bigger base font size for iPhone 12 Mini */
height: auto !important; /* Allow height to grow */
min-height: 84px !important; /* Much taller minimum height for iPhone 12 Mini */
flex-wrap: wrap !important; /* Force wrapping */
align-items: flex-start !important; /* Align to top when wrapping */
padding: 18px 0px !important; /* More padding for iPhone 12 Mini */
}
#header-quick-access * {
font-size: inherit !important; /* Inherit the 2x scaling */
}
#header-quick-access .fa,
#header-quick-access .icon {
font-size: 3em !important; /* 3x bigger icons for iPhone 12 Mini */
}
#header-quick-access .home-icon a {
font-size: 1em !important; /* Use inherited 2x scaling */
}
#header-quick-access .home-icon .fa-home {
font-size: 1em !important; /* Use inherited 2x scaling */
}
#header-quick-access .zoom-controls {
font-size: 1em !important; /* Use inherited 2x scaling */
}
#header-quick-access .zoom-controls .zoom-level {
font-size: 1em !important; /* Use inherited 2x scaling */
}
#header-quick-access .zoom-controls .zoom-input {
font-size: 1em !important; /* Use inherited 2x scaling */
}
#header-quick-access .mobile-mode-toggle .board-header-btn {
font-size: 1em !important; /* Use inherited 2x scaling */
}
#header-quick-access .mobile-mode-toggle .board-header-btn i {
font-size: 1em !important; /* Use inherited 2x scaling */
}
#header-quick-access #notifications {
font-size: 1em !important; /* Use inherited 2x scaling */
}
#header-quick-access #notifications .fa {
font-size: 1em !important; /* Use inherited 2x scaling */
}
#header-quick-access #header-user-bar {
font-size: 1em !important; /* Use inherited 2x scaling */
}
#header-quick-access #header-user-bar .fa {
font-size: 1em !important; /* Use inherited 2x scaling */
}
/* iPhone 12 Mini header wrapping and spacing */
#header-quick-access .home-icon {
flex-shrink: 0 !important;
margin-right: 0.5rem !important;
margin-bottom: 6px !important;
}
#header-quick-access .zoom-controls {
flex-shrink: 0 !important;
margin: 0 0.5rem !important;
margin-bottom: 6px !important;
}
#header-quick-access .mobile-mode-toggle {
flex-shrink: 0 !important;
margin: 0 0.5rem !important;
margin-bottom: 6px !important;
}
#header-quick-access #notifications {
flex-shrink: 0 !important;
margin: 0 0.5rem !important;
margin-bottom: 6px !important;
}
#header-quick-access #header-user-bar {
flex-shrink: 0 !important;
margin: 0 0.5rem !important;
margin-bottom: 6px !important;
}
#header-quick-access ul.header-quick-access-list {
flex-shrink: 0 !important;
margin: 0 0.5rem !important;
margin-bottom: 6px !important;
width: auto !important;
}
}
/* iPhone 12 Mini and very small screens - make header elements much larger */
@media screen and (max-width: 400px) and (max-height: 900px),
screen and (max-device-width: 400px) and (max-device-height: 900px),
screen and (-webkit-min-device-pixel-ratio: 2) and (max-width: 400px),
screen and (max-width: 400px) and (orientation: portrait),
screen and (max-width: 400px) and (orientation: landscape),
screen and (max-width: 430px) and (max-height: 950px), /* iPhone 12 Mini range */
screen and (max-width: 450px) and (max-height: 1000px), /* iPhone range */
screen and (-webkit-min-device-pixel-ratio: 3) and (max-width: 450px), /* Retina displays */
screen and (device-width: 375px) and (device-height: 812px), /* iPhone 12 Mini exact */
screen and (device-width: 390px) and (device-height: 844px), /* iPhone 12/13 */
screen and (device-width: 428px) and (device-height: 926px) /* iPhone 12 Pro Max */ {
#header-quick-access {
height: 40px !important; /* Taller header */
padding: 12px 0px !important;
}
#header-quick-access .home-icon a {
font-size: 16px !important; /* Much larger text */
padding: 8px 12px !important;
}
#header-quick-access .home-icon .fa-home {
font-size: 20px !important; /* Much larger icon */
margin-right: 6px !important;
}
#header-quick-access .home-icon {
margin-right: 1rem !important;
}
/* Make zoom controls larger */
#header-quick-access .zoom-controls {
padding: 0.8vh 1.5vw !important;
margin: 0 1.5vw !important;
}
#header-quick-access .zoom-controls .zoom-level {
font-size: 16px !important; /* Larger zoom text */
padding: 0.5vh 0.8vw !important;
min-width: 4vw !important;
}
#header-quick-access .zoom-controls .zoom-input {
font-size: 16px !important; /* Larger input text */
padding: 0.5vh 0.8vw !important;
min-width: 6vw !important; /* Much wider for mobile */
width: 60px !important; /* Fixed width to show all numbers */
}
/* Make mobile mode toggle larger */
#header-quick-access .mobile-mode-toggle .board-header-btn {
padding: 0.8vh 1.2vw !important;
font-size: 16px !important;
}
#header-quick-access .mobile-mode-toggle .board-header-btn i {
font-size: 18px !important;
}
}
/* Fallback for iPhone devices using JavaScript detection */
.iphone-device #header-quick-access {
font-size: 2em !important; /* 2x bigger base font size */
height: auto !important; /* Allow height to grow */
min-height: 48px !important; /* Minimum height for mobile */
flex-wrap: wrap !important; /* Force wrapping */
align-items: flex-start !important; /* Align to top when wrapping */
padding: 8px 0px !important; /* Adjust padding for mobile */
}
.iphone-device #header-quick-access * {
font-size: inherit !important; /* Inherit the 2x scaling */
}
.iphone-device #header-quick-access .fa,
.iphone-device #header-quick-access .icon {
font-size: 2em !important; /* 2x bigger icons */
}
.iphone-device #header-quick-access .home-icon a {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.iphone-device #header-quick-access .home-icon .fa-home {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.iphone-device #header-quick-access .zoom-controls {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.iphone-device #header-quick-access .zoom-controls .zoom-level {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.iphone-device #header-quick-access .zoom-controls .zoom-input {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.iphone-device #header-quick-access .mobile-mode-toggle .board-header-btn {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.iphone-device #header-quick-access .mobile-mode-toggle .board-header-btn i {
font-size: 1em !important; /* Use inherited 2x scaling */
}
/* iPhone device header wrapping and spacing */
.iphone-device #header-quick-access .home-icon {
flex-shrink: 0 !important;
margin-right: 0.5rem !important;
margin-bottom: 4px !important;
}
.iphone-device #header-quick-access .zoom-controls {
flex-shrink: 0 !important;
margin: 0 0.5rem !important;
margin-bottom: 4px !important;
}
.iphone-device #header-quick-access .mobile-mode-toggle {
flex-shrink: 0 !important;
margin: 0 0.5rem !important;
margin-bottom: 4px !important;
}
.iphone-device #header-quick-access #notifications {
flex-shrink: 0 !important;
margin: 0 0.5rem !important;
margin-bottom: 4px !important;
}
.iphone-device #header-quick-access #header-user-bar {
flex-shrink: 0 !important;
margin: 0 0.5rem !important;
margin-bottom: 4px !important;
}
.iphone-device #header-quick-access ul.header-quick-access-list {
flex-shrink: 0 !important;
margin: 0 0.5rem !important;
margin-bottom: 4px !important;
width: auto !important;
}
/* iPhone 12 Mini specific - JavaScript detection fallback */
.iphone-device #header-quick-access {
font-size: 3em !important; /* 3x bigger base font size for iPhone 12 Mini */
height: auto !important; /* Allow height to grow */
min-height: 84px !important; /* Much taller minimum height for iPhone 12 Mini */
flex-wrap: wrap !important; /* Force wrapping */
align-items: flex-start !important; /* Align to top when wrapping */
padding: 18px 0px !important; /* More padding for iPhone 12 Mini */
}
.iphone-device #header-quick-access * {
font-size: inherit !important; /* Inherit the 2x scaling */
}
.iphone-device #header-quick-access .fa,
.iphone-device #header-quick-access .icon {
font-size: 3em !important; /* 3x bigger icons for iPhone 12 Mini */
}
.iphone-device #header-quick-access .home-icon a {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.iphone-device #header-quick-access .home-icon .fa-home {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.iphone-device #header-quick-access .zoom-controls {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.iphone-device #header-quick-access .zoom-controls .zoom-level {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.iphone-device #header-quick-access .zoom-controls .zoom-input {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.iphone-device #header-quick-access .mobile-mode-toggle .board-header-btn {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.iphone-device #header-quick-access .mobile-mode-toggle .board-header-btn i {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.iphone-device #header-quick-access #notifications {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.iphone-device #header-quick-access #notifications .fa {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.iphone-device #header-quick-access #header-user-bar {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.iphone-device #header-quick-access #header-user-bar .fa {
font-size: 1em !important; /* Use inherited 2x scaling */
}
/* iPhone 12 Mini header wrapping and spacing - JavaScript fallback */
.iphone-device #header-quick-access .home-icon {
flex-shrink: 0 !important;
margin-right: 0.5rem !important;
margin-bottom: 6px !important;
}
.iphone-device #header-quick-access .zoom-controls {
flex-shrink: 0 !important;
margin: 0 0.5rem !important;
margin-bottom: 6px !important;
}
.iphone-device #header-quick-access .mobile-mode-toggle {
flex-shrink: 0 !important;
margin: 0 0.5rem !important;
margin-bottom: 6px !important;
}
.iphone-device #header-quick-access #notifications {
flex-shrink: 0 !important;
margin: 0 0.5rem !important;
margin-bottom: 6px !important;
}
.iphone-device #header-quick-access #header-user-bar {
flex-shrink: 0 !important;
margin: 0 0.5rem !important;
margin-bottom: 6px !important;
}
.iphone-device #header-quick-access ul.header-quick-access-list {
flex-shrink: 0 !important;
margin: 0 0.5rem !important;
margin-bottom: 6px !important;
width: auto !important;
}
/* iPhone 12 Mini All Boards page - make logo row elements 3x bigger */
.iphone-device .wrapper ~ #header-quick-access,
.iphone-device body:not(.board-view) #header-quick-access {
font-size: 3em !important; /* 3x bigger base font size for logo row */
}
.iphone-device .wrapper ~ #header-quick-access *,
.iphone-device body:not(.board-view) #header-quick-access * {
font-size: inherit !important; /* Inherit the 2x scaling */
}
.iphone-device .wrapper ~ #header-quick-access .fa,
.iphone-device .wrapper ~ #header-quick-access .icon,
.iphone-device body:not(.board-view) #header-quick-access .fa,
.iphone-device body:not(.board-view) #header-quick-access .icon {
font-size: 2em !important; /* 2x bigger icons in logo row */
}
.iphone-device .wrapper ~ #header-quick-access .home-icon a,
.iphone-device body:not(.board-view) #header-quick-access .home-icon a {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.iphone-device .wrapper ~ #header-quick-access .home-icon .fa-home,
.iphone-device body:not(.board-view) #header-quick-access .home-icon .fa-home {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.iphone-device .wrapper ~ #header-quick-access .zoom-controls,
.iphone-device body:not(.board-view) #header-quick-access .zoom-controls {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.iphone-device .wrapper ~ #header-quick-access .zoom-controls .zoom-level,
.iphone-device body:not(.board-view) #header-quick-access .zoom-controls .zoom-level {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.iphone-device .wrapper ~ #header-quick-access .zoom-controls .zoom-input,
.iphone-device body:not(.board-view) #header-quick-access .zoom-controls .zoom-input {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.iphone-device .wrapper ~ #header-quick-access .mobile-mode-toggle .board-header-btn,
.iphone-device body:not(.board-view) #header-quick-access .mobile-mode-toggle .board-header-btn {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.iphone-device .wrapper ~ #header-quick-access .mobile-mode-toggle .board-header-btn i,
.iphone-device body:not(.board-view) #header-quick-access .mobile-mode-toggle .board-header-btn i {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.iphone-device .wrapper ~ #header-quick-access #notifications,
.iphone-device body:not(.board-view) #header-quick-access #notifications {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.iphone-device .wrapper ~ #header-quick-access #notifications .fa,
.iphone-device body:not(.board-view) #header-quick-access #notifications .fa {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.iphone-device .wrapper ~ #header-quick-access #header-user-bar,
.iphone-device body:not(.board-view) #header-quick-access #header-user-bar {
font-size: 1em !important; /* Use inherited 2x scaling */
}
.iphone-device .wrapper ~ #header-quick-access #header-user-bar .fa,
.iphone-device body:not(.board-view) #header-quick-access #header-user-bar .fa {
font-size: 1em !important; /* Use inherited 2x scaling */
}
#header-quick-access ul {
width: calc(100% - 60px);
margin-right: 10px;
@@ -237,12 +1134,6 @@
#header-quick-access ul li a {
height: 100%;
}
#header-quick-access span .fa-home {
font-size: 26px;
margin-top: -2px;
margin-right: 10px;
margin-left: 10px;
}
#header-quick-access #header-new-board-icon {
display: none;
}
+50 -37
View File
@@ -6,10 +6,31 @@ template(name="header")
The first link goes to the boards page.
if currentUser
#header-quick-access(class=currentBoard.colorClass)
// Home icon - always at left side of logo
span.home-icon.allBoards
a(href="{{pathFor 'home'}}")
| 🏠
| {{_ 'all-boards'}}
// Logo - visible; on mobile constrained by CSS
unless currentSetting.hideLogo
if currentSetting.customTopLeftCornerLogoImageUrl
if currentSetting.customTopLeftCornerLogoLinkUrl
a(href="{{currentSetting.customTopLeftCornerLogoLinkUrl}}" alt="{{currentSetting.productName}}" title="{{currentSetting.productName}}")
img(src="{{currentSetting.customTopLeftCornerLogoImageUrl}}" height="{{#if currentSetting.customTopLeftCornerLogoHeight}}#{currentSetting.customTopLeftCornerLogoHeight}{{else}}27{{/if}}" width="auto" margin="0" padding="0")
unless currentSetting.customTopLeftCornerLogoLinkUrl
img(src="{{currentSetting.customTopLeftCornerLogoImageUrl}}" height="{{#if currentSetting.customTopLeftCornerLogoHeight}}#{currentSetting.customTopLeftCornerLogoHeight}{{else}}27{{/if}}" width="auto" margin="0" padding="0" alt="{{currentSetting.productName}}" title="{{currentSetting.productName}}")
unless currentSetting.customTopLeftCornerLogoImageUrl
div#headerIsSettingDatabaseCallDone
img(src="{{pathFor '/logo-header.png'}}" alt="{{currentSetting.productName}}" title="{{currentSetting.productName}}")
// Zoom controls - always visible
.zoom-controls
span.zoom-level.js-zoom-level-click(title="{{_ 'click-to-change-zoom'}}")
span.zoom-display {{zoomLevel}}%
input.zoom-input.js-zoom-input(type="number" value=zoomLevel min="50" max="300" step="10" style="display: none;")
if isMiniScreen
span
a(href="{{pathFor 'home'}}")
span.fa.fa-home
ul.header-quick-access-list
if currentList
each currentBoard.lists
@@ -23,32 +44,14 @@ template(name="header")
a(href="{{pathFor 'board' id=_id slug=slug}}")
+viewer
= title
a.js-toggle-desktop-drag-handles(title="{{_ 'show-desktop-drag-handles'}}" alt="{{_ 'show-desktop-drag-handles'}}")
i.fa.fa-arrows
if isShowDesktopDragHandles
i.fa.fa-check-square-o
unless isShowDesktopDragHandles
i.fa.fa-ban
//a.js-toggle-desktop-drag-handles(title="{{_ 'show-desktop-drag-handles'}}" alt="{{_ 'show-desktop-drag-handles'}}")
// i.fa.fa-arrows
// if isShowDesktopDragHandles
// i.fa.fa-check-square-o
// unless isShowDesktopDragHandles
// i.fa.fa-ban
#header-new-board-icon
else
//-
On sandstorm, the logo shouldn't be clickable, because we only have one
page/document on it, and we don't want to see the home page containing
the list of all boards.
unless currentSetting.hideLogo
if currentSetting.customTopLeftCornerLogoImageUrl
if currentSetting.customTopLeftCornerLogoLinkUrl
a(href="{{currentSetting.customTopLeftCornerLogoLinkUrl}}" alt="{{currentSetting.productName}}" title="{{currentSetting.productName}}")
img(src="{{currentSetting.customTopLeftCornerLogoImageUrl}}" height="{{#if currentSetting.customTopLeftCornerLogoHeight}}#{currentSetting.customTopLeftCornerLogoHeight}{{else}}27{{/if}}" width="auto" margin="0" padding="0")
unless currentSetting.customTopLeftCornerLogoLinkUrl
img(src="{{currentSetting.customTopLeftCornerLogoImageUrl}}" height="{{#if currentSetting.customTopLeftCornerLogoHeight}}#{currentSetting.customTopLeftCornerLogoHeight}{{else}}27{{/if}}" width="auto" margin="0" padding="0" alt="{{currentSetting.productName}}" title="{{currentSetting.productName}}")
unless currentSetting.customTopLeftCornerLogoImageUrl
div#headerIsSettingDatabaseCallDone
img(src="{{pathFor '/logo-header.png'}}" alt="{{currentSetting.productName}}" title="{{currentSetting.productName}}")
span.allBoards
a(href="{{pathFor 'home'}}")
span.fa.fa-home
| {{_ 'all-boards'}}
ul.header-quick-access-list
//li
// a(href="{{pathFor 'public'}}")
@@ -61,12 +64,12 @@ template(name="header")
= title
else
li.current.empty {{_ 'quick-access-description'}}
a.js-toggle-desktop-drag-handles(title="{{_ 'show-desktop-drag-handles'}}" alt="{{_ 'show-desktop-drag-handles'}}")
i.fa.fa-arrows
if isShowDesktopDragHandles
i.fa.fa-check-square-o
unless isShowDesktopDragHandles
i.fa.fa-ban
//a.js-toggle-desktop-drag-handles(title="{{_ 'show-desktop-drag-handles'}}" alt="{{_ 'show-desktop-drag-handles'}}")
// i.fa.fa-arrows
// if isShowDesktopDragHandles
// i.fa.fa-check-square-o
// unless isShowDesktopDragHandles
// i.fa.fa-ban
// Next line is used only for spacing at header,
// there is no visible clickable icon.
#header-new-board-icon
@@ -75,12 +78,22 @@ template(name="header")
//a#header-new-board-icon.js-create-board
// i.fa.fa-plus(title="Create a new board")
.mobile-mode-toggle
a.board-header-btn.js-mobile-mode-toggle(title="{{_ 'mobile-desktop-toggle'}}" class="{{#if mobileMode}}mobile-active{{else}}desktop-active{{/if}}")
i.mobile-icon(class="{{#if mobileMode}}active{{/if}}") 📱
i.desktop-icon(class="{{#unless mobileMode}}active{{/unless}}") 🖥️
// Bookmarks button - desktop opens popup, mobile routes to page
a.board-header-btn.js-open-bookmarks(title="{{_ 'bookmarks'}}")
| 🔖
// Notifications
+notifications
if currentSetting.customHelpLinkUrl
#header-help
a(href="{{currentSetting.customHelpLinkUrl}}", title="{{_ 'help'}}", target="_blank", rel="noopener noreferrer")
span.fa.fa-question
| ❓
+headerUserBar
@@ -99,15 +112,15 @@ template(name="header")
if hasAnnouncement
.announcement
p
i.fa.fa-bullhorn
| 📢
+viewer
| #{announcement}
i.fa.fa-times-circle.js-close-announcement
| ❌
template(name="offlineWarning")
.offline-warning
p
i.fa.fa-warning
| ⚠️
| {{_ 'app-is-offline'}}
a.app-try-reconnect {{_ 'app-try-reconnect'}}
+67
View File
@@ -44,10 +44,69 @@ Template.header.helpers({
const announcements = Announcements.findOne();
return announcements && announcements.body;
},
zoomLevel() {
const sessionZoom = Session.get('wekan-zoom-level');
if (sessionZoom !== undefined) {
return Math.round(sessionZoom * 100);
}
return Math.round(Utils.getZoomLevel() * 100);
},
mobileMode() {
const sessionMode = Session.get('wekan-mobile-mode');
if (sessionMode !== undefined) {
return sessionMode;
}
return Utils.getMobileMode();
},
});
Template.header.events({
'click .js-create-board': Popup.open('headerBarCreateBoard'),
'click .js-zoom-level-click'(evt) {
const $zoomDisplay = $(evt.currentTarget).find('.zoom-display');
const $zoomInput = $(evt.currentTarget).find('.zoom-input');
// Hide display, show input
$zoomDisplay.hide();
$zoomInput.show().focus().select();
},
'keypress .js-zoom-input'(evt) {
if (evt.which === 13) { // Enter key
const newZoomPercent = parseInt(evt.target.value);
if (!isNaN(newZoomPercent) && newZoomPercent >= 50 && newZoomPercent <= 300) {
const newZoom = newZoomPercent / 100;
Utils.setZoomLevel(newZoom);
// Hide input, show display
const $zoomDisplay = $(evt.target).siblings('.zoom-display');
const $zoomInput = $(evt.target);
$zoomInput.hide();
$zoomDisplay.show();
} else {
alert('Please enter a zoom level between 50% and 300%');
evt.target.focus().select();
}
}
},
'blur .js-zoom-input'(evt) {
// When input loses focus, hide it and show display
const $zoomDisplay = $(evt.target).siblings('.zoom-display');
const $zoomInput = $(evt.target);
$zoomInput.hide();
$zoomDisplay.show();
},
'click .js-mobile-mode-toggle'() {
const currentMode = Utils.getMobileMode();
Utils.setMobileMode(!currentMode);
},
'click .js-open-bookmarks'(evt) {
// Already added but ensure single definition -- safe guard
},
'click .js-close-announcement'() {
$('.announcement').hide();
},
@@ -68,6 +127,14 @@ Template.header.events({
location.reload();
}
},
'click .js-open-bookmarks'(evt) {
// Desktop: open popup, Mobile: route to page
if (Utils.isMiniScreen()) {
FlowRouter.go('bookmarks');
} else {
Popup.open('bookmarksPopup')(evt);
}
},
});
Template.offlineWarning.events({
@@ -1,12 +1,12 @@
template(name="shortcutsHeaderBar")
h1
a.back-btn(href="{{pathFor 'home'}}")
i.fa.fa-chevron-left
| ◀️
| {{_ 'keyboard-shortcuts'}}
template(name="shortcutsModalTitle")
h2
i.fa.fa-keyboard-o
| ⌨️
| {{_ 'keyboard-shortcuts'}}
template(name="keyboardShortcuts")
+342 -57
View File
@@ -52,15 +52,25 @@ input,
select,
textarea,
button {
font: 14px Roboto, Poppins, "Helvetica Neue", Arial, Helvetica, sans-serif;
line-height: 18px;
font: clamp(14px, 2.5vw, 18px) Roboto, Poppins, "Helvetica Neue", Arial, Helvetica, sans-serif;
line-height: 1.4;
color: #4d4d4d;
/* Improve text rendering */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
/* Better text selection */
-webkit-user-select: text;
user-select: text;
}
html {
font-size: 100%;
max-height: 100%;
user-select: none;
-webkit-user-select: text;
user-select: text;
-webkit-text-size-adjust: 100%;
text-size-adjust: 100%;
}
body {
background: #dedede;
@@ -81,8 +91,8 @@ body {
margin-top: 30vh;
}
#content > .wrapper {
margin-top: 10px;
padding: 15px;
margin-top: 1vh;
padding: 2vh 2vw;
}
#modal {
position: absolute;
@@ -95,56 +105,56 @@ body {
overflow-y: auto;
}
#modal .modal-content {
width: 500px;
min-height: 160px;
margin: 42px auto;
padding: 12px;
border-radius: 4px;
width: min(500px, 90vw);
min-height: 20vh;
margin: 5vh auto;
padding: 2vh 2vw;
border-radius: 0.5vw;
background: #dedede;
z-index: 110;
}
#modal .modal-content h2 {
margin-bottom: 25px;
margin-bottom: 3vh;
}
#modal .modal-content .modal-close-btn {
display: block;
float: right;
font-size: 24px;
font-size: clamp(18px, 4vw, 24px);
}
#modal .modal-content-wide {
width: 800px;
width: min(800px, 95vw);
min-height: 0px;
margin: 42px auto;
padding: 12px;
border-radius: 4px;
margin: 5vh auto;
padding: 2vh 2vw;
border-radius: 0.5vw;
background: #dedede;
z-index: 110;
}
#modal .modal-content-wide h2 {
margin-bottom: 25px;
margin-bottom: 3vh;
}
#modal .modal-content-wide .modal-close-btn {
display: block;
float: right;
font-size: 24px;
font-size: clamp(18px, 4vw, 24px);
}
h1 {
font-size: 22px;
font-size: clamp(18px, 4vw, 24px);
line-height: 1.2em;
margin: 0 0 10px;
margin: 0 0 1vh;
}
h2 {
font-size: 18px;
font-size: clamp(16px, 3.5vw, 20px);
line-height: 1.2em;
margin: 0 0 9px;
margin: 0 0 0.8vh;
}
h3,
h4,
h5,
h6 {
font-size: 16px;
font-size: clamp(14px, 3vw, 18px);
line-height: 1.25em;
margin: 0 0 6px;
margin: 0 0 0.6vh;
}
.quiet,
.quiet a {
@@ -156,14 +166,14 @@ h6 {
}
.no-items-message {
color: #666;
margin: 30px 0;
margin: 4vh 0;
text-align: center;
}
.warning {
background: #f0ecdb;
border-radius: 3px;
border-radius: 0.4vw;
color: #aa8f09;
padding: 6px 8px;
padding: 1vh 1.2vw;
}
.warning a {
color: #aa8f09;
@@ -188,7 +198,10 @@ strong {
font-weight: bold;
}
p {
-webkit-user-select: text;
user-select: text;
}
p a {
text-decoration: underline;
@@ -196,24 +209,24 @@ p a {
}
table,
p {
margin-bottom: 8px;
margin-bottom: 1vh;
}
pre {
margin: 15px 0;
margin: 2vh 0;
white-space: pre;
max-height: 516px;
max-height: 60vh;
}
pre,
code,
tt {
font-family: lucida console, monospace;
line-height: 1.25em;
line-height: 1.3em;
}
blockquote {
margin: 8px 0 8px 8px;
margin: 1vh 0 1vh 1vw;
border-left: 1px solid #ccc;
color: #666;
padding: 0 0 0 8px;
padding: 0 0 0 1vw;
}
hr {
height: 1px;
@@ -222,7 +235,7 @@ hr {
width: 100%;
background: #dbdbdb;
color: #dbdbdb;
margin: 15px 0;
margin: 2vh 0;
padding: 0;
}
table,
@@ -234,7 +247,7 @@ th {
}
td,
th {
padding: 5px;
padding: 0.8vh 0.8vw;
border-right: 1px solid #ccc;
border-bottom: 1px solid #ccc;
}
@@ -250,20 +263,20 @@ tbody {
}
dl,
dt {
margin-bottom: 8px;
margin-bottom: 1vh;
}
dd {
margin: 0 0 16px 24px;
margin: 0 0 2vh 3vw;
}
kbd {
padding: 1px 3px;
margin: 3px;
padding: 0.2vh 0.5vw;
margin: 0.5vh;
font-weight: bold;
background: #fafafa;
border-radius: 3px;
border-radius: 0.4vw;
border: 1px solid #e6e6e6;
color: unset;
box-shadow: 0px 2px 3px rgba(0,0,0,0.15);
box-shadow: 0px 0.3vh 0.5vh rgba(0,0,0,0.15);
}
.clear {
clear: both;
@@ -303,7 +316,7 @@ kbd {
padding-bottom: 0;
}
.wrapper {
width: calc(100% - 20px);
width: calc(100% - 2vw);
margin: 0 auto;
}
.relative {
@@ -394,23 +407,23 @@ a:not(.disabled).is-active i.fa {
}
.big-message {
display: block;
margin: 75px auto;
margin: 10vh auto;
text-align: center;
max-width: 600px;
max-width: min(600px, 90vw);
}
.big-message h1 {
font-size: 26px;
margin-bottom: 24px;
font-size: clamp(20px, 5vw, 28px);
margin-bottom: 3vh;
}
.big-message p {
font-size: 18px;
line-height: 22px;
font-size: clamp(16px, 3.5vw, 20px);
line-height: 1.3;
}
.gutter {
margin-left: 38px;
margin-left: 5vw;
}
.viewer {
min-height: 18px;
min-height: 2.5vh;
display: block;
word-wrap: break-word;
}
@@ -420,18 +433,19 @@ a:not(.disabled).is-active i.fa {
}
.viewer ol {
list-style-type: decimal;
padding-left: 20px;
padding-bottom: 10px;
padding-left: 3vw;
padding-bottom: 1.5vh;
}
.viewer ul {
list-style-type: initial;
padding-left: 20px;
padding-bottom: 10px;
padding-left: 3vw;
padding-bottom: 1.5vh;
}
.viewer em {
font-style: italic;
}
.viewer pre {
.viewer pre,
.viewer code {
padding: 10px 12px 7px;
background: #dedede;
overflow-y: auto;
@@ -452,20 +466,291 @@ a:not(.disabled).is-active i.fa {
.no-scrollbars::-webkit-scrollbar {
display: none !important;
}
@media screen and (max-width: 800px) {
/* ========================================
MOBILE & TABLET RESPONSIVE IMPROVEMENTS
======================================== */
/* Mobile devices (up to 800px) and all iPhone models */
@media screen and (max-width: 800px),
screen and (max-device-width: 932px) and (-webkit-min-device-pixel-ratio: 3) and (orientation: landscape),
screen and (max-device-width: 932px) and (-webkit-min-device-pixel-ratio: 3) and (orientation: portrait) {
#content {
margin: 1px 0px 0px 0px;
height: calc(100% - 0px);
/* Improve touch scrolling */
-webkit-overflow-scrolling: touch;
}
#content > .wrapper {
margin-top: 0px;
padding: 8px;
}
.wrapper {
height: calc(100% - 31px);
margin: 0px;
padding: 8px;
}
.panel-default {
width: 83vw;
width: 95vw;
max-width: 95vw;
margin: 0 auto;
}
/* Improve touch targets */
button, .btn, .js-toggle, .js-color-choice, .js-reaction, .close {
min-height: 44px;
min-width: 44px;
padding: 12px 16px;
font-size: 16px; /* Prevent zoom on iOS */
touch-action: manipulation;
}
/* Form elements */
input, select, textarea {
font-size: 16px; /* Prevent zoom on iOS */
padding: 12px;
min-height: 44px;
touch-action: manipulation;
}
/* Cards and lists */
.minicard {
min-height: 48px;
padding: 12px;
margin-bottom: 8px;
touch-action: manipulation;
}
.list {
margin: 0 8px;
min-width: 280px;
}
/* Board canvas */
.board-canvas {
padding: 8px;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
/* Header mobile layout */
#header {
padding: 8px;
/* Keep top bar on a single row on small screens */
flex-wrap: nowrap;
align-items: center;
gap: 8px;
}
#header-quick-access {
/* Keep quick-access items in one row */
display: flex;
flex-direction: row;
align-items: center;
gap: 8px;
width: 100%;
}
/* Hide elements that should move to the hamburger menu on mobile */
#header-quick-access .header-quick-access-list,
#header-quick-access #header-help {
display: none !important;
}
/* Show only the home icon (hide the trailing text) on mobile */
#header-quick-access .home-icon a {
display: inline-flex;
align-items: center;
max-width: 28px; /* enough to display the icon */
overflow: hidden;
white-space: nowrap;
}
/* Hide text in home icon on mobile, show only icon */
#header-quick-access .home-icon a span:not(.fa) {
display: none !important;
}
/* Ensure proper spacing for mobile header elements */
#header-quick-access .zoom-controls {
margin-left: auto;
margin-right: 8px;
}
.mobile-mode-toggle {
margin-right: 8px;
}
#header-user-bar {
margin-left: auto;
}
/* Ensure header elements don't wrap on very small screens */
#header-quick-access {
min-width: 0; /* Allow flexbox to shrink */
}
/* Make sure logo doesn't take too much space on mobile */
#header-quick-access img {
max-height: 24px;
max-width: 120px;
}
/* Ensure zoom controls are compact on mobile */
.zoom-controls .zoom-level {
padding: 4px 8px;
font-size: 12px;
}
/* Modal mobile optimization */
#modal .modal-content,
#modal .modal-content-wide {
width: 95vw;
max-width: 95vw;
margin: 2vh auto;
padding: 16px;
max-height: 90vh;
overflow-y: auto;
}
/* Table mobile optimization */
table {
font-size: 14px;
width: 100%;
display: block;
overflow-x: auto;
white-space: nowrap;
-webkit-overflow-scrolling: touch;
}
/* Admin panel mobile optimization */
.setting-content .content-body {
flex-direction: column;
gap: 16px;
padding: 8px;
}
.setting-content .content-body .side-menu {
width: 100%;
order: 2;
}
.setting-content .content-body .main-body {
order: 1;
min-height: 60vh;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
}
/* Tablet devices (768px - 1024px) */
@media screen and (min-width: 768px) and (max-width: 1024px) {
#content > .wrapper {
padding: 12px;
}
.wrapper {
padding: 12px;
}
.panel-default {
width: 90vw;
max-width: 90vw;
}
/* Touch-friendly but more compact */
button, .btn, .js-toggle, .js-color-choice, .js-reaction, .close {
min-height: 48px;
min-width: 48px;
padding: 10px 14px;
}
.minicard {
min-height: 40px;
padding: 10px;
}
.list {
margin: 0 12px;
min-width: 300px;
}
.board-canvas {
padding: 12px;
}
#header {
padding: 12px 16px;
}
#modal .modal-content {
width: 80vw;
max-width: 600px;
}
#modal .modal-content-wide {
width: 90vw;
max-width: 800px;
}
.setting-content .content-body {
gap: 20px;
}
.setting-content .content-body .side-menu {
width: 250px;
}
}
/* Large displays and digital signage (1920px+) */
@media screen and (min-width: 1920px) {
body {
font-size: 18px;
}
button, .btn, .js-toggle, .js-color-choice, .js-reaction, .close {
min-height: 56px;
min-width: 56px;
padding: 16px 20px;
font-size: 18px;
}
.minicard {
min-height: 56px;
padding: 16px;
font-size: 18px;
}
.list {
margin: 0 8px;
min-width: 360px;
}
.board-canvas {
padding: 0;
}
#header {
padding: 0 8px;
}
#content > .wrapper {
padding: 0;
}
#modal .modal-content {
width: 600px;
}
#modal .modal-content-wide {
width: 1000px;
}
.setting-content .content-body {
gap: 32px;
}
.setting-content .content-body .side-menu {
width: 320px;
}
}
.inline-input {
+29 -22
View File
@@ -1,22 +1,24 @@
head
title
meta(name="viewport" content="width=device-width, initial-scale=1")
meta(http-equiv="X-UA-Compatible" content="IE=edge")
//- XXX We should use pathFor in the following `href` to support the case
where the application is deployed with a path prefix, but it seems to be
difficult to do that cleanly with Blaze -- at least without adding extra
packages.
link(rel="shortcut icon" type="image/x-icon" href="/favicon.ico")
link(rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png")
link(rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png")
link(rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png")
link(rel="manifest" crossOrigin="use-credentials" href="/site.webmanifest")
link(rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5")
meta(name="apple-mobile-web-app-title" content="Wekan")
meta(name="application-name" content="Wekan")
meta(name="msapplication-TileColor" content="#00aba9")
meta(name="theme-color" content="#ffffff")
//link(rel="stylesheet" type="text/css" class="__meteor-css__" href="css/html5-default-theme.css")
template(name="main")
html(lang="{{TAPi18n.getLanguage}}")
head
title
meta(name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5, user-scalable=yes")
meta(http-equiv="X-UA-Compatible" content="IE=edge")
//- XXX We should use pathFor in the following `href` to support the case
where the application is deployed with a path prefix, but it seems to be
difficult to do that cleanly with Blaze -- at least without adding extra
packages.
link(rel="shortcut icon" type="image/x-icon" href="/favicon.ico")
link(rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png")
link(rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png")
link(rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png")
link(rel="manifest" crossOrigin="use-credentials" href="/site.webmanifest")
link(rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5")
meta(name="apple-mobile-web-app-title" content="Wekan")
meta(name="application-name" content="Wekan")
meta(name="msapplication-TileColor" content="#00aba9")
meta(name="theme-color" content="#ffffff")
//link(rel="stylesheet" type="text/css" class="__meteor-css__" href="css/html5-default-theme.css")
template(name="userFormsLayout")
section.auth-layout
@@ -48,6 +50,8 @@ template(name="userFormsLayout")
if isLoading
+loader
else
// ARIA live region for error messages
div#login-error-message(role="alert" aria-live="assertive" style="color: #d32f2f; margin-bottom: 1em;")
+Template.dynamic(template=content)
if currentSetting.displayAuthenticationMethod
+connectionMethod(authenticationMethod=currentSetting.defaultAuthenticationMethod)
@@ -59,7 +63,8 @@ template(name="userFormsLayout")
if getLegalNoticeWithWritTraduction
div
div.at-form-lang
select.select-lang.js-userform-set-language
label(for="userform-set-language-select") {{_ 'changeLanguagePopup-title'}}
select.select-lang.js-userform-set-language#userform-set-language-select(aria-label="{{_ 'changeLanguagePopup-title'}}")
each languages
if isCurrentLanguage
option(value="{{tag}}" selected="selected") {{name}}
@@ -72,19 +77,21 @@ template(name="defaultLayout")
| {{{afterBodyStart}}}
+Template.dynamic(template=content)
| {{{beforeBodyEnd}}}
+migrationProgress
+boardConversionProgress
if (Modal.isOpen)
#modal
.overlay
if (Modal.isWide)
.modal-content-wide.modal-container
a.modal-close-btn.js-close-modal
i.fa.fa-times-thin
| ❌
+Template.dynamic(template=Modal.getHeaderName)
+Template.dynamic(template=Modal.getTemplateName)
else
.modal-content.modal-container
a.modal-close-btn.js-close-modal
i.fa.fa-times-thin
| ❌
+Template.dynamic(template=Modal.getHeaderName)
+Template.dynamic(template=Modal.getTemplateName)
+18
View File
@@ -86,6 +86,24 @@ Template.userFormsLayout.onRendered(() => {
validator,
);
EscapeActions.executeAll();
// Add autocomplete attribute to login input for WCAG compliance
const loginInput = document.querySelector('input[type="text"], input[type="email"]');
if (loginInput && loginInput.name && (loginInput.name.toLowerCase().includes('user') || loginInput.name.toLowerCase().includes('email'))) {
loginInput.setAttribute('autocomplete', 'username email');
}
// Add autocomplete attributes to password fields for WCAG compliance
const passwordInputs = document.querySelectorAll('input[type="password"]');
passwordInputs.forEach(input => {
if (input.name && input.name.includes('password')) {
if (input.name.includes('password_again') || input.name.includes('new_password')) {
input.setAttribute('autocomplete', 'new-password');
} else {
input.setAttribute('autocomplete', 'current-password');
}
}
});
});
});
+18 -18
View File
@@ -1,27 +1,27 @@
.my-cards-board-wrapper {
border-radius: 0 0 4px 4px;
min-width: 400px;
margin-bottom: 2rem;
border-radius: 0 0 0.5vw 0.5vw;
min-width: min(400px, 52vw);
margin-bottom: 2.5vh;
margin-right: auto;
margin-left: auto;
border-width: 2px;
border-width: 0.3vw;
border-style: solid;
border-color: #a2a2a2;
}
.my-cards-board-title {
font-size: 1.4rem;
font-size: clamp(1.2rem, 3vw, 1.6rem);
font-weight: bold;
padding: 0.5rem;
padding: 0.7vh 0.7vw;
background-color: #808080;
color: #fff;
}
.my-cards-swimlane-title {
font-size: 1.1rem;
font-size: clamp(1rem, 2.5vw, 1.3rem);
font-weight: bold;
padding: 0.5rem;
padding-bottom: 0.4rem;
padding: 0.7vh 0.7vw;
padding-bottom: 0.5vh;
margin-top: 0;
margin-bottom: 0.5rem;
margin-bottom: 0.7vh;
text-align: center;
}
.swimlane-default-color {
@@ -29,23 +29,23 @@
}
.my-cards-list-title {
font-weight: bold;
font-size: 1.1rem;
font-size: clamp(1rem, 2.5vw, 1.3rem);
text-align: center;
margin-bottom: 0.7rem;
margin-bottom: 0.9vh;
}
.my-cards-list-wrapper {
margin: 1rem;
border-radius: 5px;
margin: 1.3vh 1.3vw;
border-radius: 0.7vw;
display: inline-grid;
min-width: 250px;
max-width: 350px;
min-width: min(250px, 32vw);
max-width: min(350px, 45vw);
}
.my-cards-card-wrapper {
margin-top: 0;
margin-bottom: 10px;
margin-bottom: 1.3vh;
}
.my-cards-dueat-list-wrapper {
max-width: 500px;
max-width: min(500px, 65vw);
margin-right: auto;
margin-left: auto;
}
+9 -9
View File
@@ -3,23 +3,23 @@ template(name="myCardsHeaderBar")
h1
//a.back-btn(href="{{pathFor 'home'}}")
// i.fa.fa-chevron-left
i.fa.fa-list
| 📋
| {{_ 'my-cards'}}
.board-header-btns.left
a.board-header-btn.js-my-cards-view-change(title="{{_ 'myCardsViewChange-title'}}")
i.fa.fa-caret-down
| ▼
if $eq myCardsView 'boards'
i.fa.fa-trello
| 📋
| {{_ 'myCardsViewChange-choice-boards'}}
if $eq myCardsView 'table'
i.fa.fa-table
| 📊
| {{_ 'myCardsViewChange-choice-table'}}
template(name="myCardsModalTitle")
if currentUser
h2
i.fa.fa-keyboard-o
| ⌨️
| {{_ 'my-cards'}}
template(name="myCards")
@@ -102,15 +102,15 @@ template(name="myCardsViewChangePopup")
li
with "myCardsViewChange-choice-boards"
a.js-my-cards-view-boards
i.fa.fa-trello.colorful
| 📋
| {{_ 'myCardsViewChange-choice-boards'}}
if $eq Utils.myCardsView "boards"
i.fa.fa-check
| ✅
hr
li
with "myCardsViewChange-choice-table"
a.js-my-cards-view-table
i.fa.fa-table.colorful
| 📊
| {{_ 'myCardsViewChange-choice-table'}}
if $eq Utils.myCardsView "table"
i.fa.fa-check
| ✅
+312 -35
View File
@@ -1,16 +1,17 @@
.pop-over {
background: #fff;
border-radius: 3px;
border-radius: 0.4vw;
border: 1px solid #dbdbdb;
border-bottom-color: #c2c2c2;
box-shadow: 0 1px 6px rgba(0,0,0,0.3);
box-shadow: 0 0.2vh 0.8vh rgba(0,0,0,0.3);
position: absolute;
width: 300px;
/* Wider default to fit full color palette */
width: min(380px, 55vw);
z-index: 99999;
margin-top: 5px;
margin-top: 0.7vh;
}
.pop-over hr {
margin: 4px 0px;
margin: 0.5vh 0px;
}
.pop-over p,
.pop-over textarea,
@@ -22,27 +23,27 @@
}
.pop-over select {
width: 100%;
margin-bottom: 14px;
margin-bottom: 1.8vh;
}
.pop-over textarea {
height: 72px;
height: 9vh;
}
.pop-over form a span {
padding: 0 0.5rem;
padding: 0 0.7vw;
}
.pop-over .header {
height: 36px;
height: 4.5vh;
position: relative;
margin-bottom: 8px;
margin-bottom: 1vh;
background: #f7f7f7;
border-bottom: 1px solid #dcdcdc;
color: #666;
}
.pop-over .header .header-title {
display: block;
line-height: 32px;
padding-top: 4px;
margin: 0 10px;
line-height: 4vh;
padding-top: 0.5vh;
margin: 0 1.3vw;
font-weight: bold;
overflow: hidden;
text-overflow: ellipsis;
@@ -51,18 +52,18 @@
.pop-over .header .back-btn {
float: left;
overflow: hidden;
width: 30px;
width: 4vw;
transition: width 0.2s;
}
.pop-over .header .back-btn i.fa {
margin: 10px;
margin-top: 12px;
margin: 1.3vw;
margin-top: 1.5vh;
}
.pop-over .header .back-btn.is-hidden {
width: 0;
}
.pop-over .header .close-btn {
padding: 10px 10px 10px 4px;
padding: 1.3vh 1.3vw 1.3vh 0.5vw;
position: absolute;
top: 0;
right: 0;
@@ -72,27 +73,303 @@
}
.pop-over .content-wrapper {
width: 100%;
overflow: hidden;
max-height: calc(70vh + 20px);
overflow-y: auto;
overflow-x: hidden;
}
/* Allow dynamic max-height to override default constraint */
.pop-over[style*="max-height"] .content-wrapper {
max-height: inherit;
}
.pop-over .content-container {
width: 5000px;
max-height: 550px;
width: 100%;
max-height: calc(70vh + 20px);
transition: transform 0.2s;
}
/* Allow dynamic max-height to override default constraint for content-container */
.pop-over[style*="max-height"] .content-container {
max-height: inherit;
}
/* Ensure language popup list can scroll properly */
.pop-over .pop-over-list {
max-height: none;
overflow: visible;
}
/* Specific styling for language popup list */
.pop-over[data-popup="changeLanguage"] .pop-over-list {
max-height: none;
overflow: visible;
height: auto;
flex: 1;
}
/* Ensure content div in language popup contains all items */
.pop-over[data-popup="changeLanguage"] .content {
height: auto;
min-height: 100%;
display: flex;
flex-direction: column;
}
/* Allow dynamic height for Change Language popup */
.pop-over[data-popup="changeLanguage"] .content-wrapper {
max-height: inherit; /* Use dynamic height from JavaScript */
}
.pop-over[data-popup="changeLanguage"] .content-container {
max-height: inherit; /* Use dynamic height from JavaScript */
}
/* Make language popup extend to bottom of browser window */
.pop-over[data-popup="changeLanguage"] {
height: calc(100vh - 30px);
min-height: 300px;
/* Adjust positioning to move popup 30px higher */
transform: translateY(-30px);
}
.pop-over[data-popup="changeLanguage"] .content-wrapper {
height: calc(100% - 50px); /* Subtract header height more precisely */
min-height: 250px;
overflow-y: auto;
max-height: none; /* Remove any max-height constraints */
display: flex;
flex-direction: column;
}
.pop-over[data-popup="changeLanguage"] .content-container {
height: auto; /* Let content determine height */
min-height: 250px;
max-height: none; /* Remove any max-height constraints */
flex: 1;
display: flex;
flex-direction: column;
}
/* Date popup sizing for native HTML inputs */
.pop-over[data-popup="editCardReceivedDatePopup"],
.pop-over[data-popup="editCardStartDatePopup"],
.pop-over[data-popup="editCardDueDatePopup"],
.pop-over[data-popup="editCardEndDatePopup"],
.pop-over[data-popup*="Date"] {
width: min(400px, 90vw) !important; /* Smaller width for native inputs */
min-width: 350px !important;
max-height: 80vh !important;
}
.pop-over[data-popup="editCardReceivedDatePopup"] .content-wrapper,
.pop-over[data-popup="editCardStartDatePopup"] .content-wrapper,
.pop-over[data-popup="editCardDueDatePopup"] .content-wrapper,
.pop-over[data-popup="editCardEndDatePopup"] .content-wrapper,
.pop-over[data-popup*="Date"] .content-wrapper {
max-height: 60vh !important;
overflow-y: auto !important;
}
.pop-over[data-popup="editCardReceivedDatePopup"] .content-container,
.pop-over[data-popup="editCardStartDatePopup"] .content-container,
.pop-over[data-popup="editCardDueDatePopup"] .content-container,
.pop-over[data-popup="editCardEndDatePopup"] .content-container,
.pop-over[data-popup*="Date"] .content-container {
max-height: 60vh !important;
}
/* Native HTML input styling */
.pop-over[data-popup*="Date"] .datepicker-container {
width: 100% !important;
padding: 15px !important;
}
.pop-over[data-popup*="Date"] .datepicker-container .fields {
display: flex !important;
gap: 15px !important;
margin-bottom: 15px !important;
}
.pop-over[data-popup*="Date"] .datepicker-container .fields .left,
.pop-over[data-popup*="Date"] .datepicker-container .fields .right {
flex: 1 !important;
width: auto !important;
}
.pop-over[data-popup*="Date"] .datepicker-container label {
display: block !important;
margin-bottom: 5px !important;
font-weight: bold !important;
}
.pop-over[data-popup*="Date"] .datepicker-container input[type="date"],
.pop-over[data-popup*="Date"] .datepicker-container input[type="time"] {
width: 100% !important;
padding: 8px !important;
border: 1px solid #ccc !important;
border-radius: 4px !important;
font-size: 14px !important;
box-sizing: border-box !important;
}
.pop-over[data-popup*="Date"] .datepicker-container input[type="date"]:focus,
.pop-over[data-popup*="Date"] .datepicker-container input[type="time"]:focus {
outline: none !important;
border-color: #007cba !important;
box-shadow: 0 0 0 2px rgba(0, 124, 186, 0.2) !important;
}
/* Ensure date popup buttons stay within popup boundaries */
.pop-over[data-popup="editCardReceivedDatePopup"] .content,
.pop-over[data-popup="editCardStartDatePopup"] .content,
.pop-over[data-popup="editCardDueDatePopup"] .content,
.pop-over[data-popup="editCardEndDatePopup"] .content,
.pop-over[data-popup*="Date"] .content {
max-height: 60vh !important; /* Leave space for buttons */
overflow-y: auto !important;
padding-bottom: 100px !important; /* More space for buttons */
margin-bottom: 0 !important;
}
.pop-over[data-popup="editCardReceivedDatePopup"] .datepicker-container,
.pop-over[data-popup="editCardStartDatePopup"] .datepicker-container,
.pop-over[data-popup="editCardDueDatePopup"] .datepicker-container,
.pop-over[data-popup="editCardEndDatePopup"] .datepicker-container,
.pop-over[data-popup*="Date"] .datepicker-container {
max-height: 50vh !important; /* Limit calendar height */
overflow-y: auto !important;
margin-bottom: 20px !important; /* Space before buttons */
}
/* Ensure buttons are properly positioned */
.pop-over[data-popup="editCardReceivedDatePopup"] .edit-date,
.pop-over[data-popup="editCardStartDatePopup"] .edit-date,
.pop-over[data-popup="editCardDueDatePopup"] .edit-date,
.pop-over[data-popup="editCardEndDatePopup"] .edit-date,
.pop-over[data-popup*="Date"] .edit-date {
display: flex !important;
flex-direction: column !important;
height: 100% !important;
}
.pop-over[data-popup="editCardReceivedDatePopup"] .edit-date .fields,
.pop-over[data-popup="editCardStartDatePopup"] .edit-date .fields,
.pop-over[data-popup="editCardDueDatePopup"] .edit-date .fields,
.pop-over[data-popup="editCardEndDatePopup"] .edit-date .fields,
.pop-over[data-popup*="Date"] .edit-date .fields {
flex-shrink: 0 !important;
margin-bottom: 15px !important;
}
.pop-over[data-popup="editCardReceivedDatePopup"] .edit-date .js-datepicker,
.pop-over[data-popup="editCardStartDatePopup"] .edit-date .js-datepicker,
.pop-over[data-popup="editCardDueDatePopup"] .edit-date .js-datepicker,
.pop-over[data-popup="editCardEndDatePopup"] .edit-date .js-datepicker,
.pop-over[data-popup*="Date"] .edit-date .js-datepicker {
flex: 1 !important;
overflow-y: auto !important;
}
.pop-over[data-popup="editCardReceivedDatePopup"] .edit-date button,
.pop-over[data-popup="editCardStartDatePopup"] .edit-date button,
.pop-over[data-popup="editCardDueDatePopup"] .edit-date button,
.pop-over[data-popup="editCardEndDatePopup"] .edit-date button,
.pop-over[data-popup*="Date"] .edit-date button {
flex-shrink: 0 !important;
margin-top: 15px !important;
position: relative !important;
z-index: 10 !important;
}
.pop-over .content-container .content {
width: 280px;
padding: 0 10px 10px;
float: left;
/* Match wider popover, leave padding */
width: 100%;
padding: 0 1.3vw 1.3vh;
box-sizing: border-box;
/* Ensure content is not shifted left */
margin-left: 0 !important;
transform: none !important;
}
/* Utility: remove left gutter inside specific popups */
.pop-over .content .flush-left {
margin-left: 0;
padding-left: 0;
width: 100%;
}
/* Swimlane popups: remove left gutter, align content fully left */
.pop-over .content form.swimlane-color-popup,
.pop-over .content .swimlane-height-popup {
margin-left: 0;
padding-left: 0;
width: 100%;
}
/* Color selection popups: ensure proper alignment */
.pop-over .content form.swimlane-color-popup .palette-colors,
.pop-over .content form.edit-label .palette-colors,
.pop-over .content form.create-label .palette-colors {
margin-left: 0;
padding-left: 0;
width: 100%;
}
/* Color palette items: ensure proper positioning */
.pop-over .content .palette-colors .palette-color {
margin-left: 0;
margin-right: 2px;
margin-bottom: 2px;
}
/* Global fix for all popup content to prevent left shifting */
.pop-over .content * {
margin-left: 0 !important;
transform: none !important;
}
/* Override any potential left shifting for specific elements */
.pop-over .content form,
.pop-over .content .palette-colors,
.pop-over .content .pop-over-list,
.pop-over .content .flush-left {
margin-left: 0 !important;
padding-left: 0 !important;
transform: none !important;
}
/* Fix popup depth containers that cause left shifting */
.pop-over .popup-container-depth-1,
.pop-over .popup-container-depth-2,
.pop-over .popup-container-depth-3,
.pop-over .popup-container-depth-4,
.pop-over .popup-container-depth-5,
.pop-over .popup-container-depth-6 {
transform: none !important;
margin-left: 0 !important;
padding-left: 0 !important;
}
/* Ensure buttons don’t reserve left space; align to flow */
.pop-over .content form.swimlane-color-popup .primary.confirm,
.pop-over .content form.swimlane-color-popup .negate.wide.right,
.pop-over .content .swimlane-height-popup .primary.confirm,
.pop-over .content .swimlane-height-popup .negate.wide.right {
float: none;
margin-left: 0;
}
.pop-over .content-container .content.no-height {
height: 20px;
height: 0;
overflow: hidden;
padding: 0;
margin: 0;
visibility: hidden;
}
.pop-over .quiet {
/* padding: 6px 6px 4px;*/
}
.pop-over.search-over {
background: #f0f0f0;
min-height: 114px;
min-height: 14vh;
}
.pop-over.search-over .header {
display: none;
@@ -104,7 +381,7 @@
.pop-over .at-form .at-error,
.pop-over .at-form .at-result {
padding: 8px 12px;
margin: -8px -10px 10px;
margin: 0 0 10px 0;
}
.pop-over .at-form .at-error {
background: #ef9a9a;
@@ -148,7 +425,7 @@
font-weight: 700;
padding: 1.5px 10px;
position: relative;
margin: 0 -10px;
margin: 0;
text-decoration: none;
overflow: hidden;
line-height: 33px;
@@ -307,12 +584,12 @@
margin: 48px 0px 0px 0px;
}
.pop-over .content-container {
width: 1000%;
width: 100%;
height: 100%;
max-height: 100%;
}
.pop-over .content-container .content {
width: calc(10% - 20px);
width: calc(100% - 20px);
height: calc(100% - 20px);
padding: 10px;
}
@@ -334,21 +611,21 @@
margin: 0px 0px;
}
.pop-over .popup-container-depth-1 {
transform: translateX(-10%);
transform: none !important;
}
.pop-over .popup-container-depth-2 {
transform: translateX(-20%);
transform: none !important;
}
.pop-over .popup-container-depth-3 {
transform: translateX(-30%);
transform: none !important;
}
.pop-over .popup-container-depth-4 {
transform: translateX(-40%);
transform: none !important;
}
.pop-over .popup-container-depth-5 {
transform: translateX(-50%);
transform: none !important;
}
.pop-over .popup-container-depth-6 {
transform: translateX(-60%);
transform: none !important;
}
}
Loaded 100 of 767 files, more files were not shown because too many files have changed in this diff. Show more