Compare commits

...
507 Commits
Author SHA1 Message Date
Lauri Ojansivu 1b57d09d0d v9.02 2026-04-28 20:01:39 +03:00
Lauri Ojansivu e7d67e3ddb Updated ChangeLog. 2026-04-28 19:42:34 +03:00
Lauri Ojansivu a4b5ac6c4e Fix mongodb in snap 9.01 on Ubuntu 24.04 crashes because of missing libcrypto.so.1.1 .
Thanks to sbaecker !

Fixes #6320
2026-04-28 19:39:35 +03:00
Lauri Ojansivu 9b22f9e157 Updates 2026-04-28 15:09:04 +03:00
Lauri Ojansivu 2ab0f59f13 v9.01 2026-04-28 13:48:23 +03:00
Lauri Ojansivu 611368abc6 Removed old file. 2026-04-28 13:06:15 +03:00
Lauri Ojansivu d3b3be2a52 Updated ChangeLog. 2026-04-28 12:59:50 +03:00
Lauri Ojansivu 11a2c95ea7 Fix Snap mongodb port wrong.
Thanks to xet7 !

Fixes #6316
2026-04-28 12:58:09 +03:00
Lauri Ojansivu 3e612c39b7 Updated ChangeLog. 2026-04-28 12:09:51 +03:00
Lauri Ojansivu 58de6bb737 Disable not working GitHub workflows.
Thanks to xet7 !
2026-04-28 12:03:16 +03:00
Lauri Ojansivu f6d8befb56 Moved website to separate repo. Moved docs2 back to docs.
Thanks to xet7 !
2026-04-28 11:59:16 +03:00
Lauri Ojansivu b49aa050e9 Removed charts, that are moved back to github.com/wekan/charts.
Thanks to xet7 !
2026-04-28 11:33:40 +03:00
Lauri Ojansivu 2ef8465e8d 9.00.0 2026-04-28 11:26:02 +03:00
Lauri Ojansivu 7878596608 9.00.0 2026-04-28 11:02:54 +03:00
Lauri Ojansivu 1f2f9f571e Updated ChangeLog. 2026-04-27 19:48:27 +03:00
Lauri Ojansivu 855cb86088 Docs: Updated s390x page.
Thanks to xet7 !
2026-04-27 19:45:56 +03:00
Lauri Ojansivu ed3cf96c6f Updated ChangeLog. 2026-04-27 08:32:23 +03:00
Lauri Ojansivu fdb477f351 Updated Docker platforms for what currently builds successfully.
Thanks to xet7 !
2026-04-27 08:30:08 +03:00
Lauri Ojansivu ac6cc1041a 9.00 2026-04-27 03:57:36 +03:00
Lauri Ojansivu 7d09a649ac Prepare v9.00 release 2026-04-27 03:38:04 +03:00
Lauri Ojansivu 040ed6e605 v9.00 2026-04-27 03:17:40 +03:00
Lauri Ojansivu d7e36f5157 Updated ChangeLog. 2026-04-27 03:03:47 +03:00
Lauri Ojansivu 1a08003834 Merge pull request #6314 from aminsaidane/fix/mycards-subscription
Fix/mycards subscription
2026-04-26 22:38:43 +03:00
Lauri Ojansivu 64bc6ea71c Updated ChangeLog. 2026-04-26 22:01:14 +03:00
Lauri Ojansivu ef4161889a Updated website.
Thanks to xet7 !
2026-04-26 21:59:13 +03:00
aminsaidane dd9d6083c0 fix:remove leftover code 2026-04-26 14:38:00 +01:00
aminsaidane 027b6a45a1 fix(myCards): fix subscription crash caused by non-cursor in publish return* 2026-04-26 14:32:30 +01:00
Lauri Ojansivu b717e5d7ad Updated ChangeLog. 2026-04-24 22:33:24 +03:00
Lauri Ojansivu 74965f5f95 Updated dependencies.
Thanks to developers of dependencies !
2026-04-24 22:32:25 +03:00
Lauri Ojansivu fa812faf6d Updated ChangeLog. 2026-04-24 22:06:50 +03:00
Lauri Ojansivu b459151798 Updated dependencies.
Thanks to developers of dependencies !
2026-04-24 22:05:36 +03:00
Lauri Ojansivu 73679919fb Updated ChangeLog. 2026-04-24 21:51:08 +03:00
Lauri Ojansivu 58b0ae29e1 Revert Fix MongoDB duplicate key error in notification_cleanup cron job.
Thanks to xet7 !
2026-04-24 21:50:18 +03:00
Lauri Ojansivu 773931a538 Updated ChangeLog. 2026-04-24 16:41:08 +03:00
Lauri Ojansivu 1ef814fe6b Merge pull request #6311 from Raawen-dev/fix-cron-5110-only
Fix MongoDB duplicate key error in notification_cleanup cron job (Issue #5110)
2026-04-24 16:33:34 +03:00
Lauri Ojansivu b010c687f3 Merge branch 'main' into fix-cron-5110-only 2026-04-24 16:33:18 +03:00
Lauri Ojansivu 05950fb287 Merge pull request #6310 from Raawen-dev/fix-popup-6221-only
Fix list actions popup overflow (Issue #6221)
2026-04-24 16:19:56 +03:00
RAWEN KHEFIFI 56a685983b Merge upstream/main into fix-cron-5110-only 2026-04-23 17:54:47 +01:00
RAWEN KHEFIFI 339c0a2bb7 Fix MongoDB duplicate key error in notification_cleanup cron job (#5110)
- Add check to remove existing cron job before adding
- Wrap job logic in try-catch for duplicate key errors
- Prevents E11000 error in cronHistory collection
2026-04-23 17:51:13 +01:00
RAWEN KHEFIFIandRaawen-dev a790453c9a Fix list actions popup overflow (#6221)
- Append CSS fix to existing popup.css
- Add max-height: 300px and overflow-y: auto to .pop-over-list
- Prevents list actions menu from overflowing viewport
- Tested working on Mac (Chrome/Firefox)

Co-authored-by: Raawen-dev <rawenkhefifi19@gmail.com>
2026-04-23 17:50:45 +01:00
RAWEN KHEFIFI 04eb8a8fbb Fix list actions popup overflow (Issue #6221)
- Add max-height: 300px to .pop-over-list
- Enable scrolling with overflow-y: auto
- Prevent popup from overflowing viewport
2026-04-23 15:42:05 +01:00
Lauri Ojansivu 4ab1bc9638 Updated ChangeLog. 2026-04-23 05:00:38 +03:00
Lauri Ojansivu 699b9a9c71 Update release and version scripts. 2026-04-23 04:59:20 +03:00
Lauri Ojansivu 3a203d274d Release scripts: Fix version detection.
Thanks to xet7 !
2026-04-23 03:46:25 +03:00
Lauri Ojansivu 887511fdda Updated ChangeLog. 2026-04-23 03:43:17 +03:00
Lauri Ojansivu 64f58455ff Update releases scripts.
Thanks to xet7 !
2026-04-23 03:41:59 +03:00
Lauri Ojansivu 5163ec0da2 Updated ChangeLog. 2026-04-23 01:59:52 +03:00
Lauri Ojansivu c47eb013b4 Fix UI hangs and data loss by disabling changeStreams, until there is new Meteor release that included bugfix to changeStreams.
Thanks to bcook-konza and italojs !

Fixes #6307
2026-04-23 01:55:13 +03:00
Lauri Ojansivu d4cbe12316 Updated translations. 2026-04-23 01:34:57 +03:00
Lauri Ojansivu 794222c85e Updated ChangeLog. 2026-04-23 01:26:56 +03:00
Lauri Ojansivu 675c661114 Updated HoF.
Thanks to xet7 !
2026-04-23 01:07:46 +03:00
Lauri Ojansivu 7700efde1f Update security.md etc.
Thanks to xet7 !
2026-04-23 00:44:47 +03:00
Lauri Ojansivu 16832124ce Updated dependencies.
Thanks to developers of dependencies !
2026-04-22 03:53:46 +03:00
Lauri Ojansivu bf5ca3d3eb Added back jam packages. Updated dependencies.
Thanks to xet7 !
2026-04-22 03:47:13 +03:00
Lauri Ojansivu 189b6cae84 Merge pull request #6306 from wekan/dependabot/github_actions/actions/deploy-pages-5
Bump actions/deploy-pages from 4 to 5
2026-04-21 09:08:55 +03:00
Lauri Ojansivu 9bf0114148 Merge pull request #6305 from wekan/dependabot/github_actions/docker/setup-buildx-action-4
Bump docker/setup-buildx-action from 3 to 4
2026-04-21 09:08:37 +03:00
Lauri Ojansivu 9f3bbb5ed2 Merge pull request #6304 from wekan/dependabot/github_actions/actions/upload-pages-artifact-5
Bump actions/upload-pages-artifact from 4 to 5
2026-04-21 09:08:15 +03:00
dependabot[bot] 85b981e1c7 Bump actions/deploy-pages from 4 to 5
Bumps [actions/deploy-pages](https://github.com/actions/deploy-pages) from 4 to 5.
- [Release notes](https://github.com/actions/deploy-pages/releases)
- [Commits](https://github.com/actions/deploy-pages/compare/v4...v5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-20 22:22:25 +00:00
dependabot[bot] eb5ac8b7dd Bump docker/setup-buildx-action from 3 to 4
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 3 to 4.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-20 22:22:22 +00:00
dependabot[bot] 5ef32af248 Bump actions/upload-pages-artifact from 4 to 5
Bumps [actions/upload-pages-artifact](https://github.com/actions/upload-pages-artifact) from 4 to 5.
- [Release notes](https://github.com/actions/upload-pages-artifact/releases)
- [Commits](https://github.com/actions/upload-pages-artifact/compare/v4...v5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-20 22:22:18 +00:00
Lauri Ojansivu 9cb5837e3e Updated ChangeLog. 2026-04-18 19:14:46 +03:00
Lauri Ojansivu 5ae9c2b7ac Merge pull request #6303 from Raawen-dev/first-fix
Fix Issue #6007: Add permission checks to list width method
2026-04-18 19:12:06 +03:00
RAWEN KHEFIFI 7d16fed34e Fix Issue #6007: Add permission checks to applyListWidth method
- Add user authentication check
- Add board membership verification
- Change update to updateAsync to fix server error
- List widths now persist after page refresh
2026-04-18 17:04:58 +01:00
Lauri Ojansivu db2701f1d7 Update release scripts. In Progress.
Thanks to xet7 !
2026-04-17 18:34:36 +03:00
Lauri Ojansivu 76818e2c98 Updated ChangeLog. 2026-04-17 15:02:27 +03:00
Lauri Ojansivu c4e084ffbb Fix MongoDB replicaset setup at docker-compose.yml.
Thanks to C0rn3j.

Fixes #6297
2026-04-17 14:59:24 +03:00
Lauri Ojansivu a10b28bf6a Updated ChangeLog. 2026-04-17 14:50:30 +03:00
Lauri Ojansivu 90c0110615 Merge pull request #6299 from snowsky/main
Update cards.js
2026-04-17 14:46:00 +03:00
Hao 73c594bb36 Update cards.js
Fix the error:

```
Exception from sub myCards id xxx TypeError: Promise.await is not a function
```
2026-04-16 18:40:58 -05:00
Lauri Ojansivu 903544c680 Prepare v8.99 release 2026-04-16 22:15:49 +03:00
Lauri Ojansivu 32e1ec9d22 Release scripts: Update versions.
Thanks to xet7 !
2026-04-16 22:11:57 +03:00
Lauri Ojansivu 3516a21329 Prepare v8.98 release 2026-04-16 21:00:29 +03:00
Lauri Ojansivu f6198dee30 Release scripts: Updated creating Helm Charts to docs/wekan and docs/charts.
Thanks to xet7 !
2026-04-16 20:57:46 +03:00
Lauri Ojansivu 13f14df880 Updated Helm Charts. 2026-04-16 20:36:37 +03:00
Lauri Ojansivu f3ae64cf21 Prepare v8.98 release 2026-04-16 19:55:20 +03:00
Lauri Ojansivu 1820804b9f Fix Helm Chart parts to docs/wekan and docs/charts.
Thanks to xet7 !
2026-04-16 19:53:24 +03:00
Lauri Ojansivu d3d76b12f7 Prepare v8.98 release 2026-04-16 17:12:56 +03:00
Lauri Ojansivu 51ad6ed99d Release scripts: use node:24-slim directly.
Thanks to xet7 !
2026-04-16 17:11:24 +03:00
Lauri Ojansivu 1c8cea9ada Prepare v8.98 release 2026-04-16 16:47:23 +03:00
Lauri Ojansivu a0c190137d Release scripts: Use 24-slim fallback.
Thanks to xet7 !
2026-04-16 16:45:37 +03:00
Lauri Ojansivu 83e1845290 Prepare v8.98 release 2026-04-16 16:19:55 +03:00
Lauri Ojansivu 052d8c1584 Release scripts: Require only newest version number when releasing.
Thanks to xet7 !
2026-04-16 16:18:11 +03:00
Lauri Ojansivu 7b0e2213cf Release scripts: Fix updating version numbers.
Thanks to xet7 !
2026-04-16 16:06:19 +03:00
Lauri Ojansivu 05e6d15478 Prepare v8.98 release 2026-04-16 15:47:05 +03:00
Lauri Ojansivu be0283381b Release scripts: Use latest available Node.js slim docker images when building.
Thanks to xet7 !
2026-04-16 15:47:05 +03:00
github-actions[bot] 379a92846e v8.98 2026-04-16 12:33:19 +00:00
Lauri Ojansivu bb6163a70f Prepare v8.98 release 2026-04-16 15:32:31 +03:00
Lauri Ojansivu 10f009f5b1 Release scripts: Fix indentation at release-all.yml.
Thanks to xet7 !
2026-04-16 15:30:13 +03:00
Lauri Ojansivu 19c882ed2f Prepare v8.98 release 2026-04-16 15:23:37 +03:00
Lauri Ojansivu e7cb6ac6f0 Updated ChangeLog. 2026-04-16 15:20:40 +03:00
Lauri Ojansivu 38e488ee4a Moved Helm Charts from helm directory to docs/wekan and docs/charts to be at wekan.fi/wekan and wekan.fi/charts.
Thanks to xet7 !
2026-04-16 15:18:14 +03:00
Lauri Ojansivu 589fa30fd1 Updated ChangeLog. 2026-04-16 15:05:44 +03:00
Lauri Ojansivu 64d351d835 Commented out requirement to update website manually.
Thanks to xet7 !
2026-04-16 15:04:19 +03:00
Lauri Ojansivu 4151ecb7e7 Release scripts: Update versions at wekan.fi/install/ as part of GitHub Actions builds.
Thanks to xet7 !
2026-04-16 15:01:10 +03:00
Lauri Ojansivu f4b5e5f07d Updated ChangeLog. 2026-04-16 14:51:42 +03:00
Lauri Ojansivu c1639ea589 Update markdown docs links from docs to docs2.
Thanks to xet7 !
2026-04-16 14:50:12 +03:00
Lauri Ojansivu 21b0ab064c Updated ChangeLog. 2026-04-16 14:38:32 +03:00
Lauri Ojansivu 1c0d8e9964 Moved docs to docs2, and wekan.fi to docs, to be able publish with GitHub Pages.
Thanks to xet7 !
2026-04-16 14:36:10 +03:00
Lauri Ojansivu 8a7949a8ab Updated ChangeLog. 2026-04-16 14:30:01 +03:00
Lauri Ojansivu 3c36db33e9 Moved website from repo wekan/wekan.fi to wekan/wekan/docs/wekan.fi .
Thanks to xet7 !
2026-04-16 14:28:15 +03:00
Lauri Ojansivu 59d0cfc457 Updated ChangeLog. 2026-04-16 14:06:21 +03:00
Lauri Ojansivu 72f6d96dbc Release scripts: Detect newest MongoDB 7.x stable version and update to use it.
Thanks to xet7 !
2026-04-16 14:03:51 +03:00
Lauri Ojansivu 49c81f5426 Updated ChangeLog. 2026-04-16 13:44:42 +03:00
Lauri Ojansivu 08ed1705b1 Release scripts: Update versions at website with release-website.sh script.
Thanks to xet7 !
2026-04-16 13:44:42 +03:00
Lauri Ojansivu 47088c30c0 Release scripts: Detect newest Node.js 24.x version.
Thanks to xet7 !
2026-04-16 13:44:42 +03:00
github-actions[bot] 539dbdc034 Helm chart 8.97.0 2026-04-16 10:43:18 +00:00
github-actions[bot] 129af0bad9 v8.97 2026-04-16 10:02:21 +00:00
Lauri Ojansivu a9d95dd6b7 Prepare v8.97 release 2026-04-16 13:01:42 +03:00
Lauri Ojansivu 6d0978d0ed Release Scripts: Clean GitHub Cache. Part 3.
Thanks to xet7 !
2026-04-16 13:00:39 +03:00
Lauri Ojansivu 13cdf08e22 Release Scripts: Clean GitHub Cache. Part 2.
Thanks to xet7 !
2026-04-16 12:56:52 +03:00
Lauri Ojansivu e59bc9cd86 Release Scripts: Clean GitHub Cache.
Thanks to xet7 !
2026-04-16 12:53:42 +03:00
Lauri Ojansivu 6478d27d1c Fix GitHub Actions builds.
Thanks to xet7 !
2026-04-16 12:49:37 +03:00
Lauri Ojansivu 76d1800ec7 Prepare v8.96 release 2026-04-16 12:46:15 +03:00
Lauri Ojansivu 03ba747bc0 Fix GitHub Actions builds.
Thanks to xet7 !
2026-04-16 12:43:35 +03:00
Lauri Ojansivu 9eb3889d51 Prepare v8.95 release 2026-04-16 12:33:53 +03:00
Lauri Ojansivu 5d4bc7a886 Updated ChangeLog. 2026-04-16 12:31:58 +03:00
Lauri Ojansivu 135af977af Helm Chart: Use current Finland time for created field at index.yaml for newest built Helm Chart release.
Thanks to xet7 !
2026-04-16 12:26:44 +03:00
Lauri Ojansivu 6dfe81cbcb Fix Cannot find module /build/main.js error in latest Docker image.
Thanks to s8321414 and xet7 !

Fixes #6291
2026-04-16 12:16:58 +03:00
Lauri Ojansivu 3ec2821058 Updated ChangeLog. 2026-04-16 12:04:27 +03:00
Lauri Ojansivu 1c5ac8efba Merge pull request #6292 from titver968/helm-improvements
helm: add single-node ReplicaSet support with auto MONGO_OPLOG_URL
2026-04-16 12:00:01 +03:00
titver968 ce7fa0e314 helm: add single-node ReplicaSet support with auto MONGO_OPLOG_URL
- mongodb.url template now appends ?replicaSet=<name> for single-replica
  setups when mongodb.replicaSet.name is set, eliminating the need for
  a secretEnv workaround
- New mongodb.oplog.url helper generates MONGO_OPLOG_URL automatically
  when a ReplicaSet is configured (single-node or multi-node)
- deployment.yaml injects MONGO_OPLOG_URL env var when the oplog URL
  helper produces a non-empty value
- values.yaml documents the replicaSet.name option with example config
2026-04-16 09:58:44 +02:00
github-actions[bot] 0f464b21a3 Helm chart 8.94.0 2026-04-16 02:34:48 +00:00
github-actions[bot] 7169da8dae v8.94 2026-04-16 02:15:31 +00:00
Lauri Ojansivu 0f56e2dfc3 Prepare v8.94 release 2026-04-16 05:14:48 +03:00
Lauri Ojansivu 1b086c80a7 Updated ChangeLog. 2026-04-16 05:12:42 +03:00
Lauri Ojansivu c27d5d0412 Updated dompurify.
Thanks to developers of dompurify !
2026-04-16 05:10:59 +03:00
Lauri Ojansivu 7b3043bf46 Updated ChangeLog. 2026-04-16 05:09:46 +03:00
Lauri Ojansivu da19160e87 Fix Can't start latest docker image because of MongoDB Index errors.
Thanks to s8321414 and xet7 !

Fixes #6290
2026-04-16 05:09:46 +03:00
github-actions[bot] 790a801f30 Helm chart 8.93.0 2026-04-16 02:07:36 +00:00
github-actions[bot] b08c66a483 v8.93 2026-04-16 01:48:41 +00:00
Lauri Ojansivu 524aa9124d Prepare v8.93 release 2026-04-16 04:48:01 +03:00
Lauri Ojansivu df85b5ac45 Updated ChangeLog. 2026-04-16 04:44:04 +03:00
Lauri Ojansivu c009ddc915 Fix SSO Login broken. Part 2.
Thanks to xet7 !

Fixes #6276
2026-04-16 04:44:04 +03:00
github-actions[bot] 5407e4533c Helm chart 8.92.0 2026-04-16 01:08:11 +00:00
github-actions[bot] 98088c7734 v8.92 2026-04-16 00:49:02 +00:00
Lauri Ojansivu 0d6fb33043 Prepare v8.92 release 2026-04-16 03:48:15 +03:00
Lauri Ojansivu 1dbad1b454 Updated ChangeLog. 2026-04-16 03:44:46 +03:00
Lauri Ojansivu 2c8a6c8ec6 GitHub Actions: Added missing sudo to snap build.
Thanks to xet7 !
2026-04-16 03:42:56 +03:00
Lauri Ojansivu 06b9946dd8 Updated ChangeLog. 2026-04-16 03:36:32 +03:00
Lauri Ojansivu dbcdf15244 Updated Helm Charts versions.
Thanks to xet7 !
2026-04-16 03:36:32 +03:00
github-actions[bot] 7e2067d0b1 v8.91 2026-04-16 00:21:29 +00:00
Lauri Ojansivu 7489f720ea Prepare v8.91 release 2026-04-16 03:02:50 +03:00
Lauri Ojansivu 140e844238 Updated Helm Chart URL at readme.
Thanks to xet7 !
2026-04-16 03:02:50 +03:00
Lauri Ojansivu de55d5afbe Fix building Snap at GitHub Actions.
Thanks to xet7 !
2026-04-16 03:02:50 +03:00
github-actions[bot] c5fccb52cc Helm chart 8.90.0 2026-04-15 23:50:03 +00:00
github-actions[bot] 507ca57a50 v8.90 2026-04-15 23:28:22 +00:00
Lauri Ojansivu b2e072e02c Prepare v8.90 release 2026-04-16 02:27:44 +03:00
Lauri Ojansivu 357f63d01f Release scripts: Moved more steps to GitHub Actions.
Thanks to xet7 !
2026-04-16 02:24:17 +03:00
Lauri Ojansivu e6422d842a Updated ChangeLog. 2026-04-16 02:02:33 +03:00
Lauri Ojansivu 99519af83a Changed ArtifactHub from wekan/charts repo to wekan/wekan repo.
Thanks to xet7 !
2026-04-16 02:00:47 +03:00
Lauri Ojansivu 514ce6ad24 Updated ChangeLog. 2026-04-16 01:52:33 +03:00
Lauri Ojansivu f57ce52198 Added GitHub Actions for Docker and Snap. Moved Helm Charts from charts repo to wekan repo.
Thanks to xet7 !
2026-04-16 01:49:42 +03:00
Lauri Ojansivu 2a9c921ba6 Updated ChangeLog. 2026-04-16 00:54:13 +03:00
Lauri Ojansivu 0dd924f148 Removed jam:offline jam:method jam:pub-sub, because they are not used, and they cause too much database usage.
Thanks to xet7 !
2026-04-16 00:50:58 +03:00
Lauri Ojansivu 678eb0896d v8.89 2026-04-15 23:01:39 +03:00
Lauri Ojansivu 233c1c8e81 Updated ChangeLog. 2026-04-15 22:57:58 +03:00
Lauri Ojansivu f6a851a3d8 Disable jam:offline keepAll BEFORE any collections are created. Part 2.
Thanks to xet7 !
2026-04-15 22:56:01 +03:00
Lauri Ojansivu 4fba9d7802 v8.88 2026-04-15 21:45:44 +03:00
Lauri Ojansivu d41ecb054c Updated ChangeLog. 2026-04-15 21:37:09 +03:00
Lauri Ojansivu ec0c1bc9af Disable jam:offline keepAll to prevent full-collection COLLSCAN queries and Ensure standard GridFS index on attachments.chunks for efficient chunk lookups.
Disable jam:offline keepAll to prevent full-collection COLLSCAN queries.
WeKan uses modifiedAt (not updatedAt) and archived (not deleted),
so the default offline sync generates queries on non-existent fields
causing massive collection scans on every sync cycle.

Ensure standard GridFS index on attachments.chunks for efficient chunk lookups.
Without this, queries like find({files_id: ObjectId}) do full collection scans.
2026-04-15 21:33:18 +03:00
Lauri Ojansivu 05b77d24fa v8.87 2026-04-15 17:39:22 +03:00
Lauri Ojansivu 4535303807 Updated ChangeLog. 2026-04-15 17:37:33 +03:00
Lauri Ojansivu f232fdc30b Merge pull request #6287 from FlorentDotMe/fix_quick-access-scroll
ui: enable the horizontal scroll in the quick access menu boards list
2026-04-15 17:36:24 +03:00
Lauri Ojansivu dab72d80dc Updated ChangeLog. 2026-04-15 17:35:55 +03:00
Lauri Ojansivu 6f83464b24 Merge pull request #6286 from FlorentDotMe/fix_rooturl-as-env-variable
improve handling of ROOT_URL as an environement variable
2026-04-15 17:34:48 +03:00
Lauri Ojansivu 41cd0a8925 Updated ChangeLog. 2026-04-15 17:34:35 +03:00
Lauri Ojansivu fc02a15753 Try to fix Snap.
Thanks to xet7 !
2026-04-15 17:31:09 +03:00
Florent Bervas bd9c5a058a improve handling of ROOT_URL as an environement variable 2026-04-15 13:47:28 +00:00
Lauri Ojansivu c1692cc6b9 v8.86 2026-04-15 16:29:44 +03:00
Florent Bervas 5259c09a5c ui: enable the horizontal scroll in the quick access menu boards list 2026-04-15 13:28:08 +00:00
Lauri Ojansivu 10f801d31b Try to fix Snap.
Thanks to xet7 !
2026-04-15 16:25:06 +03:00
Lauri Ojansivu 28d19db328 v8.85 2026-04-15 16:02:59 +03:00
Lauri Ojansivu 0e13bfc50e Try to fix Snap.
Thanks to xet7 !
2026-04-15 16:00:33 +03:00
Lauri Ojansivu 4f4aa22ca1 v8.84 2026-04-15 15:29:15 +03:00
Lauri Ojansivu 6bec01198d Updated ChangeLog. 2026-04-15 15:27:02 +03:00
Lauri Ojansivu 5dbceb43b4 Updated ChangeLog. 2026-04-15 15:17:14 +03:00
Lauri Ojansivu bc92978fa9 Try to fix Snap Candidate.
Thanks to xet7 !
2026-04-15 15:16:02 +03:00
Lauri Ojansivu 893f83b79e Updated ChangeLog. 2026-04-15 15:09:53 +03:00
Lauri Ojansivu bde23f6c4d Merge pull request #6284 from cloudron-io/oidc-login-fix
Use local content variable in error case
2026-04-15 15:05:53 +03:00
Johannes Zellner e6d4b38c64 Use local content variable in error case 2026-04-15 10:27:53 +02:00
Lauri Ojansivu 23264044e6 v8.83 2026-04-14 21:39:23 +03:00
Lauri Ojansivu d51a381a79 Updated ChangeLog. 2026-04-14 21:32:31 +03:00
Lauri Ojansivu da4459304f Changed start-wekan.sh and start-wekan.bat to not use Change Streams.
Thanks to xet7 !
2026-04-14 21:31:08 +03:00
Lauri Ojansivu 085699f202 Updated ChangeLog. 2026-04-14 01:12:42 +03:00
Lauri Ojansivu 2c43aa463a Release scripts: Fix Node.js 24.x download URL for Snap builds.
Thanks to xet7 !
2026-04-14 01:09:26 +03:00
Lauri Ojansivu 41db7b4ad1 Release scripts: Docker not for armhf, no official Node.js binary for it.
Thanks to xet7 !
2026-04-14 00:58:35 +03:00
Lauri Ojansivu d0a9fa9a71 Release scripts: Removed armhf, there is no official Node.js binary for it.
Thanks to xet7 !
2026-04-14 00:38:55 +03:00
Lauri Ojansivu ed0675aaff v8.82 2026-04-14 00:37:28 +03:00
Lauri Ojansivu cad8f58468 Merge pull request #6282 from wekan/dependabot/github_actions/docker/build-push-action-7.1.0
Bump docker/build-push-action from 7.0.0 to 7.1.0
2026-04-14 00:31:12 +03:00
Lauri Ojansivu 819d58cf31 Merge pull request #6281 from wekan/dependabot/github_actions/softprops/action-gh-release-3
Bump softprops/action-gh-release from 2 to 3
2026-04-14 00:30:43 +03:00
dependabot[bot] b951515e3e Bump docker/build-push-action from 7.0.0 to 7.1.0
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 7.0.0 to 7.1.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/d08e5c354a6adb9ed34480a06d141179aa583294...bcafcacb16a39f128d818304e6c9c0c18556b85f)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-13 21:24:10 +00:00
dependabot[bot] 4c3265bca5 Bump softprops/action-gh-release from 2 to 3
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2 to 3.
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](https://github.com/softprops/action-gh-release/compare/v2...v3)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-version: '3'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-13 21:24:05 +00:00
Lauri Ojansivu 11672a13f7 Release scripts: Added script for deleting repo git tag.
Thanks to xet7 !
2026-04-14 00:19:15 +03:00
Lauri Ojansivu 9a8141a0eb v8.81 2026-04-14 00:16:28 +03:00
Lauri Ojansivu 5462fb0647 Release scripts: Comment out build for armhf because no official Node.js 24.x binaries exist for armhf (arm/v7).
Thanks to xet7 !
2026-04-14 00:10:51 +03:00
Lauri Ojansivu 6b74b8f770 v8.81 2026-04-13 22:57:32 +03:00
Lauri Ojansivu 99fff1a1a1 Updated ChangeLog. 2026-04-13 20:25:00 +03:00
Lauri Ojansivu 645f233b83 Merge pull request #6279 from kolaente/patch-1
fix(exporter): attachments always empty in board JSON export
2026-04-13 20:22:38 +03:00
kolaente 643e2738bd fix(exporter): attachments always empty in board JSON export 2026-04-13 18:15:48 +02:00
Lauri Ojansivu cd356a29ce Updated translations. 2026-04-13 17:16:10 +03:00
Lauri Ojansivu 341be982f9 Updated ChangeLog. 2026-04-13 17:14:21 +03:00
Lauri Ojansivu 2e7565f9eb Docs: Added History of Schema Migration Systems.
Thanks to xet7 !
2026-04-13 17:13:00 +03:00
Lauri Ojansivu 1637a9c938 Updated dependencies.
Thanks to developers of dependencies !
2026-04-13 17:11:04 +03:00
Lauri Ojansivu 67c2be1858 Updated ChangeLog. 2026-04-13 16:25:14 +03:00
Lauri Ojansivu b8d4c01e60 Updated ChangeLog. 2026-04-13 16:23:41 +03:00
Lauri Ojansivu e259ab86f7 Updated dependencies.
Thanks to developers of dependencies !
2026-04-13 16:23:41 +03:00
Lauri Ojansivu 029b3523cb Merge pull request #6275 from KhaoulaMaleh/fix/export
Fix: include historical comments and activities in board exports
2026-04-13 16:21:02 +03:00
KhaoulaMaleh f4657ad817 Fix: include historical comments and activities in board exports 2026-04-13 00:12:57 -11:00
Lauri Ojansivu 5efddff154 Updated translations. 2026-04-12 17:35:51 +03:00
Lauri Ojansivu 25efe93507 Updated ChangeLog. 2026-04-12 17:31:27 +03:00
Lauri Ojansivu a393e67ef8 Merge pull request #6262 from Raawen-dev/first-fix
Fix Issue #6069: Add migration for v7.95 due dates
2026-04-12 17:29:21 +03:00
Lauri Ojansivu 7f6758cbe9 Merge pull request #6268 from YosserDerbali/fix/board-drag-tooltip-clarification
Clarify board drag tooltip text in Remaining and Workspaces
2026-04-12 17:28:48 +03:00
Lauri Ojansivu c5644a3321 Updated ChangeLog. 2026-04-12 17:28:38 +03:00
Lauri Ojansivu 4fec03101f Updated ChangeLog. 2026-04-12 17:27:32 +03:00
Lauri Ojansivu 10fed0a85c Merge pull request #6271 from YosserDerbali/fix/readme-node-version-and-changes-made
Fix/readme node version and changes made
2026-04-12 17:25:51 +03:00
Lauri Ojansivu fff6316192 Updated ChangeLog. 2026-04-12 17:23:19 +03:00
Lauri Ojansivu 61623f50b1 Merge pull request #6273 from AymenHassini19/solve-white-container-“Link-Card”-dialog
Fix Link Card modal overflow in Docker environment
2026-04-12 17:21:59 +03:00
Lauri Ojansivu 9f626407cf Updated ChangeLog. 2026-04-12 17:20:26 +03:00
Lauri Ojansivu 3a51678495 Merge pull request #6272 from snowsky/fix-activities-not-showing
Fix activities are not showing
2026-04-12 17:14:02 +03:00
Aymen Hassini c730a0e733 Add missing Meteor test dependencies 2026-04-12 10:17:18 +01:00
Aymen Hassini 80a9971508 solving white container "Link Card" 2026-04-12 09:31:49 +01:00
Hao Wang 85788e1d7d Fix activities are not showing 2026-04-11 19:39:27 +00:00
Yosser Derbali 23680547ea docs: update README to Node.js 24.x and remove Rosetta mention 2026-04-11 14:30:04 +01:00
Yosser Derbali 9767ead9f3 docs: update README Node.js version and setup guidance 2026-04-10 22:26:16 +01:00
Yosser Derbali 910c9a7f1f docs: improve README with first-time setup guide and fix typos 2026-04-10 22:24:40 +01:00
Yosser Derbali 0bc6c6abb6 docs: fix typos and grammar in SECURITY.md
- Fix capitalization: 'About Money', 'QA About PubSub', etc.
- Add missing articles ('the', 'a')
- Fix spelling: 'Wordwide' -> 'Worldwide', 'logics' -> 'logic'
- Remove duplicate 'by' and extra spaces
- Fix grammar and sentence structure for clarity
- Improve consistency with WeKan capitalization
2026-04-10 22:24:40 +01:00
Yosser Derbali 6361e16dba Clarify board drag tooltip text 2026-04-10 18:21:04 +01:00
Lauri Ojansivu 8572cbd761 v8.80 2026-04-10 11:56:56 +03:00
Lauri Ojansivu 1c0b91c5f4 Updated translations. 2026-04-10 11:56:25 +03:00
Lauri Ojansivu 2ebefdd0bf Try to fix GitHub Actions builds with custom newer bcrypt.
Thanks to xet7 !
2026-04-10 11:53:09 +03:00
Lauri Ojansivu 29f1384945 v8.79 2026-04-09 16:00:52 +03:00
Lauri Ojansivu 334e569fda Updated translations. 2026-04-09 15:58:41 +03:00
Lauri Ojansivu 2d4170a9bf Fix 8.78: WeKan build Error: Could not find rspack config. Using MongoDB Change Streams at start-wekan.sh start-wekan.bat bundle, docker-compose.yml and Snap Candidate.
Thanks to nachocodoner and xet7 !

Fixes #6264
2026-04-09 15:42:05 +03:00
Lauri Ojansivu 6b2759f97d v8.78 2026-04-09 04:52:05 +03:00
Lauri Ojansivu adf4312e5a v8.77 2026-04-09 04:40:21 +03:00
Lauri Ojansivu b1707b8691 Upgraded to Meteor 3.5-beta.7
Thanks to Meteor developers !
2026-04-09 04:37:36 +03:00
Lauri Ojansivu 25dff3f2da v8.76 2026-04-09 04:19:51 +03:00
Lauri Ojansivu bc88c0dfc2 Fix GitHub Actions builds.
Thanks to xet7 !
2026-04-09 04:14:46 +03:00
Lauri Ojansivu 1ca2dfd1b1 v8.75 2026-04-09 03:58:01 +03:00
Lauri Ojansivu bdb9274485 Updated to Node.js 24.x at release scripts.
Thanks to xet7 !
2026-04-09 03:56:06 +03:00
Lauri Ojansivu 14f9ac39ad Upgraded to Meteor 3.5-beta.7 and Node.js 24.14.1. Now prefers MongoDB Change Streams at Snap Candidate and docker-compose.yml.
Thanks to Meteor developers and xet7 !
2026-04-09 03:44:58 +03:00
Lauri Ojansivu d4b52f7de9 Updated ChangeLog. 2026-04-09 02:19:44 +03:00
Lauri Ojansivu bdc65ec6f4 Merge pull request #6261 from KhaoulaMaleh/fix/translation-refinement
Refine translations
2026-04-08 23:30:39 +03:00
RAWEN KHEFIFI 74966a0bc7 Fix Issue #6069: Add migration for v7.95 due dates
- Add migration script to convert dueDate to dueAt
- Convert string dates to Date objects
- Add missing userId and members fields
- Fix invalid board/list/swimlane references

This fixes cards not appearing in Due Cards view after migration from v7.95 to v8.20
2026-04-08 16:44:19 +01:00
KhaoulaMaleh 7fb31cecd5 fix refinement 2026-04-08 00:45:27 -11:00
KhaoulaMaleh 317c82a9f0 fixed after review 2026-04-08 00:16:18 -11:00
KhaoulaMaleh 9b5c89d235 Enhance translations with new key:value pairs 2026-04-07 23:36:43 -11:00
Lauri Ojansivu c23e190e8e Updated ChangeLog. 2026-04-08 02:16:21 +03:00
Lauri Ojansivu 4a73371499 Merge pull request #6260 from KhaoulaMaleh/fix/Card_setting_checkbox
fixed Card settings checkboxes
2026-04-08 02:14:28 +03:00
KhaoulaMaleh 6f8af55489 fixed Card settings checkboxes 2026-04-08 00:04:48 -11:00
Lauri Ojansivu 2a05f62bf9 Updated ChangeLog. 2026-04-08 01:49:33 +03:00
Lauri Ojansivu d6eb1eb76f Merge pull request #6259 from KhaoulaMaleh/fix/enhance_translation
Enhance translations with new key:value pairs
2026-04-08 01:46:33 +03:00
Lauri Ojansivu 27542f3a51 v8.74 2026-04-08 00:34:49 +03:00
Lauri Ojansivu 0328e70d5f Fix can not save Card Data. Part 2.
Thanks to xet7 !

Fixes #6254
2026-04-08 00:31:40 +03:00
Lauri Ojansivu c46932ee30 v8.73 2026-04-07 23:18:14 +03:00
Lauri Ojansivu c7408ad8d1 Fix board and card activities. Part 2.
Thanks to xet7 !

Related #6252
2026-04-07 23:12:26 +03:00
Lauri Ojansivu 05dc8f8f93 Updated ChangeLog. 2026-04-07 22:01:59 +03:00
Lauri Ojansivu aea8c285c2 Fix Change list inside or card.
Thanks to mimZD and xet7 !

Fixes #6161
2026-04-07 22:00:11 +03:00
Lauri Ojansivu a859137971 Updated ChangeLog. 2026-04-07 21:53:11 +03:00
Lauri Ojansivu 08ebad0fe4 Fix Checklists add and Subtasks reorder. Fix Board Activity part 1.
Thanks to xet7 !

Related #6252
2026-04-07 21:49:26 +03:00
Lauri Ojansivu d54ebfa0e1 Updated ChangeLog. 2026-04-07 20:39:55 +03:00
Lauri Ojansivu 304a007d07 Fix Search All Boards result opened card missing Copy Card Link etc that can be used to open correct card.
Thanks bcook-konza and xet7 !

Fixes #6251
2026-04-07 20:35:20 +03:00
Lauri Ojansivu b6b58c9d95 Updated ChangeLog. 2026-04-07 20:10:38 +03:00
Lauri Ojansivu 7f0c42cca5 Fix Error in choosing member or assignee in a card.
Thanks to hmeunier95 and xet7 !

Fixes #6257
2026-04-07 20:08:04 +03:00
Lauri Ojansivu 9508d46ebd Updated ChangeLog. 2026-04-07 19:25:15 +03:00
Lauri Ojansivu bcbf4771ea Fix 8.72 Bug: LDAP login does not work. ReactiveCache is not defined.
Thanks to poc-sm and xet7 !

Fixes #6255
2026-04-07 19:23:06 +03:00
Lauri Ojansivu 9da5ec25c6 Updated ChangeLog. 2026-04-07 19:21:20 +03:00
Lauri Ojansivu 028b404377 Removed exclude-arch from rebuild-wekan.sh, it is not necessary anymore.
Thanks to xet7 !
2026-04-07 19:19:53 +03:00
Lauri Ojansivu 8da0f34ee6 Fix can not save Card Data.
Thanks to xet7 !

Fixes #6254
2026-04-07 19:17:35 +03:00
Lauri Ojansivu cac2628b19 Updated ChangeLog. 2026-04-07 11:40:46 +03:00
Lauri Ojansivu 9c69530256 Fix Link to Windows release doesn't work.
Thanks to hmeunier95 and xet7 !

Fixes #6253
2026-04-07 11:37:50 +03:00
Lauri Ojansivu 6e108358d7 v8.72 2026-04-07 10:44:37 +03:00
Lauri Ojansivu 87f592c67f Updated release scripts.
Thanks to xet7 !
2026-04-07 10:42:45 +03:00
Lauri Ojansivu fc95abd587 v8.72 2026-04-07 09:52:41 +03:00
Lauri Ojansivu ce3e51b147 Fix Impersonate User. Show/Edit Avatar at Admin Panel/People/People.
Thanks to ahlgrimma, DnT1mmer, robbagithub, norrig and xet7 !

Fixes #6226,
fixes #6101,
fixes #3327,
fixes #4748
2026-04-07 09:52:41 +03:00
Lauri Ojansivu bb48b23266 Merge pull request #6250 from wekan/dependabot/github_actions/docker/login-action-4.1.0
Bump docker/login-action from 4.0.0 to 4.1.0
2026-04-07 09:26:54 +03:00
Lauri Ojansivu fdf00e06f5 Merge pull request #6249 from wekan/dependabot/github_actions/actions/cache-5
Bump actions/cache from 4 to 5
2026-04-07 09:26:38 +03:00
Lauri Ojansivu 950b246da8 Merge pull request #6248 from wekan/dependabot/github_actions/actions/upload-artifact-7
Bump actions/upload-artifact from 4 to 7
2026-04-07 09:26:25 +03:00
Lauri Ojansivu 6ac0df8448 Merge pull request #6247 from wekan/dependabot/github_actions/actions/download-artifact-8
Bump actions/download-artifact from 4 to 8
2026-04-07 09:25:51 +03:00
dependabot[bot] 80ae3a18a8 Bump docker/login-action from 4.0.0 to 4.1.0
Bumps [docker/login-action](https://github.com/docker/login-action) from 4.0.0 to 4.1.0.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/b45d80f862d83dbcd57f89517bcf500b2ab88fb2...4907a6ddec9925e35a0a9e82d7399ccc52663121)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-06 20:33:05 +00:00
dependabot[bot] 972bfb9677 Bump actions/cache from 4 to 5
Bumps [actions/cache](https://github.com/actions/cache) from 4 to 5.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v4...v5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-06 20:33:01 +00:00
dependabot[bot] b1149e4d6e Bump actions/upload-artifact from 4 to 7
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 7.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4...v7)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-06 20:32:55 +00:00
dependabot[bot] 3cd2ba847b Bump actions/download-artifact from 4 to 8
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4 to 8.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v4...v8)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-06 20:32:49 +00:00
Lauri Ojansivu 8bf869654c Updated ChangeLog. 2026-04-06 05:09:38 +03:00
Lauri Ojansivu 3b98f862bc Fix Admin Panel / Version not showing name of MongoDB storage engine and status of MongoDB Oplog enabled.
Thanks to xet7 !
2026-04-06 05:05:19 +03:00
Lauri Ojansivu 9bc889e274 v8.71 2026-04-06 03:40:47 +03:00
Lauri Ojansivu 5df924f416 Fix: Added missing null check to notification.
Thanks to xet7 !
2026-04-06 03:35:28 +03:00
Lauri Ojansivu 5ef94b7079 v8.70 2026-04-06 02:43:39 +03:00
Lauri Ojansivu dfcaaf0ae7 Convert upsert calls to async in authentication.js .
Thanks to snowsky and xet7 !

Fixes https://github.com/wekan/wekan/pull/6243,
fixes https://github.com/wekan/wekan/pull/6245
2026-04-06 02:36:45 +03:00
Lauri Ojansivu 34e7ff532f v8.69 2026-04-06 01:32:30 +03:00
Lauri Ojansivu fbdfcead50 Try to fix Snap to start MongoDB OpLog correctly.
Thanks to xet7.
2026-04-06 01:28:46 +03:00
Lauri Ojansivu b8bbe4431b v8.68 2026-04-06 00:26:18 +03:00
Lauri Ojansivu 9e31666280 Try to fix Snap to start MongoDB OpLog correctly.
Thanks to xet7 !
2026-04-06 00:21:39 +03:00
Lauri Ojansivu 5846d9c9b0 v8.67 2026-04-05 23:22:53 +03:00
Lauri Ojansivu 7707a5b7fc Try to fix GitHub Actions release bundle builds.
Thanks to xet7.
2026-04-05 23:20:35 +03:00
Lauri Ojansivu 18c495940e v8.65 2026-04-05 23:11:32 +03:00
Lauri Ojansivu d3155d24c6 Try to fix GitHub Actions release bundle builds.
Thanks to xet7.
2026-04-05 23:09:36 +03:00
Lauri Ojansivu c0661506c4 Fix Docker Actions Test suite.
Thanks to xet7 !
2026-04-05 22:56:29 +03:00
Lauri Ojansivu fa2b34764f v8.64 2026-04-05 22:51:36 +03:00
Lauri Ojansivu 7a7909b49f Try to fix GitHub Actions release bundle builds.
Thanks to xet7 !
2026-04-05 22:49:33 +03:00
Lauri Ojansivu 17d3b3088f v8.63 2026-04-05 22:38:05 +03:00
Lauri Ojansivu 6a78a178e2 Try to fix GitHub Actions release bundle builds.
Thanks to xet7 !
2026-04-05 22:35:50 +03:00
Lauri Ojansivu 749de39e2a v8.62 2026-04-05 22:20:33 +03:00
Lauri Ojansivu cd55c0e12e Try to fix GitHub Actions bundle builds.
Thanks to xet7 !
2026-04-05 22:17:12 +03:00
Lauri Ojansivu d04037739e v8.61 2026-04-05 21:58:49 +03:00
Lauri Ojansivu f3a97b9938 Delete all GitHub Actions Caches, so that old stuff does not break builds.
Thanks to xet7 !
2026-04-05 21:55:09 +03:00
Lauri Ojansivu 104fbef37a Reverted disabling GitHub workflows. Still trying to fix them.
Thanks to xet7 !
2026-04-05 21:28:19 +03:00
Lauri Ojansivu 98b3dc71af Updated ChangeLog. 2026-04-05 21:15:52 +03:00
Lauri Ojansivu 7b28e4d589 Merge pull request #6246 from KhaoulaMaleh/fix/comment
Fix: Server crash during comment deletion
2026-04-05 21:14:09 +03:00
KhaoulaMaleh 57291dbe50 fixing deletion comment 2026-04-05 17:51:29 -11:00
Lauri Ojansivu 9f9eefce27 Updated ChangeLog. 2026-04-05 21:13:50 +03:00
Lauri Ojansivu af3361b35b Disabled not working GitHub workflows.
Thanks to xet7 !
2026-04-05 21:12:08 +03:00
Lauri Ojansivu 24d65e1e72 Disabled GitHub workflows that produce broken builds.
Thanks to xet7 !
2026-04-05 21:06:49 +03:00
Lauri Ojansivu 29922fca6a Updated ChangeLog. 2026-04-05 18:46:12 +03:00
Lauri Ojansivu 81319fc48b Updated Docker build script to check directories and repos exist.
Thanks to xet7 !
2026-04-05 18:44:07 +03:00
Lauri Ojansivu 28b7209407 v8.60 2026-04-05 18:14:31 +03:00
Lauri Ojansivu 0b59f87887 Updated Docker build script.
Thanks to xet7 !
2026-04-05 18:10:57 +03:00
Lauri Ojansivu b22df9e93d v8.59 2026-04-05 18:09:10 +03:00
Lauri Ojansivu 1fa72fd34a Fix Snap to use OpLog.
Thanks to xet7 !
2026-04-05 18:05:29 +03:00
Lauri Ojansivu a2f38e7494 Updated ChangeLog. 2026-04-05 17:41:52 +03:00
Lauri Ojansivu 30a1792c92 Fix docker-compose.yml to use OpLog.
Thanks to xet7 !
2026-04-05 17:40:01 +03:00
Lauri Ojansivu 3d70f24a78 v8.58 2026-04-05 16:45:54 +03:00
Lauri Ojansivu b3d14fb5e8 Updated build docs.
Thanks to xet7 !
2026-04-05 16:39:25 +03:00
Lauri Ojansivu 59956ca720 Try to fix .zip Bundle, Docker and Snap.
Thanks to xet7 !
2026-04-05 16:34:58 +03:00
Lauri Ojansivu aaf7690b9a v8.57 2026-04-05 15:18:00 +03:00
Lauri Ojansivu 9f549dfcd2 Try to fix Snap.
Thanks to xet7 !
2026-04-05 15:11:40 +03:00
Lauri Ojansivu f191392e3a v8.56 2026-04-05 07:36:06 +03:00
Lauri Ojansivu 56f26418ea Try to fix Snap.
Thanks to xet7 !
2026-04-05 07:33:22 +03:00
Lauri Ojansivu 118e0e5d5c v8.55 2026-04-05 06:58:46 +03:00
Lauri Ojansivu 264dcca42b Updated ChangeLog. 2026-04-05 06:48:47 +03:00
Lauri Ojansivu 73d69f4631 Updated dependencies.
Thanks to developers of dependencies !
2026-04-05 06:47:10 +03:00
Lauri Ojansivu 485b773338 Updated ChangeLog. 2026-04-05 06:39:23 +03:00
Lauri Ojansivu 8443e3511d Try to fix Snap.
Thanks to xet7 !
2026-04-05 06:38:16 +03:00
Lauri Ojansivu b2a349aec5 Updated ChangeLog. 2026-04-05 06:18:30 +03:00
Lauri Ojansivu faf8453c6d Fix Search All Boards.
Thanks to YosserDerbali and xet7 !

Fixes #6229,
fixes #6225
2026-04-05 06:13:53 +03:00
Lauri Ojansivu 7484c8cce3 Updated ChangeLog. 2026-04-05 01:34:55 +03:00
Lauri Ojansivu 2b02e7712e Fix Cards Reports is not working.
Thanks to titver968 and xet7 !

Fixes #6209
2026-04-05 01:32:04 +03:00
Lauri Ojansivu 8a3a7b19aa Updated ChangeLog. 2026-04-04 23:03:59 +03:00
Lauri Ojansivu 9fa6574c80 Docs: IMPORTANT: Do NOT npm audit fix --force, it breaks builds.
Thanks to xet7 !

Fixes #6224
2026-04-04 23:00:29 +03:00
Lauri Ojansivu 2bad17010c Docs: IMPORTANT: Do NOT npm audit fix --force, it breaks builds. 2026-04-04 22:59:16 +03:00
Lauri Ojansivu 4168385607 Updated ChangeLog. 2026-04-04 22:54:48 +03:00
Lauri Ojansivu c0aa1ef375 v8.54 2026-04-04 18:20:00 +03:00
Lauri Ojansivu 811b79b641 Revert v8.53 Refactor upsert calls to use async method.
Thanks to xet7 !
2026-04-04 18:17:26 +03:00
Lauri Ojansivu dab9755dd4 v8.53 2026-04-04 18:07:57 +03:00
Lauri Ojansivu 6863479c09 Updated ChangeLog. 2026-04-04 18:04:57 +03:00
Lauri Ojansivu e05eebb8b0 Merge pull request #6243 from snowsky/main
Refactor upsert calls to use async method
2026-04-04 18:02:03 +03:00
Hao 28ef0e6e89 Refactor upsert calls to use async method 2026-04-04 08:34:32 -05:00
Lauri Ojansivu 09e65ccac2 v8.52 2026-04-03 20:38:13 +03:00
Lauri Ojansivu 03e0cfdc15 Try to fix Docker and Snap.
Thanks to xet7 !
2026-04-03 20:35:54 +03:00
Lauri Ojansivu 7634cd6c2e v8.51 2026-04-03 20:26:08 +03:00
Lauri Ojansivu 4594711be4 Updated release scripts.
Thanks to xet7 !
2026-04-03 20:21:18 +03:00
Lauri Ojansivu c267933bf7 Updated ChangeLog. 2026-04-03 20:20:03 +03:00
Lauri Ojansivu edc6b18b5f Fix Bug: WeKan v8.50 WRITABLE_PATH.
Thanks to snowsky and xet7 !

Fixes #6242
2026-04-03 20:16:32 +03:00
Lauri Ojansivu 60657c6e94 v8.50 2026-04-03 04:40:19 +03:00
Lauri Ojansivu 3c189f9c7f Updated ChangeLog. 2026-04-03 04:32:15 +03:00
Lauri Ojansivu bf02c9a453 Updated dependencies.
Thanks to developers of dependencies.
2026-04-03 04:23:09 +03:00
Lauri Ojansivu e6b6cd3036 Updated ChangeLog. 2026-04-03 03:43:24 +03:00
Lauri Ojansivu 6e8589b1f9 Updated npm package @wekanteam/exceljs.
Thanks to xet7 !
2026-04-03 03:38:22 +03:00
Lauri Ojansivu 1354f84a70 Updated ChangeLog. 2026-04-03 00:31:11 +03:00
Lauri Ojansivu d07cf92f7c Merge pull request #6240 from snowsky/main
Fix indentation for release-all.yml workflow
2026-04-03 00:28:26 +03:00
Hao Wang e079644ed6 Fix indentation for release-all.yml workflow 2026-04-02 17:16:00 -04:00
Lauri Ojansivu 6b393178dc Updated translations. 2026-04-02 23:10:21 +03:00
Lauri Ojansivu 26e1c9ab69 Updated ChangeLog. 2026-04-01 00:57:36 +03:00
Lauri Ojansivu 145a17fee5 Uncommented build steps. Not yet tested are required dependencies included.
Thanks to xet7 !
2026-04-01 00:55:12 +03:00
Lauri Ojansivu ccc9301ed4 v8.49 2026-03-31 19:21:00 +03:00
Lauri Ojansivu f2e46d660f Updated ChangeLog. 2026-03-31 18:55:50 +03:00
Lauri Ojansivu 8b4676175c Try to fix Snap.
Thanks to xet7 !
2026-03-31 18:54:49 +03:00
Lauri Ojansivu e9a547c89b Updated ChangeLog. 2026-03-31 18:47:01 +03:00
Lauri Ojansivu d07fcceb6a Fix docker-compose.yml.
Thanks to xet7 !
2026-03-31 18:45:54 +03:00
Lauri Ojansivu 6b5002f314 Updated ChangeLog. 2026-03-31 18:21:50 +03:00
Lauri Ojansivu 5418ffd558 Merge pull request #6237 from kovacs-andras/main
fix: missing module '@meteorjs/reify/lib/runtime'
2026-03-31 18:17:38 +03:00
Andras Kovacs ea98afeb68 fix: missing module '@meteorjs/reify/lib/runtime' 2026-03-31 15:09:17 +02:00
Lauri Ojansivu be7d7e26fb v8.48 2026-03-31 05:56:09 +03:00
Lauri Ojansivu 56377aaf4c Updated release scripts. 2026-03-31 05:51:38 +03:00
Lauri Ojansivu 43601c5e3e Updated release scripts. 2026-03-31 05:31:29 +03:00
Lauri Ojansivu 1db7f3a57d v8.47 2026-03-31 05:04:33 +03:00
Lauri Ojansivu e58e99c285 Updated attachments and avatar path to be at WRITABLE_PATH/files/attachments etc.
Thanks to xet7 !
2026-03-31 05:00:17 +03:00
Lauri Ojansivu b38e25c9f1 v8.46 2026-03-31 04:32:57 +03:00
Lauri Ojansivu ce52b8a0ab Updated release scripts. 2026-03-31 04:29:23 +03:00
Lauri Ojansivu f7800846a5 Updated ChangeLog. 2026-03-31 04:00:52 +03:00
Lauri Ojansivu 02b75824e5 Updated docker-compose.yml to change from FerretDB/PostgreSQL to MongoDB.
Thanks to xet7 !
2026-03-31 03:59:07 +03:00
Lauri Ojansivu cf52c2f6e4 Updated ChangeLog. 2026-03-31 03:44:02 +03:00
Lauri Ojansivu 6bc68ba57b Updated release scripts.
Thanks to xet7 !
2026-03-31 03:42:19 +03:00
Lauri Ojansivu 5ec9cfa537 v8.45 2026-03-31 03:23:36 +03:00
Lauri Ojansivu 3457cab0b1 Updated ChangeLog. 2026-03-31 03:20:21 +03:00
Lauri Ojansivu 8349fb6b63 Updated release scripts. Added missing dependencies.
Thanks to xet7 !
2026-03-31 02:56:06 +03:00
Lauri Ojansivu 33267fa2e4 Try to fix Snap 2026-03-31 01:54:28 +03:00
Lauri Ojansivu 5c4130311a Try to fix Snap 2026-03-31 01:45:46 +03:00
Lauri Ojansivu ed718f083a Try to fix Snap 2026-03-31 01:40:40 +03:00
Lauri Ojansivu 2aa8ecfc39 Try to fix Snap 2026-03-31 01:35:24 +03:00
Lauri Ojansivu cdf1d3305b Try to fix Snap 2026-03-31 01:30:47 +03:00
Lauri Ojansivu fd58bb9e4d Try to fix Snap 2026-03-31 01:25:11 +03:00
Lauri Ojansivu 55ec0b5ff5 Try to fix Snap.
Thanks to xet7 !
2026-03-31 01:17:58 +03:00
Lauri Ojansivu e5c551f009 Docker registry Quay.io is currently read only, it's not possible to push newest WeKan Docker image to Quay.io
Thanks to xet7 !
2026-03-31 01:08:09 +03:00
Lauri Ojansivu 7666c6c0e1 Merge pull request #6235 from wekan/dependabot/github_actions/docker/login-action-4
Bump docker/login-action from 3 to 4
2026-03-31 00:58:41 +03:00
Lauri Ojansivu e6ccbfd3b8 Merge pull request #6234 from wekan/dependabot/github_actions/docker/setup-qemu-action-4
Bump docker/setup-qemu-action from 3 to 4
2026-03-31 00:58:23 +03:00
Lauri Ojansivu d8a2e06a95 Merge pull request #6233 from wekan/dependabot/github_actions/docker/build-push-action-7
Bump docker/build-push-action from 6 to 7
2026-03-31 00:58:06 +03:00
Lauri Ojansivu c38fa1c272 Merge pull request #6232 from wekan/dependabot/github_actions/actions/checkout-6
Bump actions/checkout from 4 to 6
2026-03-31 00:57:48 +03:00
Lauri Ojansivu 1a5e23617d Merge pull request #6231 from wekan/dependabot/github_actions/actions/setup-node-6
Bump actions/setup-node from 4 to 6
2026-03-31 00:57:22 +03:00
Lauri Ojansivu 87916c20bf Try to fix Snap. Part 2.
Thanks to xet7 !
2026-03-31 00:49:33 +03:00
Lauri Ojansivu 12c223d226 Try to fix Snap.
Thanks to xet7 !
2026-03-31 00:37:53 +03:00
dependabot[bot] 9ae2842fa4 Bump docker/login-action from 3 to 4
Bumps [docker/login-action](https://github.com/docker/login-action) from 3 to 4.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/v3...v4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-30 21:33:06 +00:00
dependabot[bot] 79853a2fb7 Bump docker/setup-qemu-action from 3 to 4
Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3 to 4.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-30 21:33:02 +00:00
dependabot[bot] ec1e383558 Bump docker/build-push-action from 6 to 7
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6 to 7.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v6...v7)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-30 21:32:59 +00:00
dependabot[bot] df067f01e8 Bump actions/checkout from 4 to 6
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 6.
- [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...v6)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-30 21:32:55 +00:00
dependabot[bot] 4b8cc0683e Bump actions/setup-node from 4 to 6
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4 to 6.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v4...v6)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-30 21:32:47 +00:00
Lauri Ojansivu f43b76c323 Updated release scripts.
Thanks to xet7 !
2026-03-31 00:07:19 +03:00
Lauri Ojansivu e90b2e384c Add release-all workflow for multi-platform builds
Add a GitHub Actions workflow for releasing all platforms, including version extraction, building bundles for multiple architectures, and creating a GitHub release.
2026-03-30 23:54:05 +03:00
Lauri Ojansivu 6125822cf7 Updated release scripts. 2026-03-30 23:53:23 +03:00
Lauri Ojansivu 4223c2a79f Updated release scripts.
Thanks to xet7 !
2026-03-30 23:39:29 +03:00
Lauri Ojansivu 7059cc244a v8.44 2026-03-30 22:36:22 +03:00
Lauri Ojansivu 5fc608e310 Updated ChangeLog. 2026-03-30 21:43:33 +03:00
Lauri Ojansivu 29550cbd93 Updated release scripts. Part 2.
Thanks to xet7 !
2026-03-30 21:42:24 +03:00
Lauri Ojansivu 35674a205b Updated ChangeLog. 2026-03-30 20:57:13 +03:00
Lauri Ojansivu a702596bee Updated release scripts.
Thanks to xet7 !
2026-03-30 20:53:20 +03:00
Lauri Ojansivu 02fef72e7e Updated ChangeLog. 2026-03-30 20:38:18 +03:00
Lauri Ojansivu d5c9e402ce Fix myCards crash.
Thanks to cristomoya !

Fixes #6202
2026-03-30 20:35:17 +03:00
Lauri Ojansivu b788d398b9 Fix globalSearch.
Thanks to snowsky !

Fixes #6230
2026-03-30 20:29:40 +03:00
Lauri Ojansivu 5c3b73e3b0 Updated ChangeLog. 2026-03-30 19:32:26 +03:00
Lauri Ojansivu 82f1de078f Updated dependencies.
Thanks to developers of dependencies !
2026-03-30 19:30:26 +03:00
Lauri Ojansivu ddc353023c Updated ChangeLog. 2026-03-30 18:54:44 +03:00
Lauri Ojansivu ef75b9ecac Always show all data, like Shared Lists that were before Per-Swimlanes lists, Orphaned Swimlanes/Lists/Cards etc.
Thanks to xet7 !
2026-03-30 18:46:27 +03:00
Lauri Ojansivu cf2b3e4fc9 From FerretDB/PostgreSQL back to MongoDB 7.x.
Thanks to xet7 !
2026-03-30 18:21:05 +03:00
Lauri Ojansivu cf21991a3f Updated translations. 2026-03-30 18:00:08 +03:00
Lauri Ojansivu 9e4f72e2a4 Updated Node.js at Windows docs 2026-03-28 01:04:30 +02:00
Lauri Ojansivu f7da243c9e Updated release scripts. Part 5.
Thanks to xet7 !
2026-03-28 00:49:32 +02:00
Lauri Ojansivu a71efd2668 v8.43 2026-03-28 00:37:33 +02:00
Lauri Ojansivu 6a23290584 Updated ChangeLog. 2026-03-27 23:25:02 +02:00
Lauri Ojansivu 0e59033ea6 Updated release scripts. Part 4.
Thanks to xet7 !
2026-03-27 23:24:02 +02:00
Lauri Ojansivu 186f7a6b82 Updated ChangeLog. 2026-03-27 23:21:41 +02:00
Lauri Ojansivu 5cf3bba68f Updated release scripts. Part 3.
Thanks to xet7 !
2026-03-27 23:19:55 +02:00
Lauri Ojansivu 4647ddc2b7 Updated ChangeLog. 2026-03-27 23:10:51 +02:00
Lauri Ojansivu 1ac708b0d9 Updated ChangeLog. 2026-03-27 22:49:43 +02:00
Lauri Ojansivu 07eb062474 Updated release scripts. Part 2.
Thanks to xet7 !
2026-03-27 22:45:52 +02:00
Lauri Ojansivu f660817627 Updated ChangeLog. 2026-03-27 20:55:15 +02:00
Lauri Ojansivu 0fdf45e847 Updated release scripts.
Thanks to xet7 !
2026-03-27 20:48:39 +02:00
Lauri Ojansivu 0c3cebd30a Merge pull request #6223 from YosserDerbali/fix/list-actions-popup-overflow-scroll
Fix/list actions popup overflow scroll
2026-03-27 20:47:09 +02:00
Yosser Derbali c589918989 Merge upstream/main and resolve popup.css conflict
- Keep upstream's cardDetailsActionsPopup styling
- Combine date picker container styling from both versions
- Ensure both list actions and card details popups work correctly
2026-03-27 19:42:25 +01:00
Yosser Derbali 256aa0a053 fix(popup): keep list actions popup inside viewport and scrollable 2026-03-27 19:17:00 +01:00
Lauri Ojansivu 67c56e41b0 Merge pull request #6222 from KhaoulaMaleh/fix/global-search
fix search: guard async methods in ReactiveCache and search publications
2026-03-27 20:16:52 +02:00
KhaoulaMaleh ade9e0f589 fix search: guard async methods in ReactiveCache and search publications 2026-03-27 18:36:32 -11:00
Lauri Ojansivu e14dfb9e7e Merge pull request #6220 from aminsaidane/fix/card-actions-popup-overflow
Fix card actions popup overflow
2026-03-27 20:15:36 +02:00
Lauri Ojansivu c26cc313fc Updated ChangeLog. 2026-03-27 19:22:45 +02:00
Lauri Ojansivu 55660e0e69 Updated dependencies. Part 2.
Thanks to developers of dependencies !
2026-03-27 19:20:27 +02:00
Lauri Ojansivu 3701544f6a Updated ChangeLog. 2026-03-27 19:04:28 +02:00
Lauri Ojansivu acfb081b90 Updated dependencies.
Thanks to developers of dependencies !
2026-03-27 19:02:42 +02:00
Lauri Ojansivu a30d7cabe3 Updated ChangeLog. 2026-03-27 18:59:12 +02:00
Lauri Ojansivu 517d148387 Moved Meteor 3 migration docs to docs/Upgrade/
Thanks to xet7 !
2026-03-27 18:56:43 +02:00
aminsaidane f8aec4de73 Fix card actions popup overflow
- Added max-height constraint to card details actions popup
- Ensured popup fits within viewport on all screen sizes
- Implemented scrollable content when menu exceeds viewport
- Used min(380px, 85vw) for responsive width sizing
- Prevents menu items from being cut off or hidden

Resolves card pop-up menu content overflow issue
2026-03-27 17:47:00 +01:00
Lauri Ojansivu e1b6cf6124 Updated translations 2026-03-27 18:37:27 +02:00
Lauri Ojansivu 1eac00e7fa Updated ChangeLog. 2026-03-27 18:18:24 +02:00
Lauri Ojansivu 278ed23440 Updated ChangeLog. 2026-03-27 18:15:31 +02:00
Lauri Ojansivu b42689a54d Fix attachment rename fails for clipboard uploads and keep extension metadata in sync.
Thanks to snowsky, YosserDerbali and xet7 !

Fixes #6203,
related https://github.com/wekan/wekan/pull/6217
2026-03-27 18:14:10 +02:00
Lauri Ojansivu 10bfa30458 Merge pull request #6218 from aminsaidane/fix/workspace-settings-modal-overflow
Fix workspace settings modal overflow
2026-03-27 18:09:37 +02:00
aminsaidane a0a1cda0a7 Fix workspace settings modal overflow
- Added top padding to popup content to prevent crowding
- Added max-height constraints for workspace actions popup
- Ensured modal fits within viewport on all screen sizes
- Implemented scrollable content when exceeding viewport
- Fixes issue with partially hidden controls in Workspace Settings modal

Closes #6200
2026-03-27 16:27:14 +01:00
Lauri Ojansivu 001af3fabd Updated translations. 2026-03-26 16:52:06 +02:00
Lauri Ojansivu c0316e9446 Merge pull request #6215 from KhaoulaMaleh/fix/csv-export
Fix: Clarify CSV export separator labels for i18n
2026-03-26 15:53:33 +02:00
KhaoulaMaleh d80a6e7e38 Fix: Clarify CSV export separator labels for i18n 2026-03-26 04:20:21 -11:00
Lauri Ojansivu 8d8d8f9cee Updated ChangeLog. 2026-03-26 01:45:40 +02:00
Lauri Ojansivu 0630d62ad0 Fix Set Color of Swimlane, List and Card.
Thanks to SVANNER, brlin-tw and xet7 !

Fixes #6207,
fixes #6212,
fixes #5660
2026-03-26 01:36:06 +02:00
Lauri Ojansivu c07b282dc9 Updated translations 2026-03-25 23:54:41 +02:00
Lauri Ojansivu 5aa39463b0 Updated ChangeLog. 2026-03-25 23:45:08 +02:00
Lauri Ojansivu f1e71247c7 Updated rebuild-wekan.sh for macOS 26.4.
Thanks to xet7 !
2026-03-25 23:40:47 +02:00
Lauri Ojansivu 419fb2535b Updated ChangeLog. 2026-03-25 21:57:25 +02:00
Lauri Ojansivu 23697d2a33 Fixed typo.
Thanks to YosserDerbali and xet7 !

Related https://github.com/wekan/wekan/pull/6210
2026-03-25 21:54:52 +02:00
Lauri Ojansivu 55edc7ef63 Updated ChangeLog. 2026-03-25 02:28:16 +02:00
Lauri Ojansivu c1e3ce64a7 Removed temporary debug logs.
Thanks to xet7 !
2026-03-25 02:26:52 +02:00
Lauri Ojansivu d34163402c Updated ChangeLog. 2026-03-25 01:40:03 +02:00
Lauri Ojansivu 5c5ed102ca Export Card to Excel. Part 3. Also, fixed uploading attachments.
Thanks to xet7 !

Fixes #5324
2026-03-25 01:37:42 +02:00
Lauri Ojansivu 0bbadfe9a6 Updated ChangeLog. 2026-03-24 21:53:49 +02:00
Lauri Ojansivu 21ab640686 Updated @meteorjs/rspack.
Thanks to Meteor developers!
2026-03-24 21:52:20 +02:00
Lauri Ojansivu a3f6d200ac Updated ChangeLog. 2026-03-24 21:41:58 +02:00
Lauri Ojansivu 701c10d618 Updated @meteorjs/rspack.
Thanks to Meteor developers!
2026-03-24 21:40:00 +02:00
Lauri Ojansivu 7f8de1f988 Updated ChangeLog. 2026-03-24 21:28:01 +02:00
Lauri Ojansivu 95ff8ef1f2 Updated dependencies of npm package @wekanteam/exceljs.
Thanks to xet7 !
2026-03-24 21:26:14 +02:00
Lauri Ojansivu 4eb885f88a Updated translations 2026-03-24 20:59:27 +02:00
Lauri Ojansivu 9a7a0b7271 Updated ChangeLog. 2026-03-24 20:57:26 +02:00
Lauri Ojansivu 4a8293f881 Fixed rebuild-wekan.sh to build WeKan correctly.
Thanks to xet7 !
2026-03-24 20:55:54 +02:00
Lauri Ojansivu 56ed341510 Updated ChangeLog. 2026-03-24 20:44:53 +02:00
Lauri Ojansivu 7584afda6d Upgraded to Meteor 3.4, Node.js 22.22, MongoDB 7.x, here related fixes.
Thanks to harryadel and xet7 !
2026-03-24 20:39:21 +02:00
Harry Adel bbb8f8e51d Merge branch 'main' into meteor-3-migration
# Conflicts:
#	.meteor/packages
#	.meteor/versions
#	models/attachments.js
#	models/boards.js
#	models/server/ExporterExcelCard.js
#	package-lock.json
2026-03-24 02:22:57 +02:00
Harry Adel ddd873c7a0 Fix client runtime regressions after Meteor 3 migration 2026-03-24 01:51:20 +02:00
Harry Adel 63ce45c538 Split shared models from Meteor 3 server runtime 2026-03-24 01:50:39 +02:00
Harry Adel 8ffc0cb5ff Centralize client boot and rspack package loading 2026-03-24 01:50:25 +02:00
Lauri Ojansivu ce95907778 v8.42 2026-03-23 20:07:35 +02:00
Lauri Ojansivu 4ed3250352 Updated ChangeLog. 2026-03-23 20:04:28 +02:00
Lauri Ojansivu 2dce7a84cd Fix SSO login is broken.
Thanks to sbaecker, vladimirdulov and xet7 !

Fixes #6204
2026-03-23 20:00:26 +02:00
Lauri Ojansivu b9631659f7 Updated version. 2026-03-23 18:36:04 +02:00
Harry Adel 8486bb1e87 Fix load order: two-stage entry points with require() bootstrap
ES module import statements are hoisted and evaluated in dependency-graph
order, not textual order. This means `import '/imports/collectionHelpers'`
being first in the file does NOT guarantee it runs first.

Fix: Split entry points into two stages:
- main.js: uses require() only (sequential, not hoisted)
  1. require('/imports/collectionHelpers') — loads collection2, SimpleSchema
  2. require('./imports') — loads all app code
- imports.js: contains all the import statements (safe because
  collectionHelpers has already fully loaded via require())

This pattern guarantees attachSchema/SimpleSchema/helpers are available
before ANY model file evaluates, regardless of circular dependency chains.
2026-03-22 19:03:11 +02:00
Harry Adel 655f44919a Fix attachSchema: convert collectionHelpers to pure require()
The root cause: import statements are hoisted and trigger evaluation
chains that reach model files (via reactiveCache circular deps)
BEFORE collection2 has patched Mongo.Collection.prototype.attachSchema.

Fix: Convert collectionHelpers.js to use ONLY require() calls.
require() is NOT hoisted — each call completes synchronously before
the next, guaranteeing collection2 fully loads before any model
file can run.
2026-03-22 18:59:09 +02:00
Harry Adel 17ea7978f0 Fix attachSchema: require collection2 directly in settings.js
settings.js is loaded early via circular dependency chain before
the entry point's hoisted imports finish. Add explicit require()
for aldeed:collection2 to ensure attachSchema is available.
2026-03-22 18:56:37 +02:00
Harry Adel b518946c41 Centralize collection2/SimpleSchema setup in collectionHelpers shim
Move all collection infrastructure into imports/collectionHelpers.js:
- Import aldeed:collection2 (provides attachSchema on Mongo.Collection)
- Import and set SimpleSchema as global
- Register denyUpdate/denyInsert as allowed schema extensions
- Provide Collection.helpers() shim

This ensures collection2 is loaded before any model file uses
attachSchema, regardless of circular dependency import ordering.
2026-03-22 18:54:01 +02:00
Harry Adel ca8a4af354 Fix SimpleSchema v2: add missing parent object declarations
SimpleSchema v2.0 requires explicit parent object declarations for
dot-notation sub-keys:
- settings.js: Add mailServer as Object parent for mailServer.* sub-keys
- watchable.js: Add watchers as [Object] parent for watchers.$.* sub-keys
2026-03-22 18:51:30 +02:00
Harry Adel 1682f8834d Fix denyUpdate/denyInsert: register as allowed SimpleSchema extensions
aldeed:simple-schema v2.0 no longer includes denyUpdate/denyInsert as
built-in properties. Register them via SimpleSchema.extendOptions() in:
- settings.js (first model loaded via circular dep chain)
- client/main.js and server/main.js (for all other models)
2026-03-22 18:42:53 +02:00
Harry Adel eac472c54b Fix SimpleSchema: use default import (not named export)
aldeed:simple-schema v2.0 uses `export default SimpleSchema`, not
a named export. Fix all import/require sites:
- settings.js: require().default
- attachmentStorageSettings.js: import default
- client/main.js, server/main.js: import default for global
2026-03-22 18:41:31 +02:00
Harry Adel 3a873559f8 Fix settings.js: use require() for ALL deps to prevent import hoisting
ES module import statements are hoisted above all code, so placing
Settings = new Mongo.Collection() between imports has no effect.
Convert ALL imports to require() so the collection definition
truly executes first, breaking the circular dependency.
2026-03-22 18:36:32 +02:00
Harry Adel aec6779601 Fix SimpleSchema not defined: import explicitly + set global
- settings.js: Import SimpleSchema explicitly from aldeed:simple-schema
  before attachSchema call (breaks circular dep ordering)
- client/main.js, server/main.js: Set SimpleSchema as global for the
  29 other model files that use it without importing
2026-03-22 18:30:38 +02:00
Harry Adel 973d9eccae Fix runtime errors: circular dependency in settings.js, fibers in users.js
- settings.js: Break circular import (settings → ReactiveCache → i18n →
  translation → ReactiveCache → settings) by defining Settings collection
  before require() calls instead of after import statements
- users.js: Make Users.after.insert hook async for template container
  creation (already committed fibers/future removal)
- fileStoreStrategy.js: Remove duplicate sanitizeFilename (already committed)
- wekanCreator.js: Fix forEach→for...of for await (already committed)
2026-03-22 18:27:41 +02:00
Harry Adel 7468b30001 Fix build errors: fibers/future, duplicate sanitizeFilename, forEach await
- users.js: Replace fibers/future pattern with async/await using
  insertAsync/updateAsync for template container creation
- fileStoreStrategy.js: Remove duplicate sanitizeFilename declaration
- wekanCreator.js: Convert attachments.forEach to for...of loop
  so await works inside the iteration body
2026-03-22 18:24:07 +02:00
Harry Adel e68cb5f32f Meteor 3.4 migration: Flip release to METEOR@3.4
Update .meteor/release from METEOR@2.16 to METEOR@3.4.
This activates Node 22, Express 5, async collection APIs,
MongoDB driver 6.x, and the Rspack bundler integration.
2026-03-22 17:58:34 +02:00
Harry Adel a157ae5d1c Meteor 3.4 migration: Module restructuring + Rspack setup
Create explicit entry points required by Meteor 3.4's Rspack bundler:

- client/main.js: imports all client code (models, components, jade
  templates, libraries, config, startup)
- server/main.js: imports all server code (models, methods, publications,
  routes, migrations, notifications, cron, auth)
- rspack.config.js: configures meteor-jade-loader for .jade files
- package.json: adds meteor.mainModule pointing to entry files
- .gitignore: adds Rspack build artifact directories
2026-03-22 17:58:04 +02:00
Harry Adel 620e5ce9dc Meteor 3.4 migration: Convert remaining sync methods to async
Convert all remaining Meteor.methods with sync collection calls:
- users.js: deleteWorkspace → async with findOneAsync/updateAsync
- userPositionHistory.js: 3 methods → async with insertAsync/fetchAsync
- attachmentStorageSettings.js: 4 methods → findOneAsync/insertAsync/upsertAsync
- org.js, team.js, translation.js: insert → insertAsync
- boards.js: acceptInvite + collection hooks → updateAsync
- settings.js: sendInvitation → updateAsync/insertAsync
- import.js: importBoard/cloneBoard → async with await creator.create()
- attachments.js: onAfterUpload → async with findOneAsync/updateAsync
2026-03-22 17:50:31 +02:00
Harry Adel 2e3fbf25db Meteor 3.4 migration: Convert allow/deny callbacks to async
Meteor 3.x supports async allow/deny callbacks. Convert all 25 sync
allow/deny callbacks across 23 model files to use async/await with
findOneAsync/fetchAsync instead of sync findOne/find().fetch().

Also converts helper functions in server/lib/utils.js:
- allowIsBoardMemberWithWriteAccessByCard -> async
- allowIsBoardMemberByCard -> async
- canUpdateCard (cards.js) -> async
- permissionHelper.allow (integrations.js) -> async
2026-03-22 17:44:29 +02:00
Harry Adel e8fee653c3 Meteor 3.4 migration: Phases 0-3
Phase 0 - Pre-migration cleanup:
- Remove msavin:usercache (unused), es5-shim, http@2.0.0, fibers
- Replace Meteor.wrapAsync in counters.js, exporter.js, attachmentUrlValidation.js
- Replace meteorhacks:subs-manager with Meteor.subscribe in 6 client files

Phase 1 - Create meteor-jade-loader:
- Custom Rspack/webpack loader for .jade templates
- Extracts mquandalle:jade-compiler pipeline (lexer, parser, transpiler)
- Bundles Meteor vendor packages (htmljs, html-tools, blaze-tools, spacebars-compiler)

Phase 2 - Package version bumps & replacements:
- Remove mquandalle:jade, dburles:collection-helpers, aldeed:simple-schema
- Remove communitypackages:picker (Meteor 3 bundles Express)
- Replace simple:json-routes -> communitypackages:json-routes
- Replace simple:rest-accounts-password -> communitypackages:rest-accounts-password
- Add collection-helpers shim at imports/collectionHelpers.js
- Convert Picker.route() to WebApp.handlers.get() in export files + sandstorm.js
- Add api.versionsFrom(['2.16', '3.0']) to all 9 custom packages
- Remove version pins from .meteor/packages

Phase 3 - Express 5 migration:
- Rename WebApp.connectHandlers -> WebApp.handlers (20 occurrences)
- Rename WebApp.rawConnectHandlers -> WebApp.rawHandlers (5 occurrences)
- Convert regexp route patterns to Express 5 named params in attachmentApi.js, avatarServer.js
2026-03-22 17:17:48 +02:00
Harry Adel 0d3f3474d1 Merge remote-tracking branch 'fork/use-email-sendAsync' into meteor-3-migration 2026-03-22 15:07:38 +02:00
Harry Adel 6ecb6bdaa7 Merge remote-tracking branch 'fork/remove-meteorhacks-aggregate' into meteor-3-migration 2026-03-22 15:07:33 +02:00
Harry Adel 073c297490 Remove unused meteorhacks:aggregate package for Meteor 3.0 migration
The only aggregation usage (metrics.js) already calls rawCollection().aggregate()
directly — the native MongoDB driver method — not the monkey-patched method from
this package. Convert to async/await (replacing Promise.await) for Meteor 3.0
Fibers removal compatibility.
2026-03-22 03:02:09 +02:00
921 changed files with 76686 additions and 40834 deletions

No files matched your search

+1 -1
View File
@@ -251,7 +251,7 @@ gosu wekan:wekan /home/wekan/.meteor/meteor -- help
# Build app (Development)
cd /home/wekan/app
gosu wekan:wekan /home/wekan/.meteor/meteor add standard-minifier-js
gosu wekan:wekan /home/wekan/.meteor/meteor npm install
gosu wekan:wekan /home/wekan/.meteor/meteor npm install --no-audit
# Put back the original tar
mv $(which tar)~ $(which tar)
+1 -4
View File
@@ -93,7 +93,6 @@
"Template": false,
"$": false,
"_": false,
"autosize": false,
"Avatar": true,
"Avatars": true,
@@ -111,8 +110,7 @@
"Ps": true,
"Restivus": false,
"SimpleSchema": false,
"SubsManager": false,
"T9n": false,
"T9n": false,
"TAPi18n": false,
"AccountsTemplates": true,
"Activities": true,
@@ -146,7 +144,6 @@
"Checklists": true,
"Settings": true,
"InvitationCodes": true,
"JsonRoutes": true,
"Authentication": true,
"Integrations": true,
"HTTP": true,
+686
View File
@@ -0,0 +1,686 @@
name: Release All Platforms
# Triggered manually via GitHub CLI or Actions UI.
# Usage:
# gh workflow run release-all.yml -f old_version=8.42 -f new_version=8.43
#
# Or use the convenience script (commits pending changes, pushes, and triggers):
# ./releases/release-all.sh 8.42 8.43
#
# Required GitHub Secrets:
# GHCR_AUTH - base64(username:token) for ghcr.io
# DOCKERHUB_AUTH - base64(username:token) for Docker Hub
# QUAY_AUTH - base64(username:token) for Quay.io
# SNAP_AUTH - Snapcraft store credentials
on:
workflow_dispatch:
inputs:
old_version:
description: 'Previous version number (e.g. 8.42, without v prefix)'
required: true
type: string
new_version:
description: 'New version number (e.g. 8.43, without v prefix)'
required: true
type: string
permissions:
contents: write
packages: write
pages: write
id-token: write
jobs:
# ─── 1. Extract version and release notes ───────────────────────────────────
prepare:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
changelog: ${{ steps.changelog.outputs.content }}
steps:
- uses: actions/checkout@v6
with:
ref: refs/tags/v${{ needs.prepare.outputs.version }}
- name: Set version from input
id: version
run: |
VERSION="${{ needs.prepare.outputs.version }}"
VERSION="${VERSION#v}"
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Extract release notes from CHANGELOG.md
id: changelog
run: |
VERSION="${{ steps.version.outputs.version }}"
# Try to find the version-specific section first, then fall back to Upcoming
NOTES=$(python3 - <<PYEOF
import re, sys
version = "$VERSION"
with open("CHANGELOG.md", encoding="utf-8") as f:
content = f.read()
# Pattern: section heading through (but not including) the next heading
patterns = [
rf"(# v{re.escape(version)} .*?)(?=\n# v[0-9]|\Z)",
r"(# Upcoming WeKan.*?)(?=\n# v[0-9]|\Z)",
]
for pat in patterns:
m = re.search(pat, content, re.DOTALL)
if m:
print(m.group(1).strip())
sys.exit(0)
print(f"WeKan v{version}")
PYEOF
)
echo "content<<GHEOF" >> $GITHUB_OUTPUT
echo "$NOTES" >> $GITHUB_OUTPUT
echo "GHEOF" >> $GITHUB_OUTPUT
# ─── 2. Build amd64 bundle (full Meteor 3 build) ────────────────────────────
# Meteor 3.5-beta.7 installer supports only: Linux x86_64, Linux aarch64, macOS, Windows.
# It explicitly rejects armhf, s390x, and ppc64le at install time.
# Therefore the full Meteor build always runs here on x86_64.
# All other Linux architectures then rebuild only the native npm modules
# from this bundle (much faster than a full Meteor build).
# Node.js version must match what Meteor 3.5-beta.7 bundles (detected automatically).
build-amd64:
needs: prepare
runs-on: ubuntu-24.04
timeout-minutes: 180
env:
VERSION: ${{ needs.prepare.outputs.version }}
NODE_VERSION: ${{ needs.prepare.outputs.version }}
steps:
- uses: actions/checkout@v6
with:
ref: refs/tags/v${{ inputs.new_version }}
- name: Cache Meteor installation
uses: actions/cache@v5
with:
path: ~/.meteor
key: meteor-${{ hashFiles('.meteor/release') }}-${{ hashFiles('.meteor/packages') }}
restore-keys: meteor-
- name: Cache npm packages
uses: actions/cache@v5
with:
path: ~/.npm
key: npm-${{ hashFiles('package-lock.json') }}
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y build-essential g++ make python3 curl wget zip unzip
# Pin to detected latest Node.js 24.x — must match Meteor's bundled version.
# Using the same version for build and npm rebuild ensures consistent native module ABI.
- uses: actions/setup-node@v6
with:
node-version: '${{ needs.prepare.outputs.version }}'
- name: Install Meteor
run: |
if [ ! -f ~/.meteor/meteor ]; then
curl https://install.meteor.com/ | sh
fi
echo "$HOME/.meteor" >> $GITHUB_PATH
- name: Build WeKan bundle
run: |
rm -rf .build node_modules .meteor/local
(meteor update --npm 2>/dev/null || true) && meteor npm install
meteor build .build --directory
- name: Create amd64 zip
run: |
cd .build
zip -r wekan-${VERSION}-amd64.zip bundle
- uses: actions/upload-artifact@v7
with:
name: bundle-amd64
path: .build/wekan-${{ needs.prepare.outputs.version }}-amd64.zip
retention-days: 1
# ─── 3. Build arm64 bundle (rebuild native modules natively) ─────────────────
# Meteor 3.5-beta.7 does support Linux aarch64, but running a full Meteor build a
# second time would double the build time for the same result. npm rebuild on
# the native arm64 GitHub Actions runner is much faster and produces the same
# platform-specific native modules.
build-arm64:
needs: [prepare, build-amd64]
runs-on: ubuntu-24.04-arm
timeout-minutes: 60
env:
VERSION: ${{ needs.prepare.outputs.version }}
NODE_VERSION: ${{ needs.prepare.outputs.version }}
NODE_VERSION_ARM64: ''
steps:
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y build-essential g++ make python3 curl unzip zip
- name: Set Node.js version for arm64
id: node_arm64
run: |
echo "NODE_VERSION_ARM64=24" >> $GITHUB_ENV
- name: Download amd64 bundle
uses: actions/download-artifact@v8
with:
name: bundle-amd64
- name: Rebuild native modules for arm64 in container
run: |
unzip wekan-${VERSION}-amd64.zip
# Fix: bcrypt 5.0.1 (Meteor accounts-password) ships node-addon-api@3
# which fails to compile on Node.js 24. Replace with bcrypt 6.0.0.
BCRYPT_DIR="$(pwd)/bundle/programs/server/npm/node_modules/meteor/accounts-password/node_modules/bcrypt"
if [ -d "$BCRYPT_DIR" ]; then
rm -rf "$BCRYPT_DIR"
TMP=$(mktemp -d)
pushd "$TMP" > /dev/null
npm pack bcrypt@6.0.0
mkdir bcrypt && tar xzf bcrypt-*.tgz --strip-components=1 -C bcrypt
cd bcrypt && npm install --ignore-scripts && cd ..
cp -a bcrypt "$BCRYPT_DIR"
popd > /dev/null
rm -rf "$TMP"
fi
# Use the latest available Node.js 24.x slim image for arm64
docker run --rm --platform linux/arm64 \
-v "$(pwd)/bundle:/bundle" \
-w /bundle/programs/server \
node:24-slim \
sh -c "apt-get update -qq && apt-get install -y -q build-essential python3 && npm install"
- name: Clean up and create arm64 zip
run: |
zip -r wekan-${VERSION}-arm64.zip bundle
- uses: actions/upload-artifact@v7
with:
name: bundle-arm64
path: wekan-${{ needs.prepare.outputs.version }}-arm64.zip
retention-days: 1
# ─── 4. Build s390x bundle (rebuild native modules via QEMU container) ───────
# Meteor 3.5-beta.7 does NOT support s390x (installer exits with error on that arch).
# GitHub Actions has no native s390x runners.
# Solution: QEMU emulation + node:<version>-slim (s390x variant) for npm rebuild.
build-s390x:
needs: [prepare, build-amd64]
runs-on: ubuntu-latest
timeout-minutes: 90
env:
VERSION: ${{ needs.prepare.outputs.version }}
NODE_VERSION: ${{ needs.prepare.outputs.version }}
NODE_VERSION_S390X: ''
steps:
- name: Set up QEMU for s390x
uses: docker/setup-qemu-action@v4
with:
platforms: s390x
- name: Install unzip/zip
run: sudo apt-get update && sudo apt-get install -y unzip zip
- name: Download amd64 bundle
uses: actions/download-artifact@v8
with:
name: bundle-amd64
- name: Set Node.js version for s390x
id: node_s390x
run: |
echo "NODE_VERSION_S390X=24" >> $GITHUB_ENV
- name: Rebuild native modules for s390x in container
run: |
unzip wekan-${VERSION}-amd64.zip
rm -rf bundle/programs/server/node_modules
# Fix: bcrypt 5.0.1 (Meteor accounts-password) ships node-addon-api@3
# which fails to compile on Node.js 24. Replace with bcrypt 6.0.0.
BCRYPT_DIR="$(pwd)/bundle/programs/server/npm/node_modules/meteor/accounts-password/node_modules/bcrypt"
if [ -d "$BCRYPT_DIR" ]; then
rm -rf "$BCRYPT_DIR"
TMP=$(mktemp -d)
pushd "$TMP" > /dev/null
npm pack bcrypt@6.0.0
mkdir bcrypt && tar xzf bcrypt-*.tgz --strip-components=1 -C bcrypt
cd bcrypt && npm install --ignore-scripts && cd ..
cp -a bcrypt "$BCRYPT_DIR"
popd > /dev/null
rm -rf "$TMP"
fi
# Use the latest available Node.js 24.x slim image for s390x
docker run --rm --platform linux/s390x \
-v "$(pwd)/bundle:/bundle" \
-w /bundle/programs/server \
node:24-slim \
sh -c "apt-get update -qq && apt-get install -y -q build-essential python3 && npm install"
- name: Clean up and create s390x zip
run: |
zip -r wekan-${VERSION}-s390x.zip bundle
- uses: actions/upload-artifact@v7
with:
name: bundle-s390x
path: wekan-${{ needs.prepare.outputs.version }}-s390x.zip
retention-days: 1
# ─── 4b. Build ppc64le bundle (rebuild native modules via QEMU container) ────
# Meteor 3.5-beta.7 does NOT support ppc64le (installer exits with error on that arch).
# GitHub Actions has no native ppc64le runners.
# Solution: QEMU emulation + node:<version>-slim (ppc64le variant) for npm rebuild.
build-ppc64le:
needs: [prepare, build-amd64]
runs-on: ubuntu-latest
timeout-minutes: 90
env:
VERSION: ${{ needs.prepare.outputs.version }}
NODE_VERSION: ${{ needs.prepare.outputs.version }}
NODE_VERSION_PPC64LE: ''
steps:
- name: Set up QEMU for ppc64le
uses: docker/setup-qemu-action@v4
with:
platforms: ppc64le
- name: Install unzip/zip
run: sudo apt-get update && sudo apt-get install -y unzip zip
- name: Download amd64 bundle
uses: actions/download-artifact@v8
with:
name: bundle-amd64
- name: Set Node.js version for ppc64le
id: node_ppc64le
run: |
echo "NODE_VERSION_PPC64LE=24" >> $GITHUB_ENV
- name: Rebuild native modules for ppc64le in container
run: |
unzip wekan-${VERSION}-amd64.zip
rm -rf bundle/programs/server/node_modules
# Fix: bcrypt 5.0.1 (Meteor accounts-password) ships node-addon-api@3
# which fails to compile on Node.js 24. Replace with bcrypt 6.0.0.
BCRYPT_DIR="$(pwd)/bundle/programs/server/npm/node_modules/meteor/accounts-password/node_modules/bcrypt"
if [ -d "$BCRYPT_DIR" ]; then
rm -rf "$BCRYPT_DIR"
TMP=$(mktemp -d)
pushd "$TMP" > /dev/null
npm pack bcrypt@6.0.0
mkdir bcrypt && tar xzf bcrypt-*.tgz --strip-components=1 -C bcrypt
cd bcrypt && npm install --ignore-scripts && cd ..
cp -a bcrypt "$BCRYPT_DIR"
popd > /dev/null
rm -rf "$TMP"
fi
# Use the latest available Node.js 24.x slim image for ppc64le
docker run --rm --platform linux/ppc64le \
-v "$(pwd)/bundle:/bundle" \
-w /bundle/programs/server \
node:24-slim \
sh -c "apt-get update -qq && apt-get install -y -q build-essential python3 && npm install"
- name: Clean up and create ppc64le zip
run: |
zip -r wekan-${VERSION}-ppc64le.zip bundle
- uses: actions/upload-artifact@v7
with:
name: bundle-ppc64le
path: wekan-${{ needs.prepare.outputs.version }}-ppc64le.zip
retention-days: 1
# ─── 4c. Build armhf bundle — COMMENTED OUT ─────────────────────────────────
# No official Node.js binaries exist for armhf (arm/v7).
#build-armhf:
# needs: [prepare, build-amd64]
# runs-on: ubuntu-latest
# timeout-minutes: 120
# env:
# VERSION: ${{ needs.prepare.outputs.version }}
# steps:
# - name: Set up QEMU for arm/v7
# uses: docker/setup-qemu-action@v4
# with:
# platforms: arm
#
# - name: Install unzip/zip
# run: sudo apt-get update && sudo apt-get install -y unzip zip
#
# - name: Download amd64 bundle
# uses: actions/download-artifact@v8
# with:
# name: bundle-amd64
#
# - name: Rebuild native modules for armhf in container
# run: |
# unzip wekan-${VERSION}-amd64.zip
# rm -rf bundle/programs/server/node_modules
# # Fix: bcrypt 5.0.1 (Meteor accounts-password) ships node-addon-api@3
# # which fails to compile on Node.js 24. Replace with bcrypt 6.0.0.
# BCRYPT_DIR="$(pwd)/bundle/programs/server/npm/node_modules/meteor/accounts-password/node_modules/bcrypt"
# if [ -d "$BCRYPT_DIR" ]; then
# rm -rf "$BCRYPT_DIR"
# TMP=$(mktemp -d)
# pushd "$TMP" > /dev/null
# npm pack bcrypt@6.0.0
# mkdir bcrypt && tar xzf bcrypt-*.tgz --strip-components=1 -C bcrypt
# cd bcrypt && npm install --ignore-scripts && cd ..
# cp -a bcrypt "$BCRYPT_DIR"
# popd > /dev/null
# rm -rf "$TMP"
# fi
# # node:<version>-slim arm/v7 — same Node.js version as Meteor bundled
# docker run --rm --platform linux/arm/v7 \
# -v "$(pwd)/bundle:/bundle" \
# -w /bundle/programs/server \
# node:${NODE_VERSION}-slim \
# sh -c "apt-get update -qq && apt-get install -y -q build-essential python3 && npm install"
#
# - name: Clean up and create armhf zip
# run: |
# zip -r wekan-${VERSION}-armhf.zip bundle
#
# - uses: actions/upload-artifact@v7
# with:
# name: bundle-armhf
# path: wekan-${{ needs.prepare.outputs.version }}-armhf.zip
# retention-days: 1
# ─── 5. Build win64 bundle (rebuild native modules on Windows) ───────────────
build-win64:
needs: [prepare, build-amd64]
runs-on: windows-latest
timeout-minutes: 60
env:
VERSION: ${{ needs.prepare.outputs.version }}
steps:
- uses: actions/setup-node@v6
with:
node-version: '${{ needs.prepare.outputs.version }}'
## With Node 24 these are already preinstalled at GitHub Windows runners
#- name: Install node-gyp and build tools
# run: |
# npm install -g windows-build-tools --vs2019 2>$null; exit 0
- name: Install 7-zip
run: choco install -y 7zip
- name: Download amd64 bundle
uses: actions/download-artifact@v8
with:
name: bundle-amd64
- name: Fix bcrypt for Node.js 24
shell: bash
run: |
7z x wekan-${VERSION}-amd64.zip
# Fix: bcrypt 5.0.1 (Meteor accounts-password) ships node-addon-api@3
# which fails to compile on Node.js 24. Replace with bcrypt 6.0.0.
BCRYPT_DIR="$(pwd)/bundle/programs/server/npm/node_modules/meteor/accounts-password/node_modules/bcrypt"
if [ -d "$BCRYPT_DIR" ]; then
rm -rf "$BCRYPT_DIR"
TMP=$(mktemp -d)
pushd "$TMP" > /dev/null
npm pack bcrypt@6.0.0
mkdir bcrypt && tar xzf bcrypt-*.tgz --strip-components=1 -C bcrypt
cd bcrypt && npm install --ignore-scripts && cd ..
cp -r bcrypt "$BCRYPT_DIR"
popd > /dev/null
rm -rf "$TMP"
fi
- name: Rebuild native modules for Windows
shell: cmd
run: |
cd bundle\programs\server
rmdir /s /q node_modules
npm install
cd ..\..\..
- name: Create win64 zip
shell: cmd
run: 7z a wekan-%VERSION%-win64.zip bundle
- uses: actions/upload-artifact@v7
with:
name: bundle-win64
path: wekan-${{ needs.prepare.outputs.version }}-win64.zip
retention-days: 1
# ─── 6. Create GitHub Release and upload all bundles ────────────────────────
release:
needs: [prepare, build-amd64, build-arm64, build-s390x, build-ppc64le, build-win64]
runs-on: ubuntu-latest
env:
VERSION: ${{ needs.prepare.outputs.version }}
steps:
- name: Download all bundles
uses: actions/download-artifact@v8
with:
pattern: bundle-*
merge-multiple: true
- name: Create GitHub Release with all bundles
uses: softprops/action-gh-release@v3
with:
tag_name: v${{ needs.prepare.outputs.version }}
name: v${{ needs.prepare.outputs.version }}
body: ${{ needs.prepare.outputs.changelog }}
files: |
wekan-${{ needs.prepare.outputs.version }}-amd64.zip
wekan-${{ needs.prepare.outputs.version }}-arm64.zip
wekan-${{ needs.prepare.outputs.version }}-s390x.zip
wekan-${{ needs.prepare.outputs.version }}-ppc64le.zip
wekan-${{ needs.prepare.outputs.version }}-win64.zip
# ─── 7. Build and push multi-platform Docker image ──────────────────────────
docker:
needs: [prepare, release]
runs-on: ubuntu-latest
env:
VERSION: ${{ needs.prepare.outputs.version }}
steps:
- uses: actions/checkout@v6
with:
ref: refs/tags/v${{ inputs.new_version }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Create Docker config with registry credentials
run: |
mkdir -p ~/.docker
cat > ~/.docker/config.json <<EOF
{
"auths": {
"ghcr.io": {
"auth": "${{ secrets.GHCR_AUTH }}"
},
"https://index.docker.io/v1/": {
"auth": "${{ secrets.DOCKERHUB_AUTH }}"
},
"quay.io": {
"auth": "${{ secrets.QUAY_AUTH }}"
}
}
}
EOF
- name: Create and use buildx builder
run: |
docker buildx create --name mybuilder --driver docker-container --use
docker buildx inspect --bootstrap
- name: Build and push Docker image to all registries
run: |
docker buildx build \
--platform linux/amd64,linux/arm64,linux/s390x,linux/ppc64le \
-t wekanteam/wekan:v${VERSION} \
-t wekanteam/wekan:latest \
-t quay.io/wekan/wekan:v${VERSION} \
-t quay.io/wekan/wekan:latest \
-t ghcr.io/wekan/wekan:v${VERSION} \
-t ghcr.io/wekan/wekan:latest \
--push .
# ─── 8. Build and publish snap packages ──────────────────────────────────────
# Build snap with snapcraft pack (like releases/snap-build.sh) using LXD,
# then push to Snap Store edge channel (like releases/snap-push-to-store.sh).
# Promote to stable manually via the Snap Store dashboard when ready.
snap:
needs: [prepare, release]
runs-on: ubuntu-latest
env:
VERSION: ${{ needs.prepare.outputs.version }}
steps:
- uses: actions/checkout@v6
with:
ref: refs/tags/v${{ inputs.new_version }}
- name: Install snapcraft
run: sudo snap install snapcraft --classic
- name: Build snap
run: sudo snapcraft pack --destructive-mode
- name: Upload snap files as artifacts
uses: actions/upload-artifact@v7
with:
name: snaps
path: "*.snap"
retention-days: 30
- name: Push all snaps to Snap Store edge channel
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAP_AUTH }}
run: |
for snap_file in *.snap; do
echo "Publishing $snap_file ..."
snapcraft upload --release=edge "$snap_file"
done
- name: Upload all snaps to GitHub Release
uses: softprops/action-gh-release@v3
with:
tag_name: v${{ needs.prepare.outputs.version }}
files: "*.snap"
# ─── 9. Update Helm charts and deploy to GitHub Pages ─────────────────────────
# Chart source is in helm/wekan/, published packages + index.yaml in docs/wekan/.
# GitHub Pages deploys docs/wekan/ via Actions, serving at https://wekan.fi/wekan/
helm:
needs: [prepare, docker]
runs-on: ubuntu-latest
env:
VERSION: ${{ needs.prepare.outputs.version }}
steps:
- uses: actions/checkout@v6
with:
ref: refs/tags/v${{ inputs.new_version }}
- name: Install Helm
run: |
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
- name: Update Chart.yaml and values.yaml with new version
run: |
CHART_PATH=docs/wekan
CHART_FILE=$CHART_PATH/Chart.yaml
VALUES_FILE=$CHART_PATH/values.yaml
CHARTS_DIR=docs/charts
INDEX_FILE=$CHARTS_DIR/index.yaml
VERSION=${VERSION}
# Update Chart.yaml (appVersion and version)
sed -i "s/^appVersion: .*/appVersion: \"$VERSION\"/" "$CHART_FILE"
sed -i "s/^version: .*/version: $VERSION.0/" "$CHART_FILE"
# Update values.yaml (image.tag)
sed -i "s/^ tag: v.*/ tag: v$VERSION/" "$VALUES_FILE"
- name: Build Helm dependencies
run: |
cd docs/wekan
helm repo add mongo https://groundhog2k.github.io/helm-charts/ || true
helm dependency update
helm dependency build
- name: Package chart to docs/charts and update index.yaml
run: |
CHART_PATH=docs/wekan
CHARTS_DIR=docs/charts
INDEX_FILE=$CHARTS_DIR/index.yaml
VERSION=${VERSION}
cd $CHARTS_DIR
helm package ../wekan -d .
ARCHIVE=wekan-$VERSION.0.tgz
SHA256=$(sha256sum "$ARCHIVE" | awk '{print $1}')
CREATED=$(TZ='Europe/Helsinki' date '+%Y-%m-%dT%H:%M:%S.000000000%:z')
if [ -f "$INDEX_FILE" ]; then
awk '/^- apiVersion: v2/{flag=1;print;next}/^- apiVersion: v2/{flag=0}flag' "$INDEX_FILE" | head -n 30 > /tmp/topentry.yaml
sed -i "s/appVersion: .*/appVersion: \"$VERSION\"/" /tmp/topentry.yaml
sed -i "s/created: .*/created: \"$CREATED\"/" /tmp/topentry.yaml
sed -i "s/digest: .*/digest: $SHA256/" /tmp/topentry.yaml
sed -i "s|urls:.*|urls:\n - https://wekan.github.io/wekan/wekan-$VERSION.0.tgz|" /tmp/topentry.yaml
sed -i "s/version: .*/version: $VERSION.0/" /tmp/topentry.yaml
awk 'NR==1{print;system("cat /tmp/topentry.yaml");next} /^ - apiVersion: v2/ && !p{p=1;next} 1' "$INDEX_FILE" > /tmp/index.yaml
mv /tmp/index.yaml "$INDEX_FILE"
rm /tmp/topentry.yaml
else
echo "index.yaml not found, skipping index update."
fi
- name: Commit updated chart and index
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add docs/wekan/ docs/charts/
git commit -m "${VERSION}.0"
git push origin HEAD:refs/heads/main
- name: Upload docs/charts/ as Pages artifact
uses: actions/upload-pages-artifact@v5
with:
path: docs/charts/
- name: Commit updated chart
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add docs/wekan/
git commit -m "${VERSION}.0"
git push origin HEAD:refs/heads/main
- name: Upload docs/wekan/ as Pages artifact
uses: actions/upload-pages-artifact@v5
with:
path: docs/wekan/
# ─── 10. Deploy Helm chart to GitHub Pages ───────────────────────────────────
deploy-pages:
needs: helm
runs-on: ubuntu-latest
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v5
@@ -124,13 +124,18 @@ jobs:
restore-keys: |
v1-meteor_build_cache-
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '24.15.0'
- name: Setup meteor
uses: meteorengineer/setup-meteor@v3
with:
meteor-release: '2.2'
meteor-release: '3.5-beta.7'
- name: Install NPM Dependencies
run: meteor npm ci
run: meteor npm install
- name: Run Tests
run: sh ./test-wekan.sh -cv
+2 -2
View File
@@ -38,7 +38,7 @@ jobs:
# https://github.com/docker/login-action
- name: Log into registry ${{ env.REGISTRY }}
if: github.event_name != 'pull_request'
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
@@ -55,7 +55,7 @@ jobs:
# 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@d08e5c354a6adb9ed34480a06d141179aa583294
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
+14
View File
@@ -35,7 +35,21 @@ ehthumbs.db
.devcontainer/volumes*/
.coverage
# Rspack build artifacts
_build/
public/build-assets/
public/build-chunks/
private/build-assets/
private/build-chunks/
public/client-rspack.js.map
# Helm chart
# Chart dependencies
/helm/wekan/**/*.tgz
/helm/wekan/charts
# Meteor Modern-Tools build context directories
_build
*/build-assets
*/build-chunks
.rsdoctor
+30 -44
View File
@@ -3,32 +3,28 @@
# 'meteor add' and 'meteor remove' will edit this file for you,
# but you can also edit it by hand.
meteor-base@1.5.1
meteor-base@1.5.2
# Build system
ecmascript
standard-minifier-js
mquandalle:jade
ecmascript@0.17.0
standard-minifier-js@3.2.0
rspack@1.0.0
# Polyfills
es5-shim@4.8.0
blaze
blaze@3.0.2
# Collections
aldeed:collection2
aldeed:schema-index@4.0.0-beta.5
reywood:publish-composite
dburles:collection-helpers
mongo@1.16.8
mongo@2.3.0-beta350.7
# Account system
accounts-password@2.4.0
communitypackages:core@2.0.3
communitypackages:unstyled@2.0.1
accounts-password@3.2.3-beta350.7
communitypackages:core
communitypackages:unstyled
communitypackages:flow-routing
# Routing
ostrio:flow-router-extra
simple:rest-accounts-password
wekan-ldap
wekan-accounts-cas
wekan-accounts-sandstorm
@@ -37,42 +33,32 @@ wekan-oidc
wekan-accounts-oidc
# Utilities
check@1.3.2
jquery@3.0.0!
random@1.2.1
reactive-dict@1.3.1
session@1.2.1
tracker@1.3.3
audit-argument-checks@1.0.7
http@2.0.0! # force new http package
# Datepicker (disabled - using native HTML inputs)
# wekan-bootstrap-datepicker
check@1.5.0
random@1.2.2
reactive-dict@1.3.2
session@1.2.2
tracker@1.3.4
audit-argument-checks@1.0.8
# UI components
ostrio:i18n
reactive-var@1.0.12
meteor-autosize
shell-server@0.5.0
email@2.2.5
dynamic-import@0.7.3
msavin:usercache
# Keep stylus in 1.1.0, because building v2 takes extra 52 minutes.
meteorhacks:subs-manager
reactive-var@1.0.13
shell-server@0.7.0
email@3.2.0-beta350.7
dynamic-import@0.7.4
wekan-markdown
quave:synced-cron
ostrio:cookies
ostrio:files@2.3.0
lmieulet:meteor-coverage
meteortesting:mocha@2.0.3
aldeed:simple-schema
ostrio:files
# lmieulet:meteor-coverage
meteortesting:mocha
matb33:collection-hooks
simple:json-routes
service-configuration@1.3.2
communitypackages:picker
minifier-css@1.6.4
ejson@1.1.3
logging@1.3.3
service-configuration@1.3.5
minifier-css@2.0.1
ejson@1.2.0-beta350.7
logging@1.3.6
wekan-fullcalendar
wekan-fontawesome
jam:offline
jam:method
jam:pub-sub
+1 -1
View File
@@ -1 +1 @@
METEOR@2.16
METEOR@3.5-beta.7
+102 -114
View File
@@ -1,126 +1,114 @@
accounts-base@2.2.11
accounts-oauth@1.4.4
accounts-password@2.4.0
aldeed:collection2@2.10.0
aldeed:collection2-core@1.2.0
aldeed:schema-deny@1.1.0
aldeed:schema-index@1.1.1
aldeed:simple-schema@1.5.4
allow-deny@1.1.1
audit-argument-checks@1.0.7
autoupdate@1.8.0
babel-compiler@7.10.5
babel-runtime@1.5.1
base64@1.0.12
binary-heap@1.0.11
blaze@2.7.1
blaze-tools@1.1.3
boilerplate-generator@1.7.2
caching-compiler@1.2.2
caching-html-compiler@1.2.1
callback-hook@1.5.1
check@1.4.1
accounts-base@3.3.0-beta350.7
accounts-oauth@1.4.6
accounts-password@3.2.3-beta350.7
aldeed:collection2@4.1.5
aldeed:schema-index@4.0.0
aldeed:simple-schema@1.13.1
allow-deny@2.1.0
audit-argument-checks@1.0.8
autoupdate@2.0.1
babel-compiler@7.13.0
babel-runtime@1.5.2
base64@1.0.13
binary-heap@1.0.12
blaze@3.0.2
blaze-tools@2.0.0
boilerplate-generator@2.1.0
caching-compiler@2.0.1
caching-html-compiler@2.0.0
callback-hook@1.6.1
check@1.5.0
communitypackages:core@2.0.3
communitypackages:flow-routing@2.1.0
communitypackages:picker@1.1.1
communitypackages:unstyled@2.0.1
dburles:collection-helpers@1.1.0
ddp@1.4.1
ddp-client@2.6.2
ddp-common@1.4.1
ddp-rate-limiter@1.2.1
ddp-server@2.7.1
deps@1.0.12
diff-sequence@1.1.2
dynamic-import@0.7.3
ecmascript@0.16.8
ecmascript-runtime@0.8.1
ecmascript-runtime-client@0.12.1
ecmascript-runtime-server@0.11.0
ejson@1.1.3
email@2.2.6
es5-shim@4.8.0
fetch@0.1.4
geojson-utils@1.0.11
hot-code-push@1.0.4
html-tools@1.1.3
htmljs@1.1.1
http@2.0.0
id-map@1.1.1
inter-process-messaging@0.1.1
jquery@3.0.0
lmieulet:meteor-coverage@1.1.4
localstorage@1.2.0
logging@1.3.4
matb33:collection-hooks@1.3.0
mdg:validation-error@0.5.1
meteor@1.11.5
meteor-autosize@5.0.1
meteor-base@1.5.1
meteorhacks:picker@1.0.3
meteorhacks:subs-manager@1.6.4
core-runtime@1.0.0
ddp@1.4.2
ddp-client@3.2.0-beta350.7
ddp-common@1.4.4
ddp-rate-limiter@1.3.0-beta350.7
ddp-server@3.2.0-beta350.7
diff-sequence@1.1.3
dynamic-import@0.7.4
ecmascript@0.17.0
ecmascript-runtime@0.8.3
ecmascript-runtime-client@0.12.3
ecmascript-runtime-server@0.11.1
ejson@1.2.0-beta350.7
email@3.2.0-beta350.7
es5-shim@4.8.1
facts-base@1.0.2
fetch@0.1.6
geojson-utils@1.0.12
hot-code-push@1.0.5
html-tools@2.0.0
htmljs@2.0.1
http@1.4.4
id-map@1.2.0
inter-process-messaging@0.1.2
jam:method@1.9.1
jam:offline@0.4.1
jam:pub-sub@0.5.1
jquery@3.0.2
lai:collection-extensions@1.0.0
localstorage@1.2.1
logging@1.3.6
matb33:collection-hooks@2.1.0
meteor@2.2.0
meteor-base@1.5.2
meteortesting:browser-tests@1.4.2
meteortesting:mocha@2.1.0
meteortesting:mocha-core@8.0.1
minifier-css@1.6.4
minifier-js@2.8.0
minifiers@1.1.8-faster-rebuild.0
minimongo@1.9.4
modern-browsers@0.1.10
modules@0.20.0
modules-runtime@0.13.1
mongo@1.16.10
mongo-decimal@0.1.3
mongo-dev-server@1.1.0
mongo-id@1.0.8
mquandalle:jade@0.4.9
mquandalle:jade-compiler@0.4.5
msavin:usercache@1.8.0
npm-mongo@4.17.2
oauth@2.2.1
oauth2@1.3.2
observe-sequence@1.0.21
ordered-dict@1.1.0
ostrio:cookies@2.7.2
minifier-css@2.0.1
minifier-js@3.1.0
minimongo@2.1.0-beta350.7
modern-browsers@0.2.3
modules@0.20.3
modules-runtime@0.13.2
mongo@2.3.0-beta350.7
mongo-decimal@0.2.0
mongo-dev-server@1.1.1
mongo-id@1.0.9
npm-mongo@6.16.1
oauth@3.0.2
oauth2@1.3.3
observe-sequence@2.0.0
ordered-dict@1.2.0
ostrio:cookies@2.9.1
ostrio:cstorage@4.0.1
ostrio:files@2.3.3
ostrio:flow-router-extra@3.10.1
ostrio:files@3.0.1
ostrio:flow-router-extra@3.13.0
ostrio:i18n@3.2.1
promise@0.12.2
quave:synced-cron@2.2.1
raix:eventemitter@0.1.3
random@1.2.1
rate-limit@1.1.1
react-fast-refresh@0.2.8
reactive-dict@1.3.1
reactive-var@1.0.12
reload@1.3.1
retry@1.1.0
promise@1.0.0
quave:synced-cron@2.4.0
raix:eventemitter@1.0.0
random@1.2.2
rate-limit@1.2.0-beta350.7
react-fast-refresh@0.3.0
reactive-dict@1.3.2
reactive-var@1.0.13
reload@1.3.2
retry@1.1.1
reywood:publish-composite@1.9.0
routepolicy@1.1.1
service-configuration@1.3.4
session@1.2.1
sha@1.0.9
shell-server@0.5.0
simple:authenticate-user-by-token@1.2.1
simple:json-routes@2.3.1
simple:rest-accounts-password@1.2.2
simple:rest-bearer-token-parser@1.1.1
simple:rest-json-error-handler@1.1.1
socket-stream-client@0.5.2
spacebars@1.4.1
spacebars-compiler@1.3.1
standard-minifier-js@2.8.1
templating@1.4.1
templating-compiler@1.4.1
templating-runtime@1.5.0
templating-tools@1.2.2
tracker@1.3.3
typescript@4.9.5
underscore@1.6.1
url@1.3.2
webapp@1.13.8
webapp-hashing@1.1.1
routepolicy@1.1.2
rspack@1.0.0
service-configuration@1.3.5
session@1.2.2
sha@1.0.10
shell-server@0.7.0
socket-stream-client@0.7.0-beta350.7
spacebars@2.0.0
spacebars-compiler@2.0.0
standard-minifier-js@3.2.0
templating@1.4.4
templating-compiler@2.0.0
templating-runtime@2.0.1
templating-tools@2.0.0
tools-core@1.0.0
tracker@1.3.4
typescript@5.9.3
url@1.3.5
webapp@2.2.0-beta350.7
webapp-hashing@1.1.2
wekan-accounts-cas@0.2.0
wekan-accounts-lockout@1.1.0
wekan-accounts-oidc@1.0.10
+874 -10
View File
@@ -2,6 +2,7 @@
Newest WeKan at these platforms:
- [Upgrade WeKan](https://wekan.fi/upgrade/)
- [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)
@@ -11,23 +12,886 @@ Newest WeKan at these platforms:
- Docker amd64/arm64/s390x
- 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.
Fixing other platforms In Progress.
Versions:
- [Node.js 14.21.4](https://github.com/wekan/node-v14-esm/releases/tag/v14.21.4) or [Node.js 14.21.3](https://nodejs.org/dist/latest-v14.x/)
- MongoDB 6.x and 7.x, or [FerretDB2/PostgreSQL](https://github.com/wekan/wekan/blob/main/docs/Databases/FerretDB2-PostgreSQL.md)
- WeKan 8.75 and newer uses Meteor 3.5-beta.7, [Node.js 24.x](https://nodejs.org/dist/latest-v24.x/),
MongoDB 7.x (or 6.x), or [FerretDB2/PostgreSQL](https://github.com/wekan/wekan/blob/main/docs/Databases/FerretDB2-PostgreSQL.md)
- WeKan 8.43 upgraded to Meteor 3.x, huge thanks to harryadel:
- https://harryadel.com/dev-diary-24/
- https://harryadel.com/dev-diary-25/
- https://harryadel.com/dev-diary-26/
- WeKan 8.00-8.24 used Colorful Unicode Emoji Icons, versions before and after use mostly Font Awesome 4.7 icons.
- WeKan 8.00-8.06 had wrong raw database directory setting /var/snap/wekan/common/wekan and some cards were not visible,
it was fixed at WeKan 8.07 where database directory is back to /var/snap/wekan/common and all cards are visible.
[Upgrade WeKan](https://wekan.fi/upgrade/)
# v9.02 2026-04-28 WeKan ® release
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.
This release fixes the following bugs:
WeKan 8.00-8.24 used Colorful Unicode Emoji Icons, versions before and after use mostly Font Awesome 4.7 icons.
- [Fix mongodb in snap 9.01 on Ubuntu 24.04 crashes because of missing libcrypto.so.1.1](https://github.com/wekan/wekan/commit/a4b5ac6c4e2cfba6983b81ff0282e1c787576d06).
Thanks to sbaecker.
Upgrading to Meteor 3 progress:
Thanks to above GitHub users for their contributions and translators for their translations.
- https://harryadel.com/dev-diary-24/
- https://harryadel.com/dev-diary-25/
# v9.01 2026-04-28 WeKan ® release
This release adds the following updates:
- [Updated Docker platforms for what currently builds successfully](https://github.com/wekan/wekan/commit/fdb477f3517383eb17169b1dfff2ef94c12b6296).
Thanks to xet7.
- [Docs: Updated s390x page](https://github.com/wekan/wekan/commit/855cb86088c7c54734eddb1ddc5a007b432a0d91).
Thanks to xet7.
- [Removed charts, that are moved back to github.com/wekan/charts](https://github.com/wekan/wekan/commit/b49aa050e91b4bd560512af1beece791c6e39114).
Thanks to xet7.
- [Moved website to separate repo. Moved docs2 back to docs](https://github.com/wekan/wekan/commit/f6d8befb56c88eb0867c70bac708f867fe731a5a).
Thanks to xet7.
- [Disable not working GitHub workflows](https://github.com/wekan/wekan/commit/58de6bb73714e6c6d4010de5c72499f429bd9a41).
Thanks to xet7.
- [Fix Snap mongodb port wrong](https://github.com/wekan/wekan/commit/11a2c95ea70a3dcf9243369f8feeaea301a93bb6).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v9.00 2026-04-27 WeKan ® release
This release adds the following updates:
- [Bump actions/upload-pages-artifact from 4 to 5](https://github.com/wekan/wekan/pull/6304).
Thanks to dependabot.
- [Bump docker/setup-buildx-action from 3 to 4](https://github.com/wekan/wekan/pull/6305).
Thanks to dependabot.
- [Bump actions/deploy-pages from 4 to 5](https://github.com/wekan/wekan/pull/6306).
Thanks to dependabot.
- [Update release scripts](https://github.com/wekan/wekan/commit/db2701f1d731898c80b44606a985d5d132f7f6ed).
Thanks to xet7.
- [Added back jam packages. Updated dependencies](https://github.com/wekan/wekan/commit/bf5ca3d3ebef7f00778eb1a518fcfaf5b5fc5d5c).
Thanks to xet7.
- [Updated dependencies](https://github.com/wekan/wekan/commit/16832124ce169c823ab1f95a43a85bec22d5556f).
Thanks to xet7.
- [Update security.md etc](https://github.com/wekan/wekan/commit/7700efde1fba8483a78c67d03e3a0d214cedb67b).
Thanks to xet7.
- [Updated HoF](https://github.com/wekan/wekan/commit/675c6611146a4a47eee1e513b83152007e3b9aee).
Thanks to xet7.
- [Update releases scripts](https://github.com/wekan/wekan/commit/64f58455ff471b5a5567d7230850124cf0afacb0).
Thanks to xet7.
- [Update release and version scripts](https://github.com/wekan/wekan/commit/699b9a9c712f01068324c051be4fb149dfc6f100).
Thanks to xet7.
- Updated dependencies.
[Part 1](https://github.com/wekan/wekan/commit/b459151798d9808b4243d230ff6759a58d4016f5),
[Part 2](https://github.com/wekan/wekan/commit/74965f5f95ba4e4dfd7381becd55184731a60fca).
Thanks to developers of dependencies.
- [Updated website](https://github.com/wekan/wekan/commit/ef4161889af58827f105749362196fd471e3e38c).
Thanks to xet7.
and fixes the following bugs:
- [Update cards.js](https://github.com/wekan/wekan/pull/6299).
Thanks to snowsky.
- [Fix MongoDB replicaset setup at docker-compose.yml](https://github.com/wekan/wekan/commit/c4e084ffbbc5870a13e79f80a2982d2eb7f2f04e).
Thanks to C0rn3j.
- [Add permission checks to list width method](https://github.com/wekan/wekan/pull/6303).
Thanks to Raawen-dev.
- [Fix UI hangs and data loss by disabling changeStreams, until there is new Meteor release that included bugfix to changeStreams](https://github.com/wekan/wekan/commit/c47eb013b4d8d0f2dea7b801adc60151f6e3aee1).
Thanks to bcook-konza and italojs.
- [Fix list actions popup overflow](https://github.com/wekan/wekan/pull/6310).
Thanks to Raawen-dev.
- [Fix mycards subscription](https://github.com/wekan/wekan/pull/6314).
Thanks to aminsaidane.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.99 2026-04-16 WeKan ® release
This release adds the following updates:
- [Release scripts: Update versions](https://github.com/wekan/wekan/commit/32e1ec9d2251840dc2d57ffa7976f2c60546ca9d).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.98 2026-04-16 WeKan ® release
This release adds the following updates:
- [Release scripts: Detect newest Node.js 24.x version](https://github.com/wekan/wekan/commit/fde59148dc8bd712e30cbf969d4007b2a7f01e6f).
Thanks to xet7.
- [Release scripts: Update versions at website with release-website.sh script](https://github.com/wekan/wekan/commit/9174001862f1545626730b9a51754ab77b645338).
Thanks to xet7.
- [Release scripts: Detect newest MongoDB 7.x stable version and update to use it](https://github.com/wekan/wekan/commit/72f6d96dbc3becc2767eb6f88b0f218679d1ac78).
Thanks to xet7.
- [Moved website from repo wekan/wekan.fi to wekan/wekan/docs](https://github.com/wekan/wekan/commit/3c36db33e91e504722edc60faec5d6b4aa301850).
Thanks to xet7.
- [Moved docs to docs2, and wekan.fi to docs, to be able publish with GitHub Pages](https://github.com/wekan/wekan/commit/1c0d8e9964af40be91eda9668dfeebd01b3a5720).
Thanks to xet7.
- [Update markdown docs links from docs to docs2](https://github.com/wekan/wekan/commit/c1639ea58973efcae0758385cc5d8d7ffbf1e8d9).
Thanks to xet7.
- [Commented out requirement to update website manually](https://github.com/wekan/wekan/commit/64d351d835628dad80062e3c362701b35ae5bb33).
Thanks to xet7.
- [Moved Helm Charts from helm directory to docs/wekan and docs/charts to be at wekan.fi/wekan and wekan.fi/charts](https://github.com/wekan/wekan/commit/38e488ee4a63d61159d8efcddb6e454347bce91a).
Thanks to xet7.
- [Release scripts: Fix indentation at release-all.yml](https://github.com/wekan/wekan/commit/10f009f5b15fd077577a71504b0e0c89c4e6fd95).
Thanks to xet7.
- [Release scripts: Use latest available Node.js slim docker images when building](https://github.com/wekan/wekan/commit/d998e8cb4a5280c93448d144ab01335ce29b43dc).
Thanks to xet7.
- [Release scripts: Fix updating version numbers](https://github.com/wekan/wekan/commit/7b0e2213cfbf0e2a17a143aa7ad9f979fb0fbf07).
Thanks to xet7.
- [Release scripts: Require only newest version number when releasing](https://github.com/wekan/wekan/commit/052d8c15844dfc709dc3ac281795629c175869e0).
Thanks to xet7.
- [Release scripts: Use 24-slim fallback](https://github.com/wekan/wekan/commit/a0c190137dfe9fd8da71380f6c67344812561f9f).
Thanks to xet7.
- [Release scripts: use node:24-slim directly](https://github.com/wekan/wekan/commit/51ad6ed99df7ecdc925d314a6c5fab345f75d549).
Thanks to xet7.
- [Fix Helm Chart parts to docs/wekan and docs/charts](https://github.com/wekan/wekan/commit/1820804b9f46e510c4bf05324755a5eeec5a3b13).
Thanks to xet7.
- [Release scripts: Updated creating Helm Charts to docs/wekan and docs/charts](https://github.com/wekan/wekan/commit/f6198dee30ed6514974d2e821997d340e67fc954).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.97 2026-04-16 WeKan ® release
This release adds the following updates:
- Release Scripts: Clean GitHub Cache.
[Part 1](https://github.com/wekan/wekan/commit/e59bc9cd86cc16333e24b2e7e8e5d5cf72f81e3e),
[Part 2](https://github.com/wekan/wekan/commit/13cdf08e223e83ef9a2f36417c11ad002e884f29),
[Part 3](https://github.com/wekan/wekan/commit/6d0978d0ede8316a1d9ac6afc0ecf7f02d9193d6).
Thanks to xet7.
and fixes the following bugs:
- [Fix GitHub Actions builds](https://github.com/wekan/wekan/commit/6478d27d1ca2f6e27c025e0cc97b618bc255083f).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.96 2026-04-16 WeKan ® release
This release fixes the following bugs:
- [Fix GitHub Actions builds](https://github.com/wekan/wekan/commit/03ba747bc073f3993d933b814d35c7380fea745d).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.95 2026-04-16 WeKan ® release
This release adds the following new features:
- [Helm Chart: Add single-node ReplicaSet support with auto MONGO_OPLOG_URL](https://github.com/wekan/wekan/pull/6292).
Thanks to titver968.
- [Helm Chart: Use current Finland time for created field at index.yaml for newest built Helm Chart release](https://github.com/wekan/wekan/commit/135af977af4a1c598d2e067de0facb6eae8f8de1).
Thanks to xet7.
and fixes the following bugs:
- [Fix Cannot find module /build/main.js error in latest Docker image](https://github.com/wekan/wekan/commit/6dfe81cbcb0c6e2003b1984586d56042a6408e70).
Thanks to s8321414 and xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.94 2026-04-16 WeKan ® release
This release adds the following updates:
- [Updated dompurify](https://github.com/wekan/wekan/commit/c27d5d04124f3eec659bbe5187a625c43176fc4f).
Thanks to developers of dompurify.
and fixes the following bugs:
- [Fix Can't start latest docker image because of MongoDB Index errors](https://github.com/wekan/wekan/commit/d537b7c9f3505fd4c1653f87d5f86d7e7729a514).
Thanks to s8321414 and xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.93 2026-04-16 WeKan ® release
This release fixes the following bugs:
- [Fix SSO Login broken. Part 2](https://github.com/wekan/wekan/commit/4aa7b6aa2034f27efeb67d5dfa8f6acbe0005786).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.92 2026-04-16 WeKan ® release
This release adds the following updates:
- [Updated Helm Charts versions](https://github.com/wekan/wekan/commit/24ebb4ba45344ad152747c31e7df76217e5b8909).
Thanks to xet7.
- [GitHub Actions: Added missing sudo to snap build](https://github.com/wekan/wekan/commit/2c8a6c8ec6e24b6b6bb153da3edd089e0f0087cc).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.91 2026-04-16 WeKan ® release
This release fixes the following bugs:
- [Fix building Snap at GitHub Actions](https://github.com/wekan/wekan/commit/89de05ee91612cef4f13fa8ad32d112dca5bdb43).
Thanks to xet7.
- [Updated Helm Chart URL at readme](https://github.com/wekan/wekan/commit/529e0d25ef2ea84953d1c1e6a23b5798d34583fb).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.90 2026-04-16 WeKan ® release
This release adds the following updates:
- [Added GitHub Actions builds for Docker and Snap. Moved Helm Charts from charts repo to wekan repo](https://github.com/wekan/wekan/commit/f57ce521984f6f358410863c71fcaee342bb8107).
Thanks to xet7.
- [Changed ArtifactHub from wekan/charts repo to wekan/wekan repo](https://github.com/wekan/wekan/commit/99519af83adc45eca7beb40a4e82f5b73764b7fc).
Thanks to xet7.
- [Release scripts: Moved more steps to GitHub Actions](https://github.com/wekan/wekan/commit/357f63d01fc013ea412501dabbdd977e5f1efa61).
Thanks to xet7.
and fixes the following bugs:
- [Removed jam:offline jam:method jam:pub-sub, because they are not used, and they cause too much database usage](https://github.com/wekan/wekan/commit/0dd924f14889100e74fbd5e8d17bb3a7209025ca).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.89 2026-04-15 WeKan ® release
This release fixes the following bugs:
- [Disable jam:offline keepAll BEFORE any collections are created. Part 2](https://github.com/wekan/wekan/commit/f6a851a3d887ce9fa2fa5f62aad2a2ecf326c863).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.88 2026-04-15 WeKan ® release
This release fixes the following bugs:
- [Disable jam:offline keepAll to prevent full-collection COLLSCAN queries and Ensure standard GridFS index on attachments.chunks for efficient chunk lookups](https://github.com/wekan/wekan/commit/ec0c1bc9afd6e3fd8b60f9f17f96dc5df52dbfc2).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.87 2026-04-15 WeKan ® release
This release fixes the following bugs:
- [Try to fix Snap](https://github.com/wekan/wekan/commit/fc02a15753badc8d06d020aa97daae28ac05a310).
Thanks to xet7.
- [Improve handling of ROOT_URL as an environement variable](https://github.com/wekan/wekan/pull/6286).
Thanks to FlorentDotMe.
- [UI: Enable the horizontal scroll in the quick access menu boards list](https://github.com/wekan/wekan/pull/6287).
Thanks to FlorentDotMe.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.86 2026-04-15 WeKan ® release
This release fixes the following bugs:
- [Try to fix Snap](https://github.com/wekan/wekan/commit/10f801d31b7e2b1a8c562bf24f65c4cc6efe6013).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.85 2026-04-15 WeKan ® release
This release fixes the following bugs:
- [Try to fix Snap](https://github.com/wekan/wekan/commit/0e13bfc50eadc72adccfe785a1b47c30d0ba5429).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.84 2026-04-15 WeKan ® release
This release adds the following new features:
- [Helm Chart: Make readiness, liveness and startup probes configurable](https://github.com/wekan/charts/pull/51).
Thanks to snowsky.
and fixes the following bugs:
- [Fix SSO Login OIDC and OpenID Connect](https://github.com/wekan/wekan/pull/6284).
Thanks to sbaecker and nebulade.
- [Try to fix Snap Candidate](https://github.com/wekan/wekan/commit/bc92978fa9c9373947de40fe9ca584e133825610).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.83 2026-04-14 WeKan ® release
This release adds the following updates:
- [Release scripts: Removed armhf, there is no official Node.js binary for it](https://github.com/wekan/wekan/commit/d0a9fa9a71029a5586cf5bc87488619c7db9d0e3).
Thanks to xet7.
- [Release scripts: Docker not for armhf, no official Node.js binary for it](https://github.com/wekan/wekan/commit/41db7b4ad18359283602b8bfdb42b15acf51782d).
Thanks to xet7.
- [Release scripts: Fix Node.js 24.x download URL for Snap builds](https://github.com/wekan/wekan/commit/2c43aa463a0baadcae3db9f34066a9ddf6e77594).
Thanks to xet7.
- [Changed start-wekan.sh and start-wekan.bat to not use Change Streams](https://github.com/wekan/wekan/commit/da4459304fe7d068b1db91938f7fd4ffd04c5bcb).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.82 2026-04-14 WeKan ® release
This release adds the following updates:
- [Release scripts: Added script for deleting repo git tag](https://github.com/wekan/wekan/commit/11672a13f7b102b764978600a8fce2f757c9401a).
Thanks to xet7.
- [Bump softprops/action-gh-release from 2 to 3](https://github.com/wekan/wekan/pull/6281).
Thanks to dependabot.
- [Bump docker/build-push-action from 7.0.0 to 7.1.0](https://github.com/wekan/wekan/pull/6282).
Thanks to dependabot.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.81 2026-04-14 WeKan ® release
This release adds the following updates:
- Updated dependencies
[Part 1](https://github.com/wekan/wekan/commit/7b91439b0f9ec0e409c14ecebab389d41b40a990),
[Part 2](https://github.com/wekan/wekan/commit/1637a9c938134ef26846ba3c0cd53c065f434e3e).
Thanks to developers of dependencies.
- [Docs: Added History of Schema Migration Systems](https://github.com/wekan/wekan/commit/2e7565f9eb744bc59e454745e06fd43a85a02f97).
Thanks to xet7.
- [Release scripts: Comment out build for armhf because no official Node.js 24.x binaries exist for armhf (arm/v7)](https://github.com/wekan/wekan/commit/5462fb06477bc3a83f1d5c754ecd60ebe7643ee6).
Thanks to xet7.
and fixes the following bugs:
- [Fix activities are not showing](https://github.com/wekan/wekan/pull/6272).
Thanks to snowsky.
- [Fix Link Card modal overflow in Docker environment](https://github.com/wekan/wekan/pull/6273).
Thanks to AymenHassini19.
- [Fix/readme node version and changes made](https://github.com/wekan/wekan/pull/6271).
Thanks to YosserDerbali.
- [Clarify board drag tooltip text in Remaining and Workspaces](https://github.com/wekan/wekan/pull/6268).
Thanks to YosserDerbali.
- [Add migration for v7.95 due dates](https://github.com/wekan/wekan/pull/6262).
Thanks to Raawen-dev.
- [Fix: include historical comments and activities in board exports](https://github.com/wekan/wekan/pull/6275).
Thanks to KhaoulaMaleh.
- [Fix Exporter: Attachments always empty in board JSON export](https://github.com/wekan/wekan/pull/6279).
Thanks to kolaente.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.80 2026-04-10 WeKan ® release
This release fixes the following bugs:
- [Try to fix GitHub Actions builds with custom newer bcrypt](https://github.com/wekan/wekan/commit/2ebefdd0bf1cf482548319e130baf2b633ba259b).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.79 2026-04-09 WeKan ® release
This release fixes the following bugs:
- [Fix 8.78: WeKan build Error: Could not find rspack config. Using MongoDB Change Streams
at start-wekan.sh start-wekan.bat bundle, docker-compose.yml and Snap Candidate](https://github.com/wekan/wekan/commit/2d4170a9bfc58b8e6504abf122aea505e55802ef).
Thanks to nachocodoner and xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.78 2026-04-09 WeKan ® release
This release adds the the following updates:
- Upgraded to Meteor 3.5-beta.7 and Node.js 24.14.1. Now prefers MongoDB Change Streams at Snap Candidate and docker-compose.yml.
[Part 1](https://github.com/wekan/wekan/commit/14f9ac39ad93db4f7c06721f1efd4c18e53cc5f8),
[Part 2](https://github.com/wekan/wekan/commit/b1707b8691d0ba5f355e78e250765c8a7657c3d7).
Thanks to Meteor developers.
- [Enhance translations with new key:value pairs](https://github.com/wekan/wekan/pull/6259).
Thanks to KhaoulaMaleh.
- [Refine translations](https://github.com/wekan/wekan/pull/6261).
Thanks to KhaoulaMaleh.
- [Updated to Node.js 24.x at release scripts](https://github.com/wekan/wekan/commit/bdb92744854cc211866631746c63d748d4a93cfa).
Thanks to xet7.
- [Fix GitHub Actions builds](https://github.com/wekan/wekan/commit/bc88c0dfc2742154e94861201d239bc4b3c51cef).
Thanks to xet7.
and fixes the following bugs:
- [Fixed Card settings checkboxes](https://github.com/wekan/wekan/pull/6260).
Thanks to KhaoulaMaleh.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.74 2026-04-08 WeKan ® release
This release fixes the following bugs:
- [Fix can not save Card Data. Part 2](https://github.com/wekan/wekan/commit/0328e70d5f4a277722635e3fd9ea78312d1359cf).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.73 2026-04-07 WeKan ® release
This release fixes the following bugs:
- [Fix Link to Windows release doesn't work](https://github.com/wekan/wekan/commit/9c695302565073b971a3521c0a94a380641cc436).
Thanks to hmeunier95 and xet7.
- [Fix 8.72 Bug: Can not save Card Date](https://github.com/wekan/wekan/commit/8da0f34ee6ca66884ab8a2dcdf1a724a0c9fcc81).
Thanks to xet7.
- [Removed exclude-archs from rebuild-wekan.sh, it is not necessary anymore](https://github.com/wekan/wekan/commit/028b404377651d34e334b048ecc960d3ff5b2eab).
Thanks to xet7.
- [Fix 8.72 Bug: LDAP login does not work. ReactiveCache is not defined](https://github.com/wekan/wekan/commit/bcbf4771ea88fb617ddc44650c8ac139b0cfc998).
Thanks to poc-sm and xet7.
- [Fix Error in choosing member or assignee in a card](https://github.com/wekan/wekan/commit/7f0c42cca555dd2dca04d3b6413c0080ea75e96e).
Thanks to hmeunier95 and xet7.
- [Fix Search All Boards result opened card missing Copy Card Link etc that can be used to open correct card](https://github.com/wekan/wekan/commit/304a007d078c04dac7de5074a856c4f0cc2c0aa7).
Thanks bcook-konza and xet7.
- [Fix Checklists add and Subtasks reorder. Fix Board Activity part 1](https://github.com/wekan/wekan/commit/08ebad0fe4a2b8a7b93d75a9a9c2317ce9374fea).
Thanks bcook-konza and xet7.
- [Fix Change list inside or card](https://github.com/wekan/wekan/commit/aea8c285c246b490ac259ceafe7d9f733681c20f).
Thanks to mimZD and xet7.
- [Fix board and card activities. Part 2](https://github.com/wekan/wekan/commit/c7408ad8d147f36cfdbb71089af688bef91c3ecf).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.72 2026-04-07 WeKan ® release
This release adds the following new features and fixes:
- [Fix Impersonate User. Show/Edit Avatar at Admin Panel/People/People](https://github.com/wekan/wekan/commit/3ac7c9057e1e53da9f285149ce832ad430b3bc30).
Thanks to ahlgrimma, DnT1mmer, robbagithub, norrig and xet7.
and adds the following updates:
- [Bump actions/download-artifact from 4 to 8](https://github.com/wekan/wekan/pull/6247).
Thanks to dependabot.
- [Bump actions/upload-artifact from 4 to 7](https://github.com/wekan/wekan/pull/6248).
Thanks to dependabot.
- [Bump actions/cache from 4 to 5](https://github.com/wekan/wekan/pull/6249).
Thanks to dependabot.
- [Bump docker/login-action from 4.0.0 to 4.1.0](https://github.com/wekan/wekan/pull/6250).
Thanks to dependabot.
- [Updated release scripts](https://github.com/wekan/wekan/commit/87f592c67ffb1962ec8acb0d1676b8df6ac42a5d).
Thanks to xet7.
and fixes the following bugs:
- [Fix Admin Panel / Version not showing name of MongoDB storage engine and status of MongoDB Oplog enabled](https://github.com/wekan/wekan/commit/3b98f862bcda3e8a9b0d0131b91cdfa59986c2d8).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.71 2026-04-06 WeKan ® release
This release fixes the following bugs:
- [Fix: Added missing null check to notification](https://github.com/wekan/wekan/commit/5df924f416083a50795443687e1c28f402dc8132).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.70 2026-04-06 WeKan ® release
This release fixes the following bugs:
- [Convert upsert calls to async in authentication.js](https://github.com/wekan/wekan/commit/dfcaaf0ae719e8d8b918ac632100293a6e401371).
Thanks to snowsky and xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.69 2026-04-06 WeKan ® release
This release fixes the following bugs:
- [Try to fix Snap to start MongoDB OpLog correctly](https://github.com/wekan/wekan/commit/fbdfcead50fb599d4fafecc2f5847c46c73adf72).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.68 2026-04-06 WeKan ® release
This release fixes the following bugs:
- [Try to fix Snap to start MongoDB OpLog correctly](https://github.com/wekan/wekan/commit/9e316662800fc3b5e38b1176e70a50b18d73d2a6).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.67 2026-04-05 WeKan ® release
This release fixes the following bugs:
- [Try to fix GitHub Actions release bundle builds](https://github.com/wekan/wekan/commit/7707a5b7fc21f981f26c512cd3fea6171a57bd46).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.65 2026-04-05 WeKan ® release
This release fixes the following bugs:
- [Fix Docker Actions Test suite](https://github.com/wekan/wekan/commit/c0661506c42ac412ae9e8fc5392d160efbc446ea).
Thanks to xet7.
- [Try to fix GitHub Actions release bundle builds](https://github.com/wekan/wekan/commit/d3155d24c64fe1104afe454b756d2a6324fab5c2).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.64 2026-04-05 WeKan ® release
This release fixes the following bugs:
- [Try to fix GitHub Actions release bundle builds](https://github.com/wekan/wekan/commit/7a7909b49feaaa938b5dbfc2083b0c719187859d).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.63 2026-04-05 WeKan ® release
This release fixes the following bugs:
- [Try to fix GitHub Actions release bundle builds](https://github.com/wekan/wekan/commit/6a78a178e2fcff9ff280ba44c123403e2792bb7b).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.62 2026-04-05 WeKan ® release
This release fixes the following bugs:
- [Try to fix GitHub Actions release bundle builds](https://github.com/wekan/wekan/commit/cd55c0e12ed80ddcb07895f2d787be95c440f991).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.61 2026-04-05 WeKan ® release
This release adds the following updates:
- [Updated Docker build script to check directories and repos exist](https://github.com/wekan/wekan/commit/81319fc48bbd2a91553090428455bc11aff24fbc).
Thanks to xet7.
- [Updated release scripts: Delete all GitHub Actions Caches, so that old stuff does not break builds](https://github.com/wekan/wekan/commit/f3a97b993842425e4f13bb78e5939a069d64887e).
Thanks to xet7.
and fixes the following bugs:
- [Fix: Server crash during comment deletion](https://github.com/wekan/wekan/pull/6246).
Thanks to KhaoulaMaleh.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.60 2026-04-05 WeKan ® release
This release adds the following updates:
- [Updated Docker build script](https://github.com/wekan/wekan/commit/0b59f87887c215dc34093409ccd12da9acd3ef02).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.59 2026-04-05 WeKan ® release
This release fixes the following bugs:
- [Fix docker-compose.yml to use OpLog](https://github.com/wekan/wekan/commit/30a1792c92de9d485c0b9ed3970a1187ca974576).
Thanks to xet7.
- [Fix Snap to use OpLog](https://github.com/wekan/wekan/commit/1fa72fd34ab4547e97e7e0c201bc3d50d478ef5e).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.58 2026-04-05 WeKan ® release
This release adds the following updates:
- [Updated build docs](https://github.com/wekan/wekan/commit/b3d14fb5e87545635006958686f0340028711820).
Thanks to xet7.
and fixes the following bugs:
- [Try to fix .zip Bundle, Docker and Snap](https://github.com/wekan/wekan/commit/59956ca720d2103baaa9ecf2befff2ec76430feb).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.57 2026-04-05 WeKan ® release
This release fixes the following bugs:
- [Try to fix Snap](https://github.com/wekan/wekan/commit/9f549dfcd2efa241a2ad3bdf5b0d2d942064d4e3).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.56 2026-04-05 WeKan ® release
This release fixes the following bugs:
- [Try to fix Snap](https://github.com/wekan/wekan/commit/56f26418ea9d1321fc5109c4b39104ff98d65429).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.55 2026-04-05 WeKan ® release
This release adds the following updates:
- Docs: IMPORTANT: Do NOT `npm audit fix --force`, it breaks builds.
[Part 1](https://github.com/wekan/wekan/commit/2bad17010cd445c004a228b732b17c68e18eb316),
[Part 2](https://github.com/wekan/wekan/commit/9fa6574c80589218569dc3c49782a1a8036aa542).
Thanks to xet7.
- [Updated dependencies](https://github.com/wekan/wekan/commit/73d69f4631216dfe504d9f05230adadc2850c4f3).
Thanks to developers of dependencies.
and fixes the following bugs:
- [Fix Cards Reports is not working](https://github.com/wekan/wekan/commit/2b02e7712ee6a8ae8af5d416ce2d288a25386fdf).
Thanks to titver968 and xet7.
- [Fix Search All Boards](https://github.com/wekan/wekan/commit/faf8453c6d10613edf32a734e549961860fcdd32).
Thanks to YosserDerbali and xet7.
- [Try to fix Snap](https://github.com/wekan/wekan/commit/8443e3511d61d8c1bb419ed5bca2e28483c5d133).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.54 2026-04-04 WeKan ® release
This release fixes the following bugs:
- [Revert v8.53 Refactor upsert calls to use async method](https://github.com/wekan/wekan/commit/811b79b641c765461f3d696ed00b5459a4747867).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.53 2026-04-04 WeKan ® release
This release fixes the following bugs:
- [Refactor upsert calls to use async method](https://github.com/wekan/wekan/pull/6243).
Thanks to snowsky.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.52 2026-04-03 WeKan ® release
This release fixes the following bugs:
- [Try to fix Docker and Snap](https://github.com/wekan/wekan/commit/03e0cfdc15c56ac0219f5e435034a1c0ad5006ae).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.51 2026-04-03 WeKan ® release
This release adds the following updates:
- [Updated release scripts](https://github.com/wekan/wekan/commit/4594711be41d1e9cf15b1f8bb070fcb36bf736ca).
Thanks to xet7.
and fixes the following bugs:
- [Fix Bug: WeKan v8.50 WRITABLE_PATH](https://github.com/wekan/wekan/commit/edc6b18b5f77320e966e56c888575bcb764096dc).
Thanks to snowsky and xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.50 2026-04-03 WeKan ® release
This release adds the following updates:
- [Uncommented build steps. Not yet tested are required dependencies included](https://github.com/wekan/wekan/commit/145a17fee5dc9430de7a60100657566a4a69bc7b).
Thanks to xet7.
- [Updated npm package @wekanteam/exceljs](https://github.com/wekan/wekan/commit/6e8589b1f96b5f0c2ddec0831b238096ce610b1e).
Thanks to xet7.
- [Updated dependencies](https://github.com/wekan/wekan/commit/bf02c9a453e27edba1c08830eb4783b9dfc7bbc2).
Thanks to developers of dependencies.
NOTE: Do not `npm audit fix --force`, it downgrades @meteorjs/rspack and breaks builds.
`npm audit` shows false info, it does not recognize that elliptic is
already updated to 6.6.1 with override at package.json like you can see with
`npm list elliptic`.
and fixes the following bugs:
- [Fix indentation for release-all.yml workflow](https://github.com/wekan/wekan/pull/6240).
Thanks to snowsky.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.49 2026-03-31 WeKan ® release
This release fixes the following bugs:
- [Fix: Missing module @meteorjs/reify/lib/runtime](https://github.com/wekan/wekan/pull/6237).
Thanks to mzch and kovacs-andras.
- [Fix docker-compose.yml](https://github.com/wekan/wekan/commit/d07fcceb6a884cf1ff0c9a2d36691af0304f36e7).
Thanks to xet7.
- [Try to fix Snap](https://github.com/wekan/wekan/commit/8b4676175caa4af36bf11567676f3c3f522f9512).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.48 2026-03-31 WeKan ® release
This release adds the following updates:
- Updated release scripts.
[Part 1](https://github.com/wekan/wekan/commit/43601c5e3e95d6026f2e558f261c02a9578dcecd),
[Part 2](https://github.com/wekan/wekan/commit/56377aaf4c5e92c1a03dc996b6c649ce17553280).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.47 2026-03-31 WeKan ® release
This release adds the following updates:
- [Updated attachments and avatar path to be at WRITABLE_PATH/files/attachments etc](https://github.com/wekan/wekan/commit/e58e99c2852dd81a040dcffc0f1f2798c4b8cf15).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.46 2026-03-31 WeKan ® release
This release adds the following updates:
- Updated release scripts.
[Part 1](https://github.com/wekan/wekan/commit/6bc68ba57bf15339b80011d131a7da65124e071d),
[Part 2](https://github.com/wekan/wekan/commit/ce52b8a0ab29805c562a2af0b79e30d970594e17).
Thanks to xet7.
- [Updated docker-compose.yml to change from FerretDB/PostgreSQL to MongoDB](https://github.com/wekan/wekan/commit/02b75824e5dc24c968959d5a64f846efe33dee4f).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.45 2026-03-31 WeKan ® release
This release adds the following updates:
- Updated release scripts.
[Part 1](https://github.com/wekan/wekan/commit/4223c2a79f4c276ab1c9734a94314f1d1d1ce033),
[Part 2](https://github.com/wekan/wekan/commit/6125822cf7244242832d1fe02f99150abb7350d0),
[Part 3](https://github.com/wekan/wekan/commit/e90b2e384c168fbb4cee389d8af9b54a5bc57734),
[Part 4](https://github.com/wekan/wekan/commit/29550cbd9335fc95e4f4ac399fe754ece8573450),
[Part 5](https://github.com/wekan/wekan/commit/8349fb6b63097970d06af73af388b030761967bd),
[Part 6](https://github.com/wekan/wekan/commit/f43b76c323c1f09cf1885251792b29229ef77d2b),
[Part 7](https://github.com/wekan/wekan/commit/e90b2e384c168fbb4cee389d8af9b54a5bc57734).
Thanks to xet7.
- Updated GitHub Actions.
[Part 1](https://github.com/wekan/wekan/commit/4b8cc0683e9dfdffb7ff7896f622afe8f908a7f5),
[Part 2](https://github.com/wekan/wekan/commit/df067f01e894f43305ad36cebdf48a1bf55ff33a),
[Part 3](https://github.com/wekan/wekan/commit/ec1e3835586bdc09d93d5049794e8657b460acef),
[Part 4](https://github.com/wekan/wekan/commit/79853a2fb7efb738f2f10b6c2e9fef69def88c48),
[Part 5](https://github.com/wekan/wekan/commit/9ae2842fa4bedbd4c132943703ef462cc353ab1a),
[Part 6](https://github.com/wekan/wekan/commit/1a5e23617de98e6b65f78dfb25c233440f818d11),
[Part 7](https://github.com/wekan/wekan/commit/c38fa1c272c32323d8a164dfcf34b898127e6b1d),
[Part 8](https://github.com/wekan/wekan/commit/d8a2e06a95713e33e5676df38951dfd3f991ecb3),
[Part 9](https://github.com/wekan/wekan/commit/e6ccbfd3b88299c52fbaad9f85fc3b438e3cdc89),
[Part 10](https://github.com/wekan/wekan/commit/7666c6c0e1660b5aba300f4e465877bcf73d8066).
Thanks to dependabot.
and fixes the following bugs:
- Try to fix Snap.
[Part 1](https://github.com/wekan/wekan/commit/e5c551f0092ccb71fafa3433bfd99911f9a2e9c0),
[Part 2](https://github.com/wekan/wekan/commit/55ec0b5ff5594c10ebbc8cba74339de2ad0557a6),
[Part 3](https://github.com/wekan/wekan/commit/fd58bb9e4dd0f825237a50043a171fc223d44101),
[Part 4](https://github.com/wekan/wekan/commit/cdf1d3305bd738fe5357a5f3a9df2f7e6858d1d0),
[Part 5](https://github.com/wekan/wekan/commit/2aa8ecfc39a6f971134784817b46ba9867bb402d),
[Part 6](https://github.com/wekan/wekan/commit/ed718f083aa0c9f82e368a5badfe73393753a617),
[Part 7](https://github.com/wekan/wekan/commit/5c4130311a063340bd89dff571fbf685cda7a3ba),
[Part 8](https://github.com/wekan/wekan/commit/33267fa2e462fcc6cd2243ae1b5b39d59b03988f),
[Part 9](https://github.com/wekan/wekan/commit/87916c20bf579e16e21fa3bd9ac7687019b277eb),
[Part 10](https://github.com/wekan/wekan/commit/12c223d2260c9bd9547ae35a394869ef90e8bc83).
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.44 2026-03-30 WeKan ® release
This release adds the following updates:
- [From WeKan v8.43 FerretDB/PostgreSQL back to MongoDB 7.x](https://github.com/wekan/wekan/commit/cf2b3e4fc95ce4619c1ba13b63c3ad9d90bf59f6).
Thanks to xet7.
- [Updated dependencies](https://github.com/wekan/wekan/commit/82f1de078f16e87028ffd101e3f80550a4b18afa).
Thanks to developers of dependencies.
- Updated release scripts.
[Part 1](https://github.com/wekan/wekan/commit/a702596bee6aa812cb69a6673ad34f9b2875c729),
[Part 2](https://github.com/wekan/wekan/commit/29550cbd9335fc95e4f4ac399fe754ece8573450).
Thanks to xet7.
and fixes the following bugs:
- [Always show all data without migrations, like Shared Lists that were before Per-Swimlanes lists, Orphaned Swimlanes/Lists/Cards etc](https://github.com/wekan/wekan/commit/ef75b9ecac8210ff000439aca92765b53a1551a4).
Thanks to xet7.
- [Fix globalSearch](https://github.com/wekan/wekan/commit/b788d398b9b2cb0caad9c28f79acdceb5b577f25).
Thanks to snowsky.
- [Fix myCards crash](https://github.com/wekan/wekan/commit/d5c9e402ce4ebe0318bc29d5b83784771cbe8ac3).
Thanks to cristomoya.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.43 2026-03-27 WeKan ® release
This release adds the following updates:
- Updated to Meteor 3.4, Node.js 22.22, MongoDB 7.x.
[Part 1](https://github.com/wekan/wekan/pull/6205),
[Part 2](https://github.com/wekan/wekan/commit/7584afda6d28b75ae7bfa5388f71e8bcb5ab50a7).
Thanks to harryadel and xet7.
- [Updated dependencies of npm package @wekanteam/exceljs](https://github.com/wekan/wekan/commit/95ff8ef1f2f119c713ffba72122d51b6569b9d42).
Thanks to xet7.
- [Updated @meteorjs/rspack](https://github.com/wekan/wekan/commit/21ab640686d868ee46e16e812b36a1c3820952a5).
Thanks to Meteor developers.
- [Updated rebuild-wekan.sh for macOS 26.4](https://github.com/wekan/wekan/commit/f1e71247c70043634e040bd12f7d065b30ed6fc2).
Thanks to xet7.
- [Moved Meteor 3 migration docs to docs/Upgrade/](https://github.com/wekan/wekan/commit/517d1483876926cdbf6ce4774c8c52540eaec593).
Thanks to xet7.
- Updated dependencies.
[Part 1](https://github.com/wekan/wekan/commit/acfb081b907703c6846b8ccf71a9e2912b41713f),
[Part 2](https://github.com/wekan/wekan/commit/55660e0e69b7a283ab04ad408674d33eb71bb279).
Thanks to developers of dependencies.
- Updated release scripts.
[Part 1](https://github.com/wekan/wekan/commit/0fdf45e8476c0a619cd36891568c67d9092bd65b),
[Part 2](https://github.com/wekan/wekan/commit/07eb062474a5593683c1ed32f2637339fab72d48),
[Part 3](https://github.com/wekan/wekan/commit/5cf3bba68fdfc57a7a9358bd914c11af7bdd37d4),
[Part 4](https://github.com/wekan/wekan/commit/0e59033ea60f504d09d7eddb22a33ed113ea1326).
Thanks to xet7.
and fixes the following bugs:
- [Fixed rebuild-wekan.sh to build WeKan correctly](https://github.com/wekan/wekan/commit/4a8293f8812276f08d8a77a2c8be56441d7e87c1).
Thanks to xet7.
- [Export Card to Excel. Part 3. Also, fixed uploading attachments](https://github.com/wekan/wekan/commit/5c5ed102cabd07e1be00d3fdb01ea599da0a5517).
Thanks to xet7.
- [Removed temporary debug logs](https://github.com/wekan/wekan/commit/c1e3ce64a796fc8b921d1a30705b4aaf66c9c259).
Thanks to xet7.
- [Fixed typo](https://github.com/wekan/wekan/commit/23697d2a33ca527b29bcdadfe432fd9d1a180f9c).
Thanks to YosserDerbali and xet7.
- [Fix Set Color of Swimlane, List and Card](https://github.com/wekan/wekan/commit/0630d62ad0a02d80ab77436ce9797e4390c5cae2).
Thanks to SVANNER, brlin-tw and xet7.
- [Fix workspace settings modal overflow](https://github.com/wekan/wekan/pull/6218).
Thanks to AymenHassini19 and aminsaidane.
- [Fix attachment rename fails for clipboard uploads and keep extension metadata in sync](https://github.com/wekan/wekan/commit/38c670e08700f7b0ff185aadda40985bc71b1ac4).
Thanks to snowsky, YosserDerbali and xet7.
- [Fix card actions popup overflow](https://github.com/wekan/wekan/pull/6220).
Thanks to aminsaidane.
- [Fix Search: Guard Async methods in ReactiveCache and search publications](https://github.com/wekan/wekan/pull/6222).
Thanks to KhaoulaMaleh.
- [Fix/list actions popup overflow scroll](https://github.com/wekan/wekan/pull/6223).
Thanks to YosserDerbali.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.42 2026-03-23 WeKan ® release
This release fixes the following bugs:
- [Fix SSO login is broken](https://github.com/wekan/wekan/commit/2dce7a84cd0ce011972d377a05f5b26ff12daba7).
Thanks to sbaecker, vladimirdulov and xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v8.41 2026-03-23 WeKan ® release
+1 -1
View File
@@ -9,7 +9,7 @@ Not paid:
- This is because full-featured WeKan FLOSS kanban with MIT license is free to use
also for Commercial Use without any payment, with full source code at
https://github.com/wekan/wekan for all platforms at https://wekan.fi/install/ .
There is nothing propietary. WeKan is NOT Big Tech.
There is nothing proprietary. WeKan is NOT Big Tech.
WeKan is maintained by https://github.com/xet7 at Finland https://wekan.fi .
- Public FLOSS Developer Community Support:
- https://github.com/wekan/wekan/issues
+23 -19
View File
@@ -4,20 +4,20 @@ LABEL org.opencontainers.image.ref.name="ubuntu"
LABEL org.opencontainers.image.version="24.04"
LABEL org.opencontainers.image.source="https://github.com/wekan/wekan"
# TARGETARCH is automatically provided by Docker Buildx
# TARGETARCH and TARGETVARIANT are automatically provided by Docker Buildx
ARG TARGETARCH
ARG TARGETVARIANT
ARG VERSION=9.02
ARG DEBIAN_FRONTEND=noninteractive
ENV BUILD_DEPS="apt-utils gnupg 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.16 \
NODE_VERSION=v24.15.0 \
METEOR_RELEASE=METEOR@3.5-beta.7 \
USE_EDGE=false \
METEOR_EDGE=1.5-beta.17 \
NPM_VERSION=6.14.17 \
FIBERS_VERSION=4.0.1 \
NPM_VERSION=11.11.0 \
SRC_PATH=./ \
WITH_API=true \
MONGO_OPLOG_URL="" \
@@ -169,25 +169,28 @@ apt-get update --assume-yes
apt-get upgrade --assume-yes
apt-get install --assume-yes --no-install-recommends ${BUILD_DEPS}
# Multi-arch mapping logic
# Multi-arch mapping: Docker TARGETARCH -> Node.js arch name + WeKan bundle name
# arm/v7 uses TARGETARCH=arm; armhf has no MongoDB Community -> uses FerretDB
case "${TARGETARCH}" in
"amd64") NODE_ARCH="x64" WEKAN_ARCH="amd64" ;;
"arm64") NODE_ARCH="arm64" WEKAN_ARCH="arm64" ;;
"s390x") NODE_ARCH="s390x" WEKAN_ARCH="s390x" ;;
"amd64") NODE_ARCH="x64" WEKAN_ARCH="amd64" ;;
"arm64") NODE_ARCH="arm64" WEKAN_ARCH="arm64" ;;
"arm") NODE_ARCH="armv7l" WEKAN_ARCH="armhf" ;;
"ppc64le") NODE_ARCH="ppc64le" WEKAN_ARCH="ppc64le" ;;
"s390x") NODE_ARCH="s390x" WEKAN_ARCH="s390x" ;;
*) echo "Unsupported architecture: ${TARGETARCH}"; exit 1 ;;
esac
# Node.js Installation
# Node.js Installation (official nodejs.org builds for Node 22)
cd /tmp
wget "https://github.com/wekan/node-v14-esm/releases/download/${NODE_VERSION}/node-${NODE_VERSION}-linux-${NODE_ARCH}.tar.gz"
wget "https://github.com/wekan/node-v14-esm/releases/download/${NODE_VERSION}/SHASUMS256.txt"
grep "node-${NODE_VERSION}-linux-${NODE_ARCH}.tar.gz" SHASUMS256.txt | shasum -a 256 -c -
wget "https://nodejs.org/dist/${NODE_VERSION}/node-${NODE_VERSION}-linux-${NODE_ARCH}.tar.gz"
wget "https://nodejs.org/dist/${NODE_VERSION}/SHASUMS256.txt.asc"
grep "node-${NODE_VERSION}-linux-${NODE_ARCH}.tar.gz" SHASUMS256.txt.asc | shasum -a 256 -c -
tar xzf "node-${NODE_VERSION}-linux-${NODE_ARCH}.tar.gz" -C /usr/local --strip-components=1 --no-same-owner
rm -f "node-${NODE_VERSION}-linux-${NODE_ARCH}.tar.gz" SHASUMS256.txt
rm -f "node-${NODE_VERSION}-linux-${NODE_ARCH}.tar.gz" SHASUMS256.txt.asc
ln -s "/usr/local/bin/node" "/usr/local/bin/nodejs"
# NPM configuration
npm install -g npm@${NPM_VERSION} --production
npm install -g npm@${NPM_VERSION}
chown --recursive wekan:wekan /home/wekan/
# Temporary Tar swap for Meteor bundle
@@ -197,9 +200,10 @@ ln -sf $(which bsdtar) $(which tar)
# WeKan Bundle Installation
mkdir -p /home/wekan/app
cd /home/wekan/app
wget "https://github.com/wekan/wekan/releases/download/v8.41/wekan-8.41-${WEKAN_ARCH}.zip"
unzip "wekan-8.41-${WEKAN_ARCH}.zip"
rm "wekan-8.41-${WEKAN_ARCH}.zip"
wget "https://github.com/wekan/wekan/releases/download/v${VERSION}/wekan-${VERSION}-${WEKAN_ARCH}.zip"
unzip "wekan-${VERSION}-${WEKAN_ARCH}.zip"
rm "wekan-${VERSION}-${WEKAN_ARCH}.zip"
npm install --prefix ./bundle/programs/server
mv /home/wekan/app/bundle /build
# Restore original tar
-98
View File
@@ -1,98 +0,0 @@
FROM arm64v8/ubuntu:23.04 AS builder
#FROM amd64/alpine:latest AS builder
# Set the environment variables for builder
ENV QEMU_VERSION=v7.2.0-1 \
QEMU_ARCHITECTURE=aarch64 \
NODE_ARCHITECTURE=linux-arm64 \
NODE_VERSION=v14.21.4 \
WEKAN_VERSION=latest \
WEKAN_ARCHITECTURE=arm64
# Install dependencies
#RUN apk update && apk add ca-certificates outils-sha1 && \
RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections
RUN apt update && apt install ca-certificates wget unzip -y && \
\
# Download qemu static for our architecture
wget https://github.com/multiarch/qemu-user-static/releases/download/${QEMU_VERSION}/qemu-${QEMU_ARCHITECTURE}-static.tar.gz -O - | tar -xz && \
\
# Download wekan and shasum
wget https://releases.wekan.team/raspi3/wekan-${WEKAN_VERSION}-${WEKAN_ARCHITECTURE}.zip && \
wget https://releases.wekan.team/raspi3/SHA256SUMS.txt && \
# Verify wekan
grep wekan-${WEKAN_VERSION}-${WEKAN_ARCHITECTURE}.zip SHA256SUMS.txt | sha256sum -c - && \
\
# Unzip wekan
unzip wekan-${WEKAN_VERSION}-${WEKAN_ARCHITECTURE}.zip && \
\
# Download node and shasums
wget https://github.com/wekan/node-v14-esm/releases/download/${NODE_VERSION}/node-${NODE_VERSION}-${NODE_ARCHITECTURE}.tar.gz && \
wget https://github.com/wekan/node-v14-esm/releases/download/${NODE_VERSION}/SHASUMS256.txt && \
#wget https://nodejs.org/dist/${NODE_VERSION}/node-${NODE_VERSION}-${NODE_ARCHITECTURE}.tar.gz && \
#wget https://nodejs.org/dist/${NODE_VERSION}/SHASUMS256.txt.asc && \
#wget https://nodejs.org/dist/${NODE_VERSION}/node-${NODE_VERSION}-${NODE_ARCHITECTURE}.tar.gz && \
#wget https://nodejs.org/dist/${NODE_VERSION}/SHASUMS256.txt.asc && \
\
# Verify nodejs authenticity
grep node-${NODE_VERSION}-${NODE_ARCHITECTURE}.tar.gz SHASUMS256.txt | sha256sum -c - && \
\
# Extract node and remove tar.gz
tar xvzf node-${NODE_VERSION}-${NODE_ARCHITECTURE}.tar.gz
# Build wekan dockerfile
FROM --platform=linux/arm64 arm64v8/ubuntu:23.04
LABEL maintainer="wekan"
# Set the environment variables (defaults where required)
ENV QEMU_ARCHITECTURE=aarch64 \
NODE_ARCHITECTURE=linux-arm64 \
NODE_VERSION=v14.21.4 \
NODE_ENV=production \
NPM_VERSION=latest \
WITH_API=true \
PORT=8080 \
ROOT_URL=http://localhost \
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
# Copy the app to the image
COPY --from=builder bundle /home/wekan/bundle
# Copy
COPY --from=builder node-${NODE_VERSION}-${NODE_ARCHITECTURE} /opt/nodejs
RUN \
set -o xtrace && \
# Add non-root user wekan
useradd --user-group --system --home-dir /home/wekan wekan && \
\
# Install Node
ln -s /opt/nodejs/bin/node /usr/bin/node && \
ln -s /opt/nodejs/bin/npm /usr/bin/npm && \
mkdir -p /opt/nodejs/lib/node_modules/fibers/.node-gyp /root/.node-gyp/8.16.1 /home/wekan/.config && \
chown wekan --recursive /home/wekan/.config
# \
# # Install Node dependencies
# #npm install -g npm@${NPM_VERSION} && \
# \
# # Install Health Check dependencies
# #apk add curl
#
#HEALTHCHECK --start-period=30s --interval=30s --timeout=10s --retries=3 \
# CMD curl --fail "http://localhost:$PORT" || exit 1
EXPOSE $PORT
USER wekan
# CMD ["bash", "-c", "ulimit -s 65500; exec node --stack-size=65500 --max-old-space-size=8192 /home/wekan/bundle/main.js"]
CMD ["bash", "-c", "ulimit -s 65500; exec node /home/wekan/bundle/main.js"]
-99
View File
@@ -1,99 +0,0 @@
FROM arm64v8/ubuntu:23.04 AS builder
#FROM --platform=linux/amd64 amd64/ubuntu:23.04 AS builder
#FROM --platform=linux/amd64 ghcr.io/wekan/wekan:main AS builder
#FROM arm64v8/ubuntu:23.04 AS builder
#FROM amd64/alpine:latest AS builder
# Set the environment variables for builder
ENV QEMU_VERSION=v7.2.0-1 \
QEMU_ARCHITECTURE=s390x \
NODE_ARCHITECTURE=linux-s390x \
NODE_VERSION=v14.21.4 \
WEKAN_VERSION=latest \
WEKAN_ARCHITECTURE=s390x
# Install dependencies
#RUN apk update && apk add ca-certificates outils-sha1 && \
RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections
RUN apt update && apt install ca-certificates wget unzip -y && \
\
# Download qemu static for our architecture
wget https://github.com/multiarch/qemu-user-static/releases/download/${QEMU_VERSION}/qemu-${QEMU_ARCHITECTURE}-static.tar.gz -O - | tar -xz && \
\
# Download wekan and shasum
wget https://releases.wekan.team/${WEKAN_ARCHITECTURE}/wekan-${WEKAN_VERSION}-${WEKAN_ARCHITECTURE}.zip && \
wget https://releases.wekan.team/${WEKAN_ARCHITECTURE}/SHA256SUMS.txt && \
# Verify wekan
grep wekan-${WEKAN_VERSION}-${WEKAN_ARCHITECTURE}.zip SHA256SUMS.txt | sha256sum -c - && \
\
# Unzip wekan
unzip wekan-${WEKAN_VERSION}-${WEKAN_ARCHITECTURE}.zip && \
\
# Download node and shasums
wget https://github.com/wekan/node-v14-esm/releases/download/${NODE_VERSION}/node-${NODE_VERSION}-${NODE_ARCHITECTURE}.tar.gz && \
wget https://github.com/wekan/node-v14-esm/releases/download/${NODE_VERSION}/SHASUMS256.txt && \
#wget https://nodejs.org/dist/${NODE_VERSION}/node-${NODE_VERSION}-${NODE_ARCHITECTURE}.tar.gz && \
#wget https://nodejs.org/dist/${NODE_VERSION}/SHASUMS256.txt.asc && \
#wget https://nodejs.org/dist/${NODE_VERSION}/node-${NODE_VERSION}-${NODE_ARCHITECTURE}.tar.gz && \
#wget https://nodejs.org/dist/${NODE_VERSION}/SHASUMS256.txt.asc && \
\
# Verify nodejs authenticity
grep node-${NODE_VERSION}-${NODE_ARCHITECTURE}.tar.gz SHASUMS256.txt | sha256sum -c - && \
\
# Extract node and remove tar.gz
tar xvzf node-${NODE_VERSION}-${NODE_ARCHITECTURE}.tar.gz
# Build wekan dockerfile
FROM --platform=linux/s390x s390x/ubuntu:23.04
LABEL maintainer="wekan"
# Set the environment variables (defaults where required)
ENV QEMU_ARCHITECTURE=s390x \
NODE_ARCHITECTURE=linux-s390x \
NODE_VERSION=v14.21.4 \
NODE_ENV=production \
NPM_VERSION=latest \
WITH_API=true \
PORT=8080 \
ROOT_URL=http://localhost \
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
# Copy the app to the image
COPY --from=builder bundle /home/wekan/bundle
# Copy
COPY --from=builder node-${NODE_VERSION}-${NODE_ARCHITECTURE} /opt/nodejs
RUN \
set -o xtrace && \
# Add non-root user wekan
useradd --user-group --system --home-dir /home/wekan wekan && \
\
# Install Node
ln -s /opt/nodejs/bin/node /usr/bin/node && \
ln -s /opt/nodejs/bin/npm /usr/bin/npm && \
mkdir -p /opt/nodejs/lib/node_modules/fibers/.node-gyp /root/.node-gyp/8.16.1 /home/wekan/.config && \
chown wekan --recursive /home/wekan/.config
# \
# # Install Node dependencies
# #npm install -g npm@${NPM_VERSION} && \
# \
# # Install Health Check dependencies
# #apk add curl
#
#HEALTHCHECK --start-period=30s --interval=30s --timeout=10s --retries=3 \
# CMD curl --fail "http://localhost:$PORT" || exit 1
EXPOSE $PORT
USER wekan
CMD ["bash", "-c", "ulimit -s 65500; exec node /home/wekan/bundle/main.js"]
+70 -6
View File
@@ -47,12 +47,13 @@ See bottom of https://github.com/wekan/wekan/issues/3874
## FAQ
**NOTE**:
- 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 ®
WeKan ® is an completely [Open Source][open_source] and [Free software][free_software]
WeKan ® is a completely [Open Source][open_source] and [Free software][free_software]
collaborative kanban board application with MIT license.
Whether you’re maintaining a personal todo list, planning your holidays with some friends,
@@ -76,15 +77,15 @@ that by providing one-click installation on various platforms.
- 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.
- Enough disk space and alerts about low disk space. If you run out disk space, MongoDB database gets corrupted.
- Enough disk space and alerts about low disk space. If you run out of disk space, MongoDB database gets corrupted.
- SECURITY: Updating to newest WeKan ® version very often. Please check you do not have automatic updates of Sandstorm or Snap turned off.
Old versions have security issues because of old versions Node.js etc. Only newest WeKan ® is supported.
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/blob/main/docs/Backup/Backup.md) 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 minimum.
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.
Some bugs can cause WeKan ® board to not load at all, requiring manual fixing of database content.
## Roadmap and Demo
@@ -92,7 +93,7 @@ that by providing one-click installation on various platforms.
[Developer Documentation][dev_docs]
- There is many companies and individuals contributing code to WeKan ®, to add features and bugfixes
- There are 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.fi/commercial-support/).
@@ -102,12 +103,75 @@ By working directly with WeKan ® you get the benefit of active maintenance and
## Getting Started with Development
The default branch uses [Meteor 2 with Node.js 14](https://wekan.fi/install/).
The default branch uses Meteor 3.5-beta.7 with Node.js 24.x.
See [CHANGELOG.md](https://github.com/wekan/wekan/blob/main/CHANGELOG.md) for the latest runtime updates.
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.
## First-Time Setup for Development
### Prerequisites
Before building WeKan from source, ensure you have:
- **Git** - for cloning the repository
- **Node.js 24.x** - WeKan requires Node.js 24.x
- **Meteor** - the JavaScript framework WeKan is built with
### Installing Node.js 24.x
```bash
# Using nvm (recommended)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
source ~/.zshrc # or ~/.bashrc
nvm install 24
nvm use 24
```
### Installing Meteor
```bash
curl https://install.meteor.com/ | sh
```
### Building WeKan
The `rebuild-wekan.sh` script guides you through a three-stage process:
1. **Install dependencies (Option 1)** - Downloads all required Meteor packages and npm modules
2. **Build WeKan (Option 2)** - Compiles the application
3. **Run WeKan (Option 3)** - Starts the development server at http://localhost:4000
```bash
# Clone your fork
git clone git@github.com:YOUR_USERNAME/wekan.git
cd wekan
# Make the script executable
chmod +x rebuild-wekan.sh
# Step 1: Install dependencies
./rebuild-wekan.sh
# Press 1 and Enter
# Step 2: Build WeKan (after dependencies complete)
./rebuild-wekan.sh
# Press 2 and Enter
# Step 3: Run WeKan in development mode
./rebuild-wekan.sh
# Press 3 and Enter
```
### WSL Users
WSL users can use Snap Candidate. See [install docs](https://wekan.fi/install/).
The development server will start at http://localhost:4000. Any changes you make to the source code will automatically trigger a rebuild and refresh your browser.
## Screenshot
[More screenshots at Features page](https://github.com/wekan/wekan/tree/main/docs/Features)
+61 -79
View File
@@ -1,38 +1,21 @@
## About money
## About Money: No bounty
See [CONTRIBUTING](CONTRIBUTING.md)
There is no bounty, like described at [CONTRIBUTING.md](CONTRIBUTING.md),
because WeKan is NOT Big Tech. WeKan is FLOSS.
## Responsible Security Disclosure
1. To send email, if possible, use PGP key [security-at-wekan.fi.asc](security-at-wekan.fi.asc)
2. Send info about security issue ONLY to security@wekan.fi . NOT TO ANYWHERE ELSE. NO CC, NO BCC.
3. Wait for new WeKan release that fixes security issue to appear to top of
1. Please email info about security issue to security@wekan.fi .
If possible, include code for fixing security issue.
Optional PGP key is at [security-at-wekan.fi.asc](security-at-wekan.fi.asc)
2. Wait for new WeKan release that fixes security issue to appear to top of
https://github.com/wekan/wekan/blob/main/CHANGELOG.md
4. We will thank you by adding you to Hall of Fame: https://wekan.fi/hall-of-fame/
5. All vulnerability details will be private to security@wekan.fi ,
unless you help all WeKan platforms to have a way to upgrade, like sending
database migrations code to security@wekan.fi or PRs to https://github.com/wekan/wekan/pulls .
There is no benefit to Wordwide Security Community to have more details about vulnerabilities,
if Worldwide Security Community does not help to make upgrades possible.
6. If there some day becomes available a way to upgrade all WeKan platforms,
this page will be updated to add permission for security researchers
to request new GHSA or CVE ID and publish your vulnerability details at your blog, talks, etc,
and send that info also to security@wekan.fi to be added to
Hall of Fame: https://wekan.fi/hall-of-fame/ to get Upgrade Bonus Point Stars.
In that case, it will become possible for security@wekan.fi to publish all
remaining private security details, and publicly thank Worldwide Security Community.
3. We will thank you by adding you to
Hall the of Fame: https://wekan.fi/hall-of-fame/
4. If you get CVE number assigned, please send it to security@wekan.fi, it will be added to
Hall the of Fame: https://wekan.fi/hall-of-fame/
## Bonus Points
- If you include code for fixing security issue
## Losing Points
- If you ask about [bounty](CONTRIBUTING.md). There is no bounty. WeKan is NOT Big Tech. WeKan is FLOSS.
- If you forget to include vulnerability details.
- If you send info about security issue to somewhere else than security@wekan.fi
## How should reports be formatted?
## How Should Reports Be Formatted?
```
Name: %name
@@ -57,15 +40,15 @@ added to the WeKan Hall of Fame https://wekan.fi/hall-of-fame/
No public domains, because all those are donated to Wekan Open Source project,
and we don't have any permissions to do security scans on those donated servers.
Please don't perform research that could impact other users. Secondly, please keep
the reports short and succinct. If we fail to understand the logics of your bug, we will tell you.
Please don't perform research that could impact other users. Second, please keep
the reports short and succinct. If we fail to understand the logic of your bug, we will tell you.
You can [Install Wekan](https://github.com/wekan/wekan/releases) to your own computer
You can [Install Wekan](https://wekan.fi/install/) on your own computer
and scan it's vulnerabilities there.
## About Wekan versions
## About Wekan Versions
There are only 2 versions of Wekan: Standalone Wekan, and Sandstorm Wekan.
There are 2 versions of Wekan: Standalone Wekan, and Sandstorm Wekan.
### Standalone Wekan Security
@@ -74,11 +57,12 @@ like Snap and Docker have their own specific sandboxing etc features.
Standalone Wekan by default does not load any files from Internet, like fonts, CSS, etc.
This also means all Standalone Wekan functionality works in offline local networks.
WeKan is used at most countries of the world https://snapcraft.io/wekan
and by by companies that have 30k users.
WeKan is used in most countries of the world https://snapcraft.io/wekan
and by companies that have 30k users.
- Wekan private board attachments are not accessible without logging in.
- There is feature to set board public, so that board is visible without logging in in readonly mode, with realtime updates.
- There is feature to set board public, so that board is visible without logging
in readonly mode, with realtime updates.
- Admin Panel has feature to disable all public boards, so all boards are private.
## SSL/TLS
@@ -90,24 +74,25 @@ and by by companies that have 30k users.
## XSS
- Dompurify https://www.npmjs.com/package/dompurify
- WeKan uses dompurify npm package to filter for XSS at fields like cards, as you can see from
- DOMpurify https://www.npmjs.com/package/dompurify
- WeKan uses DOMpurify npm package to filter for XSS at fields like cards, as you can see from
[package.json](https://github.com/wekan/wekan/blob/main/package.json). Other used versions can be seen from
[Meteor versions file](https://github.com/wekan/wekan/blob/main/.meteor/versions).
- Forms can include markdown links, html, image tags etc like you see at https://wekan.github.io .
- It's possible to add attachments to cards, and markdown/html links to files.
- Dompurify cleans up viewed code, so Javascript in input fields does not execute
- Dompurify cleans up viewed code, so JavaScript in input fields does not execute
- https://wekan.github.io/hall-of-fame/fieldbleed/
- Reaction in comment is now checked, that it does not have extra added code
- https://wekan.github.io/hall-of-fame/reactionbleed/
- https://github.com/wekan/wekan/blob/main/packages/markdown/src/template-integration.js#L76
## QA about PubSub
## QA About PubSub
Q:
Hello,
I have just seen the Meteor DevTools Evolved extension and was wondering if anyone had asked themselves the question of security.
I have just seen the Meteor DevTools Evolved extension and was wondering if anyone
had asked themselves the question of security.
Insofar as all data is shown in the minimongo tab in plain text.
How can data be hidden from this extension?
@@ -115,32 +100,36 @@ A:
## PubSub
- It is not security issue to show some text or image, that user has permission to see. It is a security issue, if browserside is some text or image that user should not see.
- Meteor has browserside minimongo database, made with Javascript, updated with Publish/Subscribe, PubSub.
- Publish/Subscribe means, that realtime web framework reads database changes stream, and then immediately updates webpage,
like like dashboards, chat, kanban. That is the point in any realtime web framework in any programming language.
- Yes, you should check with Meteor DevTools Evolved Chromium/Firefox extension that at minimongo is only text that user has permission to see.
- Do checking as logged in user, and logged out user.
- It is not a security issue to show text or images that the user has permission to see.
It is a security issue, if browserside is some text or image that user should not see.
- Meteor has browserside minimongo database, made with JavaScript, updated with Publish/Subscribe, PubSub.
- Publish/Subscribe means, that realtime web framework reads database changes stream,
and then immediately updates webpage, like dashboards, chat, kanban.
That is the point of any realtime web framework in any programming language.
- Yes, you should check with Meteor DevTools Evolved Chromium/Firefox extension that
at minimongo is only text that user has permission to see.
- Check as a logged-in user and as a logged-out user.
- Check permissions and sanitize before allowing some change, because someone could modify content of input field,
PubSub/websocket data (for example with Burp Suite Community Edition), etc.
- If you have REST API, also check that only those that have login token, and have permission, can view or edit text
- If you have a REST API, also check that only those who have a login token and permission can view or edit text.
- You should not include any data user is not allowed to see. Not to webpage text, not to websockets/PubSub, etc.
- Minimongo should not have password hashes PubSub https://wekan.github.io/hall-of-fame/userbleed/
- PubSub uses Websockets, so you need those to be enabled at webserver like Caddy/Nginx/Apache etc, examples of settings
- PubSub uses WebSockets, so you need those to be enabled on the webserver like Caddy/Nginx/Apache etc, examples of settings
at right menu of https://github.com/wekan/wekan/wiki
- Clientside https://github.com/wekan/wekan/tree/main/client/components subscribes to
PubSub https://github.com/wekan/wekan/tree/main/server/publications or calls meteor methods at https://github.com/wekan/wekan/tree/main/models
- Clientside https://github.com/wekan/wekan/tree/main/client/components subscribes to
PubSub https://github.com/wekan/wekan/tree/main/server/publications or calls meteor methods
at https://github.com/wekan/wekan/tree/main/models
- For Admin:
- You can have input field for password https://github.com/wekan/wekan/blob/main/client/components/cards/attachments.js#L303-L312
- You can save password to database https://github.com/wekan/wekan/blob/main/client/components/cards/attachments.js#L303-L312
- Check that only current user or Admin can change password https://github.com/wekan/wekan/blob/main/client/components/cards/attachments.js#L303-L312
- Note that currentUser uses code like Meteor.user() in .js file
- Do not have password hashes in PubSub https://github.com/wekan/wekan/blob/main/server/publications/users.js
- Do not include password hashes in PubSub https://github.com/wekan/wekan/blob/main/server/publications/users.js
- Only show Admin Panel to Admin https://github.com/wekan/wekan/blob/main/client/components/settings/settingBody.jade#L3
- If there is a lot of data, use pagination https://github.com/wekan/wekan/blob/main/client/components/settings/peopleBody.js
- Only have limited amount of data published in PubSub. Limit in MongoDB query in publications how much is published. Too much could make browser too slow.
- Use Environment variables for any email etc passwords.
- But what if you would like to remove minimongo? And only use Meteor methods for saving? In that case, you don't have realtime updates,
- If there is a large amount of data, use pagination https://github.com/wekan/wekan/blob/main/client/components/settings/peopleBody.js
- Only publish a limited amount of data in PubSub. Limit in MongoDB query in publications how much is published. Too much could make browser too slow.
- Environment variables for email and other passwords.
- But what if you would like to remove Minimongo and only use Meteor methods for saving? In that case, you don't have realtime updates,
and you need to write much more code to load and save data yourself, handle any multi user data saving conflicts yourself,
and many Meteor Atmospherejs.com PubSub using packages would not work anymore https://github.com/wekan/we
@@ -152,10 +141,10 @@ A:
## Permissions and Roles
- For any user permissions, it's best to use Meteor package package https://github.com/Meteor-Community-Packages/meteor-roles .
- Currently WeKan has custom hardcoded permissions, WeKan does not yet use that meteor-roles package.
- For any user permissions, it's best to use Meteor package https://github.com/Meteor-Community-Packages/meteor-roles .
- Currently, WeKan has custom hardcoded permissions and does not yet use the meteor-roles package.
- Using permissions at WeKan sidebar https://github.com/wekan/wekan/blob/main/client/components/sidebar/sidebar.js#L1854-L1875
- List of roles https://github.com/wekan/wekan/wiki/REST-API-Role . Change at board or Admin Panel. Also Organizations/Teams.
- List of roles: https://github.com/wekan/wekan/wiki/REST-API-Role . Change at board or Admin Panel. Also Organizations/Teams.
- Worker role: https://github.com/wekan/wekan/issues/2788
- Not implemented yet: Granular Roles https://github.com/wekan/wekan/issues/3022
- Check is user logged in, with `if (Meteor.user()) {`
@@ -164,8 +153,8 @@ A:
## Environment variables
- For any passwords, use environment variables, those are serverside
- Do not copy environment variable to public variable that is visible browserside https://github.com/wekan/wekan/blob/main/server/max-size.js
- For any passwords, use environment variables; those are serverside
- Do not copy environment variables to public variables that are visible on the browserside https://github.com/wekan/wekan/blob/main/server/max-size.js
```
Meteor.startup(() => {
@@ -176,13 +165,13 @@ Meteor.startup(() => {
```
- For serverside, you can set Meteor.settings.variablename, without text public
- For WeKan kanban, there is feature for setting board public, it can be viewed by anyone, there is realtime updates. But
- For WeKan kanban, there is a feature for setting a board to public; it can be viewed by anyone, with realtime updates. But
- Some of those permissions are checked at users.js models at https://github.com/wekan/wekan/tree/main/models
- Environment variables are used for email server passwords, etc, at all platforms https://github.com/wekan/wekan/commit/a781c0e7dcfdbe34c1483ee83cec12455b7026f7
## Escape HTML comment tags so that HTML comments are visible
- Someone reported, that it is problem that content of HTML comments in edit mode, are not visible at at view mode, so this makes HTML comments visible.
- Someone reported that it was a problem that the content of HTML comments in edit mode was not visible in view mode, so this change makes HTML comments visible.
- https://github.com/wekan/wekan/commit/167863d95711249e69bb3511175d73b34acbbdb3
- https://wekan.github.io/hall-of-fame/invisiblebleed/
@@ -249,18 +238,16 @@ Meteor.startup(() => {
### Sandstorm Wekan Security
On Sandstorm platform using environment variable Standalone Wekan features like Admin Panel etc are
turned off, because Sandstorm platform provides SSO for all apps running on Sandstorm.
On the Sandstorm platform, Standalone WeKan features like the Admin Panel are turned off using environment variables, because Sandstorm platform provides SSO for all apps running on Sandstorm.
[Sandstorm](https://sandstorm.io) is separate Open Source platform that has been
[Sandstorm](https://sandstorm.io) is a separate Open Source platform that has been
[security audited](https://sandstorm.io/news/2017-03-02-security-review) and found bugs fixed.
Sandstorm also has passwordless login, LDAP, SAML, Google etc auth options already.
At Sandstorm code is read-only and signed by app maintainers, only grain content can be modified.
Wekan at Sandstorm runs in sandboxed grain, it does not have access elsewhere without user-visible
PowerBox request or opening randomly-generated API key URL.
Sandstorm also has passwordless login, LDAP, SAML, Google, and other auth options already.
On Sandstorm, code is read-only and signed by app maintainers; only grain content can be modified.
WeKan on Sandstorm runs in a sandboxed grain; it does not have access elsewhere without a user-visible PowerBox request or opening a randomly-generated API key URL.
Also read [Sandstorm Security Practices](https://docs.sandstorm.io/en/latest/using/security-practices/) and
[Sandstorm Security non-events](https://docs.sandstorm.io/en/latest/using/security-non-events/).
For Sandstorm specific security issues you can contact [kentonv](https://github.com/kentonv) by email.
For Sandstorm specific security issues you can contact [kentonv](https://github.com/kentonv) by email.
## What Wekan bugs are eligible?
@@ -276,16 +263,11 @@ a security issue, we'd like to know about it, and also how to fix it:
## What Wekan bugs are NOT eligible?
Typical already known or "no impact" bugs such as:
Typical already-known or 'no impact' bugs such as:
- [Wekan API old tokens not replaced correctly](https://github.com/wekan/wekan/issues/1437)
- Missing Cookie flags on non-session cookies or 3rd party cookies
- Logout CSRF
- Social engineering
- Denial of service
- SSL BEAST/CRIME/etc. Wekan does not have SSL built-in, it uses Caddy/Nginx/Apache etc at front.
Integrated Caddy support is updated often.
- Email spoofing, SPF, DMARC & DKIM. Wekan does not include email server.
- SSL BEAST/CRIME/etc. WeKan does not have SSL built-in; it uses Caddy/Nginx/Apache on the front end.
Wekan is Open Source with MIT license, and free to use also for commercial use.
We welcome all fixes to improve security by email to security@wekan.fi
+1 -1
View File
@@ -1,5 +1,5 @@
appId: wekan-public/apps/77b94f60-dec9-0136-304e-16ff53095928
appVersion: "v8.41.0"
appVersion: "v9.02.0"
files:
userUploads:
- README.md
+2 -1
View File
@@ -10,7 +10,8 @@ import '/client/config/blazeHelpers';
// Import board converter for on-demand conversion
import '/client/lib/boardConverter';
import '/client/components/boardConversionProgress';
import '/client/features/boardConversion';
import { Utils } from '/client/lib/utils';
// Import migration manager and progress UI - COMMENTED OUT
// import '/client/lib/attachmentMigrationManager';
+2 -2
View File
@@ -9,11 +9,11 @@ template(name="activities")
+cardActivities
template(name="boardActivities")
each activityData in currentBoard.activities
each activityData in boardActivitiesList
+activity(activity=activityData card=card mode=mode)
template(name="cardActivities")
each activityData in activities
each activityData in cardActivitiesList
+activity(activity=activityData card=card mode=mode)
template(name="activity")
+33 -3
View File
@@ -1,12 +1,17 @@
import { ReactiveCache } from '/imports/reactiveCache';
import { ReactiveVar } from 'meteor/reactive-var';
import { Template } from 'meteor/templating';
import DOMPurify from 'dompurify';
import { sanitizeHTML, sanitizeText } from '/imports/lib/secureDOMPurify';
import { TAPi18n } from '/imports/i18n';
import { Utils } from '/client/lib/utils';
import { getSidebarInstance } from '/client/features/sidebar/service';
const activitiesPerPage = 500;
Template.activities.onCreated(function () {
// Register with sidebar so it can call loadNextPage on us
const Sidebar = getSidebarInstance();
if (Sidebar) {
Sidebar.activitiesInstance = this;
}
@@ -22,7 +27,7 @@ Template.activities.onCreated(function () {
};
// TODO is sidebar always available? E.g. on small screens/mobile devices
const sidebar = Sidebar;
const sidebar = getSidebarInstance();
if (sidebar && sidebar.infiniteScrolling) {
sidebar.infiniteScrolling.resetNextPeak();
}
@@ -71,7 +76,9 @@ function _showActivities(data) {
const currentCard = Utils.getCurrentCard();
ret = currentCard.showActivities ?? false;
} else if (mode === 'card') {
ret = data?.card?.showActivities ?? false;
// For card mode, get showActivities from data context or fallback to current card
const card = data?.card || Utils.getCurrentCard();
ret = card?.showActivities ?? false;
} else {
ret = Utils.getCurrentBoard().showActivities ?? false;
}
@@ -80,8 +87,31 @@ function _showActivities(data) {
}
Template.activities.helpers({
showActivities() {
const data = Template.currentData();
return _showActivities(data);
},
activities() {
return this.card.activities();
const activities = this.card?.activities?.();
return activities || [];
},
});
Template.boardActivities.helpers({
boardActivitiesList() {
const board = Utils.getCurrentBoard();
const activities = board?.activities?.();
return activities || [];
},
});
Template.cardActivities.helpers({
cardActivitiesList() {
const data = Template.currentData();
const card = data?.card;
const activities = card?.activities?.();
return activities || [];
},
});
+5
View File
@@ -1,4 +1,9 @@
import { ReactiveCache } from '/imports/reactiveCache';
import CardComments from '/models/cardComments';
import { UnsavedEdits } from '/client/lib/unsavedEdits';
import { EscapeActions } from '/client/lib/escapeActions';
import { Utils } from '/client/lib/utils';
import autosize from 'autosize';
const commentFormIsOpen = new ReactiveVar(false);
+2
View File
@@ -1,5 +1,7 @@
import { ReactiveCache } from '/imports/reactiveCache';
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
import Boards from '/models/boards';
import { Utils } from '/client/lib/utils';
Template.archivedBoards.onCreated(function () {
this.subscribe('archivedBoards');
+38 -24
View File
@@ -1,5 +1,4 @@
import { ReactiveCache } from '/imports/reactiveCache';
import '../gantt/gantt.js';
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
import { TAPi18n } from '/imports/i18n';
import dragscroll from '@wekanteam/dragscroll';
@@ -7,13 +6,16 @@ import { boardConverter } from '/client/lib/boardConverter';
import { formatDateByUserPreference } from '/imports/lib/dateUtils';
import Swimlanes from '/models/swimlanes';
import Lists from '/models/lists';
import TableVisibilityModeSettings from '/models/tableVisibilityModeSettings';
import { EscapeActions } from '/client/lib/escapeActions';
import { Utils } from '/client/lib/utils';
const subManager = new SubsManager();
// SubsManager removed for Meteor 3 migration
const { calculateIndex } = Utils;
const swimlaneWhileSortingHeight = 150;
// Global reference so child components (swimlanes, cards) can access boardBody instance
BoardBody = null;
let BoardBody = null;
Template.board.onCreated(function () {
this.isBoardReady = new ReactiveVar(false);
@@ -22,7 +24,7 @@ Template.board.onCreated(function () {
this._boardProcessed = false; // Track if board has been processed
this._lastProcessedBoardId = null; // Track last processed board ID
this.ensureDefaultSwimlane = (boardId) => {
this.ensureDefaultSwimlane = async (boardId) => {
// Only create swimlane once per board
if (this._swimlaneCreated.has(boardId)) {
return;
@@ -33,25 +35,15 @@ Template.board.onCreated(function () {
if (!board) return;
const swimlanes = board.swimlanes();
if (swimlanes.length === 0) {
// Check if any swimlane exists in the database to avoid race conditions
const existingSwimlanes = ReactiveCache.getSwimlanes({ boardId });
if (existingSwimlanes.length === 0) {
const swimlaneId = Swimlanes.insert({
title: 'Default',
boardId: boardId,
});
if (process.env.DEBUG === 'true') {
console.log(
`Created default swimlane ${swimlaneId} for board ${boardId}`,
);
}
const swimlaneId = await Meteor.callAsync('ensureDefaultSwimlane', boardId);
if (process.env.DEBUG === 'true' && swimlaneId) {
console.log(
`Ensured default swimlane ${swimlaneId} for board ${boardId}`,
);
}
this._swimlaneCreated.add(boardId);
} else {
this._swimlaneCreated.add(boardId);
}
this._swimlaneCreated.add(boardId);
} catch (error) {
console.error('Error creating default swimlane:', error);
}
@@ -81,7 +73,7 @@ Template.board.onCreated(function () {
const currentBoardId = Session.get('currentBoard');
if (!currentBoardId) return;
const handle = subManager.subscribe('board', currentBoardId, false);
const handle = Meteor.subscribe('board', currentBoardId, false);
// Use a separate autorun for subscription ready state to avoid reactive loops
this.subscriptionReadyAutorun = Tracker.autorun(() => {
@@ -1091,11 +1083,33 @@ Template.calendarView.helpers({
const createCardButton = modalElement.querySelector(
'#create-card-button',
);
createCardButton.addEventListener('click', function () {
createCardButton.addEventListener('click', async function () {
const myTitle = modalElement.querySelector('#card-title-input').value;
if (myTitle) {
const firstList = currentBoard.draggableLists()[0];
const firstSwimlane = currentBoard.swimlanes()[0];
let firstSwimlane = currentBoard.swimlanes()[0];
if (!firstSwimlane) {
const swimlaneId = await Swimlanes.insertAsync({
title: 'Default',
boardId: currentBoard._id,
});
firstSwimlane = ReactiveCache.getSwimlane(swimlaneId);
}
let firstList = currentBoard.draggableLists()[0];
if (!firstList && firstSwimlane) {
const defaultTitle = TAPi18n.__('default');
const listId = await Lists.insertAsync({
title: typeof defaultTitle === 'string' ? defaultTitle : 'Default',
boardId: currentBoard._id,
swimlaneId: firstSwimlane._id,
});
firstList = ReactiveCache.getList(listId);
}
if (!firstList || !firstSwimlane) {
return;
}
Meteor.call(
'createCardWithDueDate',
currentBoard._id,
+62 -78
View File
@@ -2,6 +2,14 @@ import { ReactiveCache } from '/imports/reactiveCache';
import { TAPi18n } from '/imports/i18n';
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
import dragscroll from '@wekanteam/dragscroll';
import getSlug from 'limax';
import Boards from '/models/boards';
import Swimlanes from '/models/swimlanes';
import TableVisibilityModeSettings from '/models/tableVisibilityModeSettings';
import { Filter } from '/client/lib/filter';
import { MultiSelection } from '/client/lib/multiSelection';
import { getSidebarInstance } from '/client/features/sidebar/service';
import { Utils } from '/client/lib/utils';
/*
const DOWNCLS = 'fa-sort-down';
@@ -11,6 +19,7 @@ const sortCardsBy = new ReactiveVar('');
Template.boardChangeTitlePopup.events({
async submit(event, templateInstance) {
event.preventDefault();
const newTitle = templateInstance
.$('.js-board-name')
.val()
@@ -27,7 +36,6 @@ Template.boardChangeTitlePopup.events({
}
Popup.back();
}
event.preventDefault();
},
});
@@ -94,30 +102,32 @@ Template.boardHeaderBar.events({
console.log('Hamburger menu clicked');
}
// Use the same approach as keyboard shortcuts
if (typeof Sidebar !== 'undefined' && Sidebar && typeof Sidebar.toggle === 'function') {
const sidebar = getSidebarInstance();
if (sidebar && typeof sidebar.toggle === 'function') {
if (process.env.DEBUG === 'true') {
console.log('Using Sidebar.toggle()');
}
Sidebar.toggle();
sidebar.toggle();
} else {
if (process.env.DEBUG === 'true') {
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') {
const blazeSidebar = Blaze._globalHelpers.Sidebar();
if (blazeSidebar && typeof blazeSidebar.toggle === 'function') {
if (process.env.DEBUG === 'true') {
console.log('Using Blaze helper Sidebar.toggle()');
}
sidebar.toggle();
blazeSidebar.toggle();
}
}
}
},
'click .js-open-filter-view'() {
if (Sidebar) {
Sidebar.setView('filter');
const sidebar = getSidebarInstance();
if (sidebar) {
sidebar.setView('filter');
} else {
console.warn('Sidebar not available for setView');
}
@@ -137,8 +147,9 @@ Template.boardHeaderBar.events({
*/
'click .js-filter-reset'(event) {
event.stopPropagation();
if (Sidebar) {
Sidebar.setView();
const sidebar = getSidebarInstance();
if (sidebar) {
sidebar.setView();
} else {
console.warn('Sidebar not available for setView');
}
@@ -148,8 +159,9 @@ Template.boardHeaderBar.events({
Session.set('sortBy', '');
},
'click .js-open-search-view'() {
if (Sidebar) {
Sidebar.setView('search');
const sidebar = getSidebarInstance();
if (sidebar) {
sidebar.setView('search');
} else {
console.warn('Sidebar not available for setView');
}
@@ -214,55 +226,26 @@ function createBoardHelpers() {
};
}
function createBoardSubmit(tpl, event) {
async function createBoardSubmit(tpl, event) {
event.preventDefault();
const title = tpl.find('.js-new-board-title').value;
const slug = getSlug(title) || 'board';
const addTemplateContainer = $('#add-template-container.is-checked').length > 0;
if (addTemplateContainer) {
//const templateContainerId = Meteor.call('setCreateTemplateContainer');
//Utils.goBoardId(templateContainerId);
//alert('niinku template ' + Meteor.call('setCreateTemplateContainer'));
tpl.boardId.set(
Boards.insert({
// title: TAPi18n.__('templates'),
title: title,
permission: 'private',
type: 'template-container',
migrationVersion: 1, // Latest version - no migration needed
}),
);
// Insert the card templates swimlane
Swimlanes.insert({
// title: TAPi18n.__('card-templates-swimlane'),
title: 'Card Templates',
boardId: tpl.boardId.get(),
sort: 1,
await Meteor.callAsync('createBoardWithInitialSwimlanes', {
title,
slug,
permission: 'private',
type: 'template-container',
migrationVersion: 1,
swimlanes: [
{ title: 'Card Templates', sort: 1, type: 'template-container' },
{ title: 'List Templates', sort: 2, type: 'template-container' },
{ title: 'Board Templates', sort: 3, type: 'template-container' },
],
}),
// Insert the list templates swimlane
Swimlanes.insert(
{
// title: TAPi18n.__('list-templates-swimlane'),
title: 'List Templates',
boardId: tpl.boardId.get(),
sort: 2,
type: 'template-container',
},
);
// Insert the board templates swimlane
Swimlanes.insert(
{
//title: TAPi18n.__('board-templates-swimlane'),
title: 'Board Templates',
boardId: tpl.boardId.get(),
sort: 3,
type: 'template-container',
},
);
// Assign to space if one was selected
@@ -274,24 +257,21 @@ function createBoardSubmit(tpl, event) {
Session.set('createBoardInWorkspace', null); // Clear after use
}
Utils.goBoardId(tpl.boardId.get());
FlowRouter.go('board', { id: tpl.boardId.get(), slug });
} else {
const visibility = tpl.visibility.get();
tpl.boardId.set(
Boards.insert({
await Meteor.callAsync('createBoardWithInitialSwimlanes', {
title,
slug,
permission: visibility,
migrationVersion: 1, // Latest version - no migration needed
migrationVersion: 1,
swimlanes: [{ title: 'Default' }],
}),
);
Swimlanes.insert({
title: 'Default',
boardId: tpl.boardId.get(),
});
// Assign to space if one was selected
const spaceId = Session.get('createBoardInWorkspace');
if (spaceId) {
@@ -301,7 +281,7 @@ function createBoardSubmit(tpl, event) {
Session.set('createBoardInWorkspace', null); // Clear after use
}
Utils.goBoardId(tpl.boardId.get());
FlowRouter.go('board', { id: tpl.boardId.get(), slug });
}
}
@@ -315,8 +295,8 @@ function createBoardEvents() {
tpl.visibilityMenuIsOpen.set(!tpl.visibilityMenuIsOpen.get());
},
'click .js-import': Popup.open('boardImportBoard'),
'submit'(event, tpl) {
createBoardSubmit(tpl, event);
async 'submit'(event, tpl) {
await createBoardSubmit(tpl, event);
},
'click .js-import-board': Popup.open('chooseBoardSource'),
'click .js-board-template': Popup.open('searchElement'),
@@ -375,7 +355,7 @@ Template.headerBarCreateBoardPopup.events({
},
'click .js-import': Popup.open('boardImportBoard'),
async submit(event, tpl) {
createBoardSubmit(tpl, event);
await createBoardSubmit(tpl, event);
// Immediately star boards created with the headerbar popup.
await ReactiveCache.getCurrentUser().toggleBoardStar(tpl.boardId.get());
},
@@ -403,9 +383,11 @@ Template.boardChangeVisibilityPopup.helpers({
Template.boardChangeVisibilityPopup.events({
'click .js-select-visibility'() {
const currentBoard = Utils.getCurrentBoard();
const visibility = String(this);
currentBoard.setVisibility(visibility);
Popup.back();
const visibility = Template.currentData();
if (typeof visibility === 'string') {
currentBoard.setVisibility(visibility);
Popup.back();
}
},
});
@@ -423,16 +405,18 @@ Template.boardChangeWatchPopup.helpers({
Template.boardChangeWatchPopup.events({
'click .js-select-watch'() {
const level = String(this);
Meteor.call(
'watch',
'board',
Session.get('currentBoard'),
level,
(err, ret) => {
if (!err && ret) Popup.back();
},
);
const level = Template.currentData();
if (typeof level === 'string') {
Meteor.call(
'watch',
'board',
Session.get('currentBoard'),
level,
(err, ret) => {
if (!err && ret) Popup.back();
},
);
}
},
});
+1 -1
View File
@@ -1,4 +1,4 @@
@import url("../../../css/reset.css") print, screen;
@import url("../../../public/css/reset.css") print, screen;
/* Board List Header with Zoom Controls */
.board-list-header {
+3 -3
View File
@@ -130,7 +130,7 @@ template(name="boardList")
if BoardMultiSelection.isActive
.materialCheckBox.multi-selection-checkbox.js-toggle-board-multi-selection(
class="{{#if BoardMultiSelection.isSelected _id}}is-checked{{/if}}")
span.board-handle(title="{{_ 'drag-board'}}")
span.board-handle(title="{{boardWorkspaceDragHint}}")
span.emoji-icon
i.fa.fa-arrows
@@ -158,13 +158,13 @@ template(name="boardList")
if BoardMultiSelection.isActive
.materialCheckBox.multi-selection-checkbox.js-toggle-board-multi-selection(
class="{{#if BoardMultiSelection.isSelected _id}}is-checked{{/if}}")
span.board-handle(title="{{_ 'drag-board'}}")
span.board-handle(title="{{boardWorkspaceDragHint}}")
span.emoji-icon
i.fa.fa-arrows
a.js-open-board(href="{{pathFor 'board' id=_id slug=slug}}")
span.details
span.board-list-item-name(title="{{_ 'board-drag-drop-reorder-or-click-open'}}")
span.board-list-item-name(title="{{boardOpenAndMoveHint}}")
+viewer
= title
unless currentSetting.hideBoardMemberList
+26 -2
View File
@@ -2,8 +2,12 @@ import { ReactiveCache } from '/imports/reactiveCache';
import { TAPi18n } from '/imports/i18n';
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
import getSlug from 'limax';
import TableVisibilityModeSettings from '/models/tableVisibilityModeSettings';
import { BoardMultiSelection } from '/client/lib/boardMultiSelection';
import { EscapeActions } from '/client/lib/escapeActions';
import { Utils } from '/client/lib/utils';
const subManager = new SubsManager();
// SubsManager removed for Meteor 3 migration
const DEFAULT_WORKSPACE_ICON = '📁';
@@ -535,6 +539,26 @@ Template.boardList.helpers({
hasBoardsSelected() {
return BoardMultiSelection.count() > 0;
},
boardWorkspaceDragHint() {
const drag = TAPi18n.__('drag-board') || 'Drag board';
const remaining = TAPi18n.__('allboards.remaining') || 'Remaining';
const workspaces = TAPi18n.__('allboards.workspaces') || 'Workspaces';
return `${drag}: ${remaining} <-> ${workspaces}`;
},
boardOpenAndMoveHint() {
const remaining = TAPi18n.__('allboards.remaining') || 'Remaining';
const workspaces = TAPi18n.__('allboards.workspaces') || 'Workspaces';
return (
TAPi18n.__(
'board-open-and-move-between-remaining-and-workspaces',
{
remaining,
workspaces,
},
) ||
`Click board icon to open board. Drag board between ${remaining} and ${workspaces}.`
);
},
});
Template.workspaceTree.helpers({
@@ -657,7 +681,7 @@ Template.boardList.events({
console.error(err);
} else {
Session.set('fromBoard', null);
subManager.subscribe('board', res, false);
Meteor.subscribe('board', res, false);
FlowRouter.go('board', {
id: res,
slug: title,
+4
View File
@@ -86,6 +86,10 @@ template(name="attachmentGallery")
b
= name
span.file-size ({{fileSize size}})
if uploaderName
div.attachment-uploader {{uploaderName}}
if uploadedAt
div.attachment-uploaded-at {{uploadedAt}}
.attachment-actions
a.js-download(href="{{link}}?download=true", download="{{name}}", title="{{_ 'download'}}")
i.fa.fa-arrow-down
+37 -18
View File
@@ -1,12 +1,15 @@
import { ReactiveCache } from '/imports/reactiveCache';
import { ObjectID } from 'bson';
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';
import Attachments from '/models/attachments';
import { Utils } from '/client/lib/utils';
import { formatDateTime } from '/imports/lib/dateUtils';
const filesize = require('filesize');
const prettyMilliseconds = require('pretty-ms');
const { filesize } = require('filesize');
import prettyMilliseconds from 'pretty-ms';
// We store current card ID and the ID of currently opened attachment in a
// global var. This is used so that we know what's the next attachment to open
@@ -19,7 +22,7 @@ let touchStartCoords = null;
let touchEndCoords = null;
// Stores link to the attachment for which attachment actions popup was opened
attachmentActionsLink = null;
let attachmentActionsLink = null;
Template.attachmentGallery.events({
'click .open-preview'(event) {
@@ -40,8 +43,8 @@ Template.attachmentGallery.events({
attachmentActionsLink = event.currentTarget.getAttribute("data-attachment-link");
},
'click .js-rename': Popup.open('attachmentRename'),
'click .js-confirm-delete': Popup.afterConfirm('attachmentDelete', function() {
Attachments.remove(this._id);
'click .js-confirm-delete': Popup.afterConfirm('attachmentDelete', async function() {
await Attachments.removeAsync(this._id);
Popup.back();
}),
});
@@ -196,8 +199,6 @@ function processTouch(){
xDist = touchEndCoords.x - touchStartCoords.x;
yDist = touchEndCoords.y - touchStartCoords.y;
console.log("xDist: " + xDist);
// Left swipe
if (Math.abs(xDist) > Math.abs(yDist) && xDist < 0) {
openNextAttachment();
@@ -217,14 +218,12 @@ function processTouch(){
Template.attachmentViewer.events({
'touchstart #viewer-container'(event) {
console.log("touchstart")
touchStartCoords = {
x: event.changedTouches[0].screenX,
y: event.changedTouches[0].screenY
}
},
'touchend #viewer-container'(event) {
console.log("touchend")
touchEndCoords = {
x: event.changedTouches[0].screenX,
y: event.changedTouches[0].screenY
@@ -273,6 +272,26 @@ Template.attachmentGallery.helpers({
sanitize(value) {
return sanitizeHTML(value);
},
uploaderName() {
const uploaderId = this.userId;
if (!uploaderId) return '';
const uploader = ReactiveCache.getUser(uploaderId);
if (!uploader) return '';
return uploader.profile && uploader.profile.fullname
? uploader.profile.fullname
: uploader.username || '';
},
uploadedAt() {
if (this.uploadedAtOstrio) {
return formatDateTime(this.uploadedAtOstrio);
}
// Fall back to ObjectId timestamp (first 4 bytes = Unix seconds)
try {
const ts = parseInt(this._id.substring(0, 8), 16) * 1000;
if (!isNaN(ts)) return formatDateTime(new Date(ts));
} catch (_) {}
return '';
},
});
Template.cardAttachmentsPopup.onCreated(function() {
@@ -294,12 +313,12 @@ Template.cardAttachmentsPopup.helpers({
});
Template.cardAttachmentsPopup.events({
'change .js-attach-file'(event, templateInstance) {
async 'change .js-attach-file'(event, templateInstance) {
const card = this;
const files = event.currentTarget.files;
if (files) {
let uploads = [];
const uploaders = handleFileUpload(card, files);
const uploaders = await handleFileUpload(card, files);
uploaders.forEach(uploader => {
uploader.on('start', function() {
@@ -328,7 +347,7 @@ const COMPRESS_RATIO = Utils.IMAGE_COMPRESS_RATIO;
let pastedResults = null;
// Shared upload logic for drag-and-drop functionality
export function handleFileUpload(card, files) {
export async function handleFileUpload(card, files) {
if (!files || files.length === 0) {
return [];
}
@@ -361,7 +380,7 @@ export function handleFileUpload(card, files) {
continue;
}
const fileId = new ObjectID().toString();
const fileId = new ObjectId().toString();
let fileName = sanitizeText(file.name);
// If sanitized filename is not same as original filename,
@@ -385,7 +404,7 @@ export function handleFileUpload(card, files) {
config.meta.fileId = fileId;
try {
const uploader = Attachments.insert(
const uploader = await Attachments.insertAsync(
config,
false,
);
@@ -460,12 +479,12 @@ Template.previewClipboardImagePopup.onRendered(() => {
});
Template.previewClipboardImagePopup.events({
'click .js-upload-pasted-image'() {
async 'click .js-upload-pasted-image'() {
const card = this;
if (pastedResults && pastedResults.file) {
const file = pastedResults.file;
window.oPasted = pastedResults;
const fileId = new ObjectID().toString();
const fileId = new ObjectId().toString();
const config = {
file,
fileId: fileId,
@@ -474,7 +493,7 @@ Template.previewClipboardImagePopup.events({
chunkSize: 'dynamic',
};
config.meta.fileId = fileId;
const uploader = Attachments.insert(
const uploader = await Attachments.insertAsync(
config,
false,
);
+6 -1
View File
@@ -29,6 +29,8 @@ import {
} from '/imports/lib/dateUtils';
import { CustomFieldStringTemplate } from '/client/lib/customFields'
import { getCurrentCardFromContext } from '/client/lib/currentCard';
import { EscapeActions } from '/client/lib/escapeActions';
import { getSidebarInstance } from '/client/features/sidebar/service';
Template.cardCustomFieldsPopup.helpers({
hasCustomField() {
@@ -49,7 +51,10 @@ Template.cardCustomFieldsPopup.events({
},
'click .js-settings'(event) {
EscapeActions.executeUpTo('detailsPane');
Sidebar.setView('customFields');
const sidebar = getSidebarInstance();
if (sidebar) {
sidebar.setView('customFields');
}
event.preventDefault();
},
});
+4 -4
View File
@@ -48,7 +48,7 @@ Template.editCardReceivedDatePopup.helpers(datePickerHelpers());
Template.editCardReceivedDatePopup.events(datePickerEvents({
storeDate(date) {
this.datePicker.card.setReceived(formatDateTime(date));
this.datePicker.card.setReceived(date);
},
deleteDate() {
this.datePicker.card.unsetReceived();
@@ -72,7 +72,7 @@ Template.editCardStartDatePopup.helpers(datePickerHelpers());
Template.editCardStartDatePopup.events(datePickerEvents({
storeDate(date) {
this.datePicker.card.setStart(formatDateTime(date));
this.datePicker.card.setStart(date);
},
deleteDate() {
this.datePicker.card.unsetStart();
@@ -96,7 +96,7 @@ Template.editCardDueDatePopup.helpers(datePickerHelpers());
Template.editCardDueDatePopup.events(datePickerEvents({
storeDate(date) {
this.datePicker.card.setDue(formatDateTime(date));
this.datePicker.card.setDue(date);
},
deleteDate() {
this.datePicker.card.unsetDue();
@@ -120,7 +120,7 @@ Template.editCardEndDatePopup.helpers(datePickerHelpers());
Template.editCardEndDatePopup.events(datePickerEvents({
storeDate(date) {
this.datePicker.card.setEnd(formatDateTime(date));
this.datePicker.card.setEnd(date);
},
deleteDate() {
this.datePicker.card.unsetEnd();
+23 -24
View File
@@ -11,31 +11,30 @@ template(name="cardDetails")
+editCardTitleForm
else
unless isMiniScreen
unless isPopup
span.card-collapse-toggle.js-card-collapse-toggle(title="{{_ 'collapse-card'}}")
if cardCollapsed
i.fa.fa-caret-right
else
i.fa.fa-caret-down
a.close-card-details.js-close-card-details(title="{{_ 'close-card'}}")
i.fa.fa-times-thin
if cardMaximized
a.fa.fa-window-minimize.minimize-card-details.js-minimize-card-details(title="{{_ 'minimize-card'}}")
span.card-collapse-toggle.js-card-collapse-toggle(title="{{_ 'collapse-card'}}")
if cardCollapsed
i.fa.fa-caret-right
else
a.fa.fa-window-maximize.maximize-card-details.js-maximize-card-details(title="{{_ 'maximize-card'}}")
a.card-details-menu.js-open-card-details-menu(title="{{_ 'cardDetailsActionsPopup-title'}}")
i.fa.fa-bars
a.card-copy-button.js-copy-link(
id="cardURL_copy"
title="{{_ 'copy-card-link-to-clipboard'}}"
href="{{ originRelativeUrl }}"
)
span.emoji-icon
i.fa.fa-link
if canModifyCard
span.card-drag-handle.js-card-drag-handle(title="Drag card")
i.fa.fa-arrows
span.copied-tooltip {{_ 'copied'}}
i.fa.fa-caret-down
a.close-card-details.js-close-card-details(title="{{_ 'close-card'}}")
i.fa.fa-times-thin
if cardMaximized
a.fa.fa-window-minimize.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.card-details-menu.js-open-card-details-menu(title="{{_ 'cardDetailsActionsPopup-title'}}")
i.fa.fa-bars
a.card-copy-button.js-copy-link(
id="cardURL_copy"
title="{{_ 'copy-card-link-to-clipboard'}}"
href="{{ originRelativeUrl }}"
)
span.emoji-icon
i.fa.fa-link
if canModifyCard
span.card-drag-handle.js-card-drag-handle(title="Drag card")
i.fa.fa-arrows
span.copied-tooltip {{_ 'copied'}}
else
a.close-card-details.js-close-card-details(title="{{_ 'close-card'}}")
i.fa.fa-times-thin
+65 -30
View File
@@ -37,6 +37,7 @@ import CardComments from '/models/cardComments';
import { ALLOWED_COLORS } from '/config/const';
import { uniqBy } from '/imports/lib/collectionHelpers';
import { UserAvatar } from '../users/userAvatar';
import { Filter } from '/client/lib/filter';
import { BoardSwimlaneListCardDialog } from '/client/lib/dialogWithBoardSwimlaneListCard';
import { handleFileUpload } from './attachments';
import { InfiniteScrolling } from '/client/lib/infiniteScrolling';
@@ -45,8 +46,14 @@ import {
getCurrentCardFromContext,
} from '/client/lib/currentCard';
import uploadProgressManager from '../../lib/uploadProgressManager';
import { CSSEvents } from '/client/lib/cssEvents';
import { UnsavedEdits } from '/client/lib/unsavedEdits';
import { EscapeActions } from '/client/lib/escapeActions';
import { MultiSelection } from '/client/lib/multiSelection';
import { Utils } from '/client/lib/utils';
import autosize from 'autosize';
const subManager = new SubsManager();
// SubsManager removed for Meteor 3 migration
const { calculateIndexData } = Utils;
function getCardId() {
@@ -200,22 +207,22 @@ Template.cardDetails.onRendered(function () {
EscapeActions.executeUpTo('popup-close');
},
stop(evt, ui) {
let prevChecklist = ui.item.prev('.js-subtasks').get(0);
if (prevChecklist) {
prevChecklist = Blaze.getData(prevChecklist).subtask;
let prevSubtask = ui.item.prev('.js-subtasks').get(0);
if (prevSubtask) {
prevSubtask = Blaze.getData(prevSubtask).subtask;
}
let nextChecklist = ui.item.next('.js-subtasks').get(0);
if (nextChecklist) {
nextChecklist = Blaze.getData(nextChecklist).subtask;
let nextSubtask = ui.item.next('.js-subtasks').get(0);
if (nextSubtask) {
nextSubtask = Blaze.getData(nextSubtask).subtask;
}
const sortIndex = calculateIndexData(prevChecklist, nextChecklist, 1);
const sortIndex = calculateIndexData(prevSubtask, nextSubtask, 1);
$subtasksDom.sortable('cancel');
const subtask = Blaze.getData(ui.item.get(0)).subtask;
Subtasks.update(subtask._id, {
Cards.updateAsync(subtask._id, {
$set: {
subtaskSort: sortIndex.base,
sort: sortIndex.base,
},
});
},
@@ -265,8 +272,8 @@ Template.cardDetails.helpers({
},
showActivities() {
const user = ReactiveCache.getCurrentUser();
return user && user.hasShowActivities();
const card = Template.currentData();
return card && card.showActivities;
},
cardCollapsed() {
@@ -330,7 +337,12 @@ Template.cardDetails.helpers({
},
isCurrentListId(listId) {
const data = Template.currentData();
// When called from within 'each currentBoard.lists' block, we need the card from parent context
let data = Template.currentData();
if (!data || typeof data.listId === 'undefined') {
// Try parent context (for use within each loops)
data = Template.parentData(1);
}
if (!data || typeof data.listId === 'undefined') return false;
return data.listId == listId;
},
@@ -549,9 +561,8 @@ Template.cardDetails.events({
}
},
async 'change .js-select-card-details-lists'(event, tpl) {
const listId = event.target.value;
let card = Template.currentData();
const listSelect = tpl.$('.js-select-card-details-lists')[0];
const listId = listSelect.options[listSelect.selectedIndex].value;
const minOrder = await card.getMinSort(listId, card.swimlaneId);
await card.move(card.boardId, card.swimlaneId, listId, minOrder - 1);
@@ -595,6 +606,10 @@ Template.cardDetails.events({
const card = Template.currentData();
await card.toggleHideCheckedChecklistItems();
},
async 'change #toggleShowActivitiesCard'() {
const card = Template.currentData();
await card.toggleShowActivities();
},
'click #toggleCustomFieldsGridButton'() {
Meteor.call('toggleCustomFieldsGrid');
},
@@ -1089,14 +1104,21 @@ const filterMembers = (filterTerm) => {
let members = currBoard.activeMembers();
if (filterTerm) {
const searchTerm = filterTerm.toLowerCase();
members = members
.map(member => ({
member,
user: ReactiveCache.getUser(member.userId)
}))
.filter(({ user }) =>
(user.profile.fullname !== undefined && user.profile.fullname.toLowerCase().indexOf(filterTerm.toLowerCase()) !== -1)
|| user.profile.fullname === undefined && user.profile.username !== undefined && user.profile.username.toLowerCase().indexOf(filterTerm.toLowerCase()) !== -1)
.filter(({ user }) => {
// Check if user data is available
if (!user || !user.profile) {
return false;
}
const fullname = (user.profile.fullname || '').toLowerCase();
const username = (user.username || '').toLowerCase();
return fullname.indexOf(searchTerm) !== -1 || username.indexOf(searchTerm) !== -1;
})
.map(({ member }) => member);
}
return members;
@@ -1427,7 +1449,8 @@ Template.setCardColorPopup.helpers({
Template.setCardColorPopup.events({
'click .js-palette-color'(event, tpl) {
tpl.currentColor.set(Template.currentData().color);
const paletteData = Blaze.getData(event.currentTarget);
tpl.currentColor.set(paletteData?.color);
},
async 'click .js-submit'(event, tpl) {
event.preventDefault();
@@ -1446,7 +1469,9 @@ Template.setCardColorPopup.events({
});
Template.setSelectionColorPopup.onCreated(function () {
this.currentColor = new ReactiveVar(null);
const selectedCards = ReactiveCache.getCards(MultiSelection.getMongoSelector());
const uniqueColors = [...new Set(selectedCards.map(card => card.color || null))];
this.currentColor = new ReactiveVar(uniqueColors.length === 1 ? uniqueColors[0] : null);
});
Template.setSelectionColorPopup.helpers({
@@ -1467,22 +1492,32 @@ Template.setSelectionColorPopup.events({
const color = colorClass ? colorClass.replace('card-details-', '') : null;
tpl.currentColor.set(color);
},
async 'click .js-submit'(event, tpl) {
async 'submit form.edit-label'(event, tpl) {
event.preventDefault();
const color = tpl.currentColor.get();
// Use MultiSelection to get selected cards and set color on each
for (const card of ReactiveCache.getCards(MultiSelection.getMongoSelector())) {
await card.setColor(color);
try {
for (const card of ReactiveCache.getCards(MultiSelection.getMongoSelector())) {
await card.setColor(color);
}
Popup.back();
} catch (error) {
alert(error?.reason || error?.message || 'Failed to save selection color');
}
Popup.back();
},
async 'click .js-submit'(event, tpl) {
event.preventDefault();
await tpl.$('form.edit-label').trigger('submit');
},
async 'click .js-remove-color'(event, tpl) {
event.preventDefault();
// Use MultiSelection to get selected cards and remove color from each
for (const card of ReactiveCache.getCards(MultiSelection.getMongoSelector())) {
await card.setColor(null);
try {
for (const card of ReactiveCache.getCards(MultiSelection.getMongoSelector())) {
await card.setColor(null);
}
Popup.back();
} catch (error) {
alert(error?.reason || error?.message || 'Failed to unset selection color');
}
Popup.back();
},
});
@@ -1593,7 +1628,7 @@ Template.cardMorePopup.events({
if (selection === 'none') {
tpl.parentBoard.set(null);
} else {
subManager.subscribe('board', $(event.currentTarget).val(), false);
Meteor.subscribe('board', $(event.currentTarget).val(), false);
tpl.parentBoard.set(selection);
list.prop('disabled', false);
}
+7 -2
View File
@@ -2,9 +2,14 @@ import { ReactiveCache } from '/imports/reactiveCache';
import { TAPi18n } from '/imports/i18n';
import Cards from '/models/cards';
import Boards from '/models/boards';
import ChecklistItems from '/models/checklistItems';
import Checklists from '/models/checklists';
import { BoardSwimlaneListCardDialog } from '/client/lib/dialogWithBoardSwimlaneListCard';
import { EscapeActions } from '/client/lib/escapeActions';
import { Utils } from '/client/lib/utils';
import autosize from 'autosize';
const subManager = new SubsManager();
// SubsManager removed for Meteor 3 migration
const { calculateIndexData } = Utils;
function initSorting(items) {
@@ -218,7 +223,7 @@ Template.checklists.events({
event.preventDefault();
Template.currentData().card.toggleHideFinishedChecklist();
},
keydown(event) {
'keydown textarea.js-add-checklist-item'(event) {
//If user press enter key inside a form, submit it
//Unless the user is also holding down the 'shift' key
if (event.keyCode === 13 && !event.shiftKey) {
+4 -1
View File
@@ -1,8 +1,11 @@
import { ReactiveCache } from '/imports/reactiveCache';
import { LABEL_COLORS } from '/models/metadata/colors';
import { EscapeActions } from '/client/lib/escapeActions';
import { Utils } from '/client/lib/utils';
let labelColors;
Meteor.startup(() => {
labelColors = Boards.simpleSchema()._schema['labels.$.color'].allowedValues;
labelColors = LABEL_COLORS;
});
const getFallbackLabelColor = () => {
+33 -27
View File
@@ -11,17 +11,21 @@ template(name="minicard")
a.minicard-details-menu-with-handle.js-open-minicard-details-menu(title="{{_ 'cardDetailsActionsPopup-title'}}")
i.fa.fa-bars
.dates
if getReceived
.date
+minicardReceivedDate
if getStart
.date
+minicardStartDate
if getDue
.date
+minicardDueDate
if getEnd
+minicardEndDate
if showReceived
if getReceived
.date
+minicardReceivedDate
if showStart
if getStart
.date
+minicardStartDate
if showDue
if getDue
.date
+minicardDueDate
if showEnd
if getEnd
+minicardEndDate
if getSpentTime
.date
+cardSpentTime
@@ -68,19 +72,20 @@ template(name="minicard")
span.linked-icon.linked-archived
i.fa.fa-archive
+viewer
if currentBoard.allowsCardNumber
if showCardNumber
span.card-number
| ##{getCardNumber}
= getTitle
if labels
.minicard-labels(class="{{#if hiddenMinicardLabelText}}minicard-labels-no-text{{/if}}")
each labels
unless hiddenMinicardLabelText
span.js-card-label.card-label(class="card-label-{{color}}" title=name)
+viewer
= name
if hiddenMinicardLabelText
.minicard-label(class="card-label-{{color}}" title="{{name}}")
if showLabels
if labels
.minicard-labels(class="{{#if hiddenMinicardLabelText}}minicard-labels-no-text{{/if}}")
each labels
unless hiddenMinicardLabelText
span.js-card-label.card-label(class="card-label-{{color}}" title=name)
+viewer
= name
if hiddenMinicardLabelText
.minicard-label(class="card-label-{{color}}" title="{{name}}")
.minicard-custom-fields
each customFieldsWD
@@ -179,12 +184,13 @@ template(name="minicard")
span.badge-icon
i.fa.fa-paperclip
span.badge-text= attachments.length
if allSubtasks.count
.badge
span.badge-icon
i.fa.fa-globe
span.badge-text.check-list-text {{subtasksFinishedCount}}/{{allSubtasksCount}}
//{{subtasksFinishedCount}}/{{subtasksCount}} does not work because when a subtaks is archived, the count goes down
if showSubtasks
if allSubtasks.count
.badge
span.badge-icon
i.fa.fa-globe
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
+31 -12
View File
@@ -3,6 +3,7 @@ import { TAPi18n } from '/imports/i18n';
import { CustomFieldStringTemplate } from '/client/lib/customFields';
import { handleFileUpload } from './attachments';
import uploadProgressManager from '../../lib/uploadProgressManager';
import { Utils } from '/client/lib/utils';
// Template.cards.events({
// 'click .member': Popup.open('cardMember')
@@ -49,32 +50,50 @@ Template.minicard.helpers({
},
showMembers() {
// cache "board" to reduce the mini-mongodb access
const board = this.board();
let ret = false;
if (board) {
ret =
board.allowsMembers === null ||
board.allowsMembers === undefined ||
board.allowsMembers
;
ret = board.allowsMembersOnMinicard ?? false;
}
return ret;
},
showAssignee() {
// cache "board" to reduce the mini-mongodb access
const board = this.board();
let ret = false;
if (board) {
ret =
board.allowsAssignee === null ||
board.allowsAssignee === undefined ||
board.allowsAssignee
;
ret = board.allowsAssigneeOnMinicard ?? false;
}
return ret;
},
showReceived() {
const board = this.board();
return board ? board.allowsReceivedDateOnMinicard : false;
},
showStart() {
const board = this.board();
return board ? board.allowsStartDateOnMinicard : false;
},
showDue() {
const board = this.board();
return board ? board.allowsDueDateOnMinicard : false;
},
showEnd() {
const board = this.board();
return board ? board.allowsEndDateOnMinicard : false;
},
showLabels() {
const board = this.board();
return board ? board.allowsLabelsOnMinicard : false;
},
showCardNumber() {
const board = this.board();
return board ? board.allowsCardNumberOnMinicard : false;
},
showSubtasks() {
const board = this.board();
return board ? board.allowsSubtasksOnMinicard : false;
},
hiddenMinicardLabelText() {
const currentUser = ReactiveCache.getCurrentUser();
+45
View File
@@ -1,7 +1,52 @@
import { ReactiveCache } from '/imports/reactiveCache';
Template.resultCard.helpers({
userId() {
return Meteor.userId();
},
getBoard() {
const card = Template.currentData();
try {
if (card && card.boardId) {
const board = ReactiveCache.getBoard(card.boardId);
return board || null;
}
} catch (err) {
// Silently handle errors
}
return null;
},
getSwimlane() {
const card = Template.currentData();
try {
if (card && card.swimlaneId) {
const swimlane = ReactiveCache.getSwimlane(card.swimlaneId);
return swimlane || null;
}
} catch (err) {
// Silently handle errors
}
return null;
},
getList() {
const card = Template.currentData();
try {
if (card && card.listId) {
const list = ReactiveCache.getList(card.listId);
return list || null;
}
} catch (err) {
// Silently handle errors
}
return null;
},
originRelativeUrl() {
const card = Template.currentData();
if (card && card.boardId && card._id) {
return `/board/${card.boardId}?cardId=${card._id}`;
}
return '#';
},
});
Template.resultCard.events({
+15 -4
View File
@@ -15,7 +15,7 @@ textarea.js-edit-subtask-item {
color: #8c8c8c;
text-decoration: underline;
word-wrap: break-word;
float: right;
flex-shrink: 0;
padding-top: 6px;
}
.delete-text:hover,
@@ -24,8 +24,13 @@ textarea.js-edit-subtask-item {
.js-delete-subtask-item:hover {
color: inherit;
}
.subtask-title {
display: flex;
align-items: center;
width: 100%;
}
.subtask-title .checkbox {
float: left;
flex-shrink: 0;
width: 30px;
height: 30px;
font-size: 18px;
@@ -34,10 +39,12 @@ textarea.js-edit-subtask-item {
.subtask-title .title {
font-size: 18px;
line-height: 25px;
flex: 1;
order: 1;
}
.subtask-title .subtasks-stat {
margin: 0 0.5em;
float: right;
flex-shrink: 0;
padding-top: 6px;
}
.subtask-title .subtasks-stat.is-finished {
@@ -46,6 +53,9 @@ textarea.js-edit-subtask-item {
.subtask-title .js-delete-subtask {
margin: 0 0.5em;
}
.subtask-title .js-view-subtask {
margin: 0 0.5em;
}
.js-confirm-subtask-delete {
background-color: #f7f7f7;
position: absolute;
@@ -175,6 +185,7 @@ body.grey-icons-enabled .subtasks-item .check-box-unicode {
display: inline-block;
}
.subtask-details-menu {
float: right;
flex-shrink: 0;
padding: 6px 10px 6px 10px;
order: 2;
}
+28 -9
View File
@@ -1,8 +1,17 @@
import { ReactiveCache } from '/imports/reactiveCache';
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
import Cards from '/models/cards';
import { Filter } from '/client/lib/filter';
Template.subtasks.events({
'click .js-open-subtask-details-menu': Popup.open('subtaskActions'),
'click .js-open-subtask-details-menu'(event) {
// Close any existing popup first to avoid accumulating content
if (Popup.isOpen()) {
Popup.close();
}
// Now open the popup for this specific subtask
Popup.open('subtaskActions').call(this, event);
},
async 'submit .js-add-subtask'(event, tpl) {
event.preventDefault();
const textarea = tpl.find('textarea.js-add-subtask-item');
@@ -11,8 +20,16 @@ Template.subtasks.events({
const card = ReactiveCache.getCard(cardId);
const sortIndex = -1;
const crtBoard = ReactiveCache.getBoard(card.boardId);
const targetBoard = crtBoard.getDefaultSubtasksBoard();
const listId = targetBoard.getDefaultSubtasksListId();
const targetBoard = await crtBoard.getDefaultSubtasksBoardAsync();
if (!targetBoard) {
return;
}
const targetList = await targetBoard.getDefaultSubtasksListAsync();
if (!targetList) {
return;
}
const listId = targetList._id;
//Get the full swimlane data for the parent task.
const parentSwimlane = ReactiveCache.getSwimlane({
@@ -20,10 +37,12 @@ Template.subtasks.events({
_id: card.swimlaneId,
});
//find the swimlane of the same name in the target board.
const targetSwimlane = ReactiveCache.getSwimlane({
boardId: targetBoard._id,
title: parentSwimlane.title,
});
const targetSwimlane = parentSwimlane
? ReactiveCache.getSwimlane({
boardId: targetBoard._id,
title: parentSwimlane.title,
})
: undefined;
//If no swimlane with a matching title exists in the target board, fall back to the default swimlane.
const swimlaneId =
targetSwimlane === undefined
@@ -33,7 +52,7 @@ Template.subtasks.events({
const nextCardNumber = await targetBoard.getNextCardNumber();
if (title) {
const _id = Cards.insert({
const _id = await Cards.insertAsync({
title,
parentId: cardId,
members: [],
@@ -44,7 +63,7 @@ Template.subtasks.events({
sort: sortIndex,
swimlaneId,
type: 'cardType-card',
cardNumber: nextCardNumber
cardNumber: nextCardNumber,
});
// In case the filter is active we need to add the newly inserted card in
+2
View File
@@ -1,4 +1,5 @@
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
import Cards from '/models/cards';
// Add click handler to ganttView for card titles
Template.ganttView.events({
@@ -14,6 +15,7 @@ Template.ganttView.events({
},
});
import { Template } from 'meteor/templating';
import { Utils } from '/client/lib/utils';
// Blaze template helpers for ganttView
function getISOWeekInfo(d) {
+2
View File
@@ -4,6 +4,8 @@ import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
import { wekanGetMembersToMap } from './wekanMembersMapper';
import { csvGetMembersToMap } from './csvMembersMapper';
import getSlug from 'limax';
import { UserSearchIndex } from '/models/users';
import { Utils } from '/client/lib/utils';
const Papa = require('papaparse');
+1 -1
View File
@@ -1,4 +1,4 @@
const { ReactiveVar } = require('meteor/reactive-var');
import { ReactiveVar } from 'meteor/reactive-var';
// Creation-time stack tracking which basicTabs is currently being rendered.
// Blaze creates parent views before children, so when tabContent.onCreated fires,
+25 -25
View File
@@ -1226,52 +1226,52 @@ form.inlined-form .list-composer {
margin-top: unset !important;
}
.list-header-white {
border-bottom: 6px solid #fff;
border-bottom: 6px solid #fff !important;
}
.list-header-green {
border-bottom: 6px solid #3cb500;
border-bottom: 6px solid #3cb500 !important;
}
.list-header-yellow {
border-bottom: 6px solid #fad900;
border-bottom: 6px solid #fad900 !important;
}
.list-header-orange {
border-bottom: 6px solid #ff9f19;
border-bottom: 6px solid #ff9f19 !important;
}
.list-header-red {
border-bottom: 6px solid #eb4646;
border-bottom: 6px solid #eb4646 !important;
}
.list-header-purple {
border-bottom: 6px solid #a632db;
border-bottom: 6px solid #a632db !important;
}
.list-header-blue {
border-bottom: 6px solid #0079bf;
border-bottom: 6px solid #0079bf !important;
}
.list-header-pink {
border-bottom: 6px solid #ff78cb;
border-bottom: 6px solid #ff78cb !important;
}
.list-header-sky {
border-bottom: 6px solid #00c2e0;
border-bottom: 6px solid #00c2e0 !important;
}
.list-header-black {
border-bottom: 6px solid #4d4d4d;
border-bottom: 6px solid #4d4d4d !important;
}
.list-header-lime {
border-bottom: 6px solid #51e898;
border-bottom: 6px solid #51e898 !important;
}
.list-header-silver {
border-bottom: 6px solid #e4e4e4;
border-bottom: 6px solid #e4e4e4 !important;
}
.list-header-peachpuff {
border-bottom: 6px solid #ffdab9;
border-bottom: 6px solid #ffdab9 !important;
}
.list-header-crimson {
border-bottom: 6px solid #dc143c;
border-bottom: 6px solid #dc143c !important;
}
.list-header-plum {
border-bottom: 6px solid #dda0dd;
border-bottom: 6px solid #dda0dd !important;
}
.list-header-darkgreen {
border-bottom: 6px solid #006400;
border-bottom: 6px solid #006400 !important;
}
/* Final collapsed desktop overrides:
@@ -1379,31 +1379,31 @@ form.inlined-form .list-composer {
opacity: 1 !important;
}
.list-header-slateblue {
border-bottom: 6px solid #6a5acd;
border-bottom: 6px solid #6a5acd !important;
}
.list-header-magenta {
border-bottom: 6px solid #f0f;
border-bottom: 6px solid #f0f !important;
}
.list-header-gold {
border-bottom: 6px solid #ffd700;
border-bottom: 6px solid #ffd700 !important;
}
.list-header-navy {
border-bottom: 6px solid #000080;
border-bottom: 6px solid #000080 !important;
}
.list-header-gray {
border-bottom: 6px solid #808080;
border-bottom: 6px solid #808080 !important;
}
.list-header-saddlebrown {
border-bottom: 6px solid #8b4513;
border-bottom: 6px solid #8b4513 !important;
}
.list-header-paleturquoise {
border-bottom: 6px solid #afeeee;
border-bottom: 6px solid #afeeee !important;
}
.list-header-mistyrose {
border-bottom: 6px solid #ffe4e1;
border-bottom: 6px solid #ffe4e1 !important;
}
.list-header-indigo {
border-bottom: 6px solid #4b0082;
border-bottom: 6px solid #4b0082 !important;
}
.list.list-collapsed .collapsed-list-drag-area {
+3
View File
@@ -1,5 +1,8 @@
import { ReactiveCache } from '/imports/reactiveCache';
import { TAPi18n } from '/imports/i18n';
import { EscapeActions } from '/client/lib/escapeActions';
import { MultiSelection } from '/client/lib/multiSelection';
import { Utils } from '/client/lib/utils';
require('/client/lib/jquery-ui.js')
const { calculateIndex } = Utils;
+37 -14
View File
@@ -3,8 +3,15 @@ import { TAPi18n } from '/imports/i18n';
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
import { getSpinnerName, getSpinnerTemplate } from '/client/lib/spinner';
import getSlug from 'limax';
import Boards from '/models/boards';
import Cards from '/models/cards';
import Swimlanes from '/models/swimlanes';
import { Filter } from '/client/lib/filter';
import { MultiSelection } from '/client/lib/multiSelection';
import { Utils } from '/client/lib/utils';
import autosize from 'autosize';
const subManager = new SubsManager();
// SubsManager removed for Meteor 3 migration
const InfiniteScrollIter = 10;
Template.listBody.onCreated(function () {
@@ -59,7 +66,10 @@ Template.listBody.onCreated(function () {
};
this.idOrNull = (swimlaneId) => {
const data = Template.currentData();
const data = this.data;
if (!data) {
return undefined;
}
if (
Utils.boardView() === 'board-view-swimlanes' ||
data.board().isTemplatesBoard()
@@ -88,7 +98,10 @@ Template.listBody.onCreated(function () {
const labelIds = formComponent.labels.get();
const customFields = formComponent.customFields.get();
const data = Template.currentData();
const data = this.data;
if (!data) {
return;
}
const board = data.board();
let linkedId = '';
let swimlaneId = '';
@@ -107,13 +120,15 @@ Template.listBody.onCreated(function () {
if (swimlane.isCardTemplatesSwimlane()) cardType = 'template-card';
// If this is the board templates swimlane, insert a board template and a linked card
else if (swimlane.isBoardTemplatesSwimlane()) {
linkedId = Boards.insert({
linkedId = await Boards.insertAsync({
title,
slug: getSlug(title) || 'board',
permission: 'private',
type: 'template-board',
});
Swimlanes.insert({
title: TAPi18n.__('default'),
const defaultTitle = TAPi18n.__('default');
await Swimlanes.insertAsync({
title: typeof defaultTitle === 'string' ? defaultTitle : 'Default',
boardId: linkedId,
});
cardType = 'cardType-linkedBoard';
@@ -294,7 +309,15 @@ Template.listBody.helpers({
listId: Template.currentData()._id,
archived: false,
};
if (swimlaneId) selector.swimlaneId = swimlaneId;
if (swimlaneId) {
// Fallback: also show cards with no swimlaneId (null/empty) so that
// pre-migration / orphaned cards are always visible without a DB migration.
selector.$or = [
{ swimlaneId },
{ swimlaneId: null }, // null covers null AND missing field
{ swimlaneId: '' }, // empty string from shared-lists era
];
}
const ret = ReactiveCache.getCards(Filter.mongoSelector(selector), {
// sort: ['sort'],
sort: sortBy,
@@ -554,7 +577,7 @@ Template.linkCardPopup.onCreated(function () {
this.boardId = Session.get('currentBoard');
// In order to get current board info
subManager.subscribe('board', this.boardId, false);
Meteor.subscribe('board', this.boardId, false);
this.board = ReactiveCache.getBoard(this.boardId);
// List where to insert card
this.list = $(Popup._getTopStack().openerElement).closest('.js-list');
@@ -685,7 +708,7 @@ Template.linkCardPopup.helpers({
Template.linkCardPopup.events({
'change .js-select-boards'(evt, tpl) {
const val = $(evt.currentTarget).val();
subManager.subscribe('board', val, false);
Meteor.subscribe('board', val, false);
// Clear selections to allow linking only board or re-choose swimlane/list
tpl.selectedSwimlaneId.set('');
tpl.selectedListId.set('');
@@ -773,7 +796,7 @@ Template.searchElementPopup.onCreated(function () {
if (this.isTemplateSearch) {
const boardId = (ReactiveCache.getCurrentUser().profile || {}).templatesBoardId;
if (boardId) {
subManager.subscribe('board', boardId, false);
Meteor.subscribe('board', boardId, false);
this.board = ReactiveCache.getBoard(boardId);
}
} else {
@@ -785,7 +808,7 @@ Template.searchElementPopup.onCreated(function () {
}
this.boardId = this.board._id;
// Subscribe to this board
subManager.subscribe('board', this.boardId, false);
Meteor.subscribe('board', this.boardId, false);
this.selectedBoardId = new ReactiveVar(this.boardId);
this.list = $(Popup._getTopStack().openerElement).closest('.js-list');
@@ -849,7 +872,7 @@ Template.searchElementPopup.helpers({
} else if (tpl.isBoardTemplateSearch) {
const boards = board.searchBoards(tpl.term.get());
boards.forEach(board => {
subManager.subscribe('board', board.linkedId, false);
Meteor.subscribe('board', board.linkedId, false);
});
return boards;
} else {
@@ -860,7 +883,7 @@ Template.searchElementPopup.helpers({
Template.searchElementPopup.events({
'change .js-select-boards'(evt, tpl) {
subManager.subscribe('board', $(evt.currentTarget).val(), false);
Meteor.subscribe('board', $(evt.currentTarget).val(), false);
tpl.selectedBoardId.set($(evt.currentTarget).val());
},
'submit .js-search-term-form'(evt, tpl) {
@@ -915,7 +938,7 @@ Template.searchElementPopup.events({
},
(err, data) => {
_id = data;
subManager.subscribe('board', _id, false);
Meteor.subscribe('board', _id, false);
FlowRouter.go('board', {
id: _id,
slug: getSlug(element.title),
+33 -4
View File
@@ -3,10 +3,14 @@ import Lists from '../../../models/lists';
import { TAPi18n } from '/imports/i18n';
import dragscroll from '@wekanteam/dragscroll';
import { BoardSwimlaneListDialog } from '/client/lib/dialogWithBoardSwimlaneList';
import Cards from '/models/cards';
import { LIST_COLORS } from '/models/metadata/colors';
import { MultiSelection } from '/client/lib/multiSelection';
import { Utils } from '/client/lib/utils';
let listsColors;
Meteor.startup(() => {
listsColors = Lists.simpleSchema()._schema.color.allowedValues;
listsColors = LIST_COLORS;
});
Template.listHeader.helpers({
@@ -439,14 +443,39 @@ Template.setListColorPopup.helpers({
Template.setListColorPopup.events({
'click .js-palette-color'(event, tpl) {
tpl.currentColor.set(Template.currentData().color);
const paletteData = Blaze.getData(event.currentTarget);
tpl.currentColor.set(paletteData?.color);
},
async 'submit form'(event, tpl) {
event.preventDefault();
console.log('[ListColor] submit form, color:', tpl.currentColor.get(), 'listId:', tpl.currentList?._id, 'setColor type:', typeof tpl.currentList?.setColor);
try {
const result = await tpl.currentList.setColor(tpl.currentColor.get());
console.log('[ListColor] submit form setColor result:', result);
} catch (err) {
console.error('[ListColor] submit form setColor error:', err);
}
Popup.close();
},
async 'click .js-submit'(event, tpl) {
await tpl.currentList.setColor(tpl.currentColor.get());
event.preventDefault();
console.log('[ListColor] click submit, color:', tpl.currentColor.get(), 'listId:', tpl.currentList?._id, 'setColor type:', typeof tpl.currentList?.setColor);
try {
const result = await tpl.currentList.setColor(tpl.currentColor.get());
console.log('[ListColor] click submit setColor result:', result);
} catch (err) {
console.error('[ListColor] click submit setColor error:', err);
}
Popup.close();
},
async 'click .js-remove-color'(event, tpl) {
await tpl.currentList.setColor(null);
event.preventDefault();
console.log('[ListColor] remove color, listId:', tpl.currentList?._id);
try {
await tpl.currentList.setColor(null);
} catch (err) {
console.error('[ListColor] remove color error:', err);
}
Popup.close();
},
});
+1
View File
@@ -1,5 +1,6 @@
import { ReactiveCache } from '/imports/reactiveCache';
import { TAPi18n } from '/imports/i18n';
import AccessibilitySettings from '/models/accessibilitySettings';
// Shared helpers for both accessibility templates
const accessibilityHelpers = {
+2
View File
@@ -1,3 +1,5 @@
import Boards from '/models/boards';
Template.bookmarks.helpers({
hasStarredBoards() {
const user = ReactiveCache.getCurrentUser();
+1
View File
@@ -1,5 +1,6 @@
import { ReactiveCache } from '/imports/reactiveCache';
import { TAPi18n } from '/imports/i18n';
import { Utils } from '/client/lib/utils';
// const subManager = new SubsManager();
+6 -3
View File
@@ -1,6 +1,9 @@
import { ReactiveCache } from '/imports/reactiveCache';
import { findWhere } from '/imports/lib/collectionHelpers';
import { TAPi18n } from '/imports/i18n';
import Attachments from '/models/attachments';
import { Utils } from '/client/lib/utils';
import autosize from 'autosize';
var converter = require('@wekanteam/html-to-markdown');
const specialHandles = [
@@ -207,11 +210,11 @@ Template.editor.onRendered(function () {
const currentCard = Utils.getCurrentCard();
const MAX_IMAGE_PIXEL = Utils.MAX_IMAGE_PIXEL;
const COMPRESS_RATIO = Utils.IMAGE_COMPRESS_RATIO;
const processUpload = function(file) {
const uploader = Attachments.insert(
const processUpload = async function(file) {
const uploader = await Attachments.insertAsync(
{
file,
meta: Utils.getCommonAttachmentMetaFrom(card),
meta: Utils.getCommonAttachmentMetaFrom(currentCard),
chunkSize: 'dynamic',
},
false,
+15
View File
@@ -84,6 +84,21 @@
margin-right: auto;
margin-left: auto;
}
.js-new-search {
cursor: pointer;
transition: background-color 0.2s;
}
.board-color-belize.js-new-search {
background-color: #216694 !important;
color: #fff !important;
border: none !important;
}
.board-color-belize.js-new-search:hover {
background-color: #1a4d6a !important;
}
.board-color-belize.js-new-search:active {
background-color: #0d334d !important;
}
.global-search-operator {
font-family: Courier;
}
+19 -40
View File
@@ -11,68 +11,47 @@ template(name="globalSearchModalTitle")
| {{_ 'globalSearch-title'}}
template(name="resultsPaged")
if resultsHeading.get
h1
= resultsHeading.get
a(title="{{_ 'link-to-search' }}" href="{{ getSearchHref }}")
| 🔗
each card in results.get
if resultsHeading
h1= resultsHeading
each card in results
+resultCard(card)
table.global-search-footer
tr
td.global-search-previous-page
if hasPreviousPage.get
if hasPreviousPage
button.js-previous-page
| {{_ 'previous-page' }}
td.global-search-next-page(align="right")
if hasNextPage.get
if hasNextPage
button.js-next-page
| {{_ 'next-page' }}
template(name="globalSearch")
if currentUser
.wrapper
form.global-search-page.js-search-query-form
.wrapper(class=boardColorClass)
form.global-search-page.js-search-query-form(onsubmit="return false;")
input.global-search-query-input(
style="{# if hasResults.get #}display: inline-block;{#/if#}"
style="display: inline-block;"
id="global-search-input"
type="text"
name="searchQuery"
placeholder="{{_ 'search-example'}}"
value="{{ query.get }}"
value="{{ queryValue }}"
autofocus dir="auto"
)
a.js-new-search
button.js-new-search.board-color-belize(type="button" style="background: #216694 !important; background-color: #216694 !important; color: #fff !important; border: none !important;")
| 🧹
if debug.get.show
h1 Debug
if debug.get.showSelector
h2 Selector
button.js-copy-debug-selector
= 'Copy'
pre(
id="debug-selector"
)
= sessionData.selector
if debug.get.showProjection
h2 Projection
button.js-copy-debug-projection
= 'Copy'
pre(
id="debug-projection"
)
= sessionData.projection
if searching.get
if searching
+spinner
else if hasResults.get
else if hasResults
.global-search-results-list-wrapper
if hasQueryErrors.get
if hasQueryErrors
ul
each msg in errorMessages
li.global-search-error-messages
= msg
+resultsPaged(this)
else if serverError.get
+resultsPaged getSearchData
else if serverError
.global-search-page
.global-search-help
h1 {{_ 'server-error' }}
@@ -83,22 +62,22 @@ template(name="globalSearch")
.global-search-help
h2 {{_ 'boards' }}
.lists-wrapper
each title in myBoardNames.get
each title in myBoardNames
span.card-label.list-title.js-board-title
= title
h2 {{_ 'lists' }}
.lists-wrapper
each title in myLists.get
each title in myLists
span.card-label.list-title.js-list-title
= title
h2 {{_ 'label-colors' }}
.palette-colors: each label in labelColors
span.card-label.palette-color.js-label-color(class="card-label-{{label.color}}")
= label.name
if myLabelNames.get.length
if myLabelNames
h2 {{_ 'label-names' }}
.lists-wrapper
each name in myLabelNames.get
each name in myLabelNames
span.card-label.list-title.js-label-name
= name
.global-search-instructions
+191 -160
View File
@@ -1,7 +1,8 @@
import { TAPi18n } from '/imports/i18n';
import { CardSearchPaged } from '../../lib/cardSearch';
import Boards from '../../../models/boards';
import { LABEL_COLORS } from '/models/metadata/colors';
import { Query, QueryErrors } from '../../../config/query-classes';
import { ReactiveCache } from '/imports/reactiveCache';
// const subManager = new SubsManager();
@@ -98,36 +99,45 @@ Template.globalSearch.helpers({
return Meteor.userId();
},
// Return ReactiveVars so jade can use .get pattern
// Return ReactiveVar values so Blaze can use them
searching() {
return Template.instance().search.searching;
const val = Template.instance().search.searching.get();
return val;
},
hasResults() {
return Template.instance().search.hasResults;
const val = Template.instance().search.hasResults.get();
return val;
},
hasQueryErrors() {
return Template.instance().search.hasQueryErrors;
const val = Template.instance().search.hasQueryErrors.get();
return val;
},
serverError() {
return Template.instance().search.serverError;
const val = Template.instance().search.serverError.get();
return val;
},
query() {
return Template.instance().search.query;
},
debug() {
return Template.instance().search.debug;
const val = Template.instance().search.debug.get();
return val;
},
resultsHeading() {
return Template.instance().search.resultsHeading;
const rv = Template.instance().search.resultsHeading;
return rv ? rv.get() : '';
},
results() {
return Template.instance().search.results;
const rv = Template.instance().search.results;
return rv ? rv.get() : [];
},
hasNextPage() {
return Template.instance().search.hasNextPage;
const rv = Template.instance().search.hasNextPage;
return rv ? rv.get() : false;
},
hasPreviousPage() {
return Template.instance().search.hasPreviousPage;
const rv = Template.instance().search.hasPreviousPage;
return rv ? rv.get() : false;
},
sessionData() {
return Template.instance().search.sessionData;
@@ -137,124 +147,170 @@ Template.globalSearch.helpers({
},
myLists() {
return Template.instance().myLists;
const rv = Template.instance().myLists;
return rv ? rv.get() : [];
},
myLabelNames() {
return Template.instance().myLabelNames;
const rv = Template.instance().myLabelNames;
return rv ? rv.get() : [];
},
myBoardNames() {
return Template.instance().myBoardNames;
const rv = Template.instance().myBoardNames;
return rv ? rv.get() : [];
},
errorMessages() {
return errorMessages(Template.instance());
},
searchInstructions() {
const tags = {
operator_board: TAPi18n.__('operator-board'),
operator_list: TAPi18n.__('operator-list'),
operator_swimlane: TAPi18n.__('operator-swimlane'),
operator_comment: TAPi18n.__('operator-comment'),
operator_label: TAPi18n.__('operator-label'),
operator_label_abbrev: TAPi18n.__('operator-label-abbrev'),
operator_user: TAPi18n.__('operator-user'),
operator_user_abbrev: TAPi18n.__('operator-user-abbrev'),
operator_member: TAPi18n.__('operator-member'),
operator_member_abbrev: TAPi18n.__('operator-member-abbrev'),
operator_assignee: TAPi18n.__('operator-assignee'),
operator_assignee_abbrev: TAPi18n.__('operator-assignee-abbrev'),
operator_creator: TAPi18n.__('operator-creator'),
operator_due: TAPi18n.__('operator-due'),
operator_created: TAPi18n.__('operator-created'),
operator_modified: TAPi18n.__('operator-modified'),
operator_status: TAPi18n.__('operator-status'),
operator_has: TAPi18n.__('operator-has'),
operator_sort: TAPi18n.__('operator-sort'),
operator_limit: TAPi18n.__('operator-limit'),
operator_debug: TAPi18n.__('operator-debug'),
operator_org: TAPi18n.__('operator-org'),
operator_team: TAPi18n.__('operator-team'),
predicate_overdue: TAPi18n.__('predicate-overdue'),
predicate_archived: TAPi18n.__('predicate-archived'),
predicate_all: TAPi18n.__('predicate-all'),
predicate_ended: TAPi18n.__('predicate-ended'),
predicate_week: TAPi18n.__('predicate-week'),
predicate_month: TAPi18n.__('predicate-month'),
predicate_quarter: TAPi18n.__('predicate-quarter'),
predicate_year: TAPi18n.__('predicate-year'),
predicate_attachment: TAPi18n.__('predicate-attachment'),
predicate_description: TAPi18n.__('predicate-description'),
predicate_checklist: TAPi18n.__('predicate-checklist'),
predicate_public: TAPi18n.__('predicate-public'),
predicate_private: TAPi18n.__('predicate-private'),
predicate_due: TAPi18n.__('predicate-due'),
predicate_created: TAPi18n.__('predicate-created'),
predicate_modified: TAPi18n.__('predicate-modified'),
predicate_start: TAPi18n.__('predicate-start'),
predicate_end: TAPi18n.__('predicate-end'),
predicate_assignee: TAPi18n.__('predicate-assignee'),
predicate_member: TAPi18n.__('predicate-member'),
predicate_selector: TAPi18n.__('predicate-selector'),
predicate_projection: TAPi18n.__('predicate-projection'),
};
getSearchData() {
return Template.instance().search;
},
let text = '';
[
['# ', 'globalSearch-instructions-heading'],
['\n', 'globalSearch-instructions-description'],
['\n\n', 'globalSearch-instructions-operators'],
['\n- ', 'globalSearch-instructions-operator-board'],
['\n- ', 'globalSearch-instructions-operator-list'],
['\n- ', 'globalSearch-instructions-operator-swimlane'],
['\n- ', 'globalSearch-instructions-operator-comment'],
['\n- ', 'globalSearch-instructions-operator-label'],
['\n- ', 'globalSearch-instructions-operator-hash'],
['\n- ', 'globalSearch-instructions-operator-user'],
['\n- ', 'globalSearch-instructions-operator-at'],
['\n- ', 'globalSearch-instructions-operator-member'],
['\n- ', 'globalSearch-instructions-operator-assignee'],
['\n- ', 'globalSearch-instructions-operator-creator'],
['\n- ', 'globalSearch-instructions-operator-org'],
['\n- ', 'globalSearch-instructions-operator-team'],
['\n- ', 'globalSearch-instructions-operator-due'],
['\n- ', 'globalSearch-instructions-operator-created'],
['\n- ', 'globalSearch-instructions-operator-modified'],
['\n- ', 'globalSearch-instructions-operator-status'],
['\n - ', 'globalSearch-instructions-status-archived'],
['\n - ', 'globalSearch-instructions-status-public'],
['\n - ', 'globalSearch-instructions-status-private'],
['\n - ', 'globalSearch-instructions-status-all'],
['\n - ', 'globalSearch-instructions-status-ended'],
['\n- ', 'globalSearch-instructions-operator-has'],
['\n- ', 'globalSearch-instructions-operator-sort'],
['\n- ', 'globalSearch-instructions-operator-limit'],
['\n## ', 'heading-notes'],
['\n- ', 'globalSearch-instructions-notes-1'],
['\n- ', 'globalSearch-instructions-notes-2'],
['\n- ', 'globalSearch-instructions-notes-3'],
['\n- ', 'globalSearch-instructions-notes-3-2'],
['\n- ', 'globalSearch-instructions-notes-4'],
['\n- ', 'globalSearch-instructions-notes-5'],
].forEach(([prefix, instruction]) => {
text += `${prefix}${TAPi18n.__(instruction, tags)}`
// Replace *<text>* with `<text>` so markdown shows correctly
.replace(/\*\</, '`<')
.replace(/\>\*/, '\>\`')
});
return text;
boardColorClass() {
const boardId = Session.get('currentBoard');
const board = boardId ? ReactiveCache.getBoard(boardId) : null;
return board ? board.colorClass() : 'board-color-belize';
},
searchInstructions() {
try {
const tags = {
operator_board: TAPi18n.__('operator-board'),
operator_list: TAPi18n.__('operator-list'),
operator_swimlane: TAPi18n.__('operator-swimlane'),
operator_comment: TAPi18n.__('operator-comment'),
operator_label: TAPi18n.__('operator-label'),
operator_label_abbrev: TAPi18n.__('operator-label-abbrev'),
operator_user: TAPi18n.__('operator-user'),
operator_user_abbrev: TAPi18n.__('operator-user-abbrev'),
operator_member: TAPi18n.__('operator-member'),
operator_member_abbrev: TAPi18n.__('operator-member-abbrev'),
operator_assignee: TAPi18n.__('operator-assignee'),
operator_assignee_abbrev: TAPi18n.__('operator-assignee-abbrev'),
operator_creator: TAPi18n.__('operator-creator'),
operator_due: TAPi18n.__('operator-due'),
operator_created: TAPi18n.__('operator-created'),
operator_modified: TAPi18n.__('operator-modified'),
operator_status: TAPi18n.__('operator-status'),
operator_has: TAPi18n.__('operator-has'),
operator_sort: TAPi18n.__('operator-sort'),
operator_limit: TAPi18n.__('operator-limit'),
operator_debug: TAPi18n.__('operator-debug'),
operator_org: TAPi18n.__('operator-org'),
operator_team: TAPi18n.__('operator-team'),
predicate_overdue: TAPi18n.__('predicate-overdue'),
predicate_archived: TAPi18n.__('predicate-archived'),
predicate_all: TAPi18n.__('predicate-all'),
predicate_ended: TAPi18n.__('predicate-ended'),
predicate_week: TAPi18n.__('predicate-week'),
predicate_month: TAPi18n.__('predicate-month'),
predicate_quarter: TAPi18n.__('predicate-quarter'),
predicate_year: TAPi18n.__('predicate-year'),
predicate_attachment: TAPi18n.__('predicate-attachment'),
predicate_description: TAPi18n.__('predicate-description'),
predicate_checklist: TAPi18n.__('predicate-checklist'),
predicate_public: TAPi18n.__('predicate-public'),
predicate_private: TAPi18n.__('predicate-private'),
predicate_due: TAPi18n.__('predicate-due'),
predicate_created: TAPi18n.__('predicate-created'),
predicate_modified: TAPi18n.__('predicate-modified'),
predicate_start: TAPi18n.__('predicate-start'),
predicate_end: TAPi18n.__('predicate-end'),
predicate_assignee: TAPi18n.__('predicate-assignee'),
predicate_member: TAPi18n.__('predicate-member'),
predicate_selector: TAPi18n.__('predicate-selector'),
predicate_projection: TAPi18n.__('predicate-projection'),
};
let text = '';
[
['# ', 'globalSearch-instructions-heading'],
['\n', 'globalSearch-instructions-description'],
['\n\n', 'globalSearch-instructions-operators'],
['\n- ', 'globalSearch-instructions-operator-board'],
['\n- ', 'globalSearch-instructions-operator-list'],
['\n- ', 'globalSearch-instructions-operator-swimlane'],
['\n- ', 'globalSearch-instructions-operator-comment'],
['\n- ', 'globalSearch-instructions-operator-label'],
['\n- ', 'globalSearch-instructions-operator-hash'],
['\n- ', 'globalSearch-instructions-operator-user'],
['\n- ', 'globalSearch-instructions-operator-at'],
['\n- ', 'globalSearch-instructions-operator-member'],
['\n- ', 'globalSearch-instructions-operator-assignee'],
['\n- ', 'globalSearch-instructions-operator-creator'],
['\n- ', 'globalSearch-instructions-operator-org'],
['\n- ', 'globalSearch-instructions-operator-team'],
['\n- ', 'globalSearch-instructions-operator-due'],
['\n- ', 'globalSearch-instructions-operator-created'],
['\n- ', 'globalSearch-instructions-operator-modified'],
['\n- ', 'globalSearch-instructions-operator-status'],
['\n - ', 'globalSearch-instructions-status-archived'],
['\n - ', 'globalSearch-instructions-status-public'],
['\n - ', 'globalSearch-instructions-status-private'],
['\n - ', 'globalSearch-instructions-status-all'],
['\n - ', 'globalSearch-instructions-status-ended'],
['\n- ', 'globalSearch-instructions-operator-has'],
['\n- ', 'globalSearch-instructions-operator-sort'],
['\n- ', 'globalSearch-instructions-operator-limit'],
['\n## ', 'heading-notes'],
['\n- ', 'globalSearch-instructions-notes-1'],
['\n- ', 'globalSearch-instructions-notes-2'],
['\n- ', 'globalSearch-instructions-notes-3'],
['\n- ', 'globalSearch-instructions-notes-3-2'],
['\n- ', 'globalSearch-instructions-notes-4'],
['\n- ', 'globalSearch-instructions-notes-5'],
].forEach(([prefix, instruction]) => {
text += `${prefix}${TAPi18n.__(instruction, tags)}`
// Replace *<text>* with `<text>` so markdown shows correctly
.replace(/\*\</, '`<')
.replace(/\>\*/, '\>\`');
});
return text;
} catch (e) {
console.error('Error building searchInstructions:', e);
return '';
}
},
labelColors() {
return Boards.simpleSchema()._schema['labels.$.color'].allowedValues.map(
color => {
return { color, name: TAPi18n.__(`color-${color}`) };
},
);
try {
if (!LABEL_COLORS) return [];
return LABEL_COLORS.map(
color => {
return { color, name: TAPi18n.__(`color-${color}`) };
},
);
} catch (e) {
console.error('Error building labelColors:', e);
return [];
}
},
});
Template.globalSearch.events({
'click input.global-search-query-input'(evt) {
evt.preventDefault();
evt.stopPropagation();
},
'focus input.global-search-query-input'(evt) {
evt.preventDefault();
evt.stopPropagation();
},
'keyup input.global-search-query-input'(evt, tpl) {
evt.preventDefault();
evt.stopPropagation();
// Store the query value
tpl.search.query.set(evt.target.value);
},
'keydown input.global-search-query-input'(evt, tpl) {
// Allow normal key behavior but capture Enter key
if (evt.key === 'Enter') {
evt.preventDefault();
evt.stopPropagation();
searchAllBoards(tpl, evt.target.value);
}
},
'submit .js-search-query-form'(evt, tpl) {
evt.preventDefault();
searchAllBoards(tpl, evt.target.searchQuery.value);
@@ -269,30 +325,6 @@ Template.globalSearch.events({
);
document.getElementById('global-search-input').focus();
},
'click .js-copy-debug-selector'(evt) {
/* Get the text field */
const selector = document.getElementById("debug-selector");
try {
navigator.clipboard.writeText(selector.textContent);
alert("Selector copied to clipboard");
} catch(err) {
alert("Error copying text: " + err);
}
},
'click .js-copy-debug-projection'(evt) {
/* Get the text field */
const projection = document.getElementById("debug-projection");
try {
navigator.clipboard.writeText(projection.textContent);
alert("Projection copied to clipboard");
} catch(err) {
alert("Error copying text: " + err);
}
},
'click .js-board-title'(evt, tpl) {
evt.preventDefault();
const input = document.getElementById('global-search-input');
@@ -340,47 +372,46 @@ Template.globalSearch.events({
},
});
// resultsPaged template helpers - this template is used by globalSearch, brokenCards, and brokenCardsReport.
// It receives the parent's data context (which includes reactive vars) via +resultsPaged(this).
// The jade accesses resultsHeading.get, results.get, getSearchHref, hasPreviousPage.get, hasNextPage.get
// directly on the data context, so we don't need helpers for those when the parent passes
// the right data context. However, we need to ensure the helpers exist for the template.
// resultsPaged template helpers - receives search data context from parent template.
Template.resultsPaged.helpers({
resultsHeading() {
const data = Template.currentData();
if (data && data.resultsHeading) return data.resultsHeading;
// fallback: check parent template
const parentTpl = Template.instance().view.parentView.templateInstance && Template.instance().view.parentView.templateInstance();
if (parentTpl && parentTpl.search) return parentTpl.search.resultsHeading;
return new ReactiveVar('');
if (data && data.resultsHeading) {
const rv = data.resultsHeading;
return rv && typeof rv.get === 'function' ? rv.get() : rv;
}
return '';
},
results() {
const data = Template.currentData();
if (data && data.results) return data.results;
const parentTpl = Template.instance().view.parentView.templateInstance && Template.instance().view.parentView.templateInstance();
if (parentTpl && parentTpl.search) return parentTpl.search.results;
return new ReactiveVar([]);
if (data && data.results) {
const rv = data.results;
return rv && typeof rv.get === 'function' ? rv.get() : rv;
}
return [];
},
getSearchHref() {
const data = Template.currentData();
if (data && data.getSearchHref) return data.getSearchHref();
const parentTpl = Template.instance().view.parentView.templateInstance && Template.instance().view.parentView.templateInstance();
if (parentTpl && parentTpl.search) return parentTpl.search.getSearchHref();
if (data && typeof data.getSearchHref === 'function') {
return data.getSearchHref();
}
return '';
},
hasPreviousPage() {
const data = Template.currentData();
if (data && data.hasPreviousPage) return data.hasPreviousPage;
const parentTpl = Template.instance().view.parentView.templateInstance && Template.instance().view.parentView.templateInstance();
if (parentTpl && parentTpl.search) return parentTpl.search.hasPreviousPage;
return new ReactiveVar(false);
if (data && data.hasPreviousPage) {
const rv = data.hasPreviousPage;
return rv && typeof rv.get === 'function' ? rv.get() : rv;
}
return false;
},
hasNextPage() {
const data = Template.currentData();
if (data && data.hasNextPage) return data.hasNextPage;
const parentTpl = Template.instance().view.parentView.templateInstance && Template.instance().view.parentView.templateInstance();
if (parentTpl && parentTpl.search) return parentTpl.search.hasNextPage;
return new ReactiveVar(false);
if (data && data.hasNextPage) {
const rv = data.hasNextPage;
return rv && typeof rv.get === 'function' ? rv.get() : rv;
}
return false;
},
});
+5 -4
View File
@@ -177,7 +177,6 @@
}
#header-quick-access ul.header-quick-access-list {
transition: opacity 0.2s;
overflow: hidden;
white-space: nowrap;
padding: 10px;
margin: -10px;
@@ -185,16 +184,18 @@
min-width: 0; /* Allow shrinking below content size */
display: flex; /* Use flexbox for better control */
align-items: center;
overflow-x: auto;
scrollbar-color: white #2573a7;
}
/* Hide scrollbar completely */
#header-quick-access ul.header-quick-access-list::-webkit-scrollbar {
display: none;
display: flex;
}
#header-quick-access ul.header-quick-access-list {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
-ms-overflow-style: thin; /* IE and Edge */
scrollbar-width: thin; /* Firefox */
}
#header-quick-access ul.header-quick-access-list li {
display: inline-block; /* Keep inline-block for proper spacing */
+6 -6
View File
@@ -70,10 +70,6 @@ template(name="header")
else
li.current.empty(title="{{_ 'quick-access-description'}}")
| {{_ 'quick-access-description'}}
#header-new-board-icon
// Next line is used only for spacing at header,
// there is no visible clickable icon.
#header-new-board-icon
//
Hide duplicate create board button,
//
@@ -126,5 +122,9 @@ template(name="offlineWarning")
p
i.fa.fa-warning
| {{_ 'app-is-offline'}}
a.app-try-reconnect {{_ 'app-try-reconnect'}}
if appOfflineMessage
br
span= appOfflineMessage
if canReconnectNow
br
a.app-try-reconnect {{_ 'app-try-reconnect'}}
+26 -2
View File
@@ -1,5 +1,9 @@
import { Meteor } from 'meteor/meteor';
import { Session } from 'meteor/session';
import { ReactiveCache } from '/imports/reactiveCache';
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
import Announcements from '/models/announcements';
import { Utils } from '/client/lib/utils';
Meteor.subscribe('user-admin');
Meteor.subscribe('boards');
@@ -43,7 +47,27 @@ Template.header.helpers({
},
appIsOffline() {
return !Meteor.status().connected;
const status = Meteor.status();
return ['waiting', 'failed', 'offline'].includes(status.status);
},
appOfflineMessage() {
const status = Meteor.status();
if (status.status === 'failed' && status.reason) {
return status.reason;
}
if (status.status === 'offline') {
return 'Connection is paused.';
}
return null;
},
canReconnectNow() {
const status = Meteor.status();
return ['waiting', 'failed', 'offline'].includes(status.status);
},
hasAnnouncement() {
@@ -132,7 +156,7 @@ Template.header.events({
Session.set('currentCard', null);
},
'click .js-toggle-desktop-drag-handles'() {
currentUser = Meteor.user();
const currentUser = Meteor.user();
if (currentUser) {
Meteor.call('toggleDesktopDragHandles');
} else if (window.localStorage.getItem('showDesktopDragHandles')) {
+2
View File
@@ -1,6 +1,8 @@
import { ReactiveCache } from '/imports/reactiveCache';
import { TAPi18n } from '/imports/i18n';
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
import Users from '/models/users';
import { EscapeActions } from '/client/lib/escapeActions';
let alreadyCheck = 1;
let isCheckDone = false;
+1
View File
@@ -1,4 +1,5 @@
import { CardSearchPaged } from '../../lib/cardSearch';
import { Utils } from '/client/lib/utils';
Template.myCardsHeaderBar.helpers({
myCardsSort() {
+279 -98
View File
@@ -3,7 +3,7 @@
border-radius: 0.4vw;
border: 1px solid #dbdbdb;
border-bottom-color: #c2c2c2;
box-shadow: 0 0.2vh 0.8vh rgba(0,0,0,0.3);
box-shadow: 0 0.2vh 0.8vh rgba(0, 0, 0, 0.3);
position: absolute;
/* Wider default to fit full color palette */
width: min(380px, 55vw);
@@ -15,10 +15,10 @@
}
.pop-over p,
.pop-over textarea,
.pop-over input[type="text"],
.pop-over input[type="email"],
.pop-over input[type="password"],
.pop-over input[type="file"] {
.pop-over input[type='text'],
.pop-over input[type='email'],
.pop-over input[type='password'],
.pop-over input[type='file'] {
width: 100%;
}
.pop-over select {
@@ -78,9 +78,32 @@
overflow-x: hidden;
}
/* Allow dynamic max-height to override default constraint */
.pop-over[style*="max-height"] .content-wrapper {
max-height: inherit;
/* Bounded height: flex column + clip so inline max-height cannot spill past the shell.
Class pop-over--bounded matches popup.tpl.jade when max-height is set (more reliable
than attribute substring matching on the style attribute alone). */
.pop-over.pop-over--bounded,
.pop-over[style*='max-height'] {
overflow: hidden;
display: flex;
flex-direction: column;
min-height: 0;
box-sizing: border-box;
}
.pop-over.pop-over--bounded .header,
.pop-over[style*='max-height'] .header {
flex: 0 0 auto;
}
.pop-over.pop-over--bounded .content-wrapper,
.pop-over[style*='max-height'] .content-wrapper {
/* 0% basis: use remaining space under header, scroll inside (not past white bg) */
flex: 1 1 0%;
min-height: 0;
max-height: none;
overflow-y: auto;
overflow-x: hidden;
background: inherit;
}
.pop-over .content-container {
width: 100%;
@@ -89,17 +112,18 @@
}
/* Allow dynamic max-height to override default constraint for content-container */
.pop-over[style*="max-height"] .content-container {
max-height: inherit;
.pop-over.pop-over--bounded .content-container,
.pop-over[style*='max-height'] .content-container {
max-height: none;
}
/* Fix overflow in the Member Settings (member menu) popup:
the popup itself gets a max-height inline style, but the header consumes space.
Make the header overlay the scrollable area so the list can't spill out. */
.pop-over[data-popup="memberMenuPopup"] {
.pop-over[data-popup='memberMenuPopup'] {
overflow: hidden;
}
.pop-over[data-popup="memberMenuPopup"] > .header {
.pop-over[data-popup='memberMenuPopup'] > .header {
position: absolute;
top: 0;
left: 0;
@@ -107,30 +131,30 @@
margin-bottom: 0;
z-index: 1;
}
.pop-over[data-popup="memberMenuPopup"] > .content-wrapper {
.pop-over[data-popup='memberMenuPopup'] > .content-wrapper {
padding-top: calc(4.5vh + 1vh);
box-sizing: border-box;
}
/* Admin edit popups: use full height */
.pop-over[data-popup="editUserPopup"],
.pop-over[data-popup="editOrgPopup"],
.pop-over[data-popup="editTeamPopup"] {
.pop-over[data-popup='editUserPopup'],
.pop-over[data-popup='editOrgPopup'],
.pop-over[data-popup='editTeamPopup'] {
height: calc(100vh - 20px) !important;
max-height: calc(100vh - 20px) !important;
}
.pop-over[data-popup="editUserPopup"] .content-wrapper,
.pop-over[data-popup="editOrgPopup"] .content-wrapper,
.pop-over[data-popup="editTeamPopup"] .content-wrapper {
.pop-over[data-popup='editUserPopup'] .content-wrapper,
.pop-over[data-popup='editOrgPopup'] .content-wrapper,
.pop-over[data-popup='editTeamPopup'] .content-wrapper {
max-height: calc(100vh - 80px) !important; /* Subtract header height */
height: calc(100vh - 80px) !important;
overflow-y: auto !important;
}
.pop-over[data-popup="editUserPopup"] .content-container,
.pop-over[data-popup="editOrgPopup"] .content-container,
.pop-over[data-popup="editTeamPopup"] .content-container {
.pop-over[data-popup='editUserPopup'] .content-container,
.pop-over[data-popup='editOrgPopup'] .content-container,
.pop-over[data-popup='editTeamPopup'] .content-container {
max-height: calc(100vh - 80px) !important; /* Subtract header height */
height: calc(100vh - 80px) !important;
}
@@ -142,7 +166,7 @@
}
/* Specific styling for language popup list */
.pop-over[data-popup="changeLanguagePopup"] .pop-over-list {
.pop-over[data-popup='changeLanguagePopup'] .pop-over-list {
max-height: none;
overflow: visible;
height: auto;
@@ -150,7 +174,7 @@
}
/* Ensure content div in language popup contains all items */
.pop-over[data-popup="changeLanguagePopup"] .content {
.pop-over[data-popup='changeLanguagePopup'] .content {
height: auto;
/* Remove forced min-height to avoid top gap */
display: flex;
@@ -158,7 +182,7 @@
}
/* Ensure hidden stack pages truly take no space */
.pop-over[data-popup="changeLanguagePopup"] .content.no-height {
.pop-over[data-popup='changeLanguagePopup'] .content.no-height {
min-height: 0 !important;
height: 0 !important;
padding: 0 !important;
@@ -167,7 +191,7 @@
}
/* Make language popup extend to bottom of browser window */
.pop-over[data-popup="changeLanguagePopup"] {
.pop-over[data-popup='changeLanguagePopup'] {
position: fixed !important;
bottom: 0 !important;
top: auto !important;
@@ -184,12 +208,12 @@
}
/* Allow dynamic height for Change Language popup */
.pop-over[data-popup="changeLanguagePopup"] .header {
.pop-over[data-popup='changeLanguagePopup'] .header {
flex-shrink: 0 !important;
height: auto !important;
}
.pop-over[data-popup="changeLanguagePopup"] .content-wrapper {
.pop-over[data-popup='changeLanguagePopup'] .content-wrapper {
flex: 1 !important;
overflow-y: auto !important;
overflow-x: hidden !important;
@@ -199,7 +223,7 @@
width: 100% !important;
}
.pop-over[data-popup="changeLanguagePopup"] .content-container {
.pop-over[data-popup='changeLanguagePopup'] .content-container {
height: auto !important;
max-height: none !important;
flex: 1 !important;
@@ -208,7 +232,7 @@
width: 100% !important;
}
.pop-over[data-popup="changeLanguagePopup"] .content {
.pop-over[data-popup='changeLanguagePopup'] .content {
height: auto !important;
max-height: none !important;
padding-bottom: 50px !important;
@@ -216,59 +240,59 @@
}
/* 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"] {
.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 {
.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 {
.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 {
.pop-over[data-popup*='Date'] .datepicker-container {
width: 100% !important;
padding: 15px !important;
}
.pop-over[data-popup*="Date"] .datepicker-container .fields {
.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 {
.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 {
.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"] {
.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;
@@ -277,71 +301,109 @@
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 {
.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 {
.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 {
/* Workspace Settings popup: ensure modal doesn't overflow viewport */
.pop-over[data-popup='workspaceActionsPopup'] {
max-height: 80vh !important;
width: min(420px, 85vw) !important;
}
.pop-over[data-popup='workspaceActionsPopup'] .content-wrapper {
max-height: calc(80vh - 70px) !important; /* Subtract header height */
overflow-y: auto !important;
}
.pop-over[data-popup='workspaceActionsPopup'] .content-container {
max-height: calc(80vh - 70px) !important;
}
.pop-over[data-popup='workspaceActionsPopup'] .content {
max-height: none !important; /* Allow content to scroll within container */
overflow: visible !important; /* Overflow handled by parent */
}
/* Card Details Actions popup: ensure menu doesn't overflow viewport */
.pop-over[data-popup="cardDetailsActionsPopup"] {
max-height: 80vh !important;
width: min(380px, 85vw) !important;
}
.pop-over[data-popup="cardDetailsActionsPopup"] .content-wrapper {
max-height: calc(80vh - 70px) !important; /* Subtract header height */
overflow-y: auto !important;
}
.pop-over[data-popup="cardDetailsActionsPopup"] .content-container {
max-height: calc(80vh - 70px) !important;
}
.pop-over[data-popup="cardDetailsActionsPopup"] .content {
max-height: none !important; /* Allow content to scroll within container */
overflow: visible !important; /* Overflow handled by parent */
}
.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 {
.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 {
.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 {
.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 {
.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;
@@ -350,7 +412,7 @@
.pop-over .content-container .content {
/* Match wider popover, leave padding */
width: 100%;
padding: 0 1.3vw 1.3vh;
padding: 1.3vh 1.3vw 1.3vh;
box-sizing: border-box;
/* Ensure content is not shifted left */
margin-left: 0 !important;
@@ -387,8 +449,6 @@
margin-bottom: 2px;
}
/* Fix popup depth containers - don't block transforms which are needed for popup navigation */
.pop-over .popup-container-depth-1,
.pop-over .popup-container-depth-2,
@@ -477,15 +537,15 @@
text-decoration: none;
overflow: hidden;
line-height: 33px;
display:flex;
/* flex-wrap:wrap;*/
gap:5px;
display: flex;
/* flex-wrap:wrap;*/
gap: 5px;
align-items: center;
color: #000 !important;
}
.pop-over-list li > a > .member{
.pop-over-list li > a > .member {
align-self: flex-start;
flex:0 0 auto;
flex: 0 0 auto;
}
.pop-over-list li > a .item-name {
display: block;
@@ -657,10 +717,10 @@ body.grey-icons-enabled .pop-over-list .pop-over-list.checkable .fa-check {
}
.pop-over .content-container .content p,
.pop-over .content-container .content textarea,
.pop-over .content-container .content input[type="text"],
.pop-over .content-container .content input[type="email"],
.pop-over .content-container .content input[type="password"],
.pop-over .content-container .content input[type="file"] {
.pop-over .content-container .content input[type='text'],
.pop-over .content-container .content input[type='email'],
.pop-over .content-container .content input[type='password'],
.pop-over .content-container .content input[type='file'] {
width: 100%;
box-sizing: border-box;
}
@@ -707,3 +767,124 @@ body.mobile-mode .pop-over .content-wrapper {
overflow-y: auto !important;
overflow-x: hidden !important;
}
/* ============================================
FIX: List actions popup overflow (Issue #6221)
Prevents menu from overflowing outside viewport
============================================ */
/* General fix for all popups with many items */
.pop-over .pop-over-list {
max-height: 60vh;
overflow-y: auto;
overflow-x: hidden;
}
/* Specific fix for list actions menu */
.pop-over[data-popup="listActionsPopup"] {
max-height: 85vh !important;
overflow-y: auto !important;
}
.pop-over[data-popup="listActionsPopup"] .content-wrapper {
max-height: calc(85vh - 50px) !important;
overflow-y: auto !important;
}
/* Prevent any popup from overflowing viewport */
.pop-over {
max-height: 90vh !important;
display: flex !important;
flex-direction: column !important;
}
.pop-over .content-wrapper {
flex: 1 1 auto !important;
overflow-y: auto !important;
min-height: 0 !important;
}
.pop-over .content-container {
flex: 1 1 auto !important;
display: flex !important;
flex-direction: column !important;
min-height: 0 !important;
}
.pop-over .content {
flex: 1 1 auto !important;
overflow-y: auto !important;
min-height: 0 !important;
}
/* Keep header fixed while content scrolls */
.pop-over .header {
flex-shrink: 0 !important;
}
/* Mobile-specific adjustments */
@media screen and (max-width: 800px) {
.pop-over[data-popup="listActionsPopup"] {
max-height: 100vh !important;
}
.pop-over[data-popup="listActionsPopup"] .content-wrapper {
max-height: calc(100vh - 48px) !important;
}
}
/* STRONGER FIX - Override inline styles */
.pop-over[style*="height"],
.pop-over[style*="max-height"] {
max-height: 80vh !important;
height: auto !important;
overflow-y: auto !important;
}
.pop-over[style*="height"] .content-wrapper,
.pop-over[style*="max-height"] .content-wrapper {
max-height: calc(80vh - 60px) !important;
height: auto !important;
overflow-y: auto !important;
}
/* Force list popups to have a max height and scroll */
.pop-over .pop-over-list {
max-height: 300px !important;
overflow-y: auto !important;
}
/* Make sure the popup doesn't grow too tall */
.pop-over:has(.pop-over-list) {
max-height: 400px !important;
height: auto !important;
}
/* For the list actions menu specifically */
[data-popup="listActionsPopup"] .pop-over-list,
.js-open-list-menu + .pop-over .pop-over-list {
max-height: 350px !important;
overflow-y: auto !important;
}
/* ============================================
FIX: List actions popup overflow (Issue #6221)
============================================ */
/* Force list popups to have a max height and scroll */
.pop-over .pop-over-list {
max-height: 300px !important;
overflow-y: auto !important;
}
/* Make sure the popup doesn't grow too tall */
.pop-over:has(.pop-over-list) {
max-height: 400px !important;
height: auto !important;
}
/* For the list actions menu specifically */
[data-popup="listActionsPopup"] .pop-over-list,
.js-open-list-menu + .pop-over .pop-over-list {
max-height: 350px !important;
overflow-y: auto !important;
}
+6 -1
View File
@@ -1,3 +1,5 @@
import { CSSEvents } from '/client/lib/cssEvents';
Popup.template.events({
'click .js-back-view'() {
Popup.back();
@@ -26,8 +28,11 @@ Popup.template.events({
// When a popup content is removed (ie, when the user press the "back" button),
// we need to wait for the container translation to end before removing the
// actual DOM element. For that purpose we use the undocumented `_uihooks` API.
Popup.template.onRendered(() => {
Popup.template.onRendered(function () {
const container = this.find('.content-container');
if (!container) {
return;
}
container._uihooks = {
removeElement(node) {
$(node).addClass('no-height');
+1
View File
@@ -2,6 +2,7 @@
class="{{#unless title}}miniprofile{{/unless}}"
class=currentBoard.colorClass
class="{{#unless title}}no-title{{/unless}}"
class="{{#if offset.maxHeight}}pop-over--bounded{{/if}}"
data-popup="{{popupName}}"
style="left:{{offset.left}}px; top:{{offset.top}}px;{{#if offset.maxHeight}} max-height:{{offset.maxHeight}}px;{{/if}}")
.header
@@ -1,13 +1,14 @@
import { ReactiveCache } from '/imports/reactiveCache';
import { formatDateByUserPreference } from '/imports/lib/dateUtils';
import Users from '/models/users';
Template.notification.events({
'click .read-status .materialCheckBox'() {
async 'click .read-status .materialCheckBox'() {
const update = {};
const newReadValue = this.read ? null : Date.now();
const newReadValue = this.read ? null : new Date();
update[`profile.notifications.${this.index}.read`] = newReadValue;
Users.update(Meteor.userId(), { $set: update }, (error, result) => {
await Users.updateAsync(Meteor.userId(), { $set: update }).catch((error) => {
if (error) {
console.error('Error updating notification:', error);
}
@@ -1,5 +1,7 @@
import { ReactiveCache } from '/imports/reactiveCache';
import { TAPi18n } from '/imports/i18n';
import { toggleNotificationsDrawer } from './notifications.js';
import Users from '/models/users';
Template.notificationsDrawer.onCreated(function() {
Meteor.subscribe('notificationActivities');
@@ -34,7 +36,7 @@ Template.notificationsDrawer.events({
event.stopPropagation();
Session.set('showNotificationMenu', !Session.get('showNotificationMenu'));
},
'click .notification-menu .menu-item'(event) {
async 'click .notification-menu .menu-item'(event) {
const target = event.currentTarget;
if (target.classList.contains('mark-all-read')) {
@@ -42,8 +44,10 @@ Template.notificationsDrawer.events({
for (const index in notifications) {
if (notifications.hasOwnProperty(index) && !notifications[index].read) {
const update = {};
update[`profile.notifications.${index}.read`] = Date.now();
Users.update(Meteor.userId(), { $set: update });
update[`profile.notifications.${index}.read`] = new Date();
await Users.updateAsync(Meteor.userId(), { $set: update }).catch((error) => {
console.error('Error marking notification as read:', error);
});
}
}
Session.set('showNotificationMenu', false);
@@ -53,7 +57,9 @@ Template.notificationsDrawer.events({
if (notifications.hasOwnProperty(index) && notifications[index].read) {
const update = {};
update[`profile.notifications.${index}.read`] = null;
Users.update(Meteor.userId(), { $set: update });
await Users.updateAsync(Meteor.userId(), { $set: update }).catch((error) => {
console.error('Error marking notification as unread:', error);
});
}
}
Session.set('showNotificationMenu', false);
@@ -1,5 +1,9 @@
import { ReactiveCache } from '/imports/reactiveCache';
import { TAPi18n } from '/imports/i18n';
import Actions from '/models/actions';
import Rules from '/models/rules';
import Triggers from '/models/triggers';
import { Utils } from '/client/lib/utils';
Template.boardActions.onCreated(function () {
this.subscribe('boards');
@@ -1,6 +1,12 @@
import Actions from '/models/actions';
import { CARD_COLORS } from '/models/metadata/colors';
import Rules from '/models/rules';
import Triggers from '/models/triggers';
import { Utils } from '/client/lib/utils';
let cardColors;
Meteor.startup(() => {
cardColors = Cards.simpleSchema()._schema.color.allowedValues;
cardColors = CARD_COLORS;
});
// Module-level shared state so the color popup can read/write the
@@ -1,3 +1,8 @@
import Actions from '/models/actions';
import Rules from '/models/rules';
import Triggers from '/models/triggers';
import { Utils } from '/client/lib/utils';
Template.checklistActions.onCreated(function () {
this.subscribe('allRules');
});
@@ -1,3 +1,8 @@
import Actions from '/models/actions';
import Rules from '/models/rules';
import Triggers from '/models/triggers';
import { Utils } from '/client/lib/utils';
Template.mailActions.events({
'click .js-mail-action'(event, tpl) {
const emailTo = tpl.find('#email-to').value;
+3
View File
@@ -1,4 +1,7 @@
import { ReactiveCache } from '/imports/reactiveCache';
import Actions from '/models/actions';
import Rules from '/models/rules';
import Triggers from '/models/triggers';
Template.rulesMain.onCreated(function () {
this.rulesCurrentTab = new ReactiveVar('rulesList');
@@ -1,3 +1,5 @@
import { Utils } from '/client/lib/utils';
Template.boardTriggers.onCreated(function () {
this.provaVar = new ReactiveVar('');
this.currentPopupTriggerId = 'def';
@@ -1,4 +1,5 @@
import { TAPi18n } from '/imports/i18n';
import { Utils } from '/client/lib/utils';
Template.cardTriggers.onCreated(function () {
this.subscribe('allRules');
@@ -1,3 +1,5 @@
import { Utils } from '/client/lib/utils';
Template.checklistTriggers.onCreated(function () {
this.subscribe('allRules');
});
+5 -2
View File
@@ -1,11 +1,14 @@
import { ReactiveCache } from '/imports/reactiveCache';
import { TAPi18n } from '/imports/i18n';
import { AttachmentStorage } from '/models/attachments';
import Attachments, { AttachmentStorage } from '/models/attachments';
import { CardSearchPaged } from '/client/lib/cardSearch';
import SessionData from '/models/usersessiondata';
import { QueryParams } from '/config/query-classes';
import { OPERATOR_LIMIT } from '/config/search-const';
const filesize = require('filesize');
import Boards from '/models/boards';
import Cards from '/models/cards';
import Rules from '/models/rules';
const { filesize } = require('filesize');
// --- Shared helper functions (formerly AdminReport base class methods) ---
+6 -3
View File
@@ -1,7 +1,7 @@
import { ReactiveCache } from '/imports/reactiveCache';
import { groupBy } from '/imports/lib/collectionHelpers';
import Attachments, { fileStoreStrategyFactory } from '/models/attachments';
const filesize = require('filesize');
import Attachments from '/models/attachments';
const { filesize } = require('filesize');
Template.attachments.onCreated(function () {
this.subscription = null;
@@ -62,7 +62,10 @@ Template.moveAttachments.helpers({
_attachment.flatVersion = Object.keys(_attachment.versions)
.map(_versionName => {
const _version = Object.assign(_attachment.versions[_versionName], {"versionName": _versionName});
_version.storageName = fileStoreStrategyFactory.getFileStrategy(_attachment, _versionName).getStorageName();
// Read storage directly from the document (set by onAfterUpload on server)
_version.storageName = _version.storage || (
(_attachment.meta?.source === 'import' || _version.meta?.gridFsFileId) ? 'gridfs' : 'fs'
);
return _version;
});
});
@@ -37,7 +37,7 @@ template(name='statistics')
td {{statistics.mongo.mongoStorageEngine}}
tr
th {{_ 'MongoDB_Oplog_enabled'}}
td {{statistics.mongo.mongoOplogEnabled}}
td {{formatBoolean statistics.mongo.mongoOplogEnabled}}
tr
th {{_ 'OS_Type'}}
td {{statistics.os.type}}
@@ -1,5 +1,5 @@
import { TAPi18n } from '/imports/i18n';
const filesize = require('filesize');
const { filesize } = require('filesize');
Template.statistics.onCreated(function () {
this.info = new ReactiveVar({});
@@ -47,4 +47,8 @@ Template.statistics.helpers({
}
return ret;
},
formatBoolean(value) {
return value ? TAPi18n.__('yes') : TAPi18n.__('no');
},
});
+43
View File
@@ -311,3 +311,46 @@ table td:first-child {
text-align: center;
color: #333;
}
/* Avatar styling - removed people-avatar column */
/* Username column styling - displays avatar inline with username */
.username a.js-edit-people-avatar {
display: inline-flex;
align-items: center;
cursor: pointer;
text-decoration: none;
margin-right: 8px;
vertical-align: middle;
}
.username a.js-edit-people-avatar:hover {
background-color: rgba(0, 0, 0, 0.05);
border-radius: 50%;
}
.username .avatar {
height: 30px;
width: 30px;
border-radius: 50%;
display: inline-block;
flex-shrink: 0;
overflow: hidden;
}
.username .avatar.avatar-initials {
background-color: #dbdbdb;
color: #444;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-weight: 500;
}
.username .avatar img.avatar-image {
object-fit: cover;
object-position: center;
height: 100%;
width: 100%;
}
+35 -14
View File
@@ -114,7 +114,7 @@ template(name="orgGeneral")
th {{_ 'shortName'}}
th {{_ 'website'}}
th {{_ 'createdAt'}}
th {{_ 'active'}}
th {{_ 'active-org'}}
tbody
tr
each org in orgList
@@ -137,7 +137,7 @@ template(name="teamGeneral")
th {{_ 'shortName'}}
th {{_ 'website'}}
th {{_ 'createdAt'}}
th {{_ 'active'}}
th {{_ 'active-team'}}
tbody
tr
each team in teamList
@@ -160,7 +160,7 @@ template(name="peopleGeneral")
th {{_ 'username'}}
th {{_ 'email'}}
th {{_ 'admin'}}
th {{_ 'admin-people-active-status'}}
th {{_ 'active-person'}}
th {{_ 'accounts-lockout-status'}}
th {{_ 'createdAt'}}
th
@@ -260,17 +260,38 @@ template(name="teamRow")
template(name="peopleRow")
tr
td
a.edit-user
a.edit-user(data-user-id=userData._id)
i.fa.fa-pencil-square-o
| {{_ 'edit'}}
a.more-settings-user
a.more-settings-user(data-user-id=userData._id)
| ⋯
if userData.loginDisabled
td.username <s>{{ userData.username }}</s>
td.username
a.js-edit-people-avatar(data-user-id=userData._id)
if hasAvatarUrl
.avatar
img.avatar-image(src="{{userData.profile.avatarUrl}}", alt="{{userData.username}}", title="{{_ 'change-avatar'}}")
else
+userAvatarInitials(userId=userData._id)
<s>{{ userData.username }}</s>
else if isUserLocked
td.username {{ userData.username }}
td.username
a.js-edit-people-avatar(data-user-id=userData._id)
if hasAvatarUrl
.avatar
img.avatar-image(src="{{userData.profile.avatarUrl}}", alt="{{userData.username}}", title="{{_ 'change-avatar'}}")
else
+userAvatarInitials(userId=userData._id)
| {{ userData.username }}
else
td.username {{ userData.username }}
td.username
a.js-edit-people-avatar(data-user-id=userData._id)
if hasAvatarUrl
.avatar
img.avatar-image(src="{{userData.profile.avatarUrl}}", alt="{{userData.username}}", title="{{_ 'change-avatar'}}")
else
+userAvatarInitials(userId=userData._id)
| {{ userData.username }}
if userData.loginDisabled
td <s>{{ userData.emails.[0].address }}</s>
else
@@ -333,7 +354,7 @@ template(name="editOrgPopup")
| {{_ 'website'}}
input.js-orgWebsite(type="text" value=org.orgWebsite)
label
| {{_ 'active'}}
| {{_ 'active-org'}}
select.select-active.js-org-isactive
option(value="false") {{_ 'no'}}
option(value="true" selected="{{org.orgIsActive}}") {{_ 'yes'}}
@@ -359,7 +380,7 @@ template(name="editTeamPopup")
| {{_ 'website'}}
input.js-teamWebsite(type="text" value=team.teamWebsite)
label
| {{_ 'active'}}
| {{_ 'active-team'}}
select.select-active.js-team-isactive
option(value="false") {{_ 'no'}}
option(value="true" selected="{{team.teamIsActive}}") {{_ 'yes'}}
@@ -406,7 +427,7 @@ template(name="editUserPopup")
option(value="false") {{_ 'no'}}
option(value="true" selected="{{userData.emails.[0].verified}}") {{_ 'yes'}}
label
| {{_ 'active'}}
| {{_ 'active-person'}}
select.select-active.js-profile-isactive
option(value="false") {{_ 'yes'}}
option(value="true" selected="{{user.loginDisabled}}") {{_ 'no'}}
@@ -469,7 +490,7 @@ template(name="newOrgPopup")
| {{_ 'website'}}
input.js-orgWebsite(type="text" value="" required)
label
| {{_ 'active'}}
| {{_ 'active-org'}}
select.select-active.js-org-isactive
option(value="false" selected="selected") {{_ 'no'}}
option(value="true") {{_ 'yes'}}
@@ -493,7 +514,7 @@ template(name="newTeamPopup")
| {{_ 'website'}}
input.js-teamWebsite(type="text" value="")
label
| {{_ 'active'}}
| {{_ 'active-team'}}
select.select-active.js-team-isactive
option(value="false" selected="selected") {{_ 'no'}}
option(value="true") {{_ 'yes'}}
@@ -555,7 +576,7 @@ template(name="newUserPopup")
option(value="false" selected="selected") {{_ 'no'}}
option(value="true") {{_ 'yes'}}
label
| {{_ 'active'}}
| {{_ 'active-person'}}
select.select-active.js-profile-isactive
option(value="false" selected="selected") {{_ 'yes'}}
option(value="true") {{_ 'no'}}
+81 -12
View File
@@ -1,7 +1,11 @@
import { ReactiveCache } from '/imports/reactiveCache';
import { avatarUpdateCounter } from '/client/components/users/avatarUpdateCounter';
import { InfiniteScrolling } from '/client/lib/infiniteScrolling';
import LockoutSettings from '/models/lockoutSettings';
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
import Org from '/models/org';
import Team from '/models/team';
import Users from '/models/users';
const orgsPerPage = 25;
const teamsPerPage = 25;
@@ -533,7 +537,39 @@ Template.teamRow.helpers({
Template.peopleRow.helpers({
userData() {
return this.user || ReactiveCache.getUser(this.userId);
// Depend on global avatar update counter to reactively update when avatars change
avatarUpdateCounter.get();
// Get the user ID from either this._id or this.user._id
let userId;
if (this.user && this.user._id) {
userId = this.user._id;
} else if (this._id) {
userId = this._id;
}
// Always fetch from ReactiveCache to ensure latest data
if (userId) {
return ReactiveCache.getUser(userId);
}
return this.user || this;
},
hasAvatarUrl() {
// Depend on global avatar update counter to reactively update when avatars change
avatarUpdateCounter.get();
// Get the user ID from either this._id or this.user._id
let userId;
if (this.user && this.user._id) {
userId = this.user._id;
} else if (this._id) {
userId = this._id;
}
let user;
if (userId) {
user = ReactiveCache.getUser(userId);
} else {
user = this.user || this;
}
if (!user || !user.profile) return false;
return !!user.profile.avatarUrl;
},
isUserLocked() {
const user = this.user || ReactiveCache.getUser(this.userId);
@@ -723,8 +759,20 @@ Template.teamRow.events({
});
Template.peopleRow.events({
'click a.edit-user': Popup.open('editUser'),
'click a.more-settings-user': Popup.open('settingsUser'),
'click a.edit-user'(event) {
// Get the user ID from the data attribute
const userId = event.currentTarget.getAttribute('data-user-id');
if (userId) {
Popup.open('editUser').call({ userId: userId }, event);
}
},
'click a.more-settings-user'(event) {
// Get the user ID from the data attribute
const userId = event.currentTarget.getAttribute('data-user-id');
if (userId) {
Popup.open('settingsUser').call({ userId: userId }, event);
}
},
'click .selectUserChkBox': function(ev){
if(ev.currentTarget){
if(ev.currentTarget.checked){
@@ -788,6 +836,18 @@ Template.peopleRow.events({
// For now, we'll leave this as a no-op
}
},
'click a.js-edit-people-avatar'(event) {
// Extract the user ID from the data attribute
const userId = event.currentTarget.getAttribute('data-user-id');
if (userId) {
// Get the user from cache to pass correct context
const user = ReactiveCache.getUser(userId);
if (user) {
// Call Popup.open with the correct user data context
Popup.open('adminChangeAvatar').call({ _id: userId, user: user }, event);
}
}
},
});
Template.modifyTeamsUsers.helpers({
@@ -1180,7 +1240,7 @@ Template.editUserPopup.events({
},
});
UpdateUserOrgsOrTeamsElement = function(isNewUser = false){
const UpdateUserOrgsOrTeamsElement = function(isNewUser = false){
let selectedElt;
let selectedEltValue;
let selectedEltValueId;
@@ -1459,19 +1519,23 @@ Template.settingsTeamPopup.events({
Template.settingsUserPopup.events({
'click .impersonate-user'(event) {
event.preventDefault();
const userId = this.userId || this.user?._id;
Meteor.call('impersonate', this.userId, (err) => {
Meteor.call('impersonate', userId, (err) => {
if (!err) {
// Meteor.connection.setUserId() triggers automatic cache invalidation
// No need to manually invalidate - let Meteor handle the user data refresh
Meteor.connection.setUserId(userId);
FlowRouter.go('/');
Meteor.connection.setUserId(this.userId);
}
});
},
'click #deleteButton'(event) {
event.preventDefault();
const userId = this.userId || this.user?._id;
// Use secure server method instead of direct client-side removal
Meteor.call('removeUser', this.userId, (error, result) => {
Meteor.call('removeUser', userId, (error, result) => {
if (error) {
if (process.env.DEBUG === 'true') {
console.error('Error removing user:', error);
@@ -1498,19 +1562,24 @@ Template.settingsUserPopup.events({
Template.settingsUserPopup.helpers({
user() {
return ReactiveCache.getUser(this.userId);
const userId = this.userId || this.user?._id;
return ReactiveCache.getUser(userId);
},
authentications() {
return Template.instance().authenticationMethods.get();
},
isSelected(match) {
const userId = Template.instance().data.userId;
const selected = ReactiveCache.getUser(userId).authenticationMethod;
const userId = Template.instance().data.userId || Template.instance().data.user?._id;
const user = ReactiveCache.getUser(userId);
if (!user) return false;
const selected = user.authenticationMethod;
return selected === match;
},
isLdap() {
const userId = Template.instance().data.userId;
const selected = ReactiveCache.getUser(userId).authenticationMethod;
const userId = Template.instance().data.userId || Template.instance().data.user?._id;
const user = ReactiveCache.getUser(userId);
if (!user) return false;
const selected = user.authenticationMethod;
return selected === 'ldap';
},
errorMessage() {
+38 -14
View File
@@ -2,6 +2,11 @@ import { ReactiveCache } from '/imports/reactiveCache';
import { TAPi18n } from '/imports/i18n';
import { ALLOWED_WAIT_SPINNERS } from '/config/const';
import LockoutSettings from '/models/lockoutSettings';
import AccessibilitySettings from '/models/accessibilitySettings';
import AccountSettings from '/models/accountSettings';
import Announcements from '/models/announcements';
import Settings from '/models/settings';
import TableVisibilityModeSettings from '/models/tableVisibilityModeSettings';
// import {
// cronMigrationProgress,
// cronMigrationStatus,
@@ -492,7 +497,7 @@ Template.setting.events({
});
const validEmails = [];
emails.forEach((email) => {
if (email && SimpleSchema.RegEx.Email.test(email.trim())) {
if (email && /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test(email.trim())) {
validEmails.push(email.trim());
}
});
@@ -1083,25 +1088,44 @@ Template.announcementSettings.helpers({
});
Template.announcementSettings.events({
'click a.js-toggle-activemessage'(event, tpl) {
async 'click a.js-toggle-activemessage'(event, tpl) {
event.preventDefault();
tpl.loading.set(true);
const announcements = Announcements.findOne();
if (!announcements) {
tpl.loading.set(false);
return;
}
const isActive = announcements.enabled;
Announcements.update(announcements._id, {
$set: { enabled: !isActive },
});
tpl.loading.set(false);
if (isActive) {
$('.admin-announcement').slideUp();
} else {
$('.admin-announcement').slideDown();
try {
await Announcements.updateAsync(announcements._id, {
$set: { enabled: !isActive },
});
if (isActive) {
$('.admin-announcement').slideUp();
} else {
$('.admin-announcement').slideDown();
}
} catch (error) {
alert(error?.reason || error?.message || 'Failed to update announcement setting');
} finally {
tpl.loading.set(false);
}
},
'click button.js-announcement-save'() {
async 'click button.js-announcement-save'(event) {
event.preventDefault();
const message = $('#admin-announcement').val().trim();
Announcements.update(Announcements.findOne()._id, {
$set: { body: message },
});
const announcement = Announcements.findOne();
if (!announcement) {
return;
}
try {
await Announcements.updateAsync(announcement._id, {
$set: { body: message },
});
} catch (error) {
alert(error?.reason || error?.message || 'Failed to save announcement');
}
},
});
+34 -34
View File
@@ -177,8 +177,8 @@ template(name="boardCardSettingsPopup")
a.flex.js-field-has-receiveddate(title="{{_ 'card-received'}}" class="{{#if allowsReceivedDate}}is-checked{{/if}}")
i.fa(class="{{#if allowsReceivedDate}}fa-check{{else}}fa-square-o{{/if}}")
.card-settings-column
a.flex.js-field-has-receiveddate(title="{{_ 'card-received'}}" class="{{#if allowsReceivedDate}}is-checked{{/if}}")
i.fa(class="{{#if allowsReceivedDate}}fa-check{{else}}fa-square-o{{/if}}")
a.flex.js-field-has-receiveddate-on-minicard(title="{{_ 'card-received'}}" class="{{#if allowsReceivedDateOnMinicard}}is-checked{{/if}}")
i.fa(class="{{#if allowsReceivedDateOnMinicard}}fa-check{{else}}fa-square-o{{/if}}")
.card-settings-column
span
i.fa.fa-sign-in
@@ -188,8 +188,8 @@ template(name="boardCardSettingsPopup")
a.flex.js-field-has-startdate(title="{{_ 'card-start'}}" class="{{#if allowsStartDate}}is-checked{{/if}}")
i.fa(class="{{#if allowsStartDate}}fa-check{{else}}fa-square-o{{/if}}")
.card-settings-column
a.flex.js-field-has-startdate(title="{{_ 'card-start'}}" class="{{#if allowsStartDate}}is-checked{{/if}}")
i.fa(class="{{#if allowsStartDate}}fa-check{{else}}fa-square-o{{/if}}")
a.flex.js-field-has-startdate-on-minicard(title="{{_ 'card-start'}}" class="{{#if allowsStartDateOnMinicard}}is-checked{{/if}}")
i.fa(class="{{#if allowsStartDateOnMinicard}}fa-check{{else}}fa-square-o{{/if}}")
.card-settings-column
span
i.fa.fa-hourglass
@@ -199,8 +199,8 @@ template(name="boardCardSettingsPopup")
a.flex.js-field-has-duedate(title="{{_ 'card-due'}}" class="{{#if allowsDueDate}}is-checked{{/if}}")
i.fa(class="{{#if allowsDueDate}}fa-check{{else}}fa-square-o{{/if}}")
.card-settings-column
a.flex.js-field-has-duedate(title="{{_ 'card-due'}}" class="{{#if allowsDueDate}}is-checked{{/if}}")
i.fa(class="{{#if allowsDueDate}}fa-check{{else}}fa-square-o{{/if}}")
a.flex.js-field-has-duedate-on-minicard(title="{{_ 'card-due'}}" class="{{#if allowsDueDateOnMinicard}}is-checked{{/if}}")
i.fa(class="{{#if allowsDueDateOnMinicard}}fa-check{{else}}fa-square-o{{/if}}")
.card-settings-column
span
i.fa.fa-sign-in
@@ -210,8 +210,8 @@ template(name="boardCardSettingsPopup")
a.flex.js-field-has-enddate(title="{{_ 'card-end'}}" class="{{#if allowsEndDate}}is-checked{{/if}}")
i.fa(class="{{#if allowsEndDate}}fa-check{{else}}fa-square-o{{/if}}")
.card-settings-column
a.flex.js-field-has-enddate(title="{{_ 'card-end'}}" class="{{#if allowsEndDate}}is-checked{{/if}}")
i.fa(class="{{#if allowsEndDate}}fa-check{{else}}fa-square-o{{/if}}")
a.flex.js-field-has-enddate-on-minicard(title="{{_ 'card-end'}}" class="{{#if allowsEndDateOnMinicard}}is-checked{{/if}}")
i.fa(class="{{#if allowsEndDateOnMinicard}}fa-check{{else}}fa-square-o{{/if}}")
.card-settings-column
span
i.fa.fa-clock-o
@@ -221,8 +221,8 @@ template(name="boardCardSettingsPopup")
a.flex.js-field-has-members(title="{{_ 'members'}}" class="{{#if allowsMembers}}is-checked{{/if}}")
i.fa(class="{{#if allowsMembers}}fa-check{{else}}fa-square-o{{/if}}")
.card-settings-column
a.flex.js-field-has-members(title="{{_ 'members'}}" class="{{#if allowsMembers}}is-checked{{/if}}")
i.fa(class="{{#if allowsMembers}}fa-check{{else}}fa-square-o{{/if}}")
a.flex.js-field-has-members-on-minicard(title="{{_ 'members'}}" class="{{#if allowsMembersOnMinicard}}is-checked{{/if}}")
i.fa(class="{{#if allowsMembersOnMinicard}}fa-check{{else}}fa-square-o{{/if}}")
.card-settings-column
span
i.fa.fa-users
@@ -252,8 +252,8 @@ template(name="boardCardSettingsPopup")
a.flex.js-field-has-assignee(title="{{_ 'assignee'}}" class="{{#if allowsAssignee}}is-checked{{/if}}")
i.fa(class="{{#if allowsAssignee}}fa-check{{else}}fa-square-o{{/if}}")
.card-settings-column
a.flex.js-field-has-assignee(title="{{_ 'assignee'}}" class="{{#if allowsAssignee}}is-checked{{/if}}")
i.fa(class="{{#if allowsAssignee}}fa-check{{else}}fa-square-o{{/if}}")
a.flex.js-field-has-assignee-on-minicard(title="{{_ 'assignee'}}" class="{{#if allowsAssigneeOnMinicard}}is-checked{{/if}}")
i.fa(class="{{#if allowsAssigneeOnMinicard}}fa-check{{else}}fa-square-o{{/if}}")
.card-settings-column
span
i.fa.fa-user
@@ -263,8 +263,8 @@ template(name="boardCardSettingsPopup")
a.flex.js-field-has-assigned-by(title="{{_ 'assigned-by'}}" class="{{#if allowsAssignedBy}}is-checked{{/if}}")
i.fa(class="{{#if allowsAssignedBy}}fa-check{{else}}fa-square-o{{/if}}")
.card-settings-column
a.flex.js-field-has-assigned-by(title="{{_ 'assigned-by'}}" class="{{#if allowsAssignedBy}}is-checked{{/if}}")
i.fa(class="{{#if allowsAssignedBy}}fa-check{{else}}fa-square-o{{/if}}")
a.flex.js-field-has-assigned-by-on-minicard(title="{{_ 'assigned-by'}}" class="{{#if allowsAssignedByOnMinicard}}is-checked{{/if}}")
i.fa(class="{{#if allowsAssignedByOnMinicard}}fa-check{{else}}fa-square-o{{/if}}")
.card-settings-column
span
i.fa.fa-shopping-cart
@@ -274,8 +274,8 @@ template(name="boardCardSettingsPopup")
a.flex.js-field-has-requested-by(title="{{_ 'requested-by'}}" class="{{#if allowsRequestedBy}}is-checked{{/if}}")
i.fa(class="{{#if allowsRequestedBy}}fa-check{{else}}fa-square-o{{/if}}")
.card-settings-column
a.flex.js-field-has-requested-by(title="{{_ 'requested-by'}}" class="{{#if allowsRequestedBy}}is-checked{{/if}}")
i.fa(class="{{#if allowsRequestedBy}}fa-check{{else}}fa-square-o{{/if}}")
a.flex.js-field-has-requested-by-on-minicard(title="{{_ 'requested-by'}}" class="{{#if allowsRequestedByOnMinicard}}is-checked{{/if}}")
i.fa(class="{{#if allowsRequestedByOnMinicard}}fa-check{{else}}fa-square-o{{/if}}")
.card-settings-column
span
i.fa.fa-user
@@ -316,8 +316,8 @@ template(name="boardCardSettingsPopup")
a.flex.js-field-has-labels(title="{{_ 'labels'}}" class="{{#if allowsLabels}}is-checked{{/if}}")
i.fa(class="{{#if allowsLabels}}fa-check{{else}}fa-square-o{{/if}}")
.card-settings-column
a.flex.js-field-has-labels(title="{{_ 'labels'}}" class="{{#if allowsLabels}}is-checked{{/if}}")
i.fa(class="{{#if allowsLabels}}fa-check{{else}}fa-square-o{{/if}}")
a.flex.js-field-has-labels-on-minicard(title="{{_ 'labels'}}" class="{{#if allowsLabelsOnMinicard}}is-checked{{/if}}")
i.fa(class="{{#if allowsLabelsOnMinicard}}fa-check{{else}}fa-square-o{{/if}}")
.card-settings-column
span
i.fa.fa-tag
@@ -337,8 +337,8 @@ template(name="boardCardSettingsPopup")
a.flex.js-field-has-card-number(title="{{_ 'card'}} {{_ 'number'}}" class="{{#if allowsCardNumber}}is-checked{{/if}}")
i.fa(class="{{#if allowsCardNumber}}fa-check{{else}}fa-square-o{{/if}}")
.card-settings-column
a.flex.js-field-has-card-number(title="{{_ 'card'}} {{_ 'number'}}" class="{{#if allowsCardNumber}}is-checked{{/if}}")
i.fa(class="{{#if allowsCardNumber}}fa-check{{else}}fa-square-o{{/if}}")
a.flex.js-field-has-card-number-on-minicard(title="{{_ 'card'}} {{_ 'number'}}" class="{{#if allowsCardNumberOnMinicard}}is-checked{{/if}}")
i.fa(class="{{#if allowsCardNumberOnMinicard}}fa-check{{else}}fa-square-o{{/if}}")
.card-settings-column
span
| #️⃣
@@ -349,8 +349,8 @@ template(name="boardCardSettingsPopup")
a.flex.js-field-has-description-title(title="{{_ 'description'}} {{_ 'title'}}" class="{{#if allowsDescriptionTitle}}is-checked{{/if}}")
i.fa(class="{{#if allowsDescriptionTitle}}fa-check{{else}}fa-square-o{{/if}}")
.card-settings-column
a.flex.js-field-has-description-title(title="{{_ 'description'}} {{_ 'title'}}" class="{{#if allowsDescriptionTitle}}is-checked{{/if}}")
i.fa(class="{{#if allowsDescriptionTitle}}fa-check{{else}}fa-square-o{{/if}}")
a.flex.js-field-has-description-title-on-minicard(title="{{_ 'description'}} {{_ 'title'}}" class="{{#if allowsDescriptionTitleOnMinicard}}is-checked{{/if}}")
i.fa(class="{{#if allowsDescriptionTitleOnMinicard}}fa-check{{else}}fa-square-o{{/if}}")
.card-settings-column
span
i.fa.fa-file-text-o
@@ -361,8 +361,8 @@ template(name="boardCardSettingsPopup")
a.flex.js-field-has-description-text(title="{{_ 'description'}} {{_ 'custom-field-text'}}" class="{{#if allowsDescriptionText}}is-checked{{/if}}")
i.fa(class="{{#if allowsDescriptionText}}fa-check{{else}}fa-square-o{{/if}}")
.card-settings-column
a.flex.js-field-has-description-text(title="{{_ 'description'}} {{_ 'custom-field-text'}}" class="{{#if allowsDescriptionText}}is-checked{{/if}}")
i.fa(class="{{#if allowsDescriptionText}}fa-check{{else}}fa-square-o{{/if}}")
a.flex.js-field-has-description-text-on-minicard(title="{{_ 'description'}} {{_ 'custom-field-text'}}" class="{{#if allowsDescriptionTextOnMinicard}}is-checked{{/if}}")
i.fa(class="{{#if allowsDescriptionTextOnMinicard}}fa-check{{else}}fa-square-o{{/if}}")
.card-settings-column
span
i.fa.fa-file-text-o
@@ -383,8 +383,8 @@ template(name="boardCardSettingsPopup")
a.flex.js-field-has-checklists(title="{{_ 'checklists'}}" class="{{#if allowsChecklists}}is-checked{{/if}}")
i.fa(class="{{#if allowsChecklists}}fa-check{{else}}fa-square-o{{/if}}")
.card-settings-column
a.flex.js-field-has-checklists(title="{{_ 'checklists'}}" class="{{#if allowsChecklists}}is-checked{{/if}}")
i.fa(class="{{#if allowsChecklists}}fa-check{{else}}fa-square-o{{/if}}")
a.flex.js-field-has-checklists-on-minicard(title="{{_ 'checklists'}}" class="{{#if allowsChecklistsOnMinicard}}is-checked{{/if}}")
i.fa(class="{{#if allowsChecklistsOnMinicard}}fa-check{{else}}fa-square-o{{/if}}")
.card-settings-column
span
i.fa.fa-check
@@ -394,8 +394,8 @@ template(name="boardCardSettingsPopup")
a.flex.js-field-has-subtasks(title="{{_ 'subtasks'}}" class="{{#if allowsSubtasks}}is-checked{{/if}}")
i.fa(class="{{#if allowsSubtasks}}fa-check{{else}}fa-square-o{{/if}}")
.card-settings-column
a.flex.js-field-has-subtasks(title="{{_ 'subtasks'}}" class="{{#if allowsSubtasks}}is-checked{{/if}}")
i.fa(class="{{#if allowsSubtasks}}fa-check{{else}}fa-square-o{{/if}}")
a.flex.js-field-has-subtasks-on-minicard(title="{{_ 'subtasks'}}" class="{{#if allowsSubtasksOnMinicard}}is-checked{{/if}}")
i.fa(class="{{#if allowsSubtasksOnMinicard}}fa-check{{else}}fa-square-o{{/if}}")
.card-settings-column
span
i.fa.fa-globe
@@ -405,8 +405,8 @@ template(name="boardCardSettingsPopup")
a.flex.js-field-has-attachments(title="{{_ 'attachments'}}" class="{{#if allowsAttachments}}is-checked{{/if}}")
i.fa(class="{{#if allowsAttachments}}fa-check{{else}}fa-square-o{{/if}}")
.card-settings-column
a.flex.js-field-has-attachments(title="{{_ 'attachments'}}" class="{{#if allowsAttachments}}is-checked{{/if}}")
i.fa(class="{{#if allowsAttachments}}fa-check{{else}}fa-square-o{{/if}}")
a.flex.js-field-has-attachments-on-minicard(title="{{_ 'attachments'}}" class="{{#if allowsAttachmentsOnMinicard}}is-checked{{/if}}")
i.fa(class="{{#if allowsAttachmentsOnMinicard}}fa-check{{else}}fa-square-o{{/if}}")
.card-settings-column
span
i.fa.fa-paperclip
@@ -528,8 +528,8 @@ template(name="deleteDuplicateListsPopup")
template(name="outgoingWebhooksPopup")
each integrations
form.integration-form
a.flex
i.fa(class="{{#unless enabled}}fa-check{{else}}fa-square-o{{/unless}}")
a.flex.js-toggle-webhook-enabled
.materialCheckBox(class="{{#unless enabled}}is-checked{{/unless}}")
span {{_ 'disable-webhook'}}
input.js-outgoing-webhooks-title(placeholder="{{_ 'webhook-title'}}" type="text" name="title" value=title)
input.js-outgoing-webhooks-url(type="text" name="url" value=url)
@@ -640,11 +640,11 @@ template(name="exportBoardPopup")
li
a(href="{{exportCsvUrl}}", download="{{exportCsvFilename}}")
i.fa.fa-upload
| {{_ 'export-board-csv'}} ,
| {{_ 'export-board-csv-comma'}}
li
a(href="{{exportScsvUrl}}", download="{{exportCsvFilename}}")
i.fa.fa-upload
| {{_ 'export-board-csv'}} ;
| {{_ 'export-board-csv-semicolon'}}
li
a(href="{{exportTsvUrl}}", download="{{exportTsvFilename}}")
i.fa.fa-upload
+210 -43
View File
@@ -1,9 +1,24 @@
import { Meteor } from 'meteor/meteor';
import { Template } from 'meteor/templating';
import { ReactiveVar } from 'meteor/reactive-var';
import { ReactiveCache } from '/imports/reactiveCache';
import { TAPi18n } from '/imports/i18n';
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
import { InfiniteScrolling } from '/client/lib/infiniteScrolling';
import AccessibilitySettings from '/models/accessibilitySettings';
import Boards from '/models/boards';
import Integrations from '/models/integrations';
import Lists from '/models/lists';
import { BOARD_COLORS } from '/models/metadata/colors';
import { Filter } from '/client/lib/filter';
import { EscapeActions } from '/client/lib/escapeActions';
import { Utils } from '/client/lib/utils';
import {
clearSidebarInstance,
setSidebarInstance,
} from '/client/features/sidebar/service';
Sidebar = null;
export let Sidebar = null;
const defaultView = 'home';
const MCB = '.materialCheckBox';
@@ -25,6 +40,7 @@ Template.sidebar.onCreated(function() {
this.infiniteScrolling = new InfiniteScrolling();
this.activitiesInstance = null;
Sidebar = this;
setSidebarInstance(this);
// Subscribe to accessibility settings
Meteor.subscribe('accessibilitySettings');
@@ -106,6 +122,7 @@ Template.sidebar.onCreated(function() {
});
Template.sidebar.onDestroyed(function() {
clearSidebarInstance(this);
Sidebar = null;
});
@@ -153,7 +170,7 @@ Template.sidebar.events({
tpl.setView();
},
'click .js-toggle-minicard-label-text'() {
currentUser = ReactiveCache.getCurrentUser();
const currentUser = ReactiveCache.getCurrentUser();
if (currentUser) {
Meteor.call('toggleMinicardLabelText');
} else if (window.localStorage.getItem('hiddenMinicardLabelText')) {
@@ -176,12 +193,12 @@ Template.sidebar.events({
'click .js-show-week-of-year-toggle'() {
ReactiveCache.getCurrentUser().toggleShowWeekOfYear();
},
'click .sidebar-accessibility'() {
'click .sidebar-accessibility'(event, tpl) {
FlowRouter.go('accessibility');
Sidebar.toggle();
tpl.toggle();
},
'click .js-close-sidebar'() {
Sidebar.toggle()
'click .js-close-sidebar'(event, tpl) {
tpl.toggle();
},
'scroll .js-board-sidebar-content'(event, tpl) {
tpl.infiniteScrolling.checkScrollPosition(event.currentTarget, () => {
@@ -194,7 +211,7 @@ Blaze.registerHelper('Sidebar', () => Sidebar);
Template.homeSidebar.helpers({
hiddenMinicardLabelText() {
currentUser = ReactiveCache.getCurrentUser();
const currentUser = ReactiveCache.getCurrentUser();
if (currentUser) {
return (currentUser.profile || {}).hiddenMinicardLabelText;
} else if (window.localStorage.getItem('hiddenMinicardLabelText')) {
@@ -218,8 +235,8 @@ Template.homeSidebar.helpers({
});
Template.homeSidebar.events({
'click .js-toggle-show-activities'() {
Utils.getCurrentBoard().toggleShowActivities();
async 'click .js-toggle-show-activities'() {
await Utils.getCurrentBoard().toggleShowActivities();
},
});
@@ -237,7 +254,9 @@ Template.boardInfoOnMyBoardsPopup.helpers({
EscapeActions.register(
'sidebarView',
() => {
Sidebar.setView(defaultView);
if (Sidebar) {
Sidebar.setView(defaultView);
}
},
() => {
return Sidebar && Sidebar.getView() !== defaultView;
@@ -296,11 +315,15 @@ Template.boardMenuPopup.events({
Popup.back();
},
'click .js-custom-fields'() {
Sidebar.setView('customFields');
if (Sidebar) {
Sidebar.setView('customFields');
}
Popup.back();
},
'click .js-open-archives'() {
Sidebar.setView('archives');
if (Sidebar) {
Sidebar.setView('archives');
}
Popup.back();
},
'click .js-change-board-color': Popup.open('boardChangeColor'),
@@ -544,10 +567,6 @@ Template.membersWidget.events({
},
});
Template.outgoingWebhooksPopup.onCreated(function() {
this.disabled = new ReactiveVar(false);
});
Template.outgoingWebhooksPopup.helpers({
boardId() {
return Session.get('currentBoard') || Integrations.Const.GLOBAL_WEBHOOK_ID;
@@ -571,20 +590,23 @@ Template.outgoingWebhooksPopup.helpers({
});
Template.outgoingWebhooksPopup.events({
'click a.flex'(evt, tpl) {
tpl.disabled.set(!tpl.disabled.get());
$(evt.target).toggleClass(CKCLS, tpl.disabled.get());
},
submit(evt, tpl) {
'click .js-toggle-webhook-enabled'(evt) {
evt.preventDefault();
const url = evt.target.url.value;
$(evt.currentTarget).find(MCB).toggleClass(CKCLS);
},
async submit(evt) {
evt.preventDefault();
const url = evt.target.url.value.trim();
const boardId = Session.get('currentBoard') || Integrations.Const.GLOBAL_WEBHOOK_ID;
let id = null;
let integration = null;
const title = evt.target.title.value;
const token = evt.target.token.value;
const type = evt.target.type.value;
const enabled = !tpl.disabled.get();
const title = evt.target.title.value.trim();
const token = evt.target.token.value.trim();
const type = evt.target.type.value.trim();
const enabled = !$(evt.target)
.find('.js-toggle-webhook-enabled')
.find(MCB)
.hasClass(CKCLS);
let remove = false;
const values = {
url,
@@ -609,22 +631,27 @@ Template.outgoingWebhooksPopup.events({
} else if (url) {
integration = findIntegration({ url, token });
}
if (remove) {
Integrations.remove(integration._id);
} else if (integration && integration._id) {
Integrations.update(integration._id, {
$set: values,
});
} else if (url) {
Integrations.insert({
...values,
userId: Meteor.userId(),
enabled: true,
boardId,
activities: ['all'],
});
try {
if (remove && integration && integration._id) {
await Integrations.removeAsync(integration._id);
} else if (integration && integration._id) {
await Integrations.updateAsync(integration._id, {
$set: values,
});
} else if (url) {
await Integrations.insertAsync({
...values,
userId: Meteor.userId(),
enabled,
boardId,
activities: ['all'],
});
}
Popup.back();
} catch (error) {
alert(error?.reason || error?.message || 'Failed to save webhook');
}
Popup.back();
},
});
@@ -778,7 +805,7 @@ Template.labelsWidget.onRendered(draggableMembersLabelsWidgets);
Template.boardChangeColorPopup.helpers({
backgroundColors() {
return Boards.simpleSchema()._schema.color.allowedValues;
return BOARD_COLORS;
},
isSelected() {
const currentBoard = Utils.getCurrentBoard();
@@ -1002,26 +1029,51 @@ Template.boardCardSettingsPopup.helpers({
const currentBoard = ReactiveCache.getBoard(boardId);
return currentBoard ? currentBoard.allowsReceivedDate : false;
},
allowsReceivedDateOnMinicard() {
const boardId = Session.get('currentBoard');
const currentBoard = ReactiveCache.getBoard(boardId);
return currentBoard ? currentBoard.allowsReceivedDateOnMinicard : false;
},
allowsStartDate() {
const boardId = Session.get('currentBoard');
const currentBoard = ReactiveCache.getBoard(boardId);
return currentBoard ? currentBoard.allowsStartDate : false;
},
allowsStartDateOnMinicard() {
const boardId = Session.get('currentBoard');
const currentBoard = ReactiveCache.getBoard(boardId);
return currentBoard ? currentBoard.allowsStartDateOnMinicard : false;
},
allowsDueDate() {
const boardId = Session.get('currentBoard');
const currentBoard = ReactiveCache.getBoard(boardId);
return currentBoard ? currentBoard.allowsDueDate : false;
},
allowsDueDateOnMinicard() {
const boardId = Session.get('currentBoard');
const currentBoard = ReactiveCache.getBoard(boardId);
return currentBoard ? currentBoard.allowsDueDateOnMinicard : false;
},
allowsEndDate() {
const boardId = Session.get('currentBoard');
const currentBoard = ReactiveCache.getBoard(boardId);
return currentBoard ? currentBoard.allowsEndDate : false;
},
allowsEndDateOnMinicard() {
const boardId = Session.get('currentBoard');
const currentBoard = ReactiveCache.getBoard(boardId);
return currentBoard ? currentBoard.allowsEndDateOnMinicard : false;
},
allowsSubtasks() {
const boardId = Session.get('currentBoard');
const currentBoard = ReactiveCache.getBoard(boardId);
return currentBoard ? currentBoard.allowsSubtasks : false;
},
allowsSubtasksOnMinicard() {
const boardId = Session.get('currentBoard');
const currentBoard = ReactiveCache.getBoard(boardId);
return currentBoard ? currentBoard.allowsSubtasksOnMinicard : false;
},
allowsCreator() {
const boardId = Session.get('currentBoard');
const currentBoard = ReactiveCache.getBoard(boardId);
@@ -1037,21 +1089,41 @@ Template.boardCardSettingsPopup.helpers({
const currentBoard = ReactiveCache.getBoard(boardId);
return currentBoard ? currentBoard.allowsMembers : false;
},
allowsMembersOnMinicard() {
const boardId = Session.get('currentBoard');
const currentBoard = ReactiveCache.getBoard(boardId);
return currentBoard ? currentBoard.allowsMembersOnMinicard : false;
},
allowsAssignee() {
const boardId = Session.get('currentBoard');
const currentBoard = ReactiveCache.getBoard(boardId);
return currentBoard ? currentBoard.allowsAssignee : false;
},
allowsAssigneeOnMinicard() {
const boardId = Session.get('currentBoard');
const currentBoard = ReactiveCache.getBoard(boardId);
return currentBoard ? currentBoard.allowsAssigneeOnMinicard : false;
},
allowsAssignedBy() {
const boardId = Session.get('currentBoard');
const currentBoard = ReactiveCache.getBoard(boardId);
return currentBoard ? currentBoard.allowsAssignedBy : false;
},
allowsAssignedByOnMinicard() {
const boardId = Session.get('currentBoard');
const currentBoard = ReactiveCache.getBoard(boardId);
return currentBoard ? currentBoard.allowsAssignedByOnMinicard : false;
},
allowsRequestedBy() {
const boardId = Session.get('currentBoard');
const currentBoard = ReactiveCache.getBoard(boardId);
return currentBoard ? currentBoard.allowsRequestedBy : false;
},
allowsRequestedByOnMinicard() {
const boardId = Session.get('currentBoard');
const currentBoard = ReactiveCache.getBoard(boardId);
return currentBoard ? currentBoard.allowsRequestedByOnMinicard : false;
},
allowsCardSortingByNumber() {
const boardId = Session.get('currentBoard');
const currentBoard = ReactiveCache.getBoard(boardId);
@@ -1067,6 +1139,11 @@ Template.boardCardSettingsPopup.helpers({
const currentBoard = ReactiveCache.getBoard(boardId);
return currentBoard ? currentBoard.allowsLabels : false;
},
allowsLabelsOnMinicard() {
const boardId = Session.get('currentBoard');
const currentBoard = ReactiveCache.getBoard(boardId);
return currentBoard ? currentBoard.allowsLabelsOnMinicard : false;
},
allowsShowListsOnMinicard() {
const boardId = Session.get('currentBoard');
const currentBoard = ReactiveCache.getBoard(boardId);
@@ -1077,11 +1154,21 @@ Template.boardCardSettingsPopup.helpers({
const currentBoard = ReactiveCache.getBoard(boardId);
return currentBoard ? currentBoard.allowsChecklists : false;
},
allowsChecklistsOnMinicard() {
const boardId = Session.get('currentBoard');
const currentBoard = ReactiveCache.getBoard(boardId);
return currentBoard ? currentBoard.allowsChecklistsOnMinicard : false;
},
allowsAttachments() {
const boardId = Session.get('currentBoard');
const currentBoard = ReactiveCache.getBoard(boardId);
return currentBoard ? currentBoard.allowsAttachments : false;
},
allowsAttachmentsOnMinicard() {
const boardId = Session.get('currentBoard');
const currentBoard = ReactiveCache.getBoard(boardId);
return currentBoard ? currentBoard.allowsAttachmentsOnMinicard : false;
},
allowsComments() {
const boardId = Session.get('currentBoard');
const currentBoard = ReactiveCache.getBoard(boardId);
@@ -1092,11 +1179,21 @@ Template.boardCardSettingsPopup.helpers({
const currentBoard = ReactiveCache.getBoard(boardId);
return currentBoard ? currentBoard.allowsCardNumber : false;
},
allowsCardNumberOnMinicard() {
const boardId = Session.get('currentBoard');
const currentBoard = ReactiveCache.getBoard(boardId);
return currentBoard ? currentBoard.allowsCardNumberOnMinicard : false;
},
allowsDescriptionTitle() {
const boardId = Session.get('currentBoard');
const currentBoard = ReactiveCache.getBoard(boardId);
return currentBoard ? currentBoard.allowsDescriptionTitle : false;
},
allowsDescriptionTitleOnMinicard() {
const boardId = Session.get('currentBoard');
const currentBoard = ReactiveCache.getBoard(boardId);
return currentBoard ? currentBoard.allowsDescriptionTitleOnMinicard : false;
},
allowsDescriptionText() {
const boardId = Session.get('currentBoard');
const currentBoard = ReactiveCache.getBoard(boardId);
@@ -1186,26 +1283,51 @@ Template.boardCardSettingsPopup.events({
const newValue = !tpl.currentBoard.allowsReceivedDate;
Boards.update(tpl.currentBoard._id, { $set: { allowsReceivedDate: newValue } });
},
'click .js-field-has-receiveddate-on-minicard'(evt, tpl) {
evt.preventDefault();
const newValue = !tpl.currentBoard.allowsReceivedDateOnMinicard;
Boards.update(tpl.currentBoard._id, { $set: { allowsReceivedDateOnMinicard: newValue } });
},
'click .js-field-has-startdate'(evt, tpl) {
evt.preventDefault();
const newValue = !tpl.currentBoard.allowsStartDate;
Boards.update(tpl.currentBoard._id, { $set: { allowsStartDate: newValue } });
},
'click .js-field-has-startdate-on-minicard'(evt, tpl) {
evt.preventDefault();
const newValue = !tpl.currentBoard.allowsStartDateOnMinicard;
Boards.update(tpl.currentBoard._id, { $set: { allowsStartDateOnMinicard: newValue } });
},
'click .js-field-has-enddate'(evt, tpl) {
evt.preventDefault();
const newValue = !tpl.currentBoard.allowsEndDate;
Boards.update(tpl.currentBoard._id, { $set: { allowsEndDate: newValue } });
},
'click .js-field-has-enddate-on-minicard'(evt, tpl) {
evt.preventDefault();
const newValue = !tpl.currentBoard.allowsEndDateOnMinicard;
Boards.update(tpl.currentBoard._id, { $set: { allowsEndDateOnMinicard: newValue } });
},
'click .js-field-has-duedate'(evt, tpl) {
evt.preventDefault();
const newValue = !tpl.currentBoard.allowsDueDate;
Boards.update(tpl.currentBoard._id, { $set: { allowsDueDate: newValue } });
},
'click .js-field-has-duedate-on-minicard'(evt, tpl) {
evt.preventDefault();
const newValue = !tpl.currentBoard.allowsDueDateOnMinicard;
Boards.update(tpl.currentBoard._id, { $set: { allowsDueDateOnMinicard: newValue } });
},
'click .js-field-has-subtasks'(evt, tpl) {
evt.preventDefault();
const newValue = !tpl.currentBoard.allowsSubtasks;
Boards.update(tpl.currentBoard._id, { $set: { allowsSubtasks: newValue } });
},
'click .js-field-has-subtasks-on-minicard'(evt, tpl) {
evt.preventDefault();
const newValue = !tpl.currentBoard.allowsSubtasksOnMinicard;
Boards.update(tpl.currentBoard._id, { $set: { allowsSubtasksOnMinicard: newValue } });
},
'click .js-field-has-creator'(evt, tpl) {
evt.preventDefault();
const newValue = !tpl.currentBoard.allowsCreator;
@@ -1221,21 +1343,41 @@ Template.boardCardSettingsPopup.events({
const newValue = !tpl.currentBoard.allowsMembers;
Boards.update(tpl.currentBoard._id, { $set: { allowsMembers: newValue } });
},
'click .js-field-has-members-on-minicard'(evt, tpl) {
evt.preventDefault();
const newValue = !tpl.currentBoard.allowsMembersOnMinicard;
Boards.update(tpl.currentBoard._id, { $set: { allowsMembersOnMinicard: newValue } });
},
'click .js-field-has-assignee'(evt, tpl) {
evt.preventDefault();
const newValue = !tpl.currentBoard.allowsAssignee;
Boards.update(tpl.currentBoard._id, { $set: { allowsAssignee: newValue } });
},
'click .js-field-has-assignee-on-minicard'(evt, tpl) {
evt.preventDefault();
const newValue = !tpl.currentBoard.allowsAssigneeOnMinicard;
Boards.update(tpl.currentBoard._id, { $set: { allowsAssigneeOnMinicard: newValue } });
},
'click .js-field-has-assigned-by'(evt, tpl) {
evt.preventDefault();
const newValue = !tpl.currentBoard.allowsAssignedBy;
Boards.update(tpl.currentBoard._id, { $set: { allowsAssignedBy: newValue } });
},
'click .js-field-has-assigned-by-on-minicard'(evt, tpl) {
evt.preventDefault();
const newValue = !tpl.currentBoard.allowsAssignedByOnMinicard;
Boards.update(tpl.currentBoard._id, { $set: { allowsAssignedByOnMinicard: newValue } });
},
'click .js-field-has-requested-by'(evt, tpl) {
evt.preventDefault();
const newValue = !tpl.currentBoard.allowsRequestedBy;
Boards.update(tpl.currentBoard._id, { $set: { allowsRequestedBy: newValue } });
},
'click .js-field-has-requested-by-on-minicard'(evt, tpl) {
evt.preventDefault();
const newValue = !tpl.currentBoard.allowsRequestedByOnMinicard;
Boards.update(tpl.currentBoard._id, { $set: { allowsRequestedByOnMinicard: newValue } });
},
'click .js-field-has-card-sorting-by-number'(evt, tpl) {
evt.preventDefault();
const newValue = !tpl.currentBoard.allowsCardSortingByNumber;
@@ -1251,6 +1393,11 @@ Template.boardCardSettingsPopup.events({
const newValue = !tpl.currentBoard.allowsLabels;
Boards.update(tpl.currentBoard._id, { $set: { allowsLabels: newValue } });
},
'click .js-field-has-labels-on-minicard'(evt, tpl) {
evt.preventDefault();
const newValue = !tpl.currentBoard.allowsLabelsOnMinicard;
Boards.update(tpl.currentBoard._id, { $set: { allowsLabelsOnMinicard: newValue } });
},
'click .js-field-has-card-show-lists-on-minicard'(evt, tpl) {
evt.preventDefault();
tpl.currentBoard.allowsShowListsOnMinicard = !tpl.currentBoard
@@ -1283,6 +1430,11 @@ Template.boardCardSettingsPopup.events({
tpl.currentBoard.allowsDescriptionTitle,
);
},
'click .js-field-has-description-title-on-minicard'(evt, tpl) {
evt.preventDefault();
const newValue = !tpl.currentBoard.allowsDescriptionTitleOnMinicard;
Boards.update(tpl.currentBoard._id, { $set: { allowsDescriptionTitleOnMinicard: newValue } });
},
'click .js-field-has-card-number'(evt, tpl) {
evt.preventDefault();
tpl.currentBoard.allowsCardNumber = !tpl.currentBoard
@@ -1299,6 +1451,11 @@ Template.boardCardSettingsPopup.events({
tpl.currentBoard.allowsCardNumber,
);
},
'click .js-field-has-card-number-on-minicard'(evt, tpl) {
evt.preventDefault();
const newValue = !tpl.currentBoard.allowsCardNumberOnMinicard;
Boards.update(tpl.currentBoard._id, { $set: { allowsCardNumberOnMinicard: newValue } });
},
'click .js-field-has-description-text-on-minicard'(evt, tpl) {
evt.preventDefault();
tpl.currentBoard.allowsDescriptionTextOnMinicard = !tpl.currentBoard
@@ -1347,6 +1504,11 @@ Template.boardCardSettingsPopup.events({
tpl.currentBoard.allowsChecklists,
);
},
'click .js-field-has-checklists-on-minicard'(evt, tpl) {
evt.preventDefault();
const newValue = !tpl.currentBoard.allowsChecklistsOnMinicard;
Boards.update(tpl.currentBoard._id, { $set: { allowsChecklistsOnMinicard: newValue } });
},
'click .js-field-has-attachments'(evt, tpl) {
evt.preventDefault();
tpl.currentBoard.allowsAttachments = !tpl.currentBoard
@@ -1363,6 +1525,11 @@ Template.boardCardSettingsPopup.events({
tpl.currentBoard.allowsAttachments,
);
},
'click .js-field-has-attachments-on-minicard'(evt, tpl) {
evt.preventDefault();
const newValue = !tpl.currentBoard.allowsAttachmentsOnMinicard;
Boards.update(tpl.currentBoard._id, { $set: { allowsAttachmentsOnMinicard: newValue } });
},
'click .js-field-has-comments'(evt, tpl) {
evt.preventDefault();
tpl.currentBoard.allowsComments = !tpl.currentBoard.allowsComments;
@@ -1472,7 +1639,7 @@ Template.addMemberPopup.onCreated(function() {
};
this.isValidEmail = function(email) {
return SimpleSchema.RegEx.Email.test(email);
return /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test(email);
};
this.performSearch = function(query) {
@@ -2,10 +2,13 @@ import { ReactiveCache } from '/imports/reactiveCache';
import { TAPi18n } from '/imports/i18n';
import Lists from '/models/lists';
import Swimlanes from '/models/swimlanes';
import Cards from '/models/cards';
import { Utils } from '/client/lib/utils';
const ARCHIVE_PAGE_SIZE = 30;
const ARCHIVE_SCROLL_THRESHOLD_PX = 120;
function throttle(func, wait) {
let timeoutId = null;
let lastRan = 0;
@@ -1,5 +1,6 @@
import { ReactiveCache } from '/imports/reactiveCache';
import { TAPi18n } from '/imports/i18n';
import CustomFields from '/models/customFields';
Template.customFieldsSidebar.helpers({
customFields() {
+6 -2
View File
@@ -1,7 +1,11 @@
import { ReactiveCache } from '/imports/reactiveCache';
import { TAPi18n } from '/imports/i18n';
import { Filter } from '/client/lib/filter';
import { EscapeActions } from '/client/lib/escapeActions';
import { MultiSelection } from '/client/lib/multiSelection';
import { Utils } from '/client/lib/utils';
const subManager = new SubsManager();
// SubsManager removed for Meteor 3 migration
Template.filterSidebar.events({
'submit .js-list-filter'(evt, tpl) {
@@ -64,7 +68,7 @@ Template.filterSidebar.events({
Filter.resetExceptions();
const currentBoardId = Session.get('currentBoard');
if (!currentBoardId) return;
subManager.subscribe(
Meteor.subscribe(
'board',
currentBoardId,
Filter.archive.isSelected(),
@@ -1,3 +1,5 @@
import { Utils } from '/client/lib/utils';
Template.searchSidebar.onCreated(function () {
this.term = new ReactiveVar('');
});
+10 -4
View File
@@ -1,10 +1,13 @@
import { TAPi18n } from '/imports/i18n';
import { ReactiveCache } from '/imports/reactiveCache';
import { SWIMLANE_COLORS } from '/models/metadata/colors';
import Swimlanes from '/models/swimlanes';
import { Utils } from '/client/lib/utils';
const { calculateIndexData } = Utils;
let swimlaneColors;
Meteor.startup(() => {
swimlaneColors = Swimlanes.simpleSchema()._schema.color.allowedValues;
swimlaneColors = SWIMLANE_COLORS;
});
function swimlaneHeaderCollapsed(check = undefined) {
@@ -117,7 +120,7 @@ Template.swimlaneAddPopup.onCreated(function () {
});
Template.swimlaneAddPopup.events({
submit(event, tpl) {
async submit(event, tpl) {
event.preventDefault();
const currentBoard = Utils.getCurrentBoard();
const nextSwimlane = currentBoard.nextSwimlane(tpl.currentSwimlane);
@@ -133,7 +136,7 @@ Template.swimlaneAddPopup.events({
: 'swimlane';
if (title) {
Swimlanes.insert({
await Swimlanes.insertAsync({
title,
boardId: Session.get('currentBoard'),
sort: sortValue.base || 0,
@@ -172,13 +175,16 @@ Template.setSwimlaneColorPopup.events({
Popup.back();
},
'click .js-palette-color'(event, tpl) {
tpl.currentColor.set(Template.currentData().color);
const paletteData = Blaze.getData(event.currentTarget);
tpl.currentColor.set(paletteData?.color);
},
async 'click .js-submit'(event, tpl) {
event.preventDefault();
await tpl.currentSwimlane.setColor(tpl.currentColor.get());
Popup.back();
},
async 'click .js-remove-color'(event, tpl) {
event.preventDefault();
await tpl.currentSwimlane.setColor(null);
Popup.back();
},
+57 -6
View File
@@ -1,6 +1,11 @@
import { ReactiveCache } from '/imports/reactiveCache';
import { once } from '/imports/lib/collectionHelpers';
import dragscroll from '@wekanteam/dragscroll';
import Lists from '/models/lists';
import { CSSEvents } from '/client/lib/cssEvents';
import { Filter } from '/client/lib/filter';
import { EscapeActions } from '/client/lib/escapeActions';
import { Utils } from '/client/lib/utils';
const { calculateIndex } = Utils;
function saveSorting(ui) {
@@ -137,10 +142,23 @@ function saveSorting(ui) {
function currentListIsInThisSwimlane(swimlaneId) {
const currentList = Utils.getCurrentList();
return (
currentList &&
(currentList.swimlaneId === swimlaneId || currentList.swimlaneId === '')
if (!currentList) return false;
// Match the list's own swimlane, or shared/orphaned lists (empty/null
// swimlaneId are visible in every swimlane as a fallback).
if (currentList.swimlaneId === swimlaneId || !currentList.swimlaneId) {
return true;
}
// Also match when the list has an orphaned swimlaneId (references a deleted
// swimlane) and THIS is the first swimlane — orphaned lists are shown there.
const currentBoard = Utils.getCurrentBoard();
if (!currentBoard) return false;
const allSwimlanes = ReactiveCache.getSwimlanes(
{ boardId: currentBoard._id, archived: false },
{ sort: ['sort'] },
);
if (!allSwimlanes.length || allSwimlanes[0]._id !== swimlaneId) return false;
const validIds = new Set(allSwimlanes.map(s => s._id));
return !validIds.has(currentList.swimlaneId);
}
function currentCardIsInThisList(listId, swimlaneId) {
@@ -154,7 +172,10 @@ function currentCardIsInThisList(listId, swimlaneId) {
return (
currentCard &&
currentCard.listId === listId &&
currentCard.swimlaneId === swimlaneId
// Match cards for this swimlane, AND orphaned/shared cards that have no
// swimlaneId assigned (null/empty) — they are shown in every swimlane as
// a fallback so no content is ever invisible.
(currentCard.swimlaneId === swimlaneId || !currentCard.swimlaneId)
);
else if (
//currentUser &&
@@ -577,8 +598,38 @@ Template.swimlane.helpers({
return ReactiveCache.getCurrentUser().isBoardAdmin();
},
lists() {
// Return per-swimlane lists for this swimlane
return this.myLists();
const swimlane = this;
// myLists() already covers:
// • lists owned by this swimlane (swimlaneId === this._id)
// • shared / pre-migration lists (swimlaneId empty or null)
const regularLists = swimlane.myLists();
// Additionally, detect lists whose swimlaneId references a swimlane that
// no longer exists ("orphaned-swimlane" lists). These would be invisible
// in every swimlane without this fallback. Show them in the FIRST swimlane
// on the board so they are always accessible without a DB migration.
const allSwimlanes = ReactiveCache.getSwimlanes(
{ boardId: swimlane.boardId, archived: false },
{ sort: ['sort'] },
);
// Only the first swimlane picks up orphaned-swimlane lists.
if (!allSwimlanes.length || allSwimlanes[0]._id !== swimlane._id) {
return regularLists;
}
const validIds = allSwimlanes.map(s => s._id);
const orphaned = swimlane.orphanedSwimlaneLists(validIds);
if (!orphaned.length) return regularLists;
// Merge, deduplicating by _id (regularLists may already contain some).
const seen = new Set(regularLists.map(l => l._id));
const combined = [...regularLists];
for (const l of orphaned) {
if (!seen.has(l._id)) {
seen.add(l._id);
combined.push(l);
}
}
return combined;
},
collapseSwimlane() {
return Utils.getSwimlaneCollapseState(this);
@@ -0,0 +1,4 @@
import { ReactiveVar } from 'meteor/reactive-var';
// Reactive source for invalidating people row when avatars change
export const avatarUpdateCounter = new ReactiveVar(0);
+37 -2
View File
@@ -70,11 +70,11 @@ template(name="changeAvatarPopup")
each uploadedAvatars
li: a.js-select-avatar
.member
img.avatar.avatar-image(src="{{link}}")
img.avatar.avatar-image(src="{{avatarLink}}")
if isSelected
i.fa.fa-check
p.sub-name
a.js-delete-avatar {{_ 'delete'}}
a.js-delete-avatar(data-avatar-id="{{_id}}") {{_ 'delete'}}
| -
= name
li: a.js-select-initials
@@ -100,6 +100,41 @@ template(name="deleteAvatarPopup")
p {{_ 'delete-avatar-confirm'}}
button.js-confirm.negate.full(type="submit") {{_ 'delete'}}
template(name="adminChangeAvatarPopup")
.admin-change-avatar-header
h3 {{_ 'change-avatar'}} - {{ username }}
if error.get
.warning {{_ error.get}}
ul.pop-over-list
each uploadedAvatars
li: a.js-select-avatar
.member
img.avatar.avatar-image(src="{{avatarLink}}")
if isSelected
i.fa.fa-check
p.sub-name
a.js-delete-avatar(data-avatar-id="{{_id}}") {{_ 'delete'}}
| -
= name
li: a.js-select-initials
.member
+userAvatarInitials(userData=userData)
| {{_ 'initials' }}
if noAvatarUrl
i.fa.fa-check
p.sub-name {{_ 'default-avatar'}}
input.hide.js-upload-avatar-input(accept="image/*;capture=camera" type="file")
if Meteor.settings.public.avatarsUploadMaxSize
| {{_ 'max-avatar-filesize'}} {{Meteor.settings.public.avatarsUploadMaxSize}}
br
if Meteor.settings.public.avatarsUploadMimeTypes
| {{_ 'allowed-avatar-filetypes'}} {{Meteor.settings.public.avatarsUploadMimeTypes}}
br
| {{_ 'invalid-file'}}
button.full.js-upload-avatar
i.fa.fa-upload
| {{_ 'upload-avatar' }}
template(name="cardMemberPopup")
.board-member-menu
.mini-profile-info
+191 -14
View File
@@ -1,9 +1,9 @@
import { ReactiveCache } from '/imports/reactiveCache';
import Cards from '/models/cards';
import { ReactiveVar } from 'meteor/reactive-var';
import { avatarUpdateCounter } from '/client/components/users/avatarUpdateCounter';
import Avatars from '/models/avatars';
import Users from '/models/users';
import Org from '/models/org';
import Team from '/models/team';
import Presences from '/models/presences';
import { Utils } from '/client/lib/utils';
Template.userAvatar.helpers({
userData() {
@@ -166,6 +166,7 @@ Template.boardTeamName.helpers({
Template.changeAvatarPopup.onCreated(function () {
this.error = new ReactiveVar('');
this.avatarUpdateCounter = new ReactiveVar(0); // Trigger to force helper re-evaluation
Meteor.subscribe('my-avatars');
});
@@ -174,16 +175,28 @@ Template.changeAvatarPopup.helpers({
return Template.instance().error;
},
uploadedAvatars() {
Template.instance().avatarUpdateCounter.get(); // Create dependency on update counter
const ret = ReactiveCache.getAvatars({ userId: Meteor.userId() }, {}, true);
return ret;
},
avatarLink() {
if (this && typeof this.link === 'function') {
return this.link();
}
return '';
},
isSelected() {
Template.instance().avatarUpdateCounter.get(); // Create dependency on update counter
const userProfile = ReactiveCache.getCurrentUser().profile;
const avatarUrl = userProfile && userProfile.avatarUrl;
const currentAvatarUrl = Template.currentData().link();
return avatarUrl === currentAvatarUrl;
const currentAvatarUrl = this.link && typeof this.link === 'function' ? this.link() : '';
// Normalize URLs by removing query parameters for comparison
// (they may be added for boardId but shouldn't affect selection comparison)
const normalizeUrl = (url) => url ? url.split('?')[0] : '';
return normalizeUrl(avatarUrl) === normalizeUrl(currentAvatarUrl);
},
noAvatarUrl() {
Template.instance().avatarUpdateCounter.get(); // Create dependency on update counter
const userProfile = ReactiveCache.getCurrentUser().profile;
const avatarUrl = userProfile && userProfile.avatarUrl;
return !avatarUrl;
@@ -194,6 +207,14 @@ function changeAvatarSetAvatar(tpl, avatarUrl) {
Meteor.call('setAvatarUrl', avatarUrl, (err) => {
if (err) {
tpl.error.set(err.reason || 'Error setting avatar');
} else {
// Trigger a re-evaluation of helpers to show updated avatar selection
const counter = tpl.avatarUpdateCounter.get();
tpl.avatarUpdateCounter.set(counter + 1);
// Also increment global counter for admin people list updates
avatarUpdateCounter.set(avatarUpdateCounter.get() + 1);
// Clear input for next upload
tpl.$('.js-upload-avatar-input').val('');
}
});
}
@@ -202,9 +223,9 @@ Template.changeAvatarPopup.events({
'click .js-upload-avatar'(event, tpl) {
tpl.$('.js-upload-avatar-input').click();
},
'change .js-upload-avatar-input'(event, tpl) {
async 'change .js-upload-avatar-input'(event, tpl) {
if (event.currentTarget.files && event.currentTarget.files[0]) {
const uploader = Avatars.insert(
const uploader = await Avatars.insertAsync(
{
file: event.currentTarget.files[0],
chunkSize: 'dynamic',
@@ -214,15 +235,25 @@ Template.changeAvatarPopup.events({
uploader.on('error', (error, fileData) => {
tpl.error.set(error.reason);
});
uploader.on('uploaded', (error, fileRef) => {
if (!error) {
// Trigger a re-evaluation of helpers to show new uploaded avatar
const counter = tpl.avatarUpdateCounter.get();
tpl.avatarUpdateCounter.set(counter + 1);
// Also increment global counter for admin people list updates
avatarUpdateCounter.set(avatarUpdateCounter.get() + 1);
} else {
tpl.error.set(error.reason);
}
});
uploader.start();
}
},
'click .js-select-avatar'(event, tpl) {
event.preventDefault();
event.stopPropagation();
const data = Blaze.getData(event.currentTarget);
if (data && typeof data.link === 'function') {
const avatarUrl = data.link();
if (this && typeof this.link === 'function') {
const avatarUrl = this.link();
changeAvatarSetAvatar(tpl, avatarUrl);
}
},
@@ -231,10 +262,13 @@ Template.changeAvatarPopup.events({
event.stopPropagation();
changeAvatarSetAvatar(tpl, '');
},
'click .js-delete-avatar': Popup.afterConfirm('deleteAvatar', function (event) {
Avatars.remove(this._id);
'click .js-delete-avatar': Popup.afterConfirm('deleteAvatar', async function() {
// Inside the each loop, 'this' is the avatar object
const avatarId = this._id;
if (avatarId) {
await Avatars.removeAsync(avatarId);
}
Popup.back();
event.stopPropagation();
}),
});
@@ -251,3 +285,146 @@ Template.cardMemberPopup.events({
},
'click .js-edit-profile': Popup.open('editProfile'),
});
Template.adminChangeAvatarPopup.onCreated(function () {
this.error = new ReactiveVar('');
this.avatarUpdateCounter = new ReactiveVar(0);
const userId = this.data._id || (this.data.user && this.data.user._id);
this.targetUserId = userId;
if (userId) {
Meteor.subscribe('avatars-for-user', userId);
}
});
Template.adminChangeAvatarPopup.helpers({
error() {
return Template.instance().error;
},
userId() {
const instance = Template.instance();
return instance.targetUserId || (this._id || (this.user && this.user._id));
},
userData() {
return this.user || this;
},
uploadedAvatars() {
Template.instance().avatarUpdateCounter.get();
const instance = Template.instance();
const userId = instance.targetUserId || (this._id || (this.user && this.user._id));
if (!userId) return [];
const ret = ReactiveCache.getAvatars({ userId: userId }, {}, true);
return ret;
},
avatarLink() {
if (this && typeof this.link === 'function') {
return this.link();
}
return '';
},
currentEditingUser() {
Template.instance().avatarUpdateCounter.get();
const instance = Template.instance();
const userId = instance.targetUserId;
if (!userId) return null;
return ReactiveCache.getUser(userId);
},
isSelected() {
Template.instance().avatarUpdateCounter.get();
const instance = Template.instance();
const userId = instance.targetUserId;
if (!userId) return false;
const user = ReactiveCache.getUser(userId);
if (!user) return false;
const userProfile = user.profile;
const avatarUrl = userProfile && userProfile.avatarUrl;
const currentAvatarUrl = this.link && typeof this.link === 'function' ? this.link() : '';
const normalizeUrl = (url) => url ? url.split('?')[0] : '';
return normalizeUrl(avatarUrl) === normalizeUrl(currentAvatarUrl);
},
noAvatarUrl() {
Template.instance().avatarUpdateCounter.get();
const instance = Template.instance();
const userId = instance.targetUserId;
if (!userId) return true;
const user = ReactiveCache.getUser(userId);
if (!user) return true;
const userProfile = user.profile;
const avatarUrl = userProfile && userProfile.avatarUrl;
return !avatarUrl;
},
});
function adminChangeAvatarSetAvatar(tpl, avatarUrl) {
const userId = tpl.targetUserId || (Template.currentData()._id || (Template.currentData().user && Template.currentData().user._id));
if (!userId) {
console.error('Cannot set avatar: no userId found');
return;
}
Meteor.call('adminSetAvatarUrl', userId, avatarUrl, (err) => {
if (err) {
tpl.error.set(err.reason || 'Error setting avatar');
} else {
const counter = tpl.avatarUpdateCounter.get();
tpl.avatarUpdateCounter.set(counter + 1);
// Also increment global counter to update admin people list
avatarUpdateCounter.set(avatarUpdateCounter.get() + 1);
tpl.$('.js-upload-avatar-input').val('');
}
});
}
Template.adminChangeAvatarPopup.events({
'click .js-upload-avatar'(event, tpl) {
tpl.$('.js-upload-avatar-input').click();
},
async 'change .js-upload-avatar-input'(event, tpl) {
if (event.currentTarget.files && event.currentTarget.files[0]) {
const userId = tpl.targetUserId || (this._id || (this.user && this.user._id));
const uploader = await Avatars.insertAsync(
{
file: event.currentTarget.files[0],
chunkSize: 'dynamic',
meta: {
adminUploadForUserId: userId,
},
},
false,
);
uploader.on('error', (error, fileData) => {
tpl.error.set(error.reason);
});
uploader.on('uploaded', (error, fileRef) => {
if (!error) {
const counter = tpl.avatarUpdateCounter.get();
tpl.avatarUpdateCounter.set(counter + 1);
// Also increment global counter to update admin people list
avatarUpdateCounter.set(avatarUpdateCounter.get() + 1);
} else {
tpl.error.set(error.reason);
}
});
uploader.start();
}
},
'click .js-select-avatar'(event, tpl) {
event.preventDefault();
event.stopPropagation();
if (this && typeof this.link === 'function') {
const avatarUrl = this.link();
adminChangeAvatarSetAvatar(tpl, avatarUrl);
}
},
'click .js-select-initials'(event, tpl) {
event.preventDefault();
event.stopPropagation();
adminChangeAvatarSetAvatar(tpl, '');
},
'click .js-delete-avatar': Popup.afterConfirm('deleteAvatar', async function() {
// Inside the each loop, 'this' is the avatar object
const avatarId = this._id;
if (avatarId) {
await Avatars.removeAsync(avatarId);
}
Popup.back();
}),
});
+8 -4
View File
@@ -1,6 +1,9 @@
import { ReactiveCache } from '/imports/reactiveCache';
import { TAPi18n } from '/imports/i18n';
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
import AccountSettings from '/models/accountSettings';
import Users from '/models/users';
import { Utils } from '/client/lib/utils';
Template.headerUserBar.events({
'click .js-open-header-member-menu': Popup.open('memberMenu'),
@@ -37,7 +40,7 @@ Template.memberMenuPopup.helpers({
isSameDomainNameSettingValue(){
const currSett = ReactiveCache.getCurrentSetting();
if(currSett && currSett != undefined && currSett.disableRegistration && currSett.mailDomainName !== undefined && currSett.mailDomainName != ""){
currentUser = ReactiveCache.getCurrentUser();
const currentUser = ReactiveCache.getCurrentUser();
if (currentUser) {
let found = false;
for(let i = 0; i < currentUser.emails.length; i++) {
@@ -57,7 +60,8 @@ Template.memberMenuPopup.helpers({
isNotOAuth2AuthenticationMethod(){
const currentUser = ReactiveCache.getCurrentUser();
if (currentUser) {
return currentUser.authenticationMethod.toLowerCase() != 'oauth2';
const method = (currentUser.authenticationMethod || '').toLowerCase();
return method !== 'oauth2';
} else {
return true;
}
@@ -136,7 +140,7 @@ Template.invitePeoplePopup.events({
});
const validEmails = [];
emails.forEach(email => {
if (email && SimpleSchema.RegEx.Email.test(email.trim())) {
if (email && /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test(email.trim())) {
validEmails.push(email.trim());
}
});
@@ -361,7 +365,7 @@ Template.changeSettingsPopup.helpers({
});
},
startDayOfWeek() {
currentUser = ReactiveCache.getCurrentUser();
const currentUser = ReactiveCache.getCurrentUser();
if (currentUser) {
return currentUser.getStartDayOfWeek();
} else {
+19
View File
@@ -28,6 +28,7 @@ import {
fromNow,
calendar,
} from '/imports/lib/dateUtils';
import { Utils } from '/client/lib/utils';
Blaze.registerHelper('currentBoard', () => {
const ret = Utils.getCurrentBoard();
@@ -139,3 +140,21 @@ Blaze.registerHelper('$nin', (...args) => {
return args.every((arg) => value !== arg);
});
Blaze.registerHelper('$', () => ({ Session, Meteor }));
// Expose module-scoped objects to Blaze templates (jade references)
import { Filter } from '/client/lib/filter';
import { MultiSelection } from '/client/lib/multiSelection';
import { BoardMultiSelection } from '/client/lib/boardMultiSelection';
let _Sidebar;
function getSidebar() {
if (!_Sidebar) {
_Sidebar = require('/client/features/sidebar/service').getSidebarInstance;
}
return _Sidebar();
}
Blaze.registerHelper('Filter', () => Filter);
Blaze.registerHelper('MultiSelection', () => MultiSelection);
Blaze.registerHelper('Sidebar', () => getSidebar());
Blaze.registerHelper('BoardMultiSelection', () => BoardMultiSelection);
+9
View File
@@ -0,0 +1,9 @@
import '/client/components/activities/activities.jade';
import '/client/components/activities/comments.jade';
import '/client/components/activities/activities.js';
import '/client/components/activities/comments.js';
import '/client/components/activities/activities.css';
import '/client/components/activities/comments.css';
+4
View File
@@ -0,0 +1,4 @@
import '/client/components/boardConversionProgress.jade';
import '/client/components/boardConversionProgress.js';
import '/client/components/boardConversionProgress.css';
+20
View File
@@ -0,0 +1,20 @@
import '/client/components/boards/boardArchive.jade';
import '/client/components/boards/boardBody.jade';
import '/client/components/boards/boardHeader.jade';
import '/client/components/boards/boardsList.jade';
import '/client/components/boards/miniboard.jade';
import '/client/components/boards/originalPositionsView.jade';
import '/client/components/boards/boardArchive.js';
import '/client/components/boards/boardBody.js';
import '/client/components/boards/boardHeader.js';
import '/client/components/boards/boardsList.js';
import '/client/components/boards/originalPositionsView.js';
import '/client/components/boards/boardBody.css';
import '/client/components/boards/boardColors.css';
import '/client/components/boards/boardHeader.css';
import '/client/components/boards/boardsList.css';
import '/client/components/boards/calendarView.css';
import '/client/components/boards/originalPositionsView.css';
+36
View File
@@ -0,0 +1,36 @@
import '/client/components/cards/attachments.jade';
import '/client/components/cards/cardCustomFields.jade';
import '/client/components/cards/cardDate.jade';
import '/client/components/cards/cardDescription.jade';
import '/client/components/cards/cardDetails.jade';
import '/client/components/cards/cardTime.jade';
import '/client/components/cards/checklists.jade';
import '/client/components/cards/inlinedCardDescription.jade';
import '/client/components/cards/labels.jade';
import '/client/components/cards/minicard.jade';
import '/client/components/cards/resultCard.jade';
import '/client/components/cards/subtasks.jade';
import '/client/components/cards/attachments.js';
import '/client/components/cards/cardCustomFields.js';
import '/client/components/cards/cardDate.js';
import '/client/components/cards/cardDescription.js';
import '/client/components/cards/cardDetails.js';
import '/client/components/cards/cardTime.js';
import '/client/components/cards/checklists.js';
import '/client/components/cards/labels.js';
import '/client/components/cards/minicard.js';
import '/client/components/cards/resultCard.js';
import '/client/components/cards/subtasks.js';
import '/client/components/cards/attachments.css';
import '/client/components/cards/cardDate.css';
import '/client/components/cards/cardDescription.css';
import '/client/components/cards/cardDetails.css';
import '/client/components/cards/cardTime.css';
import '/client/components/cards/checklists.css';
import '/client/components/cards/labels.css';
import '/client/components/cards/minicard.css';
import '/client/components/cards/resultCard.css';
import '/client/components/cards/subtasks.css';
+4
View File
@@ -0,0 +1,4 @@
import '/client/components/common/originalPosition.jade';
import '/client/components/common/originalPosition.js';
import '/client/components/common/originalPosition.css';
+8
View File
@@ -0,0 +1,8 @@
import '/client/components/forms/datepicker.jade';
import '/client/components/forms/inlinedform.jade';
import '/client/lib/inlinedform';
import '/client/components/forms/datepicker.js';
import '/client/components/forms/datepicker.css';
import '/client/components/forms/forms.css';
+9
View File
@@ -0,0 +1,9 @@
import '/client/components/gantt/gantt.jade';
import '/client/components/gantt/ganttCard.jade';
import '/client/components/gantt/gantt.js';
import '/client/components/gantt/ganttCard.js';
import '/client/components/gantt/gantt.css';
import '/client/components/gantt/ganttCard.css';
Loaded 100 of 921 files, more files were not shown because too many files have changed in this diff. Show more