Compare commits

...
239 Commits
Author SHA1 Message Date
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
Lauri Ojansivu 4cadc5f4f3 v5.17 2021-04-01 01:51:20 +03:00
Lauri Ojansivu 8dfb6916c5 Added stable tag release script. 2021-04-01 01:49:41 +03:00
Lauri Ojansivu 6066be944a Updated translations. 2021-04-01 01:45:24 +03:00
Lauri Ojansivu e9f8279dbe Updated ChangeLog. 2021-04-01 01:38:05 +03:00
Lauri Ojansivu 454d3b5bbe Fix Link dialog closes card when clicking in dialog.
Thanks to ryanMushy !

Fixes #3455
2021-04-01 01:34:23 +03:00
Lauri Ojansivu fe185dd38a v5.16 2021-04-01 00:40:02 +03:00
Lauri Ojansivu 522b14d96d Updated translations. 2021-04-01 00:35:26 +03:00
Lauri Ojansivu d98b489263 Updated ChangeLog. 2021-04-01 00:33:13 +03:00
Lauri Ojansivu 47ecc654b8 Added back Summernote editor. Removed emoji picker.
Thanks to ryanMushy and xet7 !

Related https://github.com/wekan/wekan/commit/84fde1ecfc81e89ed1895cab3bcb328e4f166a87#commitcomment-48956373
2021-04-01 00:30:46 +03:00
Lauri Ojansivu b9a4ae654e Updated ChangeLog. 2021-03-31 22:00:45 +03:00
Lauri Ojansivu c40668be3f Added stable tag.
Thanks to rynr and xet7 !

Related https://github.com/wekan/wekan/discussions/3691
2021-03-31 21:58:23 +03:00
Lauri Ojansivu a9d77b975e v5.15 2021-03-31 18:20:06 +03:00
Lauri Ojansivu 085bf3b6e6 Updated ChangeLog. 2021-03-31 18:16:49 +03:00
Lauri Ojansivu 91dae9795e Updated release website scripts. Part 2.
Thanks to xet7 !
2021-03-31 18:15:21 +03:00
Lauri Ojansivu 42081db0d8 Updated translations. 2021-03-31 18:14:11 +03:00
Lauri Ojansivu 3af417209c Updated ChangeLog. 2021-03-31 18:11:13 +03:00
Lauri Ojansivu b6f60c08e5 Fixed release website script.
Thanks to xet7 !
2021-03-31 18:09:47 +03:00
Lauri Ojansivu 03d7940829 Updated ChangeLog. 2021-03-31 17:55:04 +03:00
Lauri Ojansivu 0b263cf582 - Fixed Bug: Move Swimlane to Archive does not work anymore.
- Fixed lint in router.js

Thanks to marcungeschikts and xet7 !

Fixes #3690
2021-03-31 17:52:22 +03:00
Lauri Ojansivu 8ef60a06ad Updated ChangeLog. 2021-03-31 16:18:00 +03:00
Lauri Ojansivu 91eb4b941d Merge pull request #3688 from jrsupplee/issue-1667
Fix bug in Due Cards introduced by last bug fix
2021-03-31 16:16:58 +03:00
Lauri Ojansivu 2171690d22 Updated ChangeLog. 2021-03-31 16:13:58 +03:00
Lauri Ojansivu 6819303047 Updated node update local script.
Thanks to xet7 !
2021-03-31 16:12:16 +03:00
Lauri Ojansivu 88e19bd011 Updated ChangeLog. 2021-03-31 16:08:35 +03:00
Lauri Ojansivu 09b9f690b1 Updated node-update.sh release script.
Thanks to xet7 !
2021-03-31 16:03:08 +03:00
Lauri Ojansivu 7a9c397264 Updated to Node.js v12.22.0. Thanks to Node.js developers. 2021-03-31 16:02:25 +03:00
Lauri Ojansivu c2b7525864 Updated to Node.js v12.22.0. Thanks to Node.js developers. 2021-03-31 16:01:12 +03:00
Lauri Ojansivu e2ecd490ff Updated ChangeLog. 2021-03-31 16:00:08 +03:00
Lauri Ojansivu 1d3673e9d3 Updated node update scipts. 2021-03-31 15:58:41 +03:00
Lauri Ojansivu 5bd85f975c Remove some extra text. 2021-03-31 15:44:18 +03:00
Lauri Ojansivu 89ab99b551 Updated ChangeLog. 2021-03-31 15:43:05 +03:00
Lauri Ojansivu 01de3f187c Set release script node-update.sh as executeable.
Thanks to xet7 !
2021-03-31 15:41:14 +03:00
Lauri Ojansivu dcd7925571 Updated ChangeLog. 2021-03-31 15:40:32 +03:00
Lauri Ojansivu 34b6aa0858 Updated release scripts.
Thanks to xet7 !
2021-03-31 15:39:07 +03:00
John R. Supplee 73fb62e941 Fix bug in Due Cards introduced by last bug fix 2021-03-31 13:53:35 +02:00
Lauri Ojansivu 55ced412a0 Updated ChangeLog. 2021-03-31 14:23:37 +03:00
Lauri Ojansivu 04a8d85836 Merge pull request #3687 from jrsupplee/issue-1667
Fix bug in My Cards and Global Search
2021-03-31 14:21:48 +03:00
John R. Supplee 392b701956 Fix bug in My Cards and Global Search 2021-03-31 13:05:39 +02:00
Lauri Ojansivu dee098c125 Updated translations. 2021-03-31 13:47:58 +03:00
Lauri Ojansivu 56e26a65ae Updated ChangeLog. 2021-03-31 13:15:55 +03:00
Lauri Ojansivu 69509d597f Merge pull request #3686 from ednamaeG/sort-fix
fixed sort reset
2021-03-31 13:13:44 +03:00
ednamaeG bc7b18abe7 Merge branch 'master' into sort-fix 2021-03-31 14:56:55 +08:00
mae 4a95cb42cd fixed sort reset 2021-03-31 14:51:08 +08:00
Lauri Ojansivu db311f677b Fix typo. 2021-03-29 12:05:06 +03:00
Lauri Ojansivu 685fb98e59 v5.14 2021-03-29 11:40:30 +03:00
Lauri Ojansivu 6dd98285a6 Updated translations. 2021-03-29 11:36:20 +03:00
Lauri Ojansivu e225d29d7a Updated translations. 2021-03-29 10:29:31 +03:00
Lauri Ojansivu eff7d123f2 Updated ChangeLog. 2021-03-29 10:18:22 +03:00
Lauri Ojansivu 4d62fe83f4 Merge pull request #3677 from jrsupplee/issue-3655
Require signed-in user for My Cards, Due Cards, and global search #3655
2021-03-29 10:15:31 +03:00
Lauri Ojansivu a37ca01703 Updated ChangeLog. 2021-03-29 10:13:09 +03:00
Lauri Ojansivu ba05f383ca Added environment variable RESULTS_PER_PAGE to all Wekan platforms.
Thanks to xet7 !
2021-03-29 10:11:13 +03:00
Lauri Ojansivu 4ea4913d72 Merge branch 'jrsupplee-search' 2021-03-29 09:43:21 +03:00
John R. Supplee 856f3bf864 Require signed-in user for My Cards, Due Cards, and global search 2021-03-29 02:01:12 +02:00
John R. Supplee 238b0f0725 Merge branch 'master' of https://github.com/wekan/wekan into search 2021-03-28 22:16:15 +02:00
Lauri Ojansivu a79a5c7299 Updated ChangeLog. 2021-03-28 19:31:01 +03:00
Lauri Ojansivu 59580e4b0f Updated release scripts.
Thanks to xet7 !
2021-03-28 19:28:48 +03:00
Lauri Ojansivu 7e05e02137 v5.13 2021-03-28 10:38:01 +03:00
Lauri Ojansivu d4a1611b86 Try to fix Sandstorm Wekan.
Thanks to xet7 !
2021-03-28 10:15:44 +03:00
Lauri Ojansivu 77abde7836 v5.12 2021-03-28 07:55:50 +03:00
Lauri Ojansivu 4c60916191 Fix HTTP not defined.
Thanks to xet7 !
2021-03-28 07:51:25 +03:00
Lauri Ojansivu 634eec7eb6 v5.11 2021-03-28 07:32:08 +03:00
Lauri Ojansivu 4ccbbc78cf Updated ChangeLog. 2021-03-28 07:26:05 +03:00
Lauri Ojansivu 4e1c0fdce8 Updated dependencies. Fixed lint.
Thanks to xet7 !
2021-03-28 07:24:24 +03:00
Lauri Ojansivu 9fa1d846e6 Updated translations. 2021-03-28 07:03:40 +03:00
Lauri Ojansivu 969dca292e Updated ChangeLog. 2021-03-28 07:01:28 +03:00
Lauri Ojansivu 84fde1ecfc Added emoji picker to card description edit and card comment edit.
Removed Summernote wysiwyg editor, package-lock.json etc.

Thanks to xet7 !
2021-03-28 06:56:05 +03:00
John R. Supplee db9ee59d9f Merge branch 'search' of supplee.net:wekan into search 2021-03-28 02:47:18 +02:00
John R. Supplee 20a2ea3e7a Add icon to clear the current search 2021-03-28 02:46:11 +02:00
John R. Supplee 39b4ada26d Do not include archived lists in global search help 2021-03-28 02:45:15 +02:00
John R. Supplee 103cfb936d Merge branch 'master' of https://github.com/wekan/wekan into search 2021-03-27 23:10:53 +02:00
John Supplee bb7490a32b Merge branch 'master' of https://github.com/wekan/wekan into search 2021-03-19 21:10:16 +02:00
John Supplee 9e8399612b Add environment variable and snap configuration option for results per page 2021-03-19 21:09:39 +02:00
John Supplee 0ebb427a00 Update the server error message 2021-03-18 02:04:25 +02:00
John R. Supplee 299410f8b4 Merge branch 'master' into search 2021-03-11 18:28:55 +02:00
John R. Supplee 07aa20a3cd Fix for Due Cards and some cleanup 2021-03-11 02:05:46 +02:00
John R. Supplee 06cae36199 Merge branch 'master' of https://github.com/wekan/wekan into search 2021-03-10 12:41:14 +02:00
John R. Supplee 183404a5f8 Code cleanup and remove debugging code 2021-03-10 12:40:20 +02:00
John R. Supplee 6def7d6f70 Move query parsing to Query class 2021-03-10 12:39:39 +02:00
John R. Supplee 097cae1f8c More integration with constants and query classes 2021-03-09 19:30:04 +02:00
John Supplee ba00311dd4 Add more constants and convert params object to a class 2021-03-09 02:21:43 +02:00
John R. Supplee 849b608933 Start adding constants for search operators and predicates 2021-03-08 19:18:01 +02:00
John R. Supplee 4ed5c3e4b8 Create array of cards from card search result card ids instead of re-running
the search on the client side.
2021-03-07 02:12:58 +02:00
John R. Supplee 9061180983 Code cleanup 2021-03-07 02:12:31 +02:00
John R. Supplee be970e4cea Merge branch 'search' of github.com:jrsupplee/wekan into search 2021-03-06 10:40:28 +02:00
John Supplee 8f978c23b4 Modify Broken Cards to use new search functions and component 2021-03-05 23:47:12 +02:00
John Supplee 5f53c71b0c Merge branch 'master' into search 2021-03-05 21:46:06 +02:00
John Supplee 58020863a8 Handle subscriptions better
* use onReady and onStop callbacks when subscribing
* show an server error message when the server returns an error
* call stop() on subscriptions
2021-03-05 21:20:55 +02:00
John Supplee 28aebaa06b Make search instructions code more uniform 2021-03-04 02:13:22 +02:00
John R. Supplee 329256712b Merge branch 'master' into search 2021-03-03 11:47:56 +02:00
John R. Supplee e0b544fc7e Add new Blaze search component 2021-03-01 20:18:44 +02:00
John R. Supplee a1bda1169e Update Global Search, Due Cards, and My Cards to use the same
code for searching and display
2021-03-01 01:49:56 +02:00
181 changed files with 62369 additions and 68058 deletions

No files matched your search

+2 -1
View File
@@ -10,7 +10,7 @@ ENV DEBIAN_FRONTEND=noninteractive
ENV \
DEBUG=false \
NODE_VERSION=v12.21.0 \
NODE_VERSION=v12.22.1 \
METEOR_RELEASE=1.10.2 \
USE_EDGE=false \
METEOR_EDGE=1.5-beta.17 \
@@ -19,6 +19,7 @@ ENV \
ARCHITECTURE=linux-x64 \
SRC_PATH=./ \
WITH_API=true \
RESULTS_PER_PAGE="" \
ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURES_BEFORE=3 \
ACCOUNTS_LOCKOUT_KNOWN_USERS_PERIOD=60 \
ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURE_WINDOW=15 \
+1 -1
View File
@@ -81,7 +81,7 @@ parts:
wekan:
source: .
plugin: nodejs
node-engine: 12.21.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.21.0
node-engine: 12.22.1
node-packages:
- node-gyp
- node-pre-gyp
+4 -4
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
@@ -23,7 +23,7 @@ dburles:collection-helpers
idmontie:migrations
matb33:collection-hooks
matteodem:easy-search
mongo@1.10.1
mongo@1.11.0
mquandalle:collection-mutations
# Account system
@@ -52,6 +52,7 @@ mquandalle:autofocus
ongoworks:speakingurl
raix:handlebar-helpers
tap:i18n
http@1.4.2
# UI components
blaze
@@ -90,7 +91,6 @@ meteorhacks:aggregate@1.3.0
wekan-markdown
konecty:mongo-counter
percolate:synced-cron
easylogic:summernote
cfs:filesystem
tmeasday:check-npm-versions
steffo:meteor-accounts-saml
@@ -144,4 +144,4 @@ rajit:bootstrap3-datepicker-zh-cn
rajit:bootstrap3-datepicker-zh-tw
staringatlights:fast-render
spacebars
jkuester:http
easylogic:summernote
+1 -1
View File
@@ -1 +1 @@
METEOR@2.1
METEOR@2.2
+22 -23
View File
@@ -1,5 +1,5 @@
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
aldeed:collection2@2.10.0
@@ -11,17 +11,17 @@ 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.4.0
blaze-tools@1.1.1
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
@@ -52,12 +52,12 @@ ddp@1.4.0
ddp-client@2.4.0
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,13 +70,12 @@ 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
html-tools@1.1.1
htmljs@1.1.0
http@1.4.3
id-map@1.1.0
idmontie:migrations@1.0.3
inter-process-messaging@0.1.1
jkuester:http@2.0.1
jquery@1.11.11
kadira:blaze-layout@2.3.0
kadira:dochead@1.5.0
@@ -85,7 +84,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
@@ -101,16 +100,16 @@ meteorhacks:collection-utils@1.2.0
meteorhacks:picker@1.0.3
meteorhacks:subs-manager@1.6.4
meteorspark:util@0.2.0
minifier-css@1.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.0
mongo-decimal@0.1.2
mongo-dev-server@1.1.0
mongo-id@1.0.7
@@ -126,7 +125,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
@@ -209,10 +208,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.2
softwarerero:accounts-t9n@1.3.11
spacebars@1.0.15
spacebars-compiler@1.1.3
spacebars@1.1.0
spacebars-compiler@1.2.1
srp@1.1.0
standard-minifier-css@1.7.2
standard-minifier-js@2.6.0
@@ -221,10 +220,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
templating@1.4.0
templating-compiler@1.4.0
templating-runtime@1.4.0
templating-tools@1.2.0
tmeasday:check-npm-versions@0.3.2
tracker@1.2.0
twbs:bootstrap@3.3.6
@@ -235,7 +234,7 @@ 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
+1 -1
View File
@@ -3,7 +3,7 @@ sudo: required
env:
TRAVIS_DOCKER_COMPOSE_VERSION: 1.24.0
TRAVIS_NODE_VERSION: 12.21.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
+295
View File
@@ -1,3 +1,298 @@
# 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:
- [Fix Link dialog closes card when clicking in dialog](https://github.com/wekan/wekan/commit/454d3b5bbeed6cef8ecface7e6094cabfcc4847c).
Thanks to ryanMushy.
and adds the following updates:
- [Added stable tag release script](https://github.com/wekan/wekan/commit/8dfb6916c5180f5d074748840d51dd04f9adb2bb).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v5.16 2021-04-01 Wekan release
This release adds the following new features:
- [Added stable tag](https://github.com/wekan/wekan/commit/c40668be3fb9c35d44698f49ab06fec5bcabbe1b).
Thanks to rynr and xet7.
- [Added back Summernote editor. Removed emoji picker](https://github.com/wekan/wekan/commit/47ecc654b825a875074dfd4826c36e2c5c55f599).
Thanks to ryanMushy and xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v5.15 2021-03-31 Wekan release
This release fixes the following bugs:
- [Fixed card sort reset](https://github.com/wekan/wekan/pull/3686).
Thanks to ednamaeG.
- [Fix bug in My Cards and Global Search](https://github.com/wekan/wekan/pull/3687).
Thanks to jrsupplee.
- [Fix bug in Due Cards introduced by last bug fix](https://github.com/wekan/wekan/pull/3688).
Thanks to jrsupplee.
- [Fixed Bug: Move Swimlane to Archive does not work anymore. Fixed lint in
router.js](https://github.com/wekan/wekan/commit/0b263cf582a9649ef72efbd289927105a27583af).
Thanks to marcungeschikts and xet7.
and adds the following updates:
- Updated sandstorm release script and added new node update script
[Part 1](https://github.com/wekan/wekan/commit/34b6aa0858678da937eacf9a87878bbcb476fd4b),
[Part 2](https://github.com/wekan/wekan/commit/01de3f187c90af3ac94215ba7e8c7e780c98768d),
[Part 3](https://github.com/wekan/wekan/commit/1d3673e9d320926127b46383321023f149287d6d),
[Part 4](https://github.com/wekan/wekan/commit/09b9f690b162ae2797d1996e82c96ed8b8c74221),
[Part 5](https://github.com/wekan/wekan/commit/6819303047eab17b03a0c28108fd9a2cfde23d20).
Thanks to xet7.
- Updated to Node.js v12.22.0
[Part 1](https://github.com/wekan/wekan/commit/7a9c3972642601e9d89d5e7a3816643f91448c63),
[Part 2](https://github.com/wekan/wekan/commit/c2b7525864048694c39f9cbe8f8c4cd96e36f7aa).
Thanks to Node.js developers.
- Fixed release website script
[Part 1](https://github.com/wekan/wekan/commit/b6f60c08e55c2620fe6724ae2b5da0d9dfc9ec31),
[Part 2](https://github.com/wekan/wekan/commit/91dae9795ebd1a98b8ed12c68b78ad90a6983402).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v5.14 2021-03-29 Wekan release
This release adds the following new features:
- [Clean-up Global Search, Due Cards, and My Cards. New environment variable `RESULTS_PER_PAGE` for search
results](https://github.com/wekan/wekan/pull/3676).
Thanks to jrsupplee.
- [Added environment variable `RESULTS_PER_PAGE` to all Wekan platforms](https://github.com/wekan/wekan/commit/ba05f383ca29211c5474e06c5ba6673e712afe7a).
Thanks to xet7.
and adds the following updates:
- [Updated release scripts](https://github.com/wekan/wekan/commit/59580e4b0f711ca55e8cb0d73803a4ff8b56352d).
Thanks to xet7.
and fixes the following bugs:
- [Require signed-in user for My Cards, Due Cards, and global search](https://github.com/wekan/wekan/pull/3677).
Thanks to jrsupplee.
Thanks to above GitHub users for their contributions and translators for their translations.
# v5.13 2021-03-28 Wekan release
This release fixes the following bugs:
- [Fixed Sandstorm Wekan attachments upload](https://github.com/wekan/wekan/commit/d4a1611b86521cd5913277cfa2c86c43958eec7b).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v5.12 2021-03-28 Wekan release
This release fixes the following bugs:
- [Fix HTTP not defined](https://github.com/wekan/wekan/commit/4c609161915cc46ebfccad3d9e7ffdecdef1f85c).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v5.11 2021-03-28 Wekan release
This release adds the following new features:
- [Added emoji picker to card description edit and card comment edit.
Removed and disabled Summernote wysiwyg editor, package-lock.json
etc](https://github.com/wekan/wekan/commit/84fde1ecfc81e89ed1895cab3bcb328e4f166a87).
Thanks to xet7.
and adds the following updates:
- [Updated dependencies. Fixed lint](https://github.com/wekan/wekan/commit/4e1c0fdce82e3b4add8c4ffd1832752181573e88).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v5.10 2021-03-28 Wekan release
This release adds the following new features:
+2 -1
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.21.0 \
NODE_VERSION=v12.22.1 \
METEOR_RELEASE=1.10.2 \
USE_EDGE=false \
METEOR_EDGE=1.5-beta.17 \
@@ -21,6 +21,7 @@ ENV BUILD_DEPS="apt-utils libarchive-tools gnupg gosu wget curl bzip2 g++ build-
ARCHITECTURE=linux-x64 \
SRC_PATH=./ \
WITH_API=true \
RESULTS_PER_PAGE="" \
ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURES_BEFORE=3 \
ACCOUNTS_LOCKOUT_KNOWN_USERS_PERIOD=60 \
ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURE_WINDOW=15 \
+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.21.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.21.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.10.0"
appVersion: "v5.26.0"
files:
userUploads:
- README.md
+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() {
-4
View File
@@ -54,10 +54,6 @@ Template.boardChangeTitlePopup.events({
});
BlazeComponent.extendComponent({
onCreated() {
// set sort to default
Session.set('sortBy', '');
},
watchLevel() {
const currentBoard = Boards.findOne(Session.get('currentBoard'));
return currentBoard && currentBoard.getWatchLevel(Meteor.userId());
@@ -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'}}")
.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'));
+1 -119
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() {
+1 -1
View File
@@ -3,5 +3,5 @@ template(name="descriptionForm")
.new-description.js-new-description(
class="{{#if descriptionFormIsOpen}}is-open{{/if}}")
form.js-new-description-form
+editor(class="js-new-description-input" data-meteor-emoji-large="true" autofocus="autofocus")
+editor(class="js-new-description-input" autofocus="autofocus")
| {{getUnsavedValue 'cardDescription' _id getDescription}}
@@ -3,6 +3,7 @@ const descriptionFormIsOpen = new ReactiveVar(false);
BlazeComponent.extendComponent({
onDestroyed() {
descriptionFormIsOpen.set(false);
$('.note-popover').hide();
},
descriptionFormIsOpen() {
+14 -26
View File
@@ -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'}}")
@@ -488,23 +497,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 +514,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
+19 -132
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];
@@ -160,9 +167,7 @@ BlazeComponent.extendComponent({
integration,
'CardSelected',
params,
() => {
return;
},
() => {},
);
});
}
@@ -290,7 +295,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();
@@ -407,122 +412,6 @@ BlazeComponent.extendComponent({
},
}).register('cardDetails');
Template.cardDetails.helpers({
userData() {
// We need to handle a special case for the search results provided by the
// `matteodem:easy-search` package. Since these results gets published in a
// separate collection, and not in the standard Meteor.Users collection as
// expected, we use a component parameter ("property") to distinguish the
// two cases.
const userCollection = this.esSearch ? ESSearchResults : Users;
return userCollection.findOne(this.userId, {
fields: {
profile: 1,
username: 1,
},
});
},
receivedSelected() {
if (this.getReceived().length === 0) {
return false;
} else {
return true;
}
},
startSelected() {
if (this.getStart().length === 0) {
return false;
} else {
return true;
}
},
endSelected() {
if (this.getEnd().length === 0) {
return false;
} else {
return true;
}
},
dueSelected() {
if (this.getDue().length === 0) {
return false;
} else {
return true;
}
},
memberSelected() {
if (this.getMembers().length === 0) {
return false;
} else {
return true;
}
},
labelSelected() {
if (this.getLabels().length === 0) {
return false;
} else {
return true;
}
},
assigneeSelected() {
if (this.getAssignees().length === 0) {
return false;
} else {
return true;
}
},
requestBySelected() {
if (this.getRequestBy().length === 0) {
return false;
} else {
return true;
}
},
assigneeBySelected() {
if (this.getAssigneeBy().length === 0) {
return false;
} else {
return true;
}
},
memberType() {
const user = Users.findOne(this.userId);
return user && user.isBoardAdmin() ? 'admin' : 'normal';
},
presenceStatusClassName() {
const user = Users.findOne(this.userId);
const userPresence = presences.findOne({ userId: this.userId });
if (user && user.isInvitedTo(Session.get('currentBoard'))) return 'pending';
else if (!userPresence) return 'disconnected';
else if (Session.equals('currentBoard', userPresence.state.currentBoardId))
return 'active';
else return 'idle';
},
});
Template.userAvatarAssigneeInitials.helpers({
initials() {
const user = Users.findOne(this.userId);
return user && user.getInitials();
},
viewPortWidth() {
const user = Users.findOne(this.userId);
return ((user && user.getInitials().length) || 1) * 12;
},
});
// We extends the normal InlinedForm component to support UnsavedEdits draft
// feature.
(class extends InlinedForm {
@@ -697,7 +586,7 @@ BlazeComponent.extendComponent({
},
boards() {
const boards = Boards.find(
return Boards.find(
{
archived: false,
'members.userId': Meteor.userId(),
@@ -707,7 +596,6 @@ BlazeComponent.extendComponent({
sort: { sort: 1 /* boards default sorting */ },
},
);
return boards;
},
swimlanes() {
@@ -736,7 +624,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];
@@ -801,7 +689,7 @@ Template.copyChecklistToManyCardsPopup.events({
});
// copy subtasks
cursor = Cards.find({ parentId: oldId });
const cursor = Cards.find({ parentId: oldId });
cursor.forEach(function() {
'use strict';
const subtask = arguments[0];
@@ -827,7 +715,7 @@ BlazeComponent.extendComponent({
},
colors() {
return cardColors.map(color => ({ color, name: '' }));
return ALLOWED_COLORS.map(color => ({ color, name: '' }));
},
isSelected(color) {
@@ -871,7 +759,7 @@ BlazeComponent.extendComponent({
},
boards() {
const boards = Boards.find(
return Boards.find(
{
archived: false,
'members.userId': Meteor.userId(),
@@ -883,7 +771,6 @@ BlazeComponent.extendComponent({
sort: { sort: 1 /* boards default sorting */ },
},
);
return boards;
},
cards() {
+1
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,
+12
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
@@ -122,3 +129,8 @@ 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
+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
+18 -6
View File
@@ -6,8 +6,12 @@ template(name="resultCard")
ul.result-card-context-list
li.result-card-context(title="{{_ 'board'}}")
.result-card-block-wrapper
+viewer
= getBoard.title
if boardId
+viewer
= getBoard.title
else
.broken-cards-null
| NULL
if getBoard.archived
i.fa.fa-archive
li.result-card-context.result-card-context-separator
@@ -16,8 +20,12 @@ template(name="resultCard")
= ' '
li.result-card-context(title="{{_ 'swimlane'}}")
.result-card-block-wrapper
+viewer
= getSwimlane.title
if swimlaneId
+viewer
= getSwimlane.title
else
.broken-cards-null
| NULL
if getSwimlane.archived
i.fa.fa-archive
li.result-card-context.result-card-context-separator
@@ -26,7 +34,11 @@ template(name="resultCard")
= ' '
li.result-card-context(title="{{_ 'list'}}")
.result-card-block-wrapper
+viewer
= getList.title
if listId
+viewer
= getList.title
else
.broken-cards-null
| NULL
if getList.archived
i.fa.fa-archive
+12 -36
View File
@@ -3,39 +3,15 @@ template(name="brokenCardsHeaderBar")
| {{_ 'broken-cards'}}
template(name="brokenCards")
.wrapper
.broken-cards-wrapper
each card in brokenCardsList
.broken-cards-card-wrapper
.broken-cards-card-title
= card.title
ul.broken-cards-context-list
li.broken-cards-context(title="{{_ 'board'}}")
if card.boardId
+viewer
= card.getBoard.title
else
.broken-cards-null
| NULL
li.broken-cards-context.broken-cards-context-separator
= ' '
| {{_ 'context-separator'}}
= ' '
li.broken-cards-context(title="{{_ 'swimlane'}}")
if card.swimlaneId
+viewer
= card.getSwimlane.title
else
.broken-cards-null
| NULL
li.broken-cards-context
= ' '
| {{_ 'context-separator'}}
= ' '
li.broken-cards-context(title="{{_ 'list'}}")
if card.listId
+viewer
= card.getList.title
else
.broken-cards-null
| NULL
if currentUser
if searching.get
+spinner
else if hasResults.get
.global-search-results-list-wrapper
if hasQueryErrors.get
div
each msg in errorMessages
span.global-search-error-messages
= msg
else
+resultsPaged(this)
+8 -18
View File
@@ -1,3 +1,5 @@
import { CardSearchPagedComponent } from '../../lib/cardSearch';
BlazeComponent.extendComponent({}).register('brokenCardsHeaderBar');
Template.brokenCards.helpers({
@@ -6,23 +8,11 @@ Template.brokenCards.helpers({
},
});
BlazeComponent.extendComponent({
class BrokenCardsComponent extends CardSearchPagedComponent {
onCreated() {
Meteor.subscribe('setting');
Meteor.subscribe('brokenCards');
},
super.onCreated();
brokenCardsList() {
const selector = {
$or: [
{ boardId: { $in: [null, ''] } },
{ swimlaneId: { $in: [null, ''] } },
{ listId: { $in: [null, ''] } },
{ permission: 'public' },
{ members: { $elemMatch: { userId: user._id, isActive: true } } },
],
};
return Cards.find(selector);
},
}).register('brokenCards');
Meteor.subscribe('brokenCards', this.sessionId);
}
}
BrokenCardsComponent.register('brokenCards');
+10 -6
View File
@@ -22,13 +22,17 @@ template(name="dueCardsModalTitle")
template(name="dueCards")
if currentUser
if isPageReady.get
.wrapper
.due-cards-dueat-list-wrapper
each card in dueCardsList
+resultCard(card)
else
if searching.get
+spinner
else if hasResults.get
.global-search-results-list-wrapper
if hasQueryErrors.get
div
each msg in errorMessages
span.global-search-error-messages
= msg
else
+resultsPaged(this)
template(name="dueCardsViewChangePopup")
if currentUser
+45 -88
View File
@@ -1,4 +1,14 @@
const subManager = new SubsManager();
import { CardSearchPagedComponent } from '../../lib/cardSearch';
import {
OPERATOR_HAS,
OPERATOR_SORT,
OPERATOR_USER,
ORDER_ASCENDING,
PREDICATE_DUE_AT,
} from '../../../config/search-const';
import { QueryParams } from '../../../config/query-classes';
// const subManager = new SubsManager();
BlazeComponent.extendComponent({
dueCardsView() {
@@ -40,106 +50,51 @@ BlazeComponent.extendComponent({
},
}).register('dueCardsViewChangePopup');
BlazeComponent.extendComponent({
class DueCardsComponent extends CardSearchPagedComponent {
onCreated() {
this.isPageReady = new ReactiveVar(false);
super.onCreated();
this.autorun(() => {
const handle = subManager.subscribe(
'dueCards',
Utils.dueCardsView() === 'all',
);
Tracker.nonreactive(() => {
Tracker.autorun(() => {
this.isPageReady.set(handle.ready());
});
});
const queryParams = new QueryParams();
queryParams.addPredicate(OPERATOR_HAS, {
field: PREDICATE_DUE_AT,
exists: true,
});
Meteor.subscribe('setting');
},
// queryParams[OPERATOR_LIMIT] = 5;
queryParams.addPredicate(OPERATOR_SORT, {
name: PREDICATE_DUE_AT,
order: ORDER_ASCENDING,
});
if (Utils.dueCardsView() !== 'all') {
queryParams.addPredicate(OPERATOR_USER, Meteor.user().username);
}
this.runGlobalSearch(queryParams);
}
dueCardsView() {
// eslint-disable-next-line no-console
//console.log('sort:', Utils.dueCardsView());
return Utils.dueCardsView();
},
}
sortByBoard() {
return this.dueCardsView() === 'board';
},
}
dueCardsList() {
const allUsers = Utils.dueCardsView() === 'all';
const user = Meteor.user();
const archivedBoards = [];
Boards.find({ archived: true }).forEach(board => {
archivedBoards.push(board._id);
});
const permiitedBoards = [];
let selector = {
archived: false,
};
// for every user including admin allow her to see cards only from public boards
// or those where she is a member
//if (!user.isAdmin) {
selector.$or = [
{ permission: 'public' },
{ members: { $elemMatch: { userId: user._id, isActive: true } } },
];
//}
Boards.find(selector).forEach(board => {
permiitedBoards.push(board._id);
});
const archivedSwimlanes = [];
Swimlanes.find({ archived: true }).forEach(swimlane => {
archivedSwimlanes.push(swimlane._id);
});
const archivedLists = [];
Lists.find({ archived: true }).forEach(list => {
archivedLists.push(list._id);
});
selector = {
archived: false,
boardId: {
$nin: archivedBoards,
$in: permiitedBoards,
},
swimlaneId: { $nin: archivedSwimlanes },
listId: { $nin: archivedLists },
dueAt: { $ne: null },
endAt: null,
};
if (!allUsers) {
selector.$or = [{ members: user._id }, { assignees: user._id }];
const results = this.getResults();
console.log('results:', results);
const cards = [];
if (results) {
results.forEach(card => {
cards.push(card);
});
}
const cards = [];
// eslint-disable-next-line no-console
// console.log('cards selector:', selector);
Cards.find(selector).forEach(card => {
cards.push(card);
// eslint-disable-next-line no-console
// console.log(
// 'board:',
// card.board(),
// 'swimlane:',
// card.swimlane(),
// 'list:',
// card.list(),
// );
});
cards.sort((a, b) => {
const x = a.dueAt === null ? Date('2100-12-31') : a.dueAt;
const y = b.dueAt === null ? Date('2100-12-31') : b.dueAt;
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;
@@ -148,7 +103,9 @@ BlazeComponent.extendComponent({
});
// eslint-disable-next-line no-console
// console.log('cards:', cards);
console.log('cards:', cards);
return cards;
},
}).register('dueCards');
}
}
DueCardsComponent.register('dueCards');
+19 -9
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.
@@ -231,20 +241,20 @@ Template.editor.onRendered(() => {
//the function is called before the text is really pasted.
updatePastedText(thisNote);
}, 10);
},
}
},
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']],
+29 -39
View File
@@ -10,11 +10,30 @@ template(name="globalSearchModalTitle")
i.fa.fa-keyboard-o
| {{_ 'globalSearch-title'}}
template(name="resultsPaged")
if resultsHeading.get
h1
= resultsHeading.get
a.fa.fa-link(title="{{_ 'link-to-search' }}" href="{{ getSearchHref }}")
each card in results.get
+resultCard(card)
table.global-search-footer
tr
td.global-search-previous-page
if hasPreviousPage.get
button.js-previous-page
| {{_ 'previous-page' }}
td.global-search-next-page(align="right")
if hasNextPage.get
button.js-next-page
| {{_ 'next-page' }}
template(name="globalSearch")
if currentUser
.wrapper
form.global-search-page.js-search-query-form
input.global-search-query-input(
style="{# if hasResults.get #}display: inline-block;{#/if#}"
id="global-search-input"
type="text"
name="searchQuery"
@@ -22,31 +41,23 @@ template(name="globalSearch")
value="{{ query.get }}"
autofocus dir="auto"
)
a.js-new-search.fa.fa-eraser
if searching.get
+spinner
else if hasResults.get
.global-search-results-list-wrapper
if hasQueryErrors.get
div
ul
each msg in errorMessages
span.global-search-error-messages
li.global-search-error-messages
= msg
else
h1
= resultsHeading.get
a.fa.fa-link(title="{{_ 'link-to-search' }}" href="{{ getSearchHref }}")
each card in results.get
+resultCard(card)
table.global-search-footer
tr
td.global-search-previous-page
if hasPreviousPage.get
button.js-previous-page
| {{_ 'previous-page' }}
td.global-search-next-page(align="right")
if hasNextPage.get
button.js-next-page
| {{_ 'next-page' }}
+resultsPaged(this)
else if serverError.get
.global-search-page
.global-search-help
h1 {{_ 'server-error' }}
+viewer
| {{_ 'server-error-troubleshooting' }}
else
.global-search-page
.global-search-help
@@ -73,24 +84,3 @@ template(name="globalSearch")
.global-search-instructions
+viewer
= searchInstructions
template(name="globalSearchViewChangePopup")
if currentUser
ul.pop-over-list
li
with "globalSearchViewChange-choice-me"
a.js-global-search-view-me
i.fa.fa-user.colorful
| {{_ 'globalSearchViewChange-choice-me'}}
if $eq Utils.globalSearchView "me"
i.fa.fa-check
li
with "globalSearchViewChange-choice-all"
a.js-global-search-view-all
i.fa.fa-users.colorful
| {{_ 'globalSearchViewChange-choice-all'}}
span.sub-name
+viewer
| {{_ 'globalSearchViewChange-choice-all-description' }}
if $eq Utils.globalSearchView "all"
i.fa.fa-check
+84 -562
View File
@@ -1,4 +1,8 @@
const subManager = new SubsManager();
import { CardSearchPagedComponent } from '../../lib/cardSearch';
import Boards from '../../../models/boards';
import { Query, QueryErrors } from '../../../config/query-classes';
// const subManager = new SubsManager();
BlazeComponent.extendComponent({
events() {
@@ -16,45 +20,14 @@ Template.globalSearch.helpers({
},
});
BlazeComponent.extendComponent({
events() {
return [
{
'click .js-due-cards-view-me'() {
Utils.setDueCardsView('me');
Popup.close();
},
'click .js-due-cards-view-all'() {
Utils.setDueCardsView('all');
Popup.close();
},
},
];
},
}).register('globalSearchViewChangePopup');
BlazeComponent.extendComponent({
class GlobalSearchComponent extends CardSearchPagedComponent {
onCreated() {
this.searching = new ReactiveVar(false);
this.hasResults = new ReactiveVar(false);
this.hasQueryErrors = new ReactiveVar(false);
this.query = new ReactiveVar('');
this.resultsHeading = new ReactiveVar('');
this.searchLink = new ReactiveVar(null);
super.onCreated();
this.myLists = new ReactiveVar([]);
this.myLabelNames = new ReactiveVar([]);
this.myBoardNames = new ReactiveVar([]);
this.results = new ReactiveVar([]);
this.hasNextPage = new ReactiveVar(false);
this.hasPreviousPage = new ReactiveVar(false);
this.parsingErrors = new QueryErrors();
this.queryParams = null;
this.parsingErrors = [];
this.resultsCount = 0;
this.totalHits = 0;
this.queryErrors = null;
this.colorMap = null;
this.resultsPerPage = 25;
Meteor.call('myLists', (err, data) => {
if (!err) {
@@ -73,510 +46,71 @@ BlazeComponent.extendComponent({
this.myBoardNames.set(data);
}
});
},
}
onRendered() {
Meteor.subscribe('setting');
// eslint-disable-next-line no-console
//console.log('lang:', TAPi18n.getLanguage());
this.colorMap = Boards.colorMap();
// eslint-disable-next-line no-console
// console.log('colorMap:', this.colorMap);
if (Session.get('globalQuery')) {
this.searchAllBoards(Session.get('globalQuery'));
}
},
}
resetSearch() {
this.searching.set(false);
this.results.set([]);
this.hasResults.set(false);
this.hasQueryErrors.set(false);
this.resultsHeading.set('');
this.parsingErrors = [];
this.resultsCount = 0;
this.totalHits = 0;
this.queryErrors = null;
},
getSessionData() {
return SessionData.findOne({
userId: Meteor.userId(),
sessionId: SessionData.getSessionId(),
});
},
getResults() {
// eslint-disable-next-line no-console
// console.log('getting results');
if (this.queryParams) {
const sessionData = this.getSessionData();
// eslint-disable-next-line no-console
// console.log('selector:', sessionData.getSelector());
// console.log('session data:', sessionData);
const projection = sessionData.getProjection();
projection.skip = 0;
const cards = Cards.find({ _id: { $in: sessionData.cards } }, projection);
this.queryErrors = sessionData.errors;
if (this.queryErrors.length) {
this.hasQueryErrors.set(true);
return null;
}
if (cards) {
this.totalHits = sessionData.totalHits;
this.resultsCount = cards.count();
this.resultsStart = sessionData.lastHit - this.resultsCount + 1;
this.resultsEnd = sessionData.lastHit;
this.resultsHeading.set(this.getResultsHeading());
this.results.set(cards);
this.hasNextPage.set(sessionData.lastHit < sessionData.totalHits);
this.hasPreviousPage.set(
sessionData.lastHit - sessionData.resultsCount > 0,
);
}
}
this.resultsCount = 0;
return null;
},
super.resetSearch();
this.parsingErrors = new QueryErrors();
}
errorMessages() {
if (this.parsingErrors.length) {
return this.parsingErrorMessages();
if (this.parsingErrors.hasErrors()) {
return this.parsingErrors.errorMessages();
}
return this.queryErrorMessages();
},
}
parsingErrorMessages() {
const messages = [];
this.parsingErrors.errorMessages();
}
if (this.parsingErrors.length) {
this.parsingErrors.forEach(err => {
messages.push(TAPi18n.__(err.tag, err.value));
});
}
return messages;
},
queryErrorMessages() {
messages = [];
this.queryErrors.forEach(err => {
let value = err.color ? TAPi18n.__(`color-${err.value}`) : err.value;
if (!value) {
value = err.value;
}
messages.push(TAPi18n.__(err.tag, value));
});
return messages;
},
searchAllBoards(query) {
query = query.trim();
searchAllBoards(queryText) {
queryText = queryText.trim();
// eslint-disable-next-line no-console
//console.log('query:', query);
//console.log('queryText:', queryText);
this.query.set(query);
this.query.set(queryText);
this.resetSearch();
if (!query) {
if (!queryText) {
return;
}
this.searching.set(true);
const reOperator1 = new RegExp(
'^((?<operator>[\\p{Letter}\\p{Mark}]+):|(?<abbrev>[#@]))(?<value>[\\p{Letter}\\p{Mark}]+)(\\s+|$)',
'iu',
);
const reOperator2 = new RegExp(
'^((?<operator>[\\p{Letter}\\p{Mark}]+):|(?<abbrev>[#@]))(?<quote>["\']*)(?<value>.*?)\\k<quote>(\\s+|$)',
'iu',
);
const reText = new RegExp('^(?<text>\\S+)(\\s+|$)', 'u');
const reQuotedText = new RegExp(
'^(?<quote>["\'])(?<text>.*?)\\k<quote>(\\s+|$)',
'u',
);
const reNegatedOperator = new RegExp('^-(?<operator>.*)$');
const operators = {
'operator-board': 'boards',
'operator-board-abbrev': 'boards',
'operator-swimlane': 'swimlanes',
'operator-swimlane-abbrev': 'swimlanes',
'operator-list': 'lists',
'operator-list-abbrev': 'lists',
'operator-label': 'labels',
'operator-label-abbrev': 'labels',
'operator-user': 'users',
'operator-user-abbrev': 'users',
'operator-member': 'members',
'operator-member-abbrev': 'members',
'operator-assignee': 'assignees',
'operator-assignee-abbrev': 'assignees',
'operator-status': 'status',
'operator-due': 'dueAt',
'operator-created': 'createdAt',
'operator-modified': 'modifiedAt',
'operator-comment': 'comments',
'operator-has': 'has',
'operator-sort': 'sort',
'operator-limit': 'limit',
};
const predicates = {
due: {
'predicate-overdue': 'overdue',
},
durations: {
'predicate-week': 'week',
'predicate-month': 'month',
'predicate-quarter': 'quarter',
'predicate-year': 'year',
},
status: {
'predicate-archived': 'archived',
'predicate-all': 'all',
'predicate-open': 'open',
'predicate-ended': 'ended',
'predicate-public': 'public',
'predicate-private': 'private',
},
sorts: {
'predicate-due': 'dueAt',
'predicate-created': 'createdAt',
'predicate-modified': 'modifiedAt',
},
has: {
'predicate-description': 'description',
'predicate-checklist': 'checklist',
'predicate-attachment': 'attachment',
'predicate-start': 'startAt',
'predicate-end': 'endAt',
'predicate-due': 'dueAt',
'predicate-assignee': 'assignees',
'predicate-member': 'members',
},
};
const predicateTranslations = {};
Object.entries(predicates).forEach(([category, catPreds]) => {
predicateTranslations[category] = {};
Object.entries(catPreds).forEach(([tag, value]) => {
predicateTranslations[category][TAPi18n.__(tag)] = value;
});
});
// eslint-disable-next-line no-console
// console.log('predicateTranslations:', predicateTranslations);
const operatorMap = {};
Object.entries(operators).forEach(([key, value]) => {
operatorMap[TAPi18n.__(key).toLowerCase()] = value;
});
// eslint-disable-next-line no-console
// console.log('operatorMap:', operatorMap);
const params = {
limit: this.resultsPerPage,
boards: [],
swimlanes: [],
lists: [],
users: [],
members: [],
assignees: [],
labels: [],
status: [],
dueAt: null,
createdAt: null,
modifiedAt: null,
comments: [],
has: [],
};
let text = '';
while (query) {
m = query.match(reOperator1);
if (!m) {
m = query.match(reOperator2);
if (m) {
query = query.replace(reOperator2, '');
}
} else {
query = query.replace(reOperator1, '');
}
if (m) {
let op;
if (m.groups.operator) {
op = m.groups.operator.toLowerCase();
} else {
op = m.groups.abbrev.toLowerCase();
}
// eslint-disable-next-line no-prototype-builtins
if (operatorMap.hasOwnProperty(op)) {
const operator = operatorMap[op];
let value = m.groups.value;
if (operator === 'labels') {
if (value in this.colorMap) {
value = this.colorMap[value];
// console.log('found color:', value);
}
} else if (['dueAt', 'createdAt', 'modifiedAt'].includes(operator)) {
let days = parseInt(value, 10);
let duration = null;
if (isNaN(days)) {
// duration was specified as text
if (predicateTranslations.durations[value]) {
duration = predicateTranslations.durations[value];
let date = null;
switch (duration) {
case 'week':
let week = moment().week();
if (week === 52) {
date = moment(1, 'W');
date.set('year', date.year() + 1);
} else {
date = moment(week + 1, 'W');
}
break;
case 'month':
let month = moment().month();
// .month() is zero indexed
if (month === 11) {
date = moment(1, 'M');
date.set('year', date.year() + 1);
} else {
date = moment(month + 2, 'M');
}
break;
case 'quarter':
let quarter = moment().quarter();
if (quarter === 4) {
date = moment(1, 'Q');
date.set('year', date.year() + 1);
} else {
date = moment(quarter + 1, 'Q');
}
break;
case 'year':
date = moment(moment().year() + 1, 'YYYY');
break;
}
if (date) {
value = {
operator: '$lt',
value: date.format('YYYY-MM-DD'),
};
}
} else if (operator === 'dueAt' && value === 'overdue') {
value = {
operator: '$lt',
value: moment().format('YYYY-MM-DD'),
};
} else {
this.parsingErrors.push({
tag: 'operator-number-expected',
value: { operator: op, value },
});
value = null;
}
} else {
if (operator === 'dueAt') {
value = {
operator: '$lt',
value: moment(moment().format('YYYY-MM-DD'))
.add(days + 1, duration ? duration : 'days')
.format(),
};
} else {
value = {
operator: '$gte',
value: moment(moment().format('YYYY-MM-DD'))
.subtract(days, duration ? duration : 'days')
.format(),
};
}
}
} else if (operator === 'sort') {
let negated = false;
const m = value.match(reNegatedOperator);
if (m) {
value = m.groups.operator;
negated = true;
}
if (!predicateTranslations.sorts[value]) {
this.parsingErrors.push({
tag: 'operator-sort-invalid',
value,
});
} else {
value = {
name: predicateTranslations.sorts[value],
order: negated ? 'des' : 'asc',
};
}
} else if (operator === 'status') {
if (!predicateTranslations.status[value]) {
this.parsingErrors.push({
tag: 'operator-status-invalid',
value,
});
} else {
value = predicateTranslations.status[value];
}
} else if (operator === 'has') {
let negated = false;
const m = value.match(reNegatedOperator);
if (m) {
value = m.groups.operator;
negated = true;
}
if (!predicateTranslations.has[value]) {
this.parsingErrors.push({
tag: 'operator-has-invalid',
value,
});
} else {
value = {
field: predicateTranslations.has[value],
exists: !negated,
};
}
} else if (operator === 'limit') {
const limit = parseInt(value, 10);
if (isNaN(limit) || limit < 1) {
this.parsingErrors.push({
tag: 'operator-limit-invalid',
value,
});
} else {
value = limit;
}
}
if (Array.isArray(params[operator])) {
params[operator].push(value);
} else {
params[operator] = value;
}
} else {
this.parsingErrors.push({
tag: 'operator-unknown-error',
value: op,
});
}
continue;
}
m = query.match(reQuotedText);
if (!m) {
m = query.match(reText);
if (m) {
query = query.replace(reText, '');
}
} else {
query = query.replace(reQuotedText, '');
}
if (m) {
text += (text ? ' ' : '') + m.groups.text;
}
}
const query = new Query();
query.buildParams(queryText);
// eslint-disable-next-line no-console
// console.log('text:', text);
params.text = text;
// console.log('params:', query.getParams());
// eslint-disable-next-line no-console
console.log('params:', params);
this.queryParams = query.getQueryParams().getParams();
this.queryParams = params;
if (this.parsingErrors.length) {
if (query.hasErrors()) {
this.searching.set(false);
this.queryErrors = this.parsingErrorMessages();
this.queryErrors = query.errors();
this.hasResults.set(true);
this.hasQueryErrors.set(true);
return;
}
this.autorun(() => {
const handle = Meteor.subscribe(
'globalSearch',
SessionData.getSessionId(),
params,
);
Tracker.nonreactive(() => {
Tracker.autorun(() => {
if (handle.ready()) {
this.getResults();
this.searching.set(false);
this.hasResults.set(true);
}
});
});
});
},
nextPage() {
const sessionData = this.getSessionData();
this.autorun(() => {
const handle = Meteor.subscribe('nextPage', sessionData.sessionId);
Tracker.nonreactive(() => {
Tracker.autorun(() => {
if (handle.ready()) {
this.getResults();
this.searching.set(false);
this.hasResults.set(true);
}
});
});
});
},
previousPage() {
const sessionData = this.getSessionData();
this.autorun(() => {
const handle = Meteor.subscribe('previousPage', sessionData.sessionId);
Tracker.nonreactive(() => {
Tracker.autorun(() => {
if (handle.ready()) {
this.getResults();
this.searching.set(false);
this.hasResults.set(true);
}
});
});
});
},
getResultsHeading() {
if (this.resultsCount === 0) {
return TAPi18n.__('no-cards-found');
} else if (this.resultsCount === 1) {
return TAPi18n.__('one-card-found');
} else if (this.resultsCount === this.totalHits) {
return TAPi18n.__('n-cards-found', this.resultsCount);
}
return TAPi18n.__('n-n-of-n-cards-found', {
start: this.resultsStart,
end: this.resultsEnd,
total: this.totalHits,
});
},
getSearchHref() {
const baseUrl = window.location.href.replace(/([?#].*$|\s*$)/, '');
return `${baseUrl}?q=${encodeURIComponent(this.query.get())}`;
},
this.runGlobalSearch(query.getQueryParams());
}
searchInstructions() {
tags = {
const tags = {
operator_board: TAPi18n.__('operator-board'),
operator_list: TAPi18n.__('operator-list'),
operator_swimlane: TAPi18n.__('operator-swimlane'),
@@ -589,6 +123,7 @@ BlazeComponent.extendComponent({
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'),
@@ -618,61 +153,47 @@ BlazeComponent.extendComponent({
predicate_member: TAPi18n.__('predicate-member'),
};
let text = `# ${TAPi18n.__('globalSearch-instructions-heading')}`;
text += `\n${TAPi18n.__('globalSearch-instructions-description', tags)}`;
text += `\n\n${TAPi18n.__('globalSearch-instructions-operators', tags)}`;
let text = '';
[
'globalSearch-instructions-operator-board',
'globalSearch-instructions-operator-list',
'globalSearch-instructions-operator-swimlane',
'globalSearch-instructions-operator-comment',
'globalSearch-instructions-operator-label',
'globalSearch-instructions-operator-hash',
'globalSearch-instructions-operator-user',
'globalSearch-instructions-operator-at',
'globalSearch-instructions-operator-member',
'globalSearch-instructions-operator-assignee',
'globalSearch-instructions-operator-due',
'globalSearch-instructions-operator-created',
'globalSearch-instructions-operator-modified',
'globalSearch-instructions-operator-status',
].forEach(instruction => {
text += `\n* ${TAPi18n.__(instruction, tags)}`;
});
[
'globalSearch-instructions-status-archived',
'globalSearch-instructions-status-public',
'globalSearch-instructions-status-private',
'globalSearch-instructions-status-all',
'globalSearch-instructions-status-ended',
].forEach(instruction => {
text += `\n * ${TAPi18n.__(instruction, tags)}`;
});
[
'globalSearch-instructions-operator-has',
'globalSearch-instructions-operator-sort',
'globalSearch-instructions-operator-limit',
].forEach(instruction => {
text += `\n* ${TAPi18n.__(instruction, tags)}`;
});
text += `\n## ${TAPi18n.__('heading-notes')}`;
[
'globalSearch-instructions-notes-1',
'globalSearch-instructions-notes-2',
'globalSearch-instructions-notes-3',
'globalSearch-instructions-notes-3-2',
'globalSearch-instructions-notes-4',
'globalSearch-instructions-notes-5',
].forEach(instruction => {
text += `\n* ${TAPi18n.__(instruction, tags)}`;
['# ', 'globalSearch-instructions-heading'],
['\n', 'globalSearch-instructions-description'],
['\n\n', 'globalSearch-instructions-operators'],
['\n* ', 'globalSearch-instructions-operator-board'],
['\n* ', 'globalSearch-instructions-operator-list'],
['\n* ', 'globalSearch-instructions-operator-swimlane'],
['\n* ', 'globalSearch-instructions-operator-comment'],
['\n* ', 'globalSearch-instructions-operator-label'],
['\n* ', 'globalSearch-instructions-operator-hash'],
['\n* ', 'globalSearch-instructions-operator-user'],
['\n* ', 'globalSearch-instructions-operator-at'],
['\n* ', 'globalSearch-instructions-operator-member'],
['\n* ', 'globalSearch-instructions-operator-assignee'],
['\n* ', 'globalSearch-instructions-operator-creator'],
['\n* ', 'globalSearch-instructions-operator-due'],
['\n* ', 'globalSearch-instructions-operator-created'],
['\n* ', 'globalSearch-instructions-operator-modified'],
['\n* ', 'globalSearch-instructions-operator-status'],
['\n * ', 'globalSearch-instructions-status-archived'],
['\n * ', 'globalSearch-instructions-status-public'],
['\n * ', 'globalSearch-instructions-status-private'],
['\n * ', 'globalSearch-instructions-status-all'],
['\n * ', 'globalSearch-instructions-status-ended'],
['\n* ', 'globalSearch-instructions-operator-has'],
['\n* ', 'globalSearch-instructions-operator-sort'],
['\n* ', 'globalSearch-instructions-operator-limit'],
['\n## ', 'heading-notes'],
['\n* ', 'globalSearch-instructions-notes-1'],
['\n* ', 'globalSearch-instructions-notes-2'],
['\n* ', 'globalSearch-instructions-notes-3'],
['\n* ', 'globalSearch-instructions-notes-3-2'],
['\n* ', 'globalSearch-instructions-notes-4'],
['\n* ', 'globalSearch-instructions-notes-5'],
].forEach(([prefix, instruction]) => {
text += `${prefix}${TAPi18n.__(instruction, tags)}`;
});
return text;
},
}
labelColors() {
return Boards.simpleSchema()._schema['labels.$.color'].allowedValues.map(
@@ -680,23 +201,15 @@ BlazeComponent.extendComponent({
return { color, name: TAPi18n.__(`color-${color}`) };
},
);
},
}
events() {
return [
return super.events().concat([
{
'submit .js-search-query-form'(evt) {
evt.preventDefault();
this.searchAllBoards(evt.target.searchQuery.value);
},
'click .js-next-page'(evt) {
evt.preventDefault();
this.nextPage();
},
'click .js-previous-page'(evt) {
evt.preventDefault();
this.previousPage();
},
'click .js-label-color'(evt) {
evt.preventDefault();
const input = document.getElementById('global-search-input');
@@ -737,7 +250,16 @@ BlazeComponent.extendComponent({
);
document.getElementById('global-search-input').focus();
},
'click .js-new-search'(evt) {
evt.preventDefault();
const input = document.getElementById('global-search-input');
input.value = '';
this.query.set('');
this.hasResults.set(false);
},
},
];
},
}).register('globalSearch');
]);
}
}
GlobalSearchComponent.register('globalSearch');
+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 = 'فارسی/پارسی (ایران‎)';
+22 -56
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
@@ -24,50 +14,26 @@ template(name="myCardsModalTitle")
template(name="myCards")
if currentUser
if isPageReady.get
.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)
else
if searching.get
+spinner
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
else
.wrapper
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)
+86 -198
View File
@@ -1,4 +1,4 @@
const subManager = new SubsManager();
import { CardSearchPagedComponent } from '../../lib/cardSearch';
BlazeComponent.extendComponent({
myCardsSort() {
@@ -24,227 +24,115 @@ 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');
BlazeComponent.extendComponent({
class MyCardsComponent extends CardSearchPagedComponent {
onCreated() {
this.isPageReady = new ReactiveVar(false);
super.onCreated();
this.autorun(() => {
const handle = subManager.subscribe('myCards');
Tracker.nonreactive(() => {
Tracker.autorun(() => {
this.isPageReady.set(handle.ready());
});
});
});
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() {
const userId = Meteor.userId();
const boards = [];
let board = null;
let swimlane = null;
let list = null;
const cursor = Cards.find(
{
$or: [{ members: userId }, { assignees: userId }],
archived: false,
},
{
sort: {
boardId: 1,
swimlaneId: 1,
listId: 1,
sort: 1,
},
},
);
const cursor = this.getResults();
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) {
if (cursor) {
cursor.forEach(card => {
// 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) {
// console.log('card:', card.title);
if (board === null || card.boardId !== board._id) {
// eslint-disable-next-line no-console
// console.log('new board');
board = card.getBoard();
if (board.archived) {
board = null;
return;
}
// eslint-disable-next-line no-console
// console.log('board:', b, b._id, b.title);
boards.push(board);
board.mySwimlanes = [];
swimlane = null;
return;
list = null;
}
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 = [];
}
swimlane.myLists = [list];
newSwimlane = true;
}
if (board === null || card.boardId !== board._id) {
// eslint-disable-next-line no-console
// console.log('new board');
board = card.getBoard();
if (board.archived) {
board = null;
return;
}
// 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);
}
});
newBoard = false;
newSwimlane = false;
newList = false;
});
// sort the data structure
boards.forEach(board => {
board.mySwimlanes.forEach(swimlane => {
swimlane.myLists.forEach(list => {
list.myCards.sort((a, b) => {
// sort the data structure
boards.forEach(board => {
board.mySwimlanes.forEach(swimlane => {
swimlane.myLists.forEach(list => {
list.myCards.sort((a, b) => {
return a.sort - b.sort;
});
});
swimlane.myLists.sort((a, b) => {
return a.sort - b.sort;
});
});
swimlane.myLists.sort((a, b) => {
board.mySwimlanes.sort((a, b) => {
return a.sort - b.sort;
});
});
board.mySwimlanes.sort((a, b) => {
return a.sort - b.sort;
boards.sort((a, b) => {
let x = a.sort;
let y = b.sort;
// show the template board last
if (a.type === 'template-container') {
x = 99999999;
} else if (b.type === 'template-container') {
y = 99999999;
}
return x - y;
});
});
boards.sort((a, b) => {
let x = a.sort;
let y = b.sort;
// eslint-disable-next-line no-console
// console.log('boards:', boards);
return boards;
}
// show the template board last
if (a.type === 'template-container') {
x = 99999999;
} else if (b.type === 'template-container') {
y = 99999999;
}
return x - y;
});
// eslint-disable-next-line no-console
// console.log('boards:', boards);
return boards;
},
myDueCardsList() {
const userId = Meteor.userId();
const cursor = Cards.find(
{
$or: [{ members: userId }, { assignees: userId }],
archived: false,
},
{
sort: {
dueAt: -1,
boardId: 1,
swimlaneId: 1,
listId: 1,
sort: 1,
},
},
);
// eslint-disable-next-line no-console
// console.log('cursor:', cursor);
const cards = [];
cursor.forEach(card => {
if (
!card.getBoard().archived &&
!card.getSwimlane().archived &&
!card.getList().archived
) {
cards.push(card);
}
});
cards.sort((a, b) => {
const x = a.dueAt === null ? Date('2100-12-31') : a.dueAt;
const y = b.dueAt === null ? 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;
},
events() {
return [
{
// 'click .js-my-card'(evt) {
// const card = this.currentData().card;
// // eslint-disable-next-line no-console
// console.log('currentData():', this.currentData());
// // eslint-disable-next-line no-console
// console.log('card:', card);
// if (card) {
// Utils.goCardId(card._id);
// }
// evt.preventDefault();
// },
},
];
},
}).register('myCards');
return [];
}
}
MyCardsComponent.register('myCards');
@@ -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');
@@ -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'}}
+8
View File
@@ -105,6 +105,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}}")
+21
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;
},
@@ -889,6 +897,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;
@@ -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);
@@ -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'}}
+13 -16
View File
@@ -47,25 +47,22 @@ Template.swimlaneFixedHeader.helpers({
},
});
BlazeComponent.extendComponent({
Template.swimlaneActionPopup.events({
'click .js-set-swimlane-color': Popup.open('setSwimlaneColor'),
'click .js-close-swimlane'(event) {
event.preventDefault();
this.archive();
Popup.close();
},
'click .js-move-swimlane': Popup.open('moveSwimlane'),
'click .js-copy-swimlane': Popup.open('copySwimlane'),
});
Template.swimlaneActionPopup.events({
isCommentOnly() {
return Meteor.user().isCommentOnly();
},
events() {
return [
{
'click .js-set-swimlane-color': Popup.open('setSwimlaneColor'),
'click .js-close-swimlane'(event) {
event.preventDefault();
this.archive();
Popup.close();
},
'click .js-move-swimlane': Popup.open('moveSwimlane'),
},
];
},
}).register('swimlaneActionPopup');
});
BlazeComponent.extendComponent({
onCreated() {
@@ -71,3 +71,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);
-4
View File
@@ -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') {
+189
View File
@@ -0,0 +1,189 @@
import Cards from '../../models/cards';
import SessionData from '../../models/usersessiondata';
export class CardSearchPagedComponent extends BlazeComponent {
onCreated() {
this.searching = new ReactiveVar(false);
this.hasResults = new ReactiveVar(false);
this.hasQueryErrors = new ReactiveVar(false);
this.query = new ReactiveVar('');
this.resultsHeading = new ReactiveVar('');
this.searchLink = new ReactiveVar(null);
this.results = new ReactiveVar([]);
this.hasNextPage = new ReactiveVar(false);
this.hasPreviousPage = new ReactiveVar(false);
this.resultsCount = 0;
this.totalHits = 0;
this.queryErrors = null;
this.resultsPerPage = 25;
this.sessionId = SessionData.getSessionId();
this.subscriptionHandle = null;
this.serverError = new ReactiveVar(false);
const that = this;
this.subscriptionCallbacks = {
onReady() {
that.getResults();
that.searching.set(false);
that.hasResults.set(true);
that.serverError.set(false);
},
onError(error) {
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);
},
};
}
resetSearch() {
this.searching.set(false);
this.results.set([]);
this.hasResults.set(false);
this.hasQueryErrors.set(false);
this.resultsHeading.set('');
this.serverError.set(false);
this.resultsCount = 0;
this.totalHits = 0;
this.queryErrors = null;
}
getSessionData(sessionId) {
return SessionData.findOne({
sessionId: sessionId ? sessionId : SessionData.getSessionId(),
});
}
getResults() {
// eslint-disable-next-line no-console
// console.log('getting results');
const sessionData = this.getSessionData();
// eslint-disable-next-line no-console
console.log('session data:', sessionData);
const cards = [];
sessionData.cards.forEach(cardId => {
cards.push(Cards.findOne({ _id: cardId }));
});
this.queryErrors = sessionData.errors;
if (this.queryErrors.length) {
// console.log('queryErrors:', this.queryErrorMessages());
this.hasQueryErrors.set(true);
// return null;
}
if (cards) {
this.totalHits = sessionData.totalHits;
this.resultsCount = cards.length;
this.resultsStart = sessionData.lastHit - this.resultsCount + 1;
this.resultsEnd = sessionData.lastHit;
this.resultsHeading.set(this.getResultsHeading());
this.results.set(cards);
this.hasNextPage.set(sessionData.lastHit < sessionData.totalHits);
this.hasPreviousPage.set(
sessionData.lastHit - sessionData.resultsCount > 0,
);
return cards;
}
this.resultsCount = 0;
return null;
}
stopSubscription() {
if (this.subscriptionHandle) {
this.subscriptionHandle.stop();
}
}
getSubscription(queryParams) {
return Meteor.subscribe(
'globalSearch',
this.sessionId,
queryParams.params,
queryParams.text,
this.subscriptionCallbacks,
);
}
runGlobalSearch(queryParams) {
this.searching.set(true);
this.stopSubscription();
this.subscriptionHandle = this.getSubscription(queryParams);
}
queryErrorMessages() {
const messages = [];
this.queryErrors.forEach(err => {
let value = err.color ? TAPi18n.__(`color-${err.value}`) : err.value;
if (!value) {
value = err.value;
}
messages.push(TAPi18n.__(err.tag, value));
});
return messages;
}
nextPage() {
this.searching.set(true);
this.stopSubscription();
this.subscriptionHandle = Meteor.subscribe(
'nextPage',
this.sessionId,
this.subscriptionCallbacks,
);
}
previousPage() {
this.searching.set(true);
this.stopSubscription();
this.subscriptionHandle = Meteor.subscribe(
'previousPage',
this.sessionId,
this.subscriptionCallbacks,
);
}
getResultsHeading() {
if (this.resultsCount === 0) {
return TAPi18n.__('no-cards-found');
} else if (this.resultsCount === 1) {
return TAPi18n.__('one-card-found');
} else if (this.resultsCount === this.totalHits) {
return TAPi18n.__('n-cards-found', this.resultsCount);
}
return TAPi18n.__('n-n-of-n-cards-found', {
start: this.resultsStart,
end: this.resultsEnd,
total: this.totalHits,
});
}
getSearchHref() {
const baseUrl = window.location.href.replace(/([?#].*$|\s*$)/, '');
return `${baseUrl}?q=${encodeURIComponent(this.query.get())}`;
}
events() {
return [
{
'click .js-next-page'(evt) {
evt.preventDefault();
this.nextPage();
},
'click .js-previous-page'(evt) {
evt.preventDefault();
this.previousPage();
},
},
];
}
}
+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({
},
},
];
},
});
}
}
+121
View File
@@ -7,6 +7,125 @@ 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 +581,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 +592,7 @@ Filter = {
'assignees',
'archive',
'hideEmpty',
'dueAt',
'customFields',
],
+1 -1
View File
@@ -50,5 +50,5 @@ EscapeActions.register(
'modalWindow',
() => Modal.close(),
() => Modal.isOpen(),
{ noClickEscapeOn: '.modal-container' },
{ noClickEscapeOn: '.modal-container,.model-content' },
);
+51
View File
@@ -0,0 +1,51 @@
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';
+519
View File
@@ -0,0 +1,519 @@
import {
OPERATOR_ASSIGNEE,
OPERATOR_BOARD,
OPERATOR_COMMENT,
OPERATOR_CREATED_AT,
OPERATOR_CREATOR,
OPERATOR_DUE,
OPERATOR_HAS,
OPERATOR_LABEL,
OPERATOR_LIMIT,
OPERATOR_LIST,
OPERATOR_MEMBER,
OPERATOR_MODIFIED_AT,
OPERATOR_SORT,
OPERATOR_STATUS,
OPERATOR_SWIMLANE,
OPERATOR_UNKNOWN,
OPERATOR_USER,
ORDER_ASCENDING,
ORDER_DESCENDING,
PREDICATE_ALL,
PREDICATE_ARCHIVED,
PREDICATE_ASSIGNEES,
PREDICATE_ATTACHMENT,
PREDICATE_CHECKLIST,
PREDICATE_CREATED_AT,
PREDICATE_DESCRIPTION,
PREDICATE_DUE_AT,
PREDICATE_END_AT,
PREDICATE_ENDED,
PREDICATE_MEMBERS,
PREDICATE_MODIFIED_AT,
PREDICATE_MONTH,
PREDICATE_OPEN,
PREDICATE_OVERDUE,
PREDICATE_PRIVATE,
PREDICATE_PUBLIC,
PREDICATE_QUARTER,
PREDICATE_START_AT,
PREDICATE_WEEK,
PREDICATE_YEAR,
} from './search-const';
import Boards from '../models/boards';
import moment from 'moment';
export class QueryParams {
text = '';
constructor(params = {}, text = '') {
this.params = params;
this.text = text;
}
hasOperator(operator) {
return (
this.params[operator] !== undefined &&
(this.params[operator].length === undefined ||
this.params[operator].length > 0)
);
}
addPredicate(operator, predicate) {
if (!this.hasOperator(operator)) {
this.params[operator] = [];
}
this.params[operator].push(predicate);
}
setPredicate(operator, predicate) {
this.params[operator] = predicate;
}
getPredicate(operator) {
if (typeof this.params[operator] === 'object') {
return this.params[operator][0];
} else {
return this.params[operator];
}
}
getPredicates(operator) {
return this.params[operator];
}
getParams() {
return this.params;
}
}
export class QueryErrors {
operatorTagMap = [
[OPERATOR_BOARD, 'board-title-not-found'],
[OPERATOR_SWIMLANE, 'swimlane-title-not-found'],
[
OPERATOR_LABEL,
label => {
if (Boards.labelColors().includes(label)) {
return {
tag: 'label-color-not-found',
value: label,
color: true,
};
} else {
return {
tag: 'label-not-found',
value: label,
color: false,
};
}
},
],
[OPERATOR_LIST, 'list-title-not-found'],
[OPERATOR_COMMENT, 'comment-not-found'],
[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() {
this._errors = {};
this.operatorTags = {};
this.operatorTagMap.forEach(([operator, tag]) => {
this.operatorTags[operator] = tag;
});
this.colorMap = Boards.colorMap();
}
addError(operator, error) {
if (!this._errors[operator]) {
this._errors[operator] = [];
}
this._errors[operator].push(error);
}
addNotFound(operator, value) {
if (typeof this.operatorTags[operator] === 'function') {
this.addError(operator, this.operatorTags[operator](value));
} else {
this.addError(operator, { tag: this.operatorTags[operator], value });
}
}
hasErrors() {
return Object.entries(this._errors).length > 0;
}
errors() {
const errs = [];
// eslint-disable-next-line no-unused-vars
Object.entries(this._errors).forEach(([, errors]) => {
errors.forEach(err => {
errs.push(err);
});
});
return errs;
}
errorMessages() {
const messages = [];
// eslint-disable-next-line no-unused-vars
Object.entries(this._errors).forEach(([, errors]) => {
errors.forEach(err => {
messages.push(TAPi18n.__(err.tag, err.value));
});
});
return messages;
}
}
export class Query {
selector = {};
projection = {};
constructor(selector, projection) {
this._errors = new QueryErrors();
this.queryParams = new QueryParams();
this.colorMap = Boards.colorMap();
if (selector) {
this.selector = selector;
}
if (projection) {
this.projection = projection;
}
}
hasErrors() {
return this._errors.hasErrors();
}
errors() {
return this._errors.errors();
}
errorMessages() {
return this._errors.errorMessages();
}
getQueryParams() {
return this.queryParams;
}
setQueryParams(queryParams) {
this.queryParams = queryParams;
}
addPredicate(operator, predicate) {
this.queryParams.addPredicate(operator, predicate);
}
buildParams(queryText) {
queryText = queryText.trim();
// eslint-disable-next-line no-console
//console.log('query:', query);
if (!queryText) {
return;
}
const reOperator1 = new RegExp(
'^((?<operator>[\\p{Letter}\\p{Mark}]+):|(?<abbrev>[#@]))(?<value>[\\p{Letter}\\p{Mark}]+)(\\s+|$)',
'iu',
);
const reOperator2 = new RegExp(
'^((?<operator>[\\p{Letter}\\p{Mark}]+):|(?<abbrev>[#@]))(?<quote>["\']*)(?<value>.*?)\\k<quote>(\\s+|$)',
'iu',
);
const reText = new RegExp('^(?<text>\\S+)(\\s+|$)', 'u');
const reQuotedText = new RegExp(
'^(?<quote>["\'])(?<text>.*?)\\k<quote>(\\s+|$)',
'u',
);
const reNegatedOperator = new RegExp('^-(?<operator>.*)$');
const operators = {
'operator-board': OPERATOR_BOARD,
'operator-board-abbrev': OPERATOR_BOARD,
'operator-swimlane': OPERATOR_SWIMLANE,
'operator-swimlane-abbrev': OPERATOR_SWIMLANE,
'operator-list': OPERATOR_LIST,
'operator-list-abbrev': OPERATOR_LIST,
'operator-label': OPERATOR_LABEL,
'operator-label-abbrev': OPERATOR_LABEL,
'operator-user': OPERATOR_USER,
'operator-user-abbrev': OPERATOR_USER,
'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,
'operator-created': OPERATOR_CREATED_AT,
'operator-modified': OPERATOR_MODIFIED_AT,
'operator-comment': OPERATOR_COMMENT,
'operator-has': OPERATOR_HAS,
'operator-sort': OPERATOR_SORT,
'operator-limit': OPERATOR_LIMIT,
};
const predicates = {
due: {
'predicate-overdue': PREDICATE_OVERDUE,
},
durations: {
'predicate-week': PREDICATE_WEEK,
'predicate-month': PREDICATE_MONTH,
'predicate-quarter': PREDICATE_QUARTER,
'predicate-year': PREDICATE_YEAR,
},
status: {
'predicate-archived': PREDICATE_ARCHIVED,
'predicate-all': PREDICATE_ALL,
'predicate-open': PREDICATE_OPEN,
'predicate-ended': PREDICATE_ENDED,
'predicate-public': PREDICATE_PUBLIC,
'predicate-private': PREDICATE_PRIVATE,
},
sorts: {
'predicate-due': PREDICATE_DUE_AT,
'predicate-created': PREDICATE_CREATED_AT,
'predicate-modified': PREDICATE_MODIFIED_AT,
},
has: {
'predicate-description': PREDICATE_DESCRIPTION,
'predicate-checklist': PREDICATE_CHECKLIST,
'predicate-attachment': PREDICATE_ATTACHMENT,
'predicate-start': PREDICATE_START_AT,
'predicate-end': PREDICATE_END_AT,
'predicate-due': PREDICATE_DUE_AT,
'predicate-assignee': PREDICATE_ASSIGNEES,
'predicate-member': PREDICATE_MEMBERS,
},
};
const predicateTranslations = {};
Object.entries(predicates).forEach(([category, catPreds]) => {
predicateTranslations[category] = {};
Object.entries(catPreds).forEach(([tag, value]) => {
predicateTranslations[category][TAPi18n.__(tag)] = value;
});
});
// eslint-disable-next-line no-console
// console.log('predicateTranslations:', predicateTranslations);
const operatorMap = {};
Object.entries(operators).forEach(([key, value]) => {
operatorMap[TAPi18n.__(key).toLowerCase()] = value;
});
// eslint-disable-next-line no-console
// console.log('operatorMap:', operatorMap);
let text = '';
while (queryText) {
let m = queryText.match(reOperator1);
if (!m) {
m = queryText.match(reOperator2);
if (m) {
queryText = queryText.replace(reOperator2, '');
}
} else {
queryText = queryText.replace(reOperator1, '');
}
if (m) {
let op;
if (m.groups.operator) {
op = m.groups.operator.toLowerCase();
} else {
op = m.groups.abbrev.toLowerCase();
}
// eslint-disable-next-line no-prototype-builtins
if (operatorMap.hasOwnProperty(op)) {
const operator = operatorMap[op];
let value = m.groups.value;
if (operator === OPERATOR_LABEL) {
if (value in this.colorMap) {
value = this.colorMap[value];
// console.log('found color:', value);
}
} else if (
[OPERATOR_DUE, OPERATOR_CREATED_AT, OPERATOR_MODIFIED_AT].includes(
operator,
)
) {
const days = parseInt(value, 10);
let duration = null;
if (isNaN(days)) {
// duration was specified as text
if (predicateTranslations.durations[value]) {
duration = predicateTranslations.durations[value];
let date = null;
switch (duration) {
case PREDICATE_WEEK:
// eslint-disable-next-line no-case-declarations
const week = moment().week();
if (week === 52) {
date = moment(1, 'W');
date.set('year', date.year() + 1);
} else {
date = moment(week + 1, 'W');
}
break;
case PREDICATE_MONTH:
// eslint-disable-next-line no-case-declarations
const month = moment().month();
// .month() is zero indexed
if (month === 11) {
date = moment(1, 'M');
date.set('year', date.year() + 1);
} else {
date = moment(month + 2, 'M');
}
break;
case PREDICATE_QUARTER:
// eslint-disable-next-line no-case-declarations
const quarter = moment().quarter();
if (quarter === 4) {
date = moment(1, 'Q');
date.set('year', date.year() + 1);
} else {
date = moment(quarter + 1, 'Q');
}
break;
case PREDICATE_YEAR:
date = moment(moment().year() + 1, 'YYYY');
break;
}
if (date) {
value = {
operator: '$lt',
value: date.format('YYYY-MM-DD'),
};
}
} else if (
operator === OPERATOR_DUE &&
value === PREDICATE_OVERDUE
) {
value = {
operator: '$lt',
value: moment().format('YYYY-MM-DD'),
};
} else {
this.errors.addError(OPERATOR_DUE, {
tag: 'operator-number-expected',
value: { operator: op, value },
});
continue;
}
} else if (operator === OPERATOR_DUE) {
value = {
operator: '$lt',
value: moment(moment().format('YYYY-MM-DD'))
.add(days + 1, duration ? duration : 'days')
.format(),
};
} else {
value = {
operator: '$gte',
value: moment(moment().format('YYYY-MM-DD'))
.subtract(days, duration ? duration : 'days')
.format(),
};
}
} else if (operator === OPERATOR_SORT) {
let negated = false;
const m = value.match(reNegatedOperator);
if (m) {
value = m.groups.operator;
negated = true;
}
if (!predicateTranslations.sorts[value]) {
this.errors.addError(OPERATOR_SORT, {
tag: 'operator-sort-invalid',
value,
});
continue;
} else {
value = {
name: predicateTranslations.sorts[value],
order: negated ? ORDER_DESCENDING : ORDER_ASCENDING,
};
}
} else if (operator === OPERATOR_STATUS) {
if (!predicateTranslations.status[value]) {
this.errors.addError(OPERATOR_STATUS, {
tag: 'operator-status-invalid',
value,
});
continue;
} else {
value = predicateTranslations.status[value];
}
} else if (operator === OPERATOR_HAS) {
let negated = false;
const m = value.match(reNegatedOperator);
if (m) {
value = m.groups.operator;
negated = true;
}
if (!predicateTranslations.has[value]) {
this.errors.addError(OPERATOR_HAS, {
tag: 'operator-has-invalid',
value,
});
continue;
} else {
value = {
field: predicateTranslations.has[value],
exists: !negated,
};
}
} else if (operator === OPERATOR_LIMIT) {
const limit = parseInt(value, 10);
if (isNaN(limit) || limit < 1) {
this.errors.addError(OPERATOR_LIMIT, {
tag: 'operator-limit-invalid',
value,
});
continue;
} else {
value = limit;
}
}
this.queryParams.addPredicate(operator, value);
} else {
this.errors.addError(OPERATOR_UNKNOWN, {
tag: 'operator-unknown-error',
value: op,
});
}
continue;
}
m = queryText.match(reQuotedText);
if (!m) {
m = queryText.match(reText);
if (m) {
queryText = queryText.replace(reText, '');
}
} else {
queryText = queryText.replace(reQuotedText, '');
}
if (m) {
text += (text ? ' ' : '') + m.groups.text;
}
}
// eslint-disable-next-line no-console
// console.log('text:', text);
this.queryParams.text = text;
// eslint-disable-next-line no-console
console.log('queryParams:', this.queryParams);
}
}
+36
View File
@@ -14,6 +14,7 @@ FlowRouter.route('/', {
Session.set('currentCard', null);
Filter.reset();
Session.set('sortBy', '');
EscapeActions.executeAll();
Utils.manageCustomUI();
@@ -35,6 +36,7 @@ FlowRouter.route('/public', {
Session.set('currentCard', null);
Filter.reset();
Session.set('sortBy', '');
EscapeActions.executeAll();
Utils.manageCustomUI();
@@ -59,6 +61,7 @@ FlowRouter.route('/b/:id/:slug', {
// want to excape every current actions (filters, etc.)
if (previousBoard !== currentBoard) {
Filter.reset();
Session.set('sortBy', '');
EscapeActions.executeAll();
} else {
EscapeActions.executeUpTo('popup-close');
@@ -115,8 +118,10 @@ FlowRouter.route('/shortcuts', {
FlowRouter.route('/my-cards', {
name: 'my-cards',
triggersEnter: [AccountsTemplates.ensureSignedIn],
action() {
Filter.reset();
Session.set('sortBy', '');
// EscapeActions.executeAll();
EscapeActions.executeUpTo('popup-close');
@@ -133,8 +138,10 @@ FlowRouter.route('/my-cards', {
FlowRouter.route('/due-cards', {
name: 'due-cards',
triggersEnter: [AccountsTemplates.ensureSignedIn],
action() {
Filter.reset();
Session.set('sortBy', '');
// EscapeActions.executeAll();
EscapeActions.executeUpTo('popup-close');
@@ -151,8 +158,10 @@ FlowRouter.route('/due-cards', {
FlowRouter.route('/global-search', {
name: 'global-search',
triggersEnter: [AccountsTemplates.ensureSignedIn],
action() {
Filter.reset();
Session.set('sortBy', '');
// EscapeActions.executeAll();
EscapeActions.executeUpTo('popup-close');
@@ -179,6 +188,7 @@ FlowRouter.route('/broken-cards', {
const brokenCardsTemplate = 'brokenCards';
Filter.reset();
Session.set('sortBy', '');
// EscapeActions.executeAll();
EscapeActions.executeUpTo('popup-close');
@@ -205,6 +215,7 @@ FlowRouter.route('/import/:source', {
Session.set('importSource', params.source);
Filter.reset();
Session.set('sortBy', '');
EscapeActions.executeAll();
BlazeLayout.render('defaultLayout', {
headerBar: 'importHeaderBar',
@@ -223,6 +234,7 @@ FlowRouter.route('/setting', {
Session.set('currentCard', null);
Filter.reset();
Session.set('sortBy', '');
EscapeActions.executeAll();
},
],
@@ -245,6 +257,7 @@ FlowRouter.route('/information', {
Session.set('currentCard', null);
Filter.reset();
Session.set('sortBy', '');
EscapeActions.executeAll();
},
],
@@ -266,6 +279,7 @@ FlowRouter.route('/people', {
Session.set('currentCard', null);
Filter.reset();
Session.set('sortBy', '');
EscapeActions.executeAll();
},
],
@@ -277,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' });
+42
View File
@@ -0,0 +1,42 @@
export const DEFAULT_LIMIT = 25;
export const OPERATOR_ASSIGNEE = 'assignees';
export const OPERATOR_COMMENT = 'comment';
export const OPERATOR_CREATED_AT = 'createdAt';
export const OPERATOR_CREATOR = 'userId';
export const OPERATOR_DUE = 'dueAt';
export const OPERATOR_BOARD = 'board';
export const OPERATOR_HAS = 'has';
export const OPERATOR_LABEL = 'label';
export const OPERATOR_LIMIT = 'limit';
export const OPERATOR_LIST = 'list';
export const OPERATOR_MEMBER = 'members';
export const OPERATOR_MODIFIED_AT = 'modifiedAt';
export const OPERATOR_SORT = 'sort';
export const OPERATOR_STATUS = 'status';
export const OPERATOR_SWIMLANE = 'swimlane';
export const OPERATOR_UNKNOWN = 'unknown';
export const OPERATOR_USER = 'user';
export const ORDER_ASCENDING = 'asc';
export const ORDER_DESCENDING = 'des';
export const PREDICATE_ALL = 'all';
export const PREDICATE_ARCHIVED = 'archived';
export const PREDICATE_ASSIGNEES = 'assignees';
export const PREDICATE_ATTACHMENT = 'attachment';
export const PREDICATE_CHECKLIST = 'checklist';
export const PREDICATE_CREATED_AT = 'createdAt';
export const PREDICATE_DESCRIPTION = 'description';
export const PREDICATE_DUE_AT = 'dueAt';
export const PREDICATE_END_AT = 'endAt';
export const PREDICATE_ENDED = 'ended';
export const PREDICATE_MEMBERS = 'members';
export const PREDICATE_MODIFIED_AT = 'modifiedAt';
export const PREDICATE_MONTH = 'month';
export const PREDICATE_OPEN = 'open';
export const PREDICATE_OVERDUE = 'overdue';
export const PREDICATE_PRIVATE = 'private';
export const PREDICATE_PUBLIC = 'public';
export const PREDICATE_QUARTER = 'quarter';
export const PREDICATE_START_AT = 'startAt';
export const PREDICATE_SYSTEM = 'system';
export const PREDICATE_WEEK = 'week';
export const PREDICATE_YEAR = 'year';
+5 -2
View File
@@ -118,11 +118,11 @@ services:
#-------------------------------------------------------------------------------------
# ==== MONGODB AND METEOR VERSION ====
# a) Quay automatic builds do work work, https://quay.io/wekan/wekan
# image: quay.io/wekan/wekan
image: quay.io/wekan/wekan
# b) Using specific version tag:
# image: quay.io/wekan/wekan:v4.52
# c) Docker Hub builds do work https://hub.docker.com/r/wekanteam/wekan
image: wekanteam/wekan
# image: wekanteam/wekan
#-------------------------------------------------------------------------------------
container_name: wekan-app
restart: always
@@ -209,6 +209,9 @@ services:
# There is Feature Request: Logging date and time of all activity with summary reports,
# and requesting reason for changing card to other column https://github.com/wekan/wekan/issues/1598
#---------------------------------------------------------------
# ==== NUMBER OF SEARCH RESULTS PER PAGE BY DEFAULT ====
#- RESULTS_PER_PAGE=20
#---------------------------------------------------------------
# ==== WEKAN API AND EXPORT BOARD ====
# Wekan Export Board works when WITH_API=true.
# https://github.com/wekan/wekan/wiki/REST-API
+1009 -988
View File
File diff suppressed because it is too large. Load diff
+1009 -988
View File
File diff suppressed because it is too large. Load diff
+1009 -988
View File
File diff suppressed because it is too large. Load diff
+1009 -988
View File
File diff suppressed because it is too large. Load diff
+1009 -988
View File
File diff suppressed because it is too large. Load diff
+1009 -988
View File
File diff suppressed because it is too large. Load diff
+1009 -988
View File
File diff suppressed because it is too large. Load diff
+1010
View File
File diff suppressed because it is too large. Load diff
+24 -3
View File
@@ -344,6 +344,12 @@
"list-label-short-sort": "(M)",
"filter": "Filter",
"filter-cards": "Karten oder Listen filtern",
"filter-dates-label": "Nach Datum filtern",
"filter-no-due-date": "Kein Fälligkeitsdatum",
"filter-overdue": "Überfällig",
"filter-due-today": "Heute fällig",
"filter-due-this-week": "Diese Woche fällig",
"filter-due-tomorrow": "Morgen fällig",
"list-filter-label": "Liste nach Titel filtern",
"filter-clear": "Filter entfernen",
"filter-labels-label": "Nach Label filtern",
@@ -900,6 +906,7 @@
"operator-member-abbrev": "m",
"operator-assignee": "Zugewiesener",
"operator-assignee-abbrev": "a",
"operator-creator": "Ersteller",
"operator-status": "Status",
"operator-due": "Fällig",
"operator-created": "erstellt",
@@ -946,11 +953,12 @@
"globalSearch-instructions-operator-swimlane": "`__operator_swimlane__:<title>` – Karten in Swimlanes, auf die das angebene *<title>* passt",
"globalSearch-instructions-operator-comment": "`__operator_comment__:<text>` – Karten mit einem Kommentar, das *<text>* enthält.",
"globalSearch-instructions-operator-label": "`__operator_label__:<color>` `__operator_label__:<name>` – Karten, die ein Label haben, auf das *<color>* oder *<name> passt",
"globalSearch-instructions-operator-hash": "`__operator_label_abbrev__<name | color>` – Kurzform von `__operator_label__:<color>` oder `__operator_label__:<name>`",
"globalSearch-instructions-operator-hash": "`__operator_label_abbrev__<Name|Farbe>` – Kurzform für `__operator_label__:<color>` oder `__operator_label__:<name>`",
"globalSearch-instructions-operator-user": "`__operator_user__:<username>` – Karten, für die *<username>* ein *Mitglied* oder ein *Zugewiesener* ist",
"globalSearch-instructions-operator-at": "`__operator_user_abbrev__username` – Kurzform für `user:<username>`",
"globalSearch-instructions-operator-member": "`__operator_member__:<username>` – Karten, von denen *<username>* *Mitglied* ist",
"globalSearch-instructions-operator-assignee": "`__operator_assignee__:<username>` – Karten, denen *<username>* *zugewiesen* ist",
"globalSearch-instructions-operator-creator": "`__operator_creator__:<username>` – Karten, die von *<username>* angelegt wurden",
"globalSearch-instructions-operator-due": "`__operator_due__:<n>` – Karten, die spätestens in *<n>* Tagen fällig sind. `__operator_due__:__predicate_overdue__` zeigt alle Karten, für die die Fälligkeit überschritten ist.",
"globalSearch-instructions-operator-created": "`__operator_created__:<n>` – Karten, die vor maximal *<n>* Tagen angelegt wurden",
"globalSearch-instructions-operator-modified": "`__operator_modified__:<n>` – Karten, die vor maximal *<n>* Tagen geändert wurden",
@@ -978,12 +986,25 @@
"sort-cards": "Sortiere Karten",
"cardsSortPopup-title": "Sortiere Karten",
"due-date": "Fälligkeitsdatum",
"server-error": "Server-Fehler",
"server-error-troubleshooting": "Bitte übermitteln Sie den Fehler, den der Server erzeugt hat.\nRufen Sie für eine Snap-Installation auf: `sudo snap logs wekan.wekan`\nRufen Sie für eine Docker-Installation auf: `sudo docker logs wekan-app`",
"title-alphabetically": "Überschrift (alphabetisch)",
"created-at-newest-first": "Erstelldatum (neueste zuerst)",
"created-at-oldest-first": "Erstelldatum (älteste zuerst)",
"links-heading": "Links",
"hide-system-messages-of-all-users": "Alle System-Nachrichten aller Nutzer verbergen",
"now-system-messages-of-all-users-are-hidden": "Alle System-Nachrichten aller Nutzer sind nun verborgen",
"move-swimlane": "Move Swimlane",
"moveSwimlanePopup-title": "Move Swimlane"
"move-swimlane": "Swimlane verschieben",
"moveSwimlanePopup-title": "Swimlane verschieben",
"custom-field-stringtemplate": "String-Vorlage",
"custom-field-stringtemplate-format": "Format (verwende %{value} als Platzhalter)",
"custom-field-stringtemplate-separator": "Trenner (verwende &#32; oder &nbsp; für einen Leerschritt)",
"custom-field-stringtemplate-item-placeholder": "Drücke die Eingabetaste, um weitere Einträge hinzuzufügen",
"creator": "Ersteller",
"filesReportTitle": "Dateien-Bericht",
"orphanedFilesReportTitle": "Verwaister Datei-Bericht",
"reports": "Berichte",
"rulesReportTitle": "Regeln-Bericht",
"copy-swimlane": "Kopiere Swimlane",
"copySwimlanePopup-title": "Swimlane kopieren"
}
+1009 -988
View File
File diff suppressed because it is too large. Load diff
+1009 -988
View File
File diff suppressed because it is too large. Load diff
+23 -2
View File
@@ -344,6 +344,12 @@
"list-label-short-sort": "(M)",
"filter": "Filter",
"filter-cards": "Filter Cards or Lists",
"filter-dates-label": "Filter by date",
"filter-no-due-date": "No due date",
"filter-overdue": "Overdue",
"filter-due-today": "Due today",
"filter-due-this-week": "Due this week",
"filter-due-tomorrow": "Due tomorrow",
"list-filter-label": "Filter List by Title",
"filter-clear": "Clear filter",
"filter-labels-label": "Filter by label",
@@ -901,6 +907,7 @@
"operator-member-abbrev": "m",
"operator-assignee": "assignee",
"operator-assignee-abbrev": "a",
"operator-creator": "creator",
"operator-status": "status",
"operator-due": "due",
"operator-created": "created",
@@ -947,11 +954,12 @@
"globalSearch-instructions-operator-swimlane": "`__operator_swimlane__:<title>` - cards in swimlanes matching the specified *<title>*",
"globalSearch-instructions-operator-comment": "`__operator_comment__:<text>` - cards with a comment containing *<text>*.",
"globalSearch-instructions-operator-label": "`__operator_label__:<color>` `__operator_label__:<name>` - cards that have a label matching *<color>* or *<name>",
"globalSearch-instructions-operator-hash": "`__operator_label_abbrev__<name | color>` - shorthand for `__operator_label__:<color>` or `__operator_label__:<name>`",
"globalSearch-instructions-operator-hash": "`__operator_label_abbrev__<name|color>` - shorthand for `__operator_label__:<color>` or `__operator_label__:<name>`",
"globalSearch-instructions-operator-user": "`__operator_user__:<username>` - cards where *<username>* is a *member* or *assignee*",
"globalSearch-instructions-operator-at": "`__operator_user_abbrev__username` - shorthand for `user:<username>`",
"globalSearch-instructions-operator-member": "`__operator_member__:<username>` - cards where *<username>* is a *member*",
"globalSearch-instructions-operator-assignee": "`__operator_assignee__:<username>` - cards where *<username>* is an *assignee*",
"globalSearch-instructions-operator-creator": "`__operator_creator__:<username>` - cards where *<username>* is the card's creator",
"globalSearch-instructions-operator-due": "`__operator_due__:<n>` - cards which are due up to *<n>* days from now. `__operator_due__:__predicate_overdue__ lists all cards past their due date.",
"globalSearch-instructions-operator-created": "`__operator_created__:<n>` - cards which were created *<n>* days ago or less",
"globalSearch-instructions-operator-modified": "`__operator_modified__:<n>` - cards which were modified *<n>* days ago or less",
@@ -979,6 +987,8 @@
"sort-cards": "Sort Cards",
"cardsSortPopup-title": "Sort Cards",
"due-date": "Due Date",
"server-error": "Server Error",
"server-error-troubleshooting": "Please submit the error generated by the server.\nFor a snap installation, run: `sudo snap logs wekan.wekan`\nFor a Docker installation, run: `sudo docker logs wekan-app`",
"title-alphabetically": "Title (Alphabetically)",
"created-at-newest-first": "Created At (Newest First)",
"created-at-oldest-first": "Created At (Oldest First)",
@@ -986,5 +996,16 @@
"hide-system-messages-of-all-users": "Hide system messages of all users",
"now-system-messages-of-all-users-are-hidden": "Now system messages of all users are hidden",
"move-swimlane": "Move Swimlane",
"moveSwimlanePopup-title": "Move Swimlane"
"moveSwimlanePopup-title": "Move Swimlane",
"custom-field-stringtemplate": "String Template",
"custom-field-stringtemplate-format": "Format (use %{value} as placeholder)",
"custom-field-stringtemplate-separator": "Separator (use &#32; or &nbsp; for a space)",
"custom-field-stringtemplate-item-placeholder": "Press enter to add more items",
"creator": "Creator",
"filesReportTitle": "Files Report",
"orphanedFilesReportTitle": "Orphaned Files Report",
"reports": "Reports",
"rulesReportTitle": "Rules Report",
"copy-swimlane": "Copy Swimlane",
"copySwimlanePopup-title": "Copy Swimlane"
}
+1009 -988
View File
File diff suppressed because it is too large. Load diff
+1009 -988
View File
File diff suppressed because it is too large. Load diff
+1009 -988
View File
File diff suppressed because it is too large. Load diff
+1009 -988
View File
File diff suppressed because it is too large. Load diff
+1009 -988
View File
File diff suppressed because it is too large. Load diff
+1009 -988
View File
File diff suppressed because it is too large. Load diff
+1009 -988
View File
File diff suppressed because it is too large. Load diff
+1009 -988
View File
File diff suppressed because it is too large. Load diff
+1009 -988
View File
File diff suppressed because it is too large. Load diff
+1009 -988
View File
File diff suppressed because it is too large. Load diff
+1009 -988
View File
File diff suppressed because it is too large. Load diff
+1009 -988
View File
File diff suppressed because it is too large. Load diff
+25 -4
View File
@@ -344,6 +344,12 @@
"list-label-short-sort": "(M)",
"filter": "Filtrer",
"filter-cards": "Filtrer les cartes ou listes",
"filter-dates-label": "Filtrer par date",
"filter-no-due-date": "Pas de date d'échéance",
"filter-overdue": "Échue",
"filter-due-today": "Arrive à échéance aujourd'hui",
"filter-due-this-week": "Arrive à échéance cette semaine",
"filter-due-tomorrow": "Arrive à échéance demain",
"list-filter-label": "Filtrer la liste par titre",
"filter-clear": "Supprimer les filtres",
"filter-labels-label": "Filtrer par étiquette",
@@ -900,6 +906,7 @@
"operator-member-abbrev": "m",
"operator-assignee": "personne assignée",
"operator-assignee-abbrev": "a",
"operator-creator": "créateur",
"operator-status": "statut",
"operator-due": "échéance",
"operator-created": "créé",
@@ -951,6 +958,7 @@
"globalSearch-instructions-operator-at": "`__operator_user_abbrev__nom` - raccourci pour `__operator_user__:<nom>`",
"globalSearch-instructions-operator-member": "`__operator_member__:<nom>` - cartes pour lesquelles l'utilisateur *<nom>* est *participant*",
"globalSearch-instructions-operator-assignee": "`__operator_assignee__:<nom>` - cartes *assignées* à l'utilisateur *<nom>*",
"globalSearch-instructions-operator-creator": "`__operator_creator__:<utilisateur>` - cartes dont le créateur est *<utilisateur>*",
"globalSearch-instructions-operator-due": "`__operator_due__:<n>` - cartes qui arrive à échéance dans moins de *<n>* jours à partir d'aujourd'hui.\n`__operator_due__:__predicate_overdue__` liste toutes les cartes ayant passé la date d'échéance.",
"globalSearch-instructions-operator-created": "`__operator_created__:<n>` - cartes qui ont été créées il y a *<n>* jours ou moins",
"globalSearch-instructions-operator-modified": "`__operator_modified__:<n>` - cartes qui ont été modifiées il y a *<n>* jours ou moins",
@@ -978,12 +986,25 @@
"sort-cards": "Trier les cartes",
"cardsSortPopup-title": "Trier les cartes",
"due-date": "Date d'échéance",
"server-error": "Erreur serveur",
"server-error-troubleshooting": "Merci de soumettre l'erreur générée par le serveur. Pour une installation snap, lancer `sudo snap logs wekan.wekan`. Pour une installation docker, lancer `sudo docker logs wekan-app`",
"title-alphabetically": "Titre (Alphabétiquement)",
"created-at-newest-first": "Date de création (Plus récentes en premier)",
"created-at-oldest-first": "Date de création (Plus anciennes en premier)",
"links-heading": "Liens",
"hide-system-messages-of-all-users": "Hide system messages of all users",
"now-system-messages-of-all-users-are-hidden": "Now system messages of all users are hidden",
"move-swimlane": "Move Swimlane",
"moveSwimlanePopup-title": "Move Swimlane"
"hide-system-messages-of-all-users": "Masquer les messages système de tous les utilisateurs",
"now-system-messages-of-all-users-are-hidden": "Les messages système de tous les utilisateurs seront dorénavant masqués",
"move-swimlane": "Déplacer le couloir",
"moveSwimlanePopup-title": "Déplacer le Couloir",
"custom-field-stringtemplate": "Modèle de chaîne",
"custom-field-stringtemplate-format": "Format (utiliser %{valeur} pour marquer un emplacement)",
"custom-field-stringtemplate-separator": "Séparateur (utiliser &#32; ou &nbsp; pour un espace)",
"custom-field-stringtemplate-item-placeholder": "Appuyez sur Entrée pour ajouter plus d'éléments",
"creator": "Créateur",
"filesReportTitle": "Rapports sur les fichiers",
"orphanedFilesReportTitle": "Rapports sur les fichiers orphelins",
"reports": "Rapports",
"rulesReportTitle": "Rapports sur les règles",
"copy-swimlane": "Copier le couloir",
"copySwimlanePopup-title": "Copie de Couloir"
}
+1009 -988
View File
File diff suppressed because it is too large. Load diff
+24 -3
View File
@@ -344,6 +344,12 @@
"list-label-short-sort": "(י)",
"filter": "מסנן",
"filter-cards": "סינון כרטיסים או רשימות",
"filter-dates-label": "סינון לפי תאריך",
"filter-no-due-date": "אין מועד סיום",
"filter-overdue": "מועד הסיום עבר",
"filter-due-today": "מועד הסיום הוא היום",
"filter-due-this-week": "מועד הסיום הוא השבוע",
"filter-due-tomorrow": "מועד הסיום הוא מחר",
"list-filter-label": "סינון רשימה לפי כותרת",
"filter-clear": "ניקוי המסנן",
"filter-labels-label": "סינון לפי תווית",
@@ -900,6 +906,7 @@
"operator-member-abbrev": "ח",
"operator-assignee": "אחראי",
"operator-assignee-abbrev": "א",
"operator-creator": "יוצר",
"operator-status": "מצב",
"operator-due": "תפוגה",
"operator-created": "נוצר",
@@ -946,11 +953,12 @@
"globalSearch-instructions-operator-swimlane": "`__operator_swimlane__:<title>` - כרטיסים במסלולים שעונים על *<title>* שצוין",
"globalSearch-instructions-operator-comment": "`__operator_comment__:<text>` - כרטיסים עם הערה שמכילים *<text>*.",
"globalSearch-instructions-operator-label": "`__operator_label__:<color>` `__operator_label__:<name>` - כרטיסים שיש להם תווית שתואמת את *<color>* או את *<name>",
"globalSearch-instructions-operator-hash": "`__operator_label_abbrev__<name | color>` - קיצור של `__operator_label__:<color>` או `__operator_label__:<name>`",
"globalSearch-instructions-operator-hash": "`__operator_label_abbrev__<name|color>` - קיצור של `__operator_label__:<color>` או `__operator_label__:<name>`",
"globalSearch-instructions-operator-user": "`__operator_user__:<username>` - כרטיסים שהוקצו אל *<username>* או שהוא *חבר* בהם",
"globalSearch-instructions-operator-at": "`__operator_user_abbrev__username` - קיצור של `user:<username>`",
"globalSearch-instructions-operator-member": "`__operator_member__:<username>` - כרטיסים בהם *<username>* *חבר*",
"globalSearch-instructions-operator-assignee": "`__operator_assignee__:<username>` - כרטיסים שהוקצו אל *<username>*",
"globalSearch-instructions-operator-creator": "`__operator_creator__:<username>` - כרטיסים שנוצרו על ידי *<username>*",
"globalSearch-instructions-operator-due": "`__operator_due__:<n>` - כרטיסים שהתוקף שלהם יפוג עד עוד *<n>* ימים מעכשיו. `__operator_due__:__predicate_overdue__ מציג את כל הכרטיסים שתאריך התוקף שלהם עבר.",
"globalSearch-instructions-operator-created": "`__operator_created__:<n>` - כרטיסים שנוצרו לפני *<n>* ימים או פחות",
"globalSearch-instructions-operator-modified": "`__operator_modified__:<n>` - כרטיסים שנערכו לפני *<n>* ימים או פחות",
@@ -978,12 +986,25 @@
"sort-cards": "מיון כרטיסים",
"cardsSortPopup-title": "מיון כרטיסים",
"due-date": "מועד סיום",
"server-error": "שגיאת שרת",
"server-error-troubleshooting": "נא להגיש את השגיאה שיצר השרת.\nבהתקנת snap יש להריץ: `sudo snap logs wekan.wekan`\nבהתקנת Docker יש להריץ: `sudo docker logs wekan-app`",
"title-alphabetically": "כותרת (לפי האלפבית)",
"created-at-newest-first": "מועד יצירה (החדש ביותר בהתחלה)",
"created-at-oldest-first": "מועד יצירה (הישן ביותר בהתחלה)",
"links-heading": "קישורים",
"hide-system-messages-of-all-users": "להסתיר את הודעות המערכת של כל המשתמשים",
"now-system-messages-of-all-users-are-hidden": "כעת הודעות המערכת של כל המשתמשים מוסתרות",
"move-swimlane": "Move Swimlane",
"moveSwimlanePopup-title": "Move Swimlane"
"move-swimlane": "העברת מסלול",
"moveSwimlanePopup-title": "העברת מסלול",
"custom-field-stringtemplate": "תבנית מחרוזת",
"custom-field-stringtemplate-format": "תבנית (להשתמש ב־%{value} כממלא מקום)",
"custom-field-stringtemplate-separator": "מפריד (להשתמש ב־&#32; או ב־&nbsp; כרווח)",
"custom-field-stringtemplate-item-placeholder": "יש ללחוץ על enter כדי להוסיף עוד פריטים",
"creator": "יוצר",
"filesReportTitle": "דוח קבצים",
"orphanedFilesReportTitle": "דוח קבצים יתומים",
"reports": "דוחות",
"rulesReportTitle": "דוח כללים",
"copy-swimlane": "העתקת מסלול",
"copySwimlanePopup-title": "העתקת מסלול"
}
+1009 -988
View File
File diff suppressed because it is too large. Load diff
+1009 -988
View File
File diff suppressed because it is too large. Load diff
+1009 -988
View File
File diff suppressed because it is too large. Load diff
+1009 -988
View File
File diff suppressed because it is too large. Load diff
+1009 -988
View File
File diff suppressed because it is too large. Load diff
+1009 -988
View File
File diff suppressed because it is too large. Load diff
+1009 -988
View File
File diff suppressed because it is too large. Load diff
+1009 -988
View File
File diff suppressed because it is too large. Load diff
+1009 -988
View File
File diff suppressed because it is too large. Load diff
Loaded 100 of 181 files, more files were not shown because too many files have changed in this diff. Show more