Compare commits

...
542 Commits
Author SHA1 Message Date
Lauri Ojansivu a58e6be78d v3.76 2020-02-07 03:29:21 +02:00
Lauri Ojansivu 77b43e75a2 Update translations. 2020-02-07 03:28:33 +02:00
Lauri Ojansivu 228e5389cf Update ChangeLog. 2020-02-07 03:20:46 +02:00
Lauri Ojansivu bf78b093ba Try to disable dragging Swimlanes/Lists/Cards/Checklists/Subtasks on small mobile smartphones webbrowsers, and hide drag handles on mobile web.
Thanks to xet7 !
2020-02-07 03:16:16 +02:00
Lauri Ojansivu 8384d68a06 Use Meteor 1.9 and Node.js 12.15.0 on Snap and Docker.
Thanks to xet7 !
2020-02-07 03:12:27 +02:00
Lauri Ojansivu 120a430deb Fix Bug enable/disable Comments in Card Settings.
Thanks to warnt, mdurokov and xet7 !

Closes #2923
2020-02-07 02:34:57 +02:00
Lauri Ojansivu 8e7b9b73b5 Update translations. 2020-02-07 00:08:36 +02:00
Lauri Ojansivu 47afee63d1 v3.75 2020-02-05 20:12:22 +02:00
Lauri Ojansivu 243e4c856e Update translations. 2020-02-05 19:57:19 +02:00
Lauri Ojansivu f22785dbcd - Fix adding comments.
- Added some working layout changes from https://github.com/wekan/wekan/pull/2920.
- Fixed Card Settings not working at Sandstorm.

Thanks to 2020product xet7 !

Closes #2918,
closes https://github.com/wekan/wekan/pull/2920
2020-02-05 19:47:08 +02:00
Lauri Ojansivu 5848e7fe29 Update translations. 2020-02-05 17:26:05 +02:00
Lauri Ojansivu 4c5398f643 Merge branch 'master' of github.com:wekan/wekan 2020-02-05 02:48:48 +02:00
Lauri Ojansivu 45ecd2d04f v3.74
And update translations.
2020-02-05 02:48:13 +02:00
Lauri Ojansivu 132ddfa931 Update translations. 2020-02-05 02:46:27 +02:00
Lauri Ojansivu b7f9dc77d1 Update translations. 2020-02-05 01:45:16 +02:00
Lauri Ojansivu b68557a293 Remove duplicate function. 2020-02-05 01:44:29 +02:00
Lauri Ojansivu bc6e78608b Merge branch '2020product-dollybean' 2020-02-05 01:34:28 +02:00
Lauri Ojansivu 1350694538 Add Board Card Settings to Show on Card only some of it's fields.
Default WYSIWYG comment editor not enabled, use markdown instead.
2020-02-05 01:18:01 +02:00
Lauri Ojansivu 65fa2f626f Merge branch 'dollybean' of https://github.com/2020product/wekan into 2020product-dollybean 2020-02-05 01:17:15 +02:00
dollybean 0b00a8095c Customize of some card's functions 2020-02-04 02:28:45 -08:00
Lauri Ojansivu 58df99d5be Update translations. 2020-01-31 19:50:09 +02:00
Lauri Ojansivu e37bbb9e44 Update package versions. 2020-01-31 19:48:21 +02:00
Lauri Ojansivu 85d47c4ff2 v3.73 2020-01-29 14:57:41 +02:00
Lauri Ojansivu 928973e310 Update translations. 2020-01-29 14:46:01 +02:00
Lauri Ojansivu 9e93540970 Update ChangeLog. 2020-01-29 14:41:38 +02:00
Lauri Ojansivu ecf76f2c61 Merge branch 'master' of github.com:wekan/wekan 2020-01-29 14:39:52 +02:00
Lauri Ojansivu 79223b0b8c Merge branch 'kuleuven-feature-action-move-other-board' 2020-01-29 14:39:29 +02:00
Lauri Ojansivu 5e6469bddc Add info how to install Meteor on Windows. 2020-01-29 01:40:16 +02:00
Lauri Ojansivu 2216cc1b07 Merge branch 'feature-action-move-other-board' of https://github.com/kuleuven/wekan into kuleuven-feature-action-move-other-board 2020-01-23 17:24:27 +02:00
Lauri Ojansivu f275eb5e47 Update ChangeLog. 2020-01-23 16:20:50 +02:00
Peter Verraedt 4b56bbfe6d Add rule action to move cards to other boards
Fixes #1996
2020-01-23 15:12:18 +01:00
Lauri Ojansivu d8c09369eb Merge branch 'izadpoor-hotfix-export-board' 2020-01-23 16:10:27 +02:00
Lauri Ojansivu 6ef532d187 Merge branch 'hotfix-export-board' of https://github.com/izadpoor/wekan into izadpoor-hotfix-export-board 2020-01-23 15:11:21 +02:00
Lauri Ojansivu c610ba0976 Update ChangeLog. 2020-01-23 15:07:33 +02:00
Lauri Ojansivu 362b6432a9 Merge pull request #2897 from bogie/oauth2-nc
Implemented Nextcloud OAuth2 Hack References Issue #1874
2020-01-23 15:00:17 +02:00
izadpoor f868b3d1f9 fixed board export with attchment 2020-01-23 01:16:56 -05:00
Bojan Hartmann 7bba07ccbf Implemented Nextcloud OAuth2 Hack
Fixed: OAuth2 authentication via Nextcloud(tested Nextcloud 17.0.2-18.0.0)
Todo: actually use the profile data: Fullname and Email in Profile
2020-01-22 22:28:03 +01:00
Lauri Ojansivu 70f5326099 Update translations. 2020-01-22 08:06:26 +02:00
Lauri Ojansivu a64204a0c4 Update ChangeLog. 2020-01-20 16:53:38 +02:00
Lauri Ojansivu ea73d9a424 Merge pull request #2891 from tsia/master
Show System Wide Announcement in one line
2020-01-20 16:50:26 +02:00
tsia 3e2415631f Update header.styl 2020-01-20 11:23:08 +01:00
Lauri Ojansivu f425fbfcdf v3.72 2020-01-19 15:29:28 +02:00
Lauri Ojansivu a0e9f4615c v3.71 2020-01-18 20:23:10 +02:00
Lauri Ojansivu 5e5ab95410 Try to fix Wekan at Sandstorm by using Meteor 1.8.x and Node 8.17.0 at Sandstorm.
Thanks to xet7 !
2020-01-18 20:19:20 +02:00
Lauri Ojansivu d7663316f1 v3.70 API docs. 2020-01-18 16:53:27 +02:00
Lauri Ojansivu a0b6b6dac7 v3.70 2020-01-18 16:51:26 +02:00
Lauri Ojansivu 62328c2d2a Merge branch 'master' of github.com:wekan/wekan 2020-01-18 16:42:07 +02:00
Lauri Ojansivu 48f8050c25 Add Snap Meteor 1.8.x files to lint ignore files. 2020-01-18 16:41:23 +02:00
Lauri Ojansivu 135b770400 Add Snap Meteor 1.8.x files to ignore lists. 2020-01-18 16:40:49 +02:00
Lauri Ojansivu f20c2e4565 Update translations. 2020-01-18 16:39:47 +02:00
Lauri Ojansivu 9b905c2833 Try to fix Node 12 Buffer() deprecation errors.
Thanks to xet7 !
2020-01-18 16:01:02 +02:00
Lauri Ojansivu 7efbf30af6 Update translations. 2020-01-17 19:17:43 +02:00
Lauri Ojansivu d9778c1220 Update translations. 2020-01-16 07:57:35 +02:00
Lauri Ojansivu 10f142a1a0 Add LD_LIBRARY_PATH. 2020-01-16 06:49:16 +02:00
Lauri Ojansivu 6a01170d86 Use Meteor 1.8.x on Snap. 2020-01-16 06:15:10 +02:00
Lauri Ojansivu 55a2aa90cb Use Meteor 1.8.x on Snap. 2020-01-16 06:07:22 +02:00
Lauri Ojansivu feafa1827a Try to fix Snap. 2020-01-16 05:52:11 +02:00
Lauri Ojansivu 116e63aa03 Try to fix Snap. 2020-01-14 20:04:47 +02:00
Lauri Ojansivu 1159199e90 Try to fix Snap. 2020-01-14 19:43:25 +02:00
Lauri Ojansivu 0009c4c650 Try to fix Snap. 2020-01-14 12:29:05 +02:00
Lauri Ojansivu b591727b64 Try to fix Snap. 2020-01-14 11:17:23 +02:00
Lauri Ojansivu 218b44d476 Try to fix Snap. 2020-01-14 11:16:43 +02:00
Lauri Ojansivu cc020485c1 Try to fix Snap. 2020-01-12 22:37:37 +02:00
Lauri Ojansivu fc6149d232 Try to fix Snap. 2020-01-12 15:47:01 +02:00
Lauri Ojansivu 8090b6f701 Try to fix Snap. 2020-01-11 23:29:23 +02:00
Lauri Ojansivu 7fc2f2e2e5 Try to fix Snap. 2020-01-11 23:23:27 +02:00
Lauri Ojansivu 76c7fa67d8 Try to fix Snap. 2020-01-11 21:41:09 +02:00
Lauri Ojansivu 4d73eccaa8 Try to fix Snap. 2020-01-11 20:47:26 +02:00
Lauri Ojansivu 634706ca6a Try to fix Snap. 2020-01-11 20:44:46 +02:00
Lauri Ojansivu 88ef717793 Try to fix Snap. 2020-01-11 20:19:23 +02:00
Lauri Ojansivu 870dfc4489 Try to fix snap. 2020-01-11 13:35:27 +02:00
Lauri Ojansivu e01f4dbf13 Change Buffer to Buffer.alloc on Node v12. Try to fix Snap. 2020-01-11 13:08:29 +02:00
Lauri Ojansivu 5bece0dd1e Fix prettier. 2020-01-10 04:09:40 +02:00
Lauri Ojansivu 83e4d214cd Update Travis. 2020-01-10 04:06:54 +02:00
Lauri Ojansivu 891fd60a6c v3.69 2020-01-10 04:03:41 +02:00
Lauri Ojansivu ecb76842fc Fix docker-compose.yml 2020-01-10 03:59:33 +02:00
Lauri Ojansivu da8ae71963 v3.68 2020-01-10 03:57:19 +02:00
Lauri Ojansivu 031df54a2e Try to fix Snap.
Thanks to xet7 !
2020-01-10 03:51:57 +02:00
Lauri Ojansivu 6849bb8af9 v3.67 2020-01-10 02:54:10 +02:00
Lauri Ojansivu 2b382b940b Try to fix Snap. 2020-01-10 02:49:38 +02:00
Lauri Ojansivu 8a64531ad9 v3.66 2020-01-10 01:35:51 +02:00
Lauri Ojansivu 39bf1e375e Try to fix Snap. 2020-01-10 01:31:48 +02:00
Lauri Ojansivu b3e1b8683c Update translations. 2020-01-10 00:18:50 +02:00
Lauri Ojansivu be96f16069 v3.65 2020-01-10 00:16:52 +02:00
Lauri Ojansivu 785f3cf88b Upgrade to Meteor 1.9, Node 12.14.1 and MongoDB 4.2.2.
Thanks to xet7 !
2020-01-10 00:04:43 +02:00
Lauri Ojansivu d16a601c04 More keyboard shortcuts: c for archive card
Thanks to xet7 !

Related #1878
2020-01-09 23:22:27 +02:00
Lauri Ojansivu 5724674e73 Add more issue repo links to GitHub issue template. 2020-01-08 22:09:08 +02:00
Lauri Ojansivu ba0068598c v3.64 2020-01-06 20:33:01 +02:00
Lauri Ojansivu 8f16c8bf8e Update translations. 2020-01-06 20:27:38 +02:00
Lauri Ojansivu 29726453a0 Update ChangeLog. 2020-01-06 20:19:56 +02:00
Lauri Ojansivu 5058233509 Wider sidebar.
Thanks to vjrj !

Closes #2218
2020-01-06 20:12:35 +02:00
Lauri Ojansivu 8442e1e83b Fix typo. 2020-01-06 11:20:11 +02:00
Lauri Ojansivu ef0689baf7 Update ChangeLog. 2020-01-06 11:04:58 +02:00
Lauri Ojansivu ddce0ada09 Removed Custom HTML feature that does not work.
Thanks to xet7 !

Closes #2218
2020-01-06 11:01:38 +02:00
Lauri Ojansivu 60df1fa1ee v3.63 2020-01-06 10:14:00 +02:00
Lauri Ojansivu 8e93912389 Update translations. 2020-01-06 10:09:14 +02:00
Lauri Ojansivu 064e4a2cf9 Update ChangeLog. 2020-01-06 10:07:02 +02:00
Lauri Ojansivu 8ce9939217 Fix: Unable to find Archive Card/List/Swimlane in board settings.
Thanks to neobradley and xet7 !

Closes #2872
2020-01-06 10:02:37 +02:00
Lauri Ojansivu 41a28df430 v3.62 2020-01-05 21:43:44 +02:00
Lauri Ojansivu c36a74b956 Update ChangeLog. 2020-01-05 21:36:28 +02:00
Lauri Ojansivu f6f7705f23 Add Worker role.
This was originally added at Wekan v3.58,
reverted at Wekan v3.60 because of bugs,
and now after fixes added back.

Thanks to xet7 !

Closes #2788
2020-01-05 21:28:14 +02:00
Lauri Ojansivu 6209b792aa Update translations. 2020-01-05 19:48:11 +02:00
Lauri Ojansivu f8873b009b Fix typo. 2020-01-03 17:20:04 +02:00
Lauri Ojansivu 1a1f610977 v3.61 2020-01-03 17:13:56 +02:00
Lauri Ojansivu 3d7871ebfc Update ChangeLog. 2020-01-03 17:09:49 +02:00
Lauri Ojansivu cd253522a3 Add more Font Awesome icons. This was originally added
at Wekan v3.58, removed at Wekan v3.60, and now
added back at Wekan v3.61.

Thanks to xet7 !
2020-01-03 17:02:27 +02:00
Lauri Ojansivu cc70773fd5 v3.60 2020-01-03 16:10:17 +02:00
Lauri Ojansivu 27943796ad Revert to Wekan v3.57 version of client and models directories,
removing Worker role temporarily, because Worker role changes
broke saving card.

Thanks to xet7 !
2020-01-03 16:02:31 +02:00
Lauri Ojansivu 9b28847592 v3.59 2020-01-03 13:34:55 +02:00
Lauri Ojansivu 5376bc7b79 Fix not being able to edit received date.
Thanks to xet7 !
2020-01-03 13:27:27 +02:00
Lauri Ojansivu 19d42c675b v3.58 2020-01-03 07:07:12 +02:00
Lauri Ojansivu be3e4af643 Fix typos. 2020-01-03 07:01:39 +02:00
Lauri Ojansivu 96b3352b2b Update translations. 2020-01-03 06:57:10 +02:00
Lauri Ojansivu 62408fcbf7 Update ChangeLog. 2020-01-03 06:54:39 +02:00
Lauri Ojansivu 2bf004120d Add Worker role.
Add more Font Awesome icons.
Fix browser console errors when editing user profile name etc.

Thanks to xet7 !

Closes #2788
2020-01-03 06:49:35 +02:00
Lauri Ojansivu 0709b5abc8 Update ChangeLog. 2020-01-02 14:07:35 +02:00
Lauri Ojansivu ac43b4c1e6 Merge pull request #2867 from jiangytcn/helm
fix: k8s templates update for helm
2020-01-02 14:02:43 +02:00
Jiang Yitao 5aeb88ee6c fix: k8s templates update for helm
Why:

This change addresses the need by:

1. upgrade mongo replica version
2. access mongo via service url
3. change the expose servicePort to numeric

Signed-off-by: Jiang Yitao <jiangyt.cn@gmail.com>
2020-01-02 11:52:34 +08:00
Lauri Ojansivu c25162aac7 Update translations. 2019-12-31 21:46:23 +02:00
Lauri Ojansivu da6df62042 Update translations. 2019-12-31 21:40:32 +02:00
Lauri Ojansivu 458b9eddb2 Update translations. 2019-12-30 18:25:42 +02:00
Lauri Ojansivu 7af7fe6146 v3.57 2019-12-22 10:51:05 +02:00
Lauri Ojansivu 340d7c731f Update translations. 2019-12-22 10:09:23 +02:00
Lauri Ojansivu e142acfdb7 Remove LINKED_CARDS_ENABLED settings, because it does not work.
Thanks to xet7 !
2019-12-22 09:44:58 +02:00
Lauri Ojansivu e928660bc0 LINKED_CARDS_ENABLED settings part 3.
In Progress, linked cards not completely disabled yet.

Thanks to xet7 !
2019-12-21 19:38:02 +02:00
Lauri Ojansivu 7a90e89348 Update ChangeLog. 2019-12-20 17:07:52 +02:00
Lauri Ojansivu 1f1aea87a4 Visual difference for inactive user in Administration: strikethrough.
Thanks to hever and xet7 !

Closes #2860
2019-12-20 17:04:33 +02:00
Lauri Ojansivu e9b7c4cdde Update ChangeLog. 2019-12-20 16:35:35 +02:00
Lauri Ojansivu 1bf7f95bd8 LINKED_CARDS_ENABLED settings part 2.
Thanks to xet7 !
2019-12-20 16:32:38 +02:00
Lauri Ojansivu 2a065825fa Update ChangeLog. 2019-12-20 16:18:32 +02:00
Lauri Ojansivu 492ed2bdc2 Merge pull request #2834 from Akuket/master
Issue #2688
2019-12-20 16:12:29 +02:00
Lauri Ojansivu afe7d4991d Merge branch 'Robert-Lebedeu-master' 2019-12-20 16:10:26 +02:00
Lauri Ojansivu 0649add494 Merge branch 'master' of https://github.com/Robert-Lebedeu/wekan into Robert-Lebedeu-master 2019-12-20 16:09:48 +02:00
Lauri Ojansivu 2dafde8db9 Update ChangeLog. 2019-12-20 16:06:12 +02:00
Lauri Ojansivu 782d0b6209 Upgrade to Meteor 1.8.3 and Node 8.17.0.
Update release scripts.
Fix ldap background sync documentation part 2.

Thanks to xet7 !
2019-12-20 16:03:46 +02:00
Lauri Ojansivu 8dcd360f93 Merge branch 'master' of github.com:wekan/wekan 2019-12-20 15:36:48 +02:00
Lauri Ojansivu ea1af474fd Update translations. 2019-12-20 15:36:30 +02:00
Lauri Ojansivu e3d0aece14 Merge pull request #2855 from koelle25/fix-ldap-background-sync-documentation
Fix ldap background sync documentation
2019-12-20 15:00:31 +02:00
Kevin Köllmann 75dae5c714 Made the link https and moved default value into setting 2019-12-18 15:32:17 +01:00
Kevin Köllmann f96bba7a63 Update the LDAP_BACKGROUND_SYNC_INTERVAL setting documentation 2019-12-18 15:12:36 +01:00
Lauri Ojansivu 3b3950369c Fix comment text disappearing when clicking outside of comment text area.
Fix lint error.

Thanks to xet7 !
2019-12-17 23:42:30 +02:00
Robert Lebedeu a35df88805 Allow checklist creation for board members
- Only for members with checklist add permission
2019-12-17 12:15:41 +01:00
Robert Lebedeu 40c70c439d Allow card creation for board members
- Only for members with card add permission
2019-12-17 12:15:06 +01:00
Robert Lebedeu 2c4d3fa317 Fix checkBoardAccess authentication check 2019-12-16 18:10:48 +01:00
Lauri Ojansivu a4dbe98299 Update translations. 2019-12-15 08:05:10 +02:00
Lauri Ojansivu 98602dabb0 Update translations. 2019-12-14 14:37:55 +02:00
Lauri Ojansivu 6ae5f0cea5 Update translations. 2019-12-13 22:20:00 +02:00
Lauri Ojansivu 0316802b84 Update ChangeLog. 2019-12-13 00:41:39 +02:00
Lauri Ojansivu 25561946ed Fix: Allow to set empty card title, AssignedBy and RequestedBy.
Thanks to xet7 !
2019-12-13 00:37:21 +02:00
Lauri Ojansivu c29aa76d02 Update translations. 2019-12-12 18:48:24 +02:00
Lauri Ojansivu 258af8bfed Update ChangeLog. 2019-12-12 18:45:18 +02:00
Lauri Ojansivu e2a374f0aa Fix: Don't add a blank space for empty custom fields on minicards.
Thanks to roobre and xet7 !

Closes #2850
2019-12-12 18:41:22 +02:00
Lauri Ojansivu 6a53aaf24a Update translations. 2019-12-07 15:36:06 +02:00
Lauri Ojansivu b2eccc7b54 Back to Meteor 1.8.x. 2019-12-05 14:31:28 +02:00
Lauri Ojansivu c2fa23d525 Snap back to MongoDB 3.2.22 2019-12-05 13:58:14 +02:00
Lauri Ojansivu e6fa02e0a1 Use libcurl3 instead. 2019-12-05 12:00:51 +02:00
Lauri Ojansivu e164aafbd8 Add libcurl4 to snap. 2019-12-05 11:34:10 +02:00
Lauri Ojansivu a863194e74 Make travis to wait Wekan to build.
Thanks to xet7 !
2019-12-05 11:23:40 +02:00
Lauri Ojansivu f518397f7a Merge branch 'master' of github.com:wekan/wekan 2019-12-05 10:35:46 +02:00
Lauri Ojansivu fc91d866db Try to fix Travis. 2019-12-05 10:35:28 +02:00
Lauri Ojansivu 8670561f78 Create dockerimage.yml 2019-12-05 10:31:32 +02:00
Lauri Ojansivu d8474056d2 Update Travis docker-compose version. 2019-12-05 10:22:51 +02:00
Lauri Ojansivu e48c5fb066 Update ChangeLog. 2019-12-05 10:04:23 +02:00
Lauri Ojansivu 467a3be850 Update to Meteor 1.9-beta.4, Node.js 12.13.1 and MongoDB 4.2.1.
Remove MongoDB option --smallfiles that is not supported in MongoDB 4.2.1.
Add MongoDB option --quiet to docker-compose.yml.
Update snap fibers to 4.0.1.

Thanks to xet7 !
2019-12-05 10:00:07 +02:00
Lauri Ojansivu 1b12d3f04f Update translations. 2019-12-05 07:03:59 +02:00
Guillaume c77979ff13 Merge branch 'master' of https://github.com/wekan/wekan 2019-11-29 14:57:33 +01:00
Guillaume 0a1e7006b3 Using LINKED_CARDS_ENABLED 2019-11-29 14:48:44 +01:00
Lauri Ojansivu d5a8ec1c36 Remove 1st implementation of collapse swimlanes. I will do it
differently in next 2nd implementation.

Thanks to xet7 !
2019-11-29 03:25:52 +02:00
Lauri Ojansivu d497b75c1d Update translations. 2019-11-28 15:46:39 +02:00
Akuket 07724f99ae Add LINKED_CARDS_ENABLED env var 2019-11-28 11:55:36 +01:00
Lauri Ojansivu 8898862d36 Fix lint errors. 2019-11-26 02:29:36 +02:00
Lauri Ojansivu 2cc3507740 Update translations. 2019-11-26 01:26:18 +02:00
Lauri Ojansivu 3565437728 v3.56 2019-11-21 23:03:09 +02:00
Lauri Ojansivu 15ba982810 Update ChangeLog. 2019-11-21 22:54:09 +02:00
Lauri Ojansivu 488a895850 Update translations. 2019-11-21 22:49:07 +02:00
Lauri Ojansivu c7ded51502 Add to snap Snap MongoDB logging option --quiet .
Thanks to fmeehan and xet7 !

Closes wekan/wekan-snap#113
2019-11-21 22:21:05 +02:00
Lauri Ojansivu c66cc3d4da Change base image eoan to rolling, that is probably always newest Ubuntu
rolling version.

Thanks to xet7 !
2019-11-21 20:44:46 +02:00
Lauri Ojansivu c60967e935 It seems Ubuntu eoan package bsdtar has been renamed to libarchive-tools.
Thanks to xet7 !
2019-11-21 20:42:28 +02:00
Lauri Ojansivu 343343e7e7 Dockerfile: Use eoan base image. Add missing python3 dependency for node-gyp.
Thanks to xet7 !
2019-11-21 20:22:37 +02:00
Lauri Ojansivu 4f5de87cc4 Fix typo. 2019-11-20 23:49:43 +02:00
Lauri Ojansivu f371cbd5d2 Update translations. 2019-11-20 23:49:08 +02:00
Lauri Ojansivu 99ef6e3a20 Update ChangeLog. 2019-11-20 23:47:25 +02:00
Lauri Ojansivu 599ace1db7 Fix slow scroll on card detail by setting scrollInertia to 0.
Thanks to cafeoh !

Closes #2179
2019-11-20 23:40:04 +02:00
Lauri Ojansivu a06daff92e Remove eslint option that does not work.
Thanks to xet7 !
2019-11-20 23:39:11 +02:00
Lauri Ojansivu 58e505f79a Try to fix lint errors. 2019-11-20 21:28:48 +02:00
Lauri Ojansivu 788dd0a81a Fix lint errors. 2019-11-20 21:10:11 +02:00
Lauri Ojansivu 82d76e55e5 Add babel-eslint. 2019-11-20 20:22:30 +02:00
Lauri Ojansivu 38dfe0b9a7 Update to Meteor 1.8.2. Update dependencies.
Thanks to xet7 !
2019-11-20 19:57:16 +02:00
Lauri Ojansivu b0f345ba21 Fix lint errors and update travis NPM version. 2019-11-20 10:14:53 +02:00
Lauri Ojansivu 95a4592346 Update Wekan API docs. 2019-11-19 23:08:29 +02:00
Lauri Ojansivu 3168c4067d v3.55 2019-11-19 21:59:09 +02:00
Lauri Ojansivu 115d23f929 Use database when logged in. Continued.
Thanks to xet7 !
2019-11-19 21:55:43 +02:00
Lauri Ojansivu 975258ef39 Update translations. 2019-11-19 21:52:09 +02:00
Lauri Ojansivu b13d28203b Update ChangeLog. 2019-11-19 21:45:12 +02:00
Lauri Ojansivu 4786b0c18d Use database when logged in. Continued.
Thanks to xet7 !
2019-11-19 21:42:00 +02:00
Lauri Ojansivu bc9512b750 Update ChangeLog. 2019-11-19 14:17:27 +02:00
Lauri Ojansivu abb31cb603 Update translations. 2019-11-19 14:14:49 +02:00
Lauri Ojansivu 351d4767d7 When logged in, use database for setting, so that changes are
immediate. Only on public board use cookies.
Comment out Collapse CSS that is not in use.

Thanks to xet7 !
2019-11-19 14:09:36 +02:00
Lauri Ojansivu f595120e72 v3.54 2019-11-18 23:06:10 +02:00
Lauri Ojansivu 3f2c3c1af9 Even more thanks. 2019-11-18 22:36:31 +02:00
Lauri Ojansivu 69f2e8e74d Update ChangeLog. 2019-11-18 22:35:14 +02:00
Lauri Ojansivu 72553c1f14 Update translations. 2019-11-18 22:26:20 +02:00
Lauri Ojansivu 96abe3c691 New feature: Now there is popup selection of Lists/Swimlanes/Calendar/Roles.
New feature, not set visible yet, because switching to it does not
work properly yet: Collapsible Swimlanes #2804

Fix: Public board now loads correctly. When you select one of Lists/Swimlanes/Calendar view and
reload webbrowser page, it can change view. Closes #2311

Fix: List sorting commented out. Closes #2800

Fix: Errors hasHiddenMinicardText, hasShowDragHandles, showSort, hasSortBy, profile,
FirefoxAndroid/IE11/Vivaldi/Chromium browsers not working by using
cookies instead of database.
More details at https://github.com/wekan/wekan/issues/2643#issuecomment-554907955
Note: Cookie changes are not always immediate, if there is no effect,
you may need to reload webbrowser page.
Closes #2643 .

Thanks to xet7 !
2019-11-18 22:23:49 +02:00
Lauri Ojansivu 2079a5bfa3 Update translations. 2019-11-18 21:13:20 +02:00
Lauri Ojansivu 34bfb09c85 Fix prettier. 2019-11-17 14:03:39 +02:00
Lauri Ojansivu f652b677d0 Fix prettier. 2019-11-17 01:59:57 +02:00
Lauri Ojansivu c93ea33d05 Fix prettify. 2019-11-17 01:36:33 +02:00
Lauri Ojansivu 26e0bbce17 Swimlanes collapsed by default.
TODO:
- Add count.
- Move list names to top, if possible. I did not get it working yet.
- Try to fit collapse+swimlane name etc at same row.

Related #2804
2019-11-17 01:20:26 +02:00
Lauri Ojansivu a636611471 Remove swimlane handle at desktop non-handle mode. 2019-11-17 00:57:50 +02:00
Lauri Ojansivu 9313979db7 v3.53 2019-11-14 21:43:01 +02:00
Lauri Ojansivu 1c4f93a2ce Update translations. 2019-11-14 21:37:46 +02:00
Lauri Ojansivu a34f13aa2a Update ChangeLog. 2019-11-14 21:35:43 +02:00
Lauri Ojansivu ab2a721a14 Revert list sorting change of Wekan v3.51 because it reversed
alphabetical sorting of lists.

Thanks to Dalisay and xet7 !
2019-11-14 21:31:38 +02:00
Lauri Ojansivu 16cc4d7cbd v3.52 2019-11-14 03:08:46 +02:00
Lauri Ojansivu 730352a48d Update changelog. 2019-11-14 03:03:05 +02:00
Lauri Ojansivu 5b41d72e8d Add database migration for assignee.
Thanks to ocdtrekkie and xet7 !
2019-11-14 03:00:14 +02:00
Lauri Ojansivu 33b8952c0b v3.51 2019-11-14 01:03:26 +02:00
Lauri Ojansivu 12483350a1 Update ChangeLog. 2019-11-14 00:58:36 +02:00
Lauri Ojansivu bbc3ab3f99 Change sorting to work on desktop drag handle page instead,
where it seems to work better.

Thanks to xet7 !
2019-11-14 00:55:11 +02:00
Lauri Ojansivu a9e53ec088 v3.50 2019-11-13 21:57:45 +02:00
Lauri Ojansivu 704808a6cb Update translations. 2019-11-13 21:56:53 +02:00
Lauri Ojansivu 5b2f439500 Update API docs. 2019-11-13 21:50:19 +02:00
Lauri Ojansivu 688bc063e5 Update ChangeLog. 2019-11-13 21:48:33 +02:00
Lauri Ojansivu 1eb3d25b40 Update Node.js to v8.16.2.
Thanks to xet7 !
2019-11-13 21:46:33 +02:00
Lauri Ojansivu 7206e2528f Update ChangeLog. 2019-11-13 21:28:17 +02:00
Lauri Ojansivu e1029816df Fix typo. 2019-11-13 21:26:06 +02:00
Lauri Ojansivu 639c95bade Fix typo. 2019-11-13 21:25:15 +02:00
Lauri Ojansivu 0907936d2b Update ChangeLog. 2019-11-13 19:44:18 +02:00
Lauri Ojansivu cdc988c7b1 Merge branch 'master' of github.com:wekan/wekan 2019-11-13 19:41:35 +02:00
Lauri Ojansivu 274a997e62 Fix card, list and swimlane move.
Allow moving cards in multiselect mode.

Closes #2771,
closes #2743,
closes #2704,
related #2081
2019-11-13 19:40:48 +02:00
Lauri Ojansivu 537a48bede Fix card, list and swimlane move.
Closes #2771,
closes #2743,
closes #2704,
related #2081
2019-11-13 19:33:13 +02:00
Lauri Ojansivu 82f0924914 Update translations. 2019-11-13 15:34:29 +02:00
Lauri Ojansivu 7214be488b Close card button now visible on mobile.
Thanks to xet7 !

Closes #2261,
closes #2637
2019-11-08 00:32:10 +02:00
Lauri Ojansivu 36b5965dd0 Close card button now visible on mobile.
Closes #2261
2019-11-08 00:21:04 +02:00
Lauri Ojansivu 3a00a59ffb Update translations. 2019-11-07 23:38:09 +02:00
Lauri Ojansivu 6b855ac045 Update ChangeLog:
More mobile drag handles, and optional desktop drag handles, In Progress.

Thanks to xet7 !

Related #2081
2019-11-07 23:19:06 +02:00
Lauri Ojansivu 9bba0ea283 Done: Assignee field like Jira.
Thanks to xet7 !

Closes #2452
2019-11-07 23:05:44 +02:00
Lauri Ojansivu de7509dc60 Assignee field like Jira #2452 , Done.
Update REST API docs, there can only be one assignee in array.

Thanks to xet7 !
2019-11-07 22:46:55 +02:00
Lauri Ojansivu 9fd14f7ecb Assignee field like Jira #2452 , in progress.
Now assignee is visible also at minicard.

Thanks to xet7 !
2019-11-07 06:04:23 +02:00
Lauri Ojansivu 3cf09efb13 Assignee field like Jira #2452 , in progress.
When there is one selected assignee on card, don't show + button
for adding more assignees, because there can only be one assignee.

Thanks to xet7 !
2019-11-07 00:14:50 +02:00
Lauri Ojansivu 22083787f9 Try to fix prettify. 2019-11-07 00:04:05 +02:00
Lauri Ojansivu 1728298659 Assignee field like Jira #2452 , in progress.
Prevent more than one assignee.

Thanks to xet7 !
2019-11-06 23:36:43 +02:00
Lauri Ojansivu 865c8305cb Update translations. 2019-11-05 23:58:11 +02:00
Lauri Ojansivu 31dbdc835d Assignee field like Jira #2452 , in progress.
Add assignee popup title.

Thanks to xet7 !
2019-11-05 23:53:02 +02:00
Lauri Ojansivu 56efb5c410 Assignee field like Jira #2452 , in progress.
In add assignee popup, avatars are now visible.

Thanks to xet7 !
2019-11-05 23:42:22 +02:00
Lauri Ojansivu ebdb3a0cc3 Fix prettier. 2019-11-05 23:26:07 +02:00
Lauri Ojansivu 473af2f5e2 Update API docs. 2019-11-05 22:10:01 +02:00
Lauri Ojansivu 1d2ab299b0 Update translations. 2019-11-05 22:03:03 +02:00
Lauri Ojansivu f2eda9d7ed Update ChangeLog. 2019-11-05 16:35:52 +02:00
Lauri Ojansivu 52129ed714 Merge pull request #2782 from jymcheong/master
fixed #2780
2019-11-05 16:30:51 +02:00
Lauri Ojansivu e868f9dcee Merge pull request #2783 from bentiss/openapi_gen
Openapi gen fixes
2019-11-05 16:30:14 +02:00
Benjamin Tissoires 3f059ec1e0 generate_openapi: fix enums when they are declared as const
Fixes: #2781
2019-11-05 12:58:06 +01:00
Benjamin Tissoires 8cb3974eff generate_openapi: print a more useful error
...when we can not parse a SchemaProperty
2019-11-05 12:54:58 +01:00
Benjamin Tissoires 7d62d0920c generate_openapi: add a little bit more verbosity when we get an Error 2019-11-05 12:01:04 +01:00
Benjamin Tissoires b787854289 generate_openapi.py: use the logging module
Instead of dealing with custom writes to stderr, it's always better
to rely on standard libraries.
2019-11-05 11:06:40 +01:00
jymcheong d84ea7d16f fixed #2780
https://github.com/wekan/wekan/issues/2780
2019-11-05 11:04:35 +08:00
Lauri Ojansivu 7318e42080 Comment out already existing directory. 2019-11-04 22:53:10 +02:00
Lauri Ojansivu 9f11ea0cc6 Update dependencies. 2019-11-04 22:50:29 +02:00
Lauri Ojansivu 2ec15602d2 Fix desktop swimlane drag handle position.
Thanks to xet7 !

Related #2081
2019-11-04 11:13:10 +02:00
Lauri Ojansivu 6a89605477 Some drag handle fixes.
Thanks to xet7 !

Related #2081
2019-11-04 10:52:52 +02:00
Lauri Ojansivu dff261efee Update translations. 2019-11-04 10:07:51 +02:00
Lauri Ojansivu 931f0d508a Update ChangeLog. 2019-11-04 10:06:13 +02:00
Lauri Ojansivu ea823ab68f Assignee field like Jira #2452 , in progress. 2019-11-04 10:00:28 +02:00
Lauri Ojansivu b56ddf9df9 Fix prettier 2019-11-04 09:59:40 +02:00
Lauri Ojansivu 9a468dfce5 Merge pull request #2779 from bandresen/cardDate_receivedAtfix_and_endDatechange
Card date received-date fix and end-date coloring change (simplify)
2019-11-04 09:52:31 +02:00
Lauri Ojansivu db3476c8ec Merge pull request #2778 from liske/fixes/rest-deletes-all-cards-2624
REST API: fix deletion of a single board card (closes wekan#2624)
2019-11-04 09:51:14 +02:00
Benjamin Andresen 7a5401d5f0 cardDate: endDate coloring change
if no due-date timestamp is set => Gray
if end-date timestamp is younger than due-date timestamp => Green
if end-date timestamp is older than due-date timestamp => Red

resolves #2741
2019-11-04 08:10:11 +01:00
Benjamin Andresen d3ca816762 cardDate: fix ReceivedDate startAt coloring 2019-11-04 08:08:51 +01:00
Thomas Liske b7492e4a11 REST API: fix deletion of a single board card (closes wekan#2624) 2019-11-04 06:08:40 +01:00
Lauri Ojansivu 32ce2b51d8 Assignee field like Jira #2452 , in progress.
Added features:
- When selecting new assignee (+) icon, list shows names who to add.

TODO:
- When selecting new assignee (+) icon, list does not yet show avatars who to add.

Thanks to xet7 !
2019-11-02 16:38:16 +02:00
Lauri Ojansivu e033959278 Update translations. 2019-11-02 16:18:55 +02:00
Lauri Ojansivu 3e8f9ef1a5 Assignee field like Jira #2452 , in progress.
Added features:
- Assignee can now be added and removed.
- Avatar icon is at card and assignee details

TODO:
- When selecting new assignee (+) icon, list does not yet show avatars and names who to add.
  There is empty avatar without name.

Thanks to xet7 !
2019-11-02 16:12:40 +02:00
Lauri Ojansivu 92efb8bec4 Update translations. 2019-10-31 17:08:23 +02:00
Lauri Ojansivu 9e1aaf163f Assignee field like Jira #2452 , in progress.
Assignee can not be removed yet, it removes member, wrong link in popup.

Thanks to xet7 !
2019-10-31 02:21:50 +02:00
Lauri Ojansivu 3f19091a91 Update ChangeLog. 2019-10-30 21:18:11 +02:00
Lauri Ojansivu ae6eb706c7 Update ChangeLog. 2019-10-30 21:14:23 +02:00
Lauri Ojansivu 806df30ba3 Revert creating new list to left, now creates again to right. Thanks to whowillcare !
Revert New List item moved from right to left. Thanks to derbolle and xet7 !

Closes #2772
2019-10-30 20:45:30 +02:00
Lauri Ojansivu 6a88c2f289 Merge branch 'whowillcare-master' 2019-10-30 20:38:52 +02:00
Lauri Ojansivu acc6bc0758 Merge branch 'master' of https://github.com/whowillcare/wekan into whowillcare-master 2019-10-30 20:33:53 +02:00
Lauri Ojansivu f3bc92904c Change list sorting to be visible only at DesktopDragHandle mode. 2019-10-30 18:48:54 +02:00
Sam X. Chen 3308d90a3a Fix: List last modify time will be affected by cards dueAt, endAt 2019-10-30 12:26:39 -04:00
Lauri Ojansivu 5bb99ba096 Add back miniscreen add list style. 2019-10-30 18:09:56 +02:00
Lauri Ojansivu 6ac7523d9d Update translations. 2019-10-30 17:45:24 +02:00
Lauri Ojansivu 758bbab441 Revert New List item moved from right to left.
Thanks to derbolle and xet7 !

Closes #2772
2019-10-30 17:33:02 +02:00
Sam X. Chen b26504f414 Fix: lists filter didn't get added into filter active checklist 2019-10-30 09:45:50 -04:00
Sam X. Chen e195c731de Change the radom to random typo in export.js 2019-10-29 17:45:06 -04:00
Lauri Ojansivu 00d581245c Fix typo on exporting subtasks.
Thanks to xiorcala !

Closes #2770
2019-10-29 22:02:30 +02:00
Lauri Ojansivu 7faff44825 Update translations. 2019-10-29 19:13:00 +02:00
Lauri Ojansivu 6fc9dd1323 Merge pull request #2763 from liske/changes/sensible-card-times
Enhancement: set card times more sensible using the 'Today' button in…
2019-10-29 19:12:35 +02:00
Lauri Ojansivu 7d6d3af54a Add Features: allowing lists to be sorted by modifiedAt when not in draggable mode.
Bug Fix #2093: the broken should be prior to file attachment feature introduced, and tested export board is working.

Thanks to whowillcare !

( xet7 merged this pull request manually from https://github.com/wekan/wekan/pull/2756 )

Closes #2093
2019-10-29 19:05:44 +02:00
Thomas Liske 064c4d7ca7 Lintian fixes. 2019-10-28 08:25:55 +01:00
Thomas Liske 0fb15888bc Enhancement: set card times more sensible using the 'Today' button in datepicker 2019-10-24 13:08:41 +02:00
Sam X. Chen 32f50e1658 Add Feature: allow user to search Lists in Board 2019-10-19 00:28:49 -04:00
Sam X. Chen d2d4840758 Add Feature: allowing user to filter list in Filter function not just cards 2019-10-18 18:06:34 -04:00
Sam X. Chen bc2a20f04e Add Feature: allow user to sort Lists in Board by his own preference, boardadmin can star list 2019-10-18 16:44:09 -04:00
Sam X. Chen 2737d6b23f Bug Fix:2093, need to clean up the temporary file 2019-10-11 11:56:44 -04:00
Sam X. Chen f53c624b0f Buf Fix #2093: the broken should be prior to file attachment feature introduced 2019-10-11 11:01:10 -04:00
Sam X. Chen 77f8b76d4e Add Features: allowing lists to be sorted by modifiedAt when not in draggable mode 2019-10-10 22:57:40 -04:00
Lauri Ojansivu 13a2bd6380 Update package-lock.json 2019-10-09 19:11:32 +03:00
Lauri Ojansivu bec9903f46 v3.49 2019-10-09 18:20:02 +03:00
Lauri Ojansivu 36e006fa4e Fix prettier. 2019-10-09 18:09:14 +03:00
Lauri Ojansivu 5b415c851b v3.48 2019-10-09 17:44:12 +03:00
Lauri Ojansivu 95fb392692 Update ChangeLog. 2019-10-09 17:36:49 +03:00
Lauri Ojansivu f2453834ac Merge pull request #2754 from moserben16/master
Update attachments.js
2019-10-09 17:33:56 +03:00
benji 3064cdd371 Update attachments.js
added else condition to check if MAX_IMAGE_PIXEL is not set, so that it is possible to  upload attachments using drag-and-drop or Ctrl+V without setting the environmental-variable.
If this change is not allowed, please document in the wiki, that this is necessary for a fully-running wekan-instance
2019-10-09 14:24:45 +02:00
Lauri Ojansivu 417fed8605 v3.47 2019-10-09 14:15:12 +03:00
Lauri Ojansivu 4eca27498c Update translations. 2019-10-09 14:01:39 +03:00
Lauri Ojansivu d962ecd77f Update ChangeLog. 2019-10-09 14:00:05 +03:00
Lauri Ojansivu 6feb35c079 Merge pull request #2751 from liske/fixes/rest-card-members
REST API: fix handling of members property on card creation
2019-10-09 13:56:11 +03:00
Thomas Liske ea90ce8784 REST API: fix handling of members property on card creation 2019-10-09 07:23:21 +02:00
Lauri Ojansivu 4a42d683af v3.46 2019-10-07 00:39:52 +03:00
Lauri Ojansivu 8faa48fe12 Update translations. 2019-10-07 00:30:08 +03:00
Lauri Ojansivu 67c4fae32b Update ChangeLog. 2019-10-07 00:28:00 +03:00
Lauri Ojansivu 42d31901b5 Merge pull request #2747 from liske/fixes/rest-create-checklist-2746
REST API: fix creation of Checklists (closes wekan/wekan#2746)
2019-10-07 00:22:49 +03:00
Thomas Liske 72b22a73b6 REST API: fix creation of Checklists (closes wekan/wekan#2746) 2019-10-06 22:53:43 +02:00
Lauri Ojansivu 4d30503cb5 Update translations. 2019-10-05 12:58:46 +03:00
Lauri Ojansivu 7b45a0fbfe Update ChangeLog. 2019-10-05 12:57:17 +03:00
Lauri Ojansivu 42dee10dbe Merge branch 'master' of github.com:wekan/wekan 2019-10-05 12:55:05 +03:00
Lauri Ojansivu 68be12d166 More black minicard badges.
Thanks to sfahrenholz and xet7 !

Closes #2745
2019-10-05 12:54:16 +03:00
Lauri Ojansivu fd896c29b5 More black minicard badges.
Thanks to sfahrenholz and xet7 !
2019-10-05 12:52:57 +03:00
Lauri Ojansivu 98a4be643e Update translation. 2019-10-04 23:01:57 +03:00
Lauri Ojansivu b650a89728 v3.45 2019-10-03 07:51:50 +03:00
Lauri Ojansivu 346dba4969 Update translations. 2019-10-03 07:46:50 +03:00
Lauri Ojansivu f3b858ca21 Revert drag handle changes. 2019-10-03 06:03:11 +03:00
Lauri Ojansivu 5bc355f9a5 Drag handles. In progress. 2019-10-03 04:23:33 +03:00
Lauri Ojansivu 21fa26a1be Update ChangeLog. 2019-10-01 21:09:27 +03:00
Lauri Ojansivu 66372271c2 Merge pull request #2740 from whowillcare/master
#2738 adding all pertinent help file for due days, plus modified the …
2019-10-01 18:41:01 +03:00
Sam X. Chen e60926f847 #2738 adding all pertinent help file for due days, plus modified the .list-header-name when in mobile mode 2019-10-01 10:39:36 -04:00
Lauri Ojansivu b04df1d54d Update translations. 2019-10-01 16:18:38 +03:00
Lauri Ojansivu 83f81f4586 Fix prettier. 2019-10-01 01:05:02 +03:00
Lauri Ojansivu 4785086e2f Fix lint. 2019-10-01 00:58:12 +03:00
Lauri Ojansivu 98c38fe58f Drag handles continue. In progress.
Thanks to xet7 !
2019-10-01 00:48:36 +03:00
Lauri Ojansivu 44378fe9d1 Update translations. 2019-09-30 21:31:49 +03:00
Lauri Ojansivu afcca947e3 Update translations. 2019-09-30 15:03:15 +03:00
Lauri Ojansivu 20526756db Update translations. 2019-09-28 15:15:23 +03:00
Lauri Ojansivu 58724f93c9 Update ChangeLog. 2019-09-28 15:05:53 +03:00
Lauri Ojansivu 5d6c68a9cf Merge branch 'master' of github.com:wekan/wekan 2019-09-28 14:56:51 +03:00
Lauri Ojansivu ed4c49090a Merge pull request #2738 from whowillcare/master
Add feature: Add due timeline into Calendar view
2019-09-28 14:57:00 +03:00
Lauri Ojansivu 75dc5f226c Merge branch 'feature-drag-handle' 2019-09-28 14:55:09 +03:00
Sam X. Chen 020b663836 Feature enhancement: Allow wekan master have more flexiblity on setting up due reminder 2019-09-26 12:24:40 -04:00
Sam X. Chen e5f0dd7dd8 Add feature: Add due timeline into Calendar view 2019-09-26 12:20:14 -04:00
Sam X. Chen 4f34adbd44 Merge remote-tracking branch 'upstream/master' 2019-09-26 10:53:58 -04:00
Sam X. Chen 62b72a03c4 Add feature: Add due timeline into Calendar view 2019-09-26 10:53:40 -04:00
Lauri Ojansivu 5696e4a4c6 Update translations. 2019-09-25 21:37:25 +03:00
Lauri Ojansivu 814e264521 Merge pull request #2720 from whowillcare/master
if username contains space, @ functions will fail at certain point
2019-09-25 20:51:51 +03:00
Lauri Ojansivu 872ed4b2f9 Drag handles continue. In progress. 2019-09-25 20:49:19 +03:00
Lauri Ojansivu 1d8633e7e6 Update translations. 2019-09-25 20:46:35 +03:00
Lauri Ojansivu b3637f2781 Merge branch 'master' of github.com:wekan/wekan 2019-09-25 20:44:46 +03:00
Lauri Ojansivu 68d3cdae58 Merge pull request #2734 from jernejh/patch-1
Update sl.i18n.json
2019-09-25 20:44:53 +03:00
jernejh 620a362244 Update sl.i18n.json 2019-09-25 19:08:18 +02:00
Sam X. Chen d5cff1ec48 Add feature: differentiating new due time and modified due time 2019-09-25 11:48:20 -04:00
Sam X. Chen 8d7714760c BUG FIX: archived cards still sent out notification 2019-09-25 11:26:46 -04:00
Sam X. Chen a37723f8a4 Fixing method in users.js didn't have check userId 2019-09-21 15:32:21 -04:00
Sam X. Chen e3a727edec Merge remote-tracking branch 'upstream/master' 2019-09-19 15:17:01 -04:00
Sam X. Chen 4ee88e026e Buxfixed: if username contains space, it will cause @ commment failed to send out email and other 2019-09-19 15:16:48 -04:00
Lauri Ojansivu 55a2473d81 Merge branch 'feature-drag-handle' 2019-09-19 01:12:04 +03:00
Lauri Ojansivu ba754cfaea v3.44 2019-09-17 23:54:31 +03:00
Lauri Ojansivu 7a8011f887 Update ChangeLog. 2019-09-17 22:19:01 +03:00
Lauri Ojansivu 125231beff Add language: Slovenian.
Thanks to translators!
2019-09-17 22:16:06 +03:00
Lauri Ojansivu f70e47a012 Update ChangeLog. 2019-09-17 20:28:39 +03:00
Lauri Ojansivu 0c34566aef Merge pull request #2717 from whowillcare/master
BugFix: in richer editor @ autocomplete doesn't really insert the username properly
2019-09-17 19:37:38 +03:00
Sam X. Chen f29d7daa1d BugFix: in richer editor @ autocomplete doesn't really insert the user name into comment properly 2019-09-17 09:30:26 -04:00
Sam X. Chen 0120c73a80 Merge remote-tracking branch 'upstream/master' 2019-09-17 09:27:36 -04:00
Sam X. Chen 194b6ad46e BugFix: in richer editor @ autocomplete doesn't really insert the user name into comment properly 2019-09-17 09:27:23 -04:00
Lauri Ojansivu 158ddadf54 v3.43 2019-09-17 03:06:01 +03:00
Lauri Ojansivu 9fb0ef7eb2 Update readme. 2019-09-17 02:51:44 +03:00
Lauri Ojansivu 503be10d36 Update Readme. 2019-09-17 02:49:38 +03:00
Lauri Ojansivu 3021f1cbe1 Update ChangeLog. 2019-09-17 02:39:21 +03:00
Lauri Ojansivu c66eba217b Update ChangeLog. 2019-09-17 02:36:49 +03:00
Lauri Ojansivu 8486ccbaa8 Update translations. 2019-09-17 01:43:26 +03:00
Lauri Ojansivu 03d7fc02ec Drag handles. In Progress. 2019-09-17 01:39:10 +03:00
Lauri Ojansivu ddd00a27a9 Merge pull request #2715 from whowillcare/master
fix some issues found
2019-09-17 01:34:55 +03:00
Sam X. Chen b31c42acc4 Merge remote-tracking branch 'upstream/master' 2019-09-16 14:43:51 -04:00
Sam X. Chen 616903114f Fix two-way webhooks - locking might fail sometime 2019-09-16 14:43:32 -04:00
Lauri Ojansivu 6e6e7ded82 Update translations. 2019-09-15 00:03:27 +03:00
Lauri Ojansivu 6c7e1ed3ae Add translation for "Show desktop drag handles".
Thanks to xet7 !
2019-09-14 23:53:56 +03:00
Lauri Ojansivu d896f6dbcf v3.42 2019-09-14 06:06:08 +03:00
Lauri Ojansivu 5e963375cb Update translations. 2019-09-14 06:00:05 +03:00
Lauri Ojansivu 57119868bb Revert drag handle changes.
Thanks to Keelan !

Related #2704
2019-09-14 05:55:32 +03:00
Lauri Ojansivu 7b445dfc93 v3.41 2019-09-13 03:59:51 +03:00
Lauri Ojansivu 5f27a76312 Update translations. 2019-09-13 03:56:37 +03:00
Lauri Ojansivu b34960d1bd Update ChangeLog. 2019-09-13 03:50:09 +03:00
Lauri Ojansivu ff550e9110 Mobile and Desktop drag handles part 1.
Thanks to xet7 !

Related #2081
2019-09-13 03:45:55 +03:00
Lauri Ojansivu 0fcfc3a340 Update readme. 2019-09-12 13:06:35 +03:00
Lauri Ojansivu f95d19ab49 Update readme. 2019-09-12 13:05:01 +03:00
Lauri Ojansivu 50744dead8 Update links at readme. 2019-09-12 12:59:27 +03:00
Lauri Ojansivu a0e5737e4c Anonymize data please. 2019-09-12 10:18:53 +03:00
Lauri Ojansivu ecd9b9b5f4 v3.40 2019-09-11 21:12:58 +03:00
Lauri Ojansivu 389ed2f1ad Update translations. 2019-09-11 21:07:54 +03:00
Lauri Ojansivu 273dc5223b Update ChangeLog. 2019-09-11 21:02:55 +03:00
Lauri Ojansivu db32f595fe Merge pull request #2697 from justinr1234/patch-7
Fix #2688 subcard selector
2019-09-11 19:18:32 +03:00
justinr1234 4d967360b2 Fix #2688 subcard selector 2019-09-11 10:54:32 -05:00
Lauri Ojansivu f14dce2f19 v3.39 2019-09-11 16:11:55 +03:00
Lauri Ojansivu 82f73ab58c Update translations. 2019-09-11 16:05:58 +03:00
Sam X. Chen 0b52ba4ded Merge remote-tracking branch 'upstream/master' 2019-09-11 09:05:28 -04:00
Sam X. Chen e0046032e8 Fixing @user in comments doesn't work if it's in a separate line 2019-09-11 09:05:16 -04:00
Lauri Ojansivu f094efa929 Update ChangeLog. 2019-09-11 16:04:26 +03:00
Lauri Ojansivu 6ce8eeee6c Revert Wekan v3.37 Fix Linked cards make load all cards of database.
Thanks to xet7!

Related #2688,
related #2693
2019-09-11 16:00:57 +03:00
Lauri Ojansivu be70c02922 v3.38 2019-09-11 04:47:56 +03:00
Lauri Ojansivu a61afda39e Update translations. 2019-09-11 04:30:38 +03:00
Lauri Ojansivu 126ab58e03 Create FUNDING.yml 2019-09-11 03:05:45 +03:00
Lauri Ojansivu ca95bba358 v3.37 2019-09-07 22:42:03 +03:00
Lauri Ojansivu 6407893aa6 Update translations. 2019-09-07 22:39:23 +03:00
Lauri Ojansivu 45ed9b426f [Fix Unable to drag select text without closing card details](https://github.com/wekan/wekan/pull/2690).
Thanks to justinr1234 !

Closes #2451
2019-09-07 22:35:15 +03:00
Lauri Ojansivu 11d6b77bb1 Merge pull request #2690 from justinr1234/fix-drag-select-on-card-details
Fix #2451 unable to drag select text without closing card details
2019-09-07 22:33:20 +03:00
Lauri Ojansivu f248dada3f Update ChangeLog. 2019-09-07 17:58:55 +03:00
Lauri Ojansivu a56988c487 Fix: Linked cards make load all cards of database.
Thanks to Akuket !

Closes #2688
2019-09-07 17:56:38 +03:00
Justin Reynolds 74b5bdf2aa Fix #2451 unable to drag select text without closing card details 2019-09-06 17:18:06 -05:00
Lauri Ojansivu 753d5b953d Fix prettier. 2019-09-06 21:27:06 +03:00
Lauri Ojansivu ea26f70669 Merge pull request #2683 from insertjokehere/fix_error_on_successful_webhook
Treat any 2xx webhook response status code as success
2019-09-06 19:22:36 +03:00
William Hughes 682c23e011 Treat any 2xx webhook response status code as success 2019-09-06 11:29:00 +12:00
Lauri Ojansivu d4a307f66d Update translations. 2019-09-06 01:28:43 +03:00
Lauri Ojansivu 7c9bf3ce0f Update ChangeLog. 2019-09-06 01:27:09 +03:00
Lauri Ojansivu 6cbd4cabc7 LDAP: Fix USERDN example, when parameters contain spaces:
LDAP_AUTHENTIFICATION_USERDN="CN=ldap admin,CN=users,DC=domainmatter,DC=lan"

Thanks to compumatter !
2019-09-06 01:22:15 +03:00
Lauri Ojansivu 10b953041f v3.36 2019-09-05 23:17:03 +03:00
Lauri Ojansivu 36fb31c138 Try to fix snap. 2019-09-05 23:05:50 +03:00
Lauri Ojansivu 3f2490e64d Update translations. 2019-09-05 22:58:53 +03:00
Lauri Ojansivu 37065c1d50 Update ChangeLog. 2019-09-05 22:54:37 +03:00
Lauri Ojansivu 9764e03a37 Merge branch 'justinr1234-fix-date-stamps' 2019-09-05 22:48:36 +03:00
Lauri Ojansivu 0f5226f814 Merge branch 'fix-date-stamps' of https://github.com/justinr1234/wekan into justinr1234-fix-date-stamps 2019-09-05 21:01:51 +03:00
Justin Reynolds 3b9f2ca7c2 Fixes #2596 incorrect date types for created & updated 2019-09-05 12:29:45 -05:00
Lauri Ojansivu 7953e5a52a Try to fix Snap. 2019-09-04 23:53:14 +03:00
Lauri Ojansivu 9617c78798 Update translations. 2019-09-04 22:48:17 +03:00
Lauri Ojansivu 28f21487b0 Add package-lock.json back. 2019-09-04 22:35:32 +03:00
Lauri Ojansivu 4567f08a0b Try to fix prettier. 2019-09-04 22:26:00 +03:00
Lauri Ojansivu b0281e1941 Try to fix rebuild script. 2019-09-04 22:25:30 +03:00
Lauri Ojansivu efed77b474 Update translations. 2019-09-04 22:14:44 +03:00
Lauri Ojansivu a70b97676a Remove package-lock.json 2019-09-04 22:09:22 +03:00
Lauri Ojansivu 7b54983ec2 Try to fix snap. 2019-09-04 22:05:51 +03:00
Lauri Ojansivu bc2fcef7c4 Update ChangeLog. 2019-09-04 21:54:27 +03:00
Lauri Ojansivu 750349053c Update translations. 2019-09-04 21:46:01 +03:00
Lauri Ojansivu bfbe32e1ec Merge pull request #2665 from whowillcare/master
Add Feature: complete the original author's webhook functions and add two-way webhook type
2019-09-04 21:11:41 +03:00
Lauri Ojansivu ccfaf879dd Merge pull request #2667 from Akuket/master
Patch admin search feature
2019-09-04 21:10:25 +03:00
Lauri Ojansivu ebd32af5c1 Merge pull request #2672 from Scherdel/master
Devcontainer: Removed MAIL-Vars, Add PATH with meteor to Envionment
2019-09-04 21:09:57 +03:00
Lauri Ojansivu 43ea7d761e Try to fix snap. 2019-09-04 21:07:17 +03:00
Christoph Jahn a715e30cba DevContainer: use docker extend file, fix PATH 2019-09-03 21:20:24 +02:00
Christoph Jahn 8633bd8974 Merge branch 'master' of https://github.com/scherdel/wekan 2019-09-02 20:28:48 +02:00
Christoph Jahn 9474bee7fa DevContainer: Updated node and mongodb 2019-09-02 20:28:39 +02:00
Christoph Jahn 92b86ada39 Merge branch 'master' of https://github.com/scherdel/wekan 2019-09-02 17:00:17 +00:00
Christoph Jahn 03bea6e406 Removed MAIL-Vars for DEV, Add PATH to ENV 2019-09-02 18:57:17 +02:00
guillaume 6183402754 Merge branch 'master' of https://github.com/wekan/wekan 2019-08-30 17:18:29 +02:00
guillaume 663ba26d4d Patch admin search feature 2019-08-30 17:17:25 +02:00
Sam X. Chen 510407467c Add Feature: enable two-way webhooks - add comments need userid 2019-08-30 10:36:17 -04:00
Sam X. Chen b477fc1b1c Add Feature: enable two-way webhooks - fixing lint error 2019-08-29 23:49:32 -04:00
Sam X. Chen 58155288fb Merge remote-tracking branch 'upstream/master' 2019-08-29 22:08:41 -04:00
Sam X. Chen dd0682328b Add Feature: enable two-way webhooks - stage two 2019-08-29 22:07:40 -04:00
Lauri Ojansivu d1ab787215 v3.35 2019-08-29 21:12:24 +03:00
Lauri Ojansivu 71d32c6bc8 Try to fix snap. 2019-08-29 21:08:14 +03:00
Lauri Ojansivu 3acc9d4468 v3.34 2019-08-29 19:41:26 +03:00
Lauri Ojansivu d5403bbfc5 Snap: Delete all .swp files.
Thanks to xet7 !
2019-08-29 19:38:25 +03:00
Lauri Ojansivu 583c369233 v3.33 2019-08-29 17:01:23 +03:00
Lauri Ojansivu 96efa659a8 Update ChangeLog. 2019-08-29 16:57:33 +03:00
Lauri Ojansivu 87af5ecb36 Merge pull request #2664 from grmpfhmbl/feature-colored-calendar-cards
add card color to calendar event (#2651)
2019-08-29 16:54:46 +03:00
Lauri Ojansivu 3d39793cbf v3.32 2019-08-29 16:42:43 +03:00
Lauri Ojansivu 79d4cd83b1 Fix Snap adopt-info.
https://forum.snapcraft.io/t/dirty-snap-release/12975/12?u=xet7

Thanks to popey at Snapcraft forum.
2019-08-29 16:37:03 +03:00
Lauri Ojansivu 9bc8040634 Try to fix adopt-info.
Thanks to xet7.
2019-08-29 16:31:45 +03:00
Lauri Ojansivu 93ae815946 v3.31 2019-08-29 16:20:26 +03:00
Lauri Ojansivu be5f435bc5 Try to fix adopt-info.
https://forum.snapcraft.io/t/dirty-snap-release/12975/8

Thanks to ogra at Snapcraft forum.
2019-08-29 16:15:57 +03:00
Lauri Ojansivu 8a74210c60 Try to fix adopt-info. 2019-08-29 16:02:21 +03:00
Lauri Ojansivu 629a6f0103 v3.30 2019-08-29 15:42:07 +03:00
Lauri Ojansivu 2522a98f7d Update translations. 2019-08-29 15:36:32 +03:00
Lauri Ojansivu 0ff5ce8fde Snap: Change version-script to adopt-info to fix dirty.
https://forum.snapcraft.io/t/dirty-snap-release/12975/4

Thanks to popey and daniel at Snapcraft forum.
2019-08-29 15:30:14 +03:00
Lauri Ojansivu 5084102e6e Delete another phantomjs binary from Snap.
Thanks to xet7 !
2019-08-29 15:14:35 +03:00
Steffen b7c5ba3d1b add card color to calendar event (#2651) 2019-08-29 14:12:53 +02:00
Lauri Ojansivu e5b9d41e67 Revert fix dirty, because it caused Snap build to fail.
Thanks to xet7 !
2019-08-29 15:12:00 +03:00
Lauri Ojansivu 007c064672 Fix dirty snap release.
https://stackoverflow.com/questions/16035240/why-is-git-describe-dirty-adding-a-dirty-suffix-when-describing-a-clean-ch
https://forum.snapcraft.io/t/dirty-snap-release/12975

Thanks to xet7.
2019-08-29 02:09:01 +03:00
Lauri Ojansivu 9c15fb1e69 v3.29 2019-08-29 00:51:06 +03:00
Lauri Ojansivu 7761a22bb4 Fix Snap.
Thanks to xet7 !
2019-08-29 00:45:37 +03:00
Lauri Ojansivu 16388a5e25 v2.38 2019-08-29 00:39:04 +03:00
Lauri Ojansivu 43c28a2f15 v3.27 2019-08-29 00:23:59 +03:00
Lauri Ojansivu d29b40c0ee Update translations. 2019-08-29 00:21:53 +03:00
Lauri Ojansivu e21c47d3cf Upgrade node, mongo and fibers.
Thanks to xet7 !
2019-08-29 00:18:15 +03:00
Lauri Ojansivu 971de9ca34 v3.26 2019-08-28 18:38:41 +03:00
Lauri Ojansivu 3976e02012 Update translations. 2019-08-28 18:36:02 +03:00
Lauri Ojansivu d4f5717264 Update ChangeLog. 2019-08-28 18:31:50 +03:00
Lauri Ojansivu 362220299f Merge pull request #2660 from Scherdel/master
Fixed endless compilation devcontainer and added debugging launch.conf
2019-08-28 16:43:43 +03:00
Christoph Jahn dbdb26a044 Fixed endless compilation devcontainer, debugging 2019-08-28 15:26:17 +02:00
Lauri Ojansivu 60e8deb064 Merge pull request #2659 from Scherdel/master
Adds .devcontainer feature to debug and compile with vscode
2019-08-28 13:20:44 +03:00
Christoph Jahn 4b06e2188c Merge branch 'master' of https://github.com/scherdel/wekan 2019-08-28 10:13:31 +00:00
Christoph Jahn 3307629c8c Devcontainer node-gyp fix 2019-08-28 10:13:10 +00:00
Christoph Jahn 3978c08757 Add debugging in containers for vscode 2019-08-28 10:13:09 +00:00
Christoph Jahn e21b0674e3 Devcontainer node-gyp fix 2019-08-28 10:07:35 +00:00
Lauri Ojansivu a97360dcfb Try to fix lint, and make board loading fix Sandstorm-only where
user permissions work differently.

Thanks to xet7 !

Related https://github.com/wekan/wekan/pull/2654
2019-08-26 23:24:13 +03:00
Lauri Ojansivu 6705007595 Update ChangeLog. 2019-08-26 22:58:27 +03:00
Lauri Ojansivu 8c6164f590 Merge branch 'PDIS-master' 2019-08-26 22:34:35 +03:00
Lauri Ojansivu bbb9815c30 Update ChangeLog. 2019-08-26 22:31:45 +03:00
Lauri Ojansivu 0083215ea3 Default to BIGEVENTS_PATTERN=NONE so that Wekan sends less email
notifications.

Thanks to rinnaz and xet7 !

Closes #2646,
closes #2617
2019-08-26 22:27:45 +03:00
Lauri Ojansivu cdea7e8869 Update ChangeLog. 2019-08-26 22:25:37 +03:00
Lauri Ojansivu 828822daad Merge pull request #2657 from justinr1234/patch-6
Fix last label undefined
2019-08-26 22:13:28 +03:00
justinr1234 0cf9a7b552 Fix last label undefined 2019-08-26 12:16:21 -05:00
Lauri Ojansivu 7cffce972d Update translations. 2019-08-26 19:13:01 +03:00
Lauri Ojansivu ad01526124 Add package-lock.json. 2019-08-26 19:02:48 +03:00
Lauri Ojansivu fa34950ca9 Merge pull request #2653 from GhassenRjab/add-missing-modules
Add missing modules
2019-08-26 14:42:15 +03:00
Romulus Urakagi Tsai 9bbeb73db1 Fix app hang when Meteor.user() is null and list spinner is loaded bug 2019-08-26 10:10:57 +00:00
Lauri Ojansivu ff419f0b60 Update translations. 2019-08-25 13:41:22 +03:00
Lauri Ojansivu 537a9c0568 Update translations. 2019-08-24 22:19:23 +03:00
Ghassen Rjab 843783d6cb Add missing modules 2019-08-24 11:51:08 +01:00
Lauri Ojansivu 07abed0067 v3.25 2019-08-23 03:12:29 +03:00
Lauri Ojansivu fe885860e3 Update translations. 2019-08-23 03:09:43 +03:00
Lauri Ojansivu 0dd3ff29f2 Limit card width to fixed size.
Thanks to xet7 !
2019-08-23 02:59:29 +03:00
Lauri Ojansivu 576523021e Merge pull request #2645 from justinr1234/master
Fixes #2638 importing trello
2019-08-23 00:09:06 +03:00
Justin Reynolds 9d8a38f324 Fixes #2638 importing trello 2019-08-22 16:05:41 -05:00
Lauri Ojansivu 2c78aab3dc Ask for webbrowser version at GitHub issue template.
Thanks to xet7 !
2019-08-22 23:25:56 +03:00
Lauri Ojansivu 45c8b00339 v3.24 2019-08-22 19:35:36 +03:00
Lauri Ojansivu 9156e5a6b6 Update translations. 2019-08-22 19:31:25 +03:00
Lauri Ojansivu cc792ddd57 Snap: Delete old MongoDB log, and log to syslog instead, because
syslog usually already has log rotation.

Thanks to xet7 !

Closes wekan/wekan-snap#92,
closes #1911
2019-08-22 19:22:27 +03:00
Lauri Ojansivu b8d60f0069 v3.23 2019-08-20 21:17:36 +03:00
Lauri Ojansivu b2deab544b Fix login did not work.
Thanks to xet7 !
2019-08-20 21:14:16 +03:00
Lauri Ojansivu f96c1eda70 v3.22 2019-08-20 19:25:37 +03:00
Lauri Ojansivu e9bbbb6a04 Update translations. 2019-08-20 19:21:39 +03:00
Lauri Ojansivu efcdbf2e3d Update ChangeLog. 2019-08-20 19:20:06 +03:00
Lauri Ojansivu 6e12e2fa59 [Fix: Some users cannot switch views or languages](https://github.com/wekan/wekan/issues/2630).
Thanks to xet7 and justinr1234.
2019-08-20 19:16:44 +03:00
Lauri Ojansivu a31ece0974 Update translations. 2019-08-19 12:59:27 +03:00
Lauri Ojansivu 06d85b66f1 Update ChangeLog. 2019-08-19 12:52:20 +03:00
Lauri Ojansivu af830812db Wrap minicard text labels to multiple rows.
Thanks to xet7 !
2019-08-19 12:47:02 +03:00
Sam X. Chen 3f0600fed7 Add Feature: enable two-way webhooks - stage one 2019-08-17 19:17:57 -04:00
Lauri Ojansivu 4e21e04a48 v3.21 2019-08-16 23:26:10 +03:00
Lauri Ojansivu 9779f2a7e6 Update ChangeLog. 2019-08-16 23:21:31 +03:00
Lauri Ojansivu a98f6d8e8a Update ChangeLog. 2019-08-16 23:15:17 +03:00
Lauri Ojansivu d93f33d919 Update translations. 2019-08-16 23:03:58 +03:00
Lauri Ojansivu 564ab219c6 Merge branch 'PDIS-master' 2019-08-16 22:36:19 +03:00
Lauri Ojansivu 026d8f4941 Merge branch 'master' of https://github.com/PDIS/wekan into PDIS-master 2019-08-16 22:27:44 +03:00
Lauri Ojansivu 4b2193436e Merge branch 'master' of github.com:wekan/wekan 2019-08-16 22:21:38 +03:00
Lauri Ojansivu dfb558ae99 Update translations. 2019-08-16 22:21:02 +03:00
Romulus Urakagi Tsai 76731e852d Undo translation change 2019-08-16 07:52:51 +00:00
Romulus Urakagi Tsai b794c22ac0 Merge branch 'master' of https://github.com/wekan/wekan 2019-08-16 07:43:55 +00:00
Romulus Urakagi Tsai b778cea34f Fix cannot show archived list bug 2019-08-16 07:42:18 +00:00
Lauri Ojansivu a1582c3e65 Merge pull request #2628 from justinr1234/fix-mismatched-queries
Fix mismatched queries
2019-08-16 00:39:16 +03:00
Justin Reynolds 977503fa79 Fix mismatched queries 2019-08-15 15:47:32 -05:00
Lauri Ojansivu a809284a14 Update translations. 2019-08-15 22:22:48 +03:00
Lauri Ojansivu 8e3cbd2d82 Merge pull request #2626 from whowillcare/master
bugfix 2616
2019-08-15 21:31:25 +03:00
Sam X. Chen f3147af2f7 Merge remote-tracking branch 'upstream/master' 2019-08-15 14:29:02 -04:00
Sam X. Chen 86d686fe1d Bugfix: 2621 2019-08-15 14:28:18 -04:00
Sam X. Chen 7d52ae16d5 Bugfix: 2621 Summmernote is too wide on mobile screen 2019-08-15 14:23:14 -04:00
Lauri Ojansivu c882ebe030 Update translations. 2019-08-15 16:32:55 +03:00
Lauri Ojansivu d80a258c8b v3.20 2019-08-15 13:39:24 +03:00
Romulus Urakagi Tsai 3084f24278 Merge branch 'master' of https://github.com/wekan/wekan 2019-08-14 08:42:25 +00:00
Romulus Urakagi Tsai efdab37f3f Merge branch 'master' of https://github.com/wekan/wekan 2019-08-12 01:43:21 +00:00
Christoph Jahn 47f6fee279 Merge branch 'master' of https://github.com/scherdel/wekan 2019-08-08 13:27:04 +02:00
Christoph Jahn 460caa7823 Add debugging in containers for vscode 2019-08-08 12:42:16 +02:00
Christoph Jahn 5d55b21fb6 Add debugging in containers for vscode 2019-08-07 12:18:51 +02:00
Romulus Urakagi Tsai 4f4e0a21f8 Add 'show archive' and 'hide empty lists' in filter feature. 2019-08-07 07:58:05 +00:00
227 changed files with 23898 additions and 3260 deletions

No files matched your search

+162
View File
@@ -0,0 +1,162 @@
FROM ubuntu:disco
LABEL maintainer="sgr"
ENV BUILD_DEPS="gnupg gosu bsdtar wget curl bzip2 g++ build-essential python git ca-certificates iproute2"
ENV DEBIAN_FRONTEND=noninteractive
ENV \
DEBUG=false \
NODE_VERSION=8.17.0 \
METEOR_RELEASE=1.8.1 \
USE_EDGE=false \
METEOR_EDGE=1.5-beta.17 \
NPM_VERSION=latest \
FIBERS_VERSION=4.0.1 \
ARCHITECTURE=linux-x64 \
SRC_PATH=./ \
WITH_API=true \
ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURES_BEFORE=3 \
ACCOUNTS_LOCKOUT_KNOWN_USERS_PERIOD=60 \
ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURE_WINDOW=15 \
ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURES_BERORE=3 \
ACCOUNTS_LOCKOUT_UNKNOWN_USERS_LOCKOUT_PERIOD=60 \
ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURE_WINDOW=15 \
RICHER_CARD_COMMENT_EDITOR=true \
MAX_IMAGE_PIXEL="" \
IMAGE_COMPRESS_RATIO="" \
BIGEVENTS_PATTERN="" \
NOTIFY_DUE_DAYS_BEFORE_AND_AFTER="" \
NOTIFY_DUE_AT_HOUR_OF_DAY="" \
EMAIL_NOTIFICATION_TIMEOUT=30000 \
MATOMO_ADDRESS="" \
MATOMO_SITE_ID="" \
MATOMO_DO_NOT_TRACK=true \
MATOMO_WITH_USERNAME=false \
BROWSER_POLICY_ENABLED=true \
TRUSTED_URL="" \
WEBHOOKS_ATTRIBUTES="" \
OAUTH2_ENABLED=false \
OAUTH2_LOGIN_STYLE=redirect \
OAUTH2_CLIENT_ID="" \
OAUTH2_SECRET="" \
OAUTH2_SERVER_URL="" \
OAUTH2_AUTH_ENDPOINT="" \
OAUTH2_USERINFO_ENDPOINT="" \
OAUTH2_TOKEN_ENDPOINT="" \
OAUTH2_ID_MAP="" \
OAUTH2_USERNAME_MAP="" \
OAUTH2_FULLNAME_MAP="" \
OAUTH2_ID_TOKEN_WHITELIST_FIELDS="" \
OAUTH2_REQUEST_PERMISSIONS='openid profile email' \
OAUTH2_EMAIL_MAP="" \
LDAP_ENABLE=false \
LDAP_PORT=389 \
LDAP_HOST="" \
LDAP_BASEDN="" \
LDAP_LOGIN_FALLBACK=false \
LDAP_RECONNECT=true \
LDAP_TIMEOUT=10000 \
LDAP_IDLE_TIMEOUT=10000 \
LDAP_CONNECT_TIMEOUT=10000 \
LDAP_AUTHENTIFICATION=false \
LDAP_AUTHENTIFICATION_USERDN="" \
LDAP_AUTHENTIFICATION_PASSWORD="" \
LDAP_LOG_ENABLED=false \
LDAP_BACKGROUND_SYNC=false \
LDAP_BACKGROUND_SYNC_INTERVAL="" \
LDAP_BACKGROUND_SYNC_KEEP_EXISTANT_USERS_UPDATED=false \
LDAP_BACKGROUND_SYNC_IMPORT_NEW_USERS=false \
LDAP_ENCRYPTION=false \
LDAP_CA_CERT="" \
LDAP_REJECT_UNAUTHORIZED=false \
LDAP_USER_AUTHENTICATION=false \
LDAP_USER_AUTHENTICATION_FIELD=uid \
LDAP_USER_SEARCH_FILTER="" \
LDAP_USER_SEARCH_SCOPE="" \
LDAP_USER_SEARCH_FIELD="" \
LDAP_SEARCH_PAGE_SIZE=0 \
LDAP_SEARCH_SIZE_LIMIT=0 \
LDAP_GROUP_FILTER_ENABLE=false \
LDAP_GROUP_FILTER_OBJECTCLASS="" \
LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE="" \
LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE="" \
LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT="" \
LDAP_GROUP_FILTER_GROUP_NAME="" \
LDAP_UNIQUE_IDENTIFIER_FIELD="" \
LDAP_UTF8_NAMES_SLUGIFY=true \
LDAP_USERNAME_FIELD="" \
LDAP_FULLNAME_FIELD="" \
LDAP_MERGE_EXISTING_USERS=false \
LDAP_EMAIL_FIELD="" \
LDAP_EMAIL_MATCH_ENABLE=false \
LDAP_EMAIL_MATCH_REQUIRE=false \
LDAP_EMAIL_MATCH_VERIFIED=false \
LDAP_SYNC_USER_DATA=false \
LDAP_SYNC_USER_DATA_FIELDMAP="" \
LDAP_SYNC_GROUP_ROLES="" \
LDAP_DEFAULT_DOMAIN="" \
LDAP_SYNC_ADMIN_STATUS="" \
LDAP_SYNC_ADMIN_GROUPS="" \
HEADER_LOGIN_ID="" \
HEADER_LOGIN_FIRSTNAME="" \
HEADER_LOGIN_LASTNAME="" \
HEADER_LOGIN_EMAIL="" \
LOGOUT_WITH_TIMER=false \
LOGOUT_IN="" \
LOGOUT_ON_HOURS="" \
LOGOUT_ON_MINUTES="" \
CORS="" \
CORS_ALLOW_HEADERS="" \
CORS_EXPOSE_HEADERS="" \
DEFAULT_AUTHENTICATION_METHOD=""
# Install OS
RUN set -o xtrace \
&& useradd --user-group -m --system --home-dir /home/wekan wekan \
&& apt-get update \
&& apt-get install --assume-yes --no-install-recommends apt-utils apt-transport-https ca-certificates 2>&1 \
&& apt-get install --assume-yes --no-install-recommends ${BUILD_DEPS}
# Install NodeJS
RUN set -o xtrace \
&& cd /tmp \
&& curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-$ARCHITECTURE.tar.xz" \
&& curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc" \
&& grep " node-v$NODE_VERSION-$ARCHITECTURE.tar.xz\$" SHASUMS256.txt.asc | sha256sum -c - \
&& tar -xJf "node-v$NODE_VERSION-$ARCHITECTURE.tar.xz" -C /usr/local --strip-components=1 --no-same-owner \
&& rm "node-v$NODE_VERSION-$ARCHITECTURE.tar.xz" SHASUMS256.txt.asc \
&& ln -s /usr/local/bin/node /usr/local/bin/nodejs \
&& mkdir -p /usr/local/lib/node_modules/fibers/.node-gyp /root/.node-gyp/${NODE_VERSION} /home/wekan/.config \
&& npm install -g npm@${NPM_VERSION} \
&& chown wekan:wekan --recursive /home/wekan/.config
ENV DEBIAN_FRONTEND=dialog
USER wekan
# Install Meteor
RUN set -o xtrace \
&& cd /home/wekan \
&& curl https://install.meteor.com/?release=$METEOR_VERSION --output /home/wekan/install-meteor.sh \
# Replace tar with bsdtar in the install script; https://github.com/jshimko/meteor-launchpad/issues/39
&& sed --in-place "s/tar -xzf.*/bsdtar -xf \"\$TARBALL_FILE\" -C \"\$INSTALL_TMPDIR\"/g" /home/wekan/install-meteor.sh \
&& sed --in-place 's/VERBOSITY="--silent"/VERBOSITY="--progress-bar"/' /home/wekan/install-meteor.sh \
&& printf "\n[-] Installing Meteor $METEOR_VERSION...\n\n" \
&& sh /home/wekan/install-meteor.sh
ENV PATH=$PATH:/home/wekan/.meteor/
# Copy source dir
USER root
RUN echo "export PATH=$PATH" >> /etc/environment
RUN set -o xtrace \
&& mkdir /home/wekan/app
COPY ${SRC_PATH} /home/wekan/app/
RUN set -o xtrace \
&& chown -R wekan:wekan /home/wekan/app /home/wekan/.meteor
USER wekan
+12
View File
@@ -0,0 +1,12 @@
#!/bin/bash
cd /home/wekan/app
rm -rf node_modules
/home/wekan/.meteor/meteor npm install
rm -rf .build
/home/wekan/.meteor/meteor build .build --directory
cp -f fix-download-unicode/cfs_access-point.txt .build/bundle/programs/server/packages/cfs_access-point.js
cd .build/bundle/programs/server
rm -rf node_modules
/home/wekan/.meteor/meteor npm install
cd /home/wekan/app
+17
View File
@@ -0,0 +1,17 @@
// See https://aka.ms/vscode-remote/devcontainer.json for format details.
{
"dockerComposeFile": ["docker-compose.yml", "docker-compose.extend.yml"],
"service": "wekan-dev",
"workspaceFolder": "/home/wekan/app",
"extensions": [
"mutantdino.resourcemonitor",
"editorconfig.editorconfig",
"dbaeumer.vscode-eslint",
"codezombiech.gitignore",
"eamodio.gitlens",
"gruntfuggly.todo-tree",
"dotjoshjohnson.xml",
"redhat.vscode-yaml",
"vuhrmeister.vscode-meteor"
]
}
+50
View File
@@ -0,0 +1,50 @@
version: '3.7'
services:
wekandb-dev:
image: mongo:4.0.12
container_name: wekan-dev-db
restart: unless-stopped
command: mongod --smallfiles --oplogSize 128
networks:
- wekan-dev-tier
expose:
- 27017
volumes:
- wekan-dev-db:/data/db
- wekan-dev-db-dump:/dump
wekan-dev:
container_name: wekan-dev-app
restart: always
networks:
- wekan-dev-tier
build:
context: ..
dockerfile: .devcontainer/Dockerfile
ports:
- 3000:3000
- 9229:9229
environment:
- MONGO_URL=mongodb://wekandb-dev:27017/wekan
- ROOT_URL=http://localhost:3000
- WITH_API=true
- RICHER_CARD_COMMENT_EDITOR=true
- BROWSER_POLICY_ENABLED=true
depends_on:
- wekandb-dev
volumes:
- ..:/app:delegated
command:
sleep infinity
volumes:
wekan-dev-db:
driver: local
wekan-dev-db-dump:
driver: local
networks:
wekan-dev-tier:
driver: bridge
+1
View File
@@ -1 +1,2 @@
packages/*
.snap-meteor-1.8/*
+1
View File
@@ -145,6 +145,7 @@
"allowIsBoardMemberByCard": true,
"allowIsBoardMemberCommentOnly": true,
"allowIsBoardMemberNoComments": true,
"allowIsBoardMemberWorker": true,
"Emoji": true,
"Checklists": true,
"Settings": true,
+3
View File
@@ -0,0 +1,3 @@
* text=auto eol=lf
*.{cmd,[cC][mM][dD]} text eol=crlf
*.{bat,[bB][aA][tT]} text eol=crlf
+3
View File
@@ -0,0 +1,3 @@
# These are supported funding model platforms
custom: ['https://wekan.team/commercial-support/']
+7 -4
View File
@@ -2,13 +2,16 @@
Add these issues to elsewhere:
- Snap: https://github.com/wekan/wekan-snap/issues
- LDAP: https://github.com/wekan/wekan-ldap/issues
- UCS: https://github.com/wekan/univention/issues
Other Wekan issues can be added here.
**Server Setup Information**:
* Note: Please anonymize info, and do not add to this public issue any of your Wekan board URLs, passwords, API tokens etc, do you understand?:
* Did you test in newest Wekan?:
* For new Wekan install, did you configure root-url correctly https://github.com/wekan/wekan/wiki/Settings ?
* For new Wekan install, did you configure root-url correctly so Wekan cards open correctly https://github.com/wekan/wekan/wiki/Settings ?
* Wekan version:
* If this is about old version of Wekan, what upgrade problem you have?:
* Operating System:
@@ -16,12 +19,12 @@ Other Wekan issues can be added here.
* Http frontend if any (Caddy, Nginx, Apache, see config examples from Wekan GitHub wiki first):
* Node Version:
* MongoDB Version:
* ROOT_URL environment variable http(s)://(subdomain).example.com(/suburl):
* Wekan only works on newest desktop Firefox/Chromium/Chrome/Edge/Chromium Edge and mobile Chrome. What webbrowser version are you using?
**Problem description**:
- *REQUIRED: Add recorded animated gif about how it works currently, and screenshot mockups how it should work. Use peek to record animgif in Linux https://github.com/phw/peek*
- *Explain steps how to reproduce*
- *In webbrowser, what does show Right Click / Inspect / Console ? Chrome shows more detailed info than Firefox.*
- *If using Snap, what does show command `sudo snap logs wekan.wekan` ?*
- *If using Docker, what does show command `sudo docker logs wekan-app` ?*
- *If using Snap, what does show command `sudo snap logs wekan.wekan` ? Please anonymize logs.*
- *If using Docker, what does show command `sudo docker logs wekan-app` ? Please anonymize logs.*
- *If logs are very long, attach them in .zip file*
+17
View File
@@ -0,0 +1,17 @@
name: Docker Image CI
on:
push:
branches:
- master
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Build the Docker image
run: docker build . --file Dockerfile --tag wekan:$(date +%s)
+1 -1
View File
@@ -29,6 +29,6 @@ versions.json
Thumbs.db
ehthumbs.db
.eslintcache
package-lock.json
.meteor/local
.meteor-1.6-snap/.meteor/local
.devcontainer/docker-compose.extend.yml
+1
View File
@@ -17,3 +17,4 @@ notices-for-facebook-graph-api-2
1.4.3-split-account-service-packages
1.5-add-dynamic-import-package
1.7-split-underscore-from-meteor-base
1.8.3-split-jquery-from-blaze
+6 -5
View File
@@ -6,9 +6,9 @@
meteor-base@1.4.0
# Build system
ecmascript@0.12.4
standard-minifier-css@1.5.3
standard-minifier-js@2.4.1
ecmascript@0.14.0
standard-minifier-css@1.6.0
standard-minifier-js@2.6.0
mquandalle:jade
# Polyfills
@@ -22,7 +22,7 @@ dburles:collection-helpers
idmontie:migrations
matb33:collection-hooks
matteodem:easy-search
mongo@1.6.2
mongo@1.8.0
mquandalle:collection-mutations
# Account system
@@ -75,7 +75,7 @@ horka:swipebox
dynamic-import@0.5.1
staringatlights:fast-render
accounts-password@1.5.1
accounts-password@1.5.2
cfs:gridfs
rzymek:fullcalendar
momentjs:moment@2.22.2
@@ -96,3 +96,4 @@ konecty:mongo-counter
percolate:synced-cron
easylogic:summernote
cfs:filesystem
ostrio:cookies
+1 -1
View File
@@ -1 +1 @@
METEOR@1.8.1
METEOR@1.9
+29 -28
View File
@@ -1,7 +1,7 @@
3stack:presence@1.1.2
accounts-base@1.4.4
accounts-base@1.5.0
accounts-oauth@1.1.16
accounts-password@1.5.1
accounts-password@1.5.3
aldeed:collection2@2.10.0
aldeed:collection2-core@1.2.0
aldeed:schema-deny@1.1.0
@@ -12,18 +12,18 @@ allow-deny@1.1.0
arillo:flow-router-helpers@0.5.2
audit-argument-checks@1.0.7
autoupdate@1.6.0
babel-compiler@7.3.4
babel-runtime@1.3.0
babel-compiler@7.5.1
babel-runtime@1.5.0
base64@1.0.12
binary-heap@1.0.11
blaze@2.3.3
blaze@2.3.4
blaze-tools@1.0.10
boilerplate-generator@1.6.0
browser-policy-common@1.0.11
browser-policy-framing@1.1.0
caching-compiler@1.2.1
caching-html-compiler@1.1.3
callback-hook@1.1.0
callback-hook@1.3.0
cfs:access-point@0.1.49
cfs:base-package@0.0.30
cfs:collection@0.5.5
@@ -52,16 +52,16 @@ ddp@1.4.0
ddp-client@2.3.3
ddp-common@1.4.0
ddp-rate-limiter@1.0.7
ddp-server@2.3.0
ddp-server@2.3.1
deps@1.0.12
diff-sequence@1.1.1
dynamic-import@0.5.1
easylogic:summernote@0.8.8
ecmascript@0.12.7
ecmascript@0.14.1
ecmascript-runtime@0.7.0
ecmascript-runtime-client@0.8.0
ecmascript-runtime-server@0.7.1
ejson@1.1.0
ecmascript-runtime-client@0.10.0
ecmascript-runtime-server@0.9.0
ejson@1.1.1
email@1.2.3
es5-shim@4.8.0
fastclick@1.0.13
@@ -82,16 +82,16 @@ kadira:dochead@1.5.0
kadira:flow-router@2.12.1
kenton:accounts-sandstorm@0.7.0
konecty:mongo-counter@0.0.5_3
lamhieu:meteorx@2.0.1
lamhieu:meteorx@2.1.1
lamhieu:unblock@1.0.0
launch-screen@1.1.1
livedata@1.0.18
localstorage@1.2.0
logging@1.1.20
lucasantoniassi:accounts-lockout@1.0.0
matb33:collection-hooks@0.8.4
matb33:collection-hooks@0.9.1
matteodem:easy-search@1.6.4
mdg:meteor-apm-agent@3.2.3
mdg:meteor-apm-agent@3.2.5
mdg:validation-error@0.5.1
meteor@1.9.3
meteor-base@1.4.0
@@ -101,16 +101,16 @@ meteorhacks:collection-utils@1.2.0
meteorhacks:picker@1.0.3
meteorhacks:subs-manager@1.6.4
meteorspark:util@0.2.0
minifier-css@1.4.2
minifier-js@2.4.1
minifier-css@1.5.0
minifier-js@2.6.0
minifiers@1.1.8-faster-rebuild.0
minimongo@1.4.5
mobile-status-bar@1.0.14
modern-browsers@0.1.4
modules@0.13.0
modules-runtime@0.10.3
modern-browsers@0.1.5
modules@0.15.0
modules-runtime@0.12.0
momentjs:moment@2.24.0
mongo@1.6.3
mongo@1.8.1
mongo-decimal@0.1.1
mongo-dev-server@1.1.0
mongo-id@1.0.7
@@ -127,17 +127,18 @@ mquandalle:mousetrap-bindglobal@0.0.1
mquandalle:perfect-scrollbar@0.6.5_2
msavin:usercache@1.8.0
npm-bcrypt@0.9.3
npm-mongo@3.1.2
npm-mongo@3.3.0
oauth@1.2.8
oauth2@1.2.1
observe-sequence@1.0.16
ongoworks:speakingurl@1.1.0
ordered-dict@1.1.0
peerlibrary:assert@0.2.5
ostrio:cookies@2.5.0
peerlibrary:assert@0.3.0
peerlibrary:base-component@0.16.0
peerlibrary:blaze-components@0.15.1
peerlibrary:computed-field@0.9.0
peerlibrary:reactive-field@0.5.0
peerlibrary:computed-field@0.10.0
peerlibrary:reactive-field@0.6.0
percolate:synced-cron@1.3.2
promise@0.11.2
raix:eventemitter@0.1.3
@@ -161,13 +162,13 @@ simple:json-routes@2.1.0
simple:rest-accounts-password@1.1.2
simple:rest-bearer-token-parser@1.0.1
simple:rest-json-error-handler@1.0.1
socket-stream-client@0.2.2
socket-stream-client@0.2.3
softwarerero:accounts-t9n@1.3.11
spacebars@1.0.15
spacebars-compiler@1.1.3
srp@1.0.12
standard-minifier-css@1.5.3
standard-minifier-js@2.4.1
standard-minifier-css@1.6.0
standard-minifier-js@2.6.0
staringatlights:fast-render@3.2.0
staringatlights:inject-data@2.3.0
tap:i18n@1.8.2
@@ -185,7 +186,7 @@ useraccounts:core@1.14.2
useraccounts:flow-routing@1.14.2
useraccounts:unstyled@1.14.2
verron:autosize@3.0.8
webapp@1.7.4
webapp@1.8.2
webapp-hashing@1.0.9
wekan-accounts-cas@0.1.0
wekan-accounts-oidc@1.0.10
+1
View File
@@ -5,3 +5,4 @@ node_modules/
.vscode/
.tx/
.github/
.snap-meteor-1.8/
@@ -0,0 +1,20 @@
# This file contains information which helps Meteor properly upgrade your
# app when you run 'meteor update'. You should check it into version control
# with your project.
notices-for-0.9.0
notices-for-0.9.1
0.9.4-platform-file
notices-for-facebook-graph-api-2
1.2.0-standard-minifiers-package
1.2.0-meteor-platform-split
1.2.0-cordova-changes
1.2.0-breaking-changes
1.3.0-split-minifiers-package
1.3.5-remove-old-dev-bundle-link
1.4.0-remove-old-dev-bundle-link
1.4.1-add-shell-server-package
1.4.3-split-account-service-packages
1.5-add-dynamic-import-package
1.7-split-underscore-from-meteor-base
1.8.3-split-jquery-from-blaze
+2
View File
@@ -0,0 +1,2 @@
dev_bundle
local
+7
View File
@@ -0,0 +1,7 @@
# This file contains a token that is unique to your project.
# Check it into your repository along with the rest of this directory.
# It can be used for purposes such as:
# - ensuring you don't accidentally deploy one app on top of another
# - providing package authors with aggregated statistics
dvyihgykyzec6y1dpg
Whitespace-only changes.
+99
View File
@@ -0,0 +1,99 @@
# Meteor packages used by this project, one per line.
#
# 'meteor add' and 'meteor remove' will edit this file for you,
# but you can also edit it by hand.
meteor-base@1.4.0
# Build system
ecmascript@0.13.2
standard-minifier-css@1.5.4
standard-minifier-js@2.5.2
mquandalle:jade
# Polyfills
es5-shim@4.8.0
# Collections
aldeed:collection2
cfs:standard-packages
cottz:publish-relations
dburles:collection-helpers
idmontie:migrations
matb33:collection-hooks
matteodem:easy-search
mongo@1.7.0
mquandalle:collection-mutations
# Account system
kenton:accounts-sandstorm
service-configuration@1.0.11
useraccounts:unstyled
useraccounts:flow-routing
wekan-ldap
wekan-accounts-cas
wekan-accounts-oidc
# Utilities
check@1.3.1
jquery@1.11.10
random@1.1.0
reactive-dict@1.3.0
session@1.2.0
tracker@1.2.0
underscore@1.0.10
3stack:presence
alethes:pages
arillo:flow-router-helpers
audit-argument-checks@1.0.7
kadira:blaze-layout
kadira:dochead
mquandalle:autofocus
ongoworks:speakingurl
raix:handlebar-helpers
tap:i18n
http@1.4.2
# UI components
blaze
reactive-var@1.0.11
fortawesome:fontawesome
mousetrap:mousetrap
mquandalle:jquery-textcomplete
mquandalle:jquery-ui-drag-drop-sort
mquandalle:mousetrap-bindglobal
peerlibrary:blaze-components@=0.15.1
templates:tabs
verron:autosize
simple:json-routes
rajit:bootstrap3-datepicker
shell-server@0.4.0
simple:rest-accounts-password
useraccounts:core
email@1.2.3
horka:swipebox
dynamic-import@0.5.1
staringatlights:fast-render
accounts-password@1.5.2
cfs:gridfs
rzymek:fullcalendar
momentjs:moment@2.22.2
browser-policy-framing@1.1.0
mquandalle:moment
msavin:usercache
wekan-scrollbar
mquandalle:perfect-scrollbar
mdg:meteor-apm-agent@3.2.0-rc.0!
coagmano:stylus
lucasantoniassi:accounts-lockout
meteorhacks:subs-manager
meteorhacks:picker
lamhieu:unblock
meteorhacks:aggregate@1.3.0
wekan-markdown
konecty:mongo-counter
percolate:synced-cron
easylogic:summernote
cfs:filesystem
ostrio:cookies
+2
View File
@@ -0,0 +1,2 @@
server
browser
+1
View File
@@ -0,0 +1 @@
METEOR@1.8.3
+198
View File
@@ -0,0 +1,198 @@
3stack:presence@1.1.2
accounts-base@1.4.5
accounts-oauth@1.1.16
accounts-password@1.5.2
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
alethes:pages@1.8.6
allow-deny@1.1.0
arillo:flow-router-helpers@0.5.2
audit-argument-checks@1.0.7
autoupdate@1.6.0
babel-compiler@7.4.2
babel-runtime@1.4.0
base64@1.0.12
binary-heap@1.0.11
blaze@2.3.4
blaze-tools@1.0.10
boilerplate-generator@1.6.0
browser-policy-common@1.0.11
browser-policy-framing@1.1.0
caching-compiler@1.2.1
caching-html-compiler@1.1.3
callback-hook@1.2.0
cfs:access-point@0.1.49
cfs:base-package@0.0.30
cfs:collection@0.5.5
cfs:collection-filters@0.2.4
cfs:data-man@0.0.6
cfs:file@0.1.17
cfs:filesystem@0.1.2
cfs:gridfs@0.0.34
cfs:http-methods@0.0.32
cfs:http-publish@0.0.13
cfs:power-queue@0.9.11
cfs:reactive-list@0.0.9
cfs:reactive-property@0.0.4
cfs:standard-packages@0.5.10
cfs:storage-adapter@0.2.4
cfs:tempstore@0.1.6
cfs:upload-http@0.0.20
cfs:worker@0.1.5
check@1.3.1
chuangbo:cookie@1.1.0
coagmano:stylus@2.0.0
coffeescript@1.0.17
cottz:publish-relations@2.0.8
dburles:collection-helpers@1.1.0
ddp@1.4.0
ddp-client@2.3.3
ddp-common@1.4.0
ddp-rate-limiter@1.0.7
ddp-server@2.3.0
deps@1.0.12
diff-sequence@1.1.1
dynamic-import@0.5.1
easylogic:summernote@0.8.8
ecmascript@0.13.2
ecmascript-runtime@0.7.0
ecmascript-runtime-client@0.9.0
ecmascript-runtime-server@0.8.0
ejson@1.1.1
email@1.2.3
es5-shim@4.8.0
fastclick@1.0.13
fetch@0.1.1
fortawesome:fontawesome@4.7.0
geojson-utils@1.0.10
horka:swipebox@1.0.2
hot-code-push@1.0.4
html-tools@1.0.11
htmljs@1.0.11
http@1.4.2
id-map@1.1.0
idmontie:migrations@1.0.3
inter-process-messaging@0.1.0
jquery@1.11.11
kadira:blaze-layout@2.3.0
kadira:dochead@1.5.0
kadira:flow-router@2.12.1
kenton:accounts-sandstorm@0.7.0
konecty:mongo-counter@0.0.5_3
lamhieu:meteorx@2.1.1
lamhieu:unblock@1.0.0
launch-screen@1.1.1
livedata@1.0.18
localstorage@1.2.0
logging@1.1.20
lucasantoniassi:accounts-lockout@1.0.0
matb33:collection-hooks@0.9.1
matteodem:easy-search@1.6.4
mdg:meteor-apm-agent@3.2.5
mdg:validation-error@0.5.1
meteor@1.9.3
meteor-base@1.4.0
meteor-platform@1.2.6
meteorhacks:aggregate@1.3.0
meteorhacks:collection-utils@1.2.0
meteorhacks:picker@1.0.3
meteorhacks:subs-manager@1.6.4
meteorspark:util@0.2.0
minifier-css@1.4.3
minifier-js@2.5.1
minifiers@1.1.8-faster-rebuild.0
minimongo@1.4.5
mobile-status-bar@1.0.14
modern-browsers@0.1.4
modules@0.14.0
modules-runtime@0.11.0
momentjs:moment@2.24.0
mongo@1.7.0
mongo-decimal@0.1.1
mongo-dev-server@1.1.0
mongo-id@1.0.7
mongo-livedata@1.0.12
mousetrap:mousetrap@1.4.6_1
mquandalle:autofocus@1.0.0
mquandalle:collection-mutations@0.1.0
mquandalle:jade@0.4.9
mquandalle:jade-compiler@0.4.5
mquandalle:jquery-textcomplete@0.8.0_1
mquandalle:jquery-ui-drag-drop-sort@0.2.0
mquandalle:moment@1.0.1
mquandalle:mousetrap-bindglobal@0.0.1
mquandalle:perfect-scrollbar@0.6.5_2
msavin:usercache@1.8.0
npm-bcrypt@0.9.3
npm-mongo@3.2.0
oauth@1.2.8
oauth2@1.2.1
observe-sequence@1.0.16
ongoworks:speakingurl@1.1.0
ordered-dict@1.1.0
ostrio:cookies@2.5.0
peerlibrary:assert@0.3.0
peerlibrary:base-component@0.16.0
peerlibrary:blaze-components@0.15.1
peerlibrary:computed-field@0.10.0
peerlibrary:reactive-field@0.6.0
percolate:synced-cron@1.3.2
promise@0.11.2
raix:eventemitter@0.1.3
raix:handlebar-helpers@0.2.5
rajit:bootstrap3-datepicker@1.7.1_1
random@1.1.0
rate-limit@1.0.9
reactive-dict@1.3.0
reactive-var@1.0.11
reload@1.3.0
retry@1.1.0
routepolicy@1.1.0
rzymek:fullcalendar@3.8.0
server-render@0.3.1
service-configuration@1.0.11
session@1.2.0
sha@1.0.9
shell-server@0.4.0
simple:authenticate-user-by-token@1.0.1
simple:json-routes@2.1.0
simple:rest-accounts-password@1.1.2
simple:rest-bearer-token-parser@1.0.1
simple:rest-json-error-handler@1.0.1
socket-stream-client@0.2.2
softwarerero:accounts-t9n@1.3.11
spacebars@1.0.15
spacebars-compiler@1.1.3
srp@1.0.12
standard-minifier-css@1.5.4
standard-minifier-js@2.5.2
staringatlights:fast-render@3.2.0
staringatlights:inject-data@2.3.0
tap:i18n@1.8.2
templates:tabs@2.3.0
templating@1.3.2
templating-compiler@1.3.3
templating-runtime@1.3.2
templating-tools@1.1.2
tracker@1.2.0
twbs:bootstrap@3.3.6
ui@1.0.13
underscore@1.0.10
url@1.2.0
useraccounts:core@1.14.2
useraccounts:flow-routing@1.14.2
useraccounts:unstyled@1.14.2
verron:autosize@3.0.8
webapp@1.7.5
webapp-hashing@1.0.9
wekan-accounts-cas@0.1.0
wekan-accounts-oidc@1.0.10
wekan-ldap@0.0.2
wekan-markdown@1.0.7
wekan-oidc@1.0.12
wekan-scrollbar@3.1.3
yasaricli:slugify@0.0.7
zimme:active-route@2.3.2
+914
View File
@@ -0,0 +1,914 @@
(function () {
/* Imports */
var Meteor = Package.meteor.Meteor;
var global = Package.meteor.global;
var meteorEnv = Package.meteor.meteorEnv;
var FS = Package['cfs:base-package'].FS;
var check = Package.check.check;
var Match = Package.check.Match;
var EJSON = Package.ejson.EJSON;
var HTTP = Package['cfs:http-methods'].HTTP;
/* Package-scope variables */
var rootUrlPathPrefix, baseUrl, getHeaders, getHeadersByCollection, _existingMountPoints, mountUrls;
(function(){
///////////////////////////////////////////////////////////////////////
// //
// packages/cfs_access-point/packages/cfs_access-point.js //
// //
///////////////////////////////////////////////////////////////////////
//
(function () {
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// packages/cfs:access-point/access-point-common.js //
// //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
rootUrlPathPrefix = __meteor_runtime_config__.ROOT_URL_PATH_PREFIX || ""; // 1
// Adjust the rootUrlPathPrefix if necessary // 2
if (rootUrlPathPrefix.length > 0) { // 3
if (rootUrlPathPrefix.slice(0, 1) !== '/') { // 4
rootUrlPathPrefix = '/' + rootUrlPathPrefix; // 5
} // 6
if (rootUrlPathPrefix.slice(-1) === '/') { // 7
rootUrlPathPrefix = rootUrlPathPrefix.slice(0, -1); // 8
} // 9
} // 10
// 11
// prepend ROOT_URL when isCordova // 12
if (Meteor.isCordova) { // 13
rootUrlPathPrefix = Meteor.absoluteUrl(rootUrlPathPrefix.replace(/^\/+/, '')).replace(/\/+$/, ''); // 14
} // 15
// 16
baseUrl = '/cfs'; // 17
FS.HTTP = FS.HTTP || {}; // 18
// 19
// Note the upload URL so that client uploader packages know what it is // 20
FS.HTTP.uploadUrl = rootUrlPathPrefix + baseUrl + '/files'; // 21
// 22
/** // 23
* @method FS.HTTP.setBaseUrl // 24
* @public // 25
* @param {String} newBaseUrl - Change the base URL for the HTTP GET and DELETE endpoints. // 26
* @returns {undefined} // 27
*/ // 28
FS.HTTP.setBaseUrl = function setBaseUrl(newBaseUrl) { // 29
// 30
// Adjust the baseUrl if necessary // 31
if (newBaseUrl.slice(0, 1) !== '/') { // 32
newBaseUrl = '/' + newBaseUrl; // 33
} // 34
if (newBaseUrl.slice(-1) === '/') { // 35
newBaseUrl = newBaseUrl.slice(0, -1); // 36
} // 37
// 38
// Update the base URL // 39
baseUrl = newBaseUrl; // 40
// 41
// Change the upload URL so that client uploader packages know what it is // 42
FS.HTTP.uploadUrl = rootUrlPathPrefix + baseUrl + '/files'; // 43
// 44
// Remount URLs with the new baseUrl, unmounting the old, on the server only. // 45
// If existingMountPoints is empty, then we haven't run the server startup // 46
// code yet, so this new URL will be used at that point for the initial mount. // 47
if (Meteor.isServer && !FS.Utility.isEmpty(_existingMountPoints)) { // 48
mountUrls(); // 49
} // 50
}; // 51
// 52
/* // 53
* FS.File extensions // 54
*/ // 55
// 56
/** // 57
* @method FS.File.prototype.url Construct the file url // 58
* @public // 59
* @param {Object} [options] // 60
* @param {String} [options.store] Name of the store to get from. If not defined, the first store defined in `options.stores` for the collection on the client is used.
* @param {Boolean} [options.auth=null] Add authentication token to the URL query string? By default, a token for the current logged in user is added on the client. Set this to `false` to omit the token. Set this to a string to provide your own token. Set this to a number to specify an expiration time for the token in seconds.
* @param {Boolean} [options.download=false] Should headers be set to force a download? Typically this means that clicking the link with this URL will download the file to the user's Downloads folder instead of displaying the file in the browser.
* @param {Boolean} [options.brokenIsFine=false] Return the URL even if we know it's currently a broken link because the file hasn't been saved in the requested store yet.
* @param {Boolean} [options.metadata=false] Return the URL for the file metadata access point rather than the file itself.
* @param {String} [options.uploading=null] A URL to return while the file is being uploaded. // 66
* @param {String} [options.storing=null] A URL to return while the file is being stored. // 67
* @param {String} [options.filename=null] Override the filename that should appear at the end of the URL. By default it is the name of the file in the requested store.
* // 69
* Returns the HTTP URL for getting the file or its metadata. // 70
*/ // 71
FS.File.prototype.url = function(options) { // 72
var self = this; // 73
options = options || {}; // 74
options = FS.Utility.extend({ // 75
store: null, // 76
auth: null, // 77
download: false, // 78
metadata: false, // 79
brokenIsFine: false, // 80
uploading: null, // return this URL while uploading // 81
storing: null, // return this URL while storing // 82
filename: null // override the filename that is shown to the user // 83
}, options.hash || options); // check for "hash" prop if called as helper // 84
// 85
// Primarily useful for displaying a temporary image while uploading an image // 86
if (options.uploading && !self.isUploaded()) { // 87
return options.uploading; // 88
} // 89
// 90
if (self.isMounted()) { // 91
// See if we've stored in the requested store yet // 92
var storeName = options.store || self.collection.primaryStore.name; // 93
if (!self.hasStored(storeName)) { // 94
if (options.storing) { // 95
return options.storing; // 96
} else if (!options.brokenIsFine) { // 97
// We want to return null if we know the URL will be a broken // 98
// link because then we can avoid rendering broken links, broken // 99
// images, etc. // 100
return null; // 101
} // 102
} // 103
// 104
// Add filename to end of URL if we can determine one // 105
var filename = options.filename || self.name({store: storeName}); // 106
if (typeof filename === "string" && filename.length) { // 107
filename = '/' + filename; // 108
} else { // 109
filename = ''; // 110
} // 111
// 112
// TODO: Could we somehow figure out if the collection requires login? // 113
var authToken = ''; // 114
if (Meteor.isClient && typeof Accounts !== "undefined" && typeof Accounts._storedLoginToken === "function") { // 115
if (options.auth !== false) { // 116
// Add reactive deps on the user // 117
Meteor.userId(); // 118
// 119
var authObject = { // 120
authToken: Accounts._storedLoginToken() || '' // 121
}; // 122
// 123
// If it's a number, we use that as the expiration time (in seconds) // 124
if (options.auth === +options.auth) { // 125
authObject.expiration = FS.HTTP.now() + options.auth * 1000; // 126
} // 127
// 128
// Set the authToken // 129
var authString = JSON.stringify(authObject); // 130
authToken = FS.Utility.btoa(authString); // 131
} // 132
} else if (typeof options.auth === "string") { // 133
// If the user supplies auth token the user will be responsible for // 134
// updating // 135
authToken = options.auth; // 136
} // 137
// 138
// Construct query string // 139
var params = {}; // 140
if (authToken !== '') { // 141
params.token = authToken; // 142
} // 143
if (options.download) { // 144
params.download = true; // 145
} // 146
if (options.store) { // 147
// We use options.store here instead of storeName because we want to omit the queryString // 148
// whenever possible, allowing users to have "clean" URLs if they want. The server will // 149
// assume the first store defined on the server, which means that we are assuming that // 150
// the first on the client is also the first on the server. If that's not the case, the // 151
// store option should be supplied. // 152
params.store = options.store; // 153
} // 154
var queryString = FS.Utility.encodeParams(params); // 155
if (queryString.length) { // 156
queryString = '?' + queryString; // 157
} // 158
// 159
// Determine which URL to use // 160
var area; // 161
if (options.metadata) { // 162
area = '/record'; // 163
} else { // 164
area = '/files'; // 165
} // 166
// 167
// Construct and return the http method url // 168
return rootUrlPathPrefix + baseUrl + area + '/' + self.collection.name + '/' + self._id + filename + queryString; // 169
} // 170
// 171
}; // 172
// 173
// 174
// 175
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}).call(this);
(function () {
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// packages/cfs:access-point/access-point-handlers.js //
// //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
getHeaders = []; // 1
getHeadersByCollection = {}; // 2
// 3
FS.HTTP.Handlers = {}; // 4
// 5
/** // 6
* @method FS.HTTP.Handlers.Del // 7
* @public // 8
* @returns {any} response // 9
* // 10
* HTTP DEL request handler // 11
*/ // 12
FS.HTTP.Handlers.Del = function httpDelHandler(ref) { // 13
var self = this; // 14
var opts = FS.Utility.extend({}, self.query || {}, self.params || {}); // 15
// 16
// If DELETE request, validate with 'remove' allow/deny, delete the file, and return // 17
FS.Utility.validateAction(ref.collection.files._validators['remove'], ref.file, self.userId); // 18
// 19
/* // 20
* From the DELETE spec: // 21
* A successful response SHOULD be 200 (OK) if the response includes an // 22
* entity describing the status, 202 (Accepted) if the action has not // 23
* yet been enacted, or 204 (No Content) if the action has been enacted // 24
* but the response does not include an entity. // 25
*/ // 26
self.setStatusCode(200); // 27
// 28
return { // 29
deleted: !!ref.file.remove() // 30
}; // 31
}; // 32
// 33
/** // 34
* @method FS.HTTP.Handlers.GetList // 35
* @public // 36
* @returns {Object} response // 37
* // 38
* HTTP GET file list request handler // 39
*/ // 40
FS.HTTP.Handlers.GetList = function httpGetListHandler() { // 41
// Not Yet Implemented // 42
// Need to check publications and return file list based on // 43
// what user is allowed to see // 44
}; // 45
// 46
/* // 47
requestRange will parse the range set in request header - if not possible it // 48
will throw fitting errors and autofill range for both partial and full ranges // 49
// 50
throws error or returns the object: // 51
{ // 52
start // 53
end // 54
length // 55
unit // 56
partial // 57
} // 58
*/ // 59
var requestRange = function(req, fileSize) { // 60
if (req) { // 61
if (req.headers) { // 62
var rangeString = req.headers.range; // 63
// 64
// Make sure range is a string // 65
if (rangeString === ''+rangeString) { // 66
// 67
// range will be in the format "bytes=0-32767" // 68
var parts = rangeString.split('='); // 69
var unit = parts[0]; // 70
// 71
// Make sure parts consists of two strings and range is of type "byte" // 72
if (parts.length == 2 && unit == 'bytes') { // 73
// Parse the range // 74
var range = parts[1].split('-'); // 75
var start = Number(range[0]); // 76
var end = Number(range[1]); // 77
// 78
// Fix invalid ranges? // 79
if (range[0] != start) start = 0; // 80
if (range[1] != end || !end) end = fileSize - 1; // 81
// 82
// Make sure range consists of a start and end point of numbers and start is less than end // 83
if (start < end) { // 84
// 85
var partSize = 0 - start + end + 1; // 86
// 87
// Return the parsed range // 88
return { // 89
start: start, // 90
end: end, // 91
length: partSize, // 92
size: fileSize, // 93
unit: unit, // 94
partial: (partSize < fileSize) // 95
}; // 96
// 97
} else { // 98
throw new Meteor.Error(416, "Requested Range Not Satisfiable"); // 99
} // 100
// 101
} else { // 102
// The first part should be bytes // 103
throw new Meteor.Error(416, "Requested Range Unit Not Satisfiable"); // 104
} // 105
// 106
} else { // 107
// No range found // 108
} // 109
// 110
} else { // 111
// throw new Error('No request headers set for _parseRange function'); // 112
} // 113
} else { // 114
throw new Error('No request object passed to _parseRange function'); // 115
} // 116
// 117
return { // 118
start: 0, // 119
end: fileSize - 1, // 120
length: fileSize, // 121
size: fileSize, // 122
unit: 'bytes', // 123
partial: false // 124
}; // 125
}; // 126
// 127
/** // 128
* @method FS.HTTP.Handlers.Get // 129
* @public // 130
* @returns {any} response // 131
* // 132
* HTTP GET request handler // 133
*/ // 134
FS.HTTP.Handlers.Get = function httpGetHandler(ref) { // 135
var self = this; // 136
// Once we have the file, we can test allow/deny validators // 137
// XXX: pass on the "share" query eg. ?share=342hkjh23ggj for shared url access? // 138
FS.Utility.validateAction(ref.collection._validators['download'], ref.file, self.userId /*, self.query.shareId*/); // 139
// 140
var storeName = ref.storeName; // 141
// 142
// If no storeName was specified, use the first defined storeName // 143
if (typeof storeName !== "string") { // 144
// No store handed, we default to primary store // 145
storeName = ref.collection.primaryStore.name; // 146
} // 147
// 148
// Get the storage reference // 149
var storage = ref.collection.storesLookup[storeName]; // 150
// 151
if (!storage) { // 152
throw new Meteor.Error(404, "Not Found", 'There is no store "' + storeName + '"'); // 153
} // 154
// 155
// Get the file // 156
var copyInfo = ref.file.copies[storeName]; // 157
// 158
if (!copyInfo) { // 159
throw new Meteor.Error(404, "Not Found", 'This file was not stored in the ' + storeName + ' store'); // 160
} // 161
// 162
// Set the content type for file // 163
if (typeof copyInfo.type === "string") { // 164
self.setContentType(copyInfo.type); // 165
} else { // 166
self.setContentType('application/octet-stream'); // 167
} // 168
// 169
// Add 'Content-Disposition' header if requested a download/attachment URL // 170
if (typeof ref.download !== "undefined") { // 171
var filename = ref.filename || copyInfo.name; // 172
self.addHeader('Content-Disposition', 'attachment; filename="' + filename + '"'); // 173
} else { // 174
self.addHeader('Content-Disposition', 'inline'); // 175
} // 176
// 177
// Get the contents range from request // 178
var range = requestRange(self.request, copyInfo.size); // 179
// 180
// Some browsers cope better if the content-range header is // 181
// still included even for the full file being returned. // 182
self.addHeader('Content-Range', range.unit + ' ' + range.start + '-' + range.end + '/' + range.size); // 183
// 184
// If a chunk/range was requested instead of the whole file, serve that' // 185
if (range.partial) { // 186
self.setStatusCode(206, 'Partial Content'); // 187
} else { // 188
self.setStatusCode(200, 'OK'); // 189
} // 190
// 191
// Add any other global custom headers and collection-specific custom headers // 192
FS.Utility.each(getHeaders.concat(getHeadersByCollection[ref.collection.name] || []), function(header) { // 193
self.addHeader(header[0], header[1]); // 194
}); // 195
// 196
// Inform clients about length (or chunk length in case of ranges) // 197
self.addHeader('Content-Length', range.length); // 198
// 199
// Last modified header (updatedAt from file info) // 200
self.addHeader('Last-Modified', copyInfo.updatedAt.toUTCString()); // 201
// 202
// Inform clients that we accept ranges for resumable chunked downloads // 203
self.addHeader('Accept-Ranges', range.unit); // 204
// 205
if (FS.debug) console.log('Read file "' + (ref.filename || copyInfo.name) + '" ' + range.unit + ' ' + range.start + '-' + range.end + '/' + range.size);
// 207
var readStream = storage.adapter.createReadStream(ref.file, {start: range.start, end: range.end}); // 208
// 209
readStream.on('error', function(err) { // 210
// Send proper error message on get error // 211
if (err.message && err.statusCode) { // 212
self.Error(new Meteor.Error(err.statusCode, err.message)); // 213
} else { // 214
self.Error(new Meteor.Error(503, 'Service unavailable')); // 215
} // 216
}); // 217
// 218
readStream.pipe(self.createWriteStream()); // 219
}; // 220
const originalHandler = FS.HTTP.Handlers.Get;
FS.HTTP.Handlers.Get = function (ref) {
//console.log(ref.filename);
try {
var userAgent = (this.requestHeaders['user-agent']||'').toLowerCase();
if(userAgent.indexOf('msie') >= 0 || userAgent.indexOf('trident') >= 0 || userAgent.indexOf('chrome') >= 0) {
ref.filename = encodeURIComponent(ref.filename);
} else if(userAgent.indexOf('firefox') >= 0) {
ref.filename = new Buffer(ref.filename).toString('binary');
} else {
/* safari*/
ref.filename = new Buffer(ref.filename).toString('binary');
}
} catch (ex){
ref.filename = 'tempfix';
}
return originalHandler.call(this, ref);
};
// 221
/** // 222
* @method FS.HTTP.Handlers.PutInsert // 223
* @public // 224
* @returns {Object} response object with _id property // 225
* // 226
* HTTP PUT file insert request handler // 227
*/ // 228
FS.HTTP.Handlers.PutInsert = function httpPutInsertHandler(ref) { // 229
var self = this; // 230
var opts = FS.Utility.extend({}, self.query || {}, self.params || {}); // 231
// 232
FS.debug && console.log("HTTP PUT (insert) handler"); // 233
// 234
// Create the nice FS.File // 235
var fileObj = new FS.File(); // 236
// 237
// Set its name // 238
fileObj.name(opts.filename || null); // 239
// 240
// Attach the readstream as the file's data // 241
fileObj.attachData(self.createReadStream(), {type: self.requestHeaders['content-type'] || 'application/octet-stream'});
// 243
// Validate with insert allow/deny // 244
FS.Utility.validateAction(ref.collection.files._validators['insert'], fileObj, self.userId); // 245
// 246
// Insert file into collection, triggering readStream storage // 247
ref.collection.insert(fileObj); // 248
// 249
// Send response // 250
self.setStatusCode(200); // 251
// 252
// Return the new file id // 253
return {_id: fileObj._id}; // 254
}; // 255
// 256
/** // 257
* @method FS.HTTP.Handlers.PutUpdate // 258
* @public // 259
* @returns {Object} response object with _id and chunk properties // 260
* // 261
* HTTP PUT file update chunk request handler // 262
*/ // 263
FS.HTTP.Handlers.PutUpdate = function httpPutUpdateHandler(ref) { // 264
var self = this; // 265
var opts = FS.Utility.extend({}, self.query || {}, self.params || {}); // 266
// 267
var chunk = parseInt(opts.chunk, 10); // 268
if (isNaN(chunk)) chunk = 0; // 269
// 270
FS.debug && console.log("HTTP PUT (update) handler received chunk: ", chunk); // 271
// 272
// Validate with insert allow/deny; also mounts and retrieves the file // 273
FS.Utility.validateAction(ref.collection.files._validators['insert'], ref.file, self.userId); // 274
// 275
self.createReadStream().pipe( FS.TempStore.createWriteStream(ref.file, chunk) ); // 276
// 277
// Send response // 278
self.setStatusCode(200); // 279
// 280
return { _id: ref.file._id, chunk: chunk }; // 281
}; // 282
// 283
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}).call(this);
(function () {
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// packages/cfs:access-point/access-point-server.js //
// //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
var path = Npm.require("path"); // 1
// 2
HTTP.publishFormats({ // 3
fileRecordFormat: function (input) { // 4
// Set the method scope content type to json // 5
this.setContentType('application/json'); // 6
if (FS.Utility.isArray(input)) { // 7
return EJSON.stringify(FS.Utility.map(input, function (obj) { // 8
return FS.Utility.cloneFileRecord(obj); // 9
})); // 10
} else { // 11
return EJSON.stringify(FS.Utility.cloneFileRecord(input)); // 12
} // 13
} // 14
}); // 15
// 16
/** // 17
* @method FS.HTTP.setHeadersForGet // 18
* @public // 19
* @param {Array} headers - List of headers, where each is a two-item array in which item 1 is the header name and item 2 is the header value.
* @param {Array|String} [collections] - Which collections the headers should be added for. Omit this argument to add the header for all collections.
* @returns {undefined} // 22
*/ // 23
FS.HTTP.setHeadersForGet = function setHeadersForGet(headers, collections) { // 24
if (typeof collections === "string") { // 25
collections = [collections]; // 26
} // 27
if (collections) { // 28
FS.Utility.each(collections, function(collectionName) { // 29
getHeadersByCollection[collectionName] = headers || []; // 30
}); // 31
} else { // 32
getHeaders = headers || []; // 33
} // 34
}; // 35
// 36
/** // 37
* @method FS.HTTP.publish // 38
* @public // 39
* @param {FS.Collection} collection // 40
* @param {Function} func - Publish function that returns a cursor. // 41
* @returns {undefined} // 42
* // 43
* Publishes all documents returned by the cursor at a GET URL // 44
* with the format baseUrl/record/collectionName. The publish // 45
* function `this` is similar to normal `Meteor.publish`. // 46
*/ // 47
FS.HTTP.publish = function fsHttpPublish(collection, func) { // 48
var name = baseUrl + '/record/' + collection.name; // 49
// Mount collection listing URL using http-publish package // 50
HTTP.publish({ // 51
name: name, // 52
defaultFormat: 'fileRecordFormat', // 53
collection: collection, // 54
collectionGet: true, // 55
collectionPost: false, // 56
documentGet: true, // 57
documentPut: false, // 58
documentDelete: false // 59
}, func); // 60
// 61
FS.debug && console.log("Registered HTTP method GET URLs:\n\n" + name + '\n' + name + '/:id\n'); // 62
}; // 63
// 64
/** // 65
* @method FS.HTTP.unpublish // 66
* @public // 67
* @param {FS.Collection} collection // 68
* @returns {undefined} // 69
* // 70
* Unpublishes a restpoint created by a call to `FS.HTTP.publish` // 71
*/ // 72
FS.HTTP.unpublish = function fsHttpUnpublish(collection) { // 73
// Mount collection listing URL using http-publish package // 74
HTTP.unpublish(baseUrl + '/record/' + collection.name); // 75
}; // 76
// 77
_existingMountPoints = {}; // 78
// 79
/** // 80
* @method defaultSelectorFunction // 81
* @private // 82
* @returns { collection, file } // 83
* // 84
* This is the default selector function // 85
*/ // 86
var defaultSelectorFunction = function() { // 87
var self = this; // 88
// Selector function // 89
// // 90
// This function will have to return the collection and the // 91
// file. If file not found undefined is returned - if null is returned the // 92
// search was not possible // 93
var opts = FS.Utility.extend({}, self.query || {}, self.params || {}); // 94
// 95
// Get the collection name from the url // 96
var collectionName = opts.collectionName; // 97
// 98
// Get the id from the url // 99
var id = opts.id; // 100
// 101
// Get the collection // 102
var collection = FS._collections[collectionName]; // 103
// 104
// Get the file if possible else return null // 105
var file = (id && collection)? collection.findOne({ _id: id }): null; // 106
// 107
// Return the collection and the file // 108
return { // 109
collection: collection, // 110
file: file, // 111
storeName: opts.store, // 112
download: opts.download, // 113
filename: opts.filename // 114
}; // 115
}; // 116
// 117
/* // 118
* @method FS.HTTP.mount // 119
* @public // 120
* @param {array of string} mountPoints mount points to map rest functinality on // 121
* @param {function} selector_f [selector] function returns `{ collection, file }` for mount points to work with // 122
* // 123
*/ // 124
FS.HTTP.mount = function(mountPoints, selector_f) { // 125
// We take mount points as an array and we get a selector function // 126
var selectorFunction = selector_f || defaultSelectorFunction; // 127
// 128
var accessPoint = { // 129
'stream': true, // 130
'auth': expirationAuth, // 131
'post': function(data) { // 132
// Use the selector for finding the collection and file reference // 133
var ref = selectorFunction.call(this); // 134
// 135
// We dont support post - this would be normal insert eg. of filerecord? // 136
throw new Meteor.Error(501, "Not implemented", "Post is not supported"); // 137
}, // 138
'put': function(data) { // 139
// Use the selector for finding the collection and file reference // 140
var ref = selectorFunction.call(this); // 141
// 142
// Make sure we have a collection reference // 143
if (!ref.collection) // 144
throw new Meteor.Error(404, "Not Found", "No collection found"); // 145
// 146
// Make sure we have a file reference // 147
if (ref.file === null) { // 148
// No id supplied so we will create a new FS.File instance and // 149
// insert the supplied data. // 150
return FS.HTTP.Handlers.PutInsert.apply(this, [ref]); // 151
} else { // 152
if (ref.file) { // 153
return FS.HTTP.Handlers.PutUpdate.apply(this, [ref]); // 154
} else { // 155
throw new Meteor.Error(404, "Not Found", 'No file found'); // 156
} // 157
} // 158
}, // 159
'get': function(data) { // 160
// Use the selector for finding the collection and file reference // 161
var ref = selectorFunction.call(this); // 162
// 163
// Make sure we have a collection reference // 164
if (!ref.collection) // 165
throw new Meteor.Error(404, "Not Found", "No collection found"); // 166
// 167
// Make sure we have a file reference // 168
if (ref.file === null) { // 169
// No id supplied so we will return the published list of files ala // 170
// http.publish in json format // 171
return FS.HTTP.Handlers.GetList.apply(this, [ref]); // 172
} else { // 173
if (ref.file) { // 174
return FS.HTTP.Handlers.Get.apply(this, [ref]); // 175
} else { // 176
throw new Meteor.Error(404, "Not Found", 'No file found'); // 177
} // 178
} // 179
}, // 180
'delete': function(data) { // 181
// Use the selector for finding the collection and file reference // 182
var ref = selectorFunction.call(this); // 183
// 184
// Make sure we have a collection reference // 185
if (!ref.collection) // 186
throw new Meteor.Error(404, "Not Found", "No collection found"); // 187
// 188
// Make sure we have a file reference // 189
if (ref.file) { // 190
return FS.HTTP.Handlers.Del.apply(this, [ref]); // 191
} else { // 192
throw new Meteor.Error(404, "Not Found", 'No file found'); // 193
} // 194
} // 195
}; // 196
// 197
var accessPoints = {}; // 198
// 199
// Add debug message // 200
FS.debug && console.log('Registered HTTP method URLs:'); // 201
// 202
FS.Utility.each(mountPoints, function(mountPoint) { // 203
// Couple mountpoint and accesspoint // 204
accessPoints[mountPoint] = accessPoint; // 205
// Remember our mountpoints // 206
_existingMountPoints[mountPoint] = mountPoint; // 207
// Add debug message // 208
FS.debug && console.log(mountPoint); // 209
}); // 210
// 211
// XXX: HTTP:methods should unmount existing mounts in case of overwriting? // 212
HTTP.methods(accessPoints); // 213
// 214
}; // 215
// 216
/** // 217
* @method FS.HTTP.unmount // 218
* @public // 219
* @param {string | array of string} [mountPoints] Optional, if not specified all mountpoints are unmounted // 220
* // 221
*/ // 222
FS.HTTP.unmount = function(mountPoints) { // 223
// The mountPoints is optional, can be string or array if undefined then // 224
// _existingMountPoints will be used // 225
var unmountList; // 226
// Container for the mount points to unmount // 227
var unmountPoints = {}; // 228
// 229
if (typeof mountPoints === 'undefined') { // 230
// Use existing mount points - unmount all // 231
unmountList = _existingMountPoints; // 232
} else if (mountPoints === ''+mountPoints) { // 233
// Got a string // 234
unmountList = [mountPoints]; // 235
} else if (mountPoints.length) { // 236
// Got an array // 237
unmountList = mountPoints; // 238
} // 239
// 240
// If we have a list to unmount // 241
if (unmountList) { // 242
// Iterate over each item // 243
FS.Utility.each(unmountList, function(mountPoint) { // 244
// Check _existingMountPoints to make sure the mount point exists in our // 245
// context / was created by the FS.HTTP.mount // 246
if (_existingMountPoints[mountPoint]) { // 247
// Mark as unmount // 248
unmountPoints[mountPoint] = false; // 249
// Release // 250
delete _existingMountPoints[mountPoint]; // 251
} // 252
}); // 253
FS.debug && console.log('FS.HTTP.unmount:'); // 254
FS.debug && console.log(unmountPoints); // 255
// Complete unmount // 256
HTTP.methods(unmountPoints); // 257
} // 258
}; // 259
// 260
// ### FS.Collection maps on HTTP pr. default on the following restpoints: // 261
// * // 262
// baseUrl + '/files/:collectionName/:id/:filename', // 263
// baseUrl + '/files/:collectionName/:id', // 264
// baseUrl + '/files/:collectionName' // 265
// // 266
// Change/ replace the existing mount point by: // 267
// ```js // 268
// // unmount all existing // 269
// FS.HTTP.unmount(); // 270
// // Create new mount point // 271
// FS.HTTP.mount([ // 272
// '/cfs/files/:collectionName/:id/:filename', // 273
// '/cfs/files/:collectionName/:id', // 274
// '/cfs/files/:collectionName' // 275
// ]); // 276
// ``` // 277
// // 278
mountUrls = function mountUrls() { // 279
// We unmount first in case we are calling this a second time // 280
FS.HTTP.unmount(); // 281
// 282
FS.HTTP.mount([ // 283
baseUrl + '/files/:collectionName/:id/:filename', // 284
baseUrl + '/files/:collectionName/:id', // 285
baseUrl + '/files/:collectionName' // 286
]); // 287
}; // 288
// 289
// Returns the userId from URL token // 290
var expirationAuth = function expirationAuth() { // 291
var self = this; // 292
// 293
// Read the token from '/hello?token=base64' // 294
var encodedToken = self.query.token; // 295
// 296
FS.debug && console.log("token: "+encodedToken); // 297
// 298
if (!encodedToken || !Meteor.users) return false; // 299
// 300
// Check the userToken before adding it to the db query // 301
// Set the this.userId // 302
var tokenString = FS.Utility.atob(encodedToken); // 303
// 304
var tokenObject; // 305
try { // 306
tokenObject = JSON.parse(tokenString); // 307
} catch(err) { // 308
throw new Meteor.Error(400, 'Bad Request'); // 309
} // 310
// 311
// XXX: Do some check here of the object // 312
var userToken = tokenObject.authToken; // 313
if (userToken !== ''+userToken) { // 314
throw new Meteor.Error(400, 'Bad Request'); // 315
} // 316
// 317
// If we have an expiration token we should check that it's still valid // 318
if (tokenObject.expiration != null) { // 319
// check if its too old // 320
var now = Date.now(); // 321
if (tokenObject.expiration < now) { // 322
FS.debug && console.log('Expired token: ' + tokenObject.expiration + ' is less than ' + now); // 323
throw new Meteor.Error(500, 'Expired token'); // 324
} // 325
} // 326
// 327
// We are not on a secure line - so we have to look up the user... // 328
var user = Meteor.users.findOne({ // 329
$or: [ // 330
{'services.resume.loginTokens.hashedToken': Accounts._hashLoginToken(userToken)}, // 331
{'services.resume.loginTokens.token': userToken} // 332
] // 333
}); // 334
// 335
// Set the userId in the scope // 336
return user && user._id; // 337
}; // 338
// 339
HTTP.methods( // 340
{'/cfs/servertime': { // 341
get: function(data) { // 342
return Date.now().toString(); // 343
} // 344
} // 345
}); // 346
// 347
// Unify client / server api // 348
FS.HTTP.now = function() { // 349
return Date.now(); // 350
}; // 351
// 352
// Start up the basic mount points // 353
Meteor.startup(function () { // 354
mountUrls(); // 355
}); // 356
// 357
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}).call(this);
///////////////////////////////////////////////////////////////////////
}).call(this);
/* Exports */
if (typeof Package === 'undefined') Package = {};
Package['cfs:access-point'] = {};
})();
+238
View File
@@ -0,0 +1,238 @@
/* global JsonRoutes */
if (Meteor.isServer) {
// todo XXX once we have a real API in place, move that route there
// todo XXX also share the route definition between the client and the server
// so that we could use something like
// `ApiRoutes.path('boards/export', boardId)``
// on the client instead of copy/pasting the route path manually between the
// client and the server.
/**
* @operation export
* @tag Boards
*
* @summary This route is used to export the board.
*
* @description If user is already logged-in, pass loginToken as param
* "authToken": '/api/boards/:boardId/export?authToken=:token'
*
* See https://blog.kayla.com.au/server-side-route-authentication-in-meteor/
* for detailed explanations
*
* @param {string} boardId the ID of the board we are exporting
* @param {string} authToken the loginToken
*/
JsonRoutes.add('get', '/api/boards/:boardId/export', function(req, res) {
const boardId = req.params.boardId;
let user = null;
const loginToken = req.query.authToken;
if (loginToken) {
const hashToken = Accounts._hashLoginToken(loginToken);
user = Meteor.users.findOne({
'services.resume.loginTokens.hashedToken': hashToken,
});
} else if (!Meteor.settings.public.sandstorm) {
Authentication.checkUserId(req.userId);
user = Users.findOne({ _id: req.userId, isAdmin: true });
}
const exporter = new Exporter(boardId);
if (exporter.canExport(user)) {
JsonRoutes.sendResult(res, {
code: 200,
data: exporter.build(),
});
} else {
// we could send an explicit error message, but on the other hand the only
// way to get there is by hacking the UI so let's keep it raw.
JsonRoutes.sendResult(res, 403);
}
});
}
// exporter maybe is broken since Gridfs introduced, add fs and path
export class Exporter {
constructor(boardId) {
this._boardId = boardId;
}
build() {
const fs = Npm.require('fs');
const os = Npm.require('os');
const path = Npm.require('path');
const byBoard = { boardId: this._boardId };
const byBoardNoLinked = {
boardId: this._boardId,
linkedId: { $in: ['', null] },
};
// we do not want to retrieve boardId in related elements
const noBoardId = {
fields: {
boardId: 0,
},
};
const result = {
_format: 'wekan-board-1.0.0',
};
_.extend(
result,
Boards.findOne(this._boardId, {
fields: {
stars: 0,
},
}),
);
result.lists = Lists.find(byBoard, noBoardId).fetch();
result.cards = Cards.find(byBoardNoLinked, noBoardId).fetch();
result.swimlanes = Swimlanes.find(byBoard, noBoardId).fetch();
result.customFields = CustomFields.find(
{ boardIds: { $in: [this.boardId] } },
{ fields: { boardId: 0 } },
).fetch();
result.comments = CardComments.find(byBoard, noBoardId).fetch();
result.activities = Activities.find(byBoard, noBoardId).fetch();
result.rules = Rules.find(byBoard, noBoardId).fetch();
result.checklists = [];
result.checklistItems = [];
result.subtaskItems = [];
result.triggers = [];
result.actions = [];
result.cards.forEach(card => {
result.checklists.push(
...Checklists.find({
cardId: card._id,
}).fetch(),
);
result.checklistItems.push(
...ChecklistItems.find({
cardId: card._id,
}).fetch(),
);
result.subtaskItems.push(
...Cards.find({
parentId: card._id,
}).fetch(),
);
});
result.rules.forEach(rule => {
result.triggers.push(
...Triggers.find(
{
_id: rule.triggerId,
},
noBoardId,
).fetch(),
);
result.actions.push(
...Actions.find(
{
_id: rule.actionId,
},
noBoardId,
).fetch(),
);
});
// [Old] for attachments we only export IDs and absolute url to original doc
// [New] Encode attachment to base64
const getBase64Data = function(doc, callback) {
let buffer = new Buffer(0);
// callback has the form function (err, res) {}
const tmpFile = path.join(
os.tmpdir(),
`tmpexport${process.pid}${Math.random()}`,
);
const tmpWriteable = fs.createWriteStream(tmpFile);
const readStream = doc.createReadStream();
readStream.on('data', function(chunk) {
buffer = Buffer.concat([buffer, chunk]);
});
readStream.on('error', function(err) {
callback(err, null);
});
readStream.on('end', function() {
// done
fs.unlink(tmpFile, () => {
//ignored
});
callback(null, buffer.toString('base64'));
});
readStream.pipe(tmpWriteable);
};
const getBase64DataSync = Meteor.wrapAsync(getBase64Data);
result.attachments = Attachments.find(byBoard)
.fetch()
.map(attachment => {
return {
_id: attachment._id,
cardId: attachment.cardId,
// url: FlowRouter.url(attachment.url()),
file: getBase64DataSync(attachment),
name: attachment.original.name,
type: attachment.original.type,
};
});
// we also have to export some user data - as the other elements only
// include id but we have to be careful:
// 1- only exports users that are linked somehow to that board
// 2- do not export any sensitive information
const users = {};
result.members.forEach(member => {
users[member.userId] = true;
});
result.lists.forEach(list => {
users[list.userId] = true;
});
result.cards.forEach(card => {
users[card.userId] = true;
if (card.members) {
card.members.forEach(memberId => {
users[memberId] = true;
});
}
});
result.comments.forEach(comment => {
users[comment.userId] = true;
});
result.activities.forEach(activity => {
users[activity.userId] = true;
});
result.checklists.forEach(checklist => {
users[checklist.userId] = true;
});
const byUserIds = {
_id: {
$in: Object.getOwnPropertyNames(users),
},
};
// we use whitelist to be sure we do not expose inadvertently
// some secret fields that gets added to User later.
const userFields = {
fields: {
_id: 1,
username: 1,
'profile.fullname': 1,
'profile.initials': 1,
'profile.avatarUrl': 1,
},
};
result.users = Users.find(byUserIds, userFields)
.fetch()
.map(user => {
// user avatar is stored as a relative url, we export absolute
if ((user.profile || {}).avatarUrl) {
user.profile.avatarUrl = FlowRouter.url(user.profile.avatarUrl);
}
return user;
});
return result;
}
canExport(user) {
const board = Boards.findOne(this._boardId);
return board && board.isVisibleBy(user);
}
}
+155
View File
@@ -0,0 +1,155 @@
name: wekan
version: git
summary: The open-source kanban
description: |
Wekan is an open-source and collaborative kanban board application.
Whether you’re maintaining a personal todo list, planning your holidays with some friends, or working in a team on your next revolutionary idea, Kanban boards are an unbeatable tool to keep your things organized. They give you a visual overview of the current state of your project, and make you productive by allowing you to focus on the few items that matter the most.
Depending on target environment, some configuration settings might need to be adjusted.
For full list of configuration options call:
$ wekan.help
confinement: strict
grade: stable
base: core18
architectures:
- amd64
plugs:
mongodb-plug:
interface: content
target: $SNAP_DATA/shared
hooks:
configure:
plugs:
- network
- network-bind
slots:
mongodb-slot:
interface: content
write:
- $SNAP_DATA/share
apps:
wekan:
command: wekan-control
daemon: simple
plugs: [network, network-bind]
mongodb:
command: mongodb-control
daemon: simple
plugs: [network, network-bind]
caddy:
command: caddy-control
daemon: simple
plugs: [network, network-bind]
help:
command: wekan-help
database-backup:
command: mongodb-backup
plugs: [network, network-bind]
database-list-backups:
command: ls -al $SNAP_COMMON/db-backups/
database-restore:
command: mongodb-restore
plugs: [network, network-bind]
parts:
mongodb:
source: https://repo.mongodb.org/apt/ubuntu/dists/xenial/mongodb-org/4.2/multiverse/binary-amd64/mongodb-org-server_4.2.2_amd64.deb
#https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-4.0.14.tgz
#https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1604-3.2.22.tgz
plugin: dump
stage-packages: [libssl1.0.0, libcurl3]
filesets:
mongo:
- usr
- bin
- lib
stage:
- $mongo
prime:
- $mongo
wekan:
source: .
plugin: nodejs
node-engine: 12.14.1
node-packages:
- node-gyp
- node-pre-gyp
- fibers
build-packages:
- ca-certificates
- apt-utils
- build-essential
- python
- python3
- g++
- capnproto
- curl
- libcurl3
- execstack
- nodejs
- npm
stage-packages:
- libfontconfig1
override-build: |
echo "Cleaning environment first"
rm -rf ~/.meteor ~/.npm /usr/local/lib/node_modules
rm -rf .build
echo "Installing meteor"
curl https://install.meteor.com/ -o install_meteor.sh
chmod +x install_meteor.sh
sh install_meteor.sh
rm install_meteor.sh
rm -rf .build
meteor add standard-minifier-js --allow-superuser
meteor npm install --allow-superuser
meteor npm install --allow-superuser --save babel-runtime
meteor build .build --directory --allow-superuser
cp -f fix-download-unicode/cfs_access-point.txt .build/bundle/programs/server/packages/cfs_access-point.js
cd .build/bundle/programs/server
npm install
npm install --allow-superuser --save babel-runtime
# Change back to Wekan source directory
cd ../../../..
cp -r .build/bundle/* $SNAPCRAFT_PART_INSTALL/
cp .build/bundle/.node_version.txt $SNAPCRAFT_PART_INSTALL/
rm -f $SNAPCRAFT_PART_INSTALL/lib/node_modules/wekan
rm -f $SNAPCRAFT_PART_INSTALL/programs/server/npm/node_modules/meteor/rajit_bootstrap3-datepicker/lib/bootstrap-datepicker/node_modules/phantomjs-prebuilt/lib/phantom/bin/phantomjs
rm -f $SNAPCRAFT_PART_INSTALL/programs/server/npm/node_modules/tar/lib/.mkdir.js.swp
rm -f $SNAPCRAFT_PART_INSTALL/lib/node_modules/node-pre-gyp/node_modules/tar/lib/.mkdir.js.swp
rm -f $SNAPCRAFT_PART_INSTALL/lib/node_modules/node-gyp/node_modules/tar/lib/.mkdir.js.swp
rm -f $SNAPCRAFT_PART_INSTALL/programs/server/node_modules/node-pre-gyp/node_modules/tar/lib/.mkdir.js.swp
organize:
README: README.wekan
prime:
- -lib/node_modules/node-pre-gyp/node_modules/tar/lib/.unpack.js.swp
helpers:
source: snap-src
plugin: dump
caddy:
plugin: dump
source: https://caddyserver.com/download/linux/amd64?license=personal&telemetry=off
source-type: tar
organize:
caddy: bin/caddy
CHANGES.txt: CADDY_CHANGES.txt
EULA.txt: CADDY_EULA.txt
LICENSES.txt: CADDY_LICENSES.txt
README.txt: CADDY_README.txt
stage:
- -init
+584
View File
@@ -0,0 +1,584 @@
import ldapjs from 'ldapjs';
import util from 'util';
import Bunyan from 'bunyan';
import {log_debug, log_info, log_warn, log_error} from './logger';
export default class LDAP {
constructor() {
this.ldapjs = ldapjs;
this.connected = false;
this.options = {
host : this.constructor.settings_get('LDAP_HOST'),
port : this.constructor.settings_get('LDAP_PORT'),
Reconnect : this.constructor.settings_get('LDAP_RECONNECT'),
timeout : this.constructor.settings_get('LDAP_TIMEOUT'),
connect_timeout : this.constructor.settings_get('LDAP_CONNECT_TIMEOUT'),
idle_timeout : this.constructor.settings_get('LDAP_IDLE_TIMEOUT'),
encryption : this.constructor.settings_get('LDAP_ENCRYPTION'),
ca_cert : this.constructor.settings_get('LDAP_CA_CERT'),
reject_unauthorized : this.constructor.settings_get('LDAP_REJECT_UNAUTHORIZED') || false,
Authentication : this.constructor.settings_get('LDAP_AUTHENTIFICATION'),
Authentication_UserDN : this.constructor.settings_get('LDAP_AUTHENTIFICATION_USERDN'),
Authentication_Password : this.constructor.settings_get('LDAP_AUTHENTIFICATION_PASSWORD'),
Authentication_Fallback : this.constructor.settings_get('LDAP_LOGIN_FALLBACK'),
BaseDN : this.constructor.settings_get('LDAP_BASEDN'),
Internal_Log_Level : this.constructor.settings_get('INTERNAL_LOG_LEVEL'),
User_Authentication : this.constructor.settings_get('LDAP_USER_AUTHENTICATION'),
User_Authentication_Field : this.constructor.settings_get('LDAP_USER_AUTHENTICATION_FIELD'),
User_Attributes : this.constructor.settings_get('LDAP_USER_ATTRIBUTES'),
User_Search_Filter : this.constructor.settings_get('LDAP_USER_SEARCH_FILTER'),
User_Search_Scope : this.constructor.settings_get('LDAP_USER_SEARCH_SCOPE'),
User_Search_Field : this.constructor.settings_get('LDAP_USER_SEARCH_FIELD'),
Search_Page_Size : this.constructor.settings_get('LDAP_SEARCH_PAGE_SIZE'),
Search_Size_Limit : this.constructor.settings_get('LDAP_SEARCH_SIZE_LIMIT'),
group_filter_enabled : this.constructor.settings_get('LDAP_GROUP_FILTER_ENABLE'),
group_filter_object_class : this.constructor.settings_get('LDAP_GROUP_FILTER_OBJECTCLASS'),
group_filter_group_id_attribute : this.constructor.settings_get('LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE'),
group_filter_group_member_attribute: this.constructor.settings_get('LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE'),
group_filter_group_member_format : this.constructor.settings_get('LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT'),
group_filter_group_name : this.constructor.settings_get('LDAP_GROUP_FILTER_GROUP_NAME'),
};
}
static settings_get(name, ...args) {
let value = process.env[name];
if (value !== undefined) {
if (value === 'true' || value === 'false') {
value = JSON.parse(value);
} else if (value !== '' && !isNaN(value)) {
value = Number(value);
}
return value;
} else {
log_warn(`Lookup for unset variable: ${name}`);
}
}
connectSync(...args) {
if (!this._connectSync) {
this._connectSync = Meteor.wrapAsync(this.connectAsync, this);
}
return this._connectSync(...args);
}
searchAllSync(...args) {
if (!this._searchAllSync) {
this._searchAllSync = Meteor.wrapAsync(this.searchAllAsync, this);
}
return this._searchAllSync(...args);
}
connectAsync(callback) {
log_info('Init setup');
let replied = false;
const connectionOptions = {
url : `${this.options.host}:${this.options.port}`,
timeout : this.options.timeout,
connectTimeout: this.options.connect_timeout,
idleTimeout : this.options.idle_timeout,
reconnect : this.options.Reconnect,
};
if (this.options.Internal_Log_Level !== 'disabled') {
connectionOptions.log = new Bunyan({
name : 'ldapjs',
component: 'client',
stream : process.stderr,
level : this.options.Internal_Log_Level,
});
}
const tlsOptions = {
rejectUnauthorized: this.options.reject_unauthorized,
};
if (this.options.ca_cert && this.options.ca_cert !== '') {
// Split CA cert into array of strings
const chainLines = this.constructor.settings_get('LDAP_CA_CERT').split('\n');
let cert = [];
const ca = [];
chainLines.forEach((line) => {
cert.push(line);
if (line.match(/-END CERTIFICATE-/)) {
ca.push(cert.join('\n'));
cert = [];
}
});
tlsOptions.ca = ca;
}
if (this.options.encryption === 'ssl') {
connectionOptions.url = `ldaps://${connectionOptions.url}`;
connectionOptions.tlsOptions = tlsOptions;
} else {
connectionOptions.url = `ldap://${connectionOptions.url}`;
}
log_info('Connecting', connectionOptions.url);
log_debug(`connectionOptions${util.inspect(connectionOptions)}`);
this.client = ldapjs.createClient(connectionOptions);
this.bindSync = Meteor.wrapAsync(this.client.bind, this.client);
this.client.on('error', (error) => {
log_error('connection', error);
if (replied === false) {
replied = true;
callback(error, null);
}
});
this.client.on('idle', () => {
log_info('Idle');
this.disconnect();
});
this.client.on('close', () => {
log_info('Closed');
});
if (this.options.encryption === 'tls') {
// Set host parameter for tls.connect which is used by ldapjs starttls. This shouldn't be needed in newer nodejs versions (e.g v5.6.0).
// https://github.com/RocketChat/Rocket.Chat/issues/2035
// https://github.com/mcavage/node-ldapjs/issues/349
tlsOptions.host = this.options.host;
log_info('Starting TLS');
log_debug('tlsOptions', tlsOptions);
this.client.starttls(tlsOptions, null, (error, response) => {
if (error) {
log_error('TLS connection', error);
if (replied === false) {
replied = true;
callback(error, null);
}
return;
}
log_info('TLS connected');
this.connected = true;
if (replied === false) {
replied = true;
callback(null, response);
}
});
} else {
this.client.on('connect', (response) => {
log_info('LDAP connected');
this.connected = true;
if (replied === false) {
replied = true;
callback(null, response);
}
});
}
setTimeout(() => {
if (replied === false) {
log_error('connection time out', connectionOptions.connectTimeout);
replied = true;
callback(new Error('Timeout'));
}
}, connectionOptions.connectTimeout);
}
getUserFilter(username) {
const filter = [];
if (this.options.User_Search_Filter !== '') {
if (this.options.User_Search_Filter[0] === '(') {
filter.push(`${this.options.User_Search_Filter}`);
} else {
filter.push(`(${this.options.User_Search_Filter})`);
}
}
const usernameFilter = this.options.User_Search_Field.split(',').map((item) => `(${item}=${username})`);
if (usernameFilter.length === 0) {
log_error('LDAP_LDAP_User_Search_Field not defined');
} else if (usernameFilter.length === 1) {
filter.push(`${usernameFilter[0]}`);
} else {
filter.push(`(|${usernameFilter.join('')})`);
}
return `(&${filter.join('')})`;
}
bindUserIfNecessary(username, password) {
if (this.domainBinded === true) {
return;
}
if (!this.options.User_Authentication) {
return;
}
if (!this.options.BaseDN) throw new Error('BaseDN is not provided');
const userDn = `${this.options.User_Authentication_Field}=${username},${this.options.BaseDN}`;
this.bindSync(userDn, password);
this.domainBinded = true;
}
bindIfNecessary() {
if (this.domainBinded === true) {
return;
}
if (this.options.Authentication !== true) {
return;
}
log_info('Binding UserDN', this.options.Authentication_UserDN);
this.bindSync(this.options.Authentication_UserDN, this.options.Authentication_Password);
this.domainBinded = true;
}
searchUsersSync(username, page) {
this.bindIfNecessary();
const searchOptions = {
filter : this.getUserFilter(username),
scope : this.options.User_Search_Scope || 'sub',
sizeLimit: this.options.Search_Size_Limit,
};
if (!!this.options.User_Attributes) searchOptions.attributes = this.options.User_Attributes.split(',');
if (this.options.Search_Page_Size > 0) {
searchOptions.paged = {
pageSize : this.options.Search_Page_Size,
pagePause: !!page,
};
}
log_info('Searching user', username);
log_debug('searchOptions', searchOptions);
log_debug('BaseDN', this.options.BaseDN);
if (page) {
return this.searchAllPaged(this.options.BaseDN, searchOptions, page);
}
return this.searchAllSync(this.options.BaseDN, searchOptions);
}
getUserByIdSync(id, attribute) {
this.bindIfNecessary();
const Unique_Identifier_Field = this.constructor.settings_get('LDAP_UNIQUE_IDENTIFIER_FIELD').split(',');
let filter;
if (attribute) {
filter = new this.ldapjs.filters.EqualityFilter({
attribute,
value: new Buffer(id, 'hex'),
});
} else {
const filters = [];
Unique_Identifier_Field.forEach((item) => {
filters.push(new this.ldapjs.filters.EqualityFilter({
attribute: item,
value : new Buffer(id, 'hex'),
}));
});
filter = new this.ldapjs.filters.OrFilter({ filters });
}
const searchOptions = {
filter,
scope: 'sub',
};
log_info('Searching by id', id);
log_debug('search filter', searchOptions.filter.toString());
log_debug('BaseDN', this.options.BaseDN);
const result = this.searchAllSync(this.options.BaseDN, searchOptions);
if (!Array.isArray(result) || result.length === 0) {
return;
}
if (result.length > 1) {
log_error('Search by id', id, 'returned', result.length, 'records');
}
return result[0];
}
getUserByUsernameSync(username) {
this.bindIfNecessary();
const searchOptions = {
filter: this.getUserFilter(username),
scope : this.options.User_Search_Scope || 'sub',
};
log_info('Searching user', username);
log_debug('searchOptions', searchOptions);
log_debug('BaseDN', this.options.BaseDN);
const result = this.searchAllSync(this.options.BaseDN, searchOptions);
if (!Array.isArray(result) || result.length === 0) {
return;
}
if (result.length > 1) {
log_error('Search by username', username, 'returned', result.length, 'records');
}
return result[0];
}
getUserGroups(username, ldapUser) {
if (!this.options.group_filter_enabled) {
return true;
}
const filter = ['(&'];
if (this.options.group_filter_object_class !== '') {
filter.push(`(objectclass=${this.options.group_filter_object_class})`);
}
if (this.options.group_filter_group_member_attribute !== '') {
const format_value = ldapUser[this.options.group_filter_group_member_format];
if (format_value) {
filter.push(`(${this.options.group_filter_group_member_attribute}=${format_value})`);
}
}
filter.push(')');
const searchOptions = {
filter: filter.join('').replace(/#{username}/g, username),
scope : 'sub',
};
log_debug('Group list filter LDAP:', searchOptions.filter);
const result = this.searchAllSync(this.options.BaseDN, searchOptions);
if (!Array.isArray(result) || result.length === 0) {
return [];
}
const grp_identifier = this.options.group_filter_group_id_attribute || 'cn';
const groups = [];
result.map((item) => {
groups.push(item[grp_identifier]);
});
log_debug(`Groups: ${groups.join(', ')}`);
return groups;
}
isUserInGroup(username, ldapUser) {
if (!this.options.group_filter_enabled) {
return true;
}
const grps = this.getUserGroups(username, ldapUser);
const filter = ['(&'];
if (this.options.group_filter_object_class !== '') {
filter.push(`(objectclass=${this.options.group_filter_object_class})`);
}
if (this.options.group_filter_group_member_attribute !== '') {
const format_value = ldapUser[this.options.group_filter_group_member_format];
if (format_value) {
filter.push(`(${this.options.group_filter_group_member_attribute}=${format_value})`);
}
}
if (this.options.group_filter_group_id_attribute !== '') {
filter.push(`(${this.options.group_filter_group_id_attribute}=${this.options.group_filter_group_name})`);
}
filter.push(')');
const searchOptions = {
filter: filter.join('').replace(/#{username}/g, username),
scope : 'sub',
};
log_debug('Group filter LDAP:', searchOptions.filter);
const result = this.searchAllSync(this.options.BaseDN, searchOptions);
if (!Array.isArray(result) || result.length === 0) {
return false;
}
return true;
}
extractLdapEntryData(entry) {
const values = {
_raw: entry.raw,
};
Object.keys(values._raw).forEach((key) => {
const value = values._raw[key];
if (!['thumbnailPhoto', 'jpegPhoto'].includes(key)) {
if (value instanceof Buffer) {
values[key] = value.toString();
} else {
values[key] = value;
}
}
});
return values;
}
searchAllPaged(BaseDN, options, page) {
this.bindIfNecessary();
const processPage = ({ entries, title, end, next }) => {
log_info(title);
// Force LDAP idle to wait the record processing
this.client._updateIdle(true);
page(null, entries, {
end, next: () => {
// Reset idle timer
this.client._updateIdle();
next && next();
}
});
};
this.client.search(BaseDN, options, (error, res) => {
if (error) {
log_error(error);
page(error);
return;
}
res.on('error', (error) => {
log_error(error);
page(error);
return;
});
let entries = [];
const internalPageSize = options.paged && options.paged.pageSize > 0 ? options.paged.pageSize * 2 : 500;
res.on('searchEntry', (entry) => {
entries.push(this.extractLdapEntryData(entry));
if (entries.length >= internalPageSize) {
processPage({
entries,
title: 'Internal Page',
end : false,
});
entries = [];
}
});
res.on('page', (result, next) => {
if (!next) {
this.client._updateIdle(true);
processPage({
entries,
title: 'Final Page',
end : true,
});
} else if (entries.length) {
log_info('Page');
processPage({
entries,
title: 'Page',
end : false,
next,
});
entries = [];
}
});
res.on('end', () => {
if (entries.length) {
processPage({
entries,
title: 'Final Page',
end : true,
});
entries = [];
}
});
});
}
searchAllAsync(BaseDN, options, callback) {
this.bindIfNecessary();
this.client.search(BaseDN, options, (error, res) => {
if (error) {
log_error(error);
callback(error);
return;
}
res.on('error', (error) => {
log_error(error);
callback(error);
return;
});
const entries = [];
res.on('searchEntry', (entry) => {
entries.push(this.extractLdapEntryData(entry));
});
res.on('end', () => {
log_info('Search result count', entries.length);
callback(null, entries);
});
});
}
authSync(dn, password) {
log_info('Authenticating', dn);
try {
if (password === '') {
throw new Error('Password is not provided');
}
this.bindSync(dn, password);
log_info('Authenticated', dn);
return true;
} catch (error) {
log_info('Not authenticated', dn);
log_debug('error', error);
return false;
}
}
disconnect() {
this.connected = false;
this.domainBinded = false;
log_info('Disconecting');
this.client.unbind();
}
}
+149
View File
@@ -0,0 +1,149 @@
Oidc = {};
OAuth.registerService('oidc', 2, null, function (query) {
var debug = process.env.DEBUG || false;
var token = getToken(query);
if (debug) console.log('XXX: register token:', token);
var accessToken = token.access_token || token.id_token;
var expiresAt = (+new Date) + (1000 * parseInt(token.expires_in, 10));
var userinfo = getUserInfo(accessToken);
if (debug) console.log('XXX: userinfo:', userinfo);
var serviceData = {};
serviceData.id = userinfo[process.env.OAUTH2_ID_MAP]; // || userinfo["id"];
serviceData.username = userinfo[process.env.OAUTH2_USERNAME_MAP]; // || userinfo["uid"];
serviceData.fullname = userinfo[process.env.OAUTH2_FULLNAME_MAP]; // || userinfo["displayName"];
serviceData.accessToken = accessToken;
serviceData.expiresAt = expiresAt;
serviceData.email = userinfo[process.env.OAUTH2_EMAIL_MAP]; // || userinfo["email"];
if (accessToken) {
var tokenContent = getTokenContent(accessToken);
var fields = _.pick(tokenContent, getConfiguration().idTokenWhitelistFields);
_.extend(serviceData, fields);
}
if (token.refresh_token)
serviceData.refreshToken = token.refresh_token;
if (debug) console.log('XXX: serviceData:', serviceData);
var profile = {};
profile.name = userinfo[process.env.OAUTH2_FULLNAME_MAP]; // || userinfo["displayName"];
profile.email = userinfo[process.env.OAUTH2_EMAIL_MAP]; // || userinfo["email"];
if (debug) console.log('XXX: profile:', profile);
return {
serviceData: serviceData,
options: { profile: profile }
};
});
var userAgent = "Meteor";
if (Meteor.release) {
userAgent += "/" + Meteor.release;
}
var getToken = function (query) {
var debug = process.env.DEBUG || false;
var config = getConfiguration();
if(config.tokenEndpoint.includes('https://')){
var serverTokenEndpoint = config.tokenEndpoint;
}else{
var serverTokenEndpoint = config.serverUrl + config.tokenEndpoint;
}
var requestPermissions = config.requestPermissions;
var response;
try {
response = HTTP.post(
serverTokenEndpoint,
{
headers: {
Accept: 'application/json',
"User-Agent": userAgent
},
params: {
code: query.code,
client_id: config.clientId,
client_secret: OAuth.openSecret(config.secret),
redirect_uri: OAuth._redirectUri('oidc', config),
grant_type: 'authorization_code',
scope: requestPermissions,
state: query.state
}
}
);
} catch (err) {
throw _.extend(new Error("Failed to get token from OIDC " + serverTokenEndpoint + ": " + err.message),
{ response: err.response });
}
if (response.data.error) {
// if the http response was a json object with an error attribute
throw new Error("Failed to complete handshake with OIDC " + serverTokenEndpoint + ": " + response.data.error);
} else {
if (debug) console.log('XXX: getToken response: ', response.data);
return response.data;
}
};
var getUserInfo = function (accessToken) {
var debug = process.env.DEBUG || false;
var config = getConfiguration();
// Some userinfo endpoints use a different base URL than the authorization or token endpoints.
// This logic allows the end user to override the setting by providing the full URL to userinfo in their config.
if (config.userinfoEndpoint.includes("https://")) {
var serverUserinfoEndpoint = config.userinfoEndpoint;
} else {
var serverUserinfoEndpoint = config.serverUrl + config.userinfoEndpoint;
}
var response;
try {
response = HTTP.get(
serverUserinfoEndpoint,
{
headers: {
"User-Agent": userAgent,
"Authorization": "Bearer " + accessToken
}
}
);
} catch (err) {
throw _.extend(new Error("Failed to fetch userinfo from OIDC " + serverUserinfoEndpoint + ": " + err.message),
{response: err.response});
}
if (debug) console.log('XXX: getUserInfo response: ', response.data);
return response.data;
};
var getConfiguration = function () {
var config = ServiceConfiguration.configurations.findOne({ service: 'oidc' });
if (!config) {
throw new ServiceConfiguration.ConfigError('Service oidc not configured.');
}
return config;
};
var getTokenContent = function (token) {
var content = null;
if (token) {
try {
var parts = token.split('.');
var header = JSON.parse(new Buffer(parts[0], 'base64').toString());
content = JSON.parse(new Buffer(parts[1], 'base64').toString());
var signature = new Buffer(parts[2], 'base64');
var signed = parts[0] + '.' + parts[1];
} catch (err) {
this.content = {
exp: 0
};
}
}
return content;
}
Oidc.retrieveCredential = function (credentialToken, credentialSecret) {
return OAuth.retrieveCredential(credentialToken, credentialSecret);
};
+5184
View File
File diff suppressed because it is too large. Load diff
+73
View File
@@ -0,0 +1,73 @@
{
"name": "wekan",
"version": "v3.76.0",
"description": "Open-Source kanban",
"private": true,
"scripts": {
"lint": "eslint --cache --ext .js --ignore-path .eslintignore .",
"lint:eslint:fix": "eslint --ext .js --ignore-path .eslintignore --fix .",
"lint:staged": "lint-staged",
"prettify": "prettier --write '**/*.js' '**/*.jsx'",
"test": "npm run lint"
},
"lint-staged": {
"*.js": [
"meteor npm run prettify",
"meteor npm run lint:eslint:fix",
"git add --force"
],
"*.jsx": [
"meteor npm run prettify",
"meteor npm run lint:eslint:fix",
"git add --force"
],
"*.json": [
"prettier --write",
"git add --force"
]
},
"pre-commit": "lint:staged",
"eslintConfig": {
"extends": "@meteorjs/eslint-config-meteor"
},
"repository": {
"type": "git",
"url": "git+https://github.com/wekan/wekan.git"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/wekan/wekan/issues"
},
"homepage": "https://wekan.github.io",
"devDependencies": {
"eslint": "^5.16.0",
"eslint-config-meteor": "0.0.9",
"eslint-config-prettier": "^3.6.0",
"eslint-import-resolver-meteor": "^0.4.0",
"eslint-plugin-import": "^2.18.0",
"eslint-plugin-meteor": "^5.1.0",
"eslint-plugin-prettier": "^3.1.0",
"lint-staged": "^7.3.0",
"pre-commit": "^1.2.2",
"prettier": "^1.18.2",
"prettier-eslint": "^8.8.2"
},
"dependencies": {
"@babel/runtime": "^7.6.2",
"ajv": "^5.0.0",
"babel-runtime": "^6.26.0",
"bcrypt": "^3.0.2",
"bson": "^4.0.0",
"bunyan": "^1.8.12",
"es6-promise": "^4.2.4",
"gridfs-stream": "^0.5.3",
"ldapjs": "^1.0.2",
"meteor-node-stubs": "^0.4.1",
"mongodb": "^3.3.3",
"os": "^0.1.1",
"page": "^1.8.6",
"qs": "^6.8.0",
"source-map-support": "^0.5.12",
"xss": "^1.0.6"
}
}
@@ -1,6 +1,6 @@
name: wekan
version: '0'
version-script: git describe --dirty --tags | cut -c 2-
version: 0
version-script: git describe --tags | cut -c 2-
summary: The open-source kanban
description: |
Wekan is an open-source and collaborative kanban board application.
@@ -65,75 +65,59 @@ apps:
parts:
mongodb:
source: https://repo.mongodb.org/apt/ubuntu/dists/xenial/mongodb-org/4.0/multiverse/binary-amd64/mongodb-org-server_4.0.11_amd64.deb
source: https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1604-3.2.22.tgz
plugin: dump
stage-packages: [libssl-dev]
stage-packages: [libssl1.0.0]
filesets:
mongo:
- usr
- bin
- lib
# stage:
# - $mongo
# prime:
# - $mongo
stage:
- $mongo
prime:
- $mongo
wekan:
source: .
plugin: nodejs
node-engine: 8.16.0
# node-packages:
# - node-gyp
# - node-pre-gyp
# - fibers@4.0.1
node-engine: 8.17.0
node-packages:
- node-gyp
- node-pre-gyp
- fibers@2.0.0
build-packages:
- ca-certificates
- apt-utils
- bsdtar
- gnupg
- bzip2
## - python3
- python
# - python3
- g++
- build-essential
- git
# - capnproto
- capnproto
- curl
- libcurl3
- php-curl
# - execstack
# - nodejs
# - npm
## - python3-pip
## - python3-venv
- execstack
- nodejs
- npm
stage-packages:
- libfontconfig1
override-build: |
set -o xtrace
echo "Cleaning environment first"
rm -rf ~/.meteor ~/.npm /usr/local/lib/node_modules
# Meteor installer doesn't work with the default tar binary, so using bsdtar while installing.
# https://github.com/coreos/bugs/issues/1095#issuecomment-350574389
cp $(which tar) $(which tar)~
ln -sf $(which bsdtar) $(which tar)
# Create the OpenAPI specification
rm -rf .build
mkdir .build
##mkdir -p .build/python
##cd .build/python
##python3 -m venv env
##. ./env/bin/activate
##pip3 install -U setuptools wheel
##git clone --depth 1 -b master https://github.com/Kronuz/esprima-python
##cd esprima-python
##python3 setup.py install
##cd ../../..
##mkdir -p ./public/api
##python3 ./openapi/generate_openapi.py --release $(git describe --tags --abbrev=0) > ./public/api/wekan.yml
#mkdir -p .build/python
#cd .build/python
#git clone --depth 1 -b master https://github.com/Kronuz/esprima-python
#cd esprima-python
#python3 setup.py install
#cd ../../..
#mkdir -p ./public/api
#python3 ./openapi/generate_openapi.py --release $(git describe --tags --abbrev=0) > ./public/api/wekan.yml
# we temporary need api2html and mkdirp
##npm install -g --unsafe-perm api2html@0.3.0
##npm install -g --unsafe-perm mkdirp
##api2html -c ./public/logo-header.png -o ./public/api/wekan.html ./public/api/wekan.yml
##npm uninstall -g --unsafe-perm mkdirp
##npm uninstall -g --unsafe-perm api2html
#npm install -g api2html@0.3.0
#npm install -g mkdirp
#api2html -c ./public/logo-header.png -o ./public/api/wekan.html ./public/api/wekan.yml
#npm uninstall -g mkdirp
#npm uninstall -g api2html
# Node Fibers 100% CPU usage issue:
# https://github.com/wekan/wekan-mongodb/issues/2#issuecomment-381453161
# https://github.com/meteor/meteor/issues/9796#issuecomment-381676326
@@ -206,17 +190,12 @@ parts:
# git clone --depth 1 -b master --recurse-submodules https://github.com/wekan/markdown.git
# cd ..
#fi
rm -rf package-lock.json .build
#meteor add standard-minifier-js --allow-superuser
meteor npm install --allow-superuser --unsafe-perm
rm -rf .build
meteor add standard-minifier-js --allow-superuser
meteor npm install --allow-superuser
meteor npm install --allow-superuser --save babel-runtime
meteor build .build --directory --allow-superuser
cp -f fix-download-unicode/cfs_access-point.txt .build/bundle/programs/server/packages/cfs_access-point.js
# Disable code coverage on Snap, because it crashes Wekan starting.
# https://github.com/wekan/wekan/issues/2533#issuecomment-513506939
#sed -i 's|Profile(fileInfo.path, func).apply(global, args);|//Profile(fileInfo.path, func).apply(global, args);|g' .build/bundle/programs/server/boot.js
# although it did not help, it said unable to find main function.
# Other option to fix would be to drop indexes at start of snap:
# https://stackoverflow.com/questions/24241742/drop-all-indexes-from-all-collections-in-a-mongodb-database-using-the-command-li
#Removed binary version of bcrypt because of security vulnerability that is not fixed yet.
#https://github.com/wekan/wekan/commit/4b2010213907c61b0e0482ab55abb06f6a668eac
#https://github.com/wekan/wekan/commit/7eeabf14be3c63fae2226e561ef8a0c1390c8d3c
@@ -227,18 +206,21 @@ parts:
#cd ../../../../
# Change to directory .build/bundle/programs/server
cd .build/bundle/programs/server
npm install --unsafe-perm
npm install
npm install --allow-superuser --save babel-runtime
#meteor npm install --save bcrypt
# Change back to Wekan source directory
cd ../../../..
cp -r .build/bundle/* $SNAPCRAFT_PART_INSTALL/
cp .build/bundle/.node_version.txt $SNAPCRAFT_PART_INSTALL/
# rm $SNAPCRAFT_PART_INSTALL/lib/node_modules/wekan
#rm $SNAPCRAFT_PART_INSTALL/programs/server/npm/node_modules/meteor/rajit_bootstrap3-datepicker/lib/bootstrap-datepicker/node_modules/phantomjs-prebuilt/lib/phantom/bin/phantomjs
# rm $SNAPCRAFT_PART_INSTALL/lib/node_modules/node-pre-gyp/node_modules/tar/lib/.mkdir.js.swp
# rm $SNAPCRAFT_PART_INSTALL/lib/node_modules/node-gyp/node_modules/tar/lib/.mkdir.js.swp
# Put back the original tar
mv $(which tar)~ $(which tar)
rm -f $SNAPCRAFT_PART_INSTALL/lib/node_modules/wekan
rm -f $SNAPCRAFT_PART_INSTALL/programs/server/npm/node_modules/meteor/rajit_bootstrap3-datepicker/lib/bootstrap-datepicker/node_modules/phantomjs-prebuilt/lib/phantom/bin/phantomjs
rm -f $SNAPCRAFT_PART_INSTALL/programs/server/npm/node_modules/tar/lib/.mkdir.js.swp
rm -f $SNAPCRAFT_PART_INSTALL/lib/node_modules/node-pre-gyp/node_modules/tar/lib/.mkdir.js.swp
rm -f $SNAPCRAFT_PART_INSTALL/lib/node_modules/node-gyp/node_modules/tar/lib/.mkdir.js.swp
# Meteor 1.8.x additional .swp remove
rm -f $SNAPCRAFT_PART_INSTALL/programs/server/node_modules/node-pre-gyp/node_modules/tar/lib/.mkdir.js.swp
organize:
README: README.wekan
prime:
+853
View File
@@ -0,0 +1,853 @@
const DateString = Match.Where(function(dateAsString) {
check(dateAsString, String);
return moment(dateAsString, moment.ISO_8601).isValid();
});
export class WekanCreator {
constructor(data) {
// we log current date, to use the same timestamp for all our actions.
// this helps to retrieve all elements performed by the same import.
this._nowDate = new Date();
// The object creation dates, indexed by Wekan id
// (so we only parse actions once!)
this.createdAt = {
board: null,
cards: {},
lists: {},
swimlanes: {},
};
// The object creator Wekan Id, indexed by the object Wekan id
// (so we only parse actions once!)
this.createdBy = {
cards: {}, // only cards have a field for that
};
// Map of labels Wekan ID => Wekan ID
this.labels = {};
// Map of swimlanes Wekan ID => Wekan ID
this.swimlanes = {};
// Map of lists Wekan ID => Wekan ID
this.lists = {};
// Map of cards Wekan ID => Wekan ID
this.cards = {};
// Map of comments Wekan ID => Wekan ID
this.commentIds = {};
// Map of attachments Wekan ID => Wekan ID
this.attachmentIds = {};
// Map of checklists Wekan ID => Wekan ID
this.checklists = {};
// Map of checklistItems Wekan ID => Wekan ID
this.checklistItems = {};
// The comments, indexed by Wekan card id (to map when importing cards)
this.comments = {};
// Map of rules Wekan ID => Wekan ID
this.rules = {};
// the members, indexed by Wekan member id => Wekan user ID
this.members = data.membersMapping ? data.membersMapping : {};
// Map of triggers Wekan ID => Wekan ID
this.triggers = {};
// Map of actions Wekan ID => Wekan ID
this.actions = {};
// maps a wekanCardId to an array of wekanAttachments
this.attachments = {};
}
/**
* If dateString is provided,
* return the Date it represents.
* If not, will return the date when it was first called.
* This is useful for us, as we want all import operations to
* have the exact same date for easier later retrieval.
*
* @param {String} dateString a properly formatted Date
*/
_now(dateString) {
if (dateString) {
return new Date(dateString);
}
if (!this._nowDate) {
this._nowDate = new Date();
}
return this._nowDate;
}
/**
* if wekanUserId is provided and we have a mapping,
* return it.
* Otherwise return current logged user.
* @param wekanUserId
* @private
*/
_user(wekanUserId) {
if (wekanUserId && this.members[wekanUserId]) {
return this.members[wekanUserId];
}
return Meteor.userId();
}
checkActivities(wekanActivities) {
check(wekanActivities, [
Match.ObjectIncluding({
activityType: String,
createdAt: DateString,
}),
]);
// XXX we could perform more thorough checks based on action type
}
checkBoard(wekanBoard) {
check(
wekanBoard,
Match.ObjectIncluding({
archived: Boolean,
title: String,
// XXX refine control by validating 'color' against a list of
// allowed values (is it worth the maintenance?)
color: String,
permission: Match.Where(value => {
return ['private', 'public'].indexOf(value) >= 0;
}),
}),
);
}
checkCards(wekanCards) {
check(wekanCards, [
Match.ObjectIncluding({
archived: Boolean,
dateLastActivity: DateString,
labelIds: [String],
title: String,
sort: Number,
}),
]);
}
checkLabels(wekanLabels) {
check(wekanLabels, [
Match.ObjectIncluding({
// XXX refine control by validating 'color' against a list of allowed
// values (is it worth the maintenance?)
color: String,
}),
]);
}
checkLists(wekanLists) {
check(wekanLists, [
Match.ObjectIncluding({
archived: Boolean,
title: String,
}),
]);
}
checkSwimlanes(wekanSwimlanes) {
check(wekanSwimlanes, [
Match.ObjectIncluding({
archived: Boolean,
title: String,
}),
]);
}
checkChecklists(wekanChecklists) {
check(wekanChecklists, [
Match.ObjectIncluding({
cardId: String,
title: String,
}),
]);
}
checkChecklistItems(wekanChecklistItems) {
check(wekanChecklistItems, [
Match.ObjectIncluding({
cardId: String,
title: String,
}),
]);
}
checkRules(wekanRules) {
check(wekanRules, [
Match.ObjectIncluding({
triggerId: String,
actionId: String,
title: String,
}),
]);
}
checkTriggers(wekanTriggers) {
// XXX More check based on trigger type
check(wekanTriggers, [
Match.ObjectIncluding({
activityType: String,
desc: String,
}),
]);
}
getMembersToMap(data) {
// we will work on the list itself (an ordered array of objects) when a
// mapping is done, we add a 'wekan' field to the object representing the
// imported member
const membersToMap = data.members;
const users = data.users;
// auto-map based on username
membersToMap.forEach(importedMember => {
importedMember.id = importedMember.userId;
delete importedMember.userId;
const user = users.filter(user => {
return user._id === importedMember.id;
})[0];
if (user.profile && user.profile.fullname) {
importedMember.fullName = user.profile.fullname;
}
importedMember.username = user.username;
const wekanUser = Users.findOne({ username: importedMember.username });
if (wekanUser) {
importedMember.wekanId = wekanUser._id;
}
});
return membersToMap;
}
checkActions(wekanActions) {
// XXX More check based on action type
check(wekanActions, [
Match.ObjectIncluding({
actionType: String,
desc: String,
}),
]);
}
// You must call parseActions before calling this one.
createBoardAndLabels(boardToImport) {
const boardToCreate = {
archived: boardToImport.archived,
color: boardToImport.color,
// very old boards won't have a creation activity so no creation date
createdAt: this._now(boardToImport.createdAt),
labels: [],
members: [
{
userId: Meteor.userId(),
wekanId: Meteor.userId(),
isActive: true,
isAdmin: true,
isNoComments: false,
isCommentOnly: false,
swimlaneId: false,
},
],
// Standalone Export has modifiedAt missing, adding modifiedAt to fix it
modifiedAt: this._now(boardToImport.modifiedAt),
permission: boardToImport.permission,
slug: getSlug(boardToImport.title) || 'board',
stars: 0,
title: boardToImport.title,
};
// now add other members
if (boardToImport.members) {
boardToImport.members.forEach(wekanMember => {
// do we already have it in our list?
if (
!boardToCreate.members.some(
member => member.wekanId === wekanMember.wekanId,
)
)
boardToCreate.members.push({
...wekanMember,
userId: wekanMember.wekanId,
});
});
}
boardToImport.labels.forEach(label => {
const labelToCreate = {
_id: Random.id(6),
color: label.color,
name: label.name,
};
// We need to remember them by Wekan ID, as this is the only ref we have
// when importing cards.
this.labels[label._id] = labelToCreate._id;
boardToCreate.labels.push(labelToCreate);
});
const boardId = Boards.direct.insert(boardToCreate);
Boards.direct.update(boardId, {
$set: {
modifiedAt: this._now(),
},
});
// log activity
Activities.direct.insert({
activityType: 'importBoard',
boardId,
createdAt: this._now(),
source: {
id: boardToImport.id,
system: 'Wekan',
},
// We attribute the import to current user,
// not the author from the original object.
userId: this._user(),
});
return boardId;
}
/**
* Create the Wekan cards corresponding to the supplied Wekan cards,
* as well as all linked data: activities, comments, and attachments
* @param wekanCards
* @param boardId
* @returns {Array}
*/
createCards(wekanCards, boardId) {
const result = [];
wekanCards.forEach(card => {
const cardToCreate = {
archived: card.archived,
boardId,
// very old boards won't have a creation activity so no creation date
createdAt: this._now(this.createdAt.cards[card._id]),
dateLastActivity: this._now(),
description: card.description,
listId: this.lists[card.listId],
swimlaneId: this.swimlanes[card.swimlaneId],
sort: card.sort,
title: card.title,
// we attribute the card to its creator if available
userId: this._user(this.createdBy.cards[card._id]),
isOvertime: card.isOvertime || false,
startAt: card.startAt ? this._now(card.startAt) : null,
dueAt: card.dueAt ? this._now(card.dueAt) : null,
spentTime: card.spentTime || null,
};
// add labels
if (card.labelIds) {
cardToCreate.labelIds = card.labelIds.map(wekanId => {
return this.labels[wekanId];
});
}
// add members {
if (card.members) {
const wekanMembers = [];
// we can't just map, as some members may not have been mapped
card.members.forEach(sourceMemberId => {
if (this.members[sourceMemberId]) {
const wekanId = this.members[sourceMemberId];
// we may map multiple Wekan members to the same wekan user
// in which case we risk adding the same user multiple times
if (!wekanMembers.find(wId => wId === wekanId)) {
wekanMembers.push(wekanId);
}
}
return true;
});
if (wekanMembers.length > 0) {
cardToCreate.members = wekanMembers;
}
}
// set color
if (card.color) {
cardToCreate.color = card.color;
}
// insert card
const cardId = Cards.direct.insert(cardToCreate);
// keep track of Wekan id => Wekan id
this.cards[card._id] = cardId;
// // log activity
// Activities.direct.insert({
// activityType: 'importCard',
// boardId,
// cardId,
// createdAt: this._now(),
// listId: cardToCreate.listId,
// source: {
// id: card._id,
// system: 'Wekan',
// },
// // we attribute the import to current user,
// // not the author of the original card
// userId: this._user(),
// });
// add comments
const comments = this.comments[card._id];
if (comments) {
comments.forEach(comment => {
const commentToCreate = {
boardId,
cardId,
createdAt: this._now(comment.createdAt),
text: comment.text,
// we attribute the comment to the original author, default to current user
userId: this._user(comment.userId),
};
// dateLastActivity will be set from activity insert, no need to
// update it ourselves
const commentId = CardComments.direct.insert(commentToCreate);
this.commentIds[comment._id] = commentId;
// Activities.direct.insert({
// activityType: 'addComment',
// boardId: commentToCreate.boardId,
// cardId: commentToCreate.cardId,
// commentId,
// createdAt: this._now(commentToCreate.createdAt),
// // we attribute the addComment (not the import)
// // to the original author - it is needed by some UI elements.
// userId: commentToCreate.userId,
// });
});
}
const attachments = this.attachments[card._id];
const wekanCoverId = card.coverId;
if (attachments) {
attachments.forEach(att => {
const file = new FS.File();
// Simulating file.attachData on the client generates multiple errors
// - HEAD returns null, which causes exception down the line
// - the template then tries to display the url to the attachment which causes other errors
// so we make it server only, and let UI catch up once it is done, forget about latency comp.
const self = this;
if (Meteor.isServer) {
if (att.url) {
file.attachData(att.url, function(error) {
file.boardId = boardId;
file.cardId = cardId;
file.userId = self._user(att.userId);
// The field source will only be used to prevent adding
// attachments' related activities automatically
file.source = 'import';
if (error) {
throw error;
} else {
const wekanAtt = Attachments.insert(file, () => {
// we do nothing
});
self.attachmentIds[att._id] = wekanAtt._id;
//
if (wekanCoverId === att._id) {
Cards.direct.update(cardId, {
$set: {
coverId: wekanAtt._id,
},
});
}
}
});
} else if (att.file) {
file.attachData(
new Buffer(att.file, 'base64'),
{
type: att.type,
},
error => {
file.name(att.name);
file.boardId = boardId;
file.cardId = cardId;
file.userId = self._user(att.userId);
// The field source will only be used to prevent adding
// attachments' related activities automatically
file.source = 'import';
if (error) {
throw error;
} else {
const wekanAtt = Attachments.insert(file, () => {
// we do nothing
});
this.attachmentIds[att._id] = wekanAtt._id;
//
if (wekanCoverId === att._id) {
Cards.direct.update(cardId, {
$set: {
coverId: wekanAtt._id,
},
});
}
}
},
);
}
}
// todo XXX set cover - if need be
});
}
result.push(cardId);
});
return result;
}
// Create labels if they do not exist and load this.labels.
createLabels(wekanLabels, board) {
wekanLabels.forEach(label => {
const color = label.color;
const name = label.name;
const existingLabel = board.getLabel(name, color);
if (existingLabel) {
this.labels[label.id] = existingLabel._id;
} else {
const idLabelCreated = board.pushLabel(name, color);
this.labels[label.id] = idLabelCreated;
}
});
}
createLists(wekanLists, boardId) {
wekanLists.forEach((list, listIndex) => {
const listToCreate = {
archived: list.archived,
boardId,
// We are being defensing here by providing a default date (now) if the
// creation date wasn't found on the action log. This happen on old
// Wekan boards (eg from 2013) that didn't log the 'createList' action
// we require.
createdAt: this._now(this.createdAt.lists[list.id]),
title: list.title,
sort: list.sort ? list.sort : listIndex,
};
const listId = Lists.direct.insert(listToCreate);
Lists.direct.update(listId, {
$set: {
updatedAt: this._now(),
},
});
this.lists[list._id] = listId;
// // log activity
// Activities.direct.insert({
// activityType: 'importList',
// boardId,
// createdAt: this._now(),
// listId,
// source: {
// id: list._id,
// system: 'Wekan',
// },
// // We attribute the import to current user,
// // not the creator of the original object
// userId: this._user(),
// });
});
}
createSwimlanes(wekanSwimlanes, boardId) {
wekanSwimlanes.forEach((swimlane, swimlaneIndex) => {
const swimlaneToCreate = {
archived: swimlane.archived,
boardId,
// We are being defensing here by providing a default date (now) if the
// creation date wasn't found on the action log. This happen on old
// Wekan boards (eg from 2013) that didn't log the 'createList' action
// we require.
createdAt: this._now(this.createdAt.swimlanes[swimlane._id]),
title: swimlane.title,
sort: swimlane.sort ? swimlane.sort : swimlaneIndex,
};
// set color
if (swimlane.color) {
swimlaneToCreate.color = swimlane.color;
}
const swimlaneId = Swimlanes.direct.insert(swimlaneToCreate);
Swimlanes.direct.update(swimlaneId, {
$set: {
updatedAt: this._now(),
},
});
this.swimlanes[swimlane._id] = swimlaneId;
});
}
createChecklists(wekanChecklists) {
const result = [];
wekanChecklists.forEach((checklist, checklistIndex) => {
// Create the checklist
const checklistToCreate = {
cardId: this.cards[checklist.cardId],
title: checklist.title,
createdAt: checklist.createdAt,
sort: checklist.sort ? checklist.sort : checklistIndex,
};
const checklistId = Checklists.direct.insert(checklistToCreate);
this.checklists[checklist._id] = checklistId;
result.push(checklistId);
});
return result;
}
createTriggers(wekanTriggers, boardId) {
wekanTriggers.forEach(trigger => {
if (trigger.hasOwnProperty('labelId')) {
trigger.labelId = this.labels[trigger.labelId];
}
if (trigger.hasOwnProperty('memberId')) {
trigger.memberId = this.members[trigger.memberId];
}
trigger.boardId = boardId;
const oldId = trigger._id;
delete trigger._id;
this.triggers[oldId] = Triggers.direct.insert(trigger);
});
}
createActions(wekanActions, boardId) {
wekanActions.forEach(action => {
if (action.hasOwnProperty('labelId')) {
action.labelId = this.labels[action.labelId];
}
if (action.hasOwnProperty('memberId')) {
action.memberId = this.members[action.memberId];
}
action.boardId = boardId;
const oldId = action._id;
delete action._id;
this.actions[oldId] = Actions.direct.insert(action);
});
}
createRules(wekanRules, boardId) {
wekanRules.forEach(rule => {
// Create the rule
rule.boardId = boardId;
rule.triggerId = this.triggers[rule.triggerId];
rule.actionId = this.actions[rule.actionId];
delete rule._id;
Rules.direct.insert(rule);
});
}
createChecklistItems(wekanChecklistItems) {
wekanChecklistItems.forEach((checklistitem, checklistitemIndex) => {
// Create the checklistItem
const checklistItemTocreate = {
title: checklistitem.title,
checklistId: this.checklists[checklistitem.checklistId],
cardId: this.cards[checklistitem.cardId],
sort: checklistitem.sort ? checklistitem.sort : checklistitemIndex,
isFinished: checklistitem.isFinished,
};
const checklistItemId = ChecklistItems.direct.insert(
checklistItemTocreate,
);
this.checklistItems[checklistitem._id] = checklistItemId;
});
}
parseActivities(wekanBoard) {
wekanBoard.activities.forEach(activity => {
switch (activity.activityType) {
case 'addAttachment': {
// We have to be cautious, because the attachment could have been removed later.
// In that case Wekan still reports its addition, but removes its 'url' field.
// So we test for that
const wekanAttachment = wekanBoard.attachments.filter(attachment => {
return attachment._id === activity.attachmentId;
})[0];
if (typeof wekanAttachment !== 'undefined' && wekanAttachment) {
if (wekanAttachment.url || wekanAttachment.file) {
// we cannot actually create the Wekan attachment, because we don't yet
// have the cards to attach it to, so we store it in the instance variable.
const wekanCardId = activity.cardId;
if (!this.attachments[wekanCardId]) {
this.attachments[wekanCardId] = [];
}
this.attachments[wekanCardId].push(wekanAttachment);
}
}
break;
}
case 'addComment': {
const wekanComment = wekanBoard.comments.filter(comment => {
return comment._id === activity.commentId;
})[0];
const id = activity.cardId;
if (!this.comments[id]) {
this.comments[id] = [];
}
this.comments[id].push(wekanComment);
break;
}
case 'createBoard': {
this.createdAt.board = activity.createdAt;
break;
}
case 'createCard': {
const cardId = activity.cardId;
this.createdAt.cards[cardId] = activity.createdAt;
this.createdBy.cards[cardId] = activity.userId;
break;
}
case 'createList': {
const listId = activity.listId;
this.createdAt.lists[listId] = activity.createdAt;
break;
}
case 'createSwimlane': {
const swimlaneId = activity.swimlaneId;
this.createdAt.swimlanes[swimlaneId] = activity.createdAt;
break;
}
}
});
}
importActivities(activities, boardId) {
activities.forEach(activity => {
switch (activity.activityType) {
// Board related activities
// TODO: addBoardMember, removeBoardMember
case 'createBoard': {
Activities.direct.insert({
userId: this._user(activity.userId),
type: 'board',
activityTypeId: boardId,
activityType: activity.activityType,
boardId,
createdAt: this._now(activity.createdAt),
});
break;
}
// List related activities
// TODO: removeList, archivedList
case 'createList': {
Activities.direct.insert({
userId: this._user(activity.userId),
type: 'list',
activityType: activity.activityType,
listId: this.lists[activity.listId],
boardId,
createdAt: this._now(activity.createdAt),
});
break;
}
// Card related activities
// TODO: archivedCard, restoredCard, joinMember, unjoinMember
case 'createCard': {
Activities.direct.insert({
userId: this._user(activity.userId),
activityType: activity.activityType,
listId: this.lists[activity.listId],
cardId: this.cards[activity.cardId],
boardId,
createdAt: this._now(activity.createdAt),
});
break;
}
case 'moveCard': {
Activities.direct.insert({
userId: this._user(activity.userId),
oldListId: this.lists[activity.oldListId],
activityType: activity.activityType,
listId: this.lists[activity.listId],
cardId: this.cards[activity.cardId],
boardId,
createdAt: this._now(activity.createdAt),
});
break;
}
// Comment related activities
case 'addComment': {
Activities.direct.insert({
userId: this._user(activity.userId),
activityType: activity.activityType,
cardId: this.cards[activity.cardId],
commentId: this.commentIds[activity.commentId],
boardId,
createdAt: this._now(activity.createdAt),
});
break;
}
// Attachment related activities
case 'addAttachment': {
Activities.direct.insert({
userId: this._user(activity.userId),
type: 'card',
activityType: activity.activityType,
attachmentId: this.attachmentIds[activity.attachmentId],
cardId: this.cards[activity.cardId],
boardId,
createdAt: this._now(activity.createdAt),
});
break;
}
// Checklist related activities
case 'addChecklist': {
Activities.direct.insert({
userId: this._user(activity.userId),
activityType: activity.activityType,
cardId: this.cards[activity.cardId],
checklistId: this.checklists[activity.checklistId],
boardId,
createdAt: this._now(activity.createdAt),
});
break;
}
case 'addChecklistItem': {
Activities.direct.insert({
userId: this._user(activity.userId),
activityType: activity.activityType,
cardId: this.cards[activity.cardId],
checklistId: this.checklists[activity.checklistId],
checklistItemId: activity.checklistItemId.replace(
activity.checklistId,
this.checklists[activity.checklistId],
),
boardId,
createdAt: this._now(activity.createdAt),
});
break;
}
}
});
}
//check(board) {
check() {
//try {
// check(data, {
// membersMapping: Match.Optional(Object),
// });
// this.checkActivities(board.activities);
// this.checkBoard(board);
// this.checkLabels(board.labels);
// this.checkLists(board.lists);
// this.checkSwimlanes(board.swimlanes);
// this.checkCards(board.cards);
//this.checkChecklists(board.checklists);
// this.checkRules(board.rules);
// this.checkActions(board.actions);
//this.checkTriggers(board.triggers);
//this.checkChecklistItems(board.checklistItems);
//} catch (e) {
// throw new Meteor.Error('error-json-schema');
// }
}
create(board, currentBoardId) {
// TODO : Make isSandstorm variable global
const isSandstorm =
Meteor.settings &&
Meteor.settings.public &&
Meteor.settings.public.sandstorm;
if (isSandstorm && currentBoardId) {
const currentBoard = Boards.findOne(currentBoardId);
currentBoard.archive();
}
this.parseActivities(board);
const boardId = this.createBoardAndLabels(board);
this.createLists(board.lists, boardId);
this.createSwimlanes(board.swimlanes, boardId);
this.createCards(board.cards, boardId);
this.createChecklists(board.checklists);
this.createChecklistItems(board.checklistItems);
this.importActivities(board.activities, boardId);
this.createTriggers(board.triggers, boardId);
this.createActions(board.actions, boardId);
this.createRules(board.rules, boardId);
// XXX add members
return boardId;
}
}
+4 -4
View File
@@ -1,10 +1,10 @@
dist: trusty
dist: eoan
sudo: required
env:
TRAVIS_DOCKER_COMPOSE_VERSION: 1.17.0
TRAVIS_NODE_VERSION: 8.9.3
TRAVIS_NPM_VERSION: 5.5.1
TRAVIS_DOCKER_COMPOSE_VERSION: 1.24.0
TRAVIS_NODE_VERSION: 12.15.0
TRAVIS_NPM_VERSION: latest
before_install:
- sudo apt-get update -y
+1 -1
View File
@@ -39,7 +39,7 @@ host = https://www.transifex.com
# tap:i18n requires us to use `-` separator in the language identifiers whereas
# Transifex uses a `_` separator, without an option to customize it on one side
# or the other, so we need to do a Manual mapping.
lang_map = bg_BG:bg, en_GB:en-GB, es_AR:es-AR, el_GR:el, fi_FI:fi, hu_HU:hu, id_ID:id, mn_MN:mn, no:nb, lv_LV:lv, pt_BR:pt-BR, ro_RO:ro, zh_CN:zh-CN, zh_TW:zh-TW, zh_HK:zh-HK
lang_map = bg_BG:bg, en_GB:en-GB, es_AR:es-AR, el_GR:el, fi_FI:fi, hu_HU:hu, id_ID:id, mn_MN:mn, no:nb, lv_LV:lv, pt_BR:pt-BR, ro_RO:ro, sl_SI:sl, zh_CN:zh-CN, zh_TW:zh-TW, zh_HK:zh-HK
[wekan.application]
file_filter = i18n/<lang>.i18n.json
+28
View File
@@ -0,0 +1,28 @@
{
"version": "0.2.0",
"configurations": [
{
"type": "chrome",
"request": "launch",
"name": "Meteor: Chrome",
"url": "http://localhost:3000",
"webRoot": "${workspaceFolder}"
},
{
"type": "node",
"request": "launch",
"name": "Meteor: Node",
"runtimeExecutable": "/home/wekan/.meteor/meteor",
"runtimeArgs": ["run", "--inspect-brk=9229"],
"outputCapture": "std",
"port": 9229,
"timeout": 60000
}
],
"compounds": [
{
"name": "Meteor: All",
"configurations": ["Meteor: Node", "Meteor: Chrome"]
}
]
}
+796 -2
View File
@@ -1,4 +1,798 @@
# v3.19 2019-08-15 Upcoming Wekan release
# v3.76 2020-02-07 Wekan release
This release adds the following updates:
- [Use Meteor 1.9 and Node.js 12.15.0 on Snap and Docker](https://github.com/wekan/wekan/commit/8384d68a060ef8f2c202ce2fa6064c5c823d28dc).
Thanks to xet7.
and fixes the following bugs:
- [Fix Bug enable/disable Comments in Card Settings](https://github.com/wekan/wekan/issues/2923).
Thanks to warnt, mdurokov and xet7.
- [Try to disable dragging Swimlanes/Lists/Cards/Checklists/Subtasks on small mobile smartphones webbrowsers,
and hide drag handles on mobile web](https://github.com/wekan/wekan/commit/bf78b093bad7d463ee325ad96e8b485264d4a3be).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.75 2020-02-05 Wekan release
This release adds the following new features:
- [Fix](https://github.com/wekan/wekan/commit/f22785dbcde42e425c9ead209ec224aef6e11c16)
[adding comments](https://github.com/wekan/wekan/issues/2918).
Thanks to xet7.
and fixes the following bugs:
- [Added some working layout changes like activities using less space from https://github.com/wekan/wekan/pull/2920](https://github.com/wekan/wekan/commit/f22785dbcde42e425c9ead209ec224aef6e11c16).
Thanks to 2020product.
- [Fixed Card Settings not working at Sandstorm](https://github.com/wekan/wekan/commit/f22785dbcde42e425c9ead209ec224aef6e11c16).
Thanks to xet7.
- Add [Card Description title](https://github.com/wekan/wekan/issues/2918#issuecomment-582346577)
[back](https://github.com/wekan/wekan/commit/f22785dbcde42e425c9ead209ec224aef6e11c16).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.74 2020-02-05 Wekan release
This release adds the following new features:
- [For BoardAdmin, add way to hide parts of a card, at Board Settings/Card Settings/Show on Card: Received, Start, ... etc.
Add to card title bar Copy card to Clipboard button](https://github.com/wekan/wekan/pull/2915).
Thanks to 2020product and xet7.
- [Set default to RICHER_CARD_COMMENT_EDITOR=false](https://github.com/wekan/wekan/commit/65fa2f626f503b8089e0d982901cffb3990426cb).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.73 2020-01-29 Wekan release
This release adds the following new features:
- [Login to Wekan with Nextcloud](https://github.com/wekan/wekan/pull/2897).
Thanks to bogie.
- [Add rule action to move cards to other boards](https://github.com/wekan/wekan/pull/2899).
Thanks to peterverraedt.
and fixes the following bugs:
- [Show System Wide Announcement in one line](https://github.com/wekan/wekan/pull/2891).
Thanks to tsia.
- [Fixed board export with attachment in Wekan Meteor 1.9.x version](https://github.com/wekan/wekan/pull/2898).
Thanks to izadpoor.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.72 2020-01-19 Sandstorm-only Wekan release
This release fixes the following bugs:
- Try to fix Wekan at Sandstorm.
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.71 2020-01-18 Sandstorm-only Wekan release
This release fixes the following bugs:
- [Try to fix Wekan at Sandstorm by using Meteor 1.8.x and Node 8.17.0 at Sandstorm](https://github.com/wekan/wekan/commit/5e5ab95410c715a4379631456fc5547c497898b0).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.70 2020-01-18 Wekan release
This release fixes the following bugs:
- [Add missing LD_LIBRARY_PATH to use libssl and libcurl](https://github.com/wekan/wekan/10f142a1a05acb98a175ccb0326fb0c1d3e3713f).
Thanks to xet7.
- [Use Meteor 1.8.x](https://github.com/wekan/wekan/commit/55a2aa90cbbf44200e9b0b9f4bd08b6177f1bb95)
[on Snap](https://github.com/wekan/wekan/commit/6a01170d8696322462c4065ce0cf4a637a058975), because
Snap builds do not work yet for Meteor 1.9, Node 12.14.1 and MongoDB 4.2.2.
Docker version works with Meteor 1.9.
Thanks to xet7.
- [Try to fix Node 12 Buffer() deprecation errors](https://github.com/wekan/wekan/commit/9b905c2833d54cf34d1875148075b2bf756d943a).
Thanks to xet7.
- [Add Snap Meteor 1.8.x files to lint ignore files](https://github.com/wekan/wekan/commit/48f8050c25e40f737dfdd3a98923cb87cd4e77e2).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.69 2020-01-10 Wekan release
This release fixes the following bugs:
- [Fix docker-compose.yml to not use --smallfiles that is not supported in
MongoDB 4.x](https://github.com/wekan/wekan/commit/ecb76842fcbd81701afcab8db0ed106e6be0fdec).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.68 2020-01-10 Wekan release
This release tries to fix the following bugs:
- [Try to fix Snap by removing MongoDB option --smallfiles that is not supported
in MongoDB 4.x](https://github.com/wekan/wekan/commit/031df54a2e0a03dcb7a2586667e60e5bd4eef706)
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.67 2020-01-10 Wekan release
This release tries to fix the following bugs:
- [Try to fix Snap](https://github.com/wekan/wekan/commit/2b382b940be9af575fab4c2e955702d8cde55ae9).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.66 2020-01-10 Wekan release
This release tries to fix the following bugs:
- [Try to fix Snap](https://github.com/wekan/wekan/commit/39bf1e375e2962f824e6f8cfa425ea51aa4efa24).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.65 2020-01-10 Wekan release
This release adds the following features:
- [More keyboard shortcuts: c for archive card](https://github.com/wekan/wekan/commit/d16a601c04aeb1d3550c5c541be02a67276a34cf).
Thanks to xet7.
and adds the following updates:
- [Upgrade to Meteor 1.9, Node 12.14.1 and MongoDB 4.2.2](https://github.com/wekan/wekan/commit/785f3cf88b61f687ef5ad4a529768221d1a54c86).
Thanks to xet7.
- [Add more issue repo links to GitHub issue template](https://github.com/wekan/wekan/commit/5724674e73246f4e52843a6d6906c0ecdd85cccc).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.64 2020-01-06 Wekan release
This release adds the following warning for CentOS 7 users:
- [WARNING: DO NOT USE SNAP ON CENTOS 7, THERE IS UPDATE BUG](https://github.com/wekan/wekan-snap/wiki/CentOS-7).
Thanks to andy-twosticks and xet7.
and adds the following features:
- [Wider sidebar](https://github.com/wekan/wekan/commit/5058233509e44916296e38fb8a6c5dd591c46d8b).
Thanks to vjrj.
and removes the following features:
- [Removed Custom HTML feature that does not work](https://github.com/wekan/wekan/commit/ddce0ada094e6450be260b4cda21fdfa09ae0133).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.63 2020-01-06 Wekan release
This release fixes the following bugs:
- [Fix: Unable to find Archive Card/List/Swimlane in board
settings](https://github.com/wekan/wekan/commit/8ce993921718f3e10c2daa5fabb145b939d789dd).
Thanks to neobradley and xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.62 2020-01-05 Wekan release
This release adds the following features:
- [Add Worker role](https://github.com/wekan/wekan/issues/2788).
This was originally added at Wekan v3.58, reverted at Wekan v3.60 because of bugs,
and now after fixes added back.
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.61 2020-01-03 Wekan release
This release adds the following features:
- [Add more Font Awesome icons. This was originally added
at Wekan v3.58, removed at Wekan v3.60, and now
added back at Wekan v3.61](https://github.com/wekan/wekan/commit/cd253522a305523e3e36bb73313e8c4db500a717).
Thanks to xet7.
and fixes the following bugs:
- [Fix browser javascript console errors when editing profile. This was originally added
at Wekan v3.58, removed at Wekan v3.60, and now added back at
Wekan v3.61](https://github.com/wekan/wekan/commit/cd253522a305523e3e36bb73313e8c4db500a717).
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.60 2020-01-03 Wekan release
This release fixes the following bugs:
- [Revert to Wekan v3.57 version of client and models directories,
removing Worker role temporarily, because Worker role changes
broke saving card](https://github.com/wekan/wekan/commit/27943796ade78ca3c503637a1340918bf06a1267).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.59 2020-01-03 Wekan release
This release fixes the following bugs:
- [Fix not being able to edit received date](https://github.com/wekan/wekan/commit/5376bc7b7905c0dd99fae1aeae3f63b4583a3e3f).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.58 2020-01-03 Wekan release
This release adds the following features:
- [Add Worker role](https://github.com/wekan/wekan/issues/2788). Thanks to xet7.
- [Add more Font Awesome icons](https://github.com/wekan/wekan/commit/2bf004120d5a43cd3c3c060fc7c0c30d1b01f220).
Thanks to xet7.
and fixes the following bugs:
- [Fix: k8s templates update for helm](https://github.com/wekan/wekan/pull/2867).
1. Upgrade mongo replica version.
2. Access mongo via service url.
3. Change the expose servicePort to numeric.
Thanks to jiangytcn.
- [Fix browser console errors when editing user profile name](https://github.com/wekan/wekan/commit/2bf004120d5a43cd3c3c060fc7c0c30d1b01f220).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.57 2019-12-22 Wekan release
This release adds the following features:
- [Allow card and checklist API creation for authorized board members](https://github.com/wekan/wekan/pull/2854).
Thanks to Robert-Lebedeu.
- [Visual difference for inactive user in Administration: strikethrough](https://github.com/wekan/wekan/commit/1f1aea87a421ca5e7931d220d10c838574208e2c).
Thanks to hever and xet7.
and adds the following updates:
- [Upgrade to Meteor 1.8.3 and Node 8.17.0. Update release scripts. Fix ldap background sync documentation part 2](https://github.com/wekan/wekan/commit/782d0b620988628f40f50f9cd824f6652cfb0dd9).
Thanks to xet7.
and fixes the following bugs:
- [Fix: Don't add a blank space for empty custom fields on minicards](https://github.com/wekan/wekan/commit/e2a374f0aad8489a84d6de9966c281a812b5eca3).
Thanks to roobre and xet7.
- [Fix: Allow to set empty card title, AssignedBy and RequestedBy](https://github.com/wekan/wekan/commit/25561946edf37351f67cf7500902dde7d9114d2f).
Thanks to justinr1234 and xet7.
- [Fix comment text disappearing when clicking outside of comment text area.
Fix lint error](https://github.com/wekan/wekan/commit/3b3950369ce07aa9e6fc4ab1bef9fb8a4993e398).
Thanks to xet7.
- [Fix ldap background sync documentation](https://github.com/wekan/wekan/pull/2855).
Thanks to koelle25.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.56 2019-11-21 Wekan release
This release adds the following updates:
- [Update to Meteor 1.8.2. Update dependencies](https://github.com/wekan/wekan/commit/38dfe0b9a71a083adc2de1a81170fea0e4a8e53f).
Thanks to xet7.
- [Fix lint errors and update travis NPM version](https://github.com/wekan/wekan/commit/b0f345ba21830b033c9edcc8ee5252b280111ae7).
Thanks to xet7.
- [Change base image to rolling, that is currently Ubuntu eoan
version](https://github.com/wekan/wekan/commit/c66cc3d4dadb15b669256530cfda89359cdb9340).
Thanks to xet7.
- [It seems Ubuntu eoan package bsdtar has been renamed to
libarchive-tools](https://github.com/wekan/wekan/commit/c60967e935bdc0e7e9aea0a1c23178aee8a73c29).
Thanks to xet7.
and fixes the following bugs:
- [Fix slow scroll on card detail by setting scrollInertia to 0](https://github.com/wekan/wekan/commit/599ace1db7918df41d9708d14b0351acb0f8688e).
Thanks to cafeoh.
- [Fix lint errors](https://github.com/wekan/wekan/commit/788dd0a81a06efee165007a92780f9e8c2c754ac).
Thanks to xet7.
- [Remove eslint option that does not work](https://github.com/wekan/wekan/commit/a06daff92e5f7cca55d1698252e3aa6526877c8b).
Thanks to xet7.
- [Try to fix lint errors](https://github.com/wekan/wekan/commit/58e505f79a0617011576bdded9427b0d448d6107).
Thanks to xet7.
- [Add to Snap MongoDB logging option --quiet](https://github.com/wekan/wekan/commit/c7ded515022fff2c1167ce8938405a846185a710).
Thanks to fmeehan and xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.55 2019-11-19 Wekan release
This release fixes the following bugs:
- [When logged in, use database for setting, so that changes are immediate. Only on public board use cookies.
Comment out Collapse CSS that is not in use](https://github.com/wekan/wekan/commit/351d4767d7e93c90ac798769d6071da8730d834f).
Thanks to xet7.
- [Use database when logged in. Part 2](https://github.com/wekan/wekan/commit/4786b0c18ddeb8f48525216eabebdced7159467d).
Thanks to xet7.
- [Use database when logged in. Part 3](https://github.com/wekan/wekan/commit/115d23f9293cad8a93f18f75a47a8a65756f71ce).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.54 2019-11-18 Wekan release
This release adds the following new features:
- [New feature: Now there is popup selection of Lists/Swimlanes/Calendar/Roles](https://github.com/wekan/wekan/commit/96abe3c6914ce37d9fb44da8fda375e40ad65c9e).
Thanks to xet7.
- [New feature, not set visible yet, because switching to it does not
work properly yet: Collapsible Swimlanes](https://github.com/wekan/wekan/issues/2804).
Thanks to xet7.
and fixes the following bugs:
- [Fix: Public board now loads correctly. When you select one of Lists/Swimlanes/Calendar view and
reload webbrowser page, it can change view](https://github.com/wekan/wekan/issues/2311).
Thanks to xet7.
- [Fix: List sorting commented out](https://github.com/wekan/wekan/issues/2800).
Thanks to xet7.
- [Fix: Errors hasHiddenMinicardText, hasShowDragHandles, showSort, hasSortBy, profile,
FirefoxAndroid/IE11/Vivaldi/Chromium browsers not working by using cookies instead of
database](https://github.com/wekan/wekan/issues/2643#issuecomment-554907955).
Note: Cookie changes are not always immediate, if there is no effect, you may need to
reload webbrowser page. This could be improved later.
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.53 2019-11-14 Wekan release
This release fixes the following bugs:
- [Revert list sorting change of Wekan v3.51 because it reversed alphabetical sorting of
lists](https://github.com/wekan/wekan/commit/ab2a721a1443b903cdbbbe275f41ffd3269012c6).
Thanks to Dalisay and xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.52 2019-11-14 Wekan release
This release fixes the following bugs:
- [Add database migration for assignee](https://github.com/wekan/wekan/commit/5b41d72e8de93833e1788962427422cff62c09a2).
Thanks to ocdtrekkie and xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.51 2019-11-14 Wekan release
This release fixes the following bugs:
- [Change sorting lists to work on desktop drag handle page instead,
where it seems to work better](https://github.com/wekan/wekan/commit/bbc3ab3f994c5a61a4414bc64b05f5a03d259e46).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.50 2019-11-13 Wekan release
This release adds the following new features:
- [Allowing lists to be sorted by modifiedAt when not in draggable
mode](https://github.com/wekan/wekan/commits/77f8b76d4e13c35ea3451622176bbb69a4d39a32).
Thanks to whowillcare.
- Allow user to sort Lists in Board by his own preference boardadmin can star
list [1](https://github.com/wekan/wekan/commit/bc2a20f04e32607f8488a9cecd815647fb43e40e),
[2](https://github.com/wekan/wekan/commit/bc2a20f04e32607f8488a9cecd815647fb43e40e).
Thanks to whowillcare.
- [Allowing user to filter list in Filter function not just cards
commit](https://github.com/wekan/wekan/commit/d2d4840758b0f5aed7feb4f6a459bb2b2d1a3f0b).
Thanks to whowillcare.
- [Allow user to search Lists in Board](https://github.com/wekan/wekan/commit/32f50e16586696ec7d100ce0438d1030ae1f606e).
Thanks to whowillcare.
- Enhancement: [Set card times more sensible using the 'Today' button in
datepicker](https://github.com/wekan/wekan/pull/2747).
Thanks to liske.
- [At card, added Assignee field like Jira, and REST API for it](https://github.com/wekan/wekan/issues/2452).
Parts:
[Add assignee](https://github.com/wekan/wekan/commit/9e1aaf163f3bd0b3c2d2aee8225d111f83b3d421),
[Remove Assignee. Avatar icon is at card and assignee details](https://github.com/wekan/wekan/commit/3e8f9ef1a5275a5e9b691c7e74dc73b97a43689a),
[When selecting new assignee (+) icon, list shows names who to add](https://github.com/wekan/wekan/commit/32ce2b51d8bff5e8851732394a8bae3c56f8b0b6),
[More progress](https://github.com/wekan/wekan/commit/ea823ab68fd5243c8485177e44a074be836836b8),
[In add assignee popup, avatars are now visible](https://github.com/wekan/wekan/commit/56efb5c41075151eeb259d99990a7e86695b2b69),
[Add assignee popup title](https://github.com/wekan/wekan/commit/31dbdc835d5a092b8360a4dbe93e9fbcce068855),
[Prevent more than one assignee](https://github.com/wekan/wekan/commit/1728298659521ee8e6fc94fedad3160030b9a2c3),
[When there is one selected assignee on card, don't show + button for adding more assignees, because there can only be one
assignee](https://github.com/wekan/wekan/commit/3cf09efb13438d66db6cf739591c679ea538d812),
[Now assignee is visible also at minicard](https://github.com/wekan/wekan/commit/9fd14f7ecb593d3debf5adff8f6c61adb0c3feca),
[Update REST API docs, there can only be one assignee in array](https://github.com/wekan/wekan/commit/de7509dc60257667192054e320b381f9dd0f0a31).
Thanks to xet7.
- [More mobile drag handles, and optional desktop drag handles](https://github.com/wekan/wekan/issues/2081): In Progress.
Parts:
[Some drag handle fixes](https://github.com/wekan/wekan/commit/6a8960547729148bd3085cb469f9e93d510ed66c),
[Fix desktop swimlane drag handle position](https://github.com/wekan/wekan/commit/2ec15602d284122fce1a45bed352d0d4050162e2),
[Fix card, list and swimlane move. Allow moving cards in multiselect mode](https://github.com/wekan/wekan/commit/537a48bede250155b30ec264904ba320625bab73).
Thanks to xet7.
and adds the following updates:
- [Update Node.js to v8.16.2](https://github.com/wekan/wekan/commit/1eb3d25b40797fdab41d7dd59405cfcea81dcc61).
Thanks to xet7.
and fixes the following bugs:
- Bug Fix [#2093](https://github.com/wekan/wekan/issues/2093), need to [clean up the
temporary file](https://github.com/wekan/wekan/commit/2737d6b23f3a0fd2314236a85fbdee536df745a2).
Thanks to whowillcare.
- Bug Fix [#2093](https://github.com/wekan/wekan/issues/2093): the broken [should be prior to file attachment feature introduced](https://github.com/wekan/wekan/commit/f53c624b0f6c6ebcc20c378a153e5cda8d73463c).
Thanks to whowillcare.
- [Fix typo on exporting subtasks](https://github.com/wekan/wekan/commit/00d581245c1fe6a01ef372ca87d8a25bc7b937e4).
Thanks to xiorcala.
- [Change the radom to random typo in export.js](https://github.com/wekan/wekan/commit/e195c731de88aba4026c239f4552ae821d522ec7).
Thanks to whowillcare.
- Fix: [List last modify time will be affected by cards dueAt, endAt](https://github.com/wekan/wekan/commit/3308d90a3a6a1ddeed33966767937cd2c2c90cb5).
Thanks to whowillcare.
- Revert creating new list to left, now creates again to right. Thanks to whowillcare.
Revert New List item moved from right to left. Thanks to derbolle and xet7.
[1](https://github.com/wekan/wekan/commit/806df30ba3499cef193eaf1b437cdef65282510f).
- REST API: [Fix deletion of a single board card](https://github.com/wekan/wekan/pull/2778).
Thanks to liske.
- [cardDate: endDate coloring change](https://github.com/wekan/wekan/pull/2779).
If no due-date timestamp is set => Gray.
If end-date timestamp is younger than due-date timestamp => Green.
If end-date timestamp is older than due-date timestamp => Red.
Thanks to bandresen.
- [Fixed Card Open Webhook Error](https://github.com/wekan/wekan/issues/2780).
Thanks to jymcheong.
- [Fixed OpenAPI docs generation](https://github.com/wekan/wekan/pull/2783).
Thanks to bentiss.
- [Fixed close card button not visible on mobile web](https://github.com/wekan/wekan/36b5965dd07e3f0fd90069353310739c394c220f).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.49 2019-10-09 Wekan release
This release fixes the following bugs:
- [Fix prettier errors](https://github.com/wekan/wekan/commits/36e006fa4e78fe94e627625d1cc589654668f22a).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.48 2019-10-09 Wekan release
This release fixes the following bugs:
- [Make possible to upload attachments using drag-and-drop or Ctrl+V without setting the environmental-variable MAX_IMAGE_PIXEL](https://github.com/wekan/wekan/pull/2754).
Thanks to moserben16.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.47 2019-10-09 Wekan release
This release fixes the following bugs:
- [REST API: fix handling of members property on card creation](https://github.com/wekan/wekan/pull/2751).
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.46 2019-10-07 Wekan release
This release fixes the following bugs:
- [More black minicard badges](https://github.com/wekan/wekan/commit/68be12d166b21a41b4e2c4021b0966807e5ed1e6).
Thanks to sfahrenholz and xet7.
- [REST API: fix creation of Checklists](https://github.com/wekan/wekan/pull/2747).
Thanks to liske.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.45 2019-10-03 Wekan release
This release adds the following new features:
- [Cards due timeline will be shown in Calendar view](https://github.com/wekan/wekan/pull/2738).
Thanks to whowillcare.
- [Modified due day reminders in cards.js, so wekan server admin can control the reminder more flexibly](https://github.com/wekan/wekan/pull/2738).
i.e. NOTIFY_DUE_DAYS_BEFORE_AND_AFTER = 0 notification will be sent on due day only.
NOTIFY_DUE_DAYS_BEFORE_AND_AFTER = 2,0 it means notification will be sent on both due day and two days before.
Thanks to whowillcare.
- [Added modifications the help files, related to NOTIFY_DUE_DAYS_BEFORE_AND_AFTER](https://github.com/wekan/wekan/pull/2740).
Thanks to whowillcare.
and fixes the following bugs:
- [Modified list.style regarding .list-header-name when in mobile mode. It was too close to left arrow](https://github.com/wekan/wekan/pull/2740).
Thanks to whowillcare.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.44 2019-09-17 Wekan release
This release adds the following languages:
- [Add language: Slovenian](https://github.com/wekan/wekan/commit/125231beff0fb84a18a46fe246fa12e098246985).
Thanks to translators.
and fixes the following bugs:
- [Fix: in richer editor @ autocomplete doesn't really insert the username properly](https://github.com/wekan/wekan/pull/2717).
Thanks to whowillcare.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.43 2019-09-17 Wekan release
This release fixes the following bugs:
- [In richer editor, @user might not get pickup correctly, if it's formated](https://github.com/wekan/wekan/pull/2715).
Thanks to whowillcare.
- [Table content should have word-wrap](https://github.com/wekan/wekan/pull/2715).
Thanks to whowillcare.
- [Two-way hooks locking mechanism will fail sometime, therefore, change all comment insert or update to direct, which means it won't invoke any hook](https://github.com/wekan/wekan/pull/2715).
Thanks to whowillcare.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.42 2019-09-14 Wekan release
This release removed the following new features:
- [Revert drag handle changes of Wekan v3.41](https://github.com/wekan/wekan/commit/57119868bbb49f47c7d0b51b9952df9bd83d46f5).
Thanks to Keelan.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.41 2019-09-13 Wekan release
This release adds the following new features:
- [More Mobile and Desktop drag handles for Swimlanes/Lists/Cards. Part 1](https://github.com/wekan/wekan/commit/ff550e91103115e7b731dd80c4588b93b2d4c64f).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.40 2019-09-11 Wekan release
This release fixes the following bugs:
- [Fix subcard selector](https://github.com/wekan/wekan/pull/2697).
Thanks to justinr1234.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.39 2019-09-11 Wekan release
This release fixes the following bugs:
- [To load all boards, revert Wekan v3.37 Fix Linked cards make load all cards of database](https://github.com/wekan/wekan/commit/6ce8eeee6c477cd39b684c47bf122b5872818ada).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.38 2019-09-11 Wekan release
- Update translations. Thanks to translators.
# v3.37 2019-09-07 Wekan release
This release fixes the following bugs:
- LDAP: [Fix USERDN example, when parameters contain spaces](https://github.com/wekan/wekan/commit/6cbd4cabc716c755e547abb798f657fe5476ed04).
LDAP_AUTHENTIFICATION_USERDN="CN=ldap admin,CN=users,DC=domainmatter,DC=lan" .
Thanks to compumatter.
- [Fix: Linked cards make load all cards of database](https://github.com/wekan/wekan/commit/a56988c487745b2879cebe1943e7a987016e8bef).
Thanks to Akuket.
- [Fix Unable to drag select text without closing card details](https://github.com/wekan/wekan/pull/2690).
Thanks to justinr1234.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.36 2019-09-05 Wekan release
This release adds the following new features:
- [Complete the original author's webhook functions and add two-way webhook type](https://github.com/wekan/wekan/pull/2665):
1. Make webhook function more complete by allowing user to specify name and token of
a webhook to increase security.
1. Allow wekan admin to sepcify a global webhook.
3. Add new type of two-way webhook that can act on the JSON webhook return payload:
3.1. If the payload data contains cardId, boardId, and comment key words,
3.2. If it has commentId, an existing comment will be modified
3.3. If it doesn't have commentId, then a new comment will be added, otherwise, does nothing.
Thanks to whowillcare.
- [Patch admin search feature to Search in all users, not just "loaded" users
in the client](https://github.com/wekan/wekan/pull/2667).
Thanks to Akuket.
- [Devcontainer: Moved MAIL-Vars to not-committed file, and added PATH with meteor to
Environment](https://github.com/wekan/wekan/pull/2672).
Thanks to road42.
and fixes the following bugs:
- [Fix incorrect date types for created and updated, so now newest card comments are at top](https://github.com/wekan/wekan/pull/2679).
Thanks to justinr1234.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.35 2019-08-29 Wekan release
This release fixes the following bugs:
- [Try to fix Snap](https://github.com/wekan/wekan/commit/71d32c6bc8e6affd345026797ff31e94a0a10d77).
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.34 2019-08-29 Wekan release
This release fixes the following bugs:
- [Snap: Delete all .swp files](https://github.com/wekan/wekan/commit/d5403bbfc53390aeaaf68eb452bc24d88f1e0942).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.33 2019-08-29 Wekan release
This release adds the following new features:
- [Add card color to calendar event. The color of the calendar event will match the card
color](https://github.com/wekan/wekan/pull/2664).
Thanks to grmpfhmbl.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.32 2019-08-29 Wekan release
This release fixes the following bugs:
- [Fix Snap adopt-info](https://github.com/wekan/wekan/commit/79d4cd83b1fa83bb814230683b7449ed7f3e1ede).
Thanks to [popey at Snapcraft forum](https://forum.snapcraft.io/t/dirty-snap-release/12975/12).
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.31 2019-08-29 Wekan release
This release fixes the following bugs:
- [Try](https://github.com/wekan/wekan/commit/be5f435bc5f500b24bc838ac1dc8bf3bb9a33a22) to
[fix adopt-info](https://forum.snapcraft.io/t/dirty-snap-release/12975/8).
Thanks to ogra at Snapcraft forum.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.30 2019-08-29 Wekan release
This release fixes the following bugs:
- Snap: [Change version-script to adopt-info](https://github.com/wekan/wekan/commit/0ff5ce8fde6cc9a05a3c8b93e18ebce7282d3a67)
to [fix dirty](https://forum.snapcraft.io/t/dirty-snap-release/12975/4).
Thanks to popey and daniel at Snapcraft forum.
- [Delete another phantomjs binary from Snap](https://github.com/wekan/wekan/commit/5084102e6e17fa2cb3bc8c1180745e15379fab5f).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.29 2019-08-29 Wekan release
This release fixes the following bugs:
- [Fix Snap](https://github.com/wekan/wekan/commit/7761a22bb4e88ad9a5a39ed84e1ff244015c3a58).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.28 2019-08-29 Wekan release
This release fixes the following bugs:
- Fix broken Sandstorm release by reverting upgrading MongoDB.
Thanks to xet7
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.27 2019-08-29 Wekan release
This release adds the following upgrades:
- [Upgrade Node, Mongo, fibers and qs](https://github.com/wekan/wekan/commit/e21c47d3cfe0f228ce5ab394142c6ec6ee090d65).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.26 2019-08-28 Wekan release
This release adds the following new features:
- [Add devcontainer](https://github.com/wekan/wekan/pull/2659) and some [related fixes](https://github.com/wekan/wekan/pull/2660).
Thanks to road42.
and fixes the following bugs:
- [Add missing modules](https://github.com/wekan/wekan/pull/2653).
Thanks to GhassenRjab.
- [Add package-lock.json](https://github.com/wekan/wekan/commit/ad01526124216abcc8b3c8230599c4eda331a86d).
Thanks to GhassenRjab and xet7.
- [Fix last label undefined](https://github.com/wekan/wekan/pull/2657).
Thanks to justinr1234.
- [Default to BIGEVENTS_PATTERN=NONE so that Wekan sends less email notifications](https://github.com/wekan/wekan/commit/0083215ea3955a950d345d44a8663e5b05e8f00f).
Thanks to rinnaz and xet7.
- [Fix app hang when Meteor.user() is null and list spinner is loaded bug](https://github.com/wekan/wekan/pull/2654).
Thanks to urakagi.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.25 2019-08-23 Wekan release
This release adds the following new features:
- [Limit card width to fixed size](https://github.com/wekan/wekan/commit/0dd3ff29f2b558bc912b330f178347035dcc46c7).
Thanks to xet7.
and fixes the following bugs:
- [Fix](https://github.com/wekan/wekan/pull/2645) [selecting user accounts when importing from Trello](https://github.com/wekan/wekan/issues/2638).
Thanks to justrinr1234.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.24 2019-08-22 Wekan release
This release fixes the following bugs:
- [Snap: Delete old MongoDB log, and log to syslog instead, because syslog usually already has
log rotation](https://github.com/wekan/wekan/commit/cc792ddd57691bb54972c73b9c861c768fce8c34).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.23 2019-08-20 Wekan release
This release fixes the following bugs:
- [Fix login did not work](https://github.com/wekan/wekan/commit/b2deab544bfeea49017bec27f92f1269b0b7ec43).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.22 2019-08-20 Wekan release
This release adds the following new features:
- [Wrap minicard text labels to multiple rows](https://github.com/wekan/wekan/commit/af830812dbbf7d766a754d937308b11373c66e5a).
Thanks to xet7.
and fixes the following bugs:
- [Fix: Some users cannot switch views or languages](https://github.com/wekan/wekan/issues/2630).
Thanks to xet7 and justinr1234.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.21 2019-08-16 Wekan release
This release adds the following new features:
- [In the filter menu, 1) Turning on "show archive" will request archived lists and show them on the ListView.
2) Turning on "hide empty lists" will hide empty lists on the ListView](https://github.com/wekan/wekan/pull/2632).
Thanks to urakagi.
and fixes the following bugs:
- [Fix mismatched queries](https://github.com/wekan/wekan/pull/2628).
Thanks to justinr1234.
- [Fix Summernote too wide when in mobile screen](https://github.com/wekan/wekan/issues/2621).
Thanks to whowillcare.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.20 2019-08-15 Wekan release
This release fixes the following security issues:
@@ -170,7 +964,7 @@ This release fixes the following bugs:
- [Add missing dependencies back and revert deleting phantomjs](https://github.com/wekan/wekan/commit/32e9aa0ddaf1b015825b8c62ad17ed74b449e4b1).
Thanks to whowillcare and xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.09 2019-08-07 Wekan release
+7 -7
View File
@@ -1,13 +1,13 @@
FROM ubuntu:disco
FROM ubuntu:rolling
LABEL maintainer="wekan"
# Set the environment variables (defaults where required)
# DOES NOT WORK: paxctl fix for alpine linux: https://github.com/wekan/wekan/issues/1303
# ENV BUILD_DEPS="paxctl"
ENV BUILD_DEPS="apt-utils bsdtar gnupg gosu wget curl bzip2 g++ build-essential git ca-certificates" \
ENV BUILD_DEPS="apt-utils libarchive-tools gnupg gosu wget curl bzip2 g++ build-essential git ca-certificates python3" \
DEBUG=false \
NODE_VERSION=v8.16.0 \
METEOR_RELEASE=1.8.1 \
NODE_VERSION=v12.15.0 \
METEOR_RELEASE=1.9.0 \
USE_EDGE=false \
METEOR_EDGE=1.5-beta.17 \
NPM_VERSION=latest \
@@ -21,12 +21,12 @@ ENV BUILD_DEPS="apt-utils bsdtar gnupg gosu wget curl bzip2 g++ build-essential
ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURES_BERORE=3 \
ACCOUNTS_LOCKOUT_UNKNOWN_USERS_LOCKOUT_PERIOD=60 \
ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURE_WINDOW=15 \
RICHER_CARD_COMMENT_EDITOR=true \
RICHER_CARD_COMMENT_EDITOR=false \
CARD_OPENED_WEBHOOK_ENABLED=false \
ATTACHMENTS_STORE_PATH="" \
MAX_IMAGE_PIXEL="" \
IMAGE_COMPRESS_RATIO="" \
BIGEVENTS_PATTERN="" \
BIGEVENTS_PATTERN=NONE \
NOTIFY_DUE_DAYS_BEFORE_AND_AFTER="" \
NOTIFY_DUE_AT_HOUR_OF_DAY="" \
EMAIL_NOTIFICATION_TIMEOUT=30000 \
@@ -178,7 +178,7 @@ RUN \
mv node-${NODE_VERSION}-${ARCHITECTURE} /opt/nodejs && \
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.0 /home/wekan/.config && \
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 && \
\
#DOES NOT WORK: paxctl fix for alpine linux: https://github.com/wekan/wekan/issues/1303
+8 -28
View File
@@ -54,13 +54,12 @@ that by providing one-click installation on various platforms.
- [Features][features]: Wekan has real-time user interface.
- [Platforms][platforms]: Wekan supports many platforms.
Wekan is critical part of new platforms Wekan is currently being integrated to.
- [Integrations][integrations]: Current possible integrations and future plans.
## Requirements
- 64bit: Linux [Snap](https://github.com/wekan/wekan-snap/wiki/Install) or [Sandstorm](https://sandstorm.io) /
[Mac](https://github.com/wekan/wekan/wiki/Mac) / [Windows](https://github.com/wekan/wekan/wiki/Install-Wekan-from-source-on-Windows).
[More Platforms](https://github.com/wekan/wekan/wiki/Platforms). [ARM progress](https://github.com/wekan/wekan/issues/1053#issuecomment-410919264).
[More Platforms](https://github.com/wekan/wekan/wiki/Platforms), bundle for RasPi3 ARM and other CPUs where Node.js and MongoDB exists.
- 1 GB RAM minimum free for Wekan. Production server should have minimum total 4 GB RAM.
For thousands of users, for example with [Docker](https://github.com/wekan/wekan/blob/devel/docker-compose.yml): 3 frontend servers,
each having 2 CPU and 2 wekan-app containers. One backend wekan-db server with many CPUs.
@@ -74,41 +73,25 @@ that by providing one-click installation on various platforms.
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.
## Roadmap
## Roadmap and Demo
[Roadmap Milestones](https://github.com/wekan/wekan/milestones)
[Roadmap][roadmap_wekan] - Public read-only board at Wekan demo.
[Developer Documentation][dev_docs]
- There is many companies and individuals contributing code to Wekan, to add features and bugfixes
[many times a day](https://github.com/wekan/wekan/blob/devel/CHANGELOG.md).
- [Please add Add new Feature Requests and Bug Reports immediately](https://github.com/wekan/wekan/issues).
- [Commercial Support](https://wekan.team).
- [Bounties](https://wekan.team/bounties/index.html).
- [Commercial Support](https://wekan.team/commercial-support/).
We also welcome sponsors for features and bugfixes.
By working directly with Wekan you get the benefit of active maintenance and new features added by growing Wekan developer community.
## Demo
[Wekan demo][roadmap_wefork]
## Screenshot
[More screenshots at Features page](https://github.com/wekan/wekan/wiki/Features)
[![Screenshot of Wekan][screenshot_wefork]][roadmap_wefork]
## Stable
- master+devel branch. At release, devel is merged to master.
- Receives fixes and features that have been tested at edge that they work.
- If you want automatic updates, [use Snap](https://github.com/wekan/wekan-snap/wiki/Install).
- If you want to test before update, [use Docker quay.io release tags](https://github.com/wekan/wekan/wiki/Docker).
## Edge
- edge branch. All new fixes and features are added to here first. [Testing Edge](https://github.com/wekan/wekan-snap/wiki/Snap-Developer-Docs).
[![Screenshot of Wekan][screenshot_wekan]][roadmap_wekan]
## License
@@ -117,14 +100,11 @@ with [Meteor](https://www.meteor.com).
[platforms]: https://github.com/wekan/wekan/wiki/Platforms
[dev_docs]: https://github.com/wekan/wekan/wiki/Developer-Documentation
[screenshot_wekan]: http://i.imgur.com/cI4jW2h.png
[screenshot_wefork]: https://wekan.github.io/wekan-markdown.png
[screenshot_wekan]: https://wekan.github.io/wekan-markdown.png
[features]: https://github.com/wekan/wekan/wiki/Features
[integrations]: https://github.com/wekan/wekan/wiki/Integrations
[roadmap_wekan]: http://try.wekan.io/b/MeSsFJaSqeuo9M6bs/wekan-roadmap
[roadmap_wefork]: https://wekan.indie.host/b/t2YaGmyXgNkppcFBq/wekan-fork-roadmap
[roadmap_wekan]: https://boards.wekan.team/b/D2SzJKZDS4Z48yeQH/wekan-open-source-kanban-board-with-mit-license
[wekan_issues]: https://github.com/wekan/wekan/issues
[wekan_issues]: https://github.com/wekan/wekan/issues
[wefork_issues]: https://github.com/wefork/wekan/issues
[docker_image]: https://hub.docker.com/r/wekanteam/wekan/
[travis_badge]: https://travis-ci.org/wekan/wekan.svg?branch=devel
[travis_status]: https://travis-ci.org/wekan/wekan
+1 -1
View File
@@ -1,5 +1,5 @@
appId: wekan-public/apps/77b94f60-dec9-0136-304e-16ff53095928
appVersion: "v3.19.0"
appVersion: "v3.76.0"
files:
userUploads:
- README.md
+1 -1
View File
@@ -85,7 +85,7 @@ BlazeComponent.extendComponent({
const lastLabel = Boards.findOne(Session.get('currentBoard')).getLabelById(
lastLabelId,
);
if (lastLabel.name === undefined || lastLabel.name === '') {
if (lastLabel && (lastLabel.name === undefined || lastLabel.name === '')) {
return lastLabel.color;
} else {
return lastLabel.name;
+1 -1
View File
@@ -9,7 +9,7 @@
clear: both
.activity
margin: 10px 0
margin: 0.5px 0
display: flex
.member
-3
View File
@@ -16,9 +16,6 @@ BlazeComponent.extendComponent({
events() {
return [
{
'click .js-new-comment:not(.focus)'() {
commentFormIsOpen.set(true);
},
'submit .js-new-comment-form'(evt) {
const input = this.getInput();
const text = input.val().trim();
@@ -46,3 +46,23 @@
&:is-open
cursor: auto
.comment-item
background-color: #fff
border: 0
box-shadow: 0 1px 2px rgba(0, 0, 0, .23)
color: #8c8c8c
height: 36px
margin: 4px 4px 6px 0
width: 92%
&:hover
background: darken(white, 12%)
&.add-comment
display: flex
margin: 5px
a
display: block
margin: auto
+88 -28
View File
@@ -1,3 +1,5 @@
import { Cookies } from 'meteor/ostrio:cookies';
const cookies = new Cookies();
const subManager = new SubsManager();
const { calculateIndex, enableClickOnTouch } = Utils;
const swimlaneWhileSortingHeight = 150;
@@ -89,7 +91,6 @@ BlazeComponent.extendComponent({
helper.append(list.clone());
return helper;
},
handle: '.js-swimlane-header',
items: '.swimlane:not(.placeholder)',
placeholder: 'swimlane placeholder',
distance: 7,
@@ -193,6 +194,32 @@ BlazeComponent.extendComponent({
// ugly touch event hotfix
enableClickOnTouch('.js-swimlane:not(.placeholder)');
this.autorun(() => {
let showDesktopDragHandles = false;
currentUser = Meteor.user();
if (currentUser) {
showDesktopDragHandles = (currentUser.profile || {})
.showDesktopDragHandles;
} else if (cookies.has('showDesktopDragHandles')) {
showDesktopDragHandles = true;
} else {
showDesktopDragHandles = false;
}
if (!Utils.isMiniScreen() && showDesktopDragHandles) {
$swimlanesDom.sortable({
handle: '.js-swimlane-header-handle',
});
} else if (!Utils.isMiniScreen() && !showDesktopDragHandles) {
$swimlanesDom.sortable({
handle: '.swimlane-header',
});
}
// Disable drag-dropping if the current user is not a board member or is miniscreen
$swimlanesDom.sortable('option', 'disabled', !userIsMember());
$swimlanesDom.sortable('option', 'disabled', Utils.isMiniScreen());
});
function userIsMember() {
return (
Meteor.user() &&
@@ -210,21 +237,30 @@ BlazeComponent.extendComponent({
},
isViewSwimlanes() {
const currentUser = Meteor.user();
if (!currentUser) return false;
return (currentUser.profile || {}).boardView === 'board-view-swimlanes';
currentUser = Meteor.user();
if (currentUser) {
return (currentUser.profile || {}).boardView === 'board-view-swimlanes';
} else {
return cookies.get('boardView') === 'board-view-swimlanes';
}
},
isViewLists() {
const currentUser = Meteor.user();
if (!currentUser) return true;
return (currentUser.profile || {}).boardView === 'board-view-lists';
currentUser = Meteor.user();
if (currentUser) {
return (currentUser.profile || {}).boardView === 'board-view-lists';
} else {
return cookies.get('boardView') === 'board-view-lists';
}
},
isViewCalendar() {
const currentUser = Meteor.user();
if (!currentUser) return false;
return (currentUser.profile || {}).boardView === 'board-view-cal';
currentUser = Meteor.user();
if (currentUser) {
return (currentUser.profile || {}).boardView === 'board-view-cal';
} else {
return cookies.get('boardView') === 'board-view-cal';
}
},
openNewListForm() {
@@ -309,25 +345,46 @@ BlazeComponent.extendComponent({
events(start, end, timezone, callback) {
const currentBoard = Boards.findOne(Session.get('currentBoard'));
const events = [];
const pushEvent = function(card, title, start, end, extraCls) {
start = start || card.startAt;
end = end || card.endAt;
title = title || card.title;
const className =
(extraCls ? `${extraCls} ` : '') +
(card.color ? `calendar-event-${card.color}` : '');
events.push({
id: card._id,
title,
start,
end: end || card.endAt,
allDay:
Math.abs(end.getTime() - start.getTime()) / 1000 === 24 * 3600,
url: FlowRouter.url('card', {
boardId: currentBoard._id,
slug: currentBoard.slug,
cardId: card._id,
}),
className,
});
};
currentBoard
.cardsInInterval(start.toDate(), end.toDate())
.forEach(function(card) {
events.push({
id: card._id,
title: card.title,
start: card.startAt,
end: card.endAt,
allDay:
Math.abs(card.endAt.getTime() - card.startAt.getTime()) /
1000 ===
24 * 3600,
url: FlowRouter.url('card', {
boardId: currentBoard._id,
slug: currentBoard.slug,
cardId: card._id,
}),
});
pushEvent(card);
});
currentBoard
.cardsDueInBetween(start.toDate(), end.toDate())
.forEach(function(card) {
pushEvent(
card,
`${card.title} ${TAPi18n.__('card-due')}`,
card.dueAt,
new Date(card.dueAt.getTime() + 36e5),
);
});
events.sort(function(first, second) {
return first.id > second.id ? 1 : -1;
});
callback(events);
},
eventResize(event, delta, revertFunc) {
@@ -360,8 +417,11 @@ BlazeComponent.extendComponent({
};
},
isViewCalendar() {
const currentUser = Meteor.user();
if (!currentUser) return false;
return (currentUser.profile || {}).boardView === 'board-view-cal';
currentUser = Meteor.user();
if (currentUser) {
return (currentUser.profile || {}).boardView === 'board-view-cal';
} else {
return cookies.get('boardView') === 'board-view-cal';
}
},
}).register('calendarView');
+78
View File
@@ -53,3 +53,81 @@ position()
padding: 0 0px 0px 0
overflow-x: hidden
overflow-y: auto
calendar-event-color(background, borderColor, color...)
background: background !important
border-color: borderColor
if color
color: color !important //overwrite text for better visibility
.calendar-event-green
calendar-event-color(#3cb500, #2a8000, #ffffff) //White text for better visibility
.calendar-event-yellow
calendar-event-color(#fad900, #c7ac00, #000) //Black text for better visibility
.calendar-event-orange
calendar-event-color(#ff9f19, #cc7c14, #000) //Black text for better visibility
.calendar-event-red
calendar-event-color(#eb4646, #b83737, #ffffff) //White text for better visibility
.calendar-event-purple
calendar-event-color(#a632db, #7d26a6, #ffffff) //White text for better visibility
.calendar-event-blue
calendar-event-color(#0079bf, #005a8a, #ffffff) //White text for better visibility
.calendar-event-pink
calendar-event-color(#ff78cb, #cc62a3, #000) //Black text for better visibility
.calendar-event-sky
calendar-event-color(#00c2e0, #0094ab, #ffffff) //White text for better visibility
.calendar-event-black
calendar-event-color(#4d4d4d, #1a1a1a, #ffffff) //White text for better visibility
.calendar-event-lime
calendar-event-color(#51e898, #3eb375, #000) //Black text for better visibility
.calendar-event-silver
calendar-event-color(#c0c0c0, #8c8c8c, #000) //Black text for better visibility
.calendar-event-peachpuff
calendar-event-color(#ffdab9, #ccaf95, #000) //Black text for better visibility
.calendar-event-crimson
calendar-event-color(#dc143c, #a8112f, #ffffff) //White text for better visibility
.calendar-event-plum
calendar-event-color(#dda0dd, #a87ba8, #000) //Black text for better visibility
.calendar-event-darkgreen
calendar-event-color(#006400, #003000, #ffffff) //White text for better visibility
.calendar-event-slateblue
calendar-event-color(#6a5acd, #4f4399, #ffffff) //White text for better visibility
.calendar-event-magenta
calendar-event-color(#ff00ff, #cc00cc, #ffffff) //White text for better visibility
.calendar-event-gold
calendar-event-color(#ffd700, #ccaa00, #000) //Black text for better visibility
.calendar-event-navy
calendar-event-color(#000080, #000033, #ffffff) //White text for better visibility
.calendar-event-gray
calendar-event-color(#808080, #333333, #ffffff) //White text for better visibility
.calendar-event-saddlebrown
calendar-event-color(#8b4513, #572b0c, #ffffff) //White text for better visibility
.calendar-event-paleturquoise
calendar-event-color(#afeeee, #8ababa, #000) //Black text for better visibility
.calendar-event-mistyrose
calendar-event-color(#ffe4e1, #ccb8b6, #000) //Black text for better visibility
.calendar-event-indigo
calendar-event-color(#4b0082, #2b004d, #ffffff) //White text for better visibility
@@ -241,6 +241,7 @@ setBoardColor(color)
background-color #ffffff !important
padding 15px !important
border 1px solid #000000 !important
word-wrap: break-word
// When card has comment, emphasis on minicard:
// bigger red comment icon and number of comments,
+65 -11
View File
@@ -77,6 +77,11 @@ template(name="boardHeaderBar")
i.fa.fa-archive
span {{_ 'archives'}}
//if showSort
// a.board-header-btn.js-open-sort-view(title="{{_ 'sort-desc'}}")
// i.fa(class="{{directionClass}}")
// span {{_ 'sort'}}{{_ listSortShortDesc}}
a.board-header-btn.js-open-filter-view(
title="{{#if Filter.isActive}}{{_ 'filter-on-desc'}}{{else}}{{_ 'filter'}}{{/if}}"
class="{{#if Filter.isActive}}emphasis{{/if}}")
@@ -85,15 +90,6 @@ template(name="boardHeaderBar")
if Filter.isActive
a.board-header-btn-close.js-filter-reset(title="{{_ 'filter-clear'}}")
i.fa.fa-times-thin
if currentUser.isAdmin
a.board-header-btn.js-open-rules-view(title="{{_ 'rules'}}")
i.fa.fa-magic
span {{_ 'rules'}}
else if currentUser.isBoardAdmin
a.board-header-btn.js-open-rules-view(title="{{_ 'rules'}}")
i.fa.fa-magic
span {{_ 'rules'}}
a.board-header-btn.js-open-search-view(title="{{_ 'search'}}")
i.fa.fa-search
@@ -102,8 +98,14 @@ template(name="boardHeaderBar")
unless currentBoard.isTemplatesBoard
a.board-header-btn.js-toggle-board-view(
title="{{_ 'board-view'}}")
i.fa.fa-th-large
span {{#if currentUser.profile.boardView}}{{_ currentUser.profile.boardView}}{{else}}{{_ 'board-view-lists'}}{{/if}}
i.fa.fa-caret-down
if $eq boardView 'board-view-lists'
i.fa.fa-trello
if $eq boardView 'board-view-swimlanes'
i.fa.fa-th-large
if $eq boardView 'board-view-cal'
i.fa.fa-calendar
span {{#if boardView}}{{_ boardView}}{{else}}{{_ 'board-view-lists'}}{{/if}}
if canModifyBoard
a.board-header-btn.js-multiselection-activate(
@@ -168,6 +170,44 @@ template(name="boardChangeWatchPopup")
i.fa.fa-check
span.sub-name {{_ 'muted-info'}}
template(name="boardChangeViewPopup")
ul.pop-over-list
li
with "board-view-lists"
a.js-open-lists-view
i.fa.fa-trello.colorful
| {{_ 'board-view-lists'}}
if $eq Utils.boardView "board-view-lists"
i.fa.fa-check
li
with "board-view-swimlanes"
a.js-open-swimlanes-view
i.fa.fa-th-large.colorful
| {{_ 'board-view-swimlanes'}}
if $eq Utils.boardView "board-view-swimlanes"
i.fa.fa-check
li
with "board-view-cal"
a.js-open-cal-view
i.fa.fa-calendar.colorful
| {{_ 'board-view-cal'}}
if $eq Utils.boardView "board-view-cal"
i.fa.fa-check
if currentUser.isAdmin
hr
li
with "board-view-rules"
a.js-open-rules-view(title="{{_ 'rules'}}")
i.fa.fa-magic
| {{_ 'rules'}}
else if currentUser.isBoardAdmin
hr
li
with "board-view-rules"
a.js-open-rules-view(title="{{_ 'rules'}}")
i.fa.fa-magic
| {{_ 'rules'}}
template(name="createBoard")
form
label
@@ -194,6 +234,20 @@ template(name="createBoard")
| /
a.js-board-template {{_ 'template'}}
//template(name="listsortPopup")
// h2
// | {{_ 'list-sort-by'}}
// hr
// ul.pop-over-list
// each value in allowedSortValues
// li
// a.js-sort-by(name="{{value.name}}")
// if $eq sortby value.name
// i(class="fa {{Direction}}")
// | {{_ value.label }}{{_ value.shortLabel}}
// if $eq sortby value.name
// i(class="fa fa-check")
template(name="boardChangeTitlePopup")
form
label
+133 -22
View File
@@ -1,3 +1,7 @@
/*
const DOWNCLS = 'fa-sort-down';
const UPCLS = 'fa-sort-up';
*/
Template.boardMenuPopup.events({
'click .js-rename-board': Popup.open('boardChangeTitle'),
'click .js-custom-fields'() {
@@ -26,6 +30,7 @@ Template.boardMenuPopup.events({
'click .js-outgoing-webhooks': Popup.open('outgoingWebhooks'),
'click .js-import-board': Popup.open('chooseBoardSource'),
'click .js-subtask-settings': Popup.open('boardSubtaskSettings'),
'click .js-card-settings': Popup.open('boardCardSettings'),
});
Template.boardMenuPopup.helpers({
@@ -80,7 +85,27 @@ BlazeComponent.extendComponent({
const currentBoard = Boards.findOne(Session.get('currentBoard'));
return currentBoard && currentBoard.stars >= 2;
},
/*
showSort() {
return Meteor.user().hasSortBy();
},
directionClass() {
return this.currentDirection() === -1 ? DOWNCLS : UPCLS;
},
changeDirection() {
const direction = 0 - this.currentDirection() === -1 ? '-' : '';
Meteor.call('setListSortBy', direction + this.currentListSortBy());
},
currentDirection() {
return Meteor.user().getListSortByDirection();
},
currentListSortBy() {
return Meteor.user().getListSortBy();
},
listSortShortDesc() {
return `list-label-short-${this.currentListSortBy()}`;
},
*/
events() {
return [
{
@@ -94,30 +119,25 @@ BlazeComponent.extendComponent({
'click .js-open-archived-board'() {
Modal.open('archivedBoards');
},
'click .js-toggle-board-view'() {
const currentUser = Meteor.user();
if (
(currentUser.profile || {}).boardView === 'board-view-swimlanes'
) {
currentUser.setBoardView('board-view-cal');
} else if (
(currentUser.profile || {}).boardView === 'board-view-lists'
) {
currentUser.setBoardView('board-view-swimlanes');
} else if (
(currentUser.profile || {}).boardView === 'board-view-cal'
) {
currentUser.setBoardView('board-view-lists');
} else {
currentUser.setBoardView('board-view-swimlanes');
}
},
'click .js-toggle-board-view': Popup.open('boardChangeView'),
'click .js-toggle-sidebar'() {
Sidebar.toggle();
},
'click .js-open-filter-view'() {
Sidebar.setView('filter');
},
/*
'click .js-open-sort-view'(evt) {
const target = evt.target;
if (target.tagName === 'I') {
// click on the text, popup choices
this.changeDirection();
} else {
// change the sort order
Popup.open('listsort')(evt);
}
},
*/
'click .js-filter-reset'(event) {
event.stopPropagation();
Sidebar.setView();
@@ -126,9 +146,6 @@ BlazeComponent.extendComponent({
'click .js-open-search-view'() {
Sidebar.setView('search');
},
'click .js-open-rules-view'() {
Modal.openWide('rulesMain');
},
'click .js-multiselection-activate'() {
const currentCard = Session.get('currentCard');
MultiSelection.activate();
@@ -156,6 +173,28 @@ Template.boardHeaderBar.helpers({
!Meteor.user().isCommentOnly()
);
},
boardView() {
return Utils.boardView();
},
});
Template.boardChangeViewPopup.events({
'click .js-open-lists-view'() {
Utils.setBoardView('board-view-lists');
Popup.close();
},
'click .js-open-swimlanes-view'() {
Utils.setBoardView('board-view-swimlanes');
Popup.close();
},
'click .js-open-cal-view'() {
Utils.setBoardView('board-view-cal');
Popup.close();
},
'click .js-open-rules-view'() {
Modal.openWide('rulesMain');
Popup.close();
},
});
const CreateBoard = BlazeComponent.extendComponent({
@@ -277,3 +316,75 @@ BlazeComponent.extendComponent({
];
},
}).register('boardChangeWatchPopup');
/*
BlazeComponent.extendComponent({
onCreated() {
//this.sortBy = new ReactiveVar();
////this.sortDirection = new ReactiveVar();
//this.setSortBy();
this.downClass = DOWNCLS;
this.upClass = UPCLS;
},
allowedSortValues() {
const types = [];
const pushed = {};
Meteor.user()
.getListSortTypes()
.forEach(type => {
const key = type.replace(/^-/, '');
if (pushed[key] === undefined) {
types.push({
name: key,
label: `list-label-${key}`,
shortLabel: `list-label-short-${key}`,
});
pushed[key] = 1;
}
});
return types;
},
Direction() {
return Meteor.user().getListSortByDirection() === -1
? this.downClass
: this.upClass;
},
sortby() {
return Meteor.user().getListSortBy();
},
setSortBy(type = null) {
const user = Meteor.user();
if (type === null) {
type = user._getListSortBy();
} else {
let value = '';
if (type.map) {
// is an array
value = (type[1] === -1 ? '-' : '') + type[0];
}
Meteor.call('setListSortBy', value);
}
//this.sortBy.set(type[0]);
//this.sortDirection.set(type[1]);
},
events() {
return [
{
'click .js-sort-by'(evt) {
evt.preventDefault();
const target = evt.target;
const sortby = target.getAttribute('name');
const down = !!target.querySelector(`.${this.upClass}`);
const direction = down ? -1 : 1;
this.setSortBy([sortby, direction]);
if (Utils.isMiniScreen) {
Popup.close();
}
},
},
];
},
}).register('listsortPopup');
*/
+14 -12
View File
@@ -38,18 +38,20 @@ template(name="attachmentsGalery")
| {{_ 'download'}}
if currentUser.isBoardMember
unless currentUser.isCommentOnly
if isImage
a(class="{{#if $eq ../coverId _id}}js-remove-cover{{else}}js-add-cover{{/if}}")
i.fa.fa-thumb-tack
if($eq ../coverId _id)
| {{_ 'remove-cover'}}
else
| {{_ 'add-cover'}}
a.js-confirm-delete
i.fa.fa-close
| {{_ 'delete'}}
unless currentUser.isWorker
if isImage
a(class="{{#if $eq ../coverId _id}}js-remove-cover{{else}}js-add-cover{{/if}}")
i.fa.fa-thumb-tack
if($eq ../coverId _id)
| {{_ 'remove-cover'}}
else
| {{_ 'add-cover'}}
a.js-confirm-delete
i.fa.fa-close
| {{_ 'delete'}}
if currentUser.isBoardMember
unless currentUser.isCommentOnly
li.attachment-item.add-attachment
a.js-add-attachment {{_ 'add-attachment' }}
unless currentUser.isWorker
li.attachment-item.add-attachment
a.js-add-attachment {{_ 'add-attachment' }}
+2
View File
@@ -131,6 +131,8 @@ Template.previewClipboardImagePopup.onRendered(() => {
direct(results);
},
});
} else {
direct(results);
}
}
};
+10 -9
View File
@@ -97,7 +97,8 @@ Template.dateBadge.helpers({
return (
Meteor.user() &&
Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly()
!Meteor.user().isCommentOnly() &&
!Meteor.user().isWorker()
);
},
});
@@ -105,7 +106,7 @@ Template.dateBadge.helpers({
// editCardReceivedDatePopup
(class extends DatePicker {
onCreated() {
super.onCreated();
super.onCreated(moment().format('YYYY-MM-DD HH:mm'));
this.data().getReceived() &&
this.date.set(moment(this.data().getReceived()));
}
@@ -122,7 +123,7 @@ Template.dateBadge.helpers({
// editCardStartDatePopup
(class extends DatePicker {
onCreated() {
super.onCreated();
super.onCreated(moment().format('YYYY-MM-DD HH:mm'));
this.data().getStart() && this.date.set(moment(this.data().getStart()));
}
@@ -148,7 +149,7 @@ Template.dateBadge.helpers({
// editCardDueDatePopup
(class extends DatePicker {
onCreated() {
super.onCreated();
super.onCreated('1970-01-01 17:00:00');
this.data().getDue() && this.date.set(moment(this.data().getDue()));
}
@@ -171,7 +172,7 @@ Template.dateBadge.helpers({
// editCardEndDatePopup
(class extends DatePicker {
onCreated() {
super.onCreated();
super.onCreated(moment().format('YYYY-MM-DD HH:mm'));
this.data().getEnd() && this.date.set(moment(this.data().getEnd()));
}
@@ -237,7 +238,7 @@ class CardReceivedDate extends CardDate {
const theDate = this.date.get();
// if dueAt, endAt and startAt exist & are > receivedAt, receivedAt doesn't need to be flagged
if (
(startAt && theDate.isAfter(dueAt)) ||
(startAt && theDate.isAfter(startAt)) ||
(endAt && theDate.isAfter(endAt)) ||
(dueAt && theDate.isAfter(dueAt))
)
@@ -344,9 +345,9 @@ class CardEndDate extends CardDate {
let classes = 'end-date' + ' ';
const dueAt = this.data().getDue();
const theDate = this.date.get();
if (theDate.diff(dueAt, 'days') >= 2) classes += 'long-overdue';
else if (theDate.diff(dueAt, 'days') >= 0) classes += 'due';
else if (theDate.diff(dueAt, 'days') >= -2) classes += 'almost-due';
if (!dueAt) classes += '';
else if (theDate.isBefore(dueAt)) classes += 'current';
else if (theDate.isAfter(dueAt)) classes += 'due';
return classes;
}
+318 -139
View File
@@ -4,15 +4,26 @@ template(name="cardDetails")
+inlinedForm(classNames="js-card-details-title")
+editCardTitleForm
else
a.fa.fa-times-thin.close-card-details.js-close-card-details
if currentUser.isBoardMember
a.fa.fa-navicon.card-details-menu.js-open-card-details-menu
unless isMiniScreen
a.fa.fa-times-thin.close-card-details.js-close-card-details
if currentUser.isBoardMember
a.fa.fa-navicon.card-details-menu.js-open-card-details-menu
input.inline-input(type="hidden" id="cardURL_copy" value="{{ absoluteUrl }}")
a.fa.card-copy-button.js-copy-link(
class="fa-link"
title="{{_ 'copy-card-link-to-clipboard'}}"
)
if isMiniScreen
a.fa.fa-times-thin.close-card-details-mobile-web.js-close-card-details
if currentUser.isBoardMember
a.fa.fa-navicon.card-details-menu-mobile-web.js-open-card-details-menu
a.fa.fa-link.card-copy-mobile-button
h2.card-details-title.js-card-title(
class="{{#if canModifyCard}}js-open-inlined-form is-editable{{/if}}")
+viewer
= getTitle
if isWatching
i.fa.fa-eye.card-details-watch
if isWatching
i.card-details-watch.fa.fa-eye
.card-details-path
each parentList
| &nbsp; &gt; &nbsp;
@@ -31,58 +42,103 @@ template(name="cardDetails")
p.warning {{_ 'card-archived'}}
.card-details-items
.card-details-item.card-details-item-received
h3.card-details-item-title {{_ 'card-received'}}
if getReceived
+cardReceivedDate
else
if canModifyCard
a.js-received-date {{_ 'add'}}
if currentBoard.allowsReceivedDate
.card-details-item.card-details-item-received
h3
i.fa.fa-sign-out
card-details-item-title {{_ 'card-received'}}
if getReceived
+cardReceivedDate
else
if canModifyCard
unless currentUser.isWorker
a.card-label.add-label.js-received-date
i.fa.fa-plus
.card-details-item.card-details-item-start
h3.card-details-item-title {{_ 'card-start'}}
if getStart
+cardStartDate
else
if canModifyCard
a.js-start-date {{_ 'add'}}
if currentBoard.allowsStartDate
.card-details-item.card-details-item-start
h3
i.fa.fa-hourglass-start
card-details-item-title {{_ 'card-start'}}
if getStart
+cardStartDate
else
if canModifyCard
unless currentUser.isWorker
a.card-label.add-label.js-start-date
i.fa.fa-plus
.card-details-item.card-details-item-due
h3.card-details-item-title {{_ 'card-due'}}
if getDue
+cardDueDate
else
if canModifyCard
a.js-due-date {{_ 'add'}}
if currentBoard.allowsDueDate
.card-details-item.card-details-item-due
h3
i.fa.fa-sign-in
card-details-item-title {{_ 'card-due'}}
if getDue
+cardDueDate
else
if canModifyCard
unless currentUser.isWorker
a.card-label.add-label.js-due-date
i.fa.fa-plus
.card-details-item.card-details-item-end
h3.card-details-item-title {{_ 'card-end'}}
if getEnd
+cardEndDate
else
if canModifyCard
a.js-end-date {{_ 'add'}}
if currentBoard.allowsEndDate
.card-details-item.card-details-item-end
h3
i.fa.fa-hourglass-end
card-details-item-title {{_ 'card-end'}}
if getEnd
+cardEndDate
else
if canModifyCard
unless currentUser.isWorker
a.card-label.add-label.js-end-date
i.fa.fa-plus
.card-details-items
.card-details-item.card-details-item-members
h3.card-details-item-title {{_ 'members'}}
each getMembers
+userAvatar(userId=this cardId=../_id)
| {{! XXX Hack to hide syntaxic coloration /// }}
if canModifyCard
a.member.add-member.card-details-item-add-button.js-add-members(title="{{_ 'card-members-title'}}")
i.fa.fa-plus
if currentBoard.allowsMembers
.card-details-item.card-details-item-members
h3
i.fa.fa-users
card-details-item-title {{_ 'members'}}
each getMembers
+userAvatar(userId=this cardId=../_id)
| {{! XXX Hack to hide syntaxic coloration /// }}
if canModifyCard
unless currentUser.isWorker
a.member.add-member.card-details-item-add-button.js-add-members(title="{{_ 'card-members-title'}}")
i.fa.fa-plus
.card-details-item.card-details-item-labels
h3.card-details-item-title {{_ 'labels'}}
a(class="{{#if canModifyCard}}js-add-labels{{else}}is-disabled{{/if}}" title="{{_ 'card-labels-title'}}")
each labels
span.card-label(class="card-label-{{color}}" title=name)
+viewer
= name
if canModifyCard
a.card-label.add-label.js-add-labels(title="{{_ 'card-labels-title'}}")
i.fa.fa-plus
//if assigneeSelected
if currentBoard.allowsAssignee
.card-details-item.card-details-item-assignees
h3
i.fa.fa-user
card-details-item-title {{_ 'assignee'}}
each getAssignees
+userAvatarAssignee(userId=this cardId=../_id)
| {{! XXX Hack to hide syntaxic coloration /// }}
if canModifyCard
a.assignee.add-assignee.card-details-item-add-button.js-add-assignees(title="{{_ 'assignee'}}")
i.fa.fa-plus
if currentUser.isWorker
unless assigneeSelected
a.assignee.add-assignee.card-details-item-add-button.js-add-assignees(title="{{_ 'assignee'}}")
i.fa.fa-plus
if currentBoard.allowsLabels
.card-details-item.card-details-item-labels
h3
i.fa.fa-tags
card-details-item-title {{_ 'labels'}}
a(class="{{#if canModifyCard}}js-add-labels{{else}}is-disabled{{/if}}" title="{{_ 'card-labels-title'}}")
each labels
span.card-label(class="card-label-{{color}}" title=name)
+viewer
= name
if canModifyCard
unless currentUser.isWorker
a.card-label.add-label.js-add-labels(title="{{_ 'card-labels-title'}}")
i.fa.fa-plus
.card-details-items
each customFieldsWD
@@ -103,82 +159,96 @@ template(name="cardDetails")
//- XXX We should use "editable" to avoid repetiting ourselves
if canModifyCard
h3.card-details-item-title {{_ 'description'}}
+inlinedCardDescription(classNames="card-description js-card-description")
+editor(autofocus=true)
| {{getUnsavedValue 'cardDescription' _id getDescription}}
.edit-controls.clearfix
button.primary(type="submit") {{_ 'save'}}
a.fa.fa-times-thin.js-close-inlined-form
else
a.js-open-inlined-form
if getDescription
+viewer
= getDescription
else
| {{_ 'edit'}}
if (hasUnsavedValue 'cardDescription' _id)
p.quiet
| {{_ 'unsaved-description'}}
a.js-open-inlined-form {{_ 'view-it'}}
= ' - '
a.js-close-inlined-form {{_ 'discard'}}
unless currentUser.isWorker
h3
i.fa.fa-align-left
card-details-item-title {{_ 'description'}}
+inlinedCardDescription(classNames="card-description js-card-description")
+editor(autofocus=true)
| {{getUnsavedValue 'cardDescription' _id getDescription}}
.edit-controls.clearfix
button.primary(type="submit") {{_ 'save'}}
a.fa.fa-times-thin.js-close-inlined-form
else
a.js-open-inlined-form
if getDescription
+viewer
= getDescription
else
| {{_ 'edit'}}
if (hasUnsavedValue 'cardDescription' _id)
p.quiet
| {{_ 'unsaved-description'}}
a.js-open-inlined-form {{_ 'view-it'}}
= ' - '
a.js-close-inlined-form {{_ 'discard'}}
else if getDescription
h3.card-details-item-title {{_ 'description'}}
+viewer
= getDescription
.card-details-items
.card-details-item.card-details-item-name
h3.card-details-item-title {{_ 'requested-by'}}
if canModifyCard
+inlinedForm(classNames="js-card-details-requester")
+editCardRequesterForm
else
a.js-open-inlined-form
if getRequestedBy
+viewer
= getRequestedBy
if currentBoard.allowsRequestedBy
.card-details-item.card-details-item-name
h3
i.fa.fa-shopping-cart
card-details-item-title {{_ 'requested-by'}}
if canModifyCard
unless currentUser.isWorker
+inlinedForm(classNames="js-card-details-requester")
+editCardRequesterForm
else
| {{_ 'add'}}
else if getRequestedBy
+viewer
= getRequestedBy
a.js-open-inlined-form
if getRequestedBy
+viewer
= getRequestedBy
else
| {{_ 'add'}}
else if getRequestedBy
+viewer
= getRequestedBy
.card-details-item.card-details-item-name
h3.card-details-item-title {{_ 'assigned-by'}}
if canModifyCard
+inlinedForm(classNames="js-card-details-assigner")
+editCardAssignerForm
else
a.js-open-inlined-form
if getAssignedBy
+viewer
= getAssignedBy
if currentBoard.allowsAssignedBy
.card-details-item.card-details-item-name
h3
i.fa.fa-user-plus
card-details-item-title {{_ 'assigned-by'}}
if canModifyCard
unless currentUser.isWorker
+inlinedForm(classNames="js-card-details-assigner")
+editCardAssignerForm
else
| {{_ 'add'}}
else if getRequestedBy
+viewer
= getAssignedBy
a.js-open-inlined-form
if getAssignedBy
+viewer
= getAssignedBy
else
| {{_ 'add'}}
else if getRequestedBy
+viewer
= getAssignedBy
hr
+checklists(cardId = _id)
if currentBoard.allowsSubtasks
hr
+subtasks(cardId = _id)
hr
h3
i.fa.fa-paperclip
| {{_ 'attachments'}}
+attachmentsGalery
.card-checklist-attachmentGalerys
.card-checklist-attachmentGalery.card-checklists
if currentBoard.allowsChecklists
+checklists(cardId = _id)
if currentBoard.allowsSubtasks
hr
+subtasks(cardId = _id)
if currentBoard.allowsAttachments
//- hr
//- h3
//- i.fa.fa-paperclip
//- | {{_ 'attachments'}}
.card-checklist-attachmentGalery.card-attachmentGalery
+attachmentsGalery
hr
unless currentUser.isNoComments
.activity-title
h3 {{ _ 'activity'}}
h3
i.fa.fa-history
| {{ _ 'activity'}}
if currentUser.isBoardMember
.material-toggle-switch
span.toggle-switch-title {{_ 'hide-system-messages'}}
@@ -187,9 +257,10 @@ template(name="cardDetails")
else
input.toggle-switch(type="checkbox" id="toggleButton")
label.toggle-label(for="toggleButton")
if currentUser.isBoardMember
unless currentUser.isNoComments
+commentForm
if currentBoard.allowsComments
if currentUser.isBoardMember
unless currentUser.isNoComments
+commentForm
unless currentUser.isNoComments
if isLoaded.get
if isLinkedCard
@@ -220,32 +291,79 @@ template(name="editCardAssignerForm")
template(name="cardDetailsActionsPopup")
ul.pop-over-list
li: a.js-toggle-watch-card {{#if isWatching}}{{_ 'unwatch'}}{{else}}{{_ 'watch'}}{{/if}}
li
a.js-toggle-watch-card
if isWatching
i.fa.fa-eye
| {{_ 'unwatch'}}
else
i.fa.fa-eye-slash
| {{_ 'watch'}}
if canModifyCard
hr
unless currentUser.isWorker
hr
ul.pop-over-list
//li: a.js-members {{_ 'card-edit-members'}}
//li: a.js-labels {{_ 'card-edit-labels'}}
//li: a.js-attachments {{_ 'card-edit-attachments'}}
li
a.js-custom-fields
i.fa.fa-list-alt
| {{_ 'card-edit-custom-fields'}}
//li: a.js-received-date {{_ 'editCardReceivedDatePopup-title'}}
//li: a.js-start-date {{_ 'editCardStartDatePopup-title'}}
//li: a.js-due-date {{_ 'editCardDueDatePopup-title'}}
//li: a.js-end-date {{_ 'editCardEndDatePopup-title'}}
li
a.js-spent-time
i.fa.fa-clock-o
| {{_ 'editCardSpentTimePopup-title'}}
li
a.js-set-card-color
i.fa.fa-paint-brush
| {{_ 'setCardColorPopup-title'}}
hr
ul.pop-over-list
//li: a.js-members {{_ 'card-edit-members'}}
//li: a.js-labels {{_ 'card-edit-labels'}}
//li: a.js-attachments {{_ 'card-edit-attachments'}}
li: a.js-custom-fields {{_ 'card-edit-custom-fields'}}
//li: a.js-received-date {{_ 'editCardReceivedDatePopup-title'}}
//li: a.js-start-date {{_ 'editCardStartDatePopup-title'}}
//li: a.js-due-date {{_ 'editCardDueDatePopup-title'}}
//li: a.js-end-date {{_ 'editCardEndDatePopup-title'}}
li: a.js-spent-time {{_ 'editCardSpentTimePopup-title'}}
li: a.js-set-card-color {{_ 'setCardColorPopup-title'}}
hr
ul.pop-over-list
li: a.js-move-card-to-top {{_ 'moveCardToTop-title'}}
li: a.js-move-card-to-bottom {{_ 'moveCardToBottom-title'}}
hr
ul.pop-over-list
li: a.js-move-card {{_ 'moveCardPopup-title'}}
li: a.js-copy-card {{_ 'copyCardPopup-title'}}
li: a.js-copy-checklist-cards {{_ 'copyChecklistToManyCardsPopup-title'}}
li
a.js-move-card-to-top
i.fa.fa-arrow-up
| {{_ 'moveCardToTop-title'}}
li
a.js-move-card-to-bottom
i.fa.fa-arrow-down
| {{_ 'moveCardToBottom-title'}}
unless currentUser.isWorker
hr
ul.pop-over-list
li
a.js-move-card
i.fa.fa-arrow-right
| {{_ 'moveCardPopup-title'}}
li
a.js-copy-card
i.fa.fa-copy
| {{_ 'copyCardPopup-title'}}
hr
ul.pop-over-list
li
a.js-copy-checklist-cards
i.fa.fa-list
i.fa.fa-copy
| {{_ 'copyChecklistToManyCardsPopup-title'}}
unless archived
li: a.js-archive {{_ 'archive-card'}}
li: a.js-more {{_ 'cardMorePopup-title'}}
hr
ul.pop-over-list
li
a.js-archive
i.fa.fa-arrow-right
i.fa.fa-archive
| {{_ 'archive-card'}}
hr
ul.pop-over-list
li
a.js-more
i.fa.fa-link
| {{_ 'cardMorePopup-title'}}
template(name="moveCardPopup")
+boardsAndLists
@@ -296,6 +414,67 @@ template(name="cardMembersPopup")
if isCardMember
i.fa.fa-check
template(name="cardAssigneesPopup")
unless currentUser.isWorker
ul.pop-over-list.js-card-assignee-list
each board.activeMembers
li.item(class="{{#if isCardAssignee}}active{{/if}}")
a.name.js-select-assignee(href="#")
+userAvatar(userId=user._id)
span.full-name
= user.profile.fullname
| (<span class="username">{{ user.username }}</span>)
if isCardAssignee
i.fa.fa-check
if currentUser.isWorker
ul.pop-over-list.js-card-assignee-list
li.item(class="{{#if currentUser.isCardAssignee}}active{{/if}}")
a.name.js-select-assignee(href="#")
+userAvatar(userId=currentUser._id)
span.full-name
= currentUser.profile.fullname
| (<span class="username">{{ currentUser.username }}</span>)
if currentUser.isCardAssignee
i.fa.fa-check
template(name="userAvatarAssignee")
a.assignee.js-assignee(title="{{userData.profile.fullname}} ({{userData.username}})")
if userData.profile.avatarUrl
img.avatar.avatar-image(src="{{userData.profile.avatarUrl}}")
else
+userAvatarAssigneeInitials(userId=userData._id)
if showStatus
span.assignee-presence-status(class=presenceStatusClassName)
span.member-type(class=memberType)
unless isSandstorm
if showEdit
if $eq currentUser._id userData._id
a.edit-avatar.js-change-avatar
i.fa.fa-pencil
template(name="cardAssigneePopup")
.board-assignee-menu
.mini-profile-info
+userAvatar(userId=user._id showEdit=true)
.info
h3= user.profile.fullname
p.quiet @{{ user.username }}
ul.pop-over-list
if currentUser.isNotCommentOnly
unless currentUser.isWorker
li: a.js-remove-assignee {{_ 'remove-member-from-card'}}
unless currentUser.isWorker
if $eq currentUser._id user._id
with currentUser
li: a.js-edit-profile {{_ 'edit-profile'}}
template(name="userAvatarAssigneeInitials")
svg.avatar.avatar-assignee-initials(viewBox="0 0 {{viewPortWidth}} 15")
text(x="50%" y="13" text-anchor="middle")= initials
template(name="cardMorePopup")
p.quiet
span.clearfix
+264 -12
View File
@@ -51,7 +51,8 @@ BlazeComponent.extendComponent({
return (
Meteor.user() &&
Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly()
!Meteor.user().isCommentOnly() &&
!Meteor.user().isWorker()
);
},
@@ -121,11 +122,6 @@ BlazeComponent.extendComponent({
// Send Webhook but not create Activities records ---
const card = this.currentData();
const userId = Meteor.userId();
//console.log(`userId: ${userId}`);
//console.log(`cardId: ${card._id}`);
//console.log(`boardId: ${card.boardId}`);
//console.log(`listId: ${card.listId}`);
//console.log(`swimlaneId: ${card.swimlaneId}`);
const params = {
userId,
cardId: card._id,
@@ -134,16 +130,25 @@ BlazeComponent.extendComponent({
user: Meteor.user().username,
url: '',
};
//console.log('looking for integrations...');
const integrations = Integrations.find({
boardId: card.boardId,
type: 'outgoing-webhooks',
boardId: { $in: [card.boardId, Integrations.Const.GLOBAL_WEBHOOK_ID] },
enabled: true,
activities: { $in: ['CardDetailsRendered', 'all'] },
}).fetch();
//console.log(`Investigation length: ${integrations.length}`);
if (integrations.length > 0) {
Meteor.call('outgoingWebhooks', integrations, 'CardSelected', params);
integrations.forEach(integration => {
Meteor.call(
'outgoingWebhooks',
integration,
'CardSelected',
params,
() => {
return;
},
);
});
}
//-------------
}
@@ -248,6 +253,12 @@ BlazeComponent.extendComponent({
if ($subtasksDom.data('sortable')) {
$subtasksDom.sortable('option', 'disabled', !userIsMember());
}
if ($checklistsDom.data('sortable')) {
$checklistsDom.sortable('option', 'disabled', Utils.isMiniScreen());
}
if ($subtasksDom.data('sortable')) {
$subtasksDom.sortable('option', 'disabled', Utils.isMiniScreen());
}
});
},
@@ -274,6 +285,29 @@ BlazeComponent.extendComponent({
'click .js-close-card-details'() {
Utils.goBoardId(this.data().boardId);
},
'click .js-copy-link'() {
StringToCopyElement = document.getElementById('cardURL_copy');
StringToCopyElement.select();
if (document.execCommand('copy')) {
StringToCopyElement.blur();
} else {
document.getElementById('cardURL_copy').selectionStart = 0;
document.getElementById('cardURL_copy').selectionEnd = 999;
document.execCommand('copy');
if (window.getSelection) {
if (window.getSelection().empty) {
// Chrome
window.getSelection().empty();
} else if (window.getSelection().removeAllRanges) {
// Firefox
window.getSelection().removeAllRanges();
}
} else if (document.selection) {
// IE?
document.selection.empty();
}
}
},
'click .js-open-card-details-menu': Popup.open('cardDetailsActions'),
'submit .js-card-description'(event) {
event.preventDefault();
@@ -287,6 +321,8 @@ BlazeComponent.extendComponent({
.trim();
if (title) {
this.data().setTitle(title);
} else {
this.data().setTitle('');
}
},
'submit .js-card-details-assigner'(event) {
@@ -296,6 +332,8 @@ BlazeComponent.extendComponent({
.trim();
if (assigner) {
this.data().setAssignedBy(assigner);
} else {
this.data().setAssignedBy('');
}
},
'submit .js-card-details-requester'(event) {
@@ -305,10 +343,14 @@ BlazeComponent.extendComponent({
.trim();
if (requester) {
this.data().setRequestedBy(requester);
} else {
this.data().setRequestedBy('');
}
},
'click .js-member': Popup.open('cardMember'),
'click .js-add-members': Popup.open('cardMembers'),
'click .js-assignee': Popup.open('cardAssignee'),
'click .js-add-assignees': Popup.open('cardAssignees'),
'click .js-add-labels': Popup.open('cardLabels'),
'click .js-received-date': Popup.open('editCardReceivedDate'),
'click .js-start-date': Popup.open('editCardStartDate'),
@@ -321,6 +363,19 @@ BlazeComponent.extendComponent({
parentComponent.showOverlay.set(true);
parentComponent.mouseHasEnterCardDetails = true;
},
'mousedown .js-card-details'() {
Session.set('cardDetailsIsDragging', false);
Session.set('cardDetailsIsMouseDown', true);
},
'mousemove .js-card-details'() {
if (Session.get('cardDetailsIsMouseDown')) {
Session.set('cardDetailsIsDragging', true);
}
},
'mouseup .js-card-details'() {
Session.set('cardDetailsIsDragging', false);
Session.set('cardDetailsIsMouseDown', false);
},
'click #toggleButton'() {
Meteor.call('toggleSystemMessages');
},
@@ -329,6 +384,122 @@ BlazeComponent.extendComponent({
},
}).register('cardDetails');
Template.cardDetails.helpers({
userData() {
// We need to handle a special case for the search results provided by the
// `matteodem:easy-search` package. Since these results gets published in a
// separate collection, and not in the standard Meteor.Users collection as
// expected, we use a component parameter ("property") to distinguish the
// two cases.
const userCollection = this.esSearch ? ESSearchResults : Users;
return userCollection.findOne(this.userId, {
fields: {
profile: 1,
username: 1,
},
});
},
receivedSelected() {
if (this.getReceived().length === 0) {
return false;
} else {
return true;
}
},
startSelected() {
if (this.getStart().length === 0) {
return false;
} else {
return true;
}
},
endSelected() {
if (this.getEnd().length === 0) {
return false;
} else {
return true;
}
},
dueSelected() {
if (this.getDue().length === 0) {
return false;
} else {
return true;
}
},
memberSelected() {
if (this.getMembers().length === 0) {
return false;
} else {
return true;
}
},
labelSelected() {
if (this.getLabels().length === 0) {
return false;
} else {
return true;
}
},
assigneeSelected() {
if (this.getAssignees().length === 0) {
return false;
} else {
return true;
}
},
requestBySelected() {
if (this.getRequestBy().length === 0) {
return false;
} else {
return true;
}
},
assigneeBySelected() {
if (this.getAssigneeBy().length === 0) {
return false;
} else {
return true;
}
},
memberType() {
const user = Users.findOne(this.userId);
return user && user.isBoardAdmin() ? 'admin' : 'normal';
},
presenceStatusClassName() {
const user = Users.findOne(this.userId);
const userPresence = presences.findOne({ userId: this.userId });
if (user && user.isInvitedTo(Session.get('currentBoard'))) return 'pending';
else if (!userPresence) return 'disconnected';
else if (Session.equals('currentBoard', userPresence.state.currentBoardId))
return 'active';
else return 'idle';
},
});
Template.userAvatarAssigneeInitials.helpers({
initials() {
const user = Users.findOne(this.userId);
return user && user.getInitials();
},
viewPortWidth() {
const user = Users.findOne(this.userId);
return ((user && user.getInitials().length) || 1) * 12;
},
});
// We extends the normal InlinedForm component to support UnsavedEdits draft
// feature.
(class extends InlinedForm {
@@ -386,6 +557,7 @@ Template.cardDetailsActionsPopup.helpers({
Template.cardDetailsActionsPopup.events({
'click .js-members': Popup.open('cardMembers'),
'click .js-assignees': Popup.open('cardAssignees'),
'click .js-labels': Popup.open('cardLabels'),
'click .js-attachments': Popup.open('cardAttachments'),
'click .js-custom-fields': Popup.open('cardCustomFields'),
@@ -777,7 +949,14 @@ BlazeComponent.extendComponent({
EscapeActions.register(
'detailsPane',
() => {
Utils.goBoardId(Session.get('currentBoard'));
if (Session.get('cardDetailsIsDragging')) {
// Reset dragging status as the mouse landed outside the cardDetails template area and this will prevent a mousedown event from firing
Session.set('cardDetailsIsDragging', false);
Session.set('cardDetailsIsMouseDown', false);
} else {
// Prevent close card when the user is selecting text and moves the mouse cursor outside the card detail area
Utils.goBoardId(Session.get('currentBoard'));
}
},
() => {
return !Session.equals('currentCard', null);
@@ -786,3 +965,76 @@ EscapeActions.register(
noClickEscapeOn: '.js-card-details,.board-sidebar,#header',
},
);
Template.cardAssigneesPopup.events({
'click .js-select-assignee'(event) {
const card = Cards.findOne(Session.get('currentCard'));
const assigneeId = this.userId;
card.toggleAssignee(assigneeId);
event.preventDefault();
},
});
Template.cardAssigneesPopup.helpers({
isCardAssignee() {
const card = Template.parentData();
const cardAssignees = card.getAssignees();
return _.contains(cardAssignees, this.userId);
},
user() {
return Users.findOne(this.userId);
},
});
Template.cardAssigneePopup.helpers({
userData() {
// We need to handle a special case for the search results provided by the
// `matteodem:easy-search` package. Since these results gets published in a
// separate collection, and not in the standard Meteor.Users collection as
// expected, we use a component parameter ("property") to distinguish the
// two cases.
const userCollection = this.esSearch ? ESSearchResults : Users;
return userCollection.findOne(this.userId, {
fields: {
profile: 1,
username: 1,
},
});
},
memberType() {
const user = Users.findOne(this.userId);
return user && user.isBoardAdmin() ? 'admin' : 'normal';
},
presenceStatusClassName() {
const user = Users.findOne(this.userId);
const userPresence = presences.findOne({ userId: this.userId });
if (user && user.isInvitedTo(Session.get('currentBoard'))) return 'pending';
else if (!userPresence) return 'disconnected';
else if (Session.equals('currentBoard', userPresence.state.currentBoardId))
return 'active';
else return 'idle';
},
isCardAssignee() {
const card = Template.parentData();
const cardAssignees = card.getAssignees();
return _.contains(cardAssignees, this.userId);
},
user() {
return Users.findOne(this.userId);
},
});
Template.cardAssigneePopup.events({
'click .js-remove-assignee'() {
Cards.findOne(this.cardId).unassignAssignee(this.userId);
Popup.close();
},
'click .js-edit-profile': Popup.open('editProfile'),
});
+104 -2
View File
@@ -1,5 +1,80 @@
@import 'nib'
// Assignee, code copied from wekan/client/users/userAvatar.styl
avatar-radius = 50%
.assignee
border-radius: 3px
display: block
position: relative
float: left
height: 30px
width: @height
margin: 0 4px 4px 0
cursor: pointer
user-select: none
z-index: 1
text-decoration: none
border-radius: avatar-radius
.avatar
overflow: hidden
border-radius: avatar-radius
&.avatar-assignee-initials
height: 70%
width: @height
padding: 15%
background-color: #dbdbdb
color: #444444
position: absolute
&.avatar-image
height: 100%
width: @height
.assignee-presence-status
background-color: #b3b3b3
border: 1px solid #fff
border-radius: 50%
height: 7px
width: @height
position: absolute
right: -1px
bottom: -1px
border: 1px solid white
z-index: 15
&.active
background: #64c464
border-color: #daf1da
&.idle
background: #e4e467
border-color: #f7f7d4
&.disconnected
background: #bdbdbd
border-color: #ededed
&.pending
background: #e44242
border-color: #f1dada
&.add-assignee
display: flex
align-items: center
justify-content: center
box-shadow: 0 0 0 2px darken(white, 25%) inset
&:hover, &.is-active
box-shadow: 0 0 0 2px darken(white, 60%) inset
// Other card details
.card-details
padding: 0
flex-shrink: 0
@@ -32,7 +107,11 @@
border-bottom: 1px solid darken(white, 14%)
.close-card-details,
.card-details-menu
.card-details-menu,
.card-copy-button,
.card-copy-mobile-button,
.close-card-details-mobile-web,
.card-details-menu-mobile-web
float: right
.close-card-details
@@ -40,10 +119,30 @@
padding: 5px
margin-right: -8px
.close-card-details-mobile-web
font-size: 24px
padding: 5px
margin-right: 40px
.card-copy-button
font-size: 17px
padding: 10px
margin-right: 10px
.card-copy-mobile-button
font-size: 17px
padding: 10px
margin-right: 10px
.card-details-menu
font-size: 17px
padding: 10px
.card-details-menu-mobile-web
font-size: 17px
padding: 10px
margin-right: 30px
.card-details-watch
font-size: 17px
padding-left: 7px
@@ -93,13 +192,16 @@
margin-right: 0
&.card-details-item-labels,
&.card-details-item-members,
&.card-details-item-assignees,
&.card-details-item-received,
&.card-details-item-start,
&.card-details-item-due,
&.card-details-item-end,
&.card-details-item-customfield,
&.card-details-item-name
max-width: 50%
display: block
word-wrap: break-word
max-width: 48%
flex-grow: 1
.card-details-item-title
+3 -1
View File
@@ -1,5 +1,7 @@
template(name="checklists")
h3 {{_ 'checklists'}}
h3
i.fa.fa-check
| {{_ 'checklists'}}
if toggleDeleteDialog.get
.board-overlay#card-details-overlay
+checklistDeleteDialog(checklist = checklistToDelete)
+9 -3
View File
@@ -60,6 +60,9 @@ BlazeComponent.extendComponent({
if ($itemsDom.data('sortable')) {
$(self.itemsDom).sortable('option', 'disabled', !userIsMember());
}
if ($itemsDom.data('sortable')) {
$(self.itemsDom).sortable('option', 'disabled', Utils.isMiniScreen());
}
});
},
@@ -67,7 +70,8 @@ BlazeComponent.extendComponent({
return (
Meteor.user() &&
Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly()
!Meteor.user().isCommentOnly() &&
!Meteor.user().isWorker()
);
},
}).register('checklistDetail');
@@ -120,7 +124,8 @@ BlazeComponent.extendComponent({
return (
Meteor.user() &&
Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly()
!Meteor.user().isCommentOnly() &&
!Meteor.user().isWorker()
);
},
@@ -228,7 +233,8 @@ Template.checklistItemDetail.helpers({
return (
Meteor.user() &&
Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly()
!Meteor.user().isCommentOnly() &&
!Meteor.user().isWorker()
);
},
});
+3
View File
@@ -128,6 +128,9 @@ textarea.js-add-checklist-item, textarea.js-edit-checklist-item
& .viewer
p
margin-bottom: 2px
display: block
word-wrap: break-word
max-width: 420px
.js-delete-checklist-item
margin: 0 0 0.5em 1.33em
+2 -1
View File
@@ -10,9 +10,10 @@
margin-right: 4px
margin-bottom: 5px
padding: 3px 8px
max-width: 100%
max-width: 210px
min-width: 8px
overflow: ellipsis
word-wrap: break-word
height: 18px
vertical-align: bottom
+22 -8
View File
@@ -3,6 +3,13 @@ template(name="minicard")
class="{{#if isLinkedCard}}linked-card{{/if}}"
class="{{#if isLinkedBoard}}linked-board{{/if}}"
class="minicard-{{colorClass}}")
if isMiniScreen
//.handle
// .fa.fa-arrows
unless isMiniScreen
if showDesktopDragHandles
.handle
.fa.fa-arrows
if cover
.minicard-cover(style="background-image: url('{{cover.url}}');")
if labels
@@ -15,8 +22,6 @@ template(name="minicard")
if hiddenMinicardLabelText
.minicard-label(class="card-label-{{color}}" title="{{name}}")
.minicard-title
.handle
.fa.fa-arrows
if $eq 'prefix-with-full-path' currentBoard.presentParentTask
.parent-prefix
| {{ parentString ' > ' }}
@@ -53,6 +58,8 @@ template(name="minicard")
if getDue
.date
+minicardDueDate
if getEnd
+minicardEndDate
if getSpentTime
.date
+cardSpentTime
@@ -60,14 +67,21 @@ template(name="minicard")
.minicard-custom-fields
each customFieldsWD
if definition.showOnCard
.minicard-custom-field
if definition.showLabelOnMiniCard
if trueValue
.minicard-custom-field
if definition.showLabelOnMiniCard
.minicard-custom-field-item
+viewer
= definition.name
.minicard-custom-field-item
+viewer
= definition.name
.minicard-custom-field-item
+viewer
= trueValue
= trueValue
if getAssignees
.minicard-assignees.js-minicard-assignees
each getAssignees
+userAvatar(userId=this)
hr
if getMembers
.minicard-members.js-minicard-members
+25 -2
View File
@@ -1,3 +1,5 @@
import { Cookies } from 'meteor/ostrio:cookies';
const cookies = new Cookies();
// Template.cards.events({
// 'click .member': Popup.open('cardMember')
// });
@@ -18,7 +20,11 @@ BlazeComponent.extendComponent({
},
{
'click .js-toggle-minicard-label-text'() {
Meteor.call('toggleMinicardLabelText');
if (cookies.has('hiddenMinicardLabelText')) {
cookies.remove('hiddenMinicardLabelText'); //true
} else {
cookies.set('hiddenMinicardLabelText', 'true'); //true
}
},
},
];
@@ -26,7 +32,24 @@ BlazeComponent.extendComponent({
}).register('minicard');
Template.minicard.helpers({
showDesktopDragHandles() {
currentUser = Meteor.user();
if (currentUser) {
return (currentUser.profile || {}).showDesktopDragHandles;
} else if (cookies.has('showDesktopDragHandles')) {
return true;
} else {
return false;
}
},
hiddenMinicardLabelText() {
return Meteor.user().hasHiddenMinicardLabelText();
currentUser = Meteor.user();
if (currentUser) {
return (currentUser.profile || {}).hiddenMinicardLabelText;
} else if (cookies.has('hiddenMinicardLabelText')) {
return true;
} else {
return false;
}
},
});
+21 -7
View File
@@ -81,6 +81,7 @@
.minicard-labels
float: right
display: flex
flex-wrap: wrap
.minicard-label
width: 11px
@@ -92,8 +93,11 @@
.minicard-custom-field
display:flex;
.minicard-custom-field-item
max-width:50%;
flex-grow:1;
flex-grow: 1
display: block
word-wrap: break-word
max-width: 100px
margin-right: 4px
.handle
width: 20px;
height: 20px;
@@ -101,7 +105,7 @@
right: 5px;
top: 5px;
display:none;
@media only screen and (max-width: 1199px) {
@media only screen {
display:block;
}
.fa-arrows
@@ -111,7 +115,9 @@
p:last-child
margin-bottom: 0
.viewer
display: inline-block
display: block
word-wrap: break-word
max-width: 230px
.dates
display: flex;
flex-direction: row;
@@ -154,9 +160,10 @@
padding-left: 0px
line-height: 12px
.minicard-members
.minicard-members,
.minicard-assignees
float: right
margin: 2px -8px -2px 0
margin: 2px -8px 12px 0
.member
float: right
@@ -164,10 +171,17 @@
height: 28px
width: @height
.assignee
float: right
border-radius: 50%
height: 28px
width: @height
+ .badges
margin-top: 10px
.minicard-members:empty
.minicard-members:empty,
.minicard-assignees:empty
display: none
&.minicard-composer
+3 -1
View File
@@ -1,5 +1,7 @@
template(name="subtasks")
h3 {{_ 'subtasks'}}
h3
i.fa.fa-sitemap
| {{_ 'subtasks'}}
if toggleDeleteDialog.get
.board-overlay#card-details-overlay
+subtaskDeleteDialog(subtask = subtaskToDelete)
+6 -3
View File
@@ -3,7 +3,8 @@ BlazeComponent.extendComponent({
return (
Meteor.user() &&
Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly()
!Meteor.user().isCommentOnly() &&
!Meteor.user().isWorker()
);
},
}).register('subtaskDetail');
@@ -55,7 +56,8 @@ BlazeComponent.extendComponent({
return (
Meteor.user() &&
Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly()
!Meteor.user().isCommentOnly() &&
!Meteor.user().isWorker()
);
},
@@ -154,7 +156,8 @@ Template.subtaskItemDetail.helpers({
return (
Meteor.user() &&
Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly()
!Meteor.user().isCommentOnly() &&
!Meteor.user().isWorker()
);
},
});
+10 -12
View File
@@ -211,22 +211,20 @@ BlazeComponent.extendComponent({
this.parentComponent().nextStep();
},
onMapMember(evt) {
const memberToMap = this.currentData();
if (memberToMap.wekan) {
// todo xxx ask for confirmation?
this.unmapMember(memberToMap.id);
} else {
this.setSelectedMember(memberToMap.id);
Popup.open('importMapMembersAdd')(evt);
}
},
events() {
return [
{
submit: this.onSubmit,
'click .js-select-member': this.onMapMember,
'click .js-select-member'(evt) {
const memberToMap = this.currentData();
if (memberToMap.wekan) {
// todo xxx ask for confirmation?
this.unmapMember(memberToMap.id);
} else {
this.setSelectedMember(memberToMap.id);
Popup.open('importMapMembersAdd')(evt);
}
},
},
];
},
+61 -13
View File
@@ -1,3 +1,5 @@
import { Cookies } from 'meteor/ostrio:cookies';
const cookies = new Cookies();
const { calculateIndex, enableClickOnTouch } = Utils;
BlazeComponent.extendComponent({
@@ -31,12 +33,6 @@ BlazeComponent.extendComponent({
const itemsSelector = '.js-minicard:not(.placeholder, .js-card-composer)';
const $cards = this.$('.js-minicards');
if (window.matchMedia('(max-width: 1199px)').matches) {
$('.js-minicards').sortable({
handle: '.handle',
});
}
$cards.sortable({
connectWith: '.js-minicards:not(.js-list-full)',
tolerance: 'pointer',
@@ -79,16 +75,15 @@ BlazeComponent.extendComponent({
const listId = Blaze.getData(ui.item.parents('.list').get(0))._id;
const currentBoard = Boards.findOne(Session.get('currentBoard'));
let swimlaneId = '';
const boardView = (Meteor.user().profile || {}).boardView;
if (
boardView === 'board-view-swimlanes' ||
Utils.boardView() === 'board-view-swimlanes' ||
currentBoard.isTemplatesBoard()
)
swimlaneId = Blaze.getData(ui.item.parents('.swimlane').get(0))._id;
else if (
boardView === 'board-view-lists' ||
boardView === 'board-view-cal' ||
!boardView
Utils.boardView() === 'board-view-lists' ||
Utils.boardView() === 'board-view-cal' ||
!Utils.boardView
)
swimlaneId = currentBoard.getDefaultSwimline()._id;
@@ -122,9 +117,49 @@ BlazeComponent.extendComponent({
// ugly touch event hotfix
enableClickOnTouch(itemsSelector);
// Disable drag-dropping if the current user is not a board member or is comment only
this.autorun(() => {
$cards.sortable('option', 'disabled', !userIsMember());
let showDesktopDragHandles = false;
currentUser = Meteor.user();
if (currentUser) {
showDesktopDragHandles = (currentUser.profile || {})
.showDesktopDragHandles;
} else if (cookies.has('showDesktopDragHandles')) {
showDesktopDragHandles = true;
} else {
showDesktopDragHandles = false;
}
if (!Utils.isMiniScreen() && showDesktopDragHandles) {
$cards.sortable({
handle: '.handle',
});
} else if (!Utils.isMiniScreen() && !showDesktopDragHandles) {
$cards.sortable({
handle: '.minicard',
});
}
if ($cards.data('sortable')) {
$cards.sortable(
'option',
'disabled',
// Disable drag-dropping when user is not member/is miniscreen
!userIsMember(),
// Not disable drag-dropping while in multi-selection mode
// MultiSelection.isActive() || !userIsMember(),
);
}
if ($cards.data('sortable')) {
$cards.sortable(
'option',
'disabled',
// Disable drag-dropping when user is not member/is miniscreen
Utils.isMiniScreen(),
// Not disable drag-dropping while in multi-selection mode
// MultiSelection.isActive() || !userIsMember(),
);
}
});
// We want to re-run this function any time a card is added.
@@ -155,6 +190,19 @@ BlazeComponent.extendComponent({
},
}).register('list');
Template.list.helpers({
showDesktopDragHandles() {
currentUser = Meteor.user();
if (currentUser) {
return (currentUser.profile || {}).showDesktopDragHandles;
} else if (cookies.has('showDesktopDragHandles')) {
return true;
} else {
return false;
}
},
});
Template.miniList.events({
'click .js-select-list'() {
const listId = this._id;
+21 -5
View File
@@ -84,17 +84,16 @@
padding-left: 10px
color: #a6a6a6
.list-header-menu
position: absolute
padding: 27px 19px
margin-top: 1px
top: -7px
right: -7px
right: 3px
.list-header-plus-icon
color: #a6a6a6
margin-right: 10px
margin-right: 15px
.highlight
color: #ce1414
@@ -165,7 +164,16 @@
@media screen and (max-width: 800px)
.list-header-menu
margin-right: 30px
position: absolute
padding: 27px 19px
margin-top: 1px
top: -7px
margin-right: 7px
right: -3px
.list-header
.list-header-name
margin-left: 1.4rem
.mini-list
flex: 0 0 60px
@@ -221,9 +229,17 @@
padding: 7px
top: 50%
transform: translateY(-50%)
right: 17px
right: 47px
font-size: 20px
.list-header-handle
position: absolute
padding: 7px
top: 50%
transform: translateY(-50%)
right: 10px
font-size: 24px
.link-board-wrapper
display: flex
align-items: baseline
+27 -14
View File
@@ -48,7 +48,6 @@ BlazeComponent.extendComponent({
const board = this.data().board();
let linkedId = '';
let swimlaneId = '';
const boardView = (Meteor.user().profile || {}).boardView;
let cardType = 'cardType-card';
if (title) {
if (board.isTemplatesBoard()) {
@@ -71,14 +70,14 @@ BlazeComponent.extendComponent({
});
cardType = 'cardType-linkedBoard';
}
} else if (boardView === 'board-view-swimlanes')
} else if (Utils.boardView() === 'board-view-swimlanes')
swimlaneId = this.parentComponent()
.parentComponent()
.data()._id;
else if (
boardView === 'board-view-lists' ||
boardView === 'board-view-cal' ||
!boardView
Utils.boardView() === 'board-view-lists' ||
Utils.boardView() === 'board-view-cal' ||
!Utils.boardView
)
swimlaneId = board.getDefaultSwimline()._id;
@@ -157,9 +156,8 @@ BlazeComponent.extendComponent({
},
idOrNull(swimlaneId) {
const currentUser = Meteor.user();
if (
(currentUser.profile || {}).boardView === 'board-view-swimlanes' ||
Utils.boardView() === 'board-view-swimlanes' ||
this.data()
.board()
.isTemplatesBoard()
@@ -191,7 +189,8 @@ BlazeComponent.extendComponent({
!this.reachedWipLimit() &&
Meteor.user() &&
Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly()
!Meteor.user().isCommentOnly() &&
!Meteor.user().isWorker()
);
},
@@ -397,10 +396,9 @@ BlazeComponent.extendComponent({
'.js-swimlane',
);
this.swimlaneId = '';
const boardView = (Meteor.user().profile || {}).boardView;
if (boardView === 'board-view-swimlanes')
if (Utils.boardView() === 'board-view-swimlanes')
this.swimlaneId = Blaze.getData(swimlane[0])._id;
else if (boardView === 'board-view-lists' || !boardView)
else if (Utils.boardView() === 'board-view-lists' || !Utils.boardView)
this.swimlaneId = Swimlanes.findOne({ boardId: this.boardId })._id;
},
@@ -580,7 +578,7 @@ BlazeComponent.extendComponent({
const swimlane = $(Popup._getTopStack().openerElement).parents(
'.js-swimlane',
);
if ((Meteor.user().profile || {}).boardView === 'board-view-swimlanes')
if (Utils.boardView() === 'board-view-swimlanes')
this.swimlaneId = Blaze.getData(swimlane[0])._id;
else this.swimlaneId = Swimlanes.findOne({ boardId: this.boardId })._id;
// List where to insert card
@@ -701,12 +699,27 @@ BlazeComponent.extendComponent({
this.listId = this.parentComponent().data()._id;
this.swimlaneId = '';
const boardView = (Meteor.user().profile || {}).boardView;
if (boardView === 'board-view-swimlanes')
const isSandstorm =
Meteor.settings &&
Meteor.settings.public &&
Meteor.settings.public.sandstorm;
if (isSandstorm) {
const user = Meteor.user();
if (user) {
if (Utils.boardView() === 'board-view-swimlanes') {
this.swimlaneId = this.parentComponent()
.parentComponent()
.parentComponent()
.data()._id;
}
}
} else if (Utils.boardView() === 'board-view-swimlanes') {
this.swimlaneId = this.parentComponent()
.parentComponent()
.parentComponent()
.data()._id;
}
},
onRendered() {
+43 -13
View File
@@ -9,7 +9,8 @@ template(name="listHeader")
if currentList
a.list-header-left-icon.fa.fa-angle-left.js-unselect-list
h2.list-header-name(
class="{{#if currentUser.isBoardMember}}{{#unless currentUser.isCommentOnly}}js-open-inlined-form is-editable{{/unless}}{{/if}}")
title="{{ moment modifiedAt 'LLL' }}"
class="{{#if currentUser.isBoardMember}}{{#unless currentUser.isCommentOnly}}{{#unless currentUser.isWorker}}js-open-inlined-form is-editable{{/unless}}{{/unless}}{{/if}}")
+viewer
= title
if wipLimit.enabled
@@ -29,16 +30,22 @@ template(name="listHeader")
if canSeeAddCard
a.js-add-card.fa.fa-plus.list-header-plus-icon
a.fa.fa-navicon.js-open-list-menu
//a.list-header-handle.handle.fa.fa-arrows.js-list-handle
else
a.list-header-menu-icon.fa.fa-angle-right.js-select-list
//a.list-header-handle.handle.fa.fa-arrows.js-list-handle
else if currentUser.isBoardMember
if isWatching
i.list-header-watch-icon.fa.fa-eye
div.list-header-menu
unless currentUser.isCommentOnly
//if isBoardAdmin
// a.fa.js-list-star.list-header-plus-icon(class="fa-star{{#unless starred}}-o{{/unless}}")
if canSeeAddCard
a.js-add-card.fa.fa-plus.list-header-plus-icon
a.fa.fa-navicon.js-open-list-menu
if showDesktopDragHandles
a.list-header-handle.handle.fa.fa-arrows.js-list-handle
template(name="editListTitleForm")
.list-composer
@@ -49,25 +56,47 @@ template(name="editListTitleForm")
template(name="listActionPopup")
ul.pop-over-list
li: a.js-toggle-watch-list {{#if isWatching}}{{_ 'unwatch'}}{{else}}{{_ 'watch'}}{{/if}}
li
a.js-toggle-watch-list
if isWatching
i.fa.fa-eye
| {{_ 'unwatch'}}
else
i.fa.fa-eye-slash
| {{_ 'watch'}}
unless currentUser.isCommentOnly
hr
ul.pop-over-list
li: a.js-set-color-list {{_ 'set-color-list'}}
hr
unless currentUser.isWorker
ul.pop-over-list
li
a.js-set-color-list
i.fa.fa-paint-brush
| {{_ 'set-color-list'}}
ul.pop-over-list
if cards.count
li: a.js-select-cards {{_ 'list-select-cards'}}
hr
li
a.js-select-cards
i.fa.fa-check-square
| {{_ 'list-select-cards'}}
if currentUser.isBoardAdmin
ul.pop-over-list
li: a.js-set-wip-limit {{#if isWipLimitEnabled }}{{_ 'edit-wip-limit'}}{{else}}{{_ 'setWipLimitPopup-title'}}{{/if}}
li
a.js-set-wip-limit
i.fa.fa-ban
| {{#if isWipLimitEnabled }}{{_ 'edit-wip-limit'}}{{else}}{{_ 'setWipLimitPopup-title'}}{{/if}}
unless currentUser.isWorker
hr
ul.pop-over-list
li: a.js-close-list {{_ 'archive-list'}}
ul.pop-over-list
li
a.js-close-list
i.fa.fa-arrow-right
i.fa.fa-archive
| {{_ 'archive-list'}}
hr
ul.pop-over-list
li: a.js-more {{_ 'listMorePopup-title'}}
li
a.js-more
i.fa.fa-link
| {{_ 'listMorePopup-title'}}
template(name="boardLists")
ul.pop-over-list
@@ -87,7 +116,8 @@ template(name="listMorePopup")
input.inline-input(type="text" readonly value="{{ rootUrl }}")
| {{_ 'added'}}
span.date(title=list.createdAt) {{ moment createdAt 'LLL' }}
a.js-delete {{_ 'delete'}}
unless currentUser.isWorker
a.js-delete {{_ 'delete'}}
template(name="listDeletePopup")
p {{_ "list-delete-pop"}}
+44 -6
View File
@@ -1,3 +1,5 @@
import { Cookies } from 'meteor/ostrio:cookies';
const cookies = new Cookies();
let listsColors;
Meteor.startup(() => {
listsColors = Lists.simpleSchema()._schema.color.allowedValues;
@@ -7,12 +9,27 @@ BlazeComponent.extendComponent({
canSeeAddCard() {
const list = Template.currentData();
return (
!list.getWipLimit('enabled') ||
list.getWipLimit('soft') ||
!this.reachedWipLimit()
(!list.getWipLimit('enabled') ||
list.getWipLimit('soft') ||
!this.reachedWipLimit()) &&
!Meteor.user().isWorker()
);
},
isBoardAdmin() {
return Meteor.user().isBoardAdmin();
},
starred(check = undefined) {
const list = Template.currentData();
const status = list.isStarred();
if (check === undefined) {
// just check
return status;
} else {
list.star(!status);
return !status;
}
},
editTitle(event) {
event.preventDefault();
const newTitle = this.childComponents('inlinedForm')[0]
@@ -30,14 +47,18 @@ BlazeComponent.extendComponent({
},
limitToShowCardsCount() {
return Meteor.user().getLimitToShowCardsCount();
const currentUser = Meteor.user();
if (currentUser) {
return Meteor.user().getLimitToShowCardsCount();
} else {
return false;
}
},
cardsCount() {
const list = Template.currentData();
let swimlaneId = '';
const boardView = (Meteor.user().profile || {}).boardView;
if (boardView === 'board-view-swimlanes')
if (Utils.boardView() === 'board-view-swimlanes')
swimlaneId = this.parentComponent()
.parentComponent()
.data()._id;
@@ -61,6 +82,10 @@ BlazeComponent.extendComponent({
events() {
return [
{
'click .js-list-star'(event) {
event.preventDefault();
this.starred(!this.starred());
},
'click .js-open-list-menu': Popup.open('listAction'),
'click .js-add-card'(event) {
const listDom = $(event.target).parents(
@@ -80,6 +105,19 @@ BlazeComponent.extendComponent({
},
}).register('listHeader');
Template.listHeader.helpers({
showDesktopDragHandles() {
currentUser = Meteor.user();
if (currentUser) {
return (currentUser.profile || {}).showDesktopDragHandles;
} else if (cookies.has('showDesktopDragHandles')) {
return true;
} else {
return false;
}
},
});
Template.listActionPopup.helpers({
isWipLimitEnabled() {
return Template.currentData().getWipLimit('enabled');
+40 -12
View File
@@ -94,7 +94,13 @@ Template.editor.onRendered(() => {
currentBoard
.activeMembers()
.map(member => {
const username = Users.findOne(member.userId).username;
const user = Users.findOne(member.userId);
if (user._id === Meteor.userId()) {
return null;
}
const value = user.username;
const username =
value && value.match(/\s+/) ? `"${value}"` : value;
return username.includes(term) ? username : null;
})
.filter(Boolean),
@@ -120,9 +126,10 @@ Template.editor.onRendered(() => {
? [
['view', ['fullscreen']],
['table', ['table']],
['font', ['bold', 'underline']],
//['fontsize', ['fontsize']],
['font', ['bold']],
['color', ['color']],
['insert', ['video']], // iframe tag will be sanitized TODO if iframe[class=note-video-clip] can be added into safe list, insert video can be enabled
//['fontsize', ['fontsize']],
]
: [
['style', ['style']],
@@ -156,31 +163,51 @@ Template.editor.onRendered(() => {
}
return undefined;
};
let popupShown = false;
inputs.each(function(idx, input) {
mSummernotes[idx] = $(input).summernote({
placeholder,
callbacks: {
onKeydown(e) {
if (popupShown) {
e.preventDefault();
}
},
onKeyup(e) {
if (popupShown) {
e.preventDefault();
}
},
onInit(object) {
const originalInput = this;
const setAutocomplete = function(jEditor) {
if (jEditor !== undefined) {
jEditor.escapeableTextComplete(mentions).on({
'textComplete:show'() {
popupShown = true;
},
'textComplete:hide'() {
popupShown = false;
},
});
}
};
$(originalInput).on('submitted', function() {
// resetCommentInput has been called
if (!this.value) {
const sn = getSummernote(this);
sn && sn.summernote('reset');
object && object.editingArea.find('.note-placeholder').show();
sn && sn.summernote('code', '');
}
});
const jEditor = object && object.editable;
const toolbar = object && object.toolbar;
if (jEditor !== undefined) {
jEditor.escapeableTextComplete(mentions);
}
setAutocomplete(jEditor);
if (toolbar !== undefined) {
const fBtn = toolbar.find('.btn-fullscreen');
fBtn.on('click', function() {
const $this = $(this),
isActive = $this.hasClass('active');
$('.minicards').toggle(!isActive); // mini card is still showing when editor is in fullscreen mode, we hide here manually
$('.minicards,#header-quick-access').toggle(!isActive); // mini card is still showing when editor is in fullscreen mode, we hide here manually
});
}
},
@@ -264,7 +291,7 @@ Template.editor.onRendered(() => {
const someNote = getSummernote(object);
const original = someNote.summernote('code');
const cleaned = cleanPastedHTML(original); //this is where to call whatever clean function you want. I have mine in a different file, called CleanPastedHTML.
someNote.summernote('reset'); //clear original
someNote.summernote('code', ''); //clear original
someNote.summernote('pasteHTML', cleaned); //this sets the displayed content editor to the cleaned pasted code.
};
setTimeout(function() {
@@ -325,11 +352,12 @@ Blaze.Template.registerHelper(
}
return member;
});
const mentionRegex = /\B@([\w.]*)/gi;
const mentionRegex = /\B@(?:(?:"([\w.\s]*)")|([\w.]+))/gi; // including space in username
let currentMention;
while ((currentMention = mentionRegex.exec(content)) !== null) {
const [fullMention, username] = currentMention;
const [fullMention, quoteduser, simple] = currentMention;
const username = quoteduser || simple;
const knowedUser = _.findWhere(knowedUsers, { username });
if (!knowedUser) {
continue;
+3
View File
@@ -218,6 +218,9 @@
padding: 10px
margin: -10px 0 -10px -10px
.announcement .viewer
display: inline-block
.announcement,
.offline-warning
width: 100%
+9
View File
@@ -378,6 +378,12 @@ a
.viewer
min-height: 18px
display: block
word-wrap: break-word
table
word-wrap: normal
word-break: normal
ol
list-style-type: decimal
@@ -425,6 +431,9 @@ a
height: 100%
margin: 0px
.panel-default
width: 83vw
.inline-input
height: 37px
margin: 8px 10px 0 0
+4 -2
View File
@@ -130,7 +130,8 @@ $popupWidth = 300px
.popup-container-depth-{depth}
transform: translateX(- depth * $popupWidth)
.select-members-list
.select-members-list,
.select-avatars-list
margin-bottom: 8px
.pop-over-list
@@ -230,7 +231,8 @@ $popupWidth = 300px
min-height: 56px
position: relative
.member
.member,
.avatar
position: absolute
top: 2px
left: 2px
@@ -1,29 +1,42 @@
template(name="boardActions")
div.trigger-item
div.trigger-content
div.trigger-text
div.trigger-text
| {{_'r-move-card-to'}}
div.trigger-dropdown
select(id="move-gen-action")
option(value="top") {{_'r-top-of'}}
option(value="bottom") {{_'r-bottom-of'}}
div.trigger-text
div.trigger-text
| {{_'r-its-list'}}
div.trigger-button.js-add-gen-move-action.js-goto-rules
i.fa.fa-plus
div.trigger-item
div.trigger-content
div.trigger-text
div.trigger-text
| {{_'r-move-card-to'}}
div.trigger-dropdown
select(id="move-spec-action")
option(value="top") {{_'r-top-of'}}
option(value="bottom") {{_'r-bottom-of'}}
div.trigger-text
| {{_'r-list'}}
div.trigger-text
| {{_'r-the-board'}}
div.trigger-dropdown
select(id="board-id")
each boards
if $eq _id currentBoard._id
option(value="{{_id}}" selected) {{_ 'current'}}
else
option(value="{{_id}}") {{title}}
div.trigger-text
| {{_'r-in-list'}}
div.trigger-dropdown
input(id="listName",type=text,placeholder="{{_'r-name'}}")
div.trigger-text
| {{_'r-in-swimlane'}}
div.trigger-dropdown
input(id="swimlaneName",type=text,placeholder="{{_'r-name'}}")
div.trigger-button.js-add-spec-move-action.js-goto-rules
i.fa.fa-plus
@@ -33,14 +46,14 @@ template(name="boardActions")
select(id="arch-action")
option(value="archive") {{_'r-archive'}}
option(value="unarchive") {{_'r-unarchive'}}
div.trigger-text
div.trigger-text
| {{_'r-card'}}
div.trigger-button.js-add-arch-action.js-goto-rules
i.fa.fa-plus
div.trigger-item
div.trigger-content
div.trigger-text
div.trigger-text
| {{_'r-add-swimlane'}}
div.trigger-dropdown
input(id="swimlane-name",type=text,placeholder="{{_'r-name'}}")
@@ -49,15 +62,15 @@ template(name="boardActions")
div.trigger-item
div.trigger-content
div.trigger-text
div.trigger-text
| {{_'r-create-card'}}
div.trigger-dropdown
input(id="card-name",type=text,placeholder="{{_'r-name'}}")
div.trigger-text
div.trigger-text
| {{_'r-in-list'}}
div.trigger-dropdown
input(id="list-name",type=text,placeholder="{{_'r-name'}}")
div.trigger-text
div.trigger-text
| {{_'r-in-swimlane'}}
div.trigger-dropdown
input(id="swimlane-name2",type=text,placeholder="{{_'r-name'}}")
@@ -65,8 +78,8 @@ template(name="boardActions")
i.fa.fa-plus
@@ -1,6 +1,22 @@
BlazeComponent.extendComponent({
onCreated() {},
boards() {
const boards = Boards.find(
{
archived: false,
'members.userId': Meteor.userId(),
_id: {
$ne: Meteor.user().getTemplatesBoardId(),
},
},
{
sort: ['title'],
},
);
return boards;
},
events() {
return [
{
@@ -52,15 +68,18 @@ BlazeComponent.extendComponent({
const ruleName = this.data().ruleName.get();
const trigger = this.data().triggerVar.get();
const actionSelected = this.find('#move-spec-action').value;
const listTitle = this.find('#listName').value;
const swimlaneName = this.find('#swimlaneName').value;
const listName = this.find('#listName').value;
const boardId = Session.get('currentBoard');
const destBoardId = this.find('#board-id').value;
const desc = Utils.getTriggerActionDesc(event, this);
if (actionSelected === 'top') {
const triggerId = Triggers.insert(trigger);
const actionId = Actions.insert({
actionType: 'moveCardToTop',
listTitle,
boardId,
listName,
swimlaneName,
boardId: destBoardId,
desc,
});
Rules.insert({
@@ -74,8 +93,9 @@ BlazeComponent.extendComponent({
const triggerId = Triggers.insert(trigger);
const actionId = Actions.insert({
actionType: 'moveCardToBottom',
listTitle,
boardId,
listName,
swimlaneName,
boardId: destBoardId,
desc,
});
Rules.insert({
@@ -4,12 +4,16 @@ template(name='information')
| {{_ 'error-notAuthorized'}}
else
.content-title
span {{_ 'info'}}
span
i.fa.fa-info-circle
| {{_ 'info'}}
.content-body
.side-menu
ul
li.active
a.js-setting-menu(data-id="information-display") {{_ 'info'}}
a.js-setting-menu(data-id="information-display")
i.fa.fa-info-circle
| {{_ 'info'}}
.main-body
+statistics
+54 -18
View File
@@ -5,16 +5,22 @@ template(name="people")
else
.content-title.ext-box
.ext-box-left
span {{_ 'people'}}
span
i.fa.fa-users
| {{_ 'people'}}
input#searchInput(placeholder="{{_ 'search'}}")
button#searchButton {{_ 'search'}}
button#searchButton
i.fa.fa-search
| {{_ 'search'}}
.ext-box-right
span {{_ 'people-number'}} #{peopleNumber}
.content-body
.side-menu
ul
li.active
a.js-setting-menu(data-id="people-setting") {{_ 'people'}}
a.js-setting-menu(data-id="people-setting")
i.fa.fa-users
| {{_ 'people'}}
.main-body
if loading.get
+spinner
@@ -39,28 +45,58 @@ template(name="peopleGeneral")
template(name="peopleRow")
tr
td.username {{ userData.username }}
td {{ userData.profile.fullname }}
td
if userData.isAdmin
| {{_ 'yes'}}
else
| {{_ 'no'}}
td {{ userData.emails.[0].address }}
td
if userData.emails.[0].verified
| {{_ 'yes'}}
else
| {{_ 'no'}}
td {{ moment userData.createdAt 'LLL' }}
if userData.loginDisabled
td.username <s>{{ userData.username }}</s>
else
td.username {{ userData.username }}
if userData.loginDisabled
td <s>{{ userData.profile.fullname }}</s>
else
td {{ userData.profile.fullname }}
if userData.loginDisabled
td
if userData.isAdmin
| <s>{{_ 'yes'}}</s>
else
| <s>{{_ 'no'}}</s>
else
td
if userData.isAdmin
| {{_ 'yes'}}
else
| {{_ 'no'}}
if userData.loginDisabled
td <s>{{ userData.emails.[0].address }}</s>
else
td {{ userData.emails.[0].address }}
if userData.loginDisabled
td
if userData.emails.[0].verified
| <s>{{_ 'yes'}}</s>
else
| <s>{{_ 'no'}}</s>
else
td
if userData.emails.[0].verified
| {{_ 'yes'}}
else
| {{_ 'no'}}
if userData.loginDisabled
td <s>{{ moment userData.createdAt 'LLL' }}</s>
else
td {{ moment userData.createdAt 'LLL' }}
td
if userData.loginDisabled
| {{_ 'no'}}
else
| {{_ 'yes'}}
td {{_ userData.authenticationMethod }}
if userData.loginDisabled
td <s>{{_ userData.authenticationMethod }}</s>
else
td {{_ userData.authenticationMethod }}
td
a.edit-user
i.fa.fa-edit
| {{_ 'edit'}}
template(name="editUserPopup")
+2 -2
View File
@@ -17,7 +17,7 @@ BlazeComponent.extendComponent({
this.autorun(() => {
const limit = this.page.get() * usersPerPage;
this.subscribe('people', limit, () => {
this.subscribe('people', this.findUsersOptions.get(), limit, () => {
this.loadNextPageLocked = false;
const nextPeakBefore = this.callFirstWith(null, 'getNextPeak');
this.calculateNextPeak();
@@ -85,7 +85,7 @@ BlazeComponent.extendComponent({
const users = Users.find(this.findUsersOptions.get(), {
fields: { _id: true },
});
this.number.set(users.count());
this.number.set(users.count(false));
return users;
},
peopleNumber() {
+1 -1
View File
@@ -33,7 +33,7 @@ table
padding: 0;
button
min-width: 60px;
min-width: 90px;
.content-wrapper
margin-top: 10px
+26 -11
View File
@@ -4,20 +4,35 @@ template(name="setting")
| {{_ 'error-notAuthorized'}}
else
.content-title
i.fa.fa-cog
span {{_ 'settings'}}
.content-body
.side-menu
ul
li.active
a.js-setting-menu(data-id="registration-setting") {{_ 'registration'}}
a.js-setting-menu(data-id="registration-setting")
i.fa.fa-sign-in
| {{_ 'registration'}}
li
a.js-setting-menu(data-id="email-setting") {{_ 'email'}}
a.js-setting-menu(data-id="email-setting")
i.fa.fa-envelope
| {{_ 'email'}}
li
a.js-setting-menu(data-id="account-setting") {{_ 'accounts'}}
a.js-setting-menu(data-id="account-setting")
i.fa.fa-users
| {{_ 'accounts'}}
li
a.js-setting-menu(data-id="announcement-setting") {{_ 'admin-announcement'}}
a.js-setting-menu(data-id="announcement-setting")
i.fa.fa-bullhorn
| {{_ 'admin-announcement'}}
li
a.js-setting-menu(data-id="layout-setting") {{_ 'layout'}}
a.js-setting-menu(data-id="layout-setting")
i.fa.fa-object-group
| {{_ 'layout'}}
li
a.js-setting-menu(data-id="webhook-setting")
i.fa.fa-globe
| {{_ 'global-webhook'}}
.main-body
if loading.get
+spinner
@@ -31,6 +46,12 @@ template(name="setting")
+announcementSettings
else if layoutSetting.get
+layoutSettings
else if webhookSetting.get
+webhookSettings
template(name="webhookSettings")
span
+outgoingWebhooksPopup
template(name="general")
ul#registration-setting.setting-detail
@@ -163,12 +184,6 @@ template(name='layoutSettings')
.title {{_ 'custom-product-name'}}
.form-group
input.wekan-form-control#product-name(type="text", placeholder="" value="{{currentSetting.productName}}")
li.layout-form
.title {{_ 'add-custom-html-after-body-start'}}
textarea#customHTMLafterBodyStart.wekan-form-control= currentSetting.customHTMLafterBodyStart
li.layout-form
.title {{_ 'add-custom-html-before-body-end'}}
textarea#customHTMLbeforeBodyEnd.wekan-form-control= currentSetting.customHTMLbeforeBodyEnd
li
button.js-save-layout.primary {{_ 'save'}}
+3 -8
View File
@@ -7,11 +7,13 @@ BlazeComponent.extendComponent({
this.accountSetting = new ReactiveVar(false);
this.announcementSetting = new ReactiveVar(false);
this.layoutSetting = new ReactiveVar(false);
this.webhookSetting = new ReactiveVar(false);
Meteor.subscribe('setting');
Meteor.subscribe('mailServer');
Meteor.subscribe('accountSettings');
Meteor.subscribe('announcements');
Meteor.subscribe('globalwebhooks');
},
setError(error) {
@@ -83,6 +85,7 @@ BlazeComponent.extendComponent({
this.accountSetting.set('account-setting' === targetID);
this.announcementSetting.set('announcement-setting' === targetID);
this.layoutSetting.set('layout-setting' === targetID);
this.webhookSetting.set('webhook-setting' === targetID);
}
},
@@ -168,20 +171,12 @@ BlazeComponent.extendComponent({
const displayAuthenticationMethod =
$('input[name=displayAuthenticationMethod]:checked').val() === 'true';
const defaultAuthenticationMethod = $('#defaultAuthenticationMethod').val();
const customHTMLafterBodyStart = $('#customHTMLafterBodyStart')
.val()
.trim();
const customHTMLbeforeBodyEnd = $('#customHTMLbeforeBodyEnd')
.val()
.trim();
try {
Settings.update(Settings.findOne()._id, {
$set: {
productName,
hideLogo: hideLogoChange,
customHTMLafterBodyStart,
customHTMLbeforeBodyEnd,
displayAuthenticationMethod,
defaultAuthenticationMethod,
},
+4 -1
View File
@@ -41,15 +41,18 @@
&:hover
background #fff
box-shadow 0 1px 2px rgba(0,0,0,0.15);
a
@extends .flex
padding: 1rem 0 1rem 1rem
width: 100% - 5rem
span
font-size: 13px
i
margin-right: 20px
.main-body
padding: 0.1em 1em
-webkit-user-select: text // Safari 3.1+
+190 -40
View File
@@ -37,11 +37,12 @@ template(name='homeSidebar')
template(name="membersWidget")
.board-widget.board-widget-members
h3
i.fa.fa-user
i.fa.fa-users
| {{_ 'members'}}
unless currentUser.isCommentOnly
a.board-header-btn.js-open-board-menu(title="{{_ 'boardMenuPopup-title'}}").right
i.board-header-btn-icon.fa.fa-cog
unless currentUser.isWorker
a.board-header-btn.js-open-board-menu(title="{{_ 'boardMenuPopup-title'}}").right
i.board-header-btn-icon.fa.fa-cog
.board-widget-content
each currentBoard.activeMembers
@@ -71,6 +72,88 @@ template(name="boardChangeColorPopup")
if isSelected
i.fa.fa-check
template(name="boardCardSettingsPopup")
form.board-card-settings
h3 {{_ 'show-on-card'}}
div.check-div
a.flex.js-field-has-receiveddate(class="{{#if allowsReceivedDate}}is-checked{{/if}}")
.materialCheckBox(class="{{#if allowsReceivedDate}}is-checked{{/if}}")
span
i.fa.fa-sign-out
| {{_ 'card-received'}}
div.check-div
a.flex.js-field-has-startdate(class="{{#if allowsStartDate}}is-checked{{/if}}")
.materialCheckBox(class="{{#if allowsStartDate}}is-checked{{/if}}")
span
i.fa.fa-hourglass-start
| {{_ 'card-start'}}
div.check-div
a.flex.js-field-has-duedate(class="{{#if allowsDueDate}}is-checked{{/if}}")
.materialCheckBox(class="{{#if allowsDueDate}}is-checked{{/if}}")
span
i.fa.fa-sign-in
| {{_ 'card-due'}}
div.check-div
a.flex.js-field-has-enddate(class="{{#if allowsEndDate}}is-checked{{/if}}")
.materialCheckBox(class="{{#if allowsEndDate}}is-checked{{/if}}")
span
i.fa.fa-hourglass-end
| {{_ 'card-end'}}
div.check-div
a.flex.js-field-has-members(class="{{#if allowsMembers}}is-checked{{/if}}")
.materialCheckBox(class="{{#if allowsMembers}}is-checked{{/if}}")
span
i.fa.fa-users
| {{_ 'members'}}
div.check-div
a.flex.js-field-has-assignee(class="{{#if allowsAssignee}}is-checked{{/if}}")
.materialCheckBox(class="{{#if allowsAssignee}}is-checked{{/if}}")
span
i.fa.fa-user
| {{_ 'assignee'}}
div.check-div
a.flex.js-field-has-assigned-by(class="{{#if allowsAssignedBy}}is-checked{{/if}}")
.materialCheckBox(class="{{#if allowsAssignedBy}}is-checked{{/if}}")
span
i.fa.fa-shopping-cart
| {{_ 'assigned-by'}}
div.check-div
a.flex.js-field-has-requested-by(class="{{#if allowsRequestedBy}}is-checked{{/if}}")
.materialCheckBox(class="{{#if allowsRequestedBy}}is-checked{{/if}}")
span
i.fa.fa-user-plus
| {{_ 'requested-by'}}
div.check-div
a.flex.js-field-has-labels(class="{{#if allowsLabels}}is-checked{{/if}}")
.materialCheckBox(class="{{#if allowsLabels}}is-checked{{/if}}")
span
i.fa.fa-tags
| {{_ 'labels'}}
div.check-div
a.flex.js-field-has-checklists(class="{{#if allowsChecklists}}is-checked{{/if}}")
.materialCheckBox(class="{{#if allowsChecklists}}is-checked{{/if}}")
span
i.fa.fa-check
| {{_ 'checklists'}}
div.check-div
a.flex.js-field-has-subtasks(class="{{#if allowsSubtasks}}is-checked{{/if}}")
.materialCheckBox(class="{{#if allowsSubtasks}}is-checked{{/if}}")
span
i.fa.fa-sitemap
| {{_ 'subtasks'}}
div.check-div
a.flex.js-field-has-attachments(class="{{#if allowsAttachments}}is-checked{{/if}}")
.materialCheckBox(class="{{#if allowsAttachments}}is-checked{{/if}}")
span
i.fa.fa-paperclip
| {{_ 'attachments'}}
div.check-div
a.flex.js-field-has-comments(class="{{#if allowsComments}}is-checked{{/if}}")
.materialCheckBox(class="{{#if allowsComments}}is-checked{{/if}}")
span
i.fa.fa-comment-o
| {{_ 'comment'}}
template(name="boardSubtaskSettingsPopup")
form.board-subtask-settings
h3 {{_ 'show-parent-in-minicard'}}
@@ -130,62 +213,123 @@ template(name="chooseBoardSource")
template(name="archiveBoardPopup")
p {{_ 'close-board-pop'}}
button.js-confirm.negate.full(type="submit") {{_ 'archive'}}
button.js-confirm.negate.full(type="submit")
i.fa.fa-archive
| {{_ 'archive'}}
template(name="outgoingWebhooksPopup")
each integrations
form.integration-form
if title
h4 {{title}}
else
h4 {{_ 'no-name'}}
label
| URL
input.js-outgoing-webhooks-url(type="text" name="url" value=url)
input(type="hidden" value=_id name="id")
a.flex
span {{_ 'disable-webhook'}}
b &nbsp;
.materialCheckBox(class="{{#unless enabled}}is-checked{{/unless}}")
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 autofocus)
input.js-outgoing-webhooks-token(placeholder="{{_ 'webhook-token' }}" type="text" value=token name="token")
select.js-outgoing-webhooks-type(name="type")
each _type in types
if($eq _type this.type)
option(value=_type selected="selected") {{_ _type}}
else
option(value=_type) {{_ _type}}
input(type="hidden" value=this.type name="_type")
input(type="hidden" value=_id name="id")
input.primary.wide(type="submit" value="{{_ 'save'}}")
form.integration-form
h4
| {{_ 'new-outgoing-webhook'}}
label
| URL
input.js-outgoing-webhooks-url(type="text" name="url" autofocus)
input.primary.wide(type="submit" value="{{_ 'save'}}")
input.js-outgoing-webhooks-title(placeholder="{{_ 'webhook-title'}}" type="text" name="title" autofocus)
input.js-outgoing-webhooks-url(placeholder="{{_ 'URL' }}" type="text" name="url")
input.js-outgoing-webhooks-token(placeholder="{{_ 'webhook-token' }}" type="text" name="token")
select.js-outgoing-webhooks-type(name="type")
each _type in types
option(value=_type) {{_ _type}}
input.primary.wide(type="submit" value="{{_ 'create'}}")
template(name="boardMenuPopup")
ul.pop-over-list
li: a.js-custom-fields {{_ 'custom-fields'}}
li: a.js-open-archives {{_ 'archived-items'}}
li
a.js-open-archives
i.fa.fa-archive
| {{_ 'archived-items'}}
if currentUser.isBoardAdmin
li: a.js-change-board-color {{_ 'board-change-color'}}
li
a.js-change-board-color
i.fa.fa-paint-brush
| {{_ 'board-change-color'}}
//-
XXX Language should be handled by sandstorm, but for now display a
language selection link in the board menu. This link is normally present
in the header bar that is not displayed on sandstorm.
if isSandstorm
li: a.js-change-language {{_ 'language'}}
li
a.js-change-language
i.fa.fa-flag
| {{_ 'language'}}
unless isSandstorm
if currentUser.isBoardAdmin
hr
ul.pop-over-list
li: a(href="{{exportUrl}}", download="{{exportFilename}}") {{_ 'export-board'}}
unless currentBoard.isTemplatesBoard
li: a.js-archive-board {{_ 'archive-board'}}
li: a.js-outgoing-webhooks {{_ 'outgoing-webhooks'}}
hr
ul.pop-over-list
li: a.js-subtask-settings {{_ 'subtask-settings'}}
li
a(href="{{exportUrl}}", download="{{exportFilename}}")
i.fa.fa-share-alt
| {{_ 'export-board'}}
li
a.js-outgoing-webhooks
i.fa.fa-globe
| {{_ 'outgoing-webhooks'}}
li
a.js-card-settings
i.fa.fa-id-card-o
| {{_ 'card-settings'}}
li
a.js-subtask-settings
i.fa.fa-sitemap
| {{_ 'subtask-settings'}}
unless currentBoard.isTemplatesBoard
hr
ul.pop-over-list
li
a.js-archive-board
i.fa.fa-arrow-right
i.fa.fa-archive
| {{_ 'archive-board'}}
if isSandstorm
hr
ul.pop-over-list
li: a(href="{{exportUrl}}", download="{{exportFilename}}") {{_ 'export-board'}}
li: a.js-import-board {{_ 'import-board-c'}}
li: a.js-archive-board {{_ 'archive-board'}}
li: a.js-outgoing-webhooks {{_ 'outgoing-webhooks'}}
li
a(href="{{exportUrl}}", download="{{exportFilename}}")
i.fa.fa-share-alt
i.fa.fa-sign-out
| {{_ 'export-board'}}
li
a.js-import-board
i.fa.fa-share-alt
i.fa.fa-sign-in
| {{_ 'import-board-c'}}
li
a.js-archive-board
i.fa.fa-arrow-right
i.fa.fa-archive
| {{_ 'archive-board'}}
li
a.js-outgoing-webhooks
i.fa.fa-globe
| {{_ 'outgoing-webhooks'}}
hr
ul.pop-over-list
li: a.js-subtask-settings {{_ 'subtask-settings'}}
li
a.js-card-settings
i.fa.fa-id-card-o
| {{_ 'card-settings'}}
hr
ul.pop-over-list
li
a.js-subtask-settings
i.fa.fa-sitemap
| {{_ 'subtask-settings'}}
template(name="labelsWidget")
.board-widget.board-widget-labels
@@ -195,7 +339,7 @@ template(name="labelsWidget")
.board-widget-content
each currentBoard.labels
a.card-label(class="card-label-{{color}}"
class="{{#if currentUser.isNotCommentOnly}}js-label{{/if}}")
class="{{#if currentUser.isNotCommentOnly}}{{#if currentUser.isNotWorker}}js-label{{/if}}{{/if}}")
span.card-label-name
+viewer
= name
@@ -224,12 +368,12 @@ template(name="memberPopup")
a.js-change-role
| {{_ 'change-permissions'}}
span.quiet (#{memberType})
li
if $eq currentUser._id userId
a.js-leave-member {{_ 'leave-board'}}
else if currentUser.isBoardAdmin
a.js-remove-member {{_ 'remove-from-board'}}
unless currentUser.isWorker
li
if $eq currentUser._id userId
a.js-leave-member {{_ 'leave-board'}}
else if currentUser.isBoardAdmin
a.js-remove-member {{_ 'remove-from-board'}}
template(name="removeMemberPopup")
p {{_ 'remove-member-pop' name=user.profile.fullname username=user.username boardTitle=board.title}}
@@ -293,6 +437,12 @@ template(name="changePermissionsPopup")
if isCommentOnly
i.fa.fa-check
span.sub-name {{_ 'comment-only-desc'}}
li
a(class="{{#if isLastAdmin}}disabled{{else}}js-set-worker{{/if}}")
| {{_ 'worker'}}
if isWorker
i.fa.fa-check
span.sub-name {{_ 'worker-desc'}}
if isLastAdmin
hr
p.quiet.bottom {{_ 'last-admin-desc'}}
+399 -34
View File
@@ -1,6 +1,10 @@
import { Cookies } from 'meteor/ostrio:cookies';
const cookies = new Cookies();
Sidebar = null;
const defaultView = 'home';
const MCB = '.materialCheckBox';
const CKCLS = 'is-checked';
const viewTitles = {
filter: 'filter-cards',
@@ -105,7 +109,14 @@ BlazeComponent.extendComponent({
'click .js-toggle-sidebar': this.toggle,
'click .js-back-home': this.setView,
'click .js-toggle-minicard-label-text'() {
Meteor.call('toggleMinicardLabelText');
currentUser = Meteor.user();
if (currentUser) {
Meteor.call('toggleMinicardLabelText');
} else if (cookies.has('hiddenMinicardLabelText')) {
cookies.remove('hiddenMinicardLabelText');
} else {
cookies.set('hiddenMinicardLabelText', 'true');
}
},
'click .js-shortcuts'() {
FlowRouter.go('shortcuts');
@@ -119,7 +130,14 @@ Blaze.registerHelper('Sidebar', () => Sidebar);
Template.homeSidebar.helpers({
hiddenMinicardLabelText() {
return Meteor.user().hasHiddenMinicardLabelText();
currentUser = Meteor.user();
if (currentUser) {
return (currentUser.profile || {}).hiddenMinicardLabelText;
} else if (cookies.has('hiddenMinicardLabelText')) {
return true;
} else {
return false;
}
},
});
@@ -143,10 +161,13 @@ Template.memberPopup.helpers({
const currentBoard = Boards.findOne(Session.get('currentBoard'));
const commentOnly = currentBoard.hasCommentOnly(this.userId);
const noComments = currentBoard.hasNoComments(this.userId);
const worker = currentBoard.hasWorker(this.userId);
if (commentOnly) {
return TAPi18n.__('comment-only').toLowerCase();
} else if (noComments) {
return TAPi18n.__('no-comments').toLowerCase();
} else if (worker) {
return TAPi18n.__('worker').toLowerCase();
} else {
return TAPi18n.__(type).toLowerCase();
}
@@ -187,6 +208,7 @@ Template.boardMenuPopup.events({
'click .js-outgoing-webhooks': Popup.open('outgoingWebhooks'),
'click .js-import-board': Popup.open('chooseBoardSource'),
'click .js-subtask-settings': Popup.open('boardSubtaskSettings'),
'click .js-card-settings': Popup.open('boardCardSettings'),
});
Template.boardMenuPopup.helpers({
@@ -249,6 +271,14 @@ Template.membersWidget.helpers({
const user = Meteor.user();
return user && user.isInvitedTo(Session.get('currentBoard'));
},
isWorker() {
const user = Meteor.user();
if (user) {
return Meteor.call(Boards.hasWorker(user.memberId));
} else {
return false;
}
},
});
Template.membersWidget.events({
@@ -280,44 +310,71 @@ Template.membersWidget.events({
});
BlazeComponent.extendComponent({
boardId() {
return Session.get('currentBoard') || Integrations.Const.GLOBAL_WEBHOOK_ID;
},
integrations() {
const boardId = Session.get('currentBoard');
const boardId = this.boardId();
return Integrations.find({ boardId: `${boardId}` }).fetch();
},
integration(id) {
const boardId = Session.get('currentBoard');
return Integrations.findOne({ _id: id, boardId: `${boardId}` });
types() {
return Integrations.Const.WEBHOOK_TYPES;
},
integration(cond) {
const boardId = this.boardId();
const condition = { boardId, ...cond };
for (const k in condition) {
if (!condition[k]) delete condition[k];
}
return Integrations.findOne(condition);
},
onCreated() {
this.disabled = new ReactiveVar(false);
},
events() {
return [
{
'click a.flex'(evt) {
this.disabled.set(!this.disabled.get());
$(evt.target).toggleClass(CKCLS, this.disabled.get());
},
submit(evt) {
evt.preventDefault();
const url = evt.target.url.value;
const boardId = Session.get('currentBoard');
const boardId = this.boardId();
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 = !this.disabled.get();
let remove = false;
const values = {
url,
type,
token,
title,
enabled,
};
if (evt.target.id) {
id = evt.target.id.value;
integration = this.integration(id);
if (url) {
Integrations.update(integration._id, {
$set: {
url: `${url}`,
},
});
} else {
Integrations.remove(integration._id);
}
integration = this.integration({ _id: id });
remove = !url;
} else if (url) {
integration = this.integration({ 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,
type: 'outgoing-webhooks',
url: `${url}`,
boardId: `${boardId}`,
boardId,
activities: ['all'],
});
}
@@ -416,6 +473,10 @@ BlazeComponent.extendComponent({
return this.currentBoard.allowsSubtasks;
},
allowsReceivedDate() {
return this.currentBoard.allowsReceivedDate;
},
isBoardSelected() {
return this.currentBoard.subtasksDefaultBoardId === this.currentData()._id;
},
@@ -474,12 +535,12 @@ BlazeComponent.extendComponent({
evt.preventDefault();
this.currentBoard.allowsSubtasks = !this.currentBoard.allowsSubtasks;
this.currentBoard.setAllowsSubtasks(this.currentBoard.allowsSubtasks);
$('.js-field-has-subtasks .materialCheckBox').toggleClass(
'is-checked',
$(`.js-field-has-subtasks ${MCB}`).toggleClass(
CKCLS,
this.currentBoard.allowsSubtasks,
);
$('.js-field-has-subtasks').toggleClass(
'is-checked',
CKCLS,
this.currentBoard.allowsSubtasks,
);
$('.js-field-deposit-board').prop(
@@ -515,15 +576,12 @@ BlazeComponent.extendComponent({
];
options.forEach(function(element) {
if (element !== value) {
$(`#${element} .materialCheckBox`).toggleClass(
'is-checked',
false,
);
$(`#${element}`).toggleClass('is-checked', false);
$(`#${element} ${MCB}`).toggleClass(CKCLS, false);
$(`#${element}`).toggleClass(CKCLS, false);
}
});
$(`#${value} .materialCheckBox`).toggleClass('is-checked', true);
$(`#${value}`).toggleClass('is-checked', true);
$(`#${value} ${MCB}`).toggleClass(CKCLS, true);
$(`#${value}`).toggleClass(CKCLS, true);
this.currentBoard.setPresentParentTask(value);
evt.preventDefault();
},
@@ -532,6 +590,303 @@ BlazeComponent.extendComponent({
},
}).register('boardSubtaskSettingsPopup');
BlazeComponent.extendComponent({
onCreated() {
this.currentBoard = Boards.findOne(Session.get('currentBoard'));
},
allowsReceivedDate() {
return this.currentBoard.allowsReceivedDate;
},
allowsStartDate() {
return this.currentBoard.allowsStartDate;
},
allowsDueDate() {
return this.currentBoard.allowsDueDate;
},
allowsEndDate() {
return this.currentBoard.allowsEndDate;
},
allowsSubtasks() {
return this.currentBoard.allowsSubtasks;
},
allowsMembers() {
return this.currentBoard.allowsMembers;
},
allowsAssignee() {
return this.currentBoard.allowsAssignee;
},
allowsAssignedBy() {
return this.currentBoard.allowsAssignedBy;
},
allowsRequestedBy() {
return this.currentBoard.allowsRequestedBy;
},
allowsLabels() {
return this.currentBoard.allowsLabels;
},
allowsChecklists() {
return this.currentBoard.allowsChecklists;
},
allowsAttachments() {
return this.currentBoard.allowsAttachments;
},
allowsComments() {
return this.currentBoard.allowsComments;
},
isBoardSelected() {
return this.currentBoard.dateSettingsDefaultBoardID;
},
isNullBoardSelected() {
return (
this.currentBoard.dateSettingsDefaultBoardId === null ||
this.currentBoard.dateSettingsDefaultBoardId === undefined
);
},
boards() {
return Boards.find(
{
archived: false,
'members.userId': Meteor.userId(),
},
{
sort: ['title'],
},
);
},
lists() {
return Lists.find(
{
boardId: this.currentBoard._id,
archived: false,
},
{
sort: ['title'],
},
);
},
hasLists() {
return this.lists().count() > 0;
},
isListSelected() {
return (
this.currentBoard.dateSettingsDefaultBoardId === this.currentData()._id
);
},
events() {
return [
{
'click .js-field-has-receiveddate'(evt) {
evt.preventDefault();
this.currentBoard.allowsReceivedDate = !this.currentBoard
.allowsReceivedDate;
this.currentBoard.setAllowsReceivedDate(
this.currentBoard.allowsReceivedDate,
);
$(`.js-field-has-receiveddate ${MCB}`).toggleClass(
CKCLS,
this.currentBoard.allowsReceivedDate,
);
$('.js-field-has-receiveddate').toggleClass(
CKCLS,
this.currentBoard.allowsReceivedDate,
);
},
'click .js-field-has-startdate'(evt) {
evt.preventDefault();
this.currentBoard.allowsStartDate = !this.currentBoard
.allowsStartDate;
this.currentBoard.setAllowsStartDate(
this.currentBoard.allowsStartDate,
);
$(`.js-field-has-startdate ${MCB}`).toggleClass(
CKCLS,
this.currentBoard.allowsStartDate,
);
$('.js-field-has-startdate').toggleClass(
CKCLS,
this.currentBoard.allowsStartDate,
);
},
'click .js-field-has-enddate'(evt) {
evt.preventDefault();
this.currentBoard.allowsEndDate = !this.currentBoard.allowsEndDate;
this.currentBoard.setAllowsEndDate(this.currentBoard.allowsEndDate);
$(`.js-field-has-enddate ${MCB}`).toggleClass(
CKCLS,
this.currentBoard.allowsEndDate,
);
$('.js-field-has-enddate').toggleClass(
CKCLS,
this.currentBoard.allowsEndDate,
);
},
'click .js-field-has-duedate'(evt) {
evt.preventDefault();
this.currentBoard.allowsDueDate = !this.currentBoard.allowsDueDate;
this.currentBoard.setAllowsDueDate(this.currentBoard.allowsDueDate);
$(`.js-field-has-duedate ${MCB}`).toggleClass(
CKCLS,
this.currentBoard.allowsDueDate,
);
$('.js-field-has-duedate').toggleClass(
CKCLS,
this.currentBoard.allowsDueDate,
);
},
'click .js-field-has-subtasks'(evt) {
evt.preventDefault();
this.currentBoard.allowsSubtasks = !this.currentBoard.allowsSubtasks;
this.currentBoard.setAllowsSubtasks(this.currentBoard.allowsSubtasks);
$(`.js-field-has-subtasks ${MCB}`).toggleClass(
CKCLS,
this.currentBoard.allowsSubtasks,
);
$('.js-field-has-subtasks').toggleClass(
CKCLS,
this.currentBoard.allowsSubtasks,
);
},
'click .js-field-has-members'(evt) {
evt.preventDefault();
this.currentBoard.allowsMembers = !this.currentBoard.allowsMembers;
this.currentBoard.setAllowsMembers(this.currentBoard.allowsMembers);
$(`.js-field-has-members ${MCB}`).toggleClass(
CKCLS,
this.currentBoard.allowsMembers,
);
$('.js-field-has-members').toggleClass(
CKCLS,
this.currentBoard.allowsMembers,
);
},
'click .js-field-has-assignee'(evt) {
evt.preventDefault();
this.currentBoard.allowsAssignee = !this.currentBoard.allowsAssignee;
this.currentBoard.setAllowsAssignee(this.currentBoard.allowsAssignee);
$(`.js-field-has-assignee ${MCB}`).toggleClass(
CKCLS,
this.currentBoard.allowsAssignee,
);
$('.js-field-has-assignee').toggleClass(
CKCLS,
this.currentBoard.allowsAssignee,
);
},
'click .js-field-has-assigned-by'(evt) {
evt.preventDefault();
this.currentBoard.allowsAssignedBy = !this.currentBoard
.allowsAssignedBy;
this.currentBoard.setAllowsAssignedBy(
this.currentBoard.allowsAssignedBy,
);
$(`.js-field-has-assigned-by ${MCB}`).toggleClass(
CKCLS,
this.currentBoard.allowsAssignedBy,
);
$('.js-field-has-assigned-by').toggleClass(
CKCLS,
this.currentBoard.allowsAssignedBy,
);
},
'click .js-field-has-requested-by'(evt) {
evt.preventDefault();
this.currentBoard.allowsRequestedBy = !this.currentBoard
.allowsRequestedBy;
this.currentBoard.setAllowsRequestedBy(
this.currentBoard.allowsRequestedBy,
);
$(`.js-field-has-requested-by ${MCB}`).toggleClass(
CKCLS,
this.currentBoard.allowsRequestedBy,
);
$('.js-field-has-requested-by').toggleClass(
CKCLS,
this.currentBoard.allowsRequestedBy,
);
},
'click .js-field-has-labels'(evt) {
evt.preventDefault();
this.currentBoard.allowsLabels = !this.currentBoard.allowsLabels;
this.currentBoard.setAllowsLabels(this.currentBoard.allowsLabels);
$(`.js-field-has-labels ${MCB}`).toggleClass(
CKCLS,
this.currentBoard.allowsAssignee,
);
$('.js-field-has-labels').toggleClass(
CKCLS,
this.currentBoard.allowsLabels,
);
},
'click .js-field-has-checklists'(evt) {
evt.preventDefault();
this.currentBoard.allowsChecklists = !this.currentBoard
.allowsChecklists;
this.currentBoard.setAllowsChecklists(
this.currentBoard.allowsChecklists,
);
$(`.js-field-has-checklists ${MCB}`).toggleClass(
CKCLS,
this.currentBoard.allowsChecklists,
);
$('.js-field-has-checklists').toggleClass(
CKCLS,
this.currentBoard.allowsChecklists,
);
},
'click .js-field-has-attachments'(evt) {
evt.preventDefault();
this.currentBoard.allowsAttachments = !this.currentBoard
.allowsAttachments;
this.currentBoard.setAllowsAttachments(
this.currentBoard.allowsAttachments,
);
$(`.js-field-has-attachments ${MCB}`).toggleClass(
CKCLS,
this.currentBoard.allowsAttachments,
);
$('.js-field-has-attachments').toggleClass(
CKCLS,
this.currentBoard.allowsAttachments,
);
},
'click .js-field-has-comments'(evt) {
evt.preventDefault();
this.currentBoard.allowsComments = !this.currentBoard.allowsComments;
this.currentBoard.setAllowsComments(this.currentBoard.allowsComments);
$(`.js-field-has-comments ${MCB}`).toggleClass(
CKCLS,
this.currentBoard.allowsComments,
);
$('.js-field-has-comments').toggleClass(
CKCLS,
this.currentBoard.allowsComments,
);
},
},
];
},
}).register('boardCardSettingsPopup');
BlazeComponent.extendComponent({
onCreated() {
this.error = new ReactiveVar('');
@@ -602,7 +957,7 @@ BlazeComponent.extendComponent({
}).register('addMemberPopup');
Template.changePermissionsPopup.events({
'click .js-set-admin, click .js-set-normal, click .js-set-no-comments, click .js-set-comment-only'(
'click .js-set-admin, click .js-set-normal, click .js-set-no-comments, click .js-set-comment-only, click .js-set-worker'(
event,
) {
const currentBoard = Boards.findOne(Session.get('currentBoard'));
@@ -612,11 +967,13 @@ Template.changePermissionsPopup.events({
'js-set-comment-only',
);
const isNoComments = $(event.currentTarget).hasClass('js-set-no-comments');
const isWorker = $(event.currentTarget).hasClass('js-set-worker');
currentBoard.setMemberPermission(
memberId,
isAdmin,
isNoComments,
isCommentOnly,
isWorker,
);
Popup.back(1);
},
@@ -633,7 +990,8 @@ Template.changePermissionsPopup.helpers({
return (
!currentBoard.hasAdmin(this.userId) &&
!currentBoard.hasNoComments(this.userId) &&
!currentBoard.hasCommentOnly(this.userId)
!currentBoard.hasCommentOnly(this.userId) &&
!currentBoard.hasWorker(this.userId)
);
},
@@ -653,6 +1011,13 @@ Template.changePermissionsPopup.helpers({
);
},
isWorker() {
const currentBoard = Boards.findOne(Session.get('currentBoard'));
return (
!currentBoard.hasAdmin(this.userId) && currentBoard.hasWorker(this.userId)
);
},
isLastAdmin() {
const currentBoard = Boards.findOne(Session.get('currentBoard'));
return (
+1 -1
View File
@@ -109,7 +109,7 @@
color: darken(white, 40%)
.board-sidebar
width: 248px
width: 548px
right: -@width
transition: top .1s, right .1s, width .1s
+30 -24
View File
@@ -2,54 +2,60 @@ template(name="archivesSidebar")
if isArchiveReady.get
+basicTabs(tabs=tabs)
+tabContent(slug="cards")
p.quiet
a.js-restore-all-cards {{_ 'restore-all'}}
| -
a.js-delete-all-cards {{_ 'delete-all'}}
unless isWorker
p.quiet
a.js-restore-all-cards {{_ 'restore-all'}}
| -
a.js-delete-all-cards {{_ 'delete-all'}}
each archivedCards
.minicard-wrapper.js-minicard
+minicard(this)
if currentUser.isBoardMember
p.quiet
a.js-restore-card {{_ 'restore'}}
| -
a.js-delete-card {{_ 'delete'}}
unless isWorker
p.quiet
a.js-restore-card {{_ 'restore'}}
| -
a.js-delete-card {{_ 'delete'}}
if cardIsInArchivedList
p.quiet.small ({{_ 'warn-list-archived'}})
else
p.no-items-message {{_ 'no-archived-cards'}}
+tabContent(slug="lists")
p.quiet
a.js-restore-all-lists {{_ 'restore-all'}}
| -
a.js-delete-all-lists {{_ 'delete-all'}}
unless isWorker
p.quiet
a.js-restore-all-lists {{_ 'restore-all'}}
| -
a.js-delete-all-lists {{_ 'delete-all'}}
ul.archived-lists
each archivedLists
li.archived-lists-item
= title
if currentUser.isBoardMember
p.quiet
a.js-restore-list {{_ 'restore'}}
| -
a.js-delete-list {{_ 'delete'}}
unless isWorker
p.quiet
a.js-restore-list {{_ 'restore'}}
| -
a.js-delete-list {{_ 'delete'}}
else
li.no-items-message {{_ 'no-archived-lists'}}
+tabContent(slug="swimlanes")
p.quiet
a.js-restore-all-swimlanes {{_ 'restore-all'}}
| -
a.js-delete-all-swimlanes {{_ 'delete-all'}}
unless isWorker
p.quiet
a.js-restore-all-swimlanes {{_ 'restore-all'}}
| -
a.js-delete-all-swimlanes {{_ 'delete-all'}}
ul.archived-lists
each archivedSwimlanes
li.archived-lists-item
= title
if currentUser.isBoardMember
p.quiet
a.js-restore-swimlane {{_ 'restore'}}
| -
a.js-delete-swimlane {{_ 'delete'}}
unless isWorker
p.quiet
a.js-restore-swimlane {{_ 'restore'}}
| -
a.js-delete-swimlane {{_ 'delete'}}
else
li.no-items-message {{_ 'no-archived-swimlanes'}}
else
@@ -1,3 +1,4 @@
archivedRequested = false;
const subManager = new SubsManager();
BlazeComponent.extendComponent({
@@ -12,6 +13,7 @@ BlazeComponent.extendComponent({
const currentBoardId = Session.get('currentBoard');
if (!currentBoardId) return;
const handle = subManager.subscribe('board', currentBoardId, true);
archivedRequested = true;
Tracker.nonreactive(() => {
Tracker.autorun(() => {
this.isArchiveReady.set(handle.ready());
@@ -137,3 +139,12 @@ BlazeComponent.extendComponent({
];
},
}).register('archivesSidebar');
Template.archivesSidebar.helpers({
isWorker() {
const currentBoard = Boards.findOne(Session.get('currentBoard'));
return (
!currentBoard.hasAdmin(this.userId) && currentBoard.hasWorker(this.userId)
);
},
});
+28 -7
View File
@@ -4,6 +4,10 @@
and #each x in y constructors to fix this.
template(name="filterSidebar")
ul.sidebar-list
span {{_ 'list-filter-label'}}
form.js-list-filter
input(type="text")
ul.sidebar-list
li(class="{{#if Filter.labelIds.isSelected undefined}}active{{/if}}")
a.name.js-toggle-label-filter
@@ -56,6 +60,22 @@ template(name="filterSidebar")
if Filter.customFields.isSelected _id
i.fa.fa-check
hr
ul.sidebar-list
li(class="{{#if Filter.archive.isSelected _id}}active{{/if}}")
a.name.js-toggle-archive-filter
span.sidebar-list-item-description
| {{_ 'filter-show-archive'}}
if Filter.archive.isSelected _id
i.fa.fa-check
hr
ul.sidebar-list
li(class="{{#if Filter.hideEmpty.isSelected _id}}active{{/if}}")
a.name.js-toggle-hideEmpty-filter
span.sidebar-list-item-description
| {{_ 'filter-hide-empty'}}
if Filter.hideEmpty.isSelected _id
i.fa.fa-check
hr
span {{_ 'advanced-filter-label'}}
input.js-field-advanced-filter(type="text")
span {{_ 'advanced-filter-description'}}
@@ -97,13 +117,14 @@ template(name="multiselectionSidebar")
i.fa.fa-check
else if someSelectedElementHave 'member' _id
i.fa.fa-ellipsis-h
hr
a.sidebar-btn.js-move-selection
i.fa.fa-share
span {{_ 'move-selection'}}
a.sidebar-btn.js-archive-selection
i.fa.fa-archive
span {{_ 'archive-selection'}}
unless currentUser.isWorker
hr
a.sidebar-btn.js-move-selection
i.fa.fa-share
span {{_ 'move-selection'}}
a.sidebar-btn.js-archive-selection
i.fa.fa-archive
span {{_ 'archive-selection'}}
template(name="disambiguateMultiLabelPopup")
p {{_ 'what-to-do'}}
@@ -1,7 +1,13 @@
const subManager = new SubsManager();
BlazeComponent.extendComponent({
events() {
return [
{
'submit .js-list-filter'(evt) {
evt.preventDefault();
Filter.lists.set(this.find('.js-list-filter input').value.trim());
},
'click .js-toggle-label-filter'(evt) {
evt.preventDefault();
Filter.labelIds.toggle(this.currentData()._id);
@@ -12,6 +18,23 @@ BlazeComponent.extendComponent({
Filter.members.toggle(this.currentData()._id);
Filter.resetExceptions();
},
'click .js-toggle-archive-filter'(evt) {
evt.preventDefault();
Filter.archive.toggle(this.currentData()._id);
Filter.resetExceptions();
const currentBoardId = Session.get('currentBoard');
if (!currentBoardId) return;
subManager.subscribe(
'board',
currentBoardId,
Filter.archive.isSelected(),
);
},
'click .js-toggle-hideEmpty-filter'(evt) {
evt.preventDefault();
Filter.hideEmpty.toggle(this.currentData()._id);
Filter.resetExceptions();
},
'click .js-toggle-custom-fields-filter'(evt) {
evt.preventDefault();
Filter.customFields.toggle(this.currentData()._id);
@@ -2,6 +2,10 @@ template(name="searchSidebar")
form.js-search-term-form
input(type="text" name="searchTerm" placeholder="{{_ 'search-example'}}" autofocus dir="auto")
.list-body.js-perfect-scrollbar
.minilists.clearfix.js-minilists
each (lists)
a.minilist-wrapper.js-minilist(href=absoluteUrl)
+minilist(this)
.minicards.clearfix.js-minicards
each (results)
a.minicard-wrapper.js-minicard(href=absoluteUrl)
@@ -8,6 +8,11 @@ BlazeComponent.extendComponent({
return currentBoard.searchCards(this.term.get());
},
lists() {
const currentBoard = Boards.findOne(Session.get('currentBoard'));
return currentBoard.searchLists(this.term.get());
},
events() {
return [
{
@@ -16,6 +16,11 @@ template(name="swimlaneFixedHeader")
unless currentUser.isCommentOnly
a.fa.fa-plus.js-open-add-swimlane-menu.swimlane-header-plus-icon
a.fa.fa-navicon.js-open-swimlane-menu
unless isMiniScreen
if showDesktopDragHandles
a.swimlane-header-handle.handle.fa.fa-arrows.js-swimlane-header-handle
if isMiniScreen
a.swimlane-header-miniscreen-handle.handle.fa.fa-arrows.js-swimlane-header-handle
template(name="editSwimlaneTitleForm")
.list-composer
@@ -1,3 +1,5 @@
import { Cookies } from 'meteor/ostrio:cookies';
const cookies = new Cookies();
const { calculateIndexData } = Utils;
let swimlaneColors;
@@ -28,6 +30,19 @@ BlazeComponent.extendComponent({
},
}).register('swimlaneHeader');
Template.swimlaneHeader.helpers({
showDesktopDragHandles() {
currentUser = Meteor.user();
if (currentUser) {
return (currentUser.profile || {}).showDesktopDragHandles;
} else if (cookies.has('showDesktopDragHandles')) {
return true;
} else {
return false;
}
},
});
Template.swimlaneActionPopup.events({
'click .js-set-swimlane-color': Popup.open('setSwimlaneColor'),
'click .js-close-swimlane'(event) {
+34 -31
View File
@@ -1,24 +1,25 @@
template(name="swimlane")
.swimlane
+swimlaneHeader
.swimlane.js-lists.js-swimlane
if isMiniScreen
if currentListIsInThisSwimlane _id
+list(currentList)
unless currentList
unless collapseSwimlane
.swimlane.js-lists.js-swimlane
if isMiniScreen
if currentListIsInThisSwimlane _id
+list(currentList)
unless currentList
each lists
+miniList(this)
if currentUser.isBoardMember
unless currentUser.isCommentOnly
+addListForm
else
each lists
+miniList(this)
+list(this)
if currentCardIsInThisList _id ../_id
+cardDetails(currentCard)
if currentUser.isBoardMember
unless currentUser.isCommentOnly
+addListForm
else
each lists
+list(this)
if currentCardIsInThisList _id ../_id
+cardDetails(currentCard)
if currentUser.isBoardMember
unless currentUser.isCommentOnly
+addListForm
template(name="listsGroup")
.swimlane.list-group.js-lists
@@ -33,7 +34,8 @@ template(name="listsGroup")
+addListForm
else
each lists
+list(this)
if visible this
+list(this)
if currentCardIsInThisList _id null
+cardDetails(currentCard)
if currentUser.isBoardMember
@@ -41,19 +43,20 @@ template(name="listsGroup")
+addListForm
template(name="addListForm")
.list.list-composer.js-list-composer
.list-header-add
+inlinedForm(autoclose=false)
input.list-name-input.full-line(type="text" placeholder="{{_ 'add-list'}}"
autocomplete="off" autofocus)
.edit-controls.clearfix
button.primary.confirm(type="submit") {{_ 'save'}}
unless currentBoard.isTemplatesBoard
unless currentBoard.isTemplateBoard
span.quiet
| {{_ 'or'}}
a.js-list-template {{_ 'template'}}
else
a.open-list-composer.js-open-inlined-form
i.fa.fa-plus
| {{_ 'add-list'}}
unless currentUser.isWorker
.list.list-composer.js-list-composer(class="{{#if isMiniScreen}}mini-list{{/if}}")
.list-header-add
+inlinedForm(autoclose=false)
input.list-name-input.full-line(type="text" placeholder="{{_ 'add-list'}}"
autocomplete="off" autofocus)
.edit-controls.clearfix
button.primary.confirm(type="submit") {{_ 'save'}}
unless currentBoard.isTemplatesBoard
unless currentBoard.isTemplateBoard
span.quiet
| {{_ 'or'}}
a.js-list-template {{_ 'template'}}
else
a.open-list-composer.js-open-inlined-form
i.fa.fa-plus
| {{_ 'add-list'}}
+106 -14
View File
@@ -1,3 +1,5 @@
import { Cookies } from 'meteor/ostrio:cookies';
const cookies = new Cookies();
const { calculateIndex, enableClickOnTouch } = Utils;
function currentListIsInThisSwimlane(swimlaneId) {
@@ -14,7 +16,7 @@ function currentCardIsInThisList(listId, swimlaneId) {
if (
currentUser &&
currentUser.profile &&
currentUser.profile.boardView === 'board-view-swimlanes'
Utils.boardView() === 'board-view-swimlanes'
)
return (
currentCard &&
@@ -56,7 +58,6 @@ function initSortable(boardComponent, $listsDom) {
$listsDom.sortable({
tolerance: 'pointer',
helper: 'clone',
handle: '.js-list-header',
items: '.js-list:not(.js-list-composer)',
placeholder: 'list placeholder',
distance: 7,
@@ -93,19 +94,64 @@ function initSortable(boardComponent, $listsDom) {
return (
Meteor.user() &&
Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly()
!Meteor.user().isCommentOnly() &&
!Meteor.user().isWorker()
);
}
// Disable drag-dropping while in multi-selection mode, or if the current user
// is not a board member
boardComponent.autorun(() => {
let showDesktopDragHandles = false;
currentUser = Meteor.user();
if (currentUser) {
showDesktopDragHandles = (currentUser.profile || {})
.showDesktopDragHandles;
} else if (cookies.has('showDesktopDragHandles')) {
showDesktopDragHandles = true;
} else {
showDesktopDragHandles = false;
}
if (!Utils.isMiniScreen() && showDesktopDragHandles) {
$listsDom.sortable({
handle: '.js-list-handle',
});
} else if (!Utils.isMiniScreen() && !showDesktopDragHandles) {
$listsDom.sortable({
handle: '.js-list-header',
});
}
const $listDom = $listsDom;
if ($listDom.data('sortable')) {
$listsDom.sortable(
'option',
'disabled',
MultiSelection.isActive() || !userIsMember(),
// Disable drag-dropping when user is not member/is worker/is miniscreen
!userIsMember(),
// Not disable drag-dropping while in multi-selection mode
// MultiSelection.isActive() || !userIsMember(),
);
}
if ($listDom.data('sortable')) {
$listsDom.sortable(
'option',
'disabled',
// Disable drag-dropping when user is not member/is worker/is miniscreen
Meteor.user().isWorker(),
// Not disable drag-dropping while in multi-selection mode
// MultiSelection.isActive() || !userIsMember(),
);
}
if ($listDom.data('sortable')) {
$listsDom.sortable(
'option',
'disabled',
// Disable drag-dropping when user is not member/is worker/is miniscreen
Utils.isMiniScreen(),
// Not disable drag-dropping while in multi-selection mode
// MultiSelection.isActive() || !userIsMember(),
);
}
});
@@ -151,13 +197,25 @@ BlazeComponent.extendComponent({
// define a list of elements in which we disable the dragging because
// the user will legitimately expect to be able to select some text with
// his mouse.
const noDragInside = [
'a',
'input',
'textarea',
'p',
'.js-list-header',
];
let showDesktopDragHandles = false;
currentUser = Meteor.user();
if (currentUser) {
showDesktopDragHandles = (currentUser.profile || {})
.showDesktopDragHandles;
} else if (cookies.has('showDesktopDragHandles')) {
showDesktopDragHandles = true;
} else {
showDesktopDragHandles = false;
}
const noDragInside = ['a', 'input', 'textarea', 'p'].concat(
Utils.isMiniScreen() ||
(!Utils.isMiniScreen() && showDesktopDragHandles)
? ['.js-list-handle', '.js-swimlane-header-handle']
: ['.js-list-header'],
);
if (
$(evt.target).closest(noDragInside.join(',')).length === 0 &&
this.$('.swimlane').prop('clientHeight') > evt.offsetY
@@ -233,11 +291,22 @@ BlazeComponent.extendComponent({
}).register('addListForm');
Template.swimlane.helpers({
showDesktopDragHandles() {
currentUser = Meteor.user();
if (currentUser) {
return (currentUser.profile || {}).showDesktopDragHandles;
} else if (cookies.has('showDesktopDragHandles')) {
return true;
} else {
return false;
}
},
canSeeAddList() {
return (
Meteor.user() &&
Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly()
!Meteor.user().isCommentOnly() &&
!Meteor.user().isWorker()
);
},
});
@@ -246,6 +315,29 @@ BlazeComponent.extendComponent({
currentCardIsInThisList(listId, swimlaneId) {
return currentCardIsInThisList(listId, swimlaneId);
},
visible(list) {
if (list.archived) {
// Show archived list only when filter archive is on or archive is selected
if (!(Filter.archive.isSelected() || archivedRequested)) {
return false;
}
}
if (Filter.lists._isActive()) {
if (!list.title.match(Filter.lists.getRegexSelector())) {
return false;
}
}
if (Filter.hideEmpty.isSelected()) {
const swimlaneId = this.parentComponent()
.parentComponent()
.data()._id;
const cards = list.cards(swimlaneId);
if (cards.count() === 0) {
return false;
}
}
return true;
},
onRendered() {
const boardComponent = this.parentComponent();
const $listsDom = this.$('.js-lists');
+65 -13
View File
@@ -1,5 +1,41 @@
@import 'nib'
/*
// Minimize swimlanes start https://www.w3schools.com/howto/howto_js_accordion.asp
.accordion
cursor: pointer
width: 30px
height: 20px
border: none
outline: none
font-size: 18px
transition: 0.4s
padding-top: 0px
margin-top: 0px
.accordion:after
// Unicode triagle right:
content: '\25B6'
color: #777
font-weight: bold
float: left
.active:after
// Unicode triangle down:
content: '\25BC'
.panel
width: 100%
max-height: 0
overflow: hidden
transition: max-height 0.2s ease-out
margin: 0px
padding: 0px
// Minimize swimlanes end https://www.w3schools.com/howto/howto_js_accordion.asp
*/
.swimlane
// Even if this background color is the same as the body we can't leave it
// transparent, because that won't work during a swimlane drag.
@@ -25,22 +61,22 @@
cursor: grabbing
.swimlane-header-wrap
display: flex;
flex-direction: row;
flex: 1 0 100%;
background-color: #ccc;
display: flex
flex-direction: row
flex: 1 0 100%
background-color: #ccc
.swimlane-header
font-size: 14px;
font-size: 14px
padding: 5px 5px
font-weight: bold;
min-height: 9px;
width: 100%;
overflow: hidden;
-o-text-overflow: ellipsis;
text-overflow: ellipsis;
word-wrap: break-word;
text-align: center;
font-weight: bold
min-height: 9px
width: 100%
overflow: hidden
-o-text-overflow: ellipsis
text-overflow: ellipsis
word-wrap: break-word
text-align: center
.swimlane-header-menu
position: absolute
@@ -50,6 +86,22 @@
margin-left: 5px
margin-right: 10px
.swimlane-header-handle
position: absolute
padding: 7px
top: 50%
transform: translateY(-50%)
left: 230px
font-size: 18px
.swimlane-header-miniscreen-handle
position: absolute
padding: 7px
top: 50%
transform: translateY(-50%)
left: 87vw
font-size: 24px
.list-group
height: 100%
+1
View File
@@ -73,6 +73,7 @@ template(name="cardMemberPopup")
p.quiet @{{ user.username }}
ul.pop-over-list
if currentUser.isNotCommentOnly
if currentUser.isNotWorker
li: a.js-remove-member {{_ 'remove-member-from-card'}}
if $eq currentUser._id user._id
+51 -17
View File
@@ -13,21 +13,46 @@ template(name="headerUserBar")
template(name="memberMenuPopup")
ul.pop-over-list
with currentUser
li: a.js-edit-profile {{_ 'edit-profile'}}
li: a.js-change-settings {{_ 'change-settings'}}
li: a.js-change-avatar {{_ 'edit-avatar'}}
li
a.js-edit-profile
i.fa.fa-user
| {{_ 'edit-profile'}}
li
a.js-change-settings
i.fa.fa-cog
| {{_ 'change-settings'}}
li
a.js-change-avatar
i.fa.fa-picture-o
| {{_ 'edit-avatar'}}
unless isSandstorm
li: a.js-change-password {{_ 'changePasswordPopup-title'}}
li: a.js-change-language {{_ 'changeLanguagePopup-title'}}
li
a.js-change-password
i.fa.fa-key
| {{_ 'changePasswordPopup-title'}}
li
a.js-change-language
i.fa.fa-flag
| {{_ 'changeLanguagePopup-title'}}
if currentUser.isAdmin
li: a.js-go-setting(href="{{pathFor 'setting'}}") {{_ 'admin-panel'}}
hr
ul.pop-over-list
li: a(href="{{pathFor 'board' id=templatesBoardId slug=templatesBoardSlug}}") {{_ 'templates'}}
li
a.js-go-setting(href="{{pathFor 'setting'}}")
i.fa.fa-lock
| {{_ 'admin-panel'}}
unless currentUser.isWorker
hr
ul.pop-over-list
li
a(href="{{pathFor 'board' id=templatesBoardId slug=templatesBoardSlug}}")
i.fa.fa-clone
| {{_ 'templates'}}
unless isSandstorm
hr
ul.pop-over-list
li: a.js-logout {{_ 'log-out'}}
li
a.js-logout
i.fa.fa-sign-out
| {{_ 'log-out'}}
template(name="editProfilePopup")
form
@@ -75,16 +100,25 @@ template(name="changeSettingsPopup")
ul.pop-over-list
li
a.js-toggle-system-messages
i.fa.fa-comments-o
| {{_ 'hide-system-messages'}}
if hiddenSystemMessages
i.fa.fa-check
li
label.bold
| {{_ 'show-cards-minimum-count'}}
input#show-cards-count-at.inline-input.left(type="number" value="#{showCardsCountAt}" min="0" max="99" onkeydown="return false")
input.js-apply-show-cards-at.left(type="submit" value="{{_ 'apply'}}")
a.js-toggle-desktop-drag-handles
i.fa.fa-arrows
| {{_ 'show-desktop-drag-handles'}}
if showDesktopDragHandles
i.fa.fa-check
unless currentUser.isWorker
li
label.bold
i.fa.fa-sort-numeric-asc
| {{_ 'show-cards-minimum-count'}}
input#show-cards-count-at.inline-input.left(type="number" value="#{showCardsCountAt}" min="0" max="99" onkeydown="return false")
input.js-apply-show-cards-at.left(type="submit" value="{{_ 'apply'}}")
template(name="userDeletePopup")
p {{_ 'delete-user-confirm-popup'}}
button.js-confirm.negate.full(type="submit") {{_ 'delete'}}
unless currentUser.isWorker
p {{_ 'delete-user-confirm-popup'}}
button.js-confirm.negate.full(type="submit") {{_ 'delete'}}
+86 -9
View File
@@ -1,3 +1,6 @@
import { Cookies } from 'meteor/ostrio:cookies';
const cookies = new Cookies();
Template.headerUserBar.events({
'click .js-open-header-member-menu': Popup.open('memberMenu'),
'click .js-change-avatar': Popup.open('changeAvatar'),
@@ -5,10 +8,22 @@ Template.headerUserBar.events({
Template.memberMenuPopup.helpers({
templatesBoardId() {
return Meteor.user().getTemplatesBoardId();
currentUser = Meteor.user();
if (currentUser) {
return Meteor.user().getTemplatesBoardId();
} else {
// No need to getTemplatesBoardId on public board
return false;
}
},
templatesBoardSlug() {
return Meteor.user().getTemplatesBoardSlug();
currentUser = Meteor.user();
if (currentUser) {
return Meteor.user().getTemplatesBoardSlug();
} else {
// No need to getTemplatesBoardSlug() on public board
return false;
}
},
});
@@ -30,13 +45,31 @@ Template.memberMenuPopup.events({
Template.editProfilePopup.helpers({
allowEmailChange() {
return AccountSettings.findOne('accounts-allowEmailChange').booleanValue;
Meteor.call('AccountSettings.allowEmailChange', (_, result) => {
if (result) {
return true;
} else {
return false;
}
});
},
allowUserNameChange() {
return AccountSettings.findOne('accounts-allowUserNameChange').booleanValue;
Meteor.call('AccountSettings.allowUserNameChange', (_, result) => {
if (result) {
return true;
} else {
return false;
}
});
},
allowUserDelete() {
return AccountSettings.findOne('accounts-allowUserDelete').booleanValue;
Meteor.call('AccountSettings.allowUserDelete', (_, result) => {
if (result) {
return true;
} else {
return false;
}
});
},
});
@@ -161,17 +194,56 @@ Template.changeLanguagePopup.events({
});
Template.changeSettingsPopup.helpers({
showDesktopDragHandles() {
currentUser = Meteor.user();
if (currentUser) {
return (currentUser.profile || {}).showDesktopDragHandles;
} else if (cookies.has('showDesktopDragHandles')) {
return true;
} else {
return false;
}
},
hiddenSystemMessages() {
return Meteor.user().hasHiddenSystemMessages();
currentUser = Meteor.user();
if (currentUser) {
return (currentUser.profile || {}).hasHiddenSystemMessages;
} else if (cookies.has('hasHiddenSystemMessages')) {
return true;
} else {
return false;
}
},
showCardsCountAt() {
return Meteor.user().getLimitToShowCardsCount();
currentUser = Meteor.user();
if (currentUser) {
return Meteor.user().getLimitToShowCardsCount();
} else {
return cookies.get('limitToShowCardsCount');
}
},
});
Template.changeSettingsPopup.events({
'click .js-toggle-desktop-drag-handles'() {
currentUser = Meteor.user();
if (currentUser) {
Meteor.call('toggleDesktopDragHandles');
} else if (cookies.has('showDesktopDragHandles')) {
cookies.remove('showDesktopDragHandles');
} else {
cookies.set('showDesktopDragHandles', 'true');
}
},
'click .js-toggle-system-messages'() {
Meteor.call('toggleSystemMessages');
currentUser = Meteor.user();
if (currentUser) {
Meteor.call('toggleSystemMessages');
} else if (cookies.has('hasHiddenSystemMessages')) {
cookies.remove('hasHiddenSystemMessages');
} else {
cookies.set('hasHiddenSystemMessages', 'true');
}
},
'click .js-apply-show-cards-at'(event, templateInstance) {
event.preventDefault();
@@ -180,7 +252,12 @@ Template.changeSettingsPopup.events({
10,
);
if (!isNaN(minLimit)) {
Meteor.call('changeLimitToShowCardsCount', minLimit);
currentUser = Meteor.user();
if (currentUser) {
Meteor.call('changeLimitToShowCardsCount', minLimit);
} else {
cookies.set('limitToShowCardsCount', minLimit);
}
Popup.back();
}
},
Loaded 100 of 227 files, more files were not shown because too many files have changed in this diff. Show more