Compare commits

..
301 Commits
Author SHA1 Message Date
Lauri Ojansivu d0c417b17d v5.34 2021-06-11 15:35:13 +03:00
Lauri Ojansivu d09151d8f3 Updated translations. 2021-06-11 15:22:55 +03:00
Lauri Ojansivu 01a28860fe Updated ChangeLog. 2021-06-11 15:19:45 +03:00
Lauri Ojansivu 488b765f95 Added remaining WAIT_SPINNER settings.
Thanks to xet7 !

Related https://github.com/wekan/wekan/pull/3858
2021-06-11 15:17:43 +03:00
Lauri Ojansivu 8a21b69fb0 Updated translations. 2021-06-11 14:52:31 +03:00
Lauri Ojansivu eda3ed59df Updated ChangeLog. 2021-06-11 14:48:09 +03:00
Lauri Ojansivu 611ac0ab57 Merge pull request #3858 from mfilser/more_spinners_and_configureable_in_admin_panel
More spinners + configureable in admin panel
2021-06-11 14:42:38 +03:00
Martin Filser d485154a12 Spinner Double-Bounce now available
Spinner from: https://tobiasahlin.com/spinkit/
2021-06-11 13:34:33 +02:00
Martin Filser 0caff7d5d5 Spinner Cube-Grid now available
Spinner from: https://tobiasahlin.com/spinkit/
2021-06-11 13:34:33 +02:00
Martin Filser 6df7657c8e Spinner Scaleout now available
Spinner from: https://tobiasahlin.com/spinkit/
2021-06-11 13:34:33 +02:00
Martin Filser 55d0f0bacd Spinner Rotateplane now available
Spinner from: https://tobiasahlin.com/spinkit/
2021-06-11 13:34:33 +02:00
Martin Filser a74fac4cbf Spinner Dot now available
Spinner from: https://tobiasahlin.com/spinkit/
2021-06-11 13:34:33 +02:00
Martin Filser 8aa58c0b11 Spinner Cube now available
Spinner from: https://tobiasahlin.com/spinkit/
2021-06-11 13:34:33 +02:00
Martin Filser 4b9f8ab2b1 Adding DEFAULT_WAIT_SPINNER to all environments 2021-06-11 13:34:33 +02:00
Martin Filser ba41533128 Default Spinner can be configured through environment variables 2021-06-11 13:34:33 +02:00
Martin Filser 38069c6d63 Spinner description insert in translation 2021-06-11 13:34:33 +02:00
Martin Filser 1d66058240 Spinner class in own .js library file 2021-06-11 13:34:33 +02:00
Martin Filser 43ac328e8a Spinner config is now reactive
- changed at boards as soon as changed in the settings
2021-06-11 13:34:33 +02:00
Martin Filser 7519abf3fe Spinner now configureable in the Settings 2021-06-11 13:34:33 +02:00
Martin Filser 849deae11e Spinners needs  
- it's not formatted by Meteor well, \n is missing in the generated
  Javascript code, so i insert   in the template
2021-06-11 13:34:33 +02:00
Martin Filser 06a2b08780 Spinner configureable in the source code 2021-06-11 13:34:33 +02:00
Martin Filser 4f9d3d8be5 List infinite scroll now always works
- before sometimes list scrolling stopped even the spinner was visible
  and scrolling was at the end of the list
2021-06-11 13:34:33 +02:00
Lauri Ojansivu ac3d658cb1 Merge pull request #3857 from mfilser/view_and_change_card_sort_number
View and change card sort number
2021-06-11 14:33:43 +03:00
Lauri Ojansivu 8f00191617 Updated ChangeLog. 2021-06-11 14:30:19 +03:00
Lauri Ojansivu 762391965e Try to fix BUG: Database error attempting to change a account.
Thanks to bbyszio and xet7 !

Fixes #3855
2021-06-11 14:28:25 +03:00
Lauri Ojansivu 0fa3a2729d Updated ChangeLog. 2021-06-11 13:55:33 +03:00
Martin Filser 54640bb125 Minicard, show sorting number if board config is enabled 2021-06-11 12:35:57 +02:00
Martin Filser 57d35a294d CardDetails sorting, only float number input possible (only working in an extra handler)
- defining in events() doesn't handle the return value and is ignored,
  maybe a bug in Blaze / Meteor.?
2021-06-11 12:35:57 +02:00
Martin Filser 39069b322d CardDetails sorting, only float number input possible 2021-06-11 12:35:57 +02:00
Martin Filser 540ce3a6ed CardDetails sorting number is displayed at card and can be modified (if configured) 2021-06-11 12:35:57 +02:00
Lauri Ojansivu ab22fb27ce Updated ChangeLog. 2021-06-11 13:35:06 +03:00
Lauri Ojansivu 0f564365f4 Merge pull request #3856 from mfilser/custom_fields_missing_after_adding_first_card
CustomFields were not created after adding 1 card
2021-06-11 13:33:26 +03:00
Lauri Ojansivu cccb7e4a39 Updated ChangeLog. 2021-06-11 13:16:20 +03:00
Lauri Ojansivu 1e837dec11 Fix Google SSO to access Wekan has not been working
by reverting Wekan v5.31 not-working fixes to OAUTH2_LOGIN_STYLE=redirect Has No Effect.

Thanks to unpokitodxfavor and xet7 !

Fixes #3854,
opens #3845
2021-06-11 13:08:23 +03:00
Martin Filser be3325ce35 CustomFields were not created after adding 1 card 2021-06-11 12:03:29 +02:00
Lauri Ojansivu c042d54714 Updated ChangeLog. 2021-06-11 12:59:20 +03:00
Lauri Ojansivu 77e2f95c66 Merge pull request #3852 from mfilser/development_in_docker_container
Development in docker container
2021-06-11 12:57:49 +03:00
Martin Filser 403b416b2a Development volumes now in the same folder
- It's easier for the development to have the files in the same folder
  than in an extra volume
2021-06-11 11:36:55 +02:00
Martin Filser b0901ccd01 Development uses now the same Mongo Version (4.4) 2021-06-11 11:36:55 +02:00
Martin Filser 6d2f8a97af Base Image of Development and Build are now the same
- libarchive-tools contains bsdtar. bsdtar is needed for meteor
  installation, it doesn't / or didn't work with docker and overlayfs.

  see also here:

  https://github.com/jshimko/meteor-launchpad/issues/39
  https://github.com/anchore/anchore-engine/issues/106
  https://github.com/coreos/bugs/issues/1095
2021-06-11 11:36:55 +02:00
Martin Filser bbd3324b79 ignore .git directory at docker build
- COPY copies the .git directory to the image. The docker build then
  stops with this error:

=> Errors while initializing project:

While building package wekan-cfs-data-man:
error: couldn't install npm package request@https://github.com/wekan/request:
Command failed:
/home/wekan/.meteor/packages/meteor-tool/.2.2.0.1j8auib.qcbe++os.linux.x86_64+web.browser+web.browser.legacy+web.cordova/mt-os.linux.x86_64/dev_bundle/bin/npm
install https://github.com/wekan/request
npm ERR! Error while executing:
npm ERR! /usr/bin/git ls-remote -h -t https://github.com/wekan/request.git
npm ERR!
npm ERR! fatal: not a git repository: /home/wekan/app/../.git/modules/src
npm ERR!
npm ERR! exited with error code: 128

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/wekan/.npm/_logs/2021-06-10T12_54_02_777Z-debug.log
npm ERR! Error while executing:
npm ERR! /usr/bin/git ls-remote -h -t https://github.com/wekan/request.git
npm ERR!
npm ERR! fatal: not a git repository: /home/wekan/app/../.git/modules/src
npm ERR!
npm ERR! exited with error code: 128

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/wekan/.npm/_logs/2021-06-10T12_54_02_777Z-debug.log
2021-06-11 11:36:54 +02:00
Martin Filser 5697b89817 .devcontainer now ready for Wekan Development in Docker Container
Check .devcontainer/docker-compose.yml to fit your environment

Enter this commands to start Wekan Development:

$ cd .devcontainer
$ docker-compose build
$ docker-compose up -d

Visit localhost:3000 or whatever you entered in ROOT_URL

Code changes triggers a rebuild / redeploy
2021-06-11 11:36:40 +02:00
Lauri Ojansivu 6bf6bcb192 Revert upgrading Dockerfile to hirsute, because of bug:
https://github.com/wekan/wekan/pull/3852#issuecomment-859095850

Thanks to xet7 !
2021-06-11 00:43:45 +03:00
Lauri Ojansivu df74b31541 Try different gpg package for Docker and Hirsute.
Thanks to xet7 !
2021-06-11 00:34:27 +03:00
Lauri Ojansivu 0b34bcdfa8 Updated Dockerfile gpg to gpgv2, that package is in hirsute.
Thanks to xet7 !
2021-06-11 00:26:47 +03:00
Lauri Ojansivu 09a060a57b Updated ChangeLog. 2021-06-10 23:31:59 +03:00
Lauri Ojansivu 2a5147a0c1 Updated Docker Ubuntu base image to newest hirsute.
Thanks to xet7 !
2021-06-10 23:30:13 +03:00
Lauri Ojansivu 4caa0e2f2e Updated ChangeLog. 2021-06-10 23:06:49 +03:00
Lauri Ojansivu 8f5c0af88e Merge pull request #3851 from mfilser/card_description_same_color_editing
Card Description has now the same color on editing
2021-06-10 23:04:46 +03:00
Martin Filser 665d7eb831 Card Description has now the same color on editing 2021-06-10 21:57:10 +02:00
Lauri Ojansivu d08b208bc6 v5.33 2021-06-10 22:10:34 +03:00
Lauri Ojansivu c5d8117052 Updated translations. 2021-06-10 22:03:45 +03:00
Lauri Ojansivu b769d18f73 Updated ChangeLog. 2021-06-10 22:01:14 +03:00
Lauri Ojansivu b17760d228 Merge branch 'Emile840-master' 2021-06-10 21:56:33 +03:00
Lauri Ojansivu 2983c51db1 Merge branch 'master' of https://github.com/Emile840/wekan into Emile840-master 2021-06-10 21:54:41 +03:00
Lauri Ojansivu d6e11940cc Updated ChangeLog. 2021-06-10 21:52:05 +03:00
Lauri Ojansivu 17f15f546f Merge pull request #3849 from mfilser/custom-field-stringtemplate_autofocus
custom fields stringtemplate, autofocus the last input box
2021-06-10 21:49:48 +03:00
Emile NDAGIJIMANA c5df78fd8b Addressed one of the point cited in issue #802 'Assigning a user to a team or an organization' 2021-06-10 16:38:28 +02:00
Martin Filser 62bd96e06e custom fields stringtemplate, autofocus the last input box 2021-06-09 21:25:49 +02:00
Lauri Ojansivu 399fd098b8 v5.32 2021-06-09 20:30:02 +03:00
Lauri Ojansivu f9c21a9b52 Updated translations. 2021-06-09 20:23:38 +03:00
Lauri Ojansivu 41893cf06a Updated ChangeLog. 2021-06-09 20:15:46 +03:00
Lauri Ojansivu 83a8d4c14f Updated ChangeLog. 2021-06-09 20:13:19 +03:00
Lauri Ojansivu 6ce5ab40a7 Moved many button texts etc to tooltips. Added more tooltips.
Thanks to xet7 !

Fixes #3848
2021-06-09 20:08:42 +03:00
Lauri Ojansivu 328fa194a0 v5.31 2021-06-09 01:45:05 +03:00
Lauri Ojansivu 482b9c9333 Updated ChangeLog. 2021-06-09 01:40:39 +03:00
Lauri Ojansivu 122757ca9c Updated dependencies.
Thanks to developers of dependencies !
2021-06-09 01:39:16 +03:00
Lauri Ojansivu 49dd2204f4 Updated ChangeLog. 2021-06-09 01:16:38 +03:00
Lauri Ojansivu 07a3a0b388 Gantt: Retain links created between tasks. Part 1: Database changes, not active in MIT Wekan.
Thanks to benjaminhrivera !

Related https://github.com/wekan/wekan/issues/2870#issuecomment-857115753
2021-06-09 01:12:23 +03:00
Lauri Ojansivu 56efbf1de7 Updated ChangeLog. 2021-06-09 00:57:27 +03:00
Lauri Ojansivu 57f31d443f Partial Fix: Vote and Planning Poker: Setting date and time now works
for some languages that have ascii characters in date format.

Thanks to xet7 !

Related #3837
2021-06-09 00:51:52 +03:00
Lauri Ojansivu 65f308434a Updated ChangeLog. 2021-06-08 17:02:25 +03:00
Lauri Ojansivu 007e0f1c16 Try to fix: Wekan UI fails to finish import of closed Trello boards.
Thanks to berezovskyi and xet7 !

Fixes #3840
2021-06-08 17:00:53 +03:00
Lauri Ojansivu c249a2d187 Updated ChangeLog. 2021-06-08 16:53:06 +03:00
Lauri Ojansivu 78324263c1 Try to fix OAUTH2_LOGIN_STYLE=redirect Has No Effect.
Thanks to xet7 !

Fixes #3845
2021-06-08 16:50:48 +03:00
Lauri Ojansivu c20229b56a Updated translations. 2021-06-08 16:50:29 +03:00
Lauri Ojansivu 1fd10771b0 Updated ChangeLog. 2021-06-08 16:41:14 +03:00
Lauri Ojansivu 1637920170 Admin Panel Organizations/Teams: Show confirm text above delete button.
Thanks to xet7 !

Related #802
2021-06-08 16:38:21 +03:00
Lauri Ojansivu 87ba966db8 Updated translations. 2021-06-08 05:25:01 +03:00
Lauri Ojansivu 258391cd8c Updated ChangeLog. 2021-06-08 05:15:25 +03:00
Lauri Ojansivu 481404e8d7 Now new boards do not have any labels added by default.
Thanks to tedkoch and xet7 !

Related https://github.com/wekan/wekan/discussions/3835#discussioncomment-836223 Issue 7.
2021-06-08 05:07:32 +03:00
Lauri Ojansivu 48737dcd01 Updated ChangeLog. 2021-06-08 04:54:42 +03:00
Lauri Ojansivu b95585b0f6 Updated ChangeLog. 2021-06-08 04:44:59 +03:00
Lauri Ojansivu 14b2c1309f Delete Organizations and Teams.
Thanks to xet7 !

Related #802
2021-06-08 04:38:47 +03:00
Lauri Ojansivu 7e036b9df3 Merge branch 'Emile840-master' 2021-06-08 01:24:54 +03:00
Lauri Ojansivu 72fa32151d Merge branch 'master' of https://github.com/Emile840/wekan into Emile840-master 2021-06-08 01:24:25 +03:00
Emile NDAGIJIMANA f5dbe8c6f6 Addressed one of the bugs cited in issue #802 'bug when creating and modifying organization and team' 2021-06-07 11:03:49 +02:00
Lauri Ojansivu 93950a3b2e Updated translations. 2021-06-05 16:02:59 +03:00
Lauri Ojansivu eec818f9fd Updated translations. 2021-06-04 12:52:54 +03:00
Lauri Ojansivu a6f15af270 Updated ChangeLog. 2021-06-04 12:48:03 +03:00
Lauri Ojansivu 646497c3f0 Removed extra package.
Thanks to xet7 !
2021-06-04 12:46:21 +03:00
Lauri Ojansivu 2342c27ced v5.30 2021-06-03 23:29:01 +03:00
Lauri Ojansivu 80f6004404 Updated translations. 2021-06-03 23:20:51 +03:00
Lauri Ojansivu a94946c77c Updated ChangeLog. 2021-06-03 23:18:12 +03:00
Lauri Ojansivu 659a65b8b9 Moved Keyboard Shortcuts from bottom to top of sidebar.
Thanks to ClaudiaK21 and xet7 !
2021-06-03 23:15:13 +03:00
Lauri Ojansivu c4b55e862f Updated ChangeLog. 2021-06-03 22:33:04 +03:00
Lauri Ojansivu f8a75930b3 Updated ChangeLog. 2021-06-03 22:09:05 +03:00
Lauri Ojansivu 3a04b1b787 Updated ChangeLog. 2021-06-03 21:49:13 +03:00
Lauri Ojansivu ad61a15611 Updated translations. 2021-06-03 21:47:52 +03:00
Lauri Ojansivu d21bc0c277 Merge branch 'helioguardabaxo-master' 2021-06-03 21:26:55 +03:00
helioguardabaxo fa3d117372 Add Feature: Planning Poker 2021-06-03 12:01:15 -03:00
Lauri Ojansivu 26249f5928 Updated ChangeLog. 2021-06-01 18:55:25 +03:00
Lauri Ojansivu b7e6600e88 Merge pull request #3833 from Emile840/master
Fix bug #3832 'Default language is still used although this one has b…
2021-06-01 18:53:25 +03:00
Emile NDAGIJIMANA 6e46cad56c Fix bug #3832 'Default language is still used although this one has been modified previously' 2021-06-01 15:55:42 +02:00
Lauri Ojansivu e7340c5021 Reverted PR https://github.com/wekan/wekan/pull/3831
The default wekan logo appears briefly before displaying the custom logo

Thanks to xet7 !

Related #3832
2021-06-01 16:44:23 +03:00
Lauri Ojansivu 05d6c5f15a Updated translations. 2021-06-01 16:28:49 +03:00
Lauri Ojansivu 428442b310 Updated ChangeLog. 2021-06-01 16:20:52 +03:00
Lauri Ojansivu 753d247cf6 Merge pull request #3831 from Emile840/master
Fix bug #3830 '[Login page] The default wekan logo appears briefly before d…
2021-06-01 16:16:04 +03:00
Lauri Ojansivu 802f544cd5 Updated ChangeLog. 2021-06-01 12:46:35 +03:00
Lauri Ojansivu bf62a947fb Fixed Python API example: Edit card, etc.
Thanks to Lucky-Shi and xet7 !

Fixes #3767
2021-06-01 12:41:54 +03:00
Emile NDAGIJIMANA 5b08ae12a6 Fix bug '[Login page] The default wekan logo appears briefly before displaying the custom logo #3830' 2021-05-31 15:36:57 +02:00
Lauri Ojansivu 46e881bea3 v5.29 2021-05-29 01:06:44 +03:00
Lauri Ojansivu 5d1714dbd5 Updated ChangeLog. 2021-05-29 00:56:24 +03:00
Lauri Ojansivu bd1de94312 Fixed Importing JSON exports fails.
Thanks to KeptnArgo and xet7 !

Fixes #3742
2021-05-29 00:52:36 +03:00
Lauri Ojansivu 06937d0aa0 Updated translations. 2021-05-29 00:29:03 +03:00
Lauri Ojansivu 9e39731133 Updated ChangeLog. 2021-05-29 00:26:09 +03:00
Lauri Ojansivu 3908cd5413 Fix: Impersonate user can now export Excel/CSV/TSV/JSON.
Impersonate user and export Excel/CSV/TSV/JSON is now logged into
database table impersonatedUsers.

Thanks to xet7 !

Fixes #3827,
fixes #3284
2021-05-29 00:19:12 +03:00
Lauri Ojansivu 6be1a33093 Updated dependencies.
Thanks to developers of dependencies!
2021-05-29 00:15:42 +03:00
Lauri Ojansivu c656cbf6ae Updated translations. 2021-05-25 17:42:28 +03:00
Lauri Ojansivu 83045a6512 Updated ChangeLog. 2021-05-18 12:59:07 +03:00
Lauri Ojansivu 199485640f Merge pull request #3813 from spasche/typos
Fix typos
2021-05-18 12:43:18 +03:00
Sylvain Pasche 2e41ff1121 Fix typos. 2021-05-17 23:41:36 +02:00
Lauri Ojansivu 487c6fae8d Updated ChangeLog. 2021-05-16 17:10:34 +03:00
Lauri Ojansivu ea64b17b82 Reworded issue template about can not login.
Thanks to xet7 !
2021-05-16 13:34:49 +03:00
Lauri Ojansivu 6d0578fd5a Added Forgot Password page to GitHub issue template.
Thanks to xet7 !
2021-05-16 13:27:56 +03:00
Lauri Ojansivu 76a4deda78 Updated ChangeLog. 2021-05-12 19:33:05 +03:00
Lauri Ojansivu 595abd303c Merge pull request #3799 from Enishowk/feature/excel_parentNameExport
Feature/excel parent card name export
2021-05-12 19:30:43 +03:00
Lauri Ojansivu 5dd6466c0a Removed not working GitHub workflow.
Thanks to xet7 !
2021-05-10 20:26:23 +03:00
Lauri Ojansivu 38655ee478 Merge branch 'master' of github.com:wekan/wekan 2021-05-09 07:53:08 +03:00
Lauri Ojansivu 23ca25a1ca Updated translations. 2021-05-09 07:52:45 +03:00
Enishowk 120cb4820e fix: typo 2021-05-07 18:58:24 +02:00
Enishowk 3ee0add6f6 feat: add parent name column in excel export 2021-05-07 18:54:59 +02:00
Lauri Ojansivu 8911fe5c8d Try to fix Snap: Removed linting packages.
Thanks to xet7 !
2021-05-07 18:12:49 +03:00
Lauri Ojansivu 1d9346513e Update dependencies.
Thanks to developers of dependencies !
2021-05-07 17:46:22 +03:00
Lauri Ojansivu 62150ce6c4 Updated dependencies.
Thanks to developers of dependencies!
2021-05-07 17:09:31 +03:00
Lauri Ojansivu b2d4135dca v5.28 2021-05-07 16:23:55 +03:00
Lauri Ojansivu 49e3e01ece Updated translations. 2021-05-07 13:20:53 +03:00
Lauri Ojansivu 23c2ff0e29 Updated ChangeLog. 2021-05-07 12:53:18 +03:00
Lauri Ojansivu a550c255e6 Updated dependencies.
Thanks to developers of dependencies !
2021-05-07 12:51:54 +03:00
Lauri Ojansivu 6b897faaa8 Updated ChangeLog. 2021-05-07 12:34:10 +03:00
Lauri Ojansivu 48255f6f1e Updated dependencies.
Thanks to developers of dependencies !
2021-05-07 12:32:44 +03:00
Lauri Ojansivu 45bc3bd3db Updated ChangeLog. 2021-05-07 02:23:40 +03:00
Lauri Ojansivu 5ab20a9257 Added markdown-it-mermaid for some charts support in all input fields. Replaced xss with dompurify.
Thanks to xuguotong and xet7 !

Fixes #3794
2021-05-07 02:13:20 +03:00
Lauri Ojansivu 88e4918208 Updated ChangeLog. 2021-05-06 18:29:44 +03:00
Lauri Ojansivu 521ef8b6da Updated dependencies.
Thanks to developers of dependencies !
2021-05-06 18:26:32 +03:00
Lauri Ojansivu 0d86dfa571 Updated translations. 2021-05-06 18:14:05 +03:00
Lauri Ojansivu 472350e56f Merge branch 'listenerri-patch-1' 2021-05-06 18:00:06 +03:00
listenerri 68569b9640 Update cardDate.js 2021-05-06 10:08:01 +08:00
Lauri Ojansivu 823f11d5dd v5.27 2021-04-29 13:39:08 +03:00
Lauri Ojansivu ae19018831 Updated translations. 2021-04-29 13:33:13 +03:00
Lauri Ojansivu d5b09c7b3e Updated dependencies. 2021-04-29 13:32:25 +03:00
Lauri Ojansivu d3b4aa4dc7 Updated ChangeLog. 2021-04-29 13:29:06 +03:00
Lauri Ojansivu c2da477735 Fixed Non-ASCII attachment filename will crash when downloading.
Thanks to xet7 !

Fixes #2759
2021-04-29 13:26:49 +03:00
Lauri Ojansivu 843ff8eaaa v5.26 2021-04-28 12:38:15 +03:00
Lauri Ojansivu c5143e2c54 Updated translations. 2021-04-28 12:34:30 +03:00
Lauri Ojansivu 0596f5e864 Updated ChangeLog. 2021-04-28 12:28:09 +03:00
Lauri Ojansivu 8ca1b25daf Bring back Almost-Due for Start Date.
Thanks to darren-teo !

Fixes #3766
2021-04-28 12:26:31 +03:00
Lauri Ojansivu 1f76159bfe Updated ChangeLog. 2021-04-28 12:19:43 +03:00
Lauri Ojansivu 39ece35f52 Merge pull request #3765 from HappyMushroom-TechTeam/feature/miniCard_subtaskCount
Feature/mini card subtask count
2021-04-28 12:18:03 +03:00
ryanMushy 63c1bb9e17 count subtasks just like checklists 2021-04-27 18:28:23 -07:00
Lauri Ojansivu 354622c5ad v5.25 2021-04-27 23:11:23 +03:00
Lauri Ojansivu df7fd8c900 v5.25 2021-04-27 23:02:49 +03:00
Lauri Ojansivu 7957690fca Updated translations. 2021-04-27 22:57:25 +03:00
Lauri Ojansivu 63d19447a0 Updated ChangeLog. 2021-04-27 22:50:53 +03:00
Lauri Ojansivu 018a84ec59 Merge pull request #3764 from HappyMushroom-TechTeam/feature/excel_swimlanExport
cherry pick the excel export feature to add swimlanes to the output
2021-04-27 22:47:41 +03:00
ryanMushy e5c0e623a0 cherry pick the excel export feature to add swimlanes to the output 2021-04-27 12:38:38 -07:00
ryan neal 9a2d3e15e8 Merge pull request #2 from wekan/master
newest version
2021-04-27 12:36:57 -07:00
Lauri Ojansivu c07e262e68 Updated translations. 2021-04-27 20:29:01 +03:00
Lauri Ojansivu cacb2b91ee Updated ChangeLog. 2021-04-27 20:18:37 +03:00
Lauri Ojansivu 6f269d598f Merge pull request #3762 from bentiss/nested-openapi
openapi: better handle nested schemas
2021-04-27 20:16:59 +03:00
Lauri Ojansivu 57ea254bff Updated ChangeLog. 2021-04-27 19:51:09 +03:00
Lauri Ojansivu 7e183c838f Merge pull request #3761 from HappyMushroom-TechTeam/bugfix/summernoteOnPaste
Bugfix/summernote on paste
2021-04-27 19:48:46 +03:00
Lauri Ojansivu 3ffdfd9736 Updated ChangeLog. 2021-04-27 19:47:40 +03:00
Lauri Ojansivu 621c701bef Fixed language name of Deutsch (Schweiz).
Thanks to urmel1960 !

Fixes #3763
2021-04-27 19:45:18 +03:00
Lauri Ojansivu 3c24c9966c Updated ChangeLog. 2021-04-27 19:38:20 +03:00
Lauri Ojansivu 995de525d9 Added missing postgres password to ToroDB.
Thanks to xet7 !
2021-04-27 19:36:34 +03:00
Benjamin Tissoires ae30b1220a openapi: better handle nested schemas
there is a 2 levels schemas in profile.notifications.

The code previously assumed we could only have one level, and so was not
producing the correct UsersProfileNotifications.

Fix that by being more generic in the way we retrieve the nested
subschemas.
2021-04-27 10:44:27 +02:00
ryanMushy e380ad26c2 Images are uploaded twice
- make sure only pastes which contain text are processed
- remove execCommand() as it results in errors
- enable drag & drop
- fix resize buttons to be the proper summernote commands
- remove un-needed comma
2021-04-26 23:31:07 -07:00
ryan neal 9bb31d2380 Merge pull request #1 from wekan/master
newest wekan
2021-04-26 12:10:21 -07:00
Lauri Ojansivu 8ec1e4f3fa Updated ChangeLog. 2021-04-24 20:18:04 +03:00
Lauri Ojansivu 9f0f6841b0 Updated release scripts. 2021-04-24 20:16:27 +03:00
Lauri Ojansivu 5d18f98b44 v5.24 2021-04-24 19:47:12 +03:00
Lauri Ojansivu e738177e08 Updated dependencies.
Thanks to developers of dependencies !
2021-04-24 19:40:50 +03:00
Lauri Ojansivu bb201935b9 Updated translations. 2021-04-24 19:35:48 +03:00
Lauri Ojansivu bcfe6bd616 Updated translations.
Thanks to translators and xet7 !

Fixes #3756
2021-04-24 19:08:08 +03:00
Lauri Ojansivu 9c6352a7bc Updated translations. 2021-04-23 12:53:15 +03:00
Lauri Ojansivu 9110e41f20 Updated ChangeLog. 2021-04-23 00:19:39 +03:00
Lauri Ojansivu 7ae871d3a6 Merge pull request #3753 from jrsupplee/copy-swimlane
Add copy swimlane functionality
2021-04-22 22:50:27 +03:00
Lauri Ojansivu 0ddf027d43 Updated ChangeLog. 2021-04-22 16:54:37 +03:00
Lauri Ojansivu 45124a39f3 Fix Snap: Delete extra symlink that prevented building Snap.
Thanks to xet7 !
2021-04-22 16:52:51 +03:00
John R. Supplee 4940af0573 Add copy swimlane functionality 2021-04-22 14:16:35 +02:00
Lauri Ojansivu 924b5d1733 v5.23 2021-04-22 15:16:22 +03:00
Lauri Ojansivu 2119a3a51e Updated translations. 2021-04-22 14:58:24 +03:00
Lauri Ojansivu dcda99ddf3 Updated ChangeLog. 2021-04-22 14:48:39 +03:00
Lauri Ojansivu 0e35dcd5ad Merge pull request #3749 from listenerri/patch-1
Fix: The bg color of start at button is almost-due
2021-04-22 14:44:34 +03:00
listenerri 991d42ca0b Fix: The bg color of start at button is almost-due
One minute earlier than the current time is almost-due? This should be a faulty logic.
2021-04-22 18:24:18 +08:00
Lauri Ojansivu ac242c96b7 Updated translations. 2021-04-20 21:13:04 +03:00
Lauri Ojansivu 79d45c8e33 Updated ChangeLog. 2021-04-20 21:09:43 +03:00
Lauri Ojansivu 8e9f7102f9 Merge pull request #3741 from jrsupplee/master
Bug fix for Due Cards
2021-04-20 21:07:05 +03:00
John R. Supplee 1952dc309b Bug fix for Due Cards 2021-04-20 16:23:56 +02:00
Lauri Ojansivu 5e7767247f Updated ChangeLog. 2021-04-20 10:09:49 +03:00
Lauri Ojansivu 676bf686c7 Updated dependencies.
Thanks to developers of dependencies !
2021-04-20 10:08:32 +03:00
Lauri Ojansivu 5cd6985368 Updated translations. 2021-04-20 10:02:37 +03:00
Lauri Ojansivu 807e72dcce Updated ChangeLog. 2021-04-20 09:59:30 +03:00
Lauri Ojansivu 71702b5821 Merge pull request #3733 from jrsupplee/issue-1569
Fix: Trello data without labels definition
2021-04-20 09:58:18 +03:00
Lauri Ojansivu 40c56248f1 Merge branch 'mcrute-master' 2021-04-20 08:37:40 +03:00
John R. Supplee 74ac7d8d3f Fix for issue #1569 Trello data without labels definition 2021-04-19 22:37:26 +02:00
Mike Crute e7186b47b0 Support filtering by due date 2021-04-19 07:44:09 -07:00
Lauri Ojansivu 12aa7ea2cb Updated ChangeLog. 2021-04-16 22:33:34 +03:00
Lauri Ojansivu ef584e2826 v5.22 2021-04-16 21:57:36 +03:00
Lauri Ojansivu f902be10b6 Updated translations. 2021-04-16 21:54:04 +03:00
Lauri Ojansivu 29b835de59 Updated ChangeLog. 2021-04-16 21:50:25 +03:00
Lauri Ojansivu 6ff9c5b58d Added German (Switzerland) (de_CH) Part 3.
Fixed lint.

Thanks to translators and xet7 !
2021-04-16 21:47:39 +03:00
Lauri Ojansivu 1b86aeb4f5 Updated ChangeLog. 2021-04-16 21:25:40 +03:00
Lauri Ojansivu dce99c00be Updated number of translated languages.
Thanks to translators and xet7 !
2021-04-16 21:24:00 +03:00
Lauri Ojansivu e47c700c36 Updated ChangeLog. 2021-04-16 21:23:14 +03:00
Lauri Ojansivu 09506c78f3 Updated translations. Added German (Switzerland) (de_CH).
Thanks to translators !
2021-04-16 21:20:45 +03:00
Lauri Ojansivu 9ec1fc0902 Updated ChangeLog. 2021-04-16 20:58:18 +03:00
Lauri Ojansivu 0b3ec2c19e Merge pull request #3724 from jrsupplee/master
Remove allowedValues from Cards.type schema
2021-04-16 20:55:50 +03:00
John R. Supplee a94bec921c Remove allowedValues from Cards.type schema 2021-04-16 19:52:09 +02:00
Lauri Ojansivu 3884da3c06 v5.21 2021-04-16 20:28:51 +03:00
Lauri Ojansivu d3fa177c7f Updated translations. 2021-04-16 20:24:19 +03:00
Lauri Ojansivu d2176e2c08 Updated ChangeLog. 2021-04-16 20:05:20 +03:00
Lauri Ojansivu 8d9c59486d Merge branch 'master' of github.com:wekan/wekan 2021-04-16 19:59:34 +03:00
Lauri Ojansivu 0e7c2b4b94 Upgraded to Meteor 2.2.
Thanks to Meteor developers!
2021-04-16 19:58:51 +03:00
Lauri Ojansivu 5c9bc1c745 Merge pull request #3723 from mfilser/bugfix-date_format_not_changed_to_local_format
Bugfix, date format not changed to local format
2021-04-16 19:57:24 +03:00
Martin Filser 5e7efa0098 Bugfix, date format not changed to local format
- Fixes #3718
- intoduced by commit 4aee129cdc
2021-04-16 17:04:36 +02:00
Lauri Ojansivu 424862e313 Updated ChangeLog. 2021-04-15 17:50:03 +03:00
Lauri Ojansivu 9b80ad5a3c Merge branch 'HappyMushroom-TechTeam-summernoteFeatureSet' 2021-04-15 17:44:26 +03:00
ryanMushy 96c6fa0276 - Add new button to insert a URL link
- Add new popover allowing you to edit URL links
- enable spell check and grammerly extension
2021-04-14 18:31:32 -07:00
Lauri Ojansivu 69305e1833 v5.20 2021-04-14 13:14:21 +03:00
Lauri Ojansivu 8aa74ab65e Updated translations. 2021-04-14 13:10:48 +03:00
Lauri Ojansivu aee12c2494 Updated ChangeLog. 2021-04-14 12:40:07 +03:00
Lauri Ojansivu 83976b3229 Merge pull request #3715 from bentiss/fix-docs
openapi: rework the allowedValues to allow for imported variables
2021-04-14 12:37:52 +03:00
Benjamin Tissoires 39f8a138d7 openapi: rework the allowedValues to allow for imported variables
Previously I was assuming all variables were declared in the same file.
Turns out that imports exists too, and we need to recurse one more time
in those imports./

Also "clean" up a bit the function to do the parsing in 2 steps:
- first find out the actual matching node in js
- then convert it based on its type
rinse wash repeat.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2021-04-14 11:32:03 +02:00
Lauri Ojansivu 120e4af7b2 Updated translations. 2021-04-14 12:26:58 +03:00
Lauri Ojansivu 15bddbc86a v5.19 2021-04-14 02:09:16 +03:00
Lauri Ojansivu 4e8b193801 Updated ChangeLog. 2021-04-14 02:00:15 +03:00
Lauri Ojansivu 37b65996a0 Merge branch 'master' of github.com:wekan/wekan 2021-04-14 01:55:38 +03:00
Lauri Ojansivu 9cd0d3488f Updated ChangeLog. 2021-04-14 01:55:31 +03:00
Lauri Ojansivu eaf07cef07 Merge pull request #3712 from HappyMushroom-TechTeam/bugfix/summernotePopover
popover needs to be destroyed anytime the details panel is closed.
2021-04-14 01:52:55 +03:00
Lauri Ojansivu 02a8775537 v5.18 2021-04-14 01:36:30 +03:00
ryanMushy f2d5698acb popover needs to be destroyed anytime the details panel is closed. 2021-04-13 15:34:59 -07:00
Lauri Ojansivu b0c0e456af Updated ChangeLog. 2021-04-14 01:23:40 +03:00
Lauri Ojansivu 0857a2ea91 Updated caniuse-lite etc dependencies.
Thanks to developers of dependencies !
2021-04-14 01:22:06 +03:00
Lauri Ojansivu 1f2e6d33ce Updated translations. 2021-04-14 01:12:21 +03:00
Lauri Ojansivu 250e0690d1 Updated ChangeLog. 2021-04-14 00:37:15 +03:00
Lauri Ojansivu e709094533 Merge pull request #3708 from jrsupplee/issue-3698
Bug fix for issue #3698
2021-04-14 00:35:43 +03:00
Lauri Ojansivu 4a5b16ee2e Updated ChangeLog. 2021-04-14 00:19:38 +03:00
Lauri Ojansivu 378d64f99a Merge pull request #3706 from jrsupplee/issue-3703
Bug fix: Rules for moving from list/swimlane
2021-04-14 00:18:05 +03:00
Lauri Ojansivu 4395760a40 Updated ChangeLog. 2021-04-14 00:16:12 +03:00
Lauri Ojansivu 8417fae89c Update Admin Panel Rules report icon and add missing translations.
Thanks to xet7 !
2021-04-14 00:11:05 +03:00
Lauri Ojansivu 6c8fae944c Merge branch 'jrsupplee-admin-reports' 2021-04-13 23:51:11 +03:00
Lauri Ojansivu decab9256b Merge branch 'admin-reports' of https://github.com/jrsupplee/wekan into jrsupplee-admin-reports 2021-04-13 23:50:37 +03:00
Lauri Ojansivu 183ed35ce8 Updated ChangeLog. 2021-04-13 23:33:24 +03:00
Lauri Ojansivu 2fcf85fd61 Merge branch 'tod31-customfield-stringtemplate' 2021-04-13 23:25:18 +03:00
Lauri Ojansivu 3613c1c75e Merge branch 'customfield-stringtemplate' of https://github.com/tod31/wekan into tod31-customfield-stringtemplate 2021-04-13 23:24:28 +03:00
Lauri Ojansivu e7813046f6 Updated ChangeLog. 2021-04-13 21:26:02 +03:00
Lauri Ojansivu 9871bf1963 Updated release scripts.
Thanks to xet7 !
2021-04-13 21:24:44 +03:00
Lauri Ojansivu b32e7b659b Updated ChangeLog. 2021-04-13 21:13:54 +03:00
Lauri Ojansivu 2201372744 Updated to Node.js v12.22.1. Thanks to Node.js developers. 2021-04-13 21:11:54 +03:00
Lauri Ojansivu 7431a082e2 Updated ChangeLog. 2021-04-13 21:07:45 +03:00
Lauri Ojansivu 6fe3edebb1 Added latest bundle symlink.
Thanks to xet7 !
2021-04-13 21:04:59 +03:00
Lauri Ojansivu 4b6406c264 Merge pull request #3692 from loganballard/fix-arm-dockerfile
Updating ARM Dockerfile
2021-04-13 21:03:58 +03:00
John R. Supplee 86f6bdf9af Merge branch 'master' of https://github.com/wekan/wekan into admin-reports 2021-04-13 13:02:57 +02:00
Lauri Ojansivu 1eeed6645d Updated ChangeLog. 2021-04-12 19:34:58 +03:00
Lauri Ojansivu bb8c4325c6 Updated dependencies.
Thanks to developers of dependencies !
2021-04-12 19:32:00 +03:00
John R. Supplee 07a3301414 Bug fix for issue #3698
* Rewrite routine for building the My Cards hierarchical list
* Use a separate publication for retrieving My Cards
* Fix bug with limit and skip projectsion
2021-04-12 17:34:02 +02:00
Martin Filser 1590701fc9 Custom Fields StringTemplate, Ctrl-Enter the same as the button "Save" 2021-04-12 17:23:51 +02:00
John R. Supplee 1d4a65d0b4 Add new rules report 2021-04-12 11:44:28 +02:00
tod31 c845d25ae5 add localization string 2021-04-12 11:14:25 +02:00
tod31 ceec27bf90 cleanup and cosmetics 2021-04-12 11:01:58 +02:00
tod31 4b90424773 event handling/focussing 2021-04-12 10:49:14 +02:00
tod31 31eb9b26b4 use tracker afterFlush to set focus 2021-04-11 20:07:31 +02:00
tod31 a8a3c9ad19 fix default value 2021-04-11 14:18:12 +02:00
tod31 5cfa03ec07 use string array to store items in database 2021-04-11 14:15:57 +02:00
John R. Supplee 17ab5978ad Bug fix: Rules for moving from list/swimlane
Issue: 3703
2021-04-11 00:09:32 +02:00
tod31 d4c69c3932 fix custom-field-stringtemplate-separator string 2021-04-09 12:46:12 +02:00
tod31 3778a9ebcf add configuration field for separator 2021-04-09 11:46:07 +02:00
tod31 880d94e220 use nonidentity instead of inequality 2021-04-09 11:46:03 +02:00
tod31 37607f72b2 code cosmetics 2021-04-09 11:45:56 +02:00
tod31 b1c26e6f64 remove empty lines 2021-04-09 11:45:48 +02:00
tod31 8164af281d add info text for placeholder 2021-04-09 11:45:40 +02:00
tod31 af676c8f26 fix custom field type name 2021-04-09 11:45:25 +02:00
tod31 b8dc7ff18e add custom field "stringtemplate" 2021-04-09 11:44:49 +02:00
John R. Supplee 4aee129cdc Remove redundant and unused code 2021-04-06 18:00:03 +02:00
John R. Supplee 03d29dd674 Updates to userAvatar and cardDetails template
* enable userAvatar to work with member and assignee
* remove userAvatarAssignee template
2021-04-06 17:50:03 +02:00
John R. Supplee 5a6f84ce34 * Add publications for admin reports
* remove broken cards from user menu
* Adjust report titles
2021-04-06 12:42:15 +02:00
John R. Supplee 0e593c7d81 Add admin reports to admin panel
* Broken cards
* Attachments
* Orphaned Attachments
2021-04-06 12:24:08 +02:00
John R. Supplee 4de830d663 Merge branch 'search' into admin-reports 2021-04-05 21:57:56 +02:00
Lauri Ojansivu 769bb7a55d Updated translations. 2021-04-04 22:09:23 +03:00
John R. Supplee ed6c6e5a25 Use new colors constants 2021-04-03 18:52:47 +02:00
John R. Supplee 302ba75729 * Fix bug with multiple label predicates
* Add new constants
2021-04-03 01:19:02 +02:00
John R. Supplee 69dc8f304c Only search label names in boards of type 'board' 2021-04-02 21:40:52 +02:00
John R. Supplee e43002d5ad Update Boards.userBoards() to only return type 'board' by default 2021-04-02 02:15:12 +02:00
John R. Supplee edd07befe2 Add a new search operator creator 2021-04-01 23:41:32 +02:00
John R. Supplee 5cc6a2f7d0 Add an avatar for the card creator
* Add avatar to minicard
* Add field to card details
* Add show option in Board / Card Settings
2021-04-01 23:40:07 +02:00
Lauri Ojansivu 926f39c96b Update translations. 2021-04-01 14:14:11 +03:00
Lauri Ojansivu abd38082de Updated ChangeLog. 2021-04-01 13:53:10 +03:00
Lauri Ojansivu 5ca90f4d22 Revert stable tag, because it did break Wekan version numbers.
Thanks to xet7 !
2021-04-01 13:47:57 +03:00
loganballard c9586e579a updating Dockefile 2021-03-31 21:46:52 -07:00
484 changed files with 112026 additions and 67345 deletions

No files matched your search

+64 -19
View File
@@ -1,16 +1,12 @@
FROM quay.io/wekan/ubuntu:groovy-20201125.2
FROM quay.io/wekan/ubuntu:groovy-20210115
LABEL maintainer="sgr"
# 2020-12-03:
# - Above Ubuntu base image copied from Docker Hub ubuntu:groovy-20201125.2
# to Quay to avoid Docker Hub rate limits.
ENV BUILD_DEPS="gnupg gosu bsdtar wget curl bzip2 g++ build-essential python git ca-certificates iproute2"
ENV BUILD_DEPS="gnupg gosu libarchive-tools wget curl bzip2 g++ build-essential python git ca-certificates iproute2"
ENV DEBIAN_FRONTEND=noninteractive
ENV \
DEBUG=false \
NODE_VERSION=v12.22.0 \
NODE_VERSION=v12.22.1 \
METEOR_RELEASE=1.10.2 \
USE_EDGE=false \
METEOR_EDGE=1.5-beta.17 \
@@ -134,23 +130,24 @@ ENV \
SAML_PUBLIC_CERTFILE="" \
SAML_IDENTIFIER_FORMAT="" \
SAML_LOCAL_PROFILE_MATCH_ATTRIBUTE="" \
SAML_ATTRIBUTES=""
SAML_ATTRIBUTES="" \
DEFAULT_WAIT_SPINNER=""
# 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}
&& 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 \
&& curl -fsSLO --compressed "https://nodejs.org/dist/$NODE_VERSION/node-$NODE_VERSION-$ARCHITECTURE.tar.xz" \
&& curl -fsSLO --compressed "https://nodejs.org/dist/$NODE_VERSION/SHASUMS256.txt.asc" \
&& grep " node-$NODE_VERSION-$ARCHITECTURE.tar.xz\$" SHASUMS256.txt.asc | sha256sum -c - \
&& tar -xJf "node-$NODE_VERSION-$ARCHITECTURE.tar.xz" -C /usr/local --strip-components=1 --no-same-owner \
&& rm "node-$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} \
@@ -172,17 +169,65 @@ RUN set -o xtrace \
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
USER wekan
COPY ${SRC_PATH} /home/wekan/app/
# Copy source dir
RUN set -o xtrace \
&& mkdir -p /home/wekan/app/.meteor \
&& mkdir -p /home/wekan/app/packages
COPY \
.meteor/.finished-upgraders \
.meteor/.id \
.meteor/cordova-plugins \
.meteor/packages \
.meteor/platforms \
.meteor/release \
.meteor/versions \
/home/wekan/app/.meteor/
COPY \
package.json \
settings.json \
/home/wekan/app/
COPY \
packages \
/home/wekan/app/packages/
USER root
RUN set -o xtrace \
&& chown -R wekan:wekan /home/wekan/app /home/wekan/.meteor
USER wekan
RUN \
set -o xtrace && \
sed -i 's/api\.versionsFrom/\/\/api.versionsFrom/' /home/wekan/app/packages/meteor-useraccounts-core/package.js && \
cd /home/wekan/.meteor && \
/home/wekan/.meteor/meteor -- help;
RUN \
set -o xtrace && \
# Build app
cd /home/wekan/app && \
/home/wekan/.meteor/meteor add standard-minifier-js && \
/home/wekan/.meteor/meteor npm install && \
/home/wekan/.meteor/meteor build --directory /home/wekan/app_build
RUN \
set -o xtrace && \
cd /home/wekan/app_build/bundle/programs/server/ && \
chmod u+w package.json npm-shrinkwrap.json && \
npm install
ENV PORT=3000
EXPOSE $PORT
WORKDIR /home/wekan/app
CMD ["/home/wekan/.meteor/meteor", "run", "--verbose", "--settings", "settings.json"]
+12 -7
View File
@@ -3,17 +3,18 @@ version: '3.7'
services:
wekandb-dev:
image: mongo:4.0.12
image: mongo:4.4
container_name: wekan-dev-db
restart: unless-stopped
command: mongod --smallfiles --oplogSize 128
command: mongod --oplogSize 128
networks:
- wekan-dev-tier
expose:
- 27017
volumes:
- wekan-dev-db:/data/db
- wekan-dev-db-dump:/dump
- ./volumes/wekan-db:/data/db
- ./volumes/wekan-db-dump:/dump
- /etc/localtime:/etc/localtime:ro
wekan-dev:
container_name: wekan-dev-app
@@ -35,9 +36,13 @@ services:
depends_on:
- wekandb-dev
volumes:
- ..:/app:delegated
command:
sleep infinity
- ../client:/home/wekan/app/client
- ../models:/home/wekan/app/models
- ../config:/home/wekan/app/config
- ../i18n:/home/wekan/app/i18n
- ../server:/home/wekan/app/server
- ../public:/home/wekan/app/public
- /etc/localtime:/etc/localtime:ro
volumes:
wekan-dev-db:
+36
View File
@@ -0,0 +1,36 @@
*~
*.swp
.meteor-spk
*.sublime-workspace
tmp/
node_modules/
npm-debug.log
.gitmodules
.vscode/
.idea/
.build/*
**/parts/
**/stage
**/prime
**/*.snap
snap/.snapcraft/
.idea
.DS_Store
.DS_Store?
.build*
*.browserify.js.cached
*.browserify.js.map
.build*
versions.json
.versions
.npm
.build*
._*
.Trashes
Thumbs.db
ehthumbs.db
.eslintcache
.meteor/local
.devcontainer/docker-compose.extend.yml
.devcontainer/volumes*/
.git
+1 -1
View File
@@ -81,7 +81,7 @@ parts:
wekan:
source: .
plugin: nodejs
node-engine: 12.22.0
node-engine: 12.22.1
node-packages:
- node-gyp
- node-pre-gyp
+1 -1
View File
@@ -83,7 +83,7 @@ parts:
wekan:
source: .
plugin: nodejs
node-engine: 12.22.0
node-engine: 12.22.1
node-packages:
- node-gyp
- node-pre-gyp
+3
View File
@@ -1,5 +1,8 @@
## Issue
If you can not login for any reason:
- https://github.com/wekan/wekan/wiki/Forgot-Password
Email settings:
- https://github.com/wekan/wekan/wiki/Troubleshooting-Mail
-12
View File
@@ -1,12 +0,0 @@
on: [push]
jobs:
zap_scan:
runs-on: ubuntu-latest
name: Scan the webapplication
steps:
- name: OWASP ZAP Scan
runs-on: ubuntu-latest
uses: zaproxy/action-baseline@v0.4.0
with:
target: 'https://boards.wekan.team/sign-in'
+1
View File
@@ -32,3 +32,4 @@ ehthumbs.db
.eslintcache
.meteor/local
.devcontainer/docker-compose.extend.yml
.devcontainer/volumes*/
+5 -6
View File
@@ -6,7 +6,7 @@
meteor-base@1.4.0
# Build system
ecmascript@0.15.0
ecmascript@0.15.1
standard-minifier-css@1.7.2
standard-minifier-js@2.6.0
mquandalle:jade
@@ -17,13 +17,13 @@ es5-shim@4.8.0
# Collections
aldeed:collection2
cfs:standard-packages
wekan-cfs-standard-packages
cottz:publish-relations
dburles:collection-helpers
idmontie:migrations
matb33:collection-hooks
matteodem:easy-search
mongo@1.10.1
mongo@1.11.0
mquandalle:collection-mutations
# Account system
@@ -75,7 +75,7 @@ horka:swipebox
dynamic-import@0.6.0
accounts-password@1.7.0
cfs:gridfs
wekan-cfs-gridfs
rzymek:fullcalendar
momentjs:moment@2.22.2
browser-policy-framing@1.1.0
@@ -91,8 +91,7 @@ meteorhacks:aggregate@1.3.0
wekan-markdown
konecty:mongo-counter
percolate:synced-cron
cfs:filesystem
tmeasday:check-npm-versions
wekan-cfs-filesystem
steffo:meteor-accounts-saml
rajit:bootstrap3-datepicker-fi
rajit:bootstrap3-datepicker-ar
+1 -1
View File
@@ -1 +1 @@
METEOR@2.1
METEOR@2.2
+46 -46
View File
@@ -1,7 +1,7 @@
3stack:presence@1.1.2
accounts-base@1.8.0
accounts-base@1.9.0
accounts-oauth@1.2.0
accounts-password@1.7.0
accounts-password@1.7.1
aldeed:collection2@2.10.0
aldeed:collection2-core@1.2.0
aldeed:schema-deny@1.1.0
@@ -11,36 +11,19 @@ allow-deny@1.1.0
arillo:flow-router-helpers@0.5.2
audit-argument-checks@1.0.7
autoupdate@1.7.0
babel-compiler@7.6.0
babel-compiler@7.6.1
babel-runtime@1.5.0
base64@1.0.12
binary-heap@1.0.11
blaze@2.3.4
blaze-tools@1.0.10
blaze@2.5.0
blaze-tools@1.1.2
boilerplate-generator@1.7.1
browser-policy-common@1.0.11
browser-policy-framing@1.1.0
caching-compiler@1.2.2
caching-html-compiler@1.1.3
caching-html-compiler@1.2.0
callback-hook@1.3.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@1.1.0
@@ -49,15 +32,15 @@ coffeescript-compiler@2.4.1
cottz:publish-relations@2.0.8
dburles:collection-helpers@1.1.0
ddp@1.4.0
ddp-client@2.4.0
ddp-client@2.4.1
ddp-common@1.4.0
ddp-rate-limiter@1.0.9
ddp-server@2.3.2
ddp-server@2.3.3
deps@1.0.12
diff-sequence@1.1.1
dynamic-import@0.6.0
easylogic:summernote@0.8.8
ecmascript@0.15.0
ecmascript@0.15.1
ecmascript-runtime@0.7.0
ecmascript-runtime-client@0.11.0
ecmascript-runtime-server@0.10.0
@@ -70,10 +53,10 @@ 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.3
id-map@1.1.0
html-tools@1.1.2
htmljs@1.1.1
http@1.4.4
id-map@1.1.1
idmontie:migrations@1.0.3
inter-process-messaging@0.1.1
jquery@1.11.11
@@ -84,7 +67,7 @@ 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.2.0
launch-screen@1.2.1
livedata@1.0.18
localstorage@1.2.0
logging@1.2.0
@@ -100,19 +83,19 @@ 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.5.3
minifier-css@1.5.4
minifier-js@2.6.0
minifiers@1.1.8-faster-rebuild.0
minimongo@1.6.1
minimongo@1.6.2
mobile-status-bar@1.1.0
modern-browsers@0.1.5
modules@0.16.0
modules-runtime@0.12.0
momentjs:moment@2.29.1
mongo@1.10.1
mongo@1.11.1
mongo-decimal@0.1.2
mongo-dev-server@1.1.0
mongo-id@1.0.7
mongo-id@1.0.8
mongo-livedata@1.0.12
mousetrap:mousetrap@1.4.6_1
mquandalle:autofocus@1.0.0
@@ -125,7 +108,7 @@ mquandalle:moment@1.0.1
mquandalle:mousetrap-bindglobal@0.0.1
msavin:usercache@1.8.0
npm-bcrypt@0.9.3
npm-mongo@3.8.1
npm-mongo@3.9.0
oauth@1.3.2
oauth2@1.3.0
observe-sequence@1.0.16
@@ -191,7 +174,7 @@ rajit:bootstrap3-datepicker-zh-cn@1.7.1
rajit:bootstrap3-datepicker-zh-tw@1.7.1
random@1.2.0
rate-limit@1.0.9
react-fast-refresh@0.1.0
react-fast-refresh@0.1.1
reactive-dict@1.3.0
reactive-var@1.0.11
reload@1.3.1
@@ -208,10 +191,10 @@ 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.3.1
socket-stream-client@0.3.3
softwarerero:accounts-t9n@1.3.11
spacebars@1.0.15
spacebars-compiler@1.1.3
spacebars@1.2.0
spacebars-compiler@1.2.1
srp@1.1.0
standard-minifier-css@1.7.2
standard-minifier-js@2.6.0
@@ -220,11 +203,10 @@ staringatlights:inject-data@2.3.0
steffo:meteor-accounts-saml@0.0.18
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
tmeasday:check-npm-versions@0.3.2
templating@1.4.0
templating-compiler@1.4.1
templating-runtime@1.4.0
templating-tools@1.2.0
tracker@1.2.0
twbs:bootstrap@3.3.6
ui@1.0.13
@@ -234,10 +216,28 @@ useraccounts:core@1.14.2
useraccounts:flow-routing@1.14.2
useraccounts:unstyled@1.14.2
verron:autosize@3.0.8
webapp@1.10.0
webapp@1.10.1
webapp-hashing@1.1.0
wekan-accounts-cas@0.1.0
wekan-accounts-oidc@1.0.10
wekan-cfs-access-point@0.1.50
wekan-cfs-base-package@0.0.30
wekan-cfs-collection@0.5.5
wekan-cfs-collection-filters@0.2.4
wekan-cfs-data-man@0.0.6
wekan-cfs-file@0.1.17
wekan-cfs-filesystem@0.1.2
wekan-cfs-gridfs@0.0.34
wekan-cfs-http-methods@0.0.32
wekan-cfs-http-publish@0.0.13
wekan-cfs-power-queue@0.9.11
wekan-cfs-reactive-list@0.0.9
wekan-cfs-reactive-property@0.0.4
wekan-cfs-standard-packages@0.5.10
wekan-cfs-storage-adapter@0.2.4
wekan-cfs-tempstore@0.1.6
wekan-cfs-upload-http@0.0.21
wekan-cfs-worker@0.1.5
wekan-ldap@0.0.2
wekan-markdown@1.0.9
wekan-oidc@1.0.12
+1 -1
View File
@@ -3,7 +3,7 @@ sudo: required
env:
TRAVIS_DOCKER_COMPOSE_VERSION: 1.24.0
TRAVIS_NODE_VERSION: 12.22.0
TRAVIS_NODE_VERSION: 12.22.1
TRAVIS_NPM_VERSION: latest
before_install:
+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 = ar_EG:ar-EG, bg_BG:bg, en_GB:en-GB, es_AR:es-AR, es_CL:es-CL, es_419:es-LA, es_PE:es-PE, es_MX:es-MX, es_TX:es-TX, es_PY:es-PY, el_GR:el, fa_IR:fa-IR, 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
lang_map = ar_EG:ar-EG, bg_BG:bg, de_CH:de-CH, en_GB:en-GB, es_AR:es-AR, es_CL:es-CL, es_419:es-LA, es_PE:es-PE, es_MX:es-MX, es_TX:es-TX, es_PY:es-PY, el_GR:el, fa_IR:fa-IR, fi_FI:fi, hu_HU:hu, id_ID:id, mn_MN:mn, 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
+356
View File
@@ -1,3 +1,359 @@
[Mac ChangeLog](https://github.com/wekan/wekan/wiki/Mac)
# v5.34 2021-06-11 Wekan release
This release adds the following new features:
- [View and change card sort number](https://github.com/wekan/wekan/pull/3857).
Thanks to mfilser.
- [More spinners + configureable in admin panel](https://github.com/wekan/wekan/pull/3858).
Thanks to mfilser.
- [Added remaining spinner settings](https://github.com/wekan/wekan/commit/488b765f95ad67b19630cd125543836c04eaa24f).
Thanks to xet7.
and adds the following new improvements:
- [Card Description has now the same color on view and editing](https://github.com/wekan/wekan/pull/3851).
Thanks to mfilser.
- [Development in docker container](https://github.com/wekan/wekan/pull/3852).
Thanks to mfilser.
and fixes the following bugs:
- [Fix Google SSO to access Wekan has not been working by reverting Wekan v5.31 not-working fixes
to OAUTH2_LOGIN_STYLE=redirect Has No Effect](https://github.com/wekan/wekan/commit/1e837dec11dc5cb266b83efcff4f462aa02d733d).
Thanks to unpokitodxfavor and xet7.
- [CustomFields were not created after adding 1 card](https://github.com/wekan/wekan/pull/3856).
Thanks to mfilser.
- [Try to fix BUG: Database error attempting to change a account](https://github.com/wekan/wekan/commit/762391965e6ae3cd5682d5b164131500e7d92338).
Thanks to bbyszio and xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v5.33 2021-06-10 Wekan release
This release adds the following new features:
- [Assigning a user to a team or an organization](https://github.com/wekan/wekan/pull/3850).
Thanks to Emile840.
and adds the following new improvements:
- [Custom Fields stringtemplate, autofocus the last input box](https://github.com/wekan/wekan/pull/3849).
Thanks to mfilser.
Thanks to above GitHub users for their contributions and translators for their translations.
# v5.32 2021-06-09 Wekan release
This release adds the following new features:
- [Moved many button texts etc to tooltips. Added more tooltips](https://github.com/wekan/wekan/commit/6ce5ab40a7dc013247717b5107a306eb0402cd63).
Thanks to JFa-Orkis and xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v5.31 2021-06-09 Wekan release
This release adds the following new features:
- [Admin Panel: Edit Organizations and Teams](https://github.com/wekan/wekan/issues/802).
Thanks to Emile840.
- [Admin Panel: Delete Organizations and Teams](https://github.com/wekan/wekan/commit/14b2c1309f0f910c1e46b5681d3612d7ff0cbf81).
Thanks to xet7.
- [Admin Panel Organizations/Teams: Show confirm text above delete button](https://github.com/wekan/wekan/commit/16379201704ea1a43ce14859633ffb1b9fae6710).
Thanks to xet7.
- [Gantt: Retain links created between tasks. Part 1: Database changes, not active in
MIT Wekan](https://github.com/wekan/wekan/commit/07a3a0b3882147effac890514b19ff84f1d76bdb).
Thanks to benjaminhrivera.
and adds the following updates:
- [Removed extra package](https://github.com/wekan/wekan/commit/646497c3f041e2f562d032fe28ef29169f671ac1).
Thanks to xet7.
- [Updated dependencies](https://github.com/wekan/wekan/commit/122757ca9c091e98b31d34c3abc25caa295dbdc0).
Thanks to developers of dependencies.
and fixes the following bugs:
- [Now new boards do not have any labels added by default](https://github.com/wekan/wekan/commit/481404e8d7bad7799c2ad34d6a94eaf5e87602c2).
Thanks to tedkoch and xet7.
- [Try to fix OAUTH2_LOGIN_STYLE=redirect Has No Effect](https://github.com/wekan/wekan/commit/78324263c1c78e7e9e99f153e3158e39f564b67a).
Thanks to 1ubuntuuser and xet7.
- [Try to fix: Wekan UI fails to finish import of closed Trello boards](https://github.com/wekan/wekan/commit/007e0f1c16c935ce580093a6aec31305c75d1e45).
Thanks to berezovskyi and xet7.
- [Partial Fix: Vote and Planning Poker: Setting date and time now works for some languages that have
ascii characters in date format](https://github.com/wekan/wekan/commit/57f31d443faaa32d6c7b53d81af3be133af5f040).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v5.30 2021-06-03 Wekan release
This release adds the following new features:
- [Planning Poker / Scrum Poker](https://github.com/wekan/wekan/pull/3836),
see https://github.com/wekan/wekan/wiki/Planning-Poker .
Thanks to helioguardabaxo.
and fixes the following bugs:
- [Fixed Python API example: Edit card, etc](https://github.com/wekan/wekan/commit/bf62a947fbfa7d387074550288376e682fd6ad47).
Thanks to Lucky-Shi and xet7.
- [Default language is still used although this one has been modified previously](https://github.com/wekan/wekan/pull/3833).
Thanks to Emile840.
- [Moved Keyboard Shortcuts from bottom to top of Sidebar](https://github.com/wekan/wekan/commit/659a65b8b919a49ba0beef5cc53d8e61e0f794aa).
Thanks to ClaudiaK21 and xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v5.29 2021-05-29 Wekan release
This release adds the following new features:
- [Excel parent card name export](https://github.com/wekan/wekan/pull/3799).
Thanks to marcungeschikts and Enishowk.
and adds the following updates:
- Updated dependencies
[Part 1](https://github.com/wekan/wekan/commit/62150ce6c406359fba068552b4526c60faf392bb),
[Part 2](https://github.com/wekan/wekan/commit/1d9346513e4f378379b9f5192e8dad5535287f8a),
[Part 3](https://github.com/wekan/wekan/commit/6be1a330936c89fcf478efe98dd15244a98d266d).
Thanks to developers of dependencies.
- Added updated `Forgot Password` page to GitHub issue template
[Part 1](https://github.com/wekan/wekan/commit/6d0578fd5ad5f13f5ff9a285577e35fd62bba95f),
[Part 2](https://github.com/wekan/wekan/commit/ea64b17b82cd52320c0495e16385f11031dfbe3a).
Thanks to xet7.
and fixes the following bugs:
- [Try to fix Snap: Removed linting packages](https://github.com/wekan/wekan/commit/8911fe5c8de941808585a7d3462305d5b3d2763d).
Thanks to xet7.
- [Removed not working GitHub workflow](https://github.com/wekan/wekan/commit/5dd6466c0aa7479015c72519f36c2485b16e3341).
Thanks to xet7.
- [Fix typos](https://github.com/wekan/wekan/pull/3813).
Thanks to spasche.
- [Fix: Impersonate user can now export Excel/CSV/TSV/JSON.
Impersonate user and export Excel/CSV/TSV/JSON is now logged into database table
impersonatedUsers](https://github.com/wekan/wekan/commit/3908cd5413b775d1ee549f0a95304cf9998d3855).
Thanks to xet7.
- [Fixed Importing JSON exports fails](https://github.com/wekan/wekan/commit/bd1de94312e428e56d6cf5f343098475573cba0b).
Thanks to KeptnArgo and xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v5.28 2021-05-07 Wekan release
This release adds the following new features:
- [Mermaid Diagram](https://github.com/wekan/wekan/wiki/Mermaid-Diagram).
Thanks to xuguotong and xet7.
and adds the following updates:
- Updated dependencies
[Part 1](https://github.com/wekan/wekan/commit/521ef8b6dad4f00662f22702331193c16b91b482),
[Part 2](https://github.com/wekan/wekan/commit/48255f6f1e4a0caf0be006196f28295d0825eb95),
[Part 3](https://github.com/wekan/wekan/commit/a550c255e6c3bd2d609a1a45a213cdae7ab4f74d).
Thanks to developers of dependencies.
and fixes the following bugs:
- [Fix: BG color of StartDate](https://github.com/wekan/wekan/pull/3793).
Thanks to listenerri.
Thanks to above GitHub users for their contributions and translators for their translations.
# v5.27 2021-04-29 Wekan release
This release fixes the following bugs:
- [Fixed Non-ASCII attachment filename will crash when downloading](https://github.com/wekan/wekan/commit/c2da47773552a61d45b010a095f73d2e441f687c).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v5.26 2021-04-28 Wekan release
This release adds the following new features:
- [Feature/mini card subtask count](https://github.com/wekan/wekan/pull/3765).
Thanks to ryanMushy.
and fixes the following bugs:
- [Bring back Almost-Due for Start Date](https://github.com/wekan/wekan/commit/8ca1b25daf3be60c2dc64f03830dea8437bbd8ad).
Thanks to darren-teo.
Thanks to above GitHub users for their contributions and translators for their translations.
# v5.25 2021-04-27 Wekan release
This release adds the following new features:
- [Swimlane in Export to Excel](https://github.com/wekan/wekan/pull/3764).
Thanks to Keelan and ryanMushy.
and adds the following updates:
- [Updated release scripts](https://github.com/wekan/wekan/commit/9f0f6841b01b88f5559724b047d5e245617a02c8).
Thanks to xet7.
and fixes the following bugs:
- [Added missing PostgreSQL password to ToroDB](https://github.com/wekan/wekan/commit/995de525d96946702536f0cdcb98ef281b9df94e).
Thanks to xet7.
- [Fixed language name of Deutsch (Schweiz)](https://github.com/wekan/wekan/commit/621c701bef1d09d4ddfc93be411cfad98869f0ae).
Thanks to urmel1960.
- [Bugfix/Summernote on paste](https://github.com/wekan/wekan/pull/3761).
Thanks to ryanMushy.
- [OpenAPI: Better handle nested schemas](https://github.com/wekan/wekan/pull/3762).
Thanks to bentiss.
Thanks to above GitHub users for their contributions and translators for their translations.
# v5.24 2021-04-24 Wekan release
This release adds the following new features:
- [Copy Swimlane](https://github.com/wekan/wekan/pull/3753).
Thanks to jrsupplee.
and adds the following updates:
- [Updated dependencies](https://github.com/wekan/wekan/commit/e738177e081e4a7e83fed3389f47847403551fc2).
Thanks to developers of dependencies.
and fixes the following bugs:
- [Fix Snap: Delete extra symlink that prevented building Snap](https://github.com/wekan/wekan/commit/45124a39f34a918b251a4a36fb016639b558f119).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v5.23 2021-04-22 Wekan release
This release adds the following new features:
- [Filtering by due date](https://github.com/wekan/wekan/pull/3731).
Thanks to mcrute.
and adds the following updates:
- [Updated dependencies](https://github.com/wekan/wekan/commit/676bf686c7a121b0da744afce5911807a6be48fe).
Thanks to developers of dependencies.
and fixes the following bugs:
- [Fix: Trello data without labels definition](https://github.com/wekan/wekan/pull/3733).
Thanks to jrsupplee.
- [Bug fix for Due Cards](https://github.com/wekan/wekan/pull/3741).
Thanks to jrsupplee.
- [Fix: The bg color of start at button is almost-due](https://github.com/wekan/wekan/pull/3749).
Thanks to listenerri.
Thanks to above GitHub users for their contributions and translators for their translations.
# v5.22 2021-04-16 Wekan release
This release adds the following new translations:
- Added German (Switzerland) (de_CH) (Schwiizerdütsch). Updated translations.
[Part 1](https://github.com/wekan/wekan/commit/09506c78f3c3439db622574eb851fa0c20d3a066),
[Part 2](https://github.com/wekan/wekan/commit/dce99c00be80cceba686fd73b4b78b6c778d78a6),
[Part 3](https://github.com/wekan/wekan/commit/6ff9c5b58d25ba52b11e5429c9cfe6ed6a97000e).
Thanks to translators.
and fixes the following bugs:
- [Remove allowedValues from Cards.type schema](https://github.com/wekan/wekan/pull/3724).
Thanks to jrsupplee.
Thanks to above GitHub users for their contributions and translators for their translations.
# v5.21 2021-04-16 Wekan release
This release adds the following new features:
- [Summernote Rich Text Editor](https://github.com/wekan/wekan/pull/3720):
1) Add new button to insert a URL link.
2) Add new popover allowing you to edit existing URL links.
3) Enable spell check.
4) Allow client side grammerly extension.
Thanks to ryanMushy.
and adds the following updates:
- [Upgraded to Meteor 2.2](https://github.com/wekan/wekan/commit/0e7c2b4b94b1c48e8839cfba635b53cdc1a797b1).
Thanks to Meteor developers.
and fixes the following bugs:
- [Bugfix, date format not changed to local format](https://github.com/wekan/wekan/pull/3723).
Thanks to mfilser.
Thanks to above GitHub users for their contributions and translators for their translations.
# v5.20 2021-04-14 Wekan release
This release fixes the following bugs:
- [OpenAPI: rework the allowedValues to allow for imported variables](https://github.com/wekan/wekan/pull/3715).
Thanks to bentiss.
Thanks to above GitHub users for their contributions and translators for their translations.
# v5.19 2021-04-14 Wekan release
This release adds the following new features:
- [Custom Field "String Template"](https://github.com/wekan/wekan/pull/3701).
Thanks to tod31.
- [1) Admin reports. An option added to the admin panel that has reports an admin can run.
Right now it has two reports for attachments and broken cards.
2) Add the creator avatar to `cardDetails` and `minicard`. Avatar is only shown if it is selected in card settings.
3) Added a new search operator `creator`.
4) Bug fix for multiple label predicates](https://github.com/wekan/wekan/pull/3705).
Thanks to jrsupplee.
- [Update Admin Panel Rules report icon and add missing translations](https://github.com/wekan/wekan/commit/8417fae89cc89adb4559874050ff7c56cc08eb00).
Thanks to xet7.
and adds the following updates:
- [Upgraded to Meteor 2.1.1 an updated dependencies](https://github.com/wekan/wekan/commit/bb8c4325c60582cdcda5d406071586f18681e737).
Thanks to developers of dependencies.
- [Updated to Node.js v12.22.1](https://github.com/wekan/wekan/commit/2201372744639ade3ba74b6ff9115988f011b9ac).
Thanks to Node.js developers.
- [Updated release scripts](https://github.com/wekan/wekan/commit/9871bf196352edcb5475e1b0ee4983e8f312e449).
Thanks to xet7.
- [Updated caniuse-lite etc dependencies](https://github.com/wekan/wekan/commit/0857a2ea91f672201ba96f2ba635165784b30fd8).
Thanks to developers of dependencies.
and fixes the following bugs:
- [Revert stable tag, because it did break Wekan version numbers](https://github.com/wekan/wekan/commit/5ca90f4d2245910580cb0af885fac17dcec44ef0).
Thanks to xet7.
- [Updating ARM Dockerfile](https://github.com/wekan/wekan/pull/3692).
Thanks to loganballard.
- [Added latest arm64 bundle symlink](https://github.com/wekan/wekan/commit/6fe3edebb18414ebe7e69b2de3269438662b6163).
Thanks to xet7.
- [Bug fix: Rules for moving from list/swimlane](https://github.com/wekan/wekan/pull/3706).
Thanks to jrsupplee.
- [Fixed Elements are duplicated on the view "My cards".
Rewrite routine for building the My Cards hierarchical list.
Use a separate publication for retrieving My Cards.
Fixed bug with limit and skip projection](https://github.com/wekan/wekan/pull/3708).
Thanks to jrsupplee.
- [Popover needs to be destroyed anytime the details panel is closed](https://github.com/wekan/wekan/pull/3712).
Thanks to ryanMushy.
Thanks to above GitHub users for their contributions and translators for their translations.
# v5.18 2021-04-14 Wekan release
Not released version, because some version numbers not changed in all release files.
# v5.17 2021-04-01 Wekan release
This release fixes the following bugs:
+3 -2
View File
@@ -12,7 +12,7 @@ ARG DEBIAN_FRONTEND=noninteractive
ENV BUILD_DEPS="apt-utils libarchive-tools gnupg gosu wget curl bzip2 g++ build-essential git ca-certificates python3" \
DEBUG=false \
NODE_VERSION=v12.22.0 \
NODE_VERSION=v12.22.1 \
METEOR_RELEASE=1.10.2 \
USE_EDGE=false \
METEOR_EDGE=1.5-beta.17 \
@@ -137,7 +137,8 @@ ENV BUILD_DEPS="apt-utils libarchive-tools gnupg gosu wget curl bzip2 g++ build-
SAML_IDENTIFIER_FORMAT="" \
SAML_LOCAL_PROFILE_MATCH_ATTRIBUTE="" \
SAML_ATTRIBUTES="" \
ORACLE_OIM_ENABLED=false
ORACLE_OIM_ENABLED=false \
WAIT_SPINNER=""
# Copy the app to the image
COPY ${SRC_PATH} /home/wekan/app
+9 -9
View File
@@ -4,16 +4,16 @@ FROM amd64/alpine:3.7 AS builder
ENV QEMU_VERSION=v4.2.0-6 \
QEMU_ARCHITECTURE=aarch64 \
NODE_ARCHITECTURE=linux-arm64 \
NODE_VERSION=v12.22.0 \
WEKAN_VERSION=3.96 \
NODE_VERSION=v12.22.1 \
WEKAN_VERSION=latest \
WEKAN_ARCHITECTURE=arm64
# Install dependencies
RUN apk update && apk add ca-certificates outils-sha1 && \
\
# Download qemu static for our architecture
wget https://github.com/multiarch/qemu-user-static/releases/download/${QEMU_VERSION}/qemu-${QEMU_ARCHITECTURE}-static.tar.gz -O - | tar -xz && \
\
# Install dependencies
RUN apk update && apk add ca-certificates outils-sha1 && \
\
# Download qemu static for our architecture
wget https://github.com/multiarch/qemu-user-static/releases/download/${QEMU_VERSION}/qemu-${QEMU_ARCHITECTURE}-static.tar.gz -O - | tar -xz && \
\
# Download wekan and shasum
wget https://releases.wekan.team/raspi3/wekan-${WEKAN_VERSION}-${WEKAN_ARCHITECTURE}.zip && \
wget https://releases.wekan.team/raspi3/SHA256SUMS.txt && \
@@ -40,7 +40,7 @@ LABEL maintainer="wekan"
# Set the environment variables (defaults where required)
ENV QEMU_ARCHITECTURE=aarch64 \
NODE_ARCHITECTURE=linux-arm64 \
NODE_VERSION=v12.22.0 \
NODE_VERSION=v12.22.1 \
NODE_ENV=production \
NPM_VERSION=latest \
WITH_API=true \
+1 -1
View File
@@ -52,7 +52,7 @@ that by providing one-click installation on various platforms.
- Wekan is used in [most countries of the world](https://snapcraft.io/wekan).
- Wekan largest user has 13k users using Wekan in their company.
- Wekan has been [translated](https://transifex.com/wekan/wekan) to about 62 languages.
- Wekan has been [translated](https://transifex.com/wekan/wekan) to about 63 languages.
- [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.
+1 -1
View File
@@ -1,5 +1,5 @@
appId: wekan-public/apps/77b94f60-dec9-0136-304e-16ff53095928
appVersion: "v5.17.0"
appVersion: "v5.34.0"
files:
userUploads:
- README.md
+48 -8
View File
@@ -28,12 +28,16 @@ if arguments == 0:
print(" python3 api.py board BOARDID # Info of BOARDID")
print(" python3 api.py swimlanes BOARDID # Swimlanes of BOARDID")
print(" python3 api.py lists BOARDID # Lists of BOARDID")
print(" python3 api.py list BOARDID LISTID # Info of LISTID")
print(" python3 api.py createlist BOARDID LISTTITLE # Create list")
print(" python3 api.py addcard AUTHORID BOARDID SWIMLANEID LISTID CARDTITLE CARDDESCRIPTION")
print(" python3 api.py editcard BOARDID LISTID CARDID NEWCARDTITLE NEWCARDDESCRIPTION")
print(" python3 api.py listattachments BOARDID # List attachments")
# TODO:
# print(" python3 api.py attachmentdownload BOARDID ATTACHMENTID # One attachment as file")
# print(" python3 api.py attachmentsdownload BOARDID # All attachments as files")
# print(" python3 api.py attachmentjson BOARDID ATTACHMENTID # One attachment as JSON base64")
# print(" python3 api.py attachmentbinary BOARDID ATTACHMENTID # One attachment as binary file")
# print(" python3 api.py attachmentdownload BOARDID ATTACHMENTID # One attachment as file")
# print(" python3 api.py attachmentsdownload BOARDID # All attachments as files")
exit
# ------- SETTINGS START -------------
@@ -61,13 +65,15 @@ chmod +x api.py
=== Wekan API Python CLI: Shows IDs for addcard ===
AUTHORID is USERID that writes card.
Syntax:
python3 api.py users # All users
python3 api.py boards USERID # Boards of USERID
python3 api.py board BOARDID # Info of BOARDID
python3 api.py swimlanes BOARDID # Swimlanes of BOARDID
python3 api.py lists BOARDID # Lists of BOARDID
python3 api.py users # All users
python3 api.py boards USERID # Boards of USERID
python3 api.py board BOARDID # Info of BOARDID
python3 api.py swimlanes BOARDID # Swimlanes of BOARDID
python3 api.py lists BOARDID # Lists of BOARDID
python3 api.py list BOARDID LISTID # Info of LISTID
python3 api.py createlist BOARDID LISTTITLE # Create list
python3 api.py addcard AUTHORID BOARDID SWIMLANEID LISTID CARDTITLE CARDDESCRIPTION
python3 api.py editcard BOARDID LISTID CARDID NEWCARDTITLE NEWCARDDESCRIPTION
python3 api.py listattachments BOARDID # List attachments
python3 api.py attachmentjson BOARDID ATTACHMENTID # One attachment as JSON base64
python3 api.py attachmentbinary BOARDID ATTACHMENTID # One attachment as binary file
@@ -162,6 +168,27 @@ if arguments == 7:
print(body.text)
# ------- WRITE TO CARD END -----------
if arguments == 6:
if sys.argv[1] == 'editcard':
# ------- LIST OF BOARD START -----------
boardid = sys.argv[2]
listid = sys.argv[3]
cardid = sys.argv[4]
newcardtitle = sys.argv[5]
newcarddescription = sys.argv[6]
edcard = wekanurl + apiboards + boardid + s + l + s + listid + s + cs + s + cardid
print(edcard)
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
put_data = {'title': '{}'.format(newcardtitle), 'description': '{}'.format(newcarddescription)}
body = requests.put(edcard, data=put_data, headers=headers)
print("=== EDIT CARD ===\n")
body = requests.get(edcard, headers=headers)
data2 = body.text.replace('}',"}\n")
print(data2)
# ------- LISTS OF BOARD END -----------
if arguments == 3:
if sys.argv[1] == 'createlist':
@@ -177,6 +204,19 @@ if arguments == 3:
print(body.text)
# ------- CREATE LIST END -----------
if sys.argv[1] == 'list':
# ------- LIST OF BOARD START -----------
boardid = sys.argv[2]
listid = sys.argv[3]
listone = wekanurl + apiboards + boardid + s + l + s + listid
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
print("=== INFO OF ONE LIST ===\n")
body = requests.get(listone, headers=headers)
data2 = body.text.replace('}',"}\n")
print(data2)
# ------- LISTS OF BOARD END -----------
if arguments == 2:
# ------- BOARDS LIST START -----------
@@ -217,7 +257,7 @@ if arguments == 2:
# ------- LISTS OF BOARD START -----------
boardid = sys.argv[2]
attachments = wekanurl + apiboards + boardid
lists = wekanurl + apiboards + boardid + s + l
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
print("=== LISTS ===\n")
body = requests.get(lists, headers=headers)
+12 -8
View File
@@ -1,4 +1,4 @@
import sanitizeXss from 'xss';
import DOMPurify from 'dompurify';
const activitiesPerPage = 500;
@@ -162,11 +162,15 @@ BlazeComponent.extendComponent({
{
href: source.url,
},
sanitizeXss(source.system),
DOMPurify.sanitize(source.system, {
ALLOW_UNKNOWN_PROTOCOLS: true,
}),
),
);
} else {
return sanitizeXss(source.system);
return DOMPurify.sanitize(source.system, {
ALLOW_UNKNOWN_PROTOCOLS: true,
});
}
}
return null;
@@ -190,10 +194,10 @@ BlazeComponent.extendComponent({
href: attachment.url({ download: true }),
target: '_blank',
},
sanitizeXss(attachment.name()),
DOMPurify.sanitize(attachment.name()),
),
)) ||
sanitizeXss(this.currentData().activity.attachmentName)
DOMPurify.sanitize(this.currentData().activity.attachmentName)
);
},
@@ -232,7 +236,7 @@ BlazeComponent.extendComponent({
Template.activity.helpers({
sanitize(value) {
return sanitizeXss(value);
return DOMPurify.sanitize(value, { ALLOW_UNKNOWN_PROTOCOLS: true });
},
});
@@ -246,7 +250,7 @@ function createCardLink(card) {
href: card.originRelativeUrl(),
class: 'action-card',
},
sanitizeXss(card.title),
DOMPurify.sanitize(card.title, { ALLOW_UNKNOWN_PROTOCOLS: true }),
),
)
);
@@ -263,7 +267,7 @@ function createBoardLink(board, list) {
href: board.originRelativeUrl(),
class: 'action-board',
},
sanitizeXss(text),
DOMPurify.sanitize(text, { ALLOW_UNKNOWN_PROTOCOLS: true }),
),
)
);
+1 -1
View File
@@ -1,7 +1,7 @@
template(name="commentForm")
.new-comment.js-new-comment(
class="{{#if commentFormIsOpen}}is-open{{/if}}")
+userAvatar(userId=currentUser._id)
+userAvatar(userId=currentUser._id noRemove=true)
form.js-new-comment-form
+editor(class="js-new-comment-input")
| {{getUnsavedValue 'cardComment' currentCard._id}}
+1
View File
@@ -3,6 +3,7 @@ const commentFormIsOpen = new ReactiveVar(false);
BlazeComponent.extendComponent({
onDestroyed() {
commentFormIsOpen.set(false);
$('.note-popover').hide();
},
commentFormIsOpen() {
+1 -1
View File
@@ -131,7 +131,7 @@ template(name="boardHeaderBar")
i.fa.fa-times-thin
.separator
a.board-header-btn.js-toggle-sidebar
a.board-header-btn.js-toggle-sidebar(title="{{_ 'sidebar-open'}} {{_ 'or'}} {{_ 'sidebar-close'}}")
i.fa.fa-navicon
template(name="boardVisibilityList")
+3 -2
View File
@@ -2,7 +2,8 @@ template(name="boardList")
.wrapper
ul.board-list.clearfix.js-boards
li.js-add-board
a.board-list-item.label {{_ 'add-board'}}
a.board-list-item.label(title="{{_ 'add-board'}}")
| {{_ 'add-board'}}
each boards
li(class="{{#if isStarred}}starred{{/if}}" class=colorClass).js-board
if isInvited
@@ -18,7 +19,7 @@ template(name="boardList")
else
a.js-open-board.board-list-item(href="{{pathFor 'board' id=_id slug=slug}}")
span.details
span.board-list-item-name
span.board-list-item-name(title="{{_ 'board-drag-drop-reorder-or-click-open'}}")
+viewer
= title
i.fa.js-star-board(
+9
View File
@@ -22,6 +22,15 @@ Template.boardListHeaderBar.helpers({
BlazeComponent.extendComponent({
onCreated() {
Meteor.subscribe('setting');
let currUser = Meteor.user();
let userLanguage;
if(currUser && currUser.profile){
userLanguage = currUser.profile.language
}
if (userLanguage) {
TAPi18n.setLanguage(userLanguage);
T9n.setLanguage(userLanguage);
}
},
onRendered() {
+1 -2
View File
@@ -55,6 +55,5 @@ template(name="attachmentsGalery")
unless currentUser.isCommentOnly
unless currentUser.isWorker
//li.attachment-item.add-attachment
a.js-add-attachment
a.js-add-attachment(title="{{_ 'add-attachment' }}")
i.fa.fa-plus
| {{_ 'add-attachment' }}
@@ -119,3 +119,24 @@ template(name="cardCustomField-dropdown")
if value
+viewer
= selectedItem
template(name="cardCustomField-stringtemplate")
if canModifyCard
+inlinedForm(classNames="js-card-customfield-stringtemplate")
each item in stringtemplateItems.get
input.js-card-customfield-stringtemplate-item(type="text" value=item placeholder="")
input.js-card-customfield-stringtemplate-item.last(type="text" value="" placeholder="{{_ 'custom-field-stringtemplate-item-placeholder'}}" autofocus)
.edit-controls.clearfix
button.primary(type="submit") {{_ 'save'}}
a.fa.fa-times-thin.js-close-inlined-form
else
a.js-open-inlined-form
if value
+viewer
= formattedValue
else
| {{_ 'edit'}}
else
if value
+viewer
= formattedValue
@@ -1,3 +1,6 @@
import { DatePicker } from '/client/lib/datepicker';
import Cards from '/models/cards';
Template.cardCustomFieldsPopup.helpers({
hasCustomField() {
const card = Cards.findOne(Session.get('currentCard'));
@@ -234,3 +237,90 @@ CardCustomField.register('cardCustomField');
];
}
}.register('cardCustomField-dropdown'));
// cardCustomField-stringtemplate
(class extends CardCustomField {
onCreated() {
super.onCreated();
this.stringtemplateFormat = this.data().definition.settings.stringtemplateFormat;
this.stringtemplateSeparator = this.data().definition.settings.stringtemplateSeparator;
this.stringtemplateItems = new ReactiveVar(this.data().value ?? []);
}
formattedValue() {
return (this.data().value ?? [])
.filter(value => !!value.trim())
.map(value => this.stringtemplateFormat.replace(/%\{value\}/gi, value))
.join(this.stringtemplateSeparator ?? '');
}
getItems() {
return Array.from(this.findAll('input'))
.map(input => input.value)
.filter(value => !!value.trim());
}
events() {
return [
{
'submit .js-card-customfield-stringtemplate'(event) {
event.preventDefault();
const items = this.getItems();
this.card.setCustomField(this.customFieldId, items);
},
'keydown .js-card-customfield-stringtemplate-item'(event) {
if (event.keyCode === 13) {
event.preventDefault();
if (event.metaKey || event.ctrlKey) {
this.find('button[type=submit]').click();
} else if (event.target.value.trim()) {
const inputLast = this.find('input.last');
let items = this.getItems();
if (event.target === inputLast) {
inputLast.value = '';
} else if (event.target.nextSibling === inputLast) {
inputLast.focus();
} else {
event.target.blur();
const idx = Array.from(this.findAll('input')).indexOf(
event.target,
);
items.splice(idx + 1, 0, '');
Tracker.afterFlush(() => {
const element = this.findAll('input')[idx + 1];
element.focus();
element.value = '';
});
}
this.stringtemplateItems.set(items);
}
}
},
'blur .js-card-customfield-stringtemplate-item'(event) {
if (
!event.target.value.trim() ||
event.target === this.find('input.last')
) {
const items = this.getItems();
this.stringtemplateItems.set(items);
this.find('input.last').value = '';
}
},
'click .js-close-inlined-form'(event) {
this.stringtemplateItems.set(this.data().value ?? []);
},
},
];
}
}.register('cardCustomField-stringtemplate'));
+29 -120
View File
@@ -1,122 +1,4 @@
// Helper function to replace HH with H for 24 hours format, because H allows also single-digit hours
function adjustedTimeFormat() {
return moment
.localeData()
.longDateFormat('LT')
.replace(/HH/i, 'H');
}
// Edit received, start, due & end dates
BlazeComponent.extendComponent({
template() {
return 'editCardDate';
},
onCreated() {
this.error = new ReactiveVar('');
this.card = this.data();
this.date = new ReactiveVar(moment.invalid());
},
onRendered() {
const $picker = this.$('.js-datepicker')
.datepicker({
todayHighlight: true,
todayBtn: 'linked',
language: TAPi18n.getLanguage(),
})
.on(
'changeDate',
function(evt) {
this.find('#date').value = moment(evt.date).format('L');
this.error.set('');
this.find('#time').focus();
}.bind(this),
);
if (this.date.get().isValid()) {
$picker.datepicker('update', this.date.get().toDate());
}
},
showDate() {
if (this.date.get().isValid()) return this.date.get().format('L');
return '';
},
showTime() {
if (this.date.get().isValid()) return this.date.get().format('LT');
return '';
},
dateFormat() {
return moment.localeData().longDateFormat('L');
},
timeFormat() {
return moment.localeData().longDateFormat('LT');
},
events() {
return [
{
'keyup .js-date-field'() {
// parse for localized date format in strict mode
const dateMoment = moment(this.find('#date').value, 'L', true);
if (dateMoment.isValid()) {
this.error.set('');
this.$('.js-datepicker').datepicker('update', dateMoment.toDate());
}
},
'keyup .js-time-field'() {
// parse for localized time format in strict mode
const dateMoment = moment(
this.find('#time').value,
adjustedTimeFormat(),
true,
);
if (dateMoment.isValid()) {
this.error.set('');
}
},
'submit .edit-date'(evt) {
evt.preventDefault();
// if no time was given, init with 12:00
const time =
evt.target.time.value ||
moment(new Date().setHours(12, 0, 0)).format('LT');
const newTime = moment(time, adjustedTimeFormat(), true);
const newDate = moment(evt.target.date.value, 'L', true);
const dateString = `${evt.target.date.value} ${time}`;
const newCompleteDate = moment(
dateString,
'L ' + adjustedTimeFormat(),
true,
);
if (!newTime.isValid()) {
this.error.set('invalid-time');
evt.target.time.focus();
}
if (!newDate.isValid()) {
this.error.set('invalid-date');
evt.target.date.focus();
}
if (newCompleteDate.isValid()) {
this._storeDate(newCompleteDate.toDate());
Popup.close();
} else {
if (!this.error) {
this.error.set('invalid');
}
}
},
'click .js-delete-date'(evt) {
evt.preventDefault();
this._deleteDate();
Popup.close();
},
},
];
},
});
import { DatePicker } from '/client/lib/datepicker';
Template.dateBadge.helpers({
canModifyCard() {
@@ -305,7 +187,7 @@ class CardStartDate extends CardDate {
// if dueAt or endAt exist & are > startAt, startAt doesn't need to be flagged
if ((endAt && theDate.isAfter(endAt)) || (dueAt && theDate.isAfter(dueAt)))
classes += 'long-overdue';
else if (theDate.isBefore(now, 'minute')) classes += 'almost-due';
else if (theDate.isAfter(now)) classes += '';
else classes += 'current';
return classes;
}
@@ -472,3 +354,30 @@ class VoteEndDate extends CardDate {
}
}
VoteEndDate.register('voteEndDate');
class PokerEndDate extends CardDate {
onCreated() {
super.onCreated();
const self = this;
self.autorun(() => {
self.date.set(moment(self.data().getPokerEnd()));
});
}
classes() {
const classes = 'end-date' + ' ';
return classes;
}
showDate() {
return this.date.get().format('l LT');
}
showTitle() {
return `${TAPi18n.__('card-end-on')} ${this.date.get().format('LLLL')}`;
}
events() {
return super.events().concat({
'click .js-edit-date': Popup.open('editPokerEndDate'),
});
}
}
PokerEndDate.register('pokerEndDate');
@@ -3,6 +3,7 @@ const descriptionFormIsOpen = new ReactiveVar(false);
BlazeComponent.extendComponent({
onDestroyed() {
descriptionFormIsOpen.set(false);
$('.note-popover').hide();
},
descriptionFormIsOpen() {
@@ -23,7 +23,6 @@
background-color: #fff
border: 0
box-shadow: 0 1px 2px rgba(0, 0, 0, .23)
color: #8c8c8c
height: 36px
margin: 4px 4px 6px 0
padding: 9px 11px
+268 -32
View File
@@ -5,18 +5,18 @@ template(name="cardDetails")
+editCardTitleForm
else
unless isMiniScreen
a.fa.fa-times-thin.close-card-details.js-close-card-details
a.fa.fa-times-thin.close-card-details.js-close-card-details(title="{{_ 'close-card'}}")
if currentUser.isBoardMember
a.fa.fa-navicon.card-details-menu.js-open-card-details-menu
a.fa.fa-navicon.card-details-menu.js-open-card-details-menu(title="{{_ 'cardDetailsActionsPopup-title'}}")
input.inline-input(type="text" id="cardURL_copy" value="{{ originRelativeUrl }}")
a.fa.fa-link.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
a.fa.fa-times-thin.close-card-details-mobile-web.js-close-card-details(title="{{_ 'close-card'}}")
if currentUser.isBoardMember
a.fa.fa-navicon.card-details-menu-mobile-web.js-open-card-details-menu
a.fa.fa-navicon.card-details-menu-mobile-web.js-open-card-details-menu(title="{{_ 'cardDetailsActionsPopup-title'}}")
a.fa.fa-link.card-copy-mobile-button
h2.card-details-title.js-card-title(
class="{{#if canModifyCard}}js-open-inlined-form is-editable{{/if}}")
@@ -110,15 +110,24 @@ template(name="cardDetails")
a.card-label.add-label.js-end-date
i.fa.fa-plus
hr
if currentBoard.allowsCreator
.card-details-item.card-details-item-creator
h3.card-details-item-title
i.fa.fa-user
| {{_ 'creator'}}
+userAvatar(userId=userId noRemove=true)
| {{! XXX Hack to hide syntaxic coloration /// }}
//.card-details-items
if currentBoard.allowsMembers
hr
.card-details-item.card-details-item-members
h3.card-details-item-title
i.fa.fa-users
| {{_ 'members'}}
each getMembers
+userAvatar(userId=this cardId=../_id)
each userId in getMembers
+userAvatar(userId=userId cardId=_id)
| {{! XXX Hack to hide syntaxic coloration /// }}
if canModifyCard
unless currentUser.isWorker
@@ -131,8 +140,8 @@ template(name="cardDetails")
h3.card-details-item-title
i.fa.fa-user
| {{_ 'assignee'}}
each getAssignees
+userAvatarAssignee(userId=this cardId=../_id)
each userId in getAssignees
+userAvatar(userId=userId cardId=_id assignee=true)
| {{! XXX Hack to hide syntaxic coloration /// }}
if canModifyCard
a.assignee.add-assignee.card-details-item-add-button.js-add-assignees(title="{{_ 'assignee'}}")
@@ -194,6 +203,19 @@ template(name="cardDetails")
+viewer
= getAssignedBy
if currentBoard.allowsCardSortingByNumber
.card-details-item.card-details-sort-order
h3.card-details-item-title
i.fa.fa-sort
| {{_ 'sort'}}
if canModifyCard
+inlinedForm(classNames="js-card-details-sort")
+editCardSortOrderForm
else
a.js-open-inlined-form
+viewer
= sort
//.card-details-items
if customFieldsWD
hr
@@ -234,6 +256,205 @@ template(name="cardDetails")
i.fa.fa-thumbs-down
| {{_ 'vote-against'}}
if getPokerQuestion
hr
.poker-title
div.flex
h3
i.fa.fa-thumbs-up
| {{_ 'poker-question'}}
if getPokerEnd
+pokerEndDate
div.flex
.poker-result
if expiredPoker
unless ($and currentBoard.isPublic pokerAllowNonBoardMembers )
.card-label.card-label-gray {{ pokerCount }} {{_ 'r-of' }} {{ currentBoard.activeMembers.length }}
if showPlanningPokerButtons
.poker-result
.poker-deck
.poker-card
span.inner.js-poker.js-poker-vote-one(class="{{#if $eq pokerState 'one'}}poker-voted{{/if}}") {{_ 'poker-one'}}
if $eq pokerState "one"
i.fa.fa-check
.poker-deck
.poker-card
span.inner.js-poker.js-poker-vote-two(class="{{#if $eq pokerState 'two'}}poker-voted{{/if}}") {{_ 'poker-two'}}
if $eq pokerState "two"
i.fa.fa-check
.poker-deck
.poker-card
span.inner.js-poker.js-poker-vote-three(class="{{#if $eq pokerState 'three'}}poker-voted{{/if}}") {{_ 'poker-three'}}
if $eq pokerState "three"
i.fa.fa-check
.poker-deck
.poker-card
span.inner.js-poker.js-poker-vote-five(class="{{#if $eq pokerState 'five'}}poker-voted{{/if}}") {{_ 'poker-five'}}
if $eq pokerState "five"
i.fa.fa-check
.poker-deck
.poker-card
span.inner.js-poker.js-poker-vote-eight(class="{{#if $eq pokerState 'eight'}}poker-voted{{/if}}") {{_ 'poker-eight'}}
if $eq pokerState "eight"
i.fa.fa-check
.poker-deck
.poker-card
span.inner.js-poker.js-poker-vote-thirteen(class="{{#if $eq pokerState 'thirteen'}}poker-voted{{/if}}") {{_ 'poker-thirteen'}}
if $eq pokerState "thirteen"
i.fa.fa-check
.poker-deck
.poker-card
span.inner.js-poker.js-poker-vote-twenty(class="{{#if $eq pokerState 'twenty'}}poker-voted{{/if}}") {{_ 'poker-twenty'}}
if $eq pokerState "twenty"
i.fa.fa-check
.poker-deck
.poker-card
span.inner.js-poker.js-poker-vote-forty(class="{{#if $eq pokerState 'forty'}}poker-voted{{/if}}") {{_ 'poker-forty'}}
if $eq pokerState "forty"
i.fa.fa-check
.poker-deck
.poker-card
span.inner.js-poker.js-poker-vote-one-hundred(class="{{#if $eq pokerState 'oneHundred'}}poker-voted{{/if}}") {{_ 'poker-oneHundred'}}
if $eq pokerState "oneHundred"
i.fa.fa-check
.poker-deck
.poker-card
span.inner.js-poker.js-poker-vote-unsure(class="{{#if $eq pokerState 'unsure'}}poker-voted{{/if}}") {{_ 'poker-unsure'}}
if $eq pokerState "unsure"
i.fa.fa-check
if currentUser.isBoardAdmin
button.card-details-blue.js-poker-finish(class="{{#if $eq voteState false}}poker-voted{{/if}}") {{_ 'poker-finish'}}
if expiredPoker
.poker-table
.poker-table-side-left
.poker-table-heading-left
.poker-table-row
.poker-table-cell
.poker-table-cell
| {{_ 'poker-result-votes' }}
.poker-table-cell.poker-table-cell-who
| {{_ 'poker-result-who' }}
.poker-table-body
.poker-table-row
.poker-table-cell
button.card-details-gray.js-poker.poker-card-result(class="{{#if $eq pokerWinner 1}}winner{{else}}loser{{/if}}") {{_ 'poker-one'}}
.poker-table-cell {{ pokerCountOne }}
.poker-table-cell.poker-table-cell-who
.poker-result
each m in pokerMemberOne
a.name
+userAvatar(userId=m._id noRemove=true)
.poker-table-row
.poker-table-cell
button.card-details-gray.js-poker.poker-card-result(class="{{#if $eq pokerWinner 2}}winner{{else}}loser{{/if}}") {{_ 'poker-two'}}
.poker-table-cell {{ pokerCountTwo }}
.poker-table-cell.poker-table-cell-who
.poker-result
each m in pokerMemberTwo
a.name
+userAvatar(userId=m._id noRemove=true)
.poker-table-row
.poker-table-cell
button.card-details-gray.js-poker.poker-card-result(class="{{#if $eq pokerWinner 3}}winner{{else}}loser{{/if}}") {{_ 'poker-three'}}
.poker-table-cell {{ pokerCountThree }}
.poker-table-cell.poker-table-cell-who
.poker-result
each m in pokerMemberThree
a.name
+userAvatar(userId=m._id noRemove=true)
.poker-table-row
.poker-table-cell
button.card-details-gray.js-poker.poker-card-result(class="{{#if $eq pokerWinner 5}}winner{{else}}loser{{/if}}") {{_ 'poker-five'}}
.poker-table-cell {{ pokerCountFive }}
.poker-table-cell.poker-table-cell-who
.poker-result
each m in pokerMemberFive
a.name
+userAvatar(userId=m._id noRemove=true)
.poker-table-row
.poker-table-cell
button.card-details-gray.js-poker.poker-card-result(class="{{#if $eq pokerWinner 8}}winner{{else}}loser{{/if}}") {{_ 'poker-eight'}}
.poker-table-cell {{ pokerCountEight }}
.poker-table-cell.poker-table-cell-who
.poker-result
each m in pokerMemberEight
a.name
+userAvatar(userId=m._id noRemove=true)
.poker-table-side-right
.poker-table-heading-right
.poker-table-row
.poker-table-cell
.poker-table-cell
| {{_ 'poker-result-votes' }}
.poker-table-cell.poker-table-cell-who
| {{_ 'poker-result-who' }}
.poker-table-body
.poker-table-row
.poker-table-cell
button.card-details-gray.js-poker.poker-card-result(class="{{#if $eq pokerWinner 13}}winner{{else}}loser{{/if}}") {{_ 'poker-thirteen'}}
.poker-table-cell {{ pokerCountThirteen }}
.poker-table-cell.poker-table-cell-who
.poker-result
each m in pokerMemberThirteen
a.name
+userAvatar(userId=m._id noRemove=true)
.poker-table-row
.poker-table-cell
button.card-details-gray.js-poker.poker-card-result(class="{{#if $eq pokerWinner 20}}winner{{else}}loser{{/if}}") {{_ 'poker-twenty'}}
.poker-table-cell {{ pokerCountTwenty }}
.poker-table-cell.poker-table-cell-who
.poker-result
each m in pokerMemberTwenty
a.name
+userAvatar(userId=m._id noRemove=true)
.poker-table-row
.poker-table-cell
button.card-details-gray.js-poker.poker-card-result(class="{{#if $eq pokerWinner 40}}winner{{else}}loser{{/if}}") {{_ 'poker-forty'}}
.poker-table-cell {{ pokerCountForty }}
.poker-table-cell.poker-table-cell-who
.poker-result
each m in pokerMemberForty
a.name
+userAvatar(userId=m._id noRemove=true)
.poker-table-row
.poker-table-cell
button.card-details-gray.js-poker.poker-card-result(class="{{#if $eq pokerWinner 100}}winner{{else}}loser{{/if}}") {{_ 'poker-oneHundred'}}
.poker-table-cell {{ pokerCountOneHundred }}
.poker-table-cell.poker-table-cell-who
.poker-result
each m in pokerMemberOneHundred
a.name
+userAvatar(userId=m._id noRemove=true)
.poker-table-row
.poker-table-cell
button.card-details-gray.js-poker.poker-card-result(class="{{#if $eq pokerWinner 'unsure'}}winner{{else}}loser{{/if}}") {{_ 'poker-unsure'}}
.poker-table-cell {{ pokerCountUnsure }}
.poker-table-cell.poker-table-cell-who
.poker-result
each m in pokerMemberUnsure
a.name
+userAvatar(userId=m._id noRemove=true)
if currentUser.isBoardAdmin
div.estimation-add
button.card-details-red.js-poker-replay(class="{{#if $eq voteState false}}voted{{/if}}") {{_ 'poker-replay'}}
div.estimation-add
button.js-poker-estimation
i.fa.fa-plus
| {{_ 'set-estimation'}}
input(type=text,autofocus value=getPokerEstimation,id="pokerEstimation")
//- XXX We should use "editable" to avoid repetiting ourselves
if canModifyCard
unless currentUser.isWorker
@@ -293,8 +514,8 @@ template(name="cardDetails")
i.fa.fa-history
| {{ _ 'activity'}}
if currentUser.isBoardMember
.material-toggle-switch
span.toggle-switch-title {{_ 'hide-system-messages'}}
.material-toggle-switch(title="{{_ 'hide-system-messages'}}")
//span.toggle-switch-title
if hiddenSystemMessages
input.toggle-switch(type="checkbox" id="toggleButton" checked="checked")
else
@@ -332,6 +553,12 @@ template(name="editCardAssignerForm")
button.primary.confirm.js-submit-edit-card-assigner-form(type="submit") {{_ 'save'}}
a.fa.fa-times-thin.js-close-inlined-form
template(name="editCardSortOrderForm")
input.js-edit-card-sort(type='text' autofocus value=sort dir="auto")
.edit-controls.clearfix
button.primary.confirm.js-submit-edit-card-sort-form(type="submit") {{_ 'save'}}
a.fa.fa-times-thin.js-close-inlined-form
template(name="cardDetailsActionsPopup")
ul.pop-over-list
li
@@ -353,6 +580,10 @@ template(name="cardDetailsActionsPopup")
a.js-start-voting
i.fa.fa-thumbs-up
| {{_ 'card-edit-voting'}}
li
a.js-start-planning-poker
i.fa.fa-thumbs-up
| {{_ 'card-edit-planning-poker'}}
if currentUser.isBoardAdmin
li
a.js-custom-fields
@@ -488,23 +719,6 @@ template(name="cardAssigneesPopup")
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
@@ -522,10 +736,6 @@ template(name="cardAssigneePopup")
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
@@ -633,3 +843,29 @@ template(name="negativeVoteMembersPopup")
span.full-name
= m.profile.fullname
| (<span class="username">{{ m.username }}</span>)
template(name="deletePokerPopup")
p {{_ "poker-delete-pop"}}
button.js-confirm.negate.full(type="submit") {{_ 'delete'}}
template(name="cardStartPlanningPokerPopup")
form.edit-poker-question
.fields
.check-div
a.flex(class="{{#if getPokerQuestion}}is-disabled{{else}}js-toggle-poker-allow-non-members{{/if}}")
.materialCheckBox#poker-allow-non-members(name="poker-allow-non-members" class="{{#if pokerAllowNonBoardMembers}}is-checked{{/if}}")
span {{_ 'allowNonBoardMembers'}}
.check-div.flex
i.fa.fa-hourglass-end
a.js-end-date
span
| {{_ 'card-end'}}
unless getPokerEnd
i.fa.fa-plus
if getPokerEnd
+pokerEndDate
button.primary.js-submit {{_ 'save'}}
if getPokerQuestion
if currentUser.isBoardAdmin
button.js-remove-poker.negate.wide.right {{_ 'delete'}}
+564 -155
View File
@@ -1,11 +1,18 @@
import { DatePicker } from '/client/lib/datepicker';
import Cards from '/models/cards';
import Boards from '/models/boards';
import Checklists from '/models/checklists';
import Integrations from '/models/integrations';
import Users from '/models/users';
import Lists from '/models/lists';
import CardComments from '/models/cardComments';
import { ALLOWED_COLORS } from '/config/const';
import moment from 'moment';
import { UserAvatar } from '../users/userAvatar';
const subManager = new SubsManager();
const { calculateIndexData } = Utils;
let cardColors;
Meteor.startup(() => {
cardColors = Cards.simpleSchema()._schema.color.allowedValues;
});
BlazeComponent.extendComponent({
mixins() {
return [Mixins.InfiniteScrolling];
@@ -133,6 +140,15 @@ BlazeComponent.extendComponent({
);
},
showPlanningPokerButtons() {
const card = this.currentData();
return (
(currentUser.isBoardMember() ||
(currentUser && card.pokerAllowNonBoardMembers())) &&
!card.expiredPoker()
);
},
onRendered() {
if (Meteor.settings.public.CARD_OPENED_WEBHOOK_ENABLED) {
// Send Webhook but not create Activities records ---
@@ -154,15 +170,13 @@ BlazeComponent.extendComponent({
}).fetch();
if (integrations.length > 0) {
integrations.forEach(integration => {
integrations.forEach((integration) => {
Meteor.call(
'outgoingWebhooks',
integration,
'CardSelected',
params,
() => {
return;
},
() => {},
);
});
}
@@ -290,7 +304,7 @@ BlazeComponent.extendComponent({
Utils.goBoardId(this.data().boardId);
},
'click .js-copy-link'() {
StringToCopyElement = document.getElementById('cardURL_copy');
const StringToCopyElement = document.getElementById('cardURL_copy');
StringToCopyElement.value =
window.location.origin + window.location.pathname;
StringToCopyElement.select();
@@ -322,9 +336,7 @@ BlazeComponent.extendComponent({
},
'submit .js-card-details-title'(event) {
event.preventDefault();
const title = this.currentComponent()
.getValue()
.trim();
const title = this.currentComponent().getValue().trim();
if (title) {
this.data().setTitle(title);
} else {
@@ -333,9 +345,7 @@ BlazeComponent.extendComponent({
},
'submit .js-card-details-assigner'(event) {
event.preventDefault();
const assigner = this.currentComponent()
.getValue()
.trim();
const assigner = this.currentComponent().getValue().trim();
if (assigner) {
this.data().setAssignedBy(assigner);
} else {
@@ -344,15 +354,23 @@ BlazeComponent.extendComponent({
},
'submit .js-card-details-requester'(event) {
event.preventDefault();
const requester = this.currentComponent()
.getValue()
.trim();
const requester = this.currentComponent().getValue().trim();
if (requester) {
this.data().setRequestedBy(requester);
} else {
this.data().setRequestedBy('');
}
},
'submit .js-card-details-sort'(event) {
event.preventDefault();
const sort = parseFloat(this.currentComponent()
.getValue()
.trim());
if (sort) {
let card = this.data();
card.move(card.boardId, card.swimlaneId, card.listId, sort);
}
},
'click .js-go-to-linked-card'() {
Utils.goCardId(this.data().linkedId);
},
@@ -402,125 +420,95 @@ BlazeComponent.extendComponent({
}
this.data().setVote(Meteor.userId(), newState);
},
'click .js-poker'(e) {
let newState = null;
if ($(e.target).hasClass('js-poker-vote-one')) {
newState = 'one';
this.data().setPoker(Meteor.userId(), newState);
}
if ($(e.target).hasClass('js-poker-vote-two')) {
newState = 'two';
this.data().setPoker(Meteor.userId(), newState);
}
if ($(e.target).hasClass('js-poker-vote-three')) {
newState = 'three';
this.data().setPoker(Meteor.userId(), newState);
}
if ($(e.target).hasClass('js-poker-vote-five')) {
newState = 'five';
this.data().setPoker(Meteor.userId(), newState);
}
if ($(e.target).hasClass('js-poker-vote-eight')) {
newState = 'eight';
this.data().setPoker(Meteor.userId(), newState);
}
if ($(e.target).hasClass('js-poker-vote-thirteen')) {
newState = 'thirteen';
this.data().setPoker(Meteor.userId(), newState);
}
if ($(e.target).hasClass('js-poker-vote-twenty')) {
newState = 'twenty';
this.data().setPoker(Meteor.userId(), newState);
}
if ($(e.target).hasClass('js-poker-vote-forty')) {
newState = 'forty';
this.data().setPoker(Meteor.userId(), newState);
}
if ($(e.target).hasClass('js-poker-vote-one-hundred')) {
newState = 'oneHundred';
this.data().setPoker(Meteor.userId(), newState);
}
if ($(e.target).hasClass('js-poker-vote-unsure')) {
newState = 'unsure';
this.data().setPoker(Meteor.userId(), newState);
}
},
'click .js-poker-finish'(e) {
if ($(e.target).hasClass('js-poker-finish')) {
e.preventDefault();
const now = moment().format('YYYY-MM-DD HH:mm');
this.data().setPokerEnd(now);
}
},
'click .js-poker-replay'(e) {
if ($(e.target).hasClass('js-poker-replay')) {
e.preventDefault();
this.currentCard = this.currentData();
this.currentCard.replayPoker();
this.data().unsetPokerEnd();
this.data().unsetPokerEstimation();
}
},
'click .js-poker-estimation'(event) {
event.preventDefault();
const ruleTitle = this.find('#pokerEstimation').value;
if (ruleTitle !== undefined && ruleTitle !== '') {
this.find('#pokerEstimation').value = '';
if (ruleTitle) {
this.data().setPokerEstimation(parseInt(ruleTitle, 10));
} else {
this.data().setPokerEstimation('');
}
}
},
},
];
},
}).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;
},
// only allow number input
Template.editCardSortOrderForm.onRendered(function() {
this.$('input').on("keypress paste", function() {
let keyCode = event.keyCode;
let charCode = String.fromCharCode(keyCode);
let regex = new RegExp('[-0-9.]');
let ret = regex.test(charCode);
// only working here, defining in events() doesn't handle the return value correctly
return ret;
});
});
// We extends the normal InlinedForm component to support UnsavedEdits draft
@@ -588,6 +576,7 @@ Template.cardDetailsActionsPopup.events({
'click .js-labels': Popup.open('cardLabels'),
'click .js-attachments': Popup.open('cardAttachments'),
'click .js-start-voting': Popup.open('cardStartVoting'),
'click .js-start-planning-poker': Popup.open('cardStartPlanningPoker'),
'click .js-custom-fields': Popup.open('cardCustomFields'),
'click .js-received-date': Popup.open('editCardReceivedDate'),
'click .js-start-date': Popup.open('editCardStartDate'),
@@ -603,7 +592,7 @@ Template.cardDetailsActionsPopup.events({
const minOrder = _.min(
this.list()
.cards(this.swimlaneId)
.map(c => c.sort),
.map((c) => c.sort),
);
this.move(this.boardId, this.swimlaneId, this.listId, minOrder - 1);
},
@@ -612,7 +601,7 @@ Template.cardDetailsActionsPopup.events({
const maxOrder = _.max(
this.list()
.cards(this.swimlaneId)
.map(c => c.sort),
.map((c) => c.sort),
);
this.move(this.boardId, this.swimlaneId, this.listId, maxOrder + 1);
},
@@ -631,7 +620,7 @@ Template.cardDetailsActionsPopup.events({
},
});
Template.editCardTitleForm.onRendered(function() {
Template.editCardTitleForm.onRendered(function () {
autosize(this.$('.js-edit-card-title'));
});
@@ -645,7 +634,7 @@ Template.editCardTitleForm.events({
},
});
Template.editCardRequesterForm.onRendered(function() {
Template.editCardRequesterForm.onRendered(function () {
autosize(this.$('.js-edit-card-requester'));
});
@@ -658,7 +647,7 @@ Template.editCardRequesterForm.events({
},
});
Template.editCardAssignerForm.onRendered(function() {
Template.editCardAssignerForm.onRendered(function () {
autosize(this.$('.js-edit-card-assigner'));
});
@@ -697,7 +686,7 @@ BlazeComponent.extendComponent({
},
boards() {
const boards = Boards.find(
return Boards.find(
{
archived: false,
'members.userId': Meteor.userId(),
@@ -707,7 +696,6 @@ BlazeComponent.extendComponent({
sort: { sort: 1 /* boards default sorting */ },
},
);
return boards;
},
swimlanes() {
@@ -736,7 +724,7 @@ Template.copyCardPopup.events({
'click .js-done'() {
const card = Cards.findOne(Session.get('currentCard'));
const lSelect = $('.js-select-lists')[0];
listId = lSelect.options[lSelect.selectedIndex].value;
const listId = lSelect.options[lSelect.selectedIndex].value;
const slSelect = $('.js-select-swimlanes')[0];
const swimlaneId = slSelect.options[slSelect.selectedIndex].value;
const bSelect = $('.js-select-boards')[0];
@@ -744,9 +732,7 @@ Template.copyCardPopup.events({
const textarea = $('#copy-card-title');
const title = textarea.val().trim();
// insert new card to the bottom of new list
card.sort = Lists.findOne(card.listId)
.cards()
.count();
card.sort = Lists.findOne(card.listId).cards().count();
if (title) {
card.title = title;
@@ -777,9 +763,7 @@ Template.copyChecklistToManyCardsPopup.events({
const textarea = $('#copy-card-title');
const titleEntry = textarea.val().trim();
// insert new card to the bottom of new list
card.sort = Lists.findOne(card.listId)
.cards()
.count();
card.sort = Lists.findOne(card.listId).cards().count();
if (titleEntry) {
const titleList = JSON.parse(titleEntry);
@@ -796,13 +780,13 @@ Template.copyChecklistToManyCardsPopup.events({
Filter.addException(_id);
// copy checklists
Checklists.find({ cardId: oldId }).forEach(ch => {
Checklists.find({ cardId: oldId }).forEach((ch) => {
ch.copy(_id);
});
// copy subtasks
cursor = Cards.find({ parentId: oldId });
cursor.forEach(function() {
const cursor = Cards.find({ parentId: oldId });
cursor.forEach(function () {
'use strict';
const subtask = arguments[0];
subtask.parentId = _id;
@@ -811,7 +795,7 @@ Template.copyChecklistToManyCardsPopup.events({
});
// copy card comments
CardComments.find({ cardId: oldId }).forEach(cmt => {
CardComments.find({ cardId: oldId }).forEach((cmt) => {
cmt.copy(_id);
});
}
@@ -827,7 +811,7 @@ BlazeComponent.extendComponent({
},
colors() {
return cardColors.map(color => ({ color, name: '' }));
return ALLOWED_COLORS.map((color) => ({ color, name: '' }));
},
isSelected(color) {
@@ -871,7 +855,7 @@ BlazeComponent.extendComponent({
},
boards() {
const boards = Boards.find(
return Boards.find(
{
archived: false,
'members.userId': Meteor.userId(),
@@ -883,7 +867,6 @@ BlazeComponent.extendComponent({
sort: { sort: 1 /* boards default sorting */ },
},
);
return boards;
},
cards() {
@@ -951,7 +934,7 @@ BlazeComponent.extendComponent({
}
}
},
'click .js-delete': Popup.afterConfirm('cardDelete', function() {
'click .js-delete': Popup.afterConfirm('cardDelete', function () {
Popup.close();
// verify that there are no linked cards
if (Cards.find({ linkedId: this._id }).count() === 0) {
@@ -1058,6 +1041,8 @@ BlazeComponent.extendComponent({
moment(new Date().setHours(12, 0, 0)).format('LT');
const dateString = `${evt.target.date.value} ${time}`;
/*
const newDate = moment(dateString, 'L LT', true);
if (newDate.isValid()) {
// if active vote - store it
@@ -1068,6 +1053,159 @@ BlazeComponent.extendComponent({
this.currentData().vote = { end: newDate.toDate() }; // set vote end temp
Popup.back();
}
*/
// Try to parse different date formats of all languages.
// This code is same for vote and planning poker.
const usaDate = moment(dateString, 'L LT', true);
const euroAmDate = moment(dateString, 'DD.MM.YYYY LT', true);
const euro24hDate = moment(dateString, 'DD.MM.YYYY HH.mm', true);
const eurodotDate = moment(dateString, 'DD.MM.YYYY HH:mm', true);
const minusDate = moment(dateString, 'YYYY-MM-DD HH:mm', true);
const slashDate = moment(dateString, 'DD/MM/YYYY HH.mm', true);
const dotDate = moment(dateString, 'DD/MM/YYYY HH:mm', true);
const brezhonegDate = moment(dateString, 'DD/MM/YYYY h[e]mm A', true);
const hrvatskiDate = moment(dateString, 'DD. MM. YYYY H:mm', true);
const latviaDate = moment(dateString, 'YYYY.MM.DD. H:mm', true);
const nederlandsDate = moment(dateString, 'DD-MM-YYYY HH:mm', true);
// greekDate does not work: el Greek Ελληνικά ,
// it has date format DD/MM/YYYY h:mm MM like 20/06/2021 11:15 MM
// where MM is maybe some text like AM/PM ?
// Also some other languages that have non-ascii characters in dates
// do not work.
const greekDate = moment(dateString, 'DD/MM/YYYY h:mm A', true);
const macedonianDate = moment(dateString, 'D.MM.YYYY H:mm', true);
if (usaDate.isValid()) {
// if active poker - store it
if (this.currentData().getPokerQuestion()) {
this._storeDate(usaDate.toDate());
Popup.close();
} else {
this.currentData().poker = { end: usaDate.toDate() }; // set poker end temp
Popup.back();
}
} else if (euroAmDate.isValid()) {
// if active poker - store it
if (this.currentData().getPokerQuestion()) {
this._storeDate(euroAmDate.toDate());
Popup.close();
} else {
this.currentData().poker = { end: euroAmDate.toDate() }; // set poker end temp
Popup.back();
}
} else if (euro24hDate.isValid()) {
// if active poker - store it
if (this.currentData().getPokerQuestion()) {
this._storeDate(euro24hDate.toDate());
this.card.setPokerEnd(euro24hDate.toDate());
Popup.close();
} else {
this.currentData().poker = { end: euro24hDate.toDate() }; // set poker end temp
Popup.back();
}
} else if (eurodotDate.isValid()) {
// if active poker - store it
if (this.currentData().getPokerQuestion()) {
this._storeDate(eurodotDate.toDate());
this.card.setPokerEnd(eurodotDate.toDate());
Popup.close();
} else {
this.currentData().poker = { end: eurodotDate.toDate() }; // set poker end temp
Popup.back();
}
} else if (minusDate.isValid()) {
// if active poker - store it
if (this.currentData().getPokerQuestion()) {
this._storeDate(minusDate.toDate());
this.card.setPokerEnd(minusDate.toDate());
Popup.close();
} else {
this.currentData().poker = { end: minusDate.toDate() }; // set poker end temp
Popup.back();
}
} else if (slashDate.isValid()) {
// if active poker - store it
if (this.currentData().getPokerQuestion()) {
this._storeDate(slashDate.toDate());
this.card.setPokerEnd(slashDate.toDate());
Popup.close();
} else {
this.currentData().poker = { end: slashDate.toDate() }; // set poker end temp
Popup.back();
}
} else if (dotDate.isValid()) {
// if active poker - store it
if (this.currentData().getPokerQuestion()) {
this._storeDate(dotDate.toDate());
this.card.setPokerEnd(dotDate.toDate());
Popup.close();
} else {
this.currentData().poker = { end: dotDate.toDate() }; // set poker end temp
Popup.back();
}
} else if (brezhonegDate.isValid()) {
// if active poker - store it
if (this.currentData().getPokerQuestion()) {
this._storeDate(brezhonegDate.toDate());
this.card.setPokerEnd(brezhonegDate.toDate());
Popup.close();
} else {
this.currentData().poker = { end: brezhonegDate.toDate() }; // set poker end temp
Popup.back();
}
} else if (hrvatskiDate.isValid()) {
// if active poker - store it
if (this.currentData().getPokerQuestion()) {
this._storeDate(hrvatskiDate.toDate());
this.card.setPokerEnd(hrvatskiDate.toDate());
Popup.close();
} else {
this.currentData().poker = { end: hrvatskiDate.toDate() }; // set poker end temp
Popup.back();
}
} else if (latviaDate.isValid()) {
// if active poker - store it
if (this.currentData().getPokerQuestion()) {
this._storeDate(latviaDate.toDate());
this.card.setPokerEnd(latviaDate.toDate());
Popup.close();
} else {
this.currentData().poker = { end: latviaDate.toDate() }; // set poker end temp
Popup.back();
}
} else if (nederlandsDate.isValid()) {
// if active poker - store it
if (this.currentData().getPokerQuestion()) {
this._storeDate(nederlandsDate.toDate());
this.card.setPokerEnd(nederlandsDate.toDate());
Popup.close();
} else {
this.currentData().poker = { end: nederlandsDate.toDate() }; // set poker end temp
Popup.back();
}
} else if (greekDate.isValid()) {
// if active poker - store it
if (this.currentData().getPokerQuestion()) {
this._storeDate(greekDate.toDate());
this.card.setPokerEnd(greekDate.toDate());
Popup.close();
} else {
this.currentData().poker = { end: greekDate.toDate() }; // set poker end temp
Popup.back();
}
} else if (macedonianDate.isValid()) {
// if active poker - store it
if (this.currentData().getPokerQuestion()) {
this._storeDate(macedonianDate.toDate());
this.card.setPokerEnd(macedonianDate.toDate());
Popup.close();
} else {
this.currentData().poker = { end: macedonianDate.toDate() }; // set poker end temp
Popup.back();
}
} else {
this.error.set('invalid-date');
evt.target.date.focus();
@@ -1089,6 +1227,277 @@ BlazeComponent.extendComponent({
}
}.register('editVoteEndDatePopup'));
BlazeComponent.extendComponent({
onCreated() {
this.currentCard = this.currentData();
this.pokerQuestion = new ReactiveVar(this.currentCard.pokerQuestion);
},
events() {
return [
{
'click .js-end-date': Popup.open('editPokerEndDate'),
'submit .edit-poker-question'(evt) {
evt.preventDefault();
const pokerQuestion = true;
const allowNonBoardMembers = $('#poker-allow-non-members').hasClass(
'is-checked',
);
const endString = this.currentCard.getPokerEnd();
this.currentCard.setPokerQuestion(
pokerQuestion,
allowNonBoardMembers,
);
if (endString) {
this.currentCard.setPokerEnd(endString);
}
Popup.close();
},
'click .js-remove-poker': Popup.afterConfirm('deletePoker', (event) => {
event.preventDefault();
this.currentCard.unsetPoker();
Popup.close();
}),
'click a.js-toggle-poker-allow-non-members'(event) {
event.preventDefault();
$('#poker-allow-non-members').toggleClass('is-checked');
},
},
];
},
}).register('cardStartPlanningPokerPopup');
// editPokerEndDatePopup
(class extends DatePicker {
onCreated() {
super.onCreated(moment().format('YYYY-MM-DD HH:mm'));
this.data().getPokerEnd() &&
this.date.set(moment(this.data().getPokerEnd()));
}
/*
Tried to use dateFormat and timeFormat from client/components/lib/datepicker.js
to make detecting all date formats not necessary,
but got error "language mk does not exist".
Maybe client/components/lib/datepicker.jade could have hidden input field for
datepicker format that could be used to detect date format?
dateFormat() {
return moment.localeData().longDateFormat('L');
}
timeFormat() {
return moment.localeData().longDateFormat('LT');
}
const newDate = moment(dateString, dateformat() + ' ' + timeformat(), true);
*/
events() {
return [
{
'submit .edit-date'(evt) {
evt.preventDefault();
// if no time was given, init with 12:00
const time =
evt.target.time.value ||
moment(new Date().setHours(12, 0, 0)).format('LT');
const dateString = `${evt.target.date.value} ${time}`;
/*
Tried to use dateFormat and timeFormat from client/components/lib/datepicker.js
to make detecting all date formats not necessary,
but got error "language mk does not exist".
Maybe client/components/lib/datepicker.jade could have hidden input field for
datepicker format that could be used to detect date format?
const newDate = moment(dateString, dateformat() + ' ' + timeformat(), true);
if (newDate.isValid()) {
// if active poker - store it
if (this.currentData().getPokerQuestion()) {
this._storeDate(newDate.toDate());
Popup.close();
} else {
this.currentData().poker = { end: newDate.toDate() }; // set poker end temp
Popup.back();
}
*/
// Try to parse different date formats of all languages.
// This code is same for vote and planning poker.
const usaDate = moment(dateString, 'L LT', true);
const euroAmDate = moment(dateString, 'DD.MM.YYYY LT', true);
const euro24hDate = moment(dateString, 'DD.MM.YYYY HH.mm', true);
const eurodotDate = moment(dateString, 'DD.MM.YYYY HH:mm', true);
const minusDate = moment(dateString, 'YYYY-MM-DD HH:mm', true);
const slashDate = moment(dateString, 'DD/MM/YYYY HH.mm', true);
const dotDate = moment(dateString, 'DD/MM/YYYY HH:mm', true);
const brezhonegDate = moment(dateString, 'DD/MM/YYYY h[e]mm A', true);
const hrvatskiDate = moment(dateString, 'DD. MM. YYYY H:mm', true);
const latviaDate = moment(dateString, 'YYYY.MM.DD. H:mm', true);
const nederlandsDate = moment(dateString, 'DD-MM-YYYY HH:mm', true);
// greekDate does not work: el Greek Ελληνικά ,
// it has date format DD/MM/YYYY h:mm MM like 20/06/2021 11:15 MM
// where MM is maybe some text like AM/PM ?
// Also some other languages that have non-ascii characters in dates
// do not work.
const greekDate = moment(dateString, 'DD/MM/YYYY h:mm A', true);
const macedonianDate = moment(dateString, 'D.MM.YYYY H:mm', true);
if (usaDate.isValid()) {
// if active poker - store it
if (this.currentData().getPokerQuestion()) {
this._storeDate(usaDate.toDate());
Popup.close();
} else {
this.currentData().poker = { end: usaDate.toDate() }; // set poker end temp
Popup.back();
}
} else if (euroAmDate.isValid()) {
// if active poker - store it
if (this.currentData().getPokerQuestion()) {
this._storeDate(euroAmDate.toDate());
Popup.close();
} else {
this.currentData().poker = { end: euroAmDate.toDate() }; // set poker end temp
Popup.back();
}
} else if (euro24hDate.isValid()) {
// if active poker - store it
if (this.currentData().getPokerQuestion()) {
this._storeDate(euro24hDate.toDate());
this.card.setPokerEnd(euro24hDate.toDate());
Popup.close();
} else {
this.currentData().poker = { end: euro24hDate.toDate() }; // set poker end temp
Popup.back();
}
} else if (eurodotDate.isValid()) {
// if active poker - store it
if (this.currentData().getPokerQuestion()) {
this._storeDate(eurodotDate.toDate());
this.card.setPokerEnd(eurodotDate.toDate());
Popup.close();
} else {
this.currentData().poker = { end: eurodotDate.toDate() }; // set poker end temp
Popup.back();
}
} else if (minusDate.isValid()) {
// if active poker - store it
if (this.currentData().getPokerQuestion()) {
this._storeDate(minusDate.toDate());
this.card.setPokerEnd(minusDate.toDate());
Popup.close();
} else {
this.currentData().poker = { end: minusDate.toDate() }; // set poker end temp
Popup.back();
}
} else if (slashDate.isValid()) {
// if active poker - store it
if (this.currentData().getPokerQuestion()) {
this._storeDate(slashDate.toDate());
this.card.setPokerEnd(slashDate.toDate());
Popup.close();
} else {
this.currentData().poker = { end: slashDate.toDate() }; // set poker end temp
Popup.back();
}
} else if (dotDate.isValid()) {
// if active poker - store it
if (this.currentData().getPokerQuestion()) {
this._storeDate(dotDate.toDate());
this.card.setPokerEnd(dotDate.toDate());
Popup.close();
} else {
this.currentData().poker = { end: dotDate.toDate() }; // set poker end temp
Popup.back();
}
} else if (brezhonegDate.isValid()) {
// if active poker - store it
if (this.currentData().getPokerQuestion()) {
this._storeDate(brezhonegDate.toDate());
this.card.setPokerEnd(brezhonegDate.toDate());
Popup.close();
} else {
this.currentData().poker = { end: brezhonegDate.toDate() }; // set poker end temp
Popup.back();
}
} else if (hrvatskiDate.isValid()) {
// if active poker - store it
if (this.currentData().getPokerQuestion()) {
this._storeDate(hrvatskiDate.toDate());
this.card.setPokerEnd(hrvatskiDate.toDate());
Popup.close();
} else {
this.currentData().poker = { end: hrvatskiDate.toDate() }; // set poker end temp
Popup.back();
}
} else if (latviaDate.isValid()) {
// if active poker - store it
if (this.currentData().getPokerQuestion()) {
this._storeDate(latviaDate.toDate());
this.card.setPokerEnd(latviaDate.toDate());
Popup.close();
} else {
this.currentData().poker = { end: latviaDate.toDate() }; // set poker end temp
Popup.back();
}
} else if (nederlandsDate.isValid()) {
// if active poker - store it
if (this.currentData().getPokerQuestion()) {
this._storeDate(nederlandsDate.toDate());
this.card.setPokerEnd(nederlandsDate.toDate());
Popup.close();
} else {
this.currentData().poker = { end: nederlandsDate.toDate() }; // set poker end temp
Popup.back();
}
} else if (greekDate.isValid()) {
// if active poker - store it
if (this.currentData().getPokerQuestion()) {
this._storeDate(greekDate.toDate());
this.card.setPokerEnd(greekDate.toDate());
Popup.close();
} else {
this.currentData().poker = { end: greekDate.toDate() }; // set poker end temp
Popup.back();
}
} else if (macedonianDate.isValid()) {
// if active poker - store it
if (this.currentData().getPokerQuestion()) {
this._storeDate(macedonianDate.toDate());
this.card.setPokerEnd(macedonianDate.toDate());
Popup.close();
} else {
this.currentData().poker = { end: macedonianDate.toDate() }; // set poker end temp
Popup.back();
}
} else {
// this.error.set('invalid-date);
this.error.set('invalid-date' + ' ' + dateString);
evt.target.date.focus();
}
},
'click .js-delete-date'(evt) {
evt.preventDefault();
this._deleteDate();
Popup.close();
},
},
];
}
_storeDate(newDate) {
this.card.setPokerEnd(newDate);
}
_deleteDate() {
this.card.unsetPokerEnd();
}
}.register('editPokerEndDatePopup'));
// Close the card details pane by pressing escape
EscapeActions.register(
'detailsPane',
+129
View File
@@ -211,6 +211,7 @@ avatar-radius = 50%
word-wrap: break-word
max-width: 36%
flex-grow: 1
&.card-details-item-creator,
&.card-details-item-received,
&.card-details-item-start,
&.card-details-item-due,
@@ -356,3 +357,131 @@ card-details-color(background, color...)
display: flex
.js-show-positive-votes
cursor: pointer
.poker-voted
opacity: .7
.poker-title
display: flex
justify-content: space-between
.js-edit-date
align-self: baseline
margin-left: 5px
.poker-result
display: flex
flex-flow: row wrap
.js-show-positive-poker-votes
cursor: pointer
.poker-deck
display: grid
flex-direction: column
text-align: center
.poker-card-result
width: 32px
font-size: 1em
font-weight: bold
padding: 4px 2px 4px 2px
cursor: default
.winner
font-weight: bold
outline: #2d2d2d solid 2px
.loser
opacity: .5
.responsive-table
overflow-x: auto
.poker-table
display: table
width: 100%
padding-top: 10px
.poker-table-row
display: table-row
.poker-table-heading
background-color: #EEE
display: table-header-group
.poker-table-cell
display: table-cell
padding: 0 0 5px 2px
border-bottom: 1px solid #d2d0d0
text-align: center
min-width: 45px
.poker-table-cell-who
width: 150px
vertical-align: middle
.poker-table-heading-left,
.poker-table-heading-right
display: table-header-group
font-weight: bold
border-top: 1px solid #808080
@media (max-width: 400px)
.poker-table-heading-right
display: none
.poker-table-body
display: table-row-group
.poker-table-side-left,
.poker-table-side-right
display: inline-block
.poker-table-side-right
padding-left: 10px
@media (max-width: 400px)
.poker-table-side-right
padding-left: 0px
.estimation-add
display: block
overflow: auto
margin-top: 15px
margin-bottom: 5px
input
display: inline-block
float: right
margin: auto
margin-right: 10px
width: 100px
button
display: inline-block
float: right
margin: auto
.poker-card
width:48px
height:72px
float:left
background:#fff
border-radius:5px
display:table
box-sizing:border-box
padding:5px
margin:3px
font-size:20px
font-weight: bold
text-shadow: #2d2d2d 1px 1px 0
box-shadow:0 0 5px #aaaaaa
text-align:center
position:relative
cursor: pointer
.inner
display:table-cell
vertical-align:middle
border-radius:5px
overflow:hidden
background-color: #cecece
+4 -6
View File
@@ -4,8 +4,8 @@ template(name="checklists")
i.fa.fa-check
| {{_ 'checklists'}}
if currentUser.isBoardMember
.material-toggle-switch
span.toggle-switch-title {{_ 'hide-checked-items'}}
.material-toggle-switch(title="{{_ 'hide-checked-items'}}")
//span.toggle-switch-title
if hideCheckedItems
input.toggle-switch(type="checkbox" id="toggleHideCheckedItemsButton" checked="checked")
else
@@ -25,9 +25,8 @@ template(name="checklists")
+inlinedForm(autoclose=false classNames="js-add-checklist" cardId = cardId)
+addChecklistItemForm
else
a.js-open-inlined-form
a.js-open-inlined-form(title="{{_ 'add-checklist'}}")
i.fa.fa-plus
| {{_ 'add-checklist'}}...
template(name="checklistDetail")
.js-checklist.checklist
@@ -93,9 +92,8 @@ template(name="checklistItems")
+inlinedForm(autoclose=false classNames="js-add-checklist-item" checklist = checklist)
+addChecklistItemForm
else
a.add-checklist-item.js-open-inlined-form
a.add-checklist-item.js-open-inlined-form(title="{{_ 'add-checklist-item'}}")
i.fa.fa-plus
| {{_ 'add-checklist-item'}}...
template(name='checklistItemDetail')
.js-checklist-item.checklist-item(class="{{#if item.isFinished }}is-checked{{#if hideCheckedItems}} invisible{{/if}}{{/if}}")
+21
View File
@@ -82,6 +82,9 @@ template(name="minicard")
+minicardCustomFieldDate
else if $eq definition.type "checkbox"
.materialCheckBox(class="{{#if value }}is-checked{{/if}}")
else if $eq definition.type "stringtemplate"
+viewer
= formattedStringtemplateCustomFieldValue(definition)
else
+viewer
= trueValue
@@ -96,6 +99,10 @@ template(name="minicard")
each getMembers
+userAvatar(userId=this)
if showCreator
.minicard-creator
+userAvatar(userId=this.userId noRemove=true)
.badges
unless currentUser.isNoComments
if comments.count
@@ -114,6 +121,11 @@ template(name="minicard")
span.badge-text {{ voteCountPositive }}
span.badge-icon.fa.fa-thumbs-down(class="{{#if $eq voteState false}}text-red{{/if}}")
span.badge-text {{ voteCountNegative }}
if getPokerQuestion
.badge.badge-state-image-only(title=getPokerQuestion)
span.badge-icon.fa.fa-check(class="{{#if pokerState}}text-green{{/if}}")
if expiredPoker
span.badge-text {{ getPokerEstimation }}
if attachments.count
.badge
span.badge-icon.fa.fa-paperclip
@@ -122,3 +134,12 @@ template(name="minicard")
.badge(class="{{#if checklistFinished}}is-finished{{/if}}")
span.badge-icon.fa.fa-check-square-o
span.badge-text.check-list-text {{checklistFinishedCount}}/{{checklistItemCount}}
if allSubtasks.count
.badge
span.badge-icon.fa.fa-sitemap
span.badge-text.check-list-text {{subtasksFinishedCount}}/{{allSubtasksCount}}
//{{subtasksFinishedCount}}/{{subtasksCount}} does not work because when a subtaks is archived, the count goes down
if currentBoard.allowsCardSortingByNumber
.badge
span.badge-icon.fa.fa-sort
span.badge-text {{ sort }}
+28
View File
@@ -21,6 +21,34 @@ BlazeComponent.extendComponent({
}).format(customFieldTrueValue);
},
formattedStringtemplateCustomFieldValue(definition) {
const customField = this.data()
.customFieldsWD()
.find(f => f._id === definition._id);
const customFieldTrueValue =
customField && customField.trueValue ? customField.trueValue : [];
return customFieldTrueValue
.filter(value => !!value.trim())
.map(value =>
definition.settings.stringtemplateFormat.replace(/%\{value\}/gi, value),
)
.join(definition.settings.stringtemplateSeparator ?? '');
},
showCreator() {
if (this.data().board()) {
return (
this.data().board.allowsCreator === null ||
this.data().board().allowsCreator === undefined ||
this.data().board().allowsCreator
);
// return this.data().board().allowsCreator;
}
return false;
},
events() {
return [
{
+6 -2
View File
@@ -89,7 +89,7 @@
border-radius: 2px
margin-right: 3px
margin-bottom: 3px
.minicard-custom-fields
display:block;
.minicard-custom-field
@@ -163,7 +163,8 @@
line-height: 12px
.minicard-members,
.minicard-assignees
.minicard-assignees,
.minicard-creator
float: right
margin-left: 5px
margin-bottom: 4px
@@ -187,6 +188,9 @@
.minicard-assignees
border-bottom: 1px solid red
.minicard-creator
border-bottom: 1px solid green
.minicard-members:empty,
.minicard-assignees:empty
display: none
+1 -2
View File
@@ -15,9 +15,8 @@ template(name="subtasks")
+inlinedForm(autoclose=false classNames="js-add-subtask" cardId = cardId)
+addSubtaskItemForm
else
a.js-open-inlined-form
a.js-open-inlined-form(title="{{_ 'add-subtask'}}")
i.fa.fa-plus
| {{_ 'add-subtask'}}...
template(name="subtaskDetail")
.js-subtasks.subtask
+4 -9
View File
@@ -19,19 +19,14 @@ template(name="listBody")
+inlinedForm(autoclose=false position="bottom")
+addCardForm(listId=_id position="bottom")
else
a.open-minicard-composer.js-card-composer.js-open-inlined-form
a.open-minicard-composer.js-card-composer.js-open-inlined-form(title="{{_ 'add-card-to-bottom-of-list'}}")
i.fa.fa-plus
| {{_ 'add-card'}}
template(name="spinnerList")
.sk-spinner.sk-spinner-wave.sk-spinner-list(
class=currentBoard.colorClass
.sk-spinner.sk-spinner-list(
class="{{currentBoard.colorClass}} {{getSkSpinnerName}}"
id="showMoreResults")
.sk-rect1
.sk-rect2
.sk-rect3
.sk-rect4
.sk-rect5
+spinnerRaw
template(name="addCardForm")
.minicard.minicard-composer.js-composer
+40 -29
View File
@@ -1,3 +1,5 @@
import { Spinner } from '/client/lib/spinner';
const subManager = new SubsManager();
const InfiniteScrollIter = 10;
@@ -116,8 +118,6 @@ BlazeComponent.extendComponent({
if (position === 'bottom') {
this.scrollToBottom();
}
formComponent.reset();
}
},
@@ -698,7 +698,7 @@ BlazeComponent.extendComponent({
},
}).register('searchElementPopup');
BlazeComponent.extendComponent({
(class extends Spinner {
onCreated() {
this.cardlimit = this.parentComponent().cardlimit;
@@ -726,7 +726,7 @@ BlazeComponent.extendComponent({
.parentComponent()
.data()._id;
}
},
}
onRendered() {
this.spinner = this.find('.sk-spinner-list');
@@ -741,47 +741,58 @@ BlazeComponent.extendComponent({
);
this.updateList();
},
}
onDestroyed() {
$(this.container).off(`scroll.spinner_${this.swimlaneId}_${this.listId}`);
$(window).off(`resize.spinner_${this.swimlaneId}_${this.listId}`);
},
}
checkIdleTime() {
return window.requestIdleCallback ||
function(handler) {
const startTime = Date.now();
return setTimeout(function() {
handler({
didTimeout: false,
timeRemaining() {
return Math.max(0, 50.0 - (Date.now() - startTime));
},
});
}, 1);
};
}
updateList() {
// Use fallback when requestIdleCallback is not available on iOS and Safari
// https://www.afasterweb.com/2017/11/20/utilizing-idle-moments/
checkIdleTime =
window.requestIdleCallback ||
function(handler) {
const startTime = Date.now();
return setTimeout(function() {
handler({
didTimeout: false,
timeRemaining() {
return Math.max(0, 50.0 - (Date.now() - startTime));
},
});
}, 1);
};
if (this.spinnerInView()) {
this.cardlimit.set(this.cardlimit.get() + InfiniteScrollIter);
checkIdleTime(() => this.updateList());
this.checkIdleTime(() => this.updateList());
}
},
}
spinnerInView() {
const parentViewHeight = this.container.clientHeight;
const bottomViewPosition = this.container.scrollTop + parentViewHeight;
const threshold = this.spinner.offsetTop;
// spinner deleted
if (!this.spinner.offsetTop) {
return false;
}
return bottomViewPosition > threshold;
},
}).register('spinnerList');
const parentViewHeight = this.container.clientHeight;
const bottomViewPosition = this.container.scrollTop + parentViewHeight;
let spinnerOffsetTop = this.spinner.offsetTop;
const addCard = $(this.container).find("a.open-minicard-composer").first()[0];
if (addCard !== undefined) {
spinnerOffsetTop -= addCard.clientHeight;
}
return bottomViewPosition > spinnerOffsetTop;
}
getSkSpinnerName() {
return "sk-spinner-" + super.getSpinnerName().toLowerCase();
}
}.register('spinnerList'));
+4 -4
View File
@@ -28,8 +28,8 @@ template(name="listHeader")
div.list-header-menu
unless currentUser.isCommentOnly
if canSeeAddCard
a.js-add-card.fa.fa-plus.list-header-plus-icon
a.fa.fa-navicon.js-open-list-menu
a.js-add-card.fa.fa-plus.list-header-plus-icon(title="{{_ 'add-card-to-top-of-list'}}")
a.fa.fa-navicon.js-open-list-menu(title="{{_ 'listActionPopup-title'}}")
else
a.list-header-menu-icon.fa.fa-angle-right.js-select-list
a.list-header-handle.handle.fa.fa-arrows.js-list-handle
@@ -41,8 +41,8 @@ template(name="listHeader")
//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
a.js-add-card.fa.fa-plus.list-header-plus-icon(title="{{_ 'add-card-to-top-of-list'}}")
a.fa.fa-navicon.js-open-list-menu(title="{{_ 'listActionPopup-title'}}")
if currentUser.isBoardAdmin
if showDesktopDragHandles
a.list-header-handle.handle.fa.fa-arrows.js-list-handle
+3 -3
View File
@@ -3,7 +3,7 @@ import {
OPERATOR_HAS,
OPERATOR_SORT,
OPERATOR_USER,
ORDER_DESCENDING,
ORDER_ASCENDING,
PREDICATE_DUE_AT,
} from '../../../config/search-const';
import { QueryParams } from '../../../config/query-classes';
@@ -62,14 +62,14 @@ class DueCardsComponent extends CardSearchPagedComponent {
// queryParams[OPERATOR_LIMIT] = 5;
queryParams.addPredicate(OPERATOR_SORT, {
name: PREDICATE_DUE_AT,
order: ORDER_DESCENDING,
order: ORDER_ASCENDING,
});
if (Utils.dueCardsView() !== 'all') {
queryParams.addPredicate(OPERATOR_USER, Meteor.user().username);
}
this.runGlobalSearch(queryParams.getQueryParams());
this.runGlobalSearch(queryParams);
}
dueCardsView() {
+28 -11
View File
@@ -49,7 +49,7 @@ Template.editor.onRendered(() => {
['para', ['ul', 'ol', 'paragraph']],
['table', ['table']],
//['insert', ['link', 'picture', 'video']], // iframe tag will be sanitized TODO if iframe[class=note-video-clip] can be added into safe list, insert video can be enabled
//['insert', ['link', 'picture']], // modal popup has issue somehow :(
['insert', ['link']], //, 'picture']], // modal popup has issue somehow :(
['view', ['fullscreen', 'help']],
];
const cleanPastedHTML = function(input) {
@@ -145,6 +145,7 @@ Template.editor.onRendered(() => {
const MAX_IMAGE_PIXEL = Utils.MAX_IMAGE_PIXEL;
const COMPRESS_RATIO = Utils.IMAGE_COMPRESS_RATIO;
const insertImage = src => {
// process all image upload types to the description/comment window
const img = document.createElement('img');
img.src = src;
img.setAttribute('width', '100%');
@@ -210,7 +211,16 @@ Template.editor.onRendered(() => {
}
}
},
onPaste() {
onPaste(e) {
var clipboardData = e.clipboardData;
var pastedData = clipboardData.getData('Text');
//if pasted data is an image, exit
if (!pastedData.length) {
e.preventDefault();
return;
}
// clear up unwanted tag info when user pasted in text
const thisNote = this;
const updatePastedText = function(object) {
@@ -219,7 +229,7 @@ Template.editor.onRendered(() => {
// (and multiplies by pasting more) by changing paste "p" to "br".
// Fixes https://github.com/wekan/wekan/2890 .
// == Fix Start ==
someNote.execCommand('defaultParagraphSeparator', false, 'br');
//someNote.execCommand('defaultParagraphSeparator', false, 'br');
// == Fix End ==
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.
@@ -234,17 +244,17 @@ Template.editor.onRendered(() => {
},
},
dialogsInBody: true,
disableDragAndDrop: true,
spellCheck: true,
disableGrammar: false,
disableDragAndDrop: false,
toolbar,
popover: {
image: [
[
'image',
['resizeFull', 'resizeHalf', 'resizeQuarter', 'resizeNone'],
],
['imagesize', ['imageSize100', 'imageSize50', 'imageSize25']],
['float', ['floatLeft', 'floatRight', 'floatNone']],
['remove', ['removeMedia']],
],
link: [['link', ['linkDialogShow', 'unlink']]],
table: [
['add', ['addRowDown', 'addRowUp', 'addColLeft', 'addColRight']],
['delete', ['deleteRow', 'deleteCol', 'deleteTable']],
@@ -263,10 +273,12 @@ Template.editor.onRendered(() => {
}
});
import sanitizeXss from 'xss';
import DOMPurify from 'dompurify';
// Additional safeAttrValue function to allow for other specific protocols
// See https://github.com/leizongmin/js-xss/issues/52#issuecomment-241354114
/*
function mySafeAttrValue(tag, name, value, cssFilter) {
// only when the tag is 'a' and attribute is 'href'
// then use your custom function
@@ -292,6 +304,7 @@ function mySafeAttrValue(tag, name, value, cssFilter) {
return sanitizeXss.safeAttrValue(tag, name, value, cssFilter);
}
}
*/
// XXX I believe we should compute a HTML rendered field on the server that
// would handle markdown and user mentions. We can simply have two
@@ -307,7 +320,9 @@ Blaze.Template.registerHelper(
let content = Blaze.toHTML(view.templateContentBlock);
const currentBoard = Boards.findOne(Session.get('currentBoard'));
if (!currentBoard)
return HTML.Raw(sanitizeXss(content, { safeAttrValue: mySafeAttrValue }));
return HTML.Raw(
DOMPurify.sanitize(content, { ALLOW_UNKNOWN_PROTOCOLS: true }),
);
const knowedUsers = currentBoard.members.map(member => {
const u = Users.findOne(member.userId);
if (u) {
@@ -351,7 +366,9 @@ Blaze.Template.registerHelper(
content = content.replace(fullMention, Blaze.toHTML(link));
}
return HTML.Raw(sanitizeXss(content, { safeAttrValue: mySafeAttrValue }));
return HTML.Raw(
DOMPurify.sanitize(content, { ALLOW_UNKNOWN_PROTOCOLS: true }),
);
}),
);
+5 -5
View File
@@ -11,9 +11,10 @@ template(name="globalSearchModalTitle")
| {{_ 'globalSearch-title'}}
template(name="resultsPaged")
h1
= resultsHeading.get
a.fa.fa-link(title="{{_ 'link-to-search' }}" href="{{ getSearchHref }}")
if resultsHeading.get
h1
= resultsHeading.get
a.fa.fa-link(title="{{_ 'link-to-search' }}" href="{{ getSearchHref }}")
each card in results.get
+resultCard(card)
table.global-search-footer
@@ -50,8 +51,7 @@ template(name="globalSearch")
each msg in errorMessages
li.global-search-error-messages
= msg
else
+resultsPaged(this)
+resultsPaged(this)
else if serverError.get
.global-search-page
.global-search-help
+4 -3
View File
@@ -123,6 +123,7 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
operator_member_abbrev: TAPi18n.__('operator-member-abbrev'),
operator_assignee: TAPi18n.__('operator-assignee'),
operator_assignee_abbrev: TAPi18n.__('operator-assignee-abbrev'),
operator_creator: TAPi18n.__('operator-creator'),
operator_due: TAPi18n.__('operator-due'),
operator_created: TAPi18n.__('operator-created'),
operator_modified: TAPi18n.__('operator-modified'),
@@ -167,6 +168,7 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
['\n* ', 'globalSearch-instructions-operator-at'],
['\n* ', 'globalSearch-instructions-operator-member'],
['\n* ', 'globalSearch-instructions-operator-assignee'],
['\n* ', 'globalSearch-instructions-operator-creator'],
['\n* ', 'globalSearch-instructions-operator-due'],
['\n* ', 'globalSearch-instructions-operator-created'],
['\n* ', 'globalSearch-instructions-operator-modified'],
@@ -202,9 +204,8 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
}
events() {
return [
return super.events().concat([
{
...super.events()[0],
'submit .js-search-query-form'(evt) {
evt.preventDefault();
this.searchAllBoards(evt.target.searchQuery.value);
@@ -257,7 +258,7 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
this.hasResults.set(false);
},
},
];
]);
}
}
+3 -3
View File
@@ -31,12 +31,12 @@ template(name="header")
unless currentSetting.hideLogo
if currentSetting.customTopLeftCornerLogoImageUrl
if currentSetting.customTopLeftCornerLogoLinkUrl
a(href="{{currentSetting.customTopLeftCornerLogoLinkUrl}}")
a(href="{{currentSetting.customTopLeftCornerLogoLinkUrl}}" alt="{{currentSetting.productName}}" title="{{currentSetting.productName}}")
img(src="{{currentSetting.customTopLeftCornerLogoImageUrl}}" height="{{#if currentSetting.customTopLeftCornerLogoHeight}}#{currentSetting.customTopLeftCornerLogoHeight}{{else}}27{{/if}}" width="auto" margin="0" padding="0")
unless currentSetting.customTopLeftCornerLogoLinkUrl
img(src="{{currentSetting.customTopLeftCornerLogoImageUrl}}" height="{{#if currentSetting.customTopLeftCornerLogoHeight}}#{currentSetting.customTopLeftCornerLogoHeight}{{else}}27{{/if}}" width="auto" margin="0" padding="0")
img(src="{{currentSetting.customTopLeftCornerLogoImageUrl}}" height="{{#if currentSetting.customTopLeftCornerLogoHeight}}#{currentSetting.customTopLeftCornerLogoHeight}{{else}}27{{/if}}" width="auto" margin="0" padding="0" alt="{{currentSetting.productName}}" title="{{currentSetting.productName}}")
unless currentSetting.customTopLeftCornerLogoImageUrl
img(src="{{pathFor '/logo-header.png'}}" alt="")
img(src="{{pathFor '/logo-header.png'}}" alt="{{currentSetting.productName}}" title="{{currentSetting.productName}}")
span.allBoards
a(href="{{pathFor 'home'}}")
span.fa.fa-home
+2
View File
@@ -77,6 +77,8 @@ Template.userFormsLayout.helpers({
} else if (lang.name === 'ar-EG') {
// ar-EG = Arabic (Egypt), simply Masri (مَصرى, [ˈmɑsˤɾi], Egyptian, Masr refers to Cairo)
name = 'مَصرى';
} else if (lang.name === 'de-CH') {
name = 'Deutsch (Schweiz)';
} else if (lang.name === 'fa-IR') {
// fa-IR = Persian (Iran)
name = 'فارسی/پارسی (ایران‎)';
+19 -53
View File
@@ -6,16 +6,6 @@ template(name="myCardsHeaderBar")
i.fa.fa-list
| {{_ 'my-cards'}}
.board-header-btns.left
a.board-header-btn.js-toggle-my-cards-choose-sort(title="{{_ 'myCardsSortChange-title'}}")
i.fa.fa-caret-down
if $eq myCardsSort 'board'
i.fa.fa-th-large
| {{_ 'myCardsSortChange-choice-board'}}
if $eq myCardsSort 'dueAt'
i.fa.fa-calendar
| {{_ 'myCardsSortChange-choice-dueat'}}
template(name="myCardsModalTitle")
if currentUser
h2
@@ -28,46 +18,22 @@ template(name="myCards")
+spinner
else
.wrapper
if $eq myCardsSort 'board'
each board in myCardsList
.my-cards-board-wrapper
.my-cards-board-title(class=board.colorClass, id="header")
a(href=board.originRelativeUrl)
+viewer
= board.title
each swimlane in board.mySwimlanes
.my-cards-swimlane-title(class="{{#if swimlane.colorClass}}{{ swimlane.colorClass }}{{else}}swimlane-default-color{{/if}}")
+viewer
= swimlane.title
each list in swimlane.myLists
.my-cards-list-wrapper
.my-cards-list-title(class=list.colorClass)
+viewer
= list.title
each card in list.myCards
.my-cards-card-wrapper
a.minicard-wrapper(href=card.originRelativeUrl)
+minicard(card)
else
.my-cards-dueat-list-wrapper
each card in myDueCardsList
+resultCard(card)
template(name="myCardsSortChangePopup")
if currentUser
ul.pop-over-list
li
with "my-cards-sort-board"
a.js-my-cards-sort-board
i.fa.fa-th-large.colorful
| {{_ 'myCardsSortChange-choice-board'}}
if $eq Utils.myCardsSort "board"
i.fa.fa-check
hr
li
with "my-cards-sort-dueat"
a.js-my-cards-sort-dueat
i.fa.fa-calendar.colorful
| {{_ 'myCardsSortChange-choice-dueat'}}
if $eq Utils.myCardsSort "dueAt"
i.fa.fa-check
each board in myCardsList
.my-cards-board-wrapper
.my-cards-board-title(class=board.colorClass, id="header")
a(href=board.originRelativeUrl)
+viewer
= board.title
each swimlane in board.mySwimlanes
.my-cards-swimlane-title(class="{{#if swimlane.colorClass}}{{ swimlane.colorClass }}{{else}}swimlane-default-color{{/if}}")
+viewer
= swimlane.title
each list in swimlane.myLists
.my-cards-list-wrapper
.my-cards-list-title(class=list.colorClass)
+viewer
= list.title
each card in list.myCards
.my-cards-card-wrapper
a.minicard-wrapper(href=card.originRelativeUrl)
+minicard(card)
+37 -107
View File
@@ -1,14 +1,4 @@
import { CardSearchPagedComponent } from '../../lib/cardSearch';
import { QueryParams } from '../../../config/query-classes';
import {
OPERATOR_LIMIT,
OPERATOR_SORT,
OPERATOR_USER,
ORDER_DESCENDING,
PREDICATE_DUE_AT,
} from '../../../config/search-const';
// const subManager = new SubsManager();
BlazeComponent.extendComponent({
myCardsSort() {
@@ -34,48 +24,21 @@ Template.myCards.helpers({
},
});
BlazeComponent.extendComponent({
events() {
return [
{
'click .js-my-cards-sort-board'() {
Utils.setMyCardsSort('board');
Popup.close();
},
'click .js-my-cards-sort-dueat'() {
Utils.setMyCardsSort('dueAt');
Popup.close();
},
},
];
},
}).register('myCardsSortChangePopup');
class MyCardsComponent extends CardSearchPagedComponent {
onCreated() {
super.onCreated();
const queryParams = new QueryParams();
queryParams.addPredicate(OPERATOR_USER, Meteor.user().username);
queryParams.addPredicate(OPERATOR_SORT, {
name: PREDICATE_DUE_AT,
order: ORDER_DESCENDING,
});
queryParams.addPredicate(OPERATOR_LIMIT, 100);
this.runGlobalSearch(queryParams);
this.runGlobalSearch(null);
Meteor.subscribe('setting');
}
myCardsSort() {
// eslint-disable-next-line no-console
//console.log('sort:', Utils.myCardsSort());
return Utils.myCardsSort();
}
sortByBoard() {
return this.myCardsSort() === 'board';
// eslint-disable-next-line no-unused-vars
getSubscription(queryParams) {
return Meteor.subscribe(
'myCards',
this.sessionId,
this.subscriptionCallbacks,
);
}
myCardsList() {
@@ -87,35 +50,9 @@ class MyCardsComponent extends CardSearchPagedComponent {
const cursor = this.getResults();
if (cursor) {
let newBoard = false;
let newSwimlane = false;
let newList = false;
cursor.forEach(card => {
// eslint-disable-next-line no-console
// console.log('card:', card.title);
if (list === null || card.listId !== list._id) {
// eslint-disable-next-line no-console
// console.log('new list');
list = card.getList();
if (list.archived) {
list = null;
return;
}
list.myCards = [card];
newList = true;
}
if (swimlane === null || card.swimlaneId !== swimlane._id) {
// eslint-disable-next-line no-console
// console.log('new swimlane');
swimlane = card.getSwimlane();
if (swimlane.archived) {
swimlane = null;
return;
}
swimlane.myLists = [list];
newSwimlane = true;
}
if (board === null || card.boardId !== board._id) {
// eslint-disable-next-line no-console
// console.log('new board');
@@ -126,23 +63,38 @@ class MyCardsComponent extends CardSearchPagedComponent {
}
// eslint-disable-next-line no-console
// console.log('board:', b, b._id, b.title);
board.mySwimlanes = [swimlane];
newBoard = true;
}
if (newBoard) {
boards.push(board);
} else if (newSwimlane) {
board.mySwimlanes.push(swimlane);
} else if (newList) {
swimlane.myLists.push(list);
} else {
list.myCards.push(card);
board.mySwimlanes = [];
swimlane = null;
list = null;
}
newBoard = false;
newSwimlane = false;
newList = false;
if (swimlane === null || card.swimlaneId !== swimlane._id) {
// eslint-disable-next-line no-console
// console.log('new swimlane');
swimlane = card.getSwimlane();
if (swimlane.archived) {
swimlane = null;
return;
}
board.mySwimlanes.push(swimlane);
swimlane.myLists = [];
list = null;
}
if (list === null || card.listId !== list._id) {
// eslint-disable-next-line no-console
// console.log('new list');
list = card.getList();
if (list.archived) {
list = null;
return;
}
swimlane.myLists.push(list);
list.myCards = [];
}
list.myCards.push(card);
});
// sort the data structure
@@ -182,27 +134,5 @@ class MyCardsComponent extends CardSearchPagedComponent {
return [];
}
myDueCardsList() {
const cursor = this.getResults();
const cards = [];
cursor.forEach(card => {
cards.push(card);
});
cards.sort((a, b) => {
const x = a.dueAt === null ? new Date('2100-12-31') : a.dueAt;
const y = b.dueAt === null ? new Date('2100-12-31') : b.dueAt;
if (x > y) return 1;
else if (x < y) return -1;
return 0;
});
// eslint-disable-next-line no-console
// console.log('cursor:', cards);
return cards;
}
}
MyCardsComponent.register('myCards');
+5
View File
@@ -0,0 +1,5 @@
template(name="spinner")
+Template.dynamic(template=getSpinnerTemplate)
template(name="spinnerRaw")
+Template.dynamic(template=getSpinnerTemplateRaw)
+11
View File
@@ -0,0 +1,11 @@
import { Spinner } from '/client/lib/spinner';
(class extends Spinner {
}.register('spinner'));
(class extends Spinner {
getSpinnerTemplateRaw() {
let ret = super.getSpinnerTemplate() + 'Raw';
return ret;
}
}.register('spinnerRaw'));
-6
View File
@@ -1,6 +0,0 @@
.sk-spinner.sk-spinner-wave(class=currentBoard.colorClass)
.sk-rect1
.sk-rect2
.sk-rect3
.sk-rect4
.sk-rect5
@@ -0,0 +1,11 @@
template(name="spinnerBounce")
.sk-spinner.sk-spinner-bounce(class=currentBoard.colorClass)
+spinnerBounceRaw
template(name="spinnerBounceRaw")
.sk-bounce1
| &nbsp;
.sk-bounce2
| &nbsp;
.sk-bounce3
| &nbsp;
@@ -0,0 +1,44 @@
@import 'nib'
// From https://github.com/tobiasahlin/SpinKit
.sk-spinner-bounce {
margin: 100px auto 0;
width: 70px;
text-align: center;
div {
width: 18px;
height: 18px;
background-color: #333;
border-radius: 100%;
display: inline-block;
-webkit-animation: sk-bouncedelay 1.4s infinite ease-in-out both;
animation: sk-bouncedelay 1.4s infinite ease-in-out both;
}
.sk-bounce1 {
-webkit-animation-delay: -0.32s;
animation-delay: -0.32s;
}
.sk-bounce2 {
-webkit-animation-delay: -0.16s;
animation-delay: -0.16s;
}
}
@-webkit-keyframes sk-bouncedelay {
0%, 80%, 100% { -webkit-transform: scale(0) }
40% { -webkit-transform: scale(1.0) }
}
@keyframes sk-bouncedelay {
0%, 80%, 100% {
-webkit-transform: scale(0);
transform: scale(0);
} 40% {
-webkit-transform: scale(1.0);
transform: scale(1.0);
}
}
+8
View File
@@ -0,0 +1,8 @@
template(name="spinnerCube")
.sk-spinner.sk-spinner-cube(class=currentBoard.colorClass)
+spinnerCubeRaw
template(name="spinnerCubeRaw")
.sk-cube1
.sk-cube2
.sk-cube3
+52
View File
@@ -0,0 +1,52 @@
@import 'nib'
// From https://github.com/tobiasahlin/SpinKit
.sk-spinner-cube {
margin: 100px auto;
width: 40px;
height: 40px;
position: relative;
}
.sk-cube1, .sk-cube2 {
background-color: #333;
width: 15px;
height: 15px;
position: absolute;
top: 0;
left: 0;
-webkit-animation: sk-cubemove 1.8s infinite ease-in-out;
animation: sk-cubemove 1.8s infinite ease-in-out;
}
.sk-cube2 {
-webkit-animation-delay: -0.9s;
animation-delay: -0.9s;
}
@-webkit-keyframes sk-cubemove {
25% { -webkit-transform: translateX(35px) rotate(-90deg) scale(0.5) }
50% { -webkit-transform: translateX(35px) translateY(35px) rotate(-180deg) }
75% { -webkit-transform: translateX(0px) translateY(35px) rotate(-270deg) scale(0.5) }
100% { -webkit-transform: rotate(-360deg) }
}
@keyframes sk-cubemove {
25% {
transform: translateX(35px) rotate(-90deg) scale(0.5);
-webkit-transform: translateX(35px) rotate(-90deg) scale(0.5);
} 50% {
transform: translateX(35px) translateY(35px) rotate(-179deg);
-webkit-transform: translateX(35px) translateY(35px) rotate(-179deg);
} 50.1% {
transform: translateX(35px) translateY(35px) rotate(-180deg);
-webkit-transform: translateX(35px) translateY(35px) rotate(-180deg);
} 75% {
transform: translateX(0px) translateY(35px) rotate(-270deg) scale(0.5);
-webkit-transform: translateX(0px) translateY(35px) rotate(-270deg) scale(0.5);
} 100% {
transform: rotate(-360deg);
-webkit-transform: rotate(-360deg);
}
}
@@ -0,0 +1,14 @@
template(name="spinnerCubeGrid")
.sk-spinner.sk-spinner-cube-grid(class=currentBoard.colorClass)
+spinnerCubeGridRaw
template(name="spinnerCubeGridRaw")
.sk-cube-grid.sk-cube-grid1
.sk-cube-grid.sk-cube-grid2
.sk-cube-grid.sk-cube-grid3
.sk-cube-grid.sk-cube-grid4
.sk-cube-grid.sk-cube-grid5
.sk-cube-grid.sk-cube-grid6
.sk-cube-grid.sk-cube-grid7
.sk-cube-grid.sk-cube-grid8
.sk-cube-grid.sk-cube-grid9
@@ -0,0 +1,64 @@
@import 'nib'
// From https://github.com/tobiasahlin/SpinKit
.sk-spinner-cube-grid {
width: 40px;
height: 40px;
margin: 100px auto;
}
.sk-spinner-cube-grid .sk-cube-grid {
width: 33%;
height: 33%;
background-color: #333;
float: left;
-webkit-animation: sk-cubeGridScaleDelay 1.3s infinite ease-in-out;
animation: sk-cubeGridScaleDelay 1.3s infinite ease-in-out;
}
.sk-spinner-cube-grid .sk-cube-grid1 {
-webkit-animation-delay: 0.2s;
animation-delay: 0.2s; }
.sk-spinner-cube-grid .sk-cube-grid2 {
-webkit-animation-delay: 0.3s;
animation-delay: 0.3s; }
.sk-spinner-cube-grid .sk-cube-grid3 {
-webkit-animation-delay: 0.4s;
animation-delay: 0.4s; }
.sk-spinner-cube-grid .sk-cube-grid4 {
-webkit-animation-delay: 0.1s;
animation-delay: 0.1s; }
.sk-spinner-cube-grid .sk-cube-grid5 {
-webkit-animation-delay: 0.2s;
animation-delay: 0.2s; }
.sk-spinner-cube-grid .sk-cube-grid6 {
-webkit-animation-delay: 0.3s;
animation-delay: 0.3s; }
.sk-spinner-cube-grid .sk-cube-grid7 {
-webkit-animation-delay: 0s;
animation-delay: 0s; }
.sk-spinner-cube-grid .sk-cube-grid8 {
-webkit-animation-delay: 0.1s;
animation-delay: 0.1s; }
.sk-spinner-cube-grid .sk-cube-grid9 {
-webkit-animation-delay: 0.2s;
animation-delay: 0.2s; }
@-webkit-keyframes sk-cubeGridScaleDelay {
0%, 70%, 100% {
-webkit-transform: scale3D(1, 1, 1);
transform: scale3D(1, 1, 1);
} 35% {
-webkit-transform: scale3D(0, 0, 1);
transform: scale3D(0, 0, 1);
}
}
@keyframes sk-cubeGridScaleDelay {
0%, 70%, 100% {
-webkit-transform: scale3D(1, 1, 1);
transform: scale3D(1, 1, 1);
} 35% {
-webkit-transform: scale3D(0, 0, 1);
transform: scale3D(0, 0, 1);
}
}
+7
View File
@@ -0,0 +1,7 @@
template(name="spinnerDot")
.sk-spinner.sk-spinner-dot(class=currentBoard.colorClass)
+spinnerDotRaw
template(name="spinnerDotRaw")
.sk-dot1
.sk-dot2
+51
View File
@@ -0,0 +1,51 @@
@import 'nib'
// From https://github.com/tobiasahlin/SpinKit
.sk-spinner-dot {
margin: 100px auto;
width: 40px;
height: 40px;
position: relative;
text-align: center;
-webkit-animation: sk-rotate 2.0s infinite linear;
animation: sk-rotate 2.0s infinite linear;
}
.sk-dot1, .sk-dot2 {
width: 40%;
height: 40%;
display: inline-block;
position: absolute;
top: 0;
background-color: #333;
border-radius: 100%;
-webkit-animation: sk-bounce 2.0s infinite ease-in-out;
animation: sk-bounce 2.0s infinite ease-in-out;
}
.sk-dot2 {
top: auto;
bottom: 0;
-webkit-animation-delay: -1.0s;
animation-delay: -1.0s;
}
@-webkit-keyframes sk-rotate { 100% { -webkit-transform: rotate(360deg) }}
@keyframes sk-rotate { 100% { transform: rotate(360deg); -webkit-transform: rotate(360deg) }}
@-webkit-keyframes sk-bounce {
0%, 100% { -webkit-transform: scale(0.0) }
50% { -webkit-transform: scale(1.0) }
}
@keyframes sk-bounce {
0%, 100% {
transform: scale(0.0);
-webkit-transform: scale(0.0);
} 50% {
transform: scale(1.0);
-webkit-transform: scale(1.0);
}
}
@@ -0,0 +1,7 @@
template(name="spinnerDoubleBounce")
.sk-spinner.sk-spinner-double-bounce(class=currentBoard.colorClass)
+spinnerDoubleBounceRaw
template(name="spinnerDoubleBounceRaw")
.sk-double-bounce1
.sk-double-bounce2
@@ -0,0 +1,44 @@
@import 'nib'
// From https://github.com/tobiasahlin/SpinKit
.sk-spinner-double-bounce {
width: 40px;
height: 40px;
position: relative;
margin: 100px auto;
}
.sk-double-bounce1, .sk-double-bounce2 {
width: 100%;
height: 100%;
border-radius: 50%;
background-color: #333;
opacity: 0.6;
position: absolute;
top: 0;
left: 0;
-webkit-animation: sk-bounce 2.0s infinite ease-in-out;
animation: sk-bounce 2.0s infinite ease-in-out;
}
.sk-double-bounce2 {
-webkit-animation-delay: -1.0s;
animation-delay: -1.0s;
}
@-webkit-keyframes sk-bounce {
0%, 100% { -webkit-transform: scale(0.0) }
50% { -webkit-transform: scale(1.0) }
}
@keyframes sk-bounce {
0%, 100% {
transform: scale(0.0);
-webkit-transform: scale(0.0);
} 50% {
transform: scale(1.0);
-webkit-transform: scale(1.0);
}
}
@@ -0,0 +1,6 @@
template(name="spinnerRotateplane")
.sk-spinner.sk-spinner-rotateplane(class=currentBoard.colorClass)
+spinnerRotateplaneRaw
template(name="spinnerRotateplaneRaw")
.sk-rotateplane1
@@ -0,0 +1,38 @@
@import 'nib'
// From https://github.com/tobiasahlin/SpinKit
.sk-spinner-rotateplane {
width: 40px;
height: 40px;
text-align: center;
margin: 100px auto;
-webkit-animation: sk-rotateplane 1.2s infinite ease-in-out;
animation: sk-rotateplane 1.2s infinite ease-in-out;
div {
background-color: #333;
height: 100%;
width: 100%;
display: inline-block;
}
}
@-webkit-keyframes sk-rotateplane {
0% { -webkit-transform: perspective(120px) }
50% { -webkit-transform: perspective(120px) rotateY(180deg) }
100% { -webkit-transform: perspective(120px) rotateY(180deg) rotateX(180deg) }
}
@keyframes sk-rotateplane {
0% {
transform: perspective(120px) rotateX(0deg) rotateY(0deg);
-webkit-transform: perspective(120px) rotateX(0deg) rotateY(0deg)
} 50% {
transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg);
-webkit-transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg)
} 100% {
transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);
-webkit-transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);
}
}
@@ -0,0 +1,6 @@
template(name="spinnerScaleout")
.sk-spinner.sk-spinner-scaleout(class=currentBoard.colorClass)
+spinnerScaleoutRaw
template(name="spinnerScaleoutRaw")
.sk-scaleout1
@@ -0,0 +1,40 @@
@import 'nib'
// From https://github.com/tobiasahlin/SpinKit
.sk-spinner-scaleout {
width: 40px;
height: 40px;
text-align: center;
margin: 100px auto;
border-radius: 100%;
-webkit-animation: sk-scaleout 1.0s infinite ease-in-out;
animation: sk-scaleout 1.0s infinite ease-in-out;
div {
background-color: #333;
height: 100%;
width: 100%;
display: inline-block;
}
}
@-webkit-keyframes sk-scaleout {
0% { -webkit-transform: scale(0) }
100% {
-webkit-transform: scale(1.0);
opacity: 0;
}
}
@keyframes sk-scaleout {
0% {
-webkit-transform: scale(0);
transform: scale(0);
} 100% {
-webkit-transform: scale(1.0);
transform: scale(1.0);
opacity: 0;
}
}
+15
View File
@@ -0,0 +1,15 @@
template(name="spinnerWave")
.sk-spinner.sk-spinner-wave(class=currentBoard.colorClass)
+spinnerWaveRaw
template(name="spinnerWaveRaw")
.sk-rect1
| &nbsp;
.sk-rect2
| &nbsp;
.sk-rect3
| &nbsp;
.sk-rect4
| &nbsp;
.sk-rect5
| &nbsp;
@@ -1,21 +1,7 @@
@import 'nib'
/*
* From https://github.com/tobiasahlin/SpinKit
*
* Usage:
*
* <div class="sk-spinner sk-spinner-wave">
* <div class="sk-rect1"></div>
* <div class="sk-rect2"></div>
* <div class="sk-rect3"></div>
* <div class="sk-rect4"></div>
* <div class="sk-rect5"></div>
* </div>
*
*/
.sk-spinner {
// From https://github.com/tobiasahlin/SpinKit
.sk-spinner-wave {
width: 50px;
height: 50px;
margin: auto;
@@ -1,5 +1,5 @@
template(name='notifications')
#notifications.board-header-btns.right
a.notifications-drawer-toggle.fa.fa-bell(class="{{#if $gt unreadNotifications 0}}alert{{/if}}")
a.notifications-drawer-toggle.fa.fa-bell(class="{{#if $gt unreadNotifications 0}}alert{{/if}}" title="{{_ 'notifications'}}")
if $.Session.get 'showNotificationsDrawer'
+notificationsDrawer(unreadNotifications=unreadNotifications)
@@ -0,0 +1,111 @@
template(name="adminReports")
.setting-content
unless currentUser.isAdmin
| {{_ 'error-notAuthorized'}}
else
.content-body
.side-menu
ul
li
a.js-report-broken(data-id="report-broken")
i.fa.fa-chain-broken
| {{_ 'broken-cards'}}
li
a.js-report-files(data-id="report-orphaned-files")
i.fa.fa-paperclip
| {{_ 'orphanedFilesReportTitle'}}
li
a.js-report-files(data-id="report-files")
i.fa.fa-paperclip
| {{_ 'filesReportTitle'}}
li
a.js-report-rules(data-id="report-rules")
i.fa.fa-magic
| {{_ 'rulesReportTitle'}}
.main-body
if loading.get
+spinner
else if showBrokenCardsReport.get
+brokenCardsReport
else if showFilesReport.get
+filesReport
else if showOrphanedFilesReport.get
+orphanedFilesReport
else if showRulesReport.get
+rulesReport
template(name="brokenCardsReport")
.global-search-results-list-wrapper
h1 {{_ 'broken-cards'}}
if resultsCount
+resultsPaged(this)
else
div {{_ 'no-results' }}
template(name="rulesReport")
h1 {{_ 'rulesReportTitle'}}
if resultsCount
table.table
tr
th Rule Title
th Board Title
th actionType
th activityType
each rule in rows
tr
td {{ rule.title }}
td {{ rule.boardTitle }}
td {{ rule.action.actionType }}
td {{ rule.trigger.activityType }}
else
div {{_ 'no-results' }}
template(name="filesReport")
h1 {{_ 'filesReportTitle'}}
if resultsCount
table.table
tr
th Filename
th.right Size (kB)
th MIME Type
th.center Usage
th MD5 Sum
th ID
each att in attachmentFiles
tr
td {{ att.filename }}
td.right {{fileSize att.length }}
td {{ att.contentType }}
td.center {{usageCount att._id.toHexString }}
td {{ att.md5 }}
td {{ att._id.toHexString }}
else
div {{_ 'no-results' }}
template(name="orphanedFilesReport")
h1 {{_ 'orphanedFilesReportTitle'}}
if resultsCount
table.table
tr
th Filename
th.right Size (kB)
th MIME Type
th MD5 Sum
th ID
each att in attachmentFiles
tr
td {{ att.filename }}
td.right {{fileSize att.length }}
td {{ att.contentType }}
td {{ att.md5 }}
td {{ att._id.toHexString }}
else
div {{_ 'no-results' }}
+156
View File
@@ -0,0 +1,156 @@
import { AttachmentStorage } from '/models/attachments';
import { CardSearchPagedComponent } from '/client/lib/cardSearch';
import SessionData from '/models/usersessiondata';
BlazeComponent.extendComponent({
subscription: null,
showFilesReport: new ReactiveVar(false),
showBrokenCardsReport: new ReactiveVar(false),
showOrphanedFilesReport: new ReactiveVar(false),
showRulesReport: new ReactiveVar(false),
onCreated() {
this.error = new ReactiveVar('');
this.loading = new ReactiveVar(false);
},
events() {
return [
{
'click a.js-report-broken': this.switchMenu,
'click a.js-report-files': this.switchMenu,
'click a.js-report-orphaned-files': this.switchMenu,
'click a.js-report-rules': this.switchMenu,
},
];
},
switchMenu(event) {
const target = $(event.target);
if (!target.hasClass('active')) {
this.loading.set(true);
this.showFilesReport.set(false);
this.showBrokenCardsReport.set(false);
this.showOrphanedFilesReport.set(false);
if (this.subscription) {
this.subscription.stop();
}
$('.side-menu li.active').removeClass('active');
target.parent().addClass('active');
const targetID = target.data('id');
if ('report-broken' === targetID) {
this.showBrokenCardsReport.set(true);
this.subscription = Meteor.subscribe(
'brokenCards',
SessionData.getSessionId(),
() => {
this.loading.set(false);
},
);
} else if ('report-files' === targetID) {
this.showFilesReport.set(true);
this.subscription = Meteor.subscribe('attachmentsList', () => {
this.loading.set(false);
});
} else if ('report-orphaned-files' === targetID) {
this.showOrphanedFilesReport.set(true);
this.subscription = Meteor.subscribe('orphanedAttachments', () => {
this.loading.set(false);
});
} else if ('report-rules' === targetID) {
this.subscription = Meteor.subscribe('rulesReport', () => {
this.showRulesReport.set(true);
this.loading.set(false);
});
}
}
},
}).register('adminReports');
Template.filesReport.helpers({
attachmentFiles() {
// eslint-disable-next-line no-console
// console.log('attachments:', AttachmentStorage.find());
// console.log('attachments.count:', AttachmentStorage.find().count());
return AttachmentStorage.find();
},
rulesReport() {
const rules = [];
Rules.find().forEach(rule => {
rules.push({
_id: rule._id,
title: rule.title,
boardId: rule.boardId,
boardTitle: rule.board().title,
action: rule.action().fetch(),
trigger: rule.trigger().fetch(),
});
});
return rules;
},
resultsCount() {
return AttachmentStorage.find().count();
},
fileSize(size) {
return Math.round(size / 1024);
},
usageCount(key) {
return Attachments.find({ 'copies.attachments.key': key }).count();
},
});
Template.orphanedFilesReport.helpers({
attachmentFiles() {
// eslint-disable-next-line no-console
// console.log('attachments:', AttachmentStorage.find());
// console.log('attachments.count:', AttachmentStorage.find().count());
return AttachmentStorage.find();
},
resultsCount() {
return AttachmentStorage.find().count();
},
fileSize(size) {
return Math.round(size / 1024);
},
});
Template.rulesReport.helpers({
rows() {
const rules = [];
Rules.find().forEach(rule => {
rules.push({
_id: rule._id,
title: rule.title,
boardId: rule.boardId,
boardTitle: rule.board().title,
action: rule.action(),
trigger: rule.trigger(),
});
});
console.log('rows:', rules);
return rules;
},
resultsCount() {
return Rules.find().count();
},
});
class BrokenCardsComponent extends CardSearchPagedComponent {
onCreated() {
super.onCreated();
}
}
BrokenCardsComponent.register('brokenCardsReport');
+107 -51
View File
@@ -110,6 +110,8 @@ template(name="peopleGeneral")
th {{_ 'active'}}
th {{_ 'authentication-method'}}
th {{_ 'import-usernames'}}
th {{_ 'organizations'}}
th {{_ 'teams'}}
th
+newUserRow
each user in peopleList
@@ -132,28 +134,28 @@ template(name="newUserRow")
template(name="orgRow")
tr
if orgData.loginDisabled
if orgData.orgIsActive
td <s>{{ orgData.orgDisplayName }}</s>
else
td {{ orgData.orgDisplayName }}
if orgData.loginDisabled
if orgData.orgIsActive
td <s>{{ orgData.orgDesc }}</s>
else
td {{ orgData.orgDesc }}
if orgData.loginDisabled
td <s>{{ orgData.orgName }}</s>
if orgData.orgIsActive
td <s>{{ orgData.orgShortName }}</s>
else
td {{ orgData.orgName }}
if orgData.loginDisabled
td {{ orgData.orgShortName }}
if orgData.orgIsActive
td <s>{{ orgData.orgWebsite }}</s>
else
td {{ orgData.orgWebsite }}
if orgData.loginDisabled
if orgData.orgIsActive
td <s>{{ moment orgData.createdAt 'LLL' }}</s>
else
td {{ moment orgData.createdAt 'LLL' }}
td
if orgData.loginDisabled
if orgData.orgIsActive
| {{_ 'no'}}
else
| {{_ 'yes'}}
@@ -166,28 +168,28 @@ template(name="orgRow")
template(name="teamRow")
tr
if teamData.loginDisabled
if teamData.teamIsActive
td <s>{{ teamData.teamDisplayName }}</s>
else
td {{ teamData.teamDisplayName }}
if teamData.loginDisabled
if teamData.teamIsActive
td <s>{{ teamData.teamDesc }}</s>
else
td {{ teamData.teamDesc }}
if teamData.loginDisabled
td <s>{{ teamData.teamName }}</s>
if teamData.teamIsActive
td <s>{{ teamData.teamShortName }}</s>
else
td {{ teamData.teamName }}
if teamData.loginDisabled
td {{ teamData.teamShortName }}
if teamData.teamIsActive
td <s>{{ teamData.teamWebsite }}</s>
else
td {{ teamData.teamWebsite }}
if orgData.loginDisabled
if teamData.teamIsActive
td <s>{{ moment teamData.createdAt 'LLL' }}</s>
else
td {{ moment teamData.createdAt 'LLL' }}
td
if teamData.loginDisabled
if teamData.teamIsActive
| {{_ 'no'}}
else
| {{_ 'yes'}}
@@ -257,6 +259,14 @@ template(name="peopleRow")
td <s>{{ userData.importUsernamesString }}</s>
else
td {{ userData.importUsernamesString }}
if userData.loginDisabled
td <s>{{ userData.orgsUserBelongs }}</s>
else
td {{ userData.orgsUserBelongs }}
if userData.loginDisabled
td <s>{{ userData.teamsUserBelongs }}</s>
else
td {{ userData.teamsUserBelongs }}
td
a.edit-user
i.fa.fa-edit
@@ -269,7 +279,7 @@ template(name="editOrgPopup")
label.hide.orgId(type="text" value=org._id)
label
| {{_ 'displayName'}}
input.js-orgDisplayName(type="text" value=org.displayName required)
input.js-orgDisplayName(type="text" value=org.orgDisplayName required)
span.error.hide.orgname-taken
| {{_ 'error-orgname-taken'}}
label
@@ -285,7 +295,7 @@ template(name="editOrgPopup")
| {{_ 'active'}}
select.select-active.js-org-isactive
option(value="false") {{_ 'yes'}}
option(value="true" selected="{{org.loginDisabled}}") {{_ 'no'}}
option(value="true" selected="{{org.orgIsActive}}") {{_ 'no'}}
hr
div.buttonsContainer
input.primary.wide(type="submit" value="{{_ 'save'}}")
@@ -311,7 +321,7 @@ template(name="editTeamPopup")
| {{_ 'active'}}
select.select-active.js-team-isactive
option(value="false") {{_ 'yes'}}
option(value="true" selected="{{team.loginDisabled}}") {{_ 'no'}}
option(value="true" selected="{{team.teamIsActive}}") {{_ 'no'}}
hr
div.buttonsContainer
input.primary.wide(type="submit" value="{{_ 'save'}}")
@@ -367,6 +377,27 @@ template(name="editUserPopup")
option(value="{{value}}" selected) {{_ value}}
else
option(value="{{value}}") {{_ value}}
label
| {{_ 'organizations'}}
i.fa.fa-plus-square#addUserOrg
i.fa.fa-minus-square#removeUserOrg
select.js-orgs#jsOrgs
option(value="-1") {{_ 'organizations'}} :
each value in orgsDatas
option(value="{{value._id}}") {{_ value.orgDisplayName}}
input#jsUserOrgsInPut.js-userOrgs(type="text" value=user.orgsUserBelongs, disabled)
input#jsUserOrgIdsInPut.js-userOrgIds.hide(type="text" value=user.orgIdsUserBelongs)
label
| {{_ 'teams'}}
i.fa.fa-plus-square#addUserTeam
i.fa.fa-minus-square#removeUserTeam
select.js-teams#jsTeams
option(value="-1") {{_ 'teams'}} :
each value in teamsDatas
option(value="{{value._id}}") {{_ value.teamDisplayName}}
input#jsUserTeamsInPut.js-userteams(type="text" value=user.teamsUserBelongs, disabled)
input#jsUserTeamIdsInPut.js-userteamIds.hide(type="text" value=user.teamIdsUserBelongs)
hr
label
| {{_ 'password'}}
@@ -385,7 +416,7 @@ template(name="newOrgPopup")
input.js-orgDesc(type="text" value="" required)
label
| {{_ 'shortName'}}
input.js-orgName(type="text" value="" required)
input.js-orgShortName(type="text" value="" required)
label
| {{_ 'website'}}
input.js-orgWebsite(type="text" value="" required)
@@ -409,7 +440,7 @@ template(name="newTeamPopup")
input.js-teamDesc(type="text" value="" required)
label
| {{_ 'shortName'}}
input.js-teamName(type="text" value="" required)
input.js-teamShortName(type="text" value="" required)
label
| {{_ 'website'}}
input.js-teamWebsite(type="text" value="" required)
@@ -468,6 +499,27 @@ template(name="newUserPopup")
option(value="{{value}}" selected) {{_ value}}
else
option(value="{{value}}") {{_ value}}
label
| {{_ 'organizations'}}
i.fa.fa-plus-square#addUserOrgNewUser
i.fa.fa-minus-square#removeUserOrgNewUser
select.js-orgsNewUser#jsOrgsNewUser
option(value="-1") {{_ 'organizations'}} :
each value in orgsDatas
option(value="{{value._id}}") {{_ value.orgDisplayName}}
input#jsUserOrgsInPutNewUser.js-userOrgsNewUser(type="text" value=user.orgsUserBelongs, disabled)
input#jsUserOrgIdsInPutNewUser.js-userOrgIdsNewUser.hide(type="text" value=user.orgIdsUserBelongs)
label
| {{_ 'teams'}}
i.fa.fa-plus-square#addUserTeamNewUser
i.fa.fa-minus-square#removeUserTeamNewUser
select.js-teamsNewUser#jsTeamsNewUser
option(value="-1") {{_ 'teams'}} :
each value in teamsDatas
option(value="{{value._id}}") {{_ value.teamDisplayName}}
input#jsUserTeamsInPutNewUser.js-userteamsNewUser(type="text" value=user.teamsUserBelongs, disabled)
input#jsUserTeamIdsInPutNewUser.js-userteamIdsNewUser.hide(type="text" value=user.teamIdsUserBelongs)
hr
label
| {{_ 'password'}}
@@ -478,27 +530,34 @@ template(name="newUserPopup")
template(name="settingsOrgPopup")
ul.pop-over-list
li
a.impersonate-org
i.fa.fa-user
| {{_ 'impersonate-org'}}
// Delete is not enabled yet, because it does leave empty user avatars
// to boards: boards members, card members and assignees have
// empty users. See:
// - wekan/client/components/settings/peopleBody.jade deleteButton
// - wekan/client/components/settings/peopleBody.js deleteButton
// - wekan/client/components/sidebar/sidebar.js Popup.afterConfirm('removeMember'
// that does now remove member from board, card members and assignees correctly,
// but that should be used to remove user from all boards similarly
// - wekan/models/users.js Delete is not enabled
//li
// br
// br
// hr
//li
// form
// label.hide.userId(type="text" value=user._id)
// div.buttonsContainer
// input#deleteButton.card-details-red.right.wide(type="button" value="{{_ 'delete'}}")
form
label
| {{_ 'delete-org-confirm-popup'}}
br
label.hide.orgId(type="text" value=org._id)
labeldelete-org-confirm-popup
div.buttonsContainer
input#deleteButton.card-details-red.right.wide(type="button" value="{{_ 'delete'}}")
// It's not yet possible to impersonate organization. Only impersonate user,
// because that changes current user ID. What would it mean in practice
// to impersonate organization?
// li
// a.impersonate-org
// i.fa.fa-user
// | {{_ 'impersonate-org'}}
//
//
template(name="settingsTeamPopup")
ul.pop-over-list
li
form
label
| {{_ 'delete-team-confirm-popup'}}
br
label.hide.teamId(type="text" value=team._id)
div.buttonsContainer
input#deleteButton.card-details-red.right.wide(type="button" value="{{_ 'delete'}}")
template(name="settingsUserPopup")
ul.pop-over-list
@@ -506,6 +565,12 @@ template(name="settingsUserPopup")
a.impersonate-user
i.fa.fa-user
| {{_ 'impersonate-user'}}
//hr
//li
// form
// label.hide.userId(type="text" value=user._id)
// div.buttonsContainer
// input#deleteButton.card-details-red.right.wide(type="button" value="{{_ 'delete'}}")
// Delete is not enabled yet, because it does leave empty user avatars
// to boards: boards members, card members and assignees have
// empty users. See:
@@ -515,12 +580,3 @@ template(name="settingsUserPopup")
// that does now remove member from board, card members and assignees correctly,
// but that should be used to remove user from all boards similarly
// - wekan/models/users.js Delete is not enabled
//li
// br
// br
// hr
//li
// form
// label.hide.userId(type="text" value=user._id)
// div.buttonsContainer
// input#deleteButton.card-details-red.right.wide(type="button" value="{{_ 'delete'}}")
+317 -92
View File
@@ -1,6 +1,7 @@
const orgsPerPage = 25;
const teamsPerPage = 25;
const usersPerPage = 25;
let userOrgsTeamsAction = ""; //poosible actions 'addOrg', 'addTeam', 'removeOrg' or 'removeTeam' when adding or modifying a user
BlazeComponent.extendComponent({
mixins() {
@@ -101,9 +102,7 @@ BlazeComponent.extendComponent({
];
},
filterPeople() {
const value = $('#searchInput')
.first()
.val();
const value = $('#searchInput').first().val();
if (value === '') {
this.findUsersOptions.set({});
} else {
@@ -150,7 +149,7 @@ BlazeComponent.extendComponent({
const teams = Team.find(this.findTeamsOptions.get(), {
fields: { _id: true },
});
this.numberTeams.set(team.count(false));
this.numberTeams.set(teams.count(false));
return teams;
},
peopleList() {
@@ -203,7 +202,7 @@ Template.peopleRow.helpers({
},
});
Template.editUserPopup.onCreated(function() {
Template.editUserPopup.onCreated(function () {
this.authenticationMethods = new ReactiveVar([]);
this.errorMessage = new ReactiveVar('');
@@ -215,8 +214,8 @@ Template.editUserPopup.onCreated(function() {
{ value: 'password' },
// Gets only the authentication methods availables
...Object.entries(result)
.filter(e => e[1])
.map(e => ({ value: e[0] })),
.filter((e) => e[1])
.map((e) => ({ value: e[0] })),
]);
}
});
@@ -247,6 +246,12 @@ Template.editUserPopup.helpers({
authentications() {
return Template.instance().authenticationMethods.get();
},
orgsDatas() {
return Org.find({}, {sort: { createdAt: -1 }});
},
teamsDatas() {
return Team.find({}, {sort: { createdAt: -1 }});
},
isSelected(match) {
const userId = Template.instance().data.userId;
const selected = Users.findOne(userId).authenticationMethod;
@@ -262,15 +267,15 @@ Template.editUserPopup.helpers({
},
});
Template.newOrgPopup.onCreated(function() {
Template.newOrgPopup.onCreated(function () {
this.errorMessage = new ReactiveVar('');
});
Template.newTeamPopup.onCreated(function() {
Template.newTeamPopup.onCreated(function () {
this.errorMessage = new ReactiveVar('');
});
Template.newUserPopup.onCreated(function() {
Template.newUserPopup.onCreated(function () {
this.authenticationMethods = new ReactiveVar([]);
this.errorMessage = new ReactiveVar('');
@@ -282,8 +287,8 @@ Template.newUserPopup.onCreated(function() {
{ value: 'password' },
// Gets only the authentication methods availables
...Object.entries(result)
.filter(e => e[1])
.map(e => ({ value: e[0] })),
.filter((e) => e[1])
.map((e) => ({ value: e[0] })),
]);
}
});
@@ -314,10 +319,21 @@ Template.newUserPopup.helpers({
authentications() {
return Template.instance().authenticationMethods.get();
},
orgsDatas() {
return Org.find({}, {sort: { createdAt: -1 }});
},
teamsDatas() {
return Team.find({}, {sort: { createdAt: -1 }});
},
isSelected(match) {
const userId = Template.instance().data.userId;
const selected = Users.findOne(userId).authenticationMethod;
return selected === match;
if(userId){
const selected = Users.findOne(userId).authenticationMethod;
return selected === match;
}
else{
false;
}
},
isLdap() {
const userId = Template.instance().data.userId;
@@ -407,7 +423,7 @@ BlazeComponent.extendComponent({
Template.editOrgPopup.events({
submit(event, templateInstance) {
event.preventDefault();
const org = Orgs.findOne(this.orgId);
const org = Org.findOne(this.orgId);
const orgDisplayName = templateInstance
.find('.js-orgDisplayName')
@@ -415,7 +431,8 @@ Template.editOrgPopup.events({
const orgDesc = templateInstance.find('.js-orgDesc').value.trim();
const orgShortName = templateInstance.find('.js-orgShortName').value.trim();
const orgWebsite = templateInstance.find('.js-orgWebsite').value.trim();
const orgIsActive = templateInstance.find('.js-org-isactive').value.trim();
const orgIsActive =
templateInstance.find('.js-org-isactive').value.trim() == 'true';
const isChangeOrgDisplayName = orgDisplayName !== org.orgDisplayName;
const isChangeOrgDesc = orgDesc !== org.orgDesc;
@@ -423,20 +440,22 @@ Template.editOrgPopup.events({
const isChangeOrgWebsite = orgWebsite !== org.orgWebsite;
const isChangeOrgIsActive = orgIsActive !== org.orgIsActive;
if (isChangeOrgDisplayName) {
Meteor.call('setOrgDisplayName', org, orgDisplayName);
}
if (isChangeOrgDesc) {
Meteor.call('setOrgDesc', org, orgDesc);
}
if (isChangeOrgShortName) {
Meteor.call('setOrgShortName', org, orgShortName);
}
if (isChangeOrgIsActive) {
Meteor.call('setOrgIsActive', org, orgIsActive);
if (
isChangeOrgDisplayName ||
isChangeOrgDesc ||
isChangeOrgShortName ||
isChangeOrgWebsite ||
isChangeOrgIsActive
) {
Meteor.call(
'setOrgAllFields',
org,
orgDisplayName,
orgDesc,
orgShortName,
orgWebsite,
orgIsActive,
);
}
Popup.close();
@@ -446,7 +465,7 @@ Template.editOrgPopup.events({
Template.editTeamPopup.events({
submit(event, templateInstance) {
event.preventDefault();
const team = Teams.findOne(this.teamId);
const team = Team.findOne(this.teamId);
const teamDisplayName = templateInstance
.find('.js-teamDisplayName')
@@ -456,9 +475,8 @@ Template.editTeamPopup.events({
.find('.js-teamShortName')
.value.trim();
const teamWebsite = templateInstance.find('.js-teamWebsite').value.trim();
const teamIsActive = templateInstance
.find('.js-team-isactive')
.value.trim();
const teamIsActive =
templateInstance.find('.js-team-isactive').value.trim() == 'true';
const isChangeTeamDisplayName = teamDisplayName !== team.teamDisplayName;
const isChangeTeamDesc = teamDesc !== team.teamDesc;
@@ -466,20 +484,22 @@ Template.editTeamPopup.events({
const isChangeTeamWebsite = teamWebsite !== team.teamWebsite;
const isChangeTeamIsActive = teamIsActive !== team.teamIsActive;
if (isChangeTeamDisplayName) {
Meteor.call('setTeamDisplayName', team, teamDisplayName);
}
if (isChangeTeamDesc) {
Meteor.call('setTeamDesc', team, teamDesc);
}
if (isChangeTeamShortName) {
Meteor.call('setTeamShortName', team, teamShortName);
}
if (isChangeTeamIsActive) {
Meteor.call('setTeamIsActive', team, teamIsActive);
if (
isChangeTeamDisplayName ||
isChangeTeamDesc ||
isChangeTeamShortName ||
isChangeTeamWebsite ||
isChangeTeamIsActive
) {
Meteor.call(
'setTeamAllFields',
team,
teamDisplayName,
teamDesc,
teamShortName,
teamWebsite,
teamIsActive,
);
}
Popup.close();
@@ -497,15 +517,13 @@ Template.editUserPopup.events({
const isAdmin = templateInstance.find('.js-profile-isadmin').value.trim();
const isActive = templateInstance.find('.js-profile-isactive').value.trim();
const email = templateInstance.find('.js-profile-email').value.trim();
const verified = templateInstance
.find('.js-profile-email-verified')
.value.trim();
const authentication = templateInstance
.find('.js-authenticationMethod')
.value.trim();
const importUsernames = templateInstance
.find('.js-import-usernames')
.value.trim();
const verified = templateInstance.find('.js-profile-email-verified').value.trim();
const authentication = templateInstance.find('.js-authenticationMethod').value.trim();
const importUsernames = templateInstance.find('.js-import-usernames').value.trim();
const userOrgs = templateInstance.find('.js-userOrgs').value.trim();
const userOrgsIds = templateInstance.find('.js-userOrgIds').value.trim();
const userTeams = templateInstance.find('.js-userteams').value.trim();
const userTeamsIds = templateInstance.find('.js-userteamIds').value.trim();
const isChangePassword = password.length > 0;
const isChangeUserName = username !== user.username;
@@ -530,6 +548,36 @@ Template.editUserPopup.events({
},
});
let userTeamsList = userTeams.split(",");
let userTeamsIdsList = userTeamsIds.split(",");
let userTms = [];
for(let i = 0; i < userTeamsList.length; i++){
userTms.push({
"teamId": userTeamsIdsList[i],
"teamDisplayName": userTeamsList[i],
})
}
Users.update(this.userId, {
$set:{
teams: userTms
}
});
let userOrgsList = userOrgs.split(",");
let userOrgsIdsList = userOrgsIds.split(",");
let userOrganizations = [];
for(let i = 0; i < userOrgsList.length; i++){
userOrganizations.push({
"orgId": userOrgsIdsList[i],
"orgDisplayName": userOrgsList[i],
})
}
Users.update(this.userId, {
$set:{
orgs: userOrganizations
}
});
if (isChangePassword) {
Meteor.call('setPassword', password, this.userId);
}
@@ -548,7 +596,7 @@ Template.editUserPopup.events({
username,
email.toLowerCase(),
this.userId,
function(error) {
function (error) {
const usernameMessageElement = templateInstance.$('.username-taken');
const emailMessageElement = templateInstance.$('.email-taken');
if (error) {
@@ -568,7 +616,7 @@ Template.editUserPopup.events({
},
);
} else if (isChangeUserName) {
Meteor.call('setUsername', username, this.userId, function(error) {
Meteor.call('setUsername', username, this.userId, function (error) {
const usernameMessageElement = templateInstance.$('.username-taken');
if (error) {
const errorElement = error.error;
@@ -581,24 +629,138 @@ Template.editUserPopup.events({
}
});
} else if (isChangeEmail) {
Meteor.call('setEmail', email.toLowerCase(), this.userId, function(
error,
) {
const emailMessageElement = templateInstance.$('.email-taken');
if (error) {
const errorElement = error.error;
if (errorElement === 'email-already-taken') {
emailMessageElement.show();
Meteor.call(
'setEmail',
email.toLowerCase(),
this.userId,
function (error) {
const emailMessageElement = templateInstance.$('.email-taken');
if (error) {
const errorElement = error.error;
if (errorElement === 'email-already-taken') {
emailMessageElement.show();
}
} else {
emailMessageElement.hide();
Popup.close();
}
} else {
emailMessageElement.hide();
Popup.close();
}
});
},
);
} else Popup.close();
},
'click #addUserOrg'(event) {
event.preventDefault();
userOrgsTeamsAction = "addOrg";
document.getElementById("jsOrgs").style.display = 'block';
document.getElementById("jsTeams").style.display = 'none';
},
'click #removeUserOrg'(event) {
event.preventDefault();
userOrgsTeamsAction = "removeOrg";
document.getElementById("jsOrgs").style.display = 'block';
document.getElementById("jsTeams").style.display = 'none';
},
'click #addUserTeam'(event) {
event.preventDefault();
userOrgsTeamsAction = "addTeam";
document.getElementById("jsTeams").style.display = 'block';
document.getElementById("jsOrgs").style.display = 'none';
},
'click #removeUserTeam'(event) {
event.preventDefault();
userOrgsTeamsAction = "removeTeam";
document.getElementById("jsTeams").style.display = 'block';
document.getElementById("jsOrgs").style.display = 'none';
},
'change #jsOrgs'(event) {
event.preventDefault();
UpdateUserOrgsOrTeamsElement();
},
'change #jsTeams'(event) {
event.preventDefault();
UpdateUserOrgsOrTeamsElement();
},
});
UpdateUserOrgsOrTeamsElement = function(isNewUser = false){
let selectedElt;
let selectedEltValue;
let selectedEltValueId;
let inputElt;
let inputEltId;
let lstInputValues = [];
let lstInputValuesIds = [];
let index;
let indexId;
switch(userOrgsTeamsAction)
{
case "addOrg":
case "removeOrg":
inputElt = !isNewUser ? document.getElementById("jsUserOrgsInPut") : document.getElementById("jsUserOrgsInPutNewUser");
inputEltId = !isNewUser ? document.getElementById("jsUserOrgIdsInPut") : document.getElementById("jsUserOrgIdsInPutNewUser");
selectedElt = !isNewUser ? document.getElementById("jsOrgs") : document.getElementById("jsOrgsNewUser");
break;
case "addTeam":
case "removeTeam":
inputElt = !isNewUser ? document.getElementById("jsUserTeamsInPut") : document.getElementById("jsUserTeamsInPutNewUser");
inputEltId = !isNewUser ? document.getElementById("jsUserTeamIdsInPut") : document.getElementById("jsUserTeamIdsInPutNewUser");
selectedElt = !isNewUser ? document.getElementById("jsTeams") : document.getElementById("jsTeamsNewUser");
break;
default:
break;
}
selectedEltValue = selectedElt.options[selectedElt.selectedIndex].text;
selectedEltValueId = selectedElt.options[selectedElt.selectedIndex].value;
lstInputValues = inputElt.value.trim().split(",");
if(lstInputValues.length == 1 && lstInputValues[0] == ''){
lstInputValues = [];
}
lstInputValuesIds = inputEltId.value.trim().split(",");
if(lstInputValuesIds.length == 1 && lstInputValuesIds[0] == ''){
lstInputValuesIds = [];
}
index = lstInputValues.indexOf(selectedEltValue);
indexId = lstInputValuesIds.indexOf(selectedEltValue);
if(userOrgsTeamsAction == "addOrg" || userOrgsTeamsAction == "addTeam"){
if(index <= -1 && selectedEltValueId != "-1"){
lstInputValues.push(selectedEltValue);
}
if(indexId <= -1 && selectedEltValueId != "-1"){
lstInputValuesIds.push(selectedEltValueId);
}
}
else{
if(index > -1 && selectedEltValueId != "-1"){
lstInputValues.splice(index, 1);
}
if(indexId > -1 && selectedEltValueId != "-1"){
lstInputValuesIds.splice(indexId, 1);
}
}
if(lstInputValues.length > 0){
inputElt.value = lstInputValues.join(",");
}
else{
inputElt.value = "";
}
if(lstInputValuesIds.length > 0){
inputEltId.value = lstInputValuesIds.join(",");
}
else{
inputEltId.value = "";
}
selectedElt.value = "-1";
selectedElt.style.display = "none";
}
Template.newOrgPopup.events({
submit(event, templateInstance) {
event.preventDefault();
@@ -608,7 +770,8 @@ Template.newOrgPopup.events({
const orgDesc = templateInstance.find('.js-orgDesc').value.trim();
const orgShortName = templateInstance.find('.js-orgShortName').value.trim();
const orgWebsite = templateInstance.find('.js-orgWebsite').value.trim();
const orgIsActive = templateInstance.find('.js-org-isactive').value.trim();
const orgIsActive =
templateInstance.find('.js-org-isactive').value.trim() == 'true';
Meteor.call(
'setCreateOrg',
@@ -633,9 +796,8 @@ Template.newTeamPopup.events({
.find('.js-teamShortName')
.value.trim();
const teamWebsite = templateInstance.find('.js-teamWebsite').value.trim();
const teamIsActive = templateInstance
.find('.js-team-isactive')
.value.trim();
const teamIsActive =
templateInstance.find('.js-team-isactive').value.trim() == 'true';
Meteor.call(
'setCreateTeam',
@@ -662,6 +824,30 @@ Template.newUserPopup.events({
const importUsernames = Users.parseImportUsernames(
templateInstance.find('.js-import-usernames').value,
);
const userOrgs = templateInstance.find('.js-userOrgsNewUser').value.trim();
const userOrgsIds = templateInstance.find('.js-userOrgIdsNewUser').value.trim();
const userTeams = templateInstance.find('.js-userteamsNewUser').value.trim();
const userTeamsIds = templateInstance.find('.js-userteamIdsNewUser').value.trim();
let userTeamsList = userTeams.split(",");
let userTeamsIdsList = userTeamsIds.split(",");
let userTms = [];
for(let i = 0; i < userTeamsList.length; i++){
userTms.push({
"teamId": userTeamsIdsList[i],
"teamDisplayName": userTeamsList[i],
})
}
let userOrgsList = userOrgs.split(",");
let userOrgsIdsList = userOrgsIds.split(",");
let userOrganizations = [];
for(let i = 0; i < userOrgsList.length; i++){
userOrganizations.push({
"orgId": userOrgsIdsList[i],
"orgDisplayName": userOrgsList[i],
})
}
Meteor.call(
'setCreateUser',
@@ -673,6 +859,8 @@ Template.newUserPopup.events({
isActive,
email.toLowerCase(),
importUsernames,
userOrganizations,
userTms,
function(error) {
const usernameMessageElement = templateInstance.$('.username-taken');
const emailMessageElement = templateInstance.$('.email-taken');
@@ -694,13 +882,65 @@ Template.newUserPopup.events({
);
Popup.close();
},
'click #addUserOrgNewUser'(event) {
event.preventDefault();
userOrgsTeamsAction = "addOrg";
document.getElementById("jsOrgsNewUser").style.display = 'block';
document.getElementById("jsTeamsNewUser").style.display = 'none';
},
'click #removeUserOrgNewUser'(event) {
event.preventDefault();
userOrgsTeamsAction = "removeOrg";
document.getElementById("jsOrgsNewUser").style.display = 'block';
document.getElementById("jsTeamsNewUser").style.display = 'none';
},
'click #addUserTeamNewUser'(event) {
event.preventDefault();
userOrgsTeamsAction = "addTeam";
document.getElementById("jsTeamsNewUser").style.display = 'block';
document.getElementById("jsOrgsNewUser").style.display = 'none';
},
'click #removeUserTeamNewUser'(event) {
event.preventDefault();
userOrgsTeamsAction = "removeTeam";
document.getElementById("jsTeamsNewUser").style.display = 'block';
document.getElementById("jsOrgsNewUser").style.display = 'none';
},
'change #jsOrgsNewUser'(event) {
event.preventDefault();
UpdateUserOrgsOrTeamsElement(true);
},
'change #jsTeamsNewUser'(event) {
event.preventDefault();
UpdateUserOrgsOrTeamsElement(true);
},
});
Template.settingsOrgPopup.events({
'click #deleteButton'(event) {
event.preventDefault();
Org.remove(this.orgId);
Popup.close();
}
});
Template.settingsTeamPopup.events({
'click #deleteButton'(event) {
event.preventDefault();
Team.remove(this.teamId);
Popup.close();
}
});
Template.settingsUserPopup.events({
'click .impersonate-user'(event) {
event.preventDefault();
Meteor.call('impersonate', this.userId, err => {
Meteor.call('impersonate', this.userId, (err) => {
if (!err) {
FlowRouter.go('/');
Meteor.connection.setUserId(this.userId);
@@ -720,21 +960,6 @@ Template.settingsUserPopup.events({
// but that should be used to remove user from all boards similarly
// - wekan/models/users.js Delete is not enabled
//
//console.log('user id: ' + this.userId);
//Popup.afterConfirm('userDelete', function(event) {
//Boards.find({ members: this.userId }).forEach(board => {
// console.log('board id: ' + board._id);
//Cards.find({ boardId: board._id, members: this.userId }).forEach(card => {
// card.unassignMember(this.userId);
//});
//Cards.find({ boardId: board._id, members: this.userId }).forEach(card => {
// card.unassignMember(this.userId);
//});
//Cards.find({ boardId: board._id, assignees: this.userId }).forEach(card => {
// card.unassignAssignee(this.userId);
//});
//Boards.findOne({ boardId: board._id }).removeMember(this.userId);
//});
//Users.remove(this.userId);
*/
Popup.close();
@@ -49,3 +49,9 @@ table
.more-settings-user,.more-settings-team,.more-settings-org
margin-left: 10px;
.js-orgs,.js-orgsNewUser
display: none;
.js-teams,.js-teamsNewUser
display: none;
@@ -173,6 +173,9 @@ template(name='layoutSettings')
li.layout-form
.title {{_ 'default-authentication-method'}}
+selectAuthenticationMethod(authenticationMethod=currentSetting.defaultAuthenticationMethod)
li.layout-form
.title {{_ 'wait-spinner'}}
+selectSpinnerName(spinnerName=currentSetting.spinnerName)
li.layout-form
.title {{_ 'custom-product-name'}}
.form-group
@@ -222,3 +225,11 @@ template(name='selectAuthenticationMethod')
option(value="{{value}}" selected) {{_ value}}
else
option(value="{{value}}") {{_ value}}
template(name='selectSpinnerName')
select#spinnerName
each spinner in spinners
if isSelected spinner
option(value="{{spinner}}" selected) {{spinner}}
else
option(value="{{spinner}}") {{spinner}}
+14
View File
@@ -1,3 +1,5 @@
import { ALLOWED_WAIT_SPINNERS } from '/config/const';
BlazeComponent.extendComponent({
onCreated() {
this.error = new ReactiveVar('');
@@ -199,6 +201,8 @@ BlazeComponent.extendComponent({
$('input[name=displayAuthenticationMethod]:checked').val() === 'true';
const defaultAuthenticationMethod = $('#defaultAuthenticationMethod').val();
const spinnerName = $('#spinnerName').val();
try {
Settings.update(Settings.findOne()._id, {
$set: {
@@ -213,6 +217,7 @@ BlazeComponent.extendComponent({
displayAuthenticationMethod,
defaultAuthenticationMethod,
automaticLinkedUrlSchemes,
spinnerName,
},
});
} catch (e) {
@@ -384,3 +389,12 @@ Template.selectAuthenticationMethod.helpers({
return Template.instance().data.authenticationMethod === match;
},
});
Template.selectSpinnerName.helpers({
spinners() {
return ALLOWED_WAIT_SPINNERS;
},
isSelected(match) {
return Template.instance().data.spinnerName === match;
},
});
@@ -12,6 +12,10 @@ template(name="settingHeaderBar")
i.fa(class="fa-users")
span {{_ 'people'}}
a.setting-header-btn.informations(href="{{pathFor 'admin-reports'}}")
i.fa(class="fa-list")
span {{_ 'reports'}}
a.setting-header-btn.informations(href="{{pathFor 'information'}}")
i.fa(class="fa-info-circle")
span {{_ 'info'}}
+24 -8
View File
@@ -5,10 +5,6 @@ template(name="sidebar")
// title="{{showTongueTitle}}")
// i.fa.fa-navicon
.sidebar-shadow
.sidebar-content.sidebar-shortcuts
a.board-header-btn.js-shortcuts
i.fa.fa-keyboard-o
span {{_ 'keyboard-shortcuts' }}
.sidebar-content.js-board-sidebar-content
//a.hide-btn.js-hide-sidebar
// i.fa.fa-navicon
@@ -23,7 +19,7 @@ template(name='homeSidebar')
hr
+labelsWidget
ul#cards.label-text-hidden
a.flex.js-toggle-minicard-label-text
a.flex.js-toggle-minicard-label-text(title="{{_ 'hide-minicard-label-text'}}")
span {{_ 'hide-minicard-label-text'}}
b &nbsp;
.materialCheckBox(class="{{#if hiddenMinicardLabelText}}is-checked{{/if}}")
@@ -39,6 +35,12 @@ template(name="membersWidget")
h3
i.fa.fa-users
| {{_ 'members'}}
.sidebar-shortcuts
a.board-header-btn.js-shortcuts(title="{{_ 'keyboard-shortcuts' }}")
i.fa.fa-keyboard-o
span {{_ 'keyboard-shortcuts' }}
unless currentUser.isCommentOnly
unless currentUser.isWorker
a.board-header-btn.js-open-board-menu(title="{{_ 'boardMenuPopup-title'}}").right
@@ -49,10 +51,10 @@ template(name="membersWidget")
+userAvatar(userId=this.userId showStatus=true)
if isSandstorm
if currentUser.isBoardMember
a.member.add-member.sandstorm-powerbox-request-identity
a.member.add-member.sandstorm-powerbox-request-identity(title="{{_ 'add-members'}}")
i.fa.fa-plus
else if currentUser.isBoardAdmin
a.member.add-member.js-manage-board-members
a.member.add-member.js-manage-board-members(title="{{_ 'add-members'}}")
i.fa.fa-plus
.clearfix
if isInvited
@@ -105,6 +107,14 @@ template(name="boardCardSettingsPopup")
span
i.fa.fa-users
| {{_ 'members'}}
div.check-div
a.flex.js-field-has-creator(class="{{#if allowsCreator}}is-checked{{/if}}")
.materialCheckBox(class="{{#if allowsCreator}}is-checked{{/if}}")
span
i.fa.fa-user
| {{_ 'creator'}}
div.check-div
a.flex.js-field-has-assignee(class="{{#if allowsAssignee}}is-checked{{/if}}")
.materialCheckBox(class="{{#if allowsAssignee}}is-checked{{/if}}")
@@ -123,6 +133,12 @@ template(name="boardCardSettingsPopup")
span
i.fa.fa-user-plus
| {{_ 'requested-by'}}
div.check-div
a.flex.js-field-has-card-sorting-by-number(class="{{#if allowsCardSortingByNumber}}is-checked{{/if}}")
.materialCheckBox(class="{{#if allowsCardSortingByNumber}}is-checked{{/if}}")
span
i.fa.fa-sort
| {{_ 'card-sorting-by-number'}}
div.check-div
a.flex.js-field-has-labels(class="{{#if allowsLabels}}is-checked{{/if}}")
.materialCheckBox(class="{{#if allowsLabels}}is-checked{{/if}}")
@@ -354,7 +370,7 @@ template(name="labelsWidget")
+viewer
= name
if currentUser.isBoardAdmin
a.card-label.add-label.js-add-label
a.card-label.add-label.js-add-label(title="{{_ 'label-create'}}")
i.fa.fa-plus
template(name="memberPopup")
+41
View File
@@ -730,6 +730,14 @@ BlazeComponent.extendComponent({
return this.currentBoard.allowsSubtasks;
},
allowsCreator() {
return (
this.currentBoard.allowsCreator === null ||
this.currentBoard.allowsCreator === undefined ||
this.currentBoard.allowsCreator
);
},
allowsMembers() {
return this.currentBoard.allowsMembers;
},
@@ -746,6 +754,10 @@ BlazeComponent.extendComponent({
return this.currentBoard.allowsRequestedBy;
},
allowsCardSortingByNumber() {
return this.currentBoard.allowsCardSortingByNumber;
},
allowsLabels() {
return this.currentBoard.allowsLabels;
},
@@ -889,6 +901,19 @@ BlazeComponent.extendComponent({
this.currentBoard.allowsSubtasks,
);
},
'click .js-field-has-creator'(evt) {
evt.preventDefault();
this.currentBoard.allowsCreator = !this.currentBoard.allowsCreator;
this.currentBoard.setAllowsCreator(this.currentBoard.allowsCreator);
$(`.js-field-has-creator ${MCB}`).toggleClass(
CKCLS,
this.currentBoard.allowsCreator,
);
$('.js-field-has-creator').toggleClass(
CKCLS,
this.currentBoard.allowsCreator,
);
},
'click .js-field-has-members'(evt) {
evt.preventDefault();
this.currentBoard.allowsMembers = !this.currentBoard.allowsMembers;
@@ -947,6 +972,22 @@ BlazeComponent.extendComponent({
this.currentBoard.allowsRequestedBy,
);
},
'click .js-field-has-card-sorting-by-number'(evt) {
evt.preventDefault();
this.currentBoard.allowsCardSortingByNumber = !this.currentBoard
.allowsCardSortingByNumber;
this.currentBoard.setAllowsCardSortingByNumber(
this.currentBoard.allowsCardSortingByNumber,
);
$(`.js-field-has-card-sorting-by-number ${MCB}`).toggleClass(
CKCLS,
this.currentBoard.allowsCardSortingByNumber,
);
$('.js-field-has-card-sorting-by-number').toggleClass(
CKCLS,
this.currentBoard.allowsCardSortingByNumber,
);
},
'click .js-field-has-labels'(evt) {
evt.preventDefault();
this.currentBoard.allowsLabels = !this.currentBoard.allowsLabels;
+3 -4
View File
@@ -100,13 +100,12 @@
margin-right: 10px
.sidebar-shortcuts
margin: 0
position: absolute
margin-left: 40%
padding: 0
top: auto
text-align: center
top: 7px
font-size: 0.8em
line-height: 1.6em
vertical-align: middle
color: darken(white, 40%)
.board-sidebar
@@ -50,6 +50,15 @@ template(name="createCustomFieldPopup")
each dropdownItems.get
input.js-dropdown-item(type="text" value=name placeholder="")
input.js-dropdown-item.last(type="text" value="" placeholder="{{_ 'custom-field-dropdown-options-placeholder'}}")
div.js-field-settings.js-field-settings-stringtemplate(class="{{#if isTypeNotSelected 'stringtemplate'}}hide{{/if}}")
label
| {{_ 'custom-field-stringtemplate-format'}}
input.js-field-stringtemplate-format(type="text" value=getStringtemplateFormat)
label
| {{_ 'custom-field-stringtemplate-separator'}}
input.js-field-stringtemplate-separator(type="text" value=getStringtemplateSeparator)
a.flex.js-field-show-on-card(class="{{#if showOnCard}}is-checked{{/if}}")
.materialCheckBox(class="{{#if showOnCard}}is-checked{{/if}}")
@@ -16,7 +16,15 @@ BlazeComponent.extendComponent({
}).register('customFieldsSidebar');
const CreateCustomFieldPopup = BlazeComponent.extendComponent({
_types: ['text', 'number', 'date', 'dropdown', 'currency', 'checkbox'],
_types: [
'text',
'number',
'date',
'dropdown',
'currency',
'checkbox',
'stringtemplate',
],
_currencyList: [
{
@@ -77,6 +85,18 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({
? this.data().settings.dropdownItems
: [],
);
this.stringtemplateFormat = new ReactiveVar(
this.data().settings && this.data().settings.stringtemplateFormat
? this.data().settings.stringtemplateFormat
: '',
);
this.stringtemplateSeparator = new ReactiveVar(
this.data().settings && this.data().settings.stringtemplateSeparator
? this.data().settings.stringtemplateSeparator
: '',
);
},
types() {
@@ -121,6 +141,14 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({
return items;
},
getStringtemplateFormat() {
return this.stringtemplateFormat.get();
},
getStringtemplateSeparator() {
return this.stringtemplateSeparator.get();
},
getSettings() {
const settings = {};
switch (this.type.get()) {
@@ -136,6 +164,14 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({
settings.dropdownItems = dropdownItems;
break;
}
case 'stringtemplate': {
const stringtemplateFormat = this.stringtemplateFormat.get();
settings.stringtemplateFormat = stringtemplateFormat;
const stringtemplateSeparator = this.stringtemplateSeparator.get();
settings.stringtemplateSeparator = stringtemplateSeparator;
break;
}
}
return settings;
},
@@ -158,6 +194,14 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({
evt.target.value = '';
}
},
'input .js-field-stringtemplate-format'(evt) {
const value = evt.target.value;
this.stringtemplateFormat.set(value);
},
'input .js-field-stringtemplate-separator'(evt) {
const value = evt.target.value;
this.stringtemplateSeparator.set(value);
},
'click .js-field-show-on-card'(evt) {
let $target = $(evt.target);
if (!$target.hasClass('js-field-show-on-card')) {
@@ -72,6 +72,43 @@ template(name="filterSidebar")
| (<span class="username">{{ username }}</span>)
if Filter.assignees.isSelected _id
i.fa.fa-check
hr
h3
i.fa.fa-list-alt
| {{_ 'filter-dates-label' }}
ul.sidebar-list
li(class="{{#if Filter.dueAt.isSelected 'noDate'}}active{{/if}}")
a.name.js-toggle-no-due-date-filter
span.sidebar-list-item-description
| {{_ 'filter-no-due-date' }}
if Filter.dueAt.isSelected 'noDate'
i.fa.fa-check
li(class="{{#if Filter.dueAt.isSelected 'past'}}active{{/if}}")
a.name.js-toggle-overdue-filter
span.sidebar-list-item-description
| {{_ 'filter-overdue' }}
if Filter.dueAt.isSelected 'past'
i.fa.fa-check
li(class="{{#if Filter.dueAt.isSelected 'today'}}active{{/if}}")
a.name.js-toggle-due-today-filter
span.sidebar-list-item-description
| {{_ 'filter-due-today' }}
if Filter.dueAt.isSelected 'today'
i.fa.fa-check
li(class="{{#if Filter.dueAt.isSelected 'tomorrow'}}active{{/if}}")
a.name.js-toggle-due-tomorrow-filter
span.sidebar-list-item-description
| {{_ 'filter-due-tomorrow' }}
if Filter.dueAt.isSelected 'tomorrow'
i.fa.fa-check
li(class="{{#if Filter.dueAt.isSelected 'week'}}active{{/if}}")
a.name.js-toggle-due-this-week-filter
span.sidebar-list-item-description
| {{_ 'filter-due-this-week' }}
if Filter.dueAt.isSelected 'week'
i.fa.fa-check
hr
h3
i.fa.fa-list-alt
@@ -23,6 +23,31 @@ BlazeComponent.extendComponent({
Filter.assignees.toggle(this.currentData()._id);
Filter.resetExceptions();
},
'click .js-toggle-no-due-date-filter'(evt) {
evt.preventDefault();
Filter.dueAt.noDate();
Filter.resetExceptions();
},
'click .js-toggle-overdue-filter'(evt) {
evt.preventDefault();
Filter.dueAt.past();
Filter.resetExceptions();
},
'click .js-toggle-due-today-filter'(evt) {
evt.preventDefault();
Filter.dueAt.today();
Filter.resetExceptions();
},
'click .js-toggle-due-tomorrow-filter'(evt) {
evt.preventDefault();
Filter.dueAt.tomorrow();
Filter.resetExceptions();
},
'click .js-toggle-due-this-week-filter'(evt) {
evt.preventDefault();
Filter.dueAt.thisWeek();
Filter.resetExceptions();
},
'click .js-toggle-archive-filter'(evt) {
evt.preventDefault();
Filter.archive.toggle(this.currentData()._id);
@@ -1,5 +1,5 @@
template(name="swimlaneHeader")
.swimlane-header-wrap.js-swimlane-header(class=colorClass)
.swimlane-header-wrap.js-swimlane-header(class=colorClass title="{{_ 'rename'}}")
if this.isTemplateContainer
+swimlaneFixedHeader(this)
else
@@ -16,8 +16,8 @@ template(name="swimlaneFixedHeader")
.swimlane-header-menu
unless currentUser.isCommentOnly
if currentUser.isBoardAdmin
a.fa.fa-plus.js-open-add-swimlane-menu.swimlane-header-plus-icon
a.fa.fa-navicon.js-open-swimlane-menu
a.fa.fa-plus.js-open-add-swimlane-menu.swimlane-header-plus-icon(title="{{_ 'add-swimlane'}}")
a.fa.fa-navicon.js-open-swimlane-menu(title="{{_ 'swimlaneActionPopup-title'}}")
unless isMiniScreen
if showDesktopDragHandles
a.swimlane-header-handle.handle.fa.fa-arrows.js-swimlane-header-handle
@@ -39,6 +39,8 @@ template(name="swimlaneActionPopup")
hr
ul.pop-over-list
li: a.js-close-swimlane {{_ 'archive-swimlane'}}
ul.pop-over-list
li: a.js-copy-swimlane {{_ 'copy-swimlane'}}
ul.pop-over-list
li: a.js-move-swimlane {{_ 'move-swimlane'}}
@@ -55,6 +55,7 @@ Template.swimlaneActionPopup.events({
Popup.close();
},
'click .js-move-swimlane': Popup.open('moveSwimlane'),
'click .js-copy-swimlane': Popup.open('copySwimlane'),
});
Template.swimlaneActionPopup.events({
+11 -2
View File
@@ -58,9 +58,8 @@ template(name="addListForm")
| {{_ 'or'}}
a.js-list-template {{_ 'template'}}
else
a.open-list-composer.js-open-inlined-form
a.open-list-composer.js-open-inlined-form(title="{{_ 'add-list'}}")
i.fa.fa-plus
| {{_ 'add-list'}}
template(name="moveSwimlanePopup")
unless currentUser.isWorker
@@ -71,3 +70,13 @@ template(name="moveSwimlanePopup")
.edit-controls.clearfix
button.primary.confirm.js-done {{_ 'done'}}
template(name="copySwimlanePopup")
unless currentUser.isWorker
label {{_ 'boards'}}:
select.js-select-boards(autofocus)
each toBoard in toBoards
option(value="{{toBoard._id}}" selected="{{#if $eq toBoard.title board.title}}1{{/if}}") {{toBoard.title}}
.edit-controls.clearfix
button.primary.confirm.js-done {{_ 'done'}}
+30 -21
View File
@@ -324,45 +324,54 @@ BlazeComponent.extendComponent({
},
}).register('listsGroup');
BlazeComponent.extendComponent({
class MoveSwimlaneComponent extends BlazeComponent {
serverMethod = 'moveSwimlane';
onCreated() {
this.currentSwimlane = this.currentData();
},
}
board() {
return Boards.findOne(Session.get('currentBoard'));
},
}
toBoardsSelector() {
return {
archived: false,
'members.userId': Meteor.userId(),
type: 'board',
_id: { $ne: this.board()._id },
};
}
toBoards() {
const boards = Boards.find(
{
archived: false,
'members.userId': Meteor.userId(),
type: 'board',
_id: { $ne: this.board()._id },
},
{
sort: { title: 1 },
},
);
return boards;
},
return Boards.find(this.toBoardsSelector(), { sort: { title: 1 } });
}
events() {
return [
{
'click .js-done'() {
const swimlane = Swimlanes.findOne(this.currentSwimlane._id);
// const swimlane = Swimlanes.findOne(this.currentSwimlane._id);
const bSelect = $('.js-select-boards')[0];
let boardId;
if (bSelect) {
boardId = bSelect.options[bSelect.selectedIndex].value;
Meteor.call('moveSwimlane', this.currentSwimlane._id, boardId);
Meteor.call(this.serverMethod, this.currentSwimlane._id, boardId);
}
Popup.close();
},
},
];
},
}).register('moveSwimlanePopup');
}
}
MoveSwimlaneComponent.register('moveSwimlanePopup');
(class extends MoveSwimlaneComponent {
serverMethod = 'copySwimlane';
toBoardsSelector() {
const selector = super.toBoardsSelector();
delete selector._id;
return selector;
}
}.register('copySwimlanePopup'));
+5 -4
View File
@@ -1,5 +1,5 @@
template(name="userAvatar")
a.member.js-member(title="{{userData.profile.fullname}} ({{userData.username}}) {{_ memberType}}")
a.member(class="js-{{#if assignee}}assignee{{else}}member{{/if}}" title="{{userData.profile.fullname}} ({{userData.username}}) {{_ memberType}}")
if userData.profile.avatarUrl
img.avatar.avatar-image(src="{{userData.profile.avatarUrl}}")
else
@@ -72,9 +72,10 @@ template(name="cardMemberPopup")
h3= user.profile.fullname
p.quiet @{{ user.username }}
ul.pop-over-list
if currentUser.isNotCommentOnly
if currentUser.isNotWorker
li: a.js-remove-member {{_ 'remove-member-from-card'}}
unless noRemove
if currentUser.isNotCommentOnly
if currentUser.isNotWorker
li: a.js-remove-member {{_ 'remove-member-from-card'}}
if $eq currentUser._id user._id
with currentUser
+4 -4
View File
@@ -1,3 +1,7 @@
import Cards from '/models/cards';
import Avatars from '/models/avatars';
import Users from '/models/users';
Template.userAvatar.helpers({
userData() {
// We need to handle a special case for the search results provided by the
@@ -30,10 +34,6 @@ Template.userAvatar.helpers({
},
});
Template.userAvatar.events({
'click .js-change-avatar': Popup.open('changeAvatar'),
});
Template.userAvatarInitials.helpers({
initials() {
const user = Users.findOne(this.userId);
+1 -5
View File
@@ -1,6 +1,6 @@
template(name="headerUserBar")
#header-user-bar
a.header-user-bar-name.js-open-header-member-menu
a.header-user-bar-name.js-open-header-member-menu(title="{{_ 'memberMenuPopup-title'}}")
.header-user-bar-avatar
+userAvatar(userId=currentUser._id)
unless isMiniScreen
@@ -25,10 +25,6 @@ template(name="memberMenuPopup")
a.js-global-search(href="{{pathFor 'global-search'}}")
i.fa.fa-search
| {{_ 'globalSearch-title'}}
li
a.js-broken-cards(href="{{pathFor 'broken-cards'}}")
i.fa.fa-chain-broken
| {{_ 'broken-cards'}}
li
a(href="{{pathFor 'home'}}")
span.fa.fa-home
+2
View File
@@ -174,6 +174,8 @@ Template.changeLanguagePopup.helpers({
} else if (lang.name === 'fa-IR') {
// fa-IR = Persian (Iran)
name = 'فارسی/پارسی (ایران‎)';
} else if (lang.name === 'de-CH') {
name = 'Deutsch (Schweiz)';
} else if (lang.name === 'fr-BE') {
name = 'Français (Belgique)';
} else if (lang.name === 'fr-CA') {
+12 -6
View File
@@ -32,8 +32,11 @@ export class CardSearchPagedComponent extends BlazeComponent {
that.searching.set(false);
that.hasResults.set(false);
that.serverError.set(true);
// eslint-disable-next-line no-console
console.log('Error.reason:', error.reason);
// eslint-disable-next-line no-console
console.log('Error.message:', error.message);
// eslint-disable-next-line no-console
console.log('Error.stack:', error.stack);
},
};
@@ -62,7 +65,6 @@ export class CardSearchPagedComponent extends BlazeComponent {
// console.log('getting results');
const sessionData = this.getSessionData();
// eslint-disable-next-line no-console
// console.log('selector:', sessionData.getSelector());
console.log('session data:', sessionData);
const cards = [];
sessionData.cards.forEach(cardId => {
@@ -72,7 +74,7 @@ export class CardSearchPagedComponent extends BlazeComponent {
if (this.queryErrors.length) {
// console.log('queryErrors:', this.queryErrorMessages());
this.hasQueryErrors.set(true);
return null;
// return null;
}
if (cards) {
@@ -99,10 +101,8 @@ export class CardSearchPagedComponent extends BlazeComponent {
}
}
runGlobalSearch(queryParams) {
this.searching.set(true);
this.stopSubscription();
this.subscriptionHandle = Meteor.subscribe(
getSubscription(queryParams) {
return Meteor.subscribe(
'globalSearch',
this.sessionId,
queryParams.params,
@@ -111,6 +111,12 @@ export class CardSearchPagedComponent extends BlazeComponent {
);
}
runGlobalSearch(queryParams) {
this.searching.set(true);
this.stopSubscription();
this.subscriptionHandle = this.getSubscription(queryParams);
}
queryErrorMessages() {
const messages = [];
+15 -17
View File
@@ -6,17 +6,17 @@ function adjustedTimeFormat() {
.replace(/HH/i, 'H');
}
DatePicker = BlazeComponent.extendComponent({
export class DatePicker extends BlazeComponent {
template() {
return 'datepicker';
},
}
onCreated(defaultTime = '1970-01-01 08:00:00') {
this.error = new ReactiveVar('');
this.card = this.data();
this.date = new ReactiveVar(moment.invalid());
this.defaultTime = defaultTime;
},
}
startDayOfWeek() {
const currentUser = Meteor.user();
@@ -25,7 +25,7 @@ DatePicker = BlazeComponent.extendComponent({
} else {
return 1;
}
},
}
onRendered() {
const $picker = this.$('.js-datepicker')
@@ -42,7 +42,7 @@ DatePicker = BlazeComponent.extendComponent({
this.error.set('');
const timeInput = this.find('#time');
timeInput.focus();
if (!timeInput.value) {
if (!timeInput.value && this.defaultTime) {
const currentHour = evt.date.getHours();
const defaultMoment = moment(
currentHour > 0 ? evt.date : this.defaultTime,
@@ -55,22 +55,22 @@ DatePicker = BlazeComponent.extendComponent({
if (this.date.get().isValid()) {
$picker.datepicker('update', this.date.get().toDate());
}
},
}
showDate() {
if (this.date.get().isValid()) return this.date.get().format('L');
return '';
},
}
showTime() {
if (this.date.get().isValid()) return this.date.get().format('LT');
return '';
},
}
dateFormat() {
return moment.localeData().longDateFormat('L');
},
}
timeFormat() {
return moment.localeData().longDateFormat('LT');
},
}
events() {
return [
@@ -106,7 +106,7 @@ DatePicker = BlazeComponent.extendComponent({
const dateString = `${evt.target.date.value} ${time}`;
const newCompleteDate = moment(
dateString,
'L ' + adjustedTimeFormat(),
`L ${adjustedTimeFormat()}`,
true,
);
if (!newTime.isValid()) {
@@ -120,10 +120,8 @@ DatePicker = BlazeComponent.extendComponent({
if (newCompleteDate.isValid()) {
this._storeDate(newCompleteDate.toDate());
Popup.close();
} else {
if (!this.error) {
this.error.set('invalid');
}
} else if (!this.error) {
this.error.set('invalid');
}
},
'click .js-delete-date'(evt) {
@@ -133,5 +131,5 @@ DatePicker = BlazeComponent.extendComponent({
},
},
];
},
});
}
}
+150
View File
@@ -7,6 +7,154 @@ function showFilterSidebar() {
Sidebar.setView('filter');
}
class DateFilter {
constructor() {
this._dep = new Tracker.Dependency();
this.subField = ''; // Prevent name mangling in Filter
this._filter = null;
this._filterState = null;
}
_updateState(state) {
this._filterState = state;
showFilterSidebar();
this._dep.changed();
}
// past builds a filter for all dates before now
past() {
if (this._filterState == 'past') {
this.reset();
return;
}
this._filter = { $lte: moment().toDate() };
this._updateState('past');
}
// today is a convenience method for calling relativeDay with 0
today() {
if (this._filterState == 'today') {
this.reset();
return;
}
this.relativeDay(0);
this._updateState('today');
}
// tomorrow is a convenience method for calling relativeDay with 1
tomorrow() {
if (this._filterState == 'tomorrow') {
this.reset();
return;
}
this.relativeDay(1);
this._updateState('tomorrow');
}
// thisWeek is a convenience method for calling relativeWeek with 1
thisWeek() {
this.relativeWeek(1);
}
// relativeDay builds a filter starting from now and including all
// days up to today +/- offset.
relativeDay(offset) {
if (this._filterState == 'day') {
this.reset();
return;
}
var startDay = moment()
.startOf('day')
.toDate(),
endDay = moment()
.endOf('day')
.add(offset, 'day')
.toDate();
if (offset >= 0) {
this._filter = { $gte: startDay, $lte: endDay };
} else {
this._filter = { $lte: startDay, $gte: endDay };
}
this._updateState('day');
}
// relativeWeek builds a filter starting from today and including all
// weeks up to today +/- offset. This considers the user's preferred
// start of week day (as defined by Meteor).
relativeWeek(offset) {
if (this._filterState == 'week') {
this.reset();
return;
}
// getStartDayOfWeek returns the offset from Sunday of the user's
// preferred starting day of the week. This date should be added
// to the moment start of week to get the real start of week date.
// The default is 1, meaning Monday.
const currentUser = Meteor.user();
const weekStartDay = currentUser ? currentUser.getStartDayOfWeek() : 1;
// Moments are mutable so they must be cloned before modification
var thisWeekStart = moment()
.startOf('day')
.startOf('week')
.add(weekStartDay, 'days');
var thisWeekEnd = thisWeekStart
.clone()
.add(offset, 'week')
.endOf('day');
var startDate = thisWeekStart.toDate();
var endDate = thisWeekEnd.toDate();
if (offset >= 0) {
this._filter = { $gte: startDate, $lte: endDate };
} else {
this._filter = { $lte: startDate, $gte: endDate };
}
this._updateState('week');
}
// noDate builds a filter for items where date is not set
noDate() {
if (this._filterState == 'noDate') {
this.reset();
return;
}
this._filter = null;
this._updateState('noDate');
}
reset() {
this._filter = null;
this._filterState = null;
this._dep.changed();
}
isSelected(val) {
this._dep.depend();
return this._filterState == val;
}
_isActive() {
this._dep.depend();
return this._filterState !== null;
}
_getMongoSelector() {
this._dep.depend();
return this._filter;
}
_getEmptySelector() {
this._dep.depend();
return null;
}
}
// Use a "set" filter for a field that is a set of documents uniquely
// identified. For instance `{ labels: ['labelA', 'labelC', 'labelD'] }`.
// use "subField" for searching inside object Fields.
@@ -462,6 +610,7 @@ Filter = {
assignees: new SetFilter(),
archive: new SetFilter(),
hideEmpty: new SetFilter(),
dueAt: new DateFilter(),
customFields: new SetFilter('_id'),
advanced: new AdvancedFilter(),
lists: new AdvancedFilter(), // we need the ability to filter list by name as well
@@ -472,6 +621,7 @@ Filter = {
'assignees',
'archive',
'hideEmpty',
'dueAt',
'customFields',
],
+27
View File
@@ -0,0 +1,27 @@
Meteor.subscribe('setting');
import { ALLOWED_WAIT_SPINNERS } from '/config/const';
export class Spinner extends BlazeComponent {
currentSettings() {
return Settings.findOne();
}
getSpinnerName() {
let ret = 'Bounce';
let defaultWaitSpinner = Meteor.settings.public.WAIT_SPINNER;
if (defaultWaitSpinner && ALLOWED_WAIT_SPINNERS.includes(defaultWaitSpinner)) {
ret = defaultWaitSpinner;
}
let settings = this.currentSettings();
if (settings && settings.spinnerName) {
ret = settings.spinnerName;
}
return ret;
}
getSpinnerTemplate() {
return 'spinner' + this.getSpinnerName().replace(/-/, '');
}
}
+61
View File
@@ -0,0 +1,61 @@
export const ALLOWED_BOARD_COLORS = [
'belize',
'nephritis',
'pomegranate',
'pumpkin',
'wisteria',
'moderatepink',
'strongcyan',
'limegreen',
'midnight',
'dark',
'relax',
'corteza',
'clearblue',
'natural',
'modern',
'moderndark',
];
export const ALLOWED_COLORS = [
'white',
'green',
'yellow',
'orange',
'red',
'purple',
'blue',
'sky',
'lime',
'pink',
'black',
'silver',
'peachpuff',
'crimson',
'plum',
'darkgreen',
'slateblue',
'magenta',
'gold',
'navy',
'gray',
'saddlebrown',
'paleturquoise',
'mistyrose',
'indigo',
];
export const TYPE_BOARD = 'board';
export const TYPE_CARD = 'cardType-card';
export const TYPE_LINKED_BOARD = 'cardType-linkedBoard';
export const TYPE_LINKED_CARD = 'cardType-linkedCard';
export const TYPE_TEMPLATE_BOARD = 'template-board';
export const TYPE_TEMPLATE_CONTAINER = 'template-container';
export const ALLOWED_WAIT_SPINNERS = [
'Bounce',
'Cube',
'Cube-Grid',
'Dot',
'Double-Bounce',
'Rotateplane',
'Scaleout',
'Wave'
];
+15 -2
View File
@@ -3,6 +3,7 @@ import {
OPERATOR_BOARD,
OPERATOR_COMMENT,
OPERATOR_CREATED_AT,
OPERATOR_CREATOR,
OPERATOR_DUE,
OPERATOR_HAS,
OPERATOR_LABEL,
@@ -52,7 +53,9 @@ export class QueryParams {
hasOperator(operator) {
return (
this.params[operator] !== undefined && this.params[operator].length > 0
this.params[operator] !== undefined &&
(this.params[operator].length === undefined ||
this.params[operator].length > 0)
);
}
@@ -68,7 +71,11 @@ export class QueryParams {
}
getPredicate(operator) {
return this.params[operator][0];
if (typeof this.params[operator] === 'object') {
return this.params[operator][0];
} else {
return this.params[operator];
}
}
getPredicates(operator) {
@@ -107,6 +114,7 @@ export class QueryErrors {
[OPERATOR_USER, 'user-username-not-found'],
[OPERATOR_ASSIGNEE, 'user-username-not-found'],
[OPERATOR_MEMBER, 'user-username-not-found'],
[OPERATOR_CREATOR, 'user-username-not-found'],
];
constructor() {
@@ -196,6 +204,10 @@ export class Query {
return this.queryParams;
}
setQueryParams(queryParams) {
this.queryParams = queryParams;
}
addPredicate(operator, predicate) {
this.queryParams.addPredicate(operator, predicate);
}
@@ -238,6 +250,7 @@ export class Query {
'operator-member': OPERATOR_MEMBER,
'operator-member-abbrev': OPERATOR_MEMBER,
'operator-assignee': OPERATOR_ASSIGNEE,
'operator-creator': OPERATOR_CREATOR,
'operator-assignee-abbrev': OPERATOR_ASSIGNEE,
'operator-status': OPERATOR_STATUS,
'operator-due': OPERATOR_DUE,
+22
View File
@@ -291,6 +291,28 @@ FlowRouter.route('/people', {
},
});
FlowRouter.route('/admin-reports', {
name: 'admin-reports',
triggersEnter: [
AccountsTemplates.ensureSignedIn,
() => {
Session.set('currentBoard', null);
Session.set('currentList', null);
Session.set('currentCard', null);
Filter.reset();
Session.set('sortBy', '');
EscapeActions.executeAll();
},
],
action() {
BlazeLayout.render('defaultLayout', {
headerBar: 'settingHeaderBar',
content: 'adminReports',
});
},
});
FlowRouter.notFound = {
action() {
BlazeLayout.render('defaultLayout', { content: 'notFound' });
Loaded 100 of 484 files, more files were not shown because too many files have changed in this diff. Show more