Compare commits

...
217 Commits
Author SHA1 Message Date
Lauri Ojansivu b33f44e23f v3.87 2020-04-01 00:12:35 +03:00
Lauri Ojansivu a65e39563b Update translations. 2020-04-01 00:04:18 +03:00
Lauri Ojansivu 703efa5216 Update ChangeLog. 2020-04-01 00:01:15 +03:00
Lauri Ojansivu 6b902bdb36 Improvements on card details visualization.
Thanks to helioguardabaxo and xet7 !

Closes #2974
2020-03-31 23:56:37 +03:00
Lauri Ojansivu d599fc12ff Update ChangeLog. 2020-03-31 23:36:47 +03:00
Lauri Ojansivu e7d2343593 Move "Rules" from "Board View" to "Board Settings".
Thanks to helioguardabaxo and xet7 !

Closes #2973
2020-03-31 23:32:32 +03:00
Lauri Ojansivu c8ec5d5f62 Update ChangeLog. 2020-03-31 23:20:07 +03:00
Lauri Ojansivu 033d671047 Fix richer editor submit did not clear edit area.
Thanks to xet7 !
2020-03-31 23:17:58 +03:00
Lauri Ojansivu 6ef3f8a1c8 Update ChangeLog. 2020-03-31 17:05:02 +03:00
Lauri Ojansivu 3546d7aa02 Fix Browser always reload the whole page when I change one of the card color.
Fixed by making label colors and text again editable.
Regression from [Wekan v3.86 2)](https://github.com/wekan/wekan/commit/b9099a8b7ea6f63c79bdcbb871cb993b2cb7e325).

Thanks to javen9881 and xet7 !

Closes #2971
2020-03-31 16:56:32 +03:00
Lauri Ojansivu 29d62440a5 Update ChangeLog. 2020-03-25 13:41:33 +02:00
Lauri Ojansivu 5bd0459cc2 Hide duplicate "Hide system messages" at Change Settings/Member Settings, because it's also on card slider.
Thanks to notohiro and xet7 !

Closes #2837
2020-03-25 13:35:54 +02:00
Lauri Ojansivu 8b118c1939 v3.86 2020-03-24 21:01:14 +02:00
Lauri Ojansivu d7a0ab3c15 Update translations. 2020-03-24 20:48:58 +02:00
Lauri Ojansivu 8609e0e82d Update ChangeLog. 2020-03-24 20:45:25 +02:00
Lauri Ojansivu b9099a8b7e 1) Fix Pasting text into a card is adding a line before and after
(and multiplies by pasting more) by changing paste "p" to "br".
2) Fixes to summernote and markdown comment editors, related
   to keeping them open when adding comments, having
   @member mention not close card, and disabling clicking of
   @member mention.

Thanks to xet7 !

Closes #2890
2020-03-24 20:39:49 +02:00
Lauri Ojansivu 87a81789d1 Update ChangeLog. 2020-03-24 11:10:15 +02:00
Lauri Ojansivu 12ab8fac5d Fix Rich editor can not be disabled, regression from changes yesterday at Wekan v3.85.
Thanks to uusijani, vjrj and xet7 !

Closes #2967,
closes #104
2020-03-24 11:04:04 +02:00
Lauri Ojansivu 10b09b2da1 Fix typo. 2020-03-24 02:01:08 +02:00
Lauri Ojansivu 7c5f4a3849 v3.85 2020-03-23 23:01:58 +02:00
Lauri Ojansivu ec71849d84 Update ChangeLog. 2020-03-23 22:49:28 +02:00
Lauri Ojansivu 482682e500 SECURITY VULNERABILITY FIX: Fix XSS bug reported today 4 hours ago by Cyb3rjunky.
Logged in users could run javascript in input fields.
This affects Wekan versions v3.12-v3.84.
In [Wekan v3.12](https://github.com/wekan/wekan/blob/master/CHANGELOG.md#v312-2019-08-09-wekan-release)
there was [changes for XSS filter to allow inserting images, videos etc
on comment WYSIWYG editor](https://github.com/wekan/wekan/pull/2593)
so features related to that are now removed.
After this fix, Javascript in input fields is not executed.

Thanks to Cyb3rjunky and xet7 !
2020-03-23 22:29:20 +02:00
Lauri Ojansivu 3a6303e5c2 Update translations. 2020-03-23 22:25:08 +02:00
Lauri Ojansivu 81635d9217 Revert: Fix Pasting text into a card is adding a line before and after
(and multiplies by pasting more) by disabling WYSIWYG comment editor
for everyone.

Thanks to xet7.

TODO later: Fix wysiwyg editor paste bug.

Opens #2890
2020-03-23 22:23:31 +02:00
Lauri Ojansivu e8f89a8c4c Update translations. 2020-03-20 14:03:11 +02:00
Lauri Ojansivu 393482315f Fix typo. 2020-03-20 14:02:29 +02:00
Lauri Ojansivu cb67af02d7 Update ChangeLog. 2020-03-20 13:57:44 +02:00
Lauri Ojansivu a064b8d643 Fix Pasting text into a card is adding a line before and after
(and multiplies by pasting more) by disabling WYSIWYG comment editor
for everyone.

Closes #2890
2020-03-20 13:52:27 +02:00
Lauri Ojansivu 556621e60e Meteor 1.8 only in use at Sandstorm. 2020-03-17 14:04:50 +02:00
Lauri Ojansivu 0f95a513bf Meteor 1.8 only in use at Sandstorm. 2020-03-17 14:02:43 +02:00
Lauri Ojansivu dce624486f Merge branch 'master' of github.com:wekan/wekan 2020-03-17 13:35:00 +02:00
Lauri Ojansivu 64e76af8de Fix snap syntax. 2020-03-17 13:34:34 +02:00
Lauri Ojansivu acf2ede94e Fix typo. 2020-03-16 10:11:20 -04:00
Lauri Ojansivu b3a7c4dc88 For Sandstorm version of Wekan, leep stylus in 1.1.0, because building v2 takes extra 52 minutes.
Thanks to zodern !
2020-03-16 10:01:28 -04:00
Lauri Ojansivu 8d83c641e3 v3.84 2020-03-16 14:04:18 +02:00
Lauri Ojansivu c1aa0047c6 Update ChangeLog. 2020-03-16 13:55:39 +02:00
Lauri Ojansivu 7af4fe75ba Update ChangeLog. 2020-03-16 13:53:12 +02:00
Lauri Ojansivu ea6baa5c2b Update minimist. 2020-03-16 13:42:29 +02:00
Lauri Ojansivu 8183b7bdaa Update prettier-eslint 2020-03-16 13:33:08 +02:00
Lauri Ojansivu 51514ae22c Update prettier-eslint 2020-03-16 13:29:44 +02:00
Lauri Ojansivu 14b8610837 Update ostrio:cookies 2020-03-16 13:24:39 +02:00
Lauri Ojansivu 369a29707b Update acorn. 2020-03-16 13:22:16 +02:00
Lauri Ojansivu ae240af323 Update translations. 2020-03-16 13:16:47 +02:00
Lauri Ojansivu 2a715ec6c6 Update ChangeLog. 2020-03-15 03:02:08 +02:00
Lauri Ojansivu fca4cdcebf Downgrade stylus to v1.1.0 to speed up building Wekan.
https://github.com/meteor/meteor/issues/10881#issuecomment-599152333

Thanks to zodern !
2020-03-15 02:49:53 +02:00
Lauri Ojansivu 49bbfef3ec Update dependencies. 2020-03-15 02:00:11 +02:00
Lauri Ojansivu f968109e73 Add profiling to build script. 2020-03-15 00:44:03 +02:00
Lauri Ojansivu c0349c1ff9 Update dependencies. 2020-03-15 00:06:09 +02:00
Lauri Ojansivu e009bcf06e Update dependencies. 2020-03-14 17:00:21 +02:00
Lauri Ojansivu 8aae812aa4 Merge pull request #2959 from daMihe/add-current-description-ldap-groups
Added some descriptions for ldap-group authentication
2020-03-14 16:59:37 +02:00
Michael Wodniok 0b8f1cabef Added some descriptions for ldap-group authentication 2020-03-14 15:38:19 +01:00
Lauri Ojansivu e80d3ac79a Update dependencies. 2020-03-14 14:30:59 +02:00
Lauri Ojansivu ed9ba169fe Update dependencies. 2020-03-14 14:08:48 +02:00
Lauri Ojansivu 4720a6fbc3 Fix to be valid JSON. 2020-03-14 14:07:15 +02:00
Lauri Ojansivu ad45f82e27 Update ChangeLog. 2020-03-14 13:21:44 +02:00
Lauri Ojansivu 2174936613 Update acorn. 2020-03-14 13:19:54 +02:00
Lauri Ojansivu a6d7388919 Update ChangeLog. 2020-03-14 13:09:29 +02:00
Lauri Ojansivu f4e2bc0d32 Update dependencies. 2020-03-14 13:06:17 +02:00
Lauri Ojansivu 2f13141907 Update dependencies. 2020-03-14 12:36:12 +02:00
Lauri Ojansivu 365eded5c4 Update ChangeLog. 2020-03-14 12:22:28 +02:00
Lauri Ojansivu ed365c3d41 Update translations. 2020-03-14 12:10:33 +02:00
Lauri Ojansivu e16c65babc Upgrade to Meteor 1.10.1 2020-03-14 12:05:48 +02:00
Lauri Ojansivu 6e0fa78022 Update markdown. 2020-03-14 12:03:34 +02:00
Lauri Ojansivu 06fb201d80 Update translations. 2020-03-11 08:48:11 +02:00
Lauri Ojansivu 62e5622523 Update ChangeLog. 2020-03-06 03:59:09 +02:00
Lauri Ojansivu 2b26bbe78a Fix: img tag did not allow width and height.
Removed swipebox from markdown editor img tag and
updated marked markdown to newest version.

Thanks to hradec and xet7 !

Closes #2956
2020-03-06 03:52:12 +02:00
Lauri Ojansivu a6d702d1ab Update ChangeLog. 2020-03-05 22:04:22 +02:00
Lauri Ojansivu aaecac0912 Mouse scroll part 2. 2020-03-05 22:02:52 +02:00
Lauri Ojansivu 08b35e1fe7 Fix typo. 2020-03-05 21:57:48 +02:00
Lauri Ojansivu 78cf4618d5 Update ChangeLog. 2020-03-05 21:56:44 +02:00
Lauri Ojansivu 9d13001b90 Add settings for mouse wheen scroll inertia and scroll amount.
For example:
  sudo snap set scrollinertia='200'
  sudo snap set scrollamount='200'

Thanks to danger89 and xet7 !

Related #2949
2020-03-05 21:51:03 +02:00
Lauri Ojansivu 965c3fb07f Update translations. 2020-03-05 21:09:33 +02:00
Lauri Ojansivu c43366b7ac Update ChangeLog. 2020-03-05 20:51:27 +02:00
Lauri Ojansivu 3a50556656 Merge pull request #2955 from benoitm76/master
Fix: Error when retrieve token from some OIDC due to not necessary sc…
2020-03-05 20:29:37 +02:00
Benoit MOUQUET b26e65c70c Fix: Error when retrieve token from some OIDC due to not necessary scope parameter 2020-03-05 12:52:46 +01:00
Lauri Ojansivu 75073bbdb2 v3.83 2020-03-01 22:12:11 +02:00
Lauri Ojansivu 88573ad2cd Try to revert remaining of the following changes:
- Revert change made at Wekan v3.81, because building did not work: [Try to make Meteor build time shorter
  by excluding legacy and cordova. This was made possible by
  Meteor 1.10-rc.2](https://github.com/wekan/wekan/commit/0d3002f69d97e646fa7368bfdade4f78c51e9884).

Thanks to xet7.
2020-03-01 22:07:55 +02:00
Lauri Ojansivu 058180687a v3.82 2020-03-01 21:44:38 +02:00
Lauri Ojansivu 897506eeac v3.81 2020-03-01 21:30:43 +02:00
Lauri Ojansivu aac7c380c8 - Fix critical and moderate security vulnerabilities reported at 2020-02-26 with
responsible disclosure by [Dejan Zelic](https://twitter.com/dejandayoff),
  Justin Benjamin and others at [Offensive Security](https://twitter.com/offsectraining),
  that follow standard 90 days before public disclosure.
  Thanks to xet7.
- Fix webhook error that prevented some card etc deleting from web UI of board.
  Thanks to xet7.
- Add some more Font Awesome icons.
  Thanks to xet7.
- Remove autofocus from many form input boxes so that they would not cause warnings.
  Thanks to xet7.
2020-03-01 20:59:53 +02:00
Lauri Ojansivu fc35c234a7 Update translations. 2020-03-01 20:38:05 +02:00
Lauri Ojansivu 0d3002f69d Try to make Meteor build time shorter by excluding legacy and cordova.
Thanks to xet7 !
2020-03-01 20:33:23 +02:00
Lauri Ojansivu 7d920e938b Merge branch 'master' of github.com:wekan/wekan 2020-03-01 20:27:22 +02:00
Lauri Ojansivu 26b521e86e Upgrade Meteor to 1.10-rc.2 2020-03-01 20:26:29 +02:00
Lauri Ojansivu 83b042701f Merge pull request #2948 from NicoP-S/master
Make profile.initials available in publications
2020-02-28 23:11:03 +02:00
Nico 21b75edcdd Make profile.initials available in publications 2020-02-28 22:05:33 +01:00
Lauri Ojansivu 493d71a198 Update translations. 2020-02-23 22:45:43 +02:00
Lauri Ojansivu 27f9c6b925 Update translations. 2020-02-22 20:07:16 +02:00
Lauri Ojansivu fa07dcf422 Update ChangeLog. 2020-02-22 16:39:28 +02:00
Lauri Ojansivu 2b9540ce02 Try to fix afterwards loading of cards by adding
fallback when requestIdleCallback is not available.

Thanks to xet7 !

Closes #2878,
closes #2250
2020-02-22 16:32:54 +02:00
Lauri Ojansivu 906c25d5e8 Update translations. 2020-02-22 10:32:48 +02:00
Lauri Ojansivu e9924a2f03 v3.80 2020-02-22 03:10:08 +02:00
Lauri Ojansivu 16e7fc6edc Update translations. 2020-02-22 02:55:26 +02:00
Lauri Ojansivu e0ca960a35 Create New User in Admin Panel. Works, but does not save fullname yet,
so currently it's needed to edit add fullname later.

Thanks to xet7 !

Related #802
2020-02-22 02:49:14 +02:00
Lauri Ojansivu 0e755e021b Update ChangeLog. 2020-02-21 00:48:02 +02:00
Lauri Ojansivu 9be3f3714a Update to Meteor 1.9.2 2020-02-21 00:46:05 +02:00
Lauri Ojansivu 02e425b39d Update ChangeLog. 2020-02-20 22:57:04 +02:00
Lauri Ojansivu ec01206030 Remove alethes:pages package, that had some indentation error.
Package is about pagination, but I did not find any pagination
related code in Wekan yet.

Thanks to xet7 !
2020-02-20 22:00:28 +02:00
Lauri Ojansivu cbbb5deff7 Update to Meteor 1.9.1, Node 12.16.1 etc newest dependencies. 2020-02-20 20:46:25 +02:00
Lauri Ojansivu 4377be8aa4 Merge branch 'master' of github.com:wekan/wekan 2020-02-20 20:20:11 +02:00
Lauri Ojansivu 26010476b8 Update translations. 2020-02-20 20:19:42 +02:00
Lauri Ojansivu aac27d4db3 Merge pull request #2937 from pbek/patch-1
Fix docker-compose link
2020-02-20 17:13:55 +02:00
Patrizio Bekerle 9762030920 Fix docker-compose link 2020-02-20 15:25:05 +01:00
Lauri Ojansivu cb1ffea5d6 Update translations. 2020-02-19 10:36:12 +02:00
Lauri Ojansivu a4ff6cc0af Update Sandstorm release build script. 2020-02-13 20:04:18 -05:00
Lauri Ojansivu 84acc65d04 v3.79 2020-02-13 15:16:07 +02:00
Lauri Ojansivu 2b1727c6e1 Update translations. 2020-02-13 15:12:46 +02:00
Lauri Ojansivu 178f376e21 Fix Card Opened Webhook can not be disabled.
Thanks to mvanvoorden and xet7 !

Closes #2932,
closes #2889,
closes #2666
2020-02-13 14:58:19 +02:00
Lauri Ojansivu 92538a6ac3 Update translations. 2020-02-13 14:34:53 +02:00
Lauri Ojansivu 3fcde252f7 v3.78 2020-02-12 02:23:26 +02:00
Lauri Ojansivu e89965f642 Remove card element grouping to create compact card layout.
Card Settings / Show on Card: Description Title and Description Text.

Thanks to e-stoniauk, 2020product and xet7 !

Fixes https://github.com/wekan/wekan/pull/2922
2020-02-12 02:08:29 +02:00
Lauri Ojansivu b4e5583065 Update translations. 2020-02-12 02:06:26 +02:00
Lauri Ojansivu 65b11ce3bd v3.77 2020-02-10 20:45:02 +02:00
Lauri Ojansivu ea74e6ad23 Update ChangeLog. 2020-02-10 19:39:30 +02:00
Lauri Ojansivu 4467a68b97 Part 2: Fix Copy Card Link to Clipboard button at card title did not work.
Thanks to 2020product and xet7 !

Related https://github.com/wekan/wekan/pull/2922
2020-02-10 19:37:25 +02:00
Lauri Ojansivu 799bf5735b Update translations. 2020-02-10 19:14:25 +02:00
Lauri Ojansivu 92d0385911 Update ChangeLog. 2020-02-10 19:04:03 +02:00
Lauri Ojansivu 9a21b0a1c9 Fix Copy Card Link to Clipboard button at card title did not work.
Thanks to 2020product and xet7 !

Related https://github.com/wekan/wekan/pull/2922
2020-02-10 18:47:03 +02:00
Lauri Ojansivu 6eb21faca2 Update ChangeLog. 2020-02-10 17:27:18 +02:00
Lauri Ojansivu 2a54218f3f Remove hiding comments and activities. 2020-02-10 17:25:27 +02:00
Lauri Ojansivu 6eac38bde5 Update translations. 2020-02-07 14:05:39 +02:00
Lauri Ojansivu 947c1f81ee Update ChangeLog. 2020-02-07 14:03:57 +02:00
Lauri Ojansivu 2fce02afbc Add Feature: Card Settings/Show on card/Activities.
Fix: When in Card Settings hiding Comments, only adding new comment is hidden, not old comments.

Thanks to xet7 !

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

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

Thanks to 2020product xet7 !

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

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

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

Closes #2218
2020-01-06 11:01:38 +02:00
366 changed files with 19731 additions and 18954 deletions

No files matched your search

+1
View File
@@ -1 +1,2 @@
packages/*
.snap-meteor-1.8/*
+2
View File
@@ -2,6 +2,8 @@
Add these issues to elsewhere:
- Snap: https://github.com/wekan/wekan-snap/issues
- LDAP: https://github.com/wekan/wekan-ldap/issues
- UCS: https://github.com/wekan/univention/issues
Other Wekan issues can be added here.
+10 -9
View File
@@ -6,10 +6,11 @@
meteor-base@1.4.0
# Build system
ecmascript@0.13.2
standard-minifier-css@1.5.4
standard-minifier-js@2.5.2
ecmascript@0.14.2
standard-minifier-css@1.6.0
standard-minifier-js@2.6.0
mquandalle:jade
coffeescript@2.4.1!
# Polyfills
es5-shim@4.8.0
@@ -22,7 +23,7 @@ dburles:collection-helpers
idmontie:migrations
matb33:collection-hooks
matteodem:easy-search
mongo@1.7.0
mongo@1.9.0
mquandalle:collection-mutations
# Account system
@@ -37,13 +38,12 @@ wekan-accounts-oidc
# Utilities
check@1.3.1
jquery@1.11.10
random@1.1.0
random@1.2.0
reactive-dict@1.3.0
session@1.2.0
tracker@1.2.0
underscore@1.0.10
3stack:presence
alethes:pages
arillo:flow-router-helpers
audit-argument-checks@1.0.7
kadira:blaze-layout
@@ -67,7 +67,7 @@ templates:tabs
verron:autosize
simple:json-routes
rajit:bootstrap3-datepicker
shell-server@0.4.0
shell-server@0.5.0
simple:rest-accounts-password
useraccounts:core
email@1.2.3
@@ -75,7 +75,7 @@ horka:swipebox
dynamic-import@0.5.1
staringatlights:fast-render
accounts-password@1.5.2
accounts-password@1.6.0
cfs:gridfs
rzymek:fullcalendar
momentjs:moment@2.22.2
@@ -85,7 +85,8 @@ msavin:usercache
wekan-scrollbar
mquandalle:perfect-scrollbar
mdg:meteor-apm-agent@3.2.0-rc.0!
coagmano:stylus
# Keep stylus in 1.1.0, because building v2 takes extra 52 minutes.
coagmano:stylus@1.1.0!
lucasantoniassi:accounts-lockout
meteorhacks:subs-manager
meteorhacks:picker
+1 -1
View File
@@ -1 +1 @@
METEOR@1.8.3
METEOR@1.10.1
+34 -34
View File
@@ -1,29 +1,28 @@
3stack:presence@1.1.2
accounts-base@1.4.5
accounts-oauth@1.1.16
accounts-password@1.5.2
accounts-base@1.6.0
accounts-oauth@1.2.0
accounts-password@1.6.0
aldeed:collection2@2.10.0
aldeed:collection2-core@1.2.0
aldeed:schema-deny@1.1.0
aldeed:schema-index@1.1.1
aldeed:simple-schema@1.5.4
alethes:pages@1.8.6
allow-deny@1.1.0
arillo:flow-router-helpers@0.5.2
audit-argument-checks@1.0.7
autoupdate@1.6.0
babel-compiler@7.4.2
babel-runtime@1.4.0
babel-compiler@7.5.2
babel-runtime@1.5.0
base64@1.0.12
binary-heap@1.0.11
blaze@2.3.4
blaze-tools@1.0.10
boilerplate-generator@1.6.0
boilerplate-generator@1.7.0
browser-policy-common@1.0.11
browser-policy-framing@1.1.0
caching-compiler@1.2.1
caching-html-compiler@1.1.3
callback-hook@1.2.0
callback-hook@1.3.0
cfs:access-point@0.1.49
cfs:base-package@0.0.30
cfs:collection@0.5.5
@@ -44,23 +43,24 @@ cfs:upload-http@0.0.20
cfs:worker@0.1.5
check@1.3.1
chuangbo:cookie@1.1.0
coagmano:stylus@2.0.0
coffeescript@1.0.17
coagmano:stylus@1.1.0
coffeescript@2.4.1
coffeescript-compiler@2.4.1
cottz:publish-relations@2.0.8
dburles:collection-helpers@1.1.0
ddp@1.4.0
ddp-client@2.3.3
ddp-common@1.4.0
ddp-rate-limiter@1.0.7
ddp-server@2.3.0
ddp-server@2.3.1
deps@1.0.12
diff-sequence@1.1.1
dynamic-import@0.5.1
easylogic:summernote@0.8.8
ecmascript@0.13.2
ecmascript@0.14.2
ecmascript-runtime@0.7.0
ecmascript-runtime-client@0.9.0
ecmascript-runtime-server@0.8.0
ecmascript-runtime-client@0.10.0
ecmascript-runtime-server@0.9.0
ejson@1.1.1
email@1.2.3
es5-shim@4.8.0
@@ -75,7 +75,7 @@ htmljs@1.0.11
http@1.4.2
id-map@1.1.0
idmontie:migrations@1.0.3
inter-process-messaging@0.1.0
inter-process-messaging@0.1.1
jquery@1.11.11
kadira:blaze-layout@2.3.0
kadira:dochead@1.5.0
@@ -84,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.1.1
launch-screen@1.2.0
livedata@1.0.18
localstorage@1.2.0
logging@1.1.20
@@ -101,16 +101,16 @@ meteorhacks:collection-utils@1.2.0
meteorhacks:picker@1.0.3
meteorhacks:subs-manager@1.6.4
meteorspark:util@0.2.0
minifier-css@1.4.3
minifier-js@2.5.1
minifier-css@1.5.0
minifier-js@2.6.0
minifiers@1.1.8-faster-rebuild.0
minimongo@1.4.5
mobile-status-bar@1.0.14
modern-browsers@0.1.4
modules@0.14.0
modules-runtime@0.11.0
minimongo@1.5.0
mobile-status-bar@1.1.0
modern-browsers@0.1.5
modules@0.15.0
modules-runtime@0.12.0
momentjs:moment@2.24.0
mongo@1.7.0
mongo@1.9.0
mongo-decimal@0.1.1
mongo-dev-server@1.1.0
mongo-id@1.0.7
@@ -127,13 +127,13 @@ mquandalle:mousetrap-bindglobal@0.0.1
mquandalle:perfect-scrollbar@0.6.5_2
msavin:usercache@1.8.0
npm-bcrypt@0.9.3
npm-mongo@3.2.0
oauth@1.2.8
oauth2@1.2.1
npm-mongo@3.7.0
oauth@1.3.0
oauth2@1.3.0
observe-sequence@1.0.16
ongoworks:speakingurl@1.1.0
ordered-dict@1.1.0
ostrio:cookies@2.5.0
ostrio:cookies@2.6.0
peerlibrary:assert@0.3.0
peerlibrary:base-component@0.16.0
peerlibrary:blaze-components@0.15.1
@@ -144,7 +144,7 @@ promise@0.11.2
raix:eventemitter@0.1.3
raix:handlebar-helpers@0.2.5
rajit:bootstrap3-datepicker@1.7.1_1
random@1.1.0
random@1.2.0
rate-limit@1.0.9
reactive-dict@1.3.0
reactive-var@1.0.11
@@ -156,19 +156,19 @@ server-render@0.3.1
service-configuration@1.0.11
session@1.2.0
sha@1.0.9
shell-server@0.4.0
shell-server@0.5.0
simple:authenticate-user-by-token@1.0.1
simple:json-routes@2.1.0
simple:rest-accounts-password@1.1.2
simple:rest-bearer-token-parser@1.0.1
simple:rest-json-error-handler@1.0.1
socket-stream-client@0.2.2
socket-stream-client@0.2.3
softwarerero:accounts-t9n@1.3.11
spacebars@1.0.15
spacebars-compiler@1.1.3
srp@1.0.12
standard-minifier-css@1.5.4
standard-minifier-js@2.5.2
standard-minifier-css@1.6.0
standard-minifier-js@2.6.0
staringatlights:fast-render@3.2.0
staringatlights:inject-data@2.3.0
tap:i18n@1.8.2
@@ -186,7 +186,7 @@ useraccounts:core@1.14.2
useraccounts:flow-routing@1.14.2
useraccounts:unstyled@1.14.2
verron:autosize@3.0.8
webapp@1.7.5
webapp@1.9.0
webapp-hashing@1.0.9
wekan-accounts-cas@0.1.0
wekan-accounts-oidc@1.0.10
+1
View File
@@ -5,3 +5,4 @@ node_modules/
.vscode/
.tx/
.github/
.snap-meteor-1.8/
@@ -0,0 +1,20 @@
# This file contains information which helps Meteor properly upgrade your
# app when you run 'meteor update'. You should check it into version control
# with your project.
notices-for-0.9.0
notices-for-0.9.1
0.9.4-platform-file
notices-for-facebook-graph-api-2
1.2.0-standard-minifiers-package
1.2.0-meteor-platform-split
1.2.0-cordova-changes
1.2.0-breaking-changes
1.3.0-split-minifiers-package
1.3.5-remove-old-dev-bundle-link
1.4.0-remove-old-dev-bundle-link
1.4.1-add-shell-server-package
1.4.3-split-account-service-packages
1.5-add-dynamic-import-package
1.7-split-underscore-from-meteor-base
1.8.3-split-jquery-from-blaze
+2
View File
@@ -0,0 +1,2 @@
dev_bundle
local
+7
View File
@@ -0,0 +1,7 @@
# This file contains a token that is unique to your project.
# Check it into your repository along with the rest of this directory.
# It can be used for purposes such as:
# - ensuring you don't accidentally deploy one app on top of another
# - providing package authors with aggregated statistics
dvyihgykyzec6y1dpg
Whitespace-only changes.
+100
View File
@@ -0,0 +1,100 @@
# Meteor packages used by this project, one per line.
#
# 'meteor add' and 'meteor remove' will edit this file for you,
# but you can also edit it by hand.
meteor-base@1.4.0
# Build system
ecmascript@0.13.2
standard-minifier-css@1.5.4
standard-minifier-js@2.5.2
mquandalle:jade
# Polyfills
es5-shim@4.8.0
# Collections
aldeed:collection2
cfs:standard-packages
cottz:publish-relations
dburles:collection-helpers
idmontie:migrations
matb33:collection-hooks
matteodem:easy-search
mongo@1.7.0
mquandalle:collection-mutations
# Account system
kenton:accounts-sandstorm
service-configuration@1.0.11
useraccounts:unstyled
useraccounts:flow-routing
wekan-ldap
wekan-accounts-cas
wekan-accounts-oidc
# Utilities
check@1.3.1
jquery@1.11.10
random@1.1.0
reactive-dict@1.3.0
session@1.2.0
tracker@1.2.0
underscore@1.0.10
3stack:presence
alethes:pages
arillo:flow-router-helpers
audit-argument-checks@1.0.7
kadira:blaze-layout
kadira:dochead
mquandalle:autofocus
ongoworks:speakingurl
raix:handlebar-helpers
tap:i18n
http@1.4.2
# UI components
blaze
reactive-var@1.0.11
fortawesome:fontawesome
mousetrap:mousetrap
mquandalle:jquery-textcomplete
mquandalle:jquery-ui-drag-drop-sort
mquandalle:mousetrap-bindglobal
peerlibrary:blaze-components@=0.15.1
templates:tabs
verron:autosize
simple:json-routes
rajit:bootstrap3-datepicker
shell-server@0.4.0
simple:rest-accounts-password
useraccounts:core
email@1.2.3
horka:swipebox
dynamic-import@0.5.1
staringatlights:fast-render
accounts-password@1.5.2
cfs:gridfs
rzymek:fullcalendar
momentjs:moment@2.22.2
browser-policy-framing@1.1.0
mquandalle:moment
msavin:usercache
wekan-scrollbar
mquandalle:perfect-scrollbar
mdg:meteor-apm-agent@3.2.0-rc.0!
# Keep stylus in 1.1.0, because building v2 takes extra 52 minutes.
coagmano:stylus@1.1.0!
lucasantoniassi:accounts-lockout
meteorhacks:subs-manager
meteorhacks:picker
lamhieu:unblock
meteorhacks:aggregate@1.3.0
wekan-markdown
konecty:mongo-counter
percolate:synced-cron
easylogic:summernote
cfs:filesystem
ostrio:cookies
+2
View File
@@ -0,0 +1,2 @@
server
browser
+1
View File
@@ -0,0 +1 @@
METEOR@1.8.3
+198
View File
@@ -0,0 +1,198 @@
3stack:presence@1.1.2
accounts-base@1.4.5
accounts-oauth@1.1.16
accounts-password@1.5.2
aldeed:collection2@2.10.0
aldeed:collection2-core@1.2.0
aldeed:schema-deny@1.1.0
aldeed:schema-index@1.1.1
aldeed:simple-schema@1.5.4
alethes:pages@1.8.6
allow-deny@1.1.0
arillo:flow-router-helpers@0.5.2
audit-argument-checks@1.0.7
autoupdate@1.6.0
babel-compiler@7.4.2
babel-runtime@1.4.0
base64@1.0.12
binary-heap@1.0.11
blaze@2.3.4
blaze-tools@1.0.10
boilerplate-generator@1.6.0
browser-policy-common@1.0.11
browser-policy-framing@1.1.0
caching-compiler@1.2.1
caching-html-compiler@1.1.3
callback-hook@1.2.0
cfs:access-point@0.1.49
cfs:base-package@0.0.30
cfs:collection@0.5.5
cfs:collection-filters@0.2.4
cfs:data-man@0.0.6
cfs:file@0.1.17
cfs:filesystem@0.1.2
cfs:gridfs@0.0.34
cfs:http-methods@0.0.32
cfs:http-publish@0.0.13
cfs:power-queue@0.9.11
cfs:reactive-list@0.0.9
cfs:reactive-property@0.0.4
cfs:standard-packages@0.5.10
cfs:storage-adapter@0.2.4
cfs:tempstore@0.1.6
cfs:upload-http@0.0.20
cfs:worker@0.1.5
check@1.3.1
chuangbo:cookie@1.1.0
coagmano:stylus@1.1.0
coffeescript@1.0.17
cottz:publish-relations@2.0.8
dburles:collection-helpers@1.1.0
ddp@1.4.0
ddp-client@2.3.3
ddp-common@1.4.0
ddp-rate-limiter@1.0.7
ddp-server@2.3.0
deps@1.0.12
diff-sequence@1.1.1
dynamic-import@0.5.1
easylogic:summernote@0.8.8
ecmascript@0.13.2
ecmascript-runtime@0.7.0
ecmascript-runtime-client@0.9.0
ecmascript-runtime-server@0.8.0
ejson@1.1.1
email@1.2.3
es5-shim@4.8.0
fastclick@1.0.13
fetch@0.1.1
fortawesome:fontawesome@4.7.0
geojson-utils@1.0.10
horka:swipebox@1.0.2
hot-code-push@1.0.4
html-tools@1.0.11
htmljs@1.0.11
http@1.4.2
id-map@1.1.0
idmontie:migrations@1.0.3
inter-process-messaging@0.1.0
jquery@1.11.11
kadira:blaze-layout@2.3.0
kadira:dochead@1.5.0
kadira:flow-router@2.12.1
kenton:accounts-sandstorm@0.7.0
konecty:mongo-counter@0.0.5_3
lamhieu:meteorx@2.1.1
lamhieu:unblock@1.0.0
launch-screen@1.1.1
livedata@1.0.18
localstorage@1.2.0
logging@1.1.20
lucasantoniassi:accounts-lockout@1.0.0
matb33:collection-hooks@0.9.1
matteodem:easy-search@1.6.4
mdg:meteor-apm-agent@3.2.5
mdg:validation-error@0.5.1
meteor@1.9.3
meteor-base@1.4.0
meteor-platform@1.2.6
meteorhacks:aggregate@1.3.0
meteorhacks:collection-utils@1.2.0
meteorhacks:picker@1.0.3
meteorhacks:subs-manager@1.6.4
meteorspark:util@0.2.0
minifier-css@1.4.3
minifier-js@2.5.1
minifiers@1.1.8-faster-rebuild.0
minimongo@1.4.5
mobile-status-bar@1.0.14
modern-browsers@0.1.4
modules@0.14.0
modules-runtime@0.11.0
momentjs:moment@2.24.0
mongo@1.7.0
mongo-decimal@0.1.1
mongo-dev-server@1.1.0
mongo-id@1.0.7
mongo-livedata@1.0.12
mousetrap:mousetrap@1.4.6_1
mquandalle:autofocus@1.0.0
mquandalle:collection-mutations@0.1.0
mquandalle:jade@0.4.9
mquandalle:jade-compiler@0.4.5
mquandalle:jquery-textcomplete@0.8.0_1
mquandalle:jquery-ui-drag-drop-sort@0.2.0
mquandalle:moment@1.0.1
mquandalle:mousetrap-bindglobal@0.0.1
mquandalle:perfect-scrollbar@0.6.5_2
msavin:usercache@1.8.0
npm-bcrypt@0.9.3
npm-mongo@3.2.0
oauth@1.2.8
oauth2@1.2.1
observe-sequence@1.0.16
ongoworks:speakingurl@1.1.0
ordered-dict@1.1.0
ostrio:cookies@2.5.0
peerlibrary:assert@0.3.0
peerlibrary:base-component@0.16.0
peerlibrary:blaze-components@0.15.1
peerlibrary:computed-field@0.10.0
peerlibrary:reactive-field@0.6.0
percolate:synced-cron@1.3.2
promise@0.11.2
raix:eventemitter@0.1.3
raix:handlebar-helpers@0.2.5
rajit:bootstrap3-datepicker@1.7.1_1
random@1.1.0
rate-limit@1.0.9
reactive-dict@1.3.0
reactive-var@1.0.11
reload@1.3.0
retry@1.1.0
routepolicy@1.1.0
rzymek:fullcalendar@3.8.0
server-render@0.3.1
service-configuration@1.0.11
session@1.2.0
sha@1.0.9
shell-server@0.4.0
simple:authenticate-user-by-token@1.0.1
simple:json-routes@2.1.0
simple:rest-accounts-password@1.1.2
simple:rest-bearer-token-parser@1.0.1
simple:rest-json-error-handler@1.0.1
socket-stream-client@0.2.2
softwarerero:accounts-t9n@1.3.11
spacebars@1.0.15
spacebars-compiler@1.1.3
srp@1.0.12
standard-minifier-css@1.5.4
standard-minifier-js@2.5.2
staringatlights:fast-render@3.2.0
staringatlights:inject-data@2.3.0
tap:i18n@1.8.2
templates:tabs@2.3.0
templating@1.3.2
templating-compiler@1.3.3
templating-runtime@1.3.2
templating-tools@1.1.2
tracker@1.2.0
twbs:bootstrap@3.3.6
ui@1.0.13
underscore@1.0.10
url@1.2.0
useraccounts:core@1.14.2
useraccounts:flow-routing@1.14.2
useraccounts:unstyled@1.14.2
verron:autosize@3.0.8
webapp@1.7.5
webapp-hashing@1.0.9
wekan-accounts-cas@0.1.0
wekan-accounts-oidc@1.0.10
wekan-ldap@0.0.2
wekan-markdown@1.0.7
wekan-oidc@1.0.12
wekan-scrollbar@3.1.3
yasaricli:slugify@0.0.7
zimme:active-route@2.3.2
+914
View File
@@ -0,0 +1,914 @@
(function () {
/* Imports */
var Meteor = Package.meteor.Meteor;
var global = Package.meteor.global;
var meteorEnv = Package.meteor.meteorEnv;
var FS = Package['cfs:base-package'].FS;
var check = Package.check.check;
var Match = Package.check.Match;
var EJSON = Package.ejson.EJSON;
var HTTP = Package['cfs:http-methods'].HTTP;
/* Package-scope variables */
var rootUrlPathPrefix, baseUrl, getHeaders, getHeadersByCollection, _existingMountPoints, mountUrls;
(function(){
///////////////////////////////////////////////////////////////////////
// //
// packages/cfs_access-point/packages/cfs_access-point.js //
// //
///////////////////////////////////////////////////////////////////////
//
(function () {
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// packages/cfs:access-point/access-point-common.js //
// //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
rootUrlPathPrefix = __meteor_runtime_config__.ROOT_URL_PATH_PREFIX || ""; // 1
// Adjust the rootUrlPathPrefix if necessary // 2
if (rootUrlPathPrefix.length > 0) { // 3
if (rootUrlPathPrefix.slice(0, 1) !== '/') { // 4
rootUrlPathPrefix = '/' + rootUrlPathPrefix; // 5
} // 6
if (rootUrlPathPrefix.slice(-1) === '/') { // 7
rootUrlPathPrefix = rootUrlPathPrefix.slice(0, -1); // 8
} // 9
} // 10
// 11
// prepend ROOT_URL when isCordova // 12
if (Meteor.isCordova) { // 13
rootUrlPathPrefix = Meteor.absoluteUrl(rootUrlPathPrefix.replace(/^\/+/, '')).replace(/\/+$/, ''); // 14
} // 15
// 16
baseUrl = '/cfs'; // 17
FS.HTTP = FS.HTTP || {}; // 18
// 19
// Note the upload URL so that client uploader packages know what it is // 20
FS.HTTP.uploadUrl = rootUrlPathPrefix + baseUrl + '/files'; // 21
// 22
/** // 23
* @method FS.HTTP.setBaseUrl // 24
* @public // 25
* @param {String} newBaseUrl - Change the base URL for the HTTP GET and DELETE endpoints. // 26
* @returns {undefined} // 27
*/ // 28
FS.HTTP.setBaseUrl = function setBaseUrl(newBaseUrl) { // 29
// 30
// Adjust the baseUrl if necessary // 31
if (newBaseUrl.slice(0, 1) !== '/') { // 32
newBaseUrl = '/' + newBaseUrl; // 33
} // 34
if (newBaseUrl.slice(-1) === '/') { // 35
newBaseUrl = newBaseUrl.slice(0, -1); // 36
} // 37
// 38
// Update the base URL // 39
baseUrl = newBaseUrl; // 40
// 41
// Change the upload URL so that client uploader packages know what it is // 42
FS.HTTP.uploadUrl = rootUrlPathPrefix + baseUrl + '/files'; // 43
// 44
// Remount URLs with the new baseUrl, unmounting the old, on the server only. // 45
// If existingMountPoints is empty, then we haven't run the server startup // 46
// code yet, so this new URL will be used at that point for the initial mount. // 47
if (Meteor.isServer && !FS.Utility.isEmpty(_existingMountPoints)) { // 48
mountUrls(); // 49
} // 50
}; // 51
// 52
/* // 53
* FS.File extensions // 54
*/ // 55
// 56
/** // 57
* @method FS.File.prototype.url Construct the file url // 58
* @public // 59
* @param {Object} [options] // 60
* @param {String} [options.store] Name of the store to get from. If not defined, the first store defined in `options.stores` for the collection on the client is used.
* @param {Boolean} [options.auth=null] Add authentication token to the URL query string? By default, a token for the current logged in user is added on the client. Set this to `false` to omit the token. Set this to a string to provide your own token. Set this to a number to specify an expiration time for the token in seconds.
* @param {Boolean} [options.download=false] Should headers be set to force a download? Typically this means that clicking the link with this URL will download the file to the user's Downloads folder instead of displaying the file in the browser.
* @param {Boolean} [options.brokenIsFine=false] Return the URL even if we know it's currently a broken link because the file hasn't been saved in the requested store yet.
* @param {Boolean} [options.metadata=false] Return the URL for the file metadata access point rather than the file itself.
* @param {String} [options.uploading=null] A URL to return while the file is being uploaded. // 66
* @param {String} [options.storing=null] A URL to return while the file is being stored. // 67
* @param {String} [options.filename=null] Override the filename that should appear at the end of the URL. By default it is the name of the file in the requested store.
* // 69
* Returns the HTTP URL for getting the file or its metadata. // 70
*/ // 71
FS.File.prototype.url = function(options) { // 72
var self = this; // 73
options = options || {}; // 74
options = FS.Utility.extend({ // 75
store: null, // 76
auth: null, // 77
download: false, // 78
metadata: false, // 79
brokenIsFine: false, // 80
uploading: null, // return this URL while uploading // 81
storing: null, // return this URL while storing // 82
filename: null // override the filename that is shown to the user // 83
}, options.hash || options); // check for "hash" prop if called as helper // 84
// 85
// Primarily useful for displaying a temporary image while uploading an image // 86
if (options.uploading && !self.isUploaded()) { // 87
return options.uploading; // 88
} // 89
// 90
if (self.isMounted()) { // 91
// See if we've stored in the requested store yet // 92
var storeName = options.store || self.collection.primaryStore.name; // 93
if (!self.hasStored(storeName)) { // 94
if (options.storing) { // 95
return options.storing; // 96
} else if (!options.brokenIsFine) { // 97
// We want to return null if we know the URL will be a broken // 98
// link because then we can avoid rendering broken links, broken // 99
// images, etc. // 100
return null; // 101
} // 102
} // 103
// 104
// Add filename to end of URL if we can determine one // 105
var filename = options.filename || self.name({store: storeName}); // 106
if (typeof filename === "string" && filename.length) { // 107
filename = '/' + filename; // 108
} else { // 109
filename = ''; // 110
} // 111
// 112
// TODO: Could we somehow figure out if the collection requires login? // 113
var authToken = ''; // 114
if (Meteor.isClient && typeof Accounts !== "undefined" && typeof Accounts._storedLoginToken === "function") { // 115
if (options.auth !== false) { // 116
// Add reactive deps on the user // 117
Meteor.userId(); // 118
// 119
var authObject = { // 120
authToken: Accounts._storedLoginToken() || '' // 121
}; // 122
// 123
// If it's a number, we use that as the expiration time (in seconds) // 124
if (options.auth === +options.auth) { // 125
authObject.expiration = FS.HTTP.now() + options.auth * 1000; // 126
} // 127
// 128
// Set the authToken // 129
var authString = JSON.stringify(authObject); // 130
authToken = FS.Utility.btoa(authString); // 131
} // 132
} else if (typeof options.auth === "string") { // 133
// If the user supplies auth token the user will be responsible for // 134
// updating // 135
authToken = options.auth; // 136
} // 137
// 138
// Construct query string // 139
var params = {}; // 140
if (authToken !== '') { // 141
params.token = authToken; // 142
} // 143
if (options.download) { // 144
params.download = true; // 145
} // 146
if (options.store) { // 147
// We use options.store here instead of storeName because we want to omit the queryString // 148
// whenever possible, allowing users to have "clean" URLs if they want. The server will // 149
// assume the first store defined on the server, which means that we are assuming that // 150
// the first on the client is also the first on the server. If that's not the case, the // 151
// store option should be supplied. // 152
params.store = options.store; // 153
} // 154
var queryString = FS.Utility.encodeParams(params); // 155
if (queryString.length) { // 156
queryString = '?' + queryString; // 157
} // 158
// 159
// Determine which URL to use // 160
var area; // 161
if (options.metadata) { // 162
area = '/record'; // 163
} else { // 164
area = '/files'; // 165
} // 166
// 167
// Construct and return the http method url // 168
return rootUrlPathPrefix + baseUrl + area + '/' + self.collection.name + '/' + self._id + filename + queryString; // 169
} // 170
// 171
}; // 172
// 173
// 174
// 175
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}).call(this);
(function () {
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// packages/cfs:access-point/access-point-handlers.js //
// //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
getHeaders = []; // 1
getHeadersByCollection = {}; // 2
// 3
FS.HTTP.Handlers = {}; // 4
// 5
/** // 6
* @method FS.HTTP.Handlers.Del // 7
* @public // 8
* @returns {any} response // 9
* // 10
* HTTP DEL request handler // 11
*/ // 12
FS.HTTP.Handlers.Del = function httpDelHandler(ref) { // 13
var self = this; // 14
var opts = FS.Utility.extend({}, self.query || {}, self.params || {}); // 15
// 16
// If DELETE request, validate with 'remove' allow/deny, delete the file, and return // 17
FS.Utility.validateAction(ref.collection.files._validators['remove'], ref.file, self.userId); // 18
// 19
/* // 20
* From the DELETE spec: // 21
* A successful response SHOULD be 200 (OK) if the response includes an // 22
* entity describing the status, 202 (Accepted) if the action has not // 23
* yet been enacted, or 204 (No Content) if the action has been enacted // 24
* but the response does not include an entity. // 25
*/ // 26
self.setStatusCode(200); // 27
// 28
return { // 29
deleted: !!ref.file.remove() // 30
}; // 31
}; // 32
// 33
/** // 34
* @method FS.HTTP.Handlers.GetList // 35
* @public // 36
* @returns {Object} response // 37
* // 38
* HTTP GET file list request handler // 39
*/ // 40
FS.HTTP.Handlers.GetList = function httpGetListHandler() { // 41
// Not Yet Implemented // 42
// Need to check publications and return file list based on // 43
// what user is allowed to see // 44
}; // 45
// 46
/* // 47
requestRange will parse the range set in request header - if not possible it // 48
will throw fitting errors and autofill range for both partial and full ranges // 49
// 50
throws error or returns the object: // 51
{ // 52
start // 53
end // 54
length // 55
unit // 56
partial // 57
} // 58
*/ // 59
var requestRange = function(req, fileSize) { // 60
if (req) { // 61
if (req.headers) { // 62
var rangeString = req.headers.range; // 63
// 64
// Make sure range is a string // 65
if (rangeString === ''+rangeString) { // 66
// 67
// range will be in the format "bytes=0-32767" // 68
var parts = rangeString.split('='); // 69
var unit = parts[0]; // 70
// 71
// Make sure parts consists of two strings and range is of type "byte" // 72
if (parts.length == 2 && unit == 'bytes') { // 73
// Parse the range // 74
var range = parts[1].split('-'); // 75
var start = Number(range[0]); // 76
var end = Number(range[1]); // 77
// 78
// Fix invalid ranges? // 79
if (range[0] != start) start = 0; // 80
if (range[1] != end || !end) end = fileSize - 1; // 81
// 82
// Make sure range consists of a start and end point of numbers and start is less than end // 83
if (start < end) { // 84
// 85
var partSize = 0 - start + end + 1; // 86
// 87
// Return the parsed range // 88
return { // 89
start: start, // 90
end: end, // 91
length: partSize, // 92
size: fileSize, // 93
unit: unit, // 94
partial: (partSize < fileSize) // 95
}; // 96
// 97
} else { // 98
throw new Meteor.Error(416, "Requested Range Not Satisfiable"); // 99
} // 100
// 101
} else { // 102
// The first part should be bytes // 103
throw new Meteor.Error(416, "Requested Range Unit Not Satisfiable"); // 104
} // 105
// 106
} else { // 107
// No range found // 108
} // 109
// 110
} else { // 111
// throw new Error('No request headers set for _parseRange function'); // 112
} // 113
} else { // 114
throw new Error('No request object passed to _parseRange function'); // 115
} // 116
// 117
return { // 118
start: 0, // 119
end: fileSize - 1, // 120
length: fileSize, // 121
size: fileSize, // 122
unit: 'bytes', // 123
partial: false // 124
}; // 125
}; // 126
// 127
/** // 128
* @method FS.HTTP.Handlers.Get // 129
* @public // 130
* @returns {any} response // 131
* // 132
* HTTP GET request handler // 133
*/ // 134
FS.HTTP.Handlers.Get = function httpGetHandler(ref) { // 135
var self = this; // 136
// Once we have the file, we can test allow/deny validators // 137
// XXX: pass on the "share" query eg. ?share=342hkjh23ggj for shared url access? // 138
FS.Utility.validateAction(ref.collection._validators['download'], ref.file, self.userId /*, self.query.shareId*/); // 139
// 140
var storeName = ref.storeName; // 141
// 142
// If no storeName was specified, use the first defined storeName // 143
if (typeof storeName !== "string") { // 144
// No store handed, we default to primary store // 145
storeName = ref.collection.primaryStore.name; // 146
} // 147
// 148
// Get the storage reference // 149
var storage = ref.collection.storesLookup[storeName]; // 150
// 151
if (!storage) { // 152
throw new Meteor.Error(404, "Not Found", 'There is no store "' + storeName + '"'); // 153
} // 154
// 155
// Get the file // 156
var copyInfo = ref.file.copies[storeName]; // 157
// 158
if (!copyInfo) { // 159
throw new Meteor.Error(404, "Not Found", 'This file was not stored in the ' + storeName + ' store'); // 160
} // 161
// 162
// Set the content type for file // 163
if (typeof copyInfo.type === "string") { // 164
self.setContentType(copyInfo.type); // 165
} else { // 166
self.setContentType('application/octet-stream'); // 167
} // 168
// 169
// Add 'Content-Disposition' header if requested a download/attachment URL // 170
if (typeof ref.download !== "undefined") { // 171
var filename = ref.filename || copyInfo.name; // 172
self.addHeader('Content-Disposition', 'attachment; filename="' + filename + '"'); // 173
} else { // 174
self.addHeader('Content-Disposition', 'inline'); // 175
} // 176
// 177
// Get the contents range from request // 178
var range = requestRange(self.request, copyInfo.size); // 179
// 180
// Some browsers cope better if the content-range header is // 181
// still included even for the full file being returned. // 182
self.addHeader('Content-Range', range.unit + ' ' + range.start + '-' + range.end + '/' + range.size); // 183
// 184
// If a chunk/range was requested instead of the whole file, serve that' // 185
if (range.partial) { // 186
self.setStatusCode(206, 'Partial Content'); // 187
} else { // 188
self.setStatusCode(200, 'OK'); // 189
} // 190
// 191
// Add any other global custom headers and collection-specific custom headers // 192
FS.Utility.each(getHeaders.concat(getHeadersByCollection[ref.collection.name] || []), function(header) { // 193
self.addHeader(header[0], header[1]); // 194
}); // 195
// 196
// Inform clients about length (or chunk length in case of ranges) // 197
self.addHeader('Content-Length', range.length); // 198
// 199
// Last modified header (updatedAt from file info) // 200
self.addHeader('Last-Modified', copyInfo.updatedAt.toUTCString()); // 201
// 202
// Inform clients that we accept ranges for resumable chunked downloads // 203
self.addHeader('Accept-Ranges', range.unit); // 204
// 205
if (FS.debug) console.log('Read file "' + (ref.filename || copyInfo.name) + '" ' + range.unit + ' ' + range.start + '-' + range.end + '/' + range.size);
// 207
var readStream = storage.adapter.createReadStream(ref.file, {start: range.start, end: range.end}); // 208
// 209
readStream.on('error', function(err) { // 210
// Send proper error message on get error // 211
if (err.message && err.statusCode) { // 212
self.Error(new Meteor.Error(err.statusCode, err.message)); // 213
} else { // 214
self.Error(new Meteor.Error(503, 'Service unavailable')); // 215
} // 216
}); // 217
// 218
readStream.pipe(self.createWriteStream()); // 219
}; // 220
const originalHandler = FS.HTTP.Handlers.Get;
FS.HTTP.Handlers.Get = function (ref) {
//console.log(ref.filename);
try {
var userAgent = (this.requestHeaders['user-agent']||'').toLowerCase();
if(userAgent.indexOf('msie') >= 0 || userAgent.indexOf('trident') >= 0 || userAgent.indexOf('chrome') >= 0) {
ref.filename = encodeURIComponent(ref.filename);
} else if(userAgent.indexOf('firefox') >= 0) {
ref.filename = new Buffer(ref.filename).toString('binary');
} else {
/* safari*/
ref.filename = new Buffer(ref.filename).toString('binary');
}
} catch (ex){
ref.filename = 'tempfix';
}
return originalHandler.call(this, ref);
};
// 221
/** // 222
* @method FS.HTTP.Handlers.PutInsert // 223
* @public // 224
* @returns {Object} response object with _id property // 225
* // 226
* HTTP PUT file insert request handler // 227
*/ // 228
FS.HTTP.Handlers.PutInsert = function httpPutInsertHandler(ref) { // 229
var self = this; // 230
var opts = FS.Utility.extend({}, self.query || {}, self.params || {}); // 231
// 232
FS.debug && console.log("HTTP PUT (insert) handler"); // 233
// 234
// Create the nice FS.File // 235
var fileObj = new FS.File(); // 236
// 237
// Set its name // 238
fileObj.name(opts.filename || null); // 239
// 240
// Attach the readstream as the file's data // 241
fileObj.attachData(self.createReadStream(), {type: self.requestHeaders['content-type'] || 'application/octet-stream'});
// 243
// Validate with insert allow/deny // 244
FS.Utility.validateAction(ref.collection.files._validators['insert'], fileObj, self.userId); // 245
// 246
// Insert file into collection, triggering readStream storage // 247
ref.collection.insert(fileObj); // 248
// 249
// Send response // 250
self.setStatusCode(200); // 251
// 252
// Return the new file id // 253
return {_id: fileObj._id}; // 254
}; // 255
// 256
/** // 257
* @method FS.HTTP.Handlers.PutUpdate // 258
* @public // 259
* @returns {Object} response object with _id and chunk properties // 260
* // 261
* HTTP PUT file update chunk request handler // 262
*/ // 263
FS.HTTP.Handlers.PutUpdate = function httpPutUpdateHandler(ref) { // 264
var self = this; // 265
var opts = FS.Utility.extend({}, self.query || {}, self.params || {}); // 266
// 267
var chunk = parseInt(opts.chunk, 10); // 268
if (isNaN(chunk)) chunk = 0; // 269
// 270
FS.debug && console.log("HTTP PUT (update) handler received chunk: ", chunk); // 271
// 272
// Validate with insert allow/deny; also mounts and retrieves the file // 273
FS.Utility.validateAction(ref.collection.files._validators['insert'], ref.file, self.userId); // 274
// 275
self.createReadStream().pipe( FS.TempStore.createWriteStream(ref.file, chunk) ); // 276
// 277
// Send response // 278
self.setStatusCode(200); // 279
// 280
return { _id: ref.file._id, chunk: chunk }; // 281
}; // 282
// 283
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}).call(this);
(function () {
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// packages/cfs:access-point/access-point-server.js //
// //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
var path = Npm.require("path"); // 1
// 2
HTTP.publishFormats({ // 3
fileRecordFormat: function (input) { // 4
// Set the method scope content type to json // 5
this.setContentType('application/json'); // 6
if (FS.Utility.isArray(input)) { // 7
return EJSON.stringify(FS.Utility.map(input, function (obj) { // 8
return FS.Utility.cloneFileRecord(obj); // 9
})); // 10
} else { // 11
return EJSON.stringify(FS.Utility.cloneFileRecord(input)); // 12
} // 13
} // 14
}); // 15
// 16
/** // 17
* @method FS.HTTP.setHeadersForGet // 18
* @public // 19
* @param {Array} headers - List of headers, where each is a two-item array in which item 1 is the header name and item 2 is the header value.
* @param {Array|String} [collections] - Which collections the headers should be added for. Omit this argument to add the header for all collections.
* @returns {undefined} // 22
*/ // 23
FS.HTTP.setHeadersForGet = function setHeadersForGet(headers, collections) { // 24
if (typeof collections === "string") { // 25
collections = [collections]; // 26
} // 27
if (collections) { // 28
FS.Utility.each(collections, function(collectionName) { // 29
getHeadersByCollection[collectionName] = headers || []; // 30
}); // 31
} else { // 32
getHeaders = headers || []; // 33
} // 34
}; // 35
// 36
/** // 37
* @method FS.HTTP.publish // 38
* @public // 39
* @param {FS.Collection} collection // 40
* @param {Function} func - Publish function that returns a cursor. // 41
* @returns {undefined} // 42
* // 43
* Publishes all documents returned by the cursor at a GET URL // 44
* with the format baseUrl/record/collectionName. The publish // 45
* function `this` is similar to normal `Meteor.publish`. // 46
*/ // 47
FS.HTTP.publish = function fsHttpPublish(collection, func) { // 48
var name = baseUrl + '/record/' + collection.name; // 49
// Mount collection listing URL using http-publish package // 50
HTTP.publish({ // 51
name: name, // 52
defaultFormat: 'fileRecordFormat', // 53
collection: collection, // 54
collectionGet: true, // 55
collectionPost: false, // 56
documentGet: true, // 57
documentPut: false, // 58
documentDelete: false // 59
}, func); // 60
// 61
FS.debug && console.log("Registered HTTP method GET URLs:\n\n" + name + '\n' + name + '/:id\n'); // 62
}; // 63
// 64
/** // 65
* @method FS.HTTP.unpublish // 66
* @public // 67
* @param {FS.Collection} collection // 68
* @returns {undefined} // 69
* // 70
* Unpublishes a restpoint created by a call to `FS.HTTP.publish` // 71
*/ // 72
FS.HTTP.unpublish = function fsHttpUnpublish(collection) { // 73
// Mount collection listing URL using http-publish package // 74
HTTP.unpublish(baseUrl + '/record/' + collection.name); // 75
}; // 76
// 77
_existingMountPoints = {}; // 78
// 79
/** // 80
* @method defaultSelectorFunction // 81
* @private // 82
* @returns { collection, file } // 83
* // 84
* This is the default selector function // 85
*/ // 86
var defaultSelectorFunction = function() { // 87
var self = this; // 88
// Selector function // 89
// // 90
// This function will have to return the collection and the // 91
// file. If file not found undefined is returned - if null is returned the // 92
// search was not possible // 93
var opts = FS.Utility.extend({}, self.query || {}, self.params || {}); // 94
// 95
// Get the collection name from the url // 96
var collectionName = opts.collectionName; // 97
// 98
// Get the id from the url // 99
var id = opts.id; // 100
// 101
// Get the collection // 102
var collection = FS._collections[collectionName]; // 103
// 104
// Get the file if possible else return null // 105
var file = (id && collection)? collection.findOne({ _id: id }): null; // 106
// 107
// Return the collection and the file // 108
return { // 109
collection: collection, // 110
file: file, // 111
storeName: opts.store, // 112
download: opts.download, // 113
filename: opts.filename // 114
}; // 115
}; // 116
// 117
/* // 118
* @method FS.HTTP.mount // 119
* @public // 120
* @param {array of string} mountPoints mount points to map rest functinality on // 121
* @param {function} selector_f [selector] function returns `{ collection, file }` for mount points to work with // 122
* // 123
*/ // 124
FS.HTTP.mount = function(mountPoints, selector_f) { // 125
// We take mount points as an array and we get a selector function // 126
var selectorFunction = selector_f || defaultSelectorFunction; // 127
// 128
var accessPoint = { // 129
'stream': true, // 130
'auth': expirationAuth, // 131
'post': function(data) { // 132
// Use the selector for finding the collection and file reference // 133
var ref = selectorFunction.call(this); // 134
// 135
// We dont support post - this would be normal insert eg. of filerecord? // 136
throw new Meteor.Error(501, "Not implemented", "Post is not supported"); // 137
}, // 138
'put': function(data) { // 139
// Use the selector for finding the collection and file reference // 140
var ref = selectorFunction.call(this); // 141
// 142
// Make sure we have a collection reference // 143
if (!ref.collection) // 144
throw new Meteor.Error(404, "Not Found", "No collection found"); // 145
// 146
// Make sure we have a file reference // 147
if (ref.file === null) { // 148
// No id supplied so we will create a new FS.File instance and // 149
// insert the supplied data. // 150
return FS.HTTP.Handlers.PutInsert.apply(this, [ref]); // 151
} else { // 152
if (ref.file) { // 153
return FS.HTTP.Handlers.PutUpdate.apply(this, [ref]); // 154
} else { // 155
throw new Meteor.Error(404, "Not Found", 'No file found'); // 156
} // 157
} // 158
}, // 159
'get': function(data) { // 160
// Use the selector for finding the collection and file reference // 161
var ref = selectorFunction.call(this); // 162
// 163
// Make sure we have a collection reference // 164
if (!ref.collection) // 165
throw new Meteor.Error(404, "Not Found", "No collection found"); // 166
// 167
// Make sure we have a file reference // 168
if (ref.file === null) { // 169
// No id supplied so we will return the published list of files ala // 170
// http.publish in json format // 171
return FS.HTTP.Handlers.GetList.apply(this, [ref]); // 172
} else { // 173
if (ref.file) { // 174
return FS.HTTP.Handlers.Get.apply(this, [ref]); // 175
} else { // 176
throw new Meteor.Error(404, "Not Found", 'No file found'); // 177
} // 178
} // 179
}, // 180
'delete': function(data) { // 181
// Use the selector for finding the collection and file reference // 182
var ref = selectorFunction.call(this); // 183
// 184
// Make sure we have a collection reference // 185
if (!ref.collection) // 186
throw new Meteor.Error(404, "Not Found", "No collection found"); // 187
// 188
// Make sure we have a file reference // 189
if (ref.file) { // 190
return FS.HTTP.Handlers.Del.apply(this, [ref]); // 191
} else { // 192
throw new Meteor.Error(404, "Not Found", 'No file found'); // 193
} // 194
} // 195
}; // 196
// 197
var accessPoints = {}; // 198
// 199
// Add debug message // 200
FS.debug && console.log('Registered HTTP method URLs:'); // 201
// 202
FS.Utility.each(mountPoints, function(mountPoint) { // 203
// Couple mountpoint and accesspoint // 204
accessPoints[mountPoint] = accessPoint; // 205
// Remember our mountpoints // 206
_existingMountPoints[mountPoint] = mountPoint; // 207
// Add debug message // 208
FS.debug && console.log(mountPoint); // 209
}); // 210
// 211
// XXX: HTTP:methods should unmount existing mounts in case of overwriting? // 212
HTTP.methods(accessPoints); // 213
// 214
}; // 215
// 216
/** // 217
* @method FS.HTTP.unmount // 218
* @public // 219
* @param {string | array of string} [mountPoints] Optional, if not specified all mountpoints are unmounted // 220
* // 221
*/ // 222
FS.HTTP.unmount = function(mountPoints) { // 223
// The mountPoints is optional, can be string or array if undefined then // 224
// _existingMountPoints will be used // 225
var unmountList; // 226
// Container for the mount points to unmount // 227
var unmountPoints = {}; // 228
// 229
if (typeof mountPoints === 'undefined') { // 230
// Use existing mount points - unmount all // 231
unmountList = _existingMountPoints; // 232
} else if (mountPoints === ''+mountPoints) { // 233
// Got a string // 234
unmountList = [mountPoints]; // 235
} else if (mountPoints.length) { // 236
// Got an array // 237
unmountList = mountPoints; // 238
} // 239
// 240
// If we have a list to unmount // 241
if (unmountList) { // 242
// Iterate over each item // 243
FS.Utility.each(unmountList, function(mountPoint) { // 244
// Check _existingMountPoints to make sure the mount point exists in our // 245
// context / was created by the FS.HTTP.mount // 246
if (_existingMountPoints[mountPoint]) { // 247
// Mark as unmount // 248
unmountPoints[mountPoint] = false; // 249
// Release // 250
delete _existingMountPoints[mountPoint]; // 251
} // 252
}); // 253
FS.debug && console.log('FS.HTTP.unmount:'); // 254
FS.debug && console.log(unmountPoints); // 255
// Complete unmount // 256
HTTP.methods(unmountPoints); // 257
} // 258
}; // 259
// 260
// ### FS.Collection maps on HTTP pr. default on the following restpoints: // 261
// * // 262
// baseUrl + '/files/:collectionName/:id/:filename', // 263
// baseUrl + '/files/:collectionName/:id', // 264
// baseUrl + '/files/:collectionName' // 265
// // 266
// Change/ replace the existing mount point by: // 267
// ```js // 268
// // unmount all existing // 269
// FS.HTTP.unmount(); // 270
// // Create new mount point // 271
// FS.HTTP.mount([ // 272
// '/cfs/files/:collectionName/:id/:filename', // 273
// '/cfs/files/:collectionName/:id', // 274
// '/cfs/files/:collectionName' // 275
// ]); // 276
// ``` // 277
// // 278
mountUrls = function mountUrls() { // 279
// We unmount first in case we are calling this a second time // 280
FS.HTTP.unmount(); // 281
// 282
FS.HTTP.mount([ // 283
baseUrl + '/files/:collectionName/:id/:filename', // 284
baseUrl + '/files/:collectionName/:id', // 285
baseUrl + '/files/:collectionName' // 286
]); // 287
}; // 288
// 289
// Returns the userId from URL token // 290
var expirationAuth = function expirationAuth() { // 291
var self = this; // 292
// 293
// Read the token from '/hello?token=base64' // 294
var encodedToken = self.query.token; // 295
// 296
FS.debug && console.log("token: "+encodedToken); // 297
// 298
if (!encodedToken || !Meteor.users) return false; // 299
// 300
// Check the userToken before adding it to the db query // 301
// Set the this.userId // 302
var tokenString = FS.Utility.atob(encodedToken); // 303
// 304
var tokenObject; // 305
try { // 306
tokenObject = JSON.parse(tokenString); // 307
} catch(err) { // 308
throw new Meteor.Error(400, 'Bad Request'); // 309
} // 310
// 311
// XXX: Do some check here of the object // 312
var userToken = tokenObject.authToken; // 313
if (userToken !== ''+userToken) { // 314
throw new Meteor.Error(400, 'Bad Request'); // 315
} // 316
// 317
// If we have an expiration token we should check that it's still valid // 318
if (tokenObject.expiration != null) { // 319
// check if its too old // 320
var now = Date.now(); // 321
if (tokenObject.expiration < now) { // 322
FS.debug && console.log('Expired token: ' + tokenObject.expiration + ' is less than ' + now); // 323
throw new Meteor.Error(500, 'Expired token'); // 324
} // 325
} // 326
// 327
// We are not on a secure line - so we have to look up the user... // 328
var user = Meteor.users.findOne({ // 329
$or: [ // 330
{'services.resume.loginTokens.hashedToken': Accounts._hashLoginToken(userToken)}, // 331
{'services.resume.loginTokens.token': userToken} // 332
] // 333
}); // 334
// 335
// Set the userId in the scope // 336
return user && user._id; // 337
}; // 338
// 339
HTTP.methods( // 340
{'/cfs/servertime': { // 341
get: function(data) { // 342
return Date.now().toString(); // 343
} // 344
} // 345
}); // 346
// 347
// Unify client / server api // 348
FS.HTTP.now = function() { // 349
return Date.now(); // 350
}; // 351
// 352
// Start up the basic mount points // 353
Meteor.startup(function () { // 354
mountUrls(); // 355
}); // 356
// 357
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}).call(this);
///////////////////////////////////////////////////////////////////////
}).call(this);
/* Exports */
if (typeof Package === 'undefined') Package = {};
Package['cfs:access-point'] = {};
})();
+238
View File
@@ -0,0 +1,238 @@
/* global JsonRoutes */
if (Meteor.isServer) {
// todo XXX once we have a real API in place, move that route there
// todo XXX also share the route definition between the client and the server
// so that we could use something like
// `ApiRoutes.path('boards/export', boardId)``
// on the client instead of copy/pasting the route path manually between the
// client and the server.
/**
* @operation export
* @tag Boards
*
* @summary This route is used to export the board.
*
* @description If user is already logged-in, pass loginToken as param
* "authToken": '/api/boards/:boardId/export?authToken=:token'
*
* See https://blog.kayla.com.au/server-side-route-authentication-in-meteor/
* for detailed explanations
*
* @param {string} boardId the ID of the board we are exporting
* @param {string} authToken the loginToken
*/
JsonRoutes.add('get', '/api/boards/:boardId/export', function(req, res) {
const boardId = req.params.boardId;
let user = null;
const loginToken = req.query.authToken;
if (loginToken) {
const hashToken = Accounts._hashLoginToken(loginToken);
user = Meteor.users.findOne({
'services.resume.loginTokens.hashedToken': hashToken,
});
} else if (!Meteor.settings.public.sandstorm) {
Authentication.checkUserId(req.userId);
user = Users.findOne({ _id: req.userId, isAdmin: true });
}
const exporter = new Exporter(boardId);
if (exporter.canExport(user)) {
JsonRoutes.sendResult(res, {
code: 200,
data: exporter.build(),
});
} else {
// we could send an explicit error message, but on the other hand the only
// way to get there is by hacking the UI so let's keep it raw.
JsonRoutes.sendResult(res, 403);
}
});
}
// exporter maybe is broken since Gridfs introduced, add fs and path
export class Exporter {
constructor(boardId) {
this._boardId = boardId;
}
build() {
const fs = Npm.require('fs');
const os = Npm.require('os');
const path = Npm.require('path');
const byBoard = { boardId: this._boardId };
const byBoardNoLinked = {
boardId: this._boardId,
linkedId: { $in: ['', null] },
};
// we do not want to retrieve boardId in related elements
const noBoardId = {
fields: {
boardId: 0,
},
};
const result = {
_format: 'wekan-board-1.0.0',
};
_.extend(
result,
Boards.findOne(this._boardId, {
fields: {
stars: 0,
},
}),
);
result.lists = Lists.find(byBoard, noBoardId).fetch();
result.cards = Cards.find(byBoardNoLinked, noBoardId).fetch();
result.swimlanes = Swimlanes.find(byBoard, noBoardId).fetch();
result.customFields = CustomFields.find(
{ boardIds: { $in: [this.boardId] } },
{ fields: { boardId: 0 } },
).fetch();
result.comments = CardComments.find(byBoard, noBoardId).fetch();
result.activities = Activities.find(byBoard, noBoardId).fetch();
result.rules = Rules.find(byBoard, noBoardId).fetch();
result.checklists = [];
result.checklistItems = [];
result.subtaskItems = [];
result.triggers = [];
result.actions = [];
result.cards.forEach(card => {
result.checklists.push(
...Checklists.find({
cardId: card._id,
}).fetch(),
);
result.checklistItems.push(
...ChecklistItems.find({
cardId: card._id,
}).fetch(),
);
result.subtaskItems.push(
...Cards.find({
parentId: card._id,
}).fetch(),
);
});
result.rules.forEach(rule => {
result.triggers.push(
...Triggers.find(
{
_id: rule.triggerId,
},
noBoardId,
).fetch(),
);
result.actions.push(
...Actions.find(
{
_id: rule.actionId,
},
noBoardId,
).fetch(),
);
});
// [Old] for attachments we only export IDs and absolute url to original doc
// [New] Encode attachment to base64
const getBase64Data = function(doc, callback) {
let buffer = new Buffer(0);
// callback has the form function (err, res) {}
const tmpFile = path.join(
os.tmpdir(),
`tmpexport${process.pid}${Math.random()}`,
);
const tmpWriteable = fs.createWriteStream(tmpFile);
const readStream = doc.createReadStream();
readStream.on('data', function(chunk) {
buffer = Buffer.concat([buffer, chunk]);
});
readStream.on('error', function(err) {
callback(err, null);
});
readStream.on('end', function() {
// done
fs.unlink(tmpFile, () => {
//ignored
});
callback(null, buffer.toString('base64'));
});
readStream.pipe(tmpWriteable);
};
const getBase64DataSync = Meteor.wrapAsync(getBase64Data);
result.attachments = Attachments.find(byBoard)
.fetch()
.map(attachment => {
return {
_id: attachment._id,
cardId: attachment.cardId,
// url: FlowRouter.url(attachment.url()),
file: getBase64DataSync(attachment),
name: attachment.original.name,
type: attachment.original.type,
};
});
// we also have to export some user data - as the other elements only
// include id but we have to be careful:
// 1- only exports users that are linked somehow to that board
// 2- do not export any sensitive information
const users = {};
result.members.forEach(member => {
users[member.userId] = true;
});
result.lists.forEach(list => {
users[list.userId] = true;
});
result.cards.forEach(card => {
users[card.userId] = true;
if (card.members) {
card.members.forEach(memberId => {
users[memberId] = true;
});
}
});
result.comments.forEach(comment => {
users[comment.userId] = true;
});
result.activities.forEach(activity => {
users[activity.userId] = true;
});
result.checklists.forEach(checklist => {
users[checklist.userId] = true;
});
const byUserIds = {
_id: {
$in: Object.getOwnPropertyNames(users),
},
};
// we use whitelist to be sure we do not expose inadvertently
// some secret fields that gets added to User later.
const userFields = {
fields: {
_id: 1,
username: 1,
'profile.fullname': 1,
'profile.initials': 1,
'profile.avatarUrl': 1,
},
};
result.users = Users.find(byUserIds, userFields)
.fetch()
.map(user => {
// user avatar is stored as a relative url, we export absolute
if ((user.profile || {}).avatarUrl) {
user.profile.avatarUrl = FlowRouter.url(user.profile.avatarUrl);
}
return user;
});
return result;
}
canExport(user) {
const board = Boards.findOne(this._boardId);
return board && board.isVisibleBy(user);
}
}
+155
View File
@@ -0,0 +1,155 @@
name: wekan
version: git
summary: The open-source kanban
description: |
Wekan is an open-source and collaborative kanban board application.
Whether you’re maintaining a personal todo list, planning your holidays with some friends, or working in a team on your next revolutionary idea, Kanban boards are an unbeatable tool to keep your things organized. They give you a visual overview of the current state of your project, and make you productive by allowing you to focus on the few items that matter the most.
Depending on target environment, some configuration settings might need to be adjusted.
For full list of configuration options call:
$ wekan.help
confinement: strict
grade: stable
base: core18
architectures:
- amd64
plugs:
mongodb-plug:
interface: content
target: $SNAP_DATA/shared
hooks:
configure:
plugs:
- network
- network-bind
slots:
mongodb-slot:
interface: content
write:
- $SNAP_DATA/share
apps:
wekan:
command: wekan-control
daemon: simple
plugs: [network, network-bind]
mongodb:
command: mongodb-control
daemon: simple
plugs: [network, network-bind]
caddy:
command: caddy-control
daemon: simple
plugs: [network, network-bind]
help:
command: wekan-help
database-backup:
command: mongodb-backup
plugs: [network, network-bind]
database-list-backups:
command: ls -al $SNAP_COMMON/db-backups/
database-restore:
command: mongodb-restore
plugs: [network, network-bind]
parts:
mongodb:
source: https://repo.mongodb.org/apt/ubuntu/dists/xenial/mongodb-org/4.2/multiverse/binary-amd64/mongodb-org-server_4.2.2_amd64.deb
#https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-4.0.14.tgz
#https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1604-3.2.22.tgz
plugin: dump
stage-packages: [libssl1.0.0, libcurl3]
filesets:
mongo:
- usr
- bin
- lib
stage:
- $mongo
prime:
- $mongo
wekan:
source: .
plugin: nodejs
node-engine: 12.14.1
node-packages:
- node-gyp
- node-pre-gyp
- fibers
build-packages:
- ca-certificates
- apt-utils
- build-essential
- python
- python3
- g++
- capnproto
- curl
- libcurl3
- execstack
- nodejs
- npm
stage-packages:
- libfontconfig1
override-build: |
echo "Cleaning environment first"
rm -rf ~/.meteor ~/.npm /usr/local/lib/node_modules
rm -rf .build
echo "Installing meteor"
curl https://install.meteor.com/ -o install_meteor.sh
chmod +x install_meteor.sh
sh install_meteor.sh
rm install_meteor.sh
rm -rf .build
meteor add standard-minifier-js --allow-superuser
meteor npm install --allow-superuser
meteor npm install --allow-superuser --save babel-runtime
meteor build .build --directory --allow-superuser
cp -f fix-download-unicode/cfs_access-point.txt .build/bundle/programs/server/packages/cfs_access-point.js
cd .build/bundle/programs/server
npm install
npm install --allow-superuser --save babel-runtime
# Change back to Wekan source directory
cd ../../../..
cp -r .build/bundle/* $SNAPCRAFT_PART_INSTALL/
cp .build/bundle/.node_version.txt $SNAPCRAFT_PART_INSTALL/
rm -f $SNAPCRAFT_PART_INSTALL/lib/node_modules/wekan
rm -f $SNAPCRAFT_PART_INSTALL/programs/server/npm/node_modules/meteor/rajit_bootstrap3-datepicker/lib/bootstrap-datepicker/node_modules/phantomjs-prebuilt/lib/phantom/bin/phantomjs
rm -f $SNAPCRAFT_PART_INSTALL/programs/server/npm/node_modules/tar/lib/.mkdir.js.swp
rm -f $SNAPCRAFT_PART_INSTALL/lib/node_modules/node-pre-gyp/node_modules/tar/lib/.mkdir.js.swp
rm -f $SNAPCRAFT_PART_INSTALL/lib/node_modules/node-gyp/node_modules/tar/lib/.mkdir.js.swp
rm -f $SNAPCRAFT_PART_INSTALL/programs/server/node_modules/node-pre-gyp/node_modules/tar/lib/.mkdir.js.swp
organize:
README: README.wekan
prime:
- -lib/node_modules/node-pre-gyp/node_modules/tar/lib/.unpack.js.swp
helpers:
source: snap-src
plugin: dump
caddy:
plugin: dump
source: https://caddyserver.com/download/linux/amd64?license=personal&telemetry=off
source-type: tar
organize:
caddy: bin/caddy
CHANGES.txt: CADDY_CHANGES.txt
EULA.txt: CADDY_EULA.txt
LICENSES.txt: CADDY_LICENSES.txt
README.txt: CADDY_README.txt
stage:
- -init
+640
View File
@@ -0,0 +1,640 @@
import ldapjs from 'ldapjs';
import util from 'util';
import Bunyan from 'bunyan';
import { log_debug, log_info, log_warn, log_error } from './logger';
export default class LDAP {
constructor() {
this.ldapjs = ldapjs;
this.connected = false;
this.options = {
host: this.constructor.settings_get('LDAP_HOST'),
port: this.constructor.settings_get('LDAP_PORT'),
Reconnect: this.constructor.settings_get('LDAP_RECONNECT'),
timeout: this.constructor.settings_get('LDAP_TIMEOUT'),
connect_timeout: this.constructor.settings_get('LDAP_CONNECT_TIMEOUT'),
idle_timeout: this.constructor.settings_get('LDAP_IDLE_TIMEOUT'),
encryption: this.constructor.settings_get('LDAP_ENCRYPTION'),
ca_cert: this.constructor.settings_get('LDAP_CA_CERT'),
reject_unauthorized:
this.constructor.settings_get('LDAP_REJECT_UNAUTHORIZED') || false,
Authentication: this.constructor.settings_get('LDAP_AUTHENTIFICATION'),
Authentication_UserDN: this.constructor.settings_get(
'LDAP_AUTHENTIFICATION_USERDN',
),
Authentication_Password: this.constructor.settings_get(
'LDAP_AUTHENTIFICATION_PASSWORD',
),
Authentication_Fallback: this.constructor.settings_get(
'LDAP_LOGIN_FALLBACK',
),
BaseDN: this.constructor.settings_get('LDAP_BASEDN'),
Internal_Log_Level: this.constructor.settings_get('INTERNAL_LOG_LEVEL'),
User_Authentication: this.constructor.settings_get(
'LDAP_USER_AUTHENTICATION',
),
User_Authentication_Field: this.constructor.settings_get(
'LDAP_USER_AUTHENTICATION_FIELD',
),
User_Attributes: this.constructor.settings_get('LDAP_USER_ATTRIBUTES'),
User_Search_Filter: this.constructor.settings_get(
'LDAP_USER_SEARCH_FILTER',
),
User_Search_Scope: this.constructor.settings_get(
'LDAP_USER_SEARCH_SCOPE',
),
User_Search_Field: this.constructor.settings_get(
'LDAP_USER_SEARCH_FIELD',
),
Search_Page_Size: this.constructor.settings_get('LDAP_SEARCH_PAGE_SIZE'),
Search_Size_Limit: this.constructor.settings_get(
'LDAP_SEARCH_SIZE_LIMIT',
),
group_filter_enabled: this.constructor.settings_get(
'LDAP_GROUP_FILTER_ENABLE',
),
group_filter_object_class: this.constructor.settings_get(
'LDAP_GROUP_FILTER_OBJECTCLASS',
),
group_filter_group_id_attribute: this.constructor.settings_get(
'LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE',
),
group_filter_group_member_attribute: this.constructor.settings_get(
'LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE',
),
group_filter_group_member_format: this.constructor.settings_get(
'LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT',
),
group_filter_group_name: this.constructor.settings_get(
'LDAP_GROUP_FILTER_GROUP_NAME',
),
};
}
static settings_get(name, ...args) {
let value = process.env[name];
if (value !== undefined) {
if (value === 'true' || value === 'false') {
value = JSON.parse(value);
} else if (value !== '' && !isNaN(value)) {
value = Number(value);
}
return value;
} else {
log_warn(`Lookup for unset variable: ${name}`);
}
}
connectSync(...args) {
if (!this._connectSync) {
this._connectSync = Meteor.wrapAsync(this.connectAsync, this);
}
return this._connectSync(...args);
}
searchAllSync(...args) {
if (!this._searchAllSync) {
this._searchAllSync = Meteor.wrapAsync(this.searchAllAsync, this);
}
return this._searchAllSync(...args);
}
connectAsync(callback) {
log_info('Init setup');
let replied = false;
const connectionOptions = {
url: `${this.options.host}:${this.options.port}`,
timeout: this.options.timeout,
connectTimeout: this.options.connect_timeout,
idleTimeout: this.options.idle_timeout,
reconnect: this.options.Reconnect,
};
if (this.options.Internal_Log_Level !== 'disabled') {
connectionOptions.log = new Bunyan({
name: 'ldapjs',
component: 'client',
stream: process.stderr,
level: this.options.Internal_Log_Level,
});
}
const tlsOptions = {
rejectUnauthorized: this.options.reject_unauthorized,
};
if (this.options.ca_cert && this.options.ca_cert !== '') {
// Split CA cert into array of strings
const chainLines = this.constructor
.settings_get('LDAP_CA_CERT')
.split('\n');
let cert = [];
const ca = [];
chainLines.forEach(line => {
cert.push(line);
if (line.match(/-END CERTIFICATE-/)) {
ca.push(cert.join('\n'));
cert = [];
}
});
tlsOptions.ca = ca;
}
if (this.options.encryption === 'ssl') {
connectionOptions.url = `ldaps://${connectionOptions.url}`;
connectionOptions.tlsOptions = tlsOptions;
} else {
connectionOptions.url = `ldap://${connectionOptions.url}`;
}
log_info('Connecting', connectionOptions.url);
log_debug(`connectionOptions${util.inspect(connectionOptions)}`);
this.client = ldapjs.createClient(connectionOptions);
this.bindSync = Meteor.wrapAsync(this.client.bind, this.client);
this.client.on('error', error => {
log_error('connection', error);
if (replied === false) {
replied = true;
callback(error, null);
}
});
this.client.on('idle', () => {
log_info('Idle');
this.disconnect();
});
this.client.on('close', () => {
log_info('Closed');
});
if (this.options.encryption === 'tls') {
// Set host parameter for tls.connect which is used by ldapjs starttls. This shouldn't be needed in newer nodejs versions (e.g v5.6.0).
// https://github.com/RocketChat/Rocket.Chat/issues/2035
// https://github.com/mcavage/node-ldapjs/issues/349
tlsOptions.host = this.options.host;
log_info('Starting TLS');
log_debug('tlsOptions', tlsOptions);
this.client.starttls(tlsOptions, null, (error, response) => {
if (error) {
log_error('TLS connection', error);
if (replied === false) {
replied = true;
callback(error, null);
}
return;
}
log_info('TLS connected');
this.connected = true;
if (replied === false) {
replied = true;
callback(null, response);
}
});
} else {
this.client.on('connect', response => {
log_info('LDAP connected');
this.connected = true;
if (replied === false) {
replied = true;
callback(null, response);
}
});
}
setTimeout(() => {
if (replied === false) {
log_error('connection time out', connectionOptions.connectTimeout);
replied = true;
callback(new Error('Timeout'));
}
}, connectionOptions.connectTimeout);
}
getUserFilter(username) {
const filter = [];
if (this.options.User_Search_Filter !== '') {
if (this.options.User_Search_Filter[0] === '(') {
filter.push(`${this.options.User_Search_Filter}`);
} else {
filter.push(`(${this.options.User_Search_Filter})`);
}
}
const usernameFilter = this.options.User_Search_Field.split(',').map(
item => `(${item}=${username})`,
);
if (usernameFilter.length === 0) {
log_error('LDAP_LDAP_User_Search_Field not defined');
} else if (usernameFilter.length === 1) {
filter.push(`${usernameFilter[0]}`);
} else {
filter.push(`(|${usernameFilter.join('')})`);
}
return `(&${filter.join('')})`;
}
bindUserIfNecessary(username, password) {
if (this.domainBinded === true) {
return;
}
if (!this.options.User_Authentication) {
return;
}
if (!this.options.BaseDN) throw new Error('BaseDN is not provided');
const userDn = `${this.options.User_Authentication_Field}=${username},${this.options.BaseDN}`;
this.bindSync(userDn, password);
this.domainBinded = true;
}
bindIfNecessary() {
if (this.domainBinded === true) {
return;
}
if (this.options.Authentication !== true) {
return;
}
log_info('Binding UserDN', this.options.Authentication_UserDN);
this.bindSync(
this.options.Authentication_UserDN,
this.options.Authentication_Password,
);
this.domainBinded = true;
}
searchUsersSync(username, page) {
this.bindIfNecessary();
const searchOptions = {
filter: this.getUserFilter(username),
scope: this.options.User_Search_Scope || 'sub',
sizeLimit: this.options.Search_Size_Limit,
};
if (!!this.options.User_Attributes)
searchOptions.attributes = this.options.User_Attributes.split(',');
if (this.options.Search_Page_Size > 0) {
searchOptions.paged = {
pageSize: this.options.Search_Page_Size,
pagePause: !!page,
};
}
log_info('Searching user', username);
log_debug('searchOptions', searchOptions);
log_debug('BaseDN', this.options.BaseDN);
if (page) {
return this.searchAllPaged(this.options.BaseDN, searchOptions, page);
}
return this.searchAllSync(this.options.BaseDN, searchOptions);
}
getUserByIdSync(id, attribute) {
this.bindIfNecessary();
const Unique_Identifier_Field = this.constructor
.settings_get('LDAP_UNIQUE_IDENTIFIER_FIELD')
.split(',');
let filter;
if (attribute) {
filter = new this.ldapjs.filters.EqualityFilter({
attribute,
value: new Buffer(id, 'hex'),
});
} else {
const filters = [];
Unique_Identifier_Field.forEach(item => {
filters.push(
new this.ldapjs.filters.EqualityFilter({
attribute: item,
value: new Buffer(id, 'hex'),
}),
);
});
filter = new this.ldapjs.filters.OrFilter({ filters });
}
const searchOptions = {
filter,
scope: 'sub',
};
log_info('Searching by id', id);
log_debug('search filter', searchOptions.filter.toString());
log_debug('BaseDN', this.options.BaseDN);
const result = this.searchAllSync(this.options.BaseDN, searchOptions);
if (!Array.isArray(result) || result.length === 0) {
return;
}
if (result.length > 1) {
log_error('Search by id', id, 'returned', result.length, 'records');
}
return result[0];
}
getUserByUsernameSync(username) {
this.bindIfNecessary();
const searchOptions = {
filter: this.getUserFilter(username),
scope: this.options.User_Search_Scope || 'sub',
};
log_info('Searching user', username);
log_debug('searchOptions', searchOptions);
log_debug('BaseDN', this.options.BaseDN);
const result = this.searchAllSync(this.options.BaseDN, searchOptions);
if (!Array.isArray(result) || result.length === 0) {
return;
}
if (result.length > 1) {
log_error(
'Search by username',
username,
'returned',
result.length,
'records',
);
}
return result[0];
}
getUserGroups(username, ldapUser) {
if (!this.options.group_filter_enabled) {
return true;
}
const filter = ['(&'];
if (this.options.group_filter_object_class !== '') {
filter.push(`(objectclass=${this.options.group_filter_object_class})`);
}
if (this.options.group_filter_group_member_attribute !== '') {
const format_value =
ldapUser[this.options.group_filter_group_member_format];
if (format_value) {
filter.push(
`(${this.options.group_filter_group_member_attribute}=${format_value})`,
);
}
}
filter.push(')');
const searchOptions = {
filter: filter.join('').replace(/#{username}/g, username),
scope: 'sub',
};
log_debug('Group list filter LDAP:', searchOptions.filter);
const result = this.searchAllSync(this.options.BaseDN, searchOptions);
if (!Array.isArray(result) || result.length === 0) {
return [];
}
const grp_identifier = this.options.group_filter_group_id_attribute || 'cn';
const groups = [];
result.map(item => {
groups.push(item[grp_identifier]);
});
log_debug(`Groups: ${groups.join(', ')}`);
return groups;
}
isUserInGroup(username, ldapUser) {
if (!this.options.group_filter_enabled) {
return true;
}
const grps = this.getUserGroups(username, ldapUser);
const filter = ['(&'];
if (this.options.group_filter_object_class !== '') {
filter.push(`(objectclass=${this.options.group_filter_object_class})`);
}
if (this.options.group_filter_group_member_attribute !== '') {
const format_value =
ldapUser[this.options.group_filter_group_member_format];
if (format_value) {
filter.push(
`(${this.options.group_filter_group_member_attribute}=${format_value})`,
);
}
}
if (this.options.group_filter_group_id_attribute !== '') {
filter.push(
`(${this.options.group_filter_group_id_attribute}=${this.options.group_filter_group_name})`,
);
}
filter.push(')');
const searchOptions = {
filter: filter.join('').replace(/#{username}/g, username),
scope: 'sub',
};
log_debug('Group filter LDAP:', searchOptions.filter);
const result = this.searchAllSync(this.options.BaseDN, searchOptions);
if (!Array.isArray(result) || result.length === 0) {
return false;
}
return true;
}
extractLdapEntryData(entry) {
const values = {
_raw: entry.raw,
};
Object.keys(values._raw).forEach(key => {
const value = values._raw[key];
if (!['thumbnailPhoto', 'jpegPhoto'].includes(key)) {
if (value instanceof Buffer) {
values[key] = value.toString();
} else {
values[key] = value;
}
}
});
return values;
}
searchAllPaged(BaseDN, options, page) {
this.bindIfNecessary();
const processPage = ({ entries, title, end, next }) => {
log_info(title);
// Force LDAP idle to wait the record processing
this.client._updateIdle(true);
page(null, entries, {
end,
next: () => {
// Reset idle timer
this.client._updateIdle();
next && next();
},
});
};
this.client.search(BaseDN, options, (error, res) => {
if (error) {
log_error(error);
page(error);
return;
}
res.on('error', error => {
log_error(error);
page(error);
return;
});
let entries = [];
const internalPageSize =
options.paged && options.paged.pageSize > 0
? options.paged.pageSize * 2
: 500;
res.on('searchEntry', entry => {
entries.push(this.extractLdapEntryData(entry));
if (entries.length >= internalPageSize) {
processPage({
entries,
title: 'Internal Page',
end: false,
});
entries = [];
}
});
res.on('page', (result, next) => {
if (!next) {
this.client._updateIdle(true);
processPage({
entries,
title: 'Final Page',
end: true,
});
} else if (entries.length) {
log_info('Page');
processPage({
entries,
title: 'Page',
end: false,
next,
});
entries = [];
}
});
res.on('end', () => {
if (entries.length) {
processPage({
entries,
title: 'Final Page',
end: true,
});
entries = [];
}
});
});
}
searchAllAsync(BaseDN, options, callback) {
this.bindIfNecessary();
this.client.search(BaseDN, options, (error, res) => {
if (error) {
log_error(error);
callback(error);
return;
}
res.on('error', error => {
log_error(error);
callback(error);
return;
});
const entries = [];
res.on('searchEntry', entry => {
entries.push(this.extractLdapEntryData(entry));
});
res.on('end', () => {
log_info('Search result count', entries.length);
callback(null, entries);
});
});
}
authSync(dn, password) {
log_info('Authenticating', dn);
try {
if (password === '') {
throw new Error('Password is not provided');
}
this.bindSync(dn, password);
log_info('Authenticated', dn);
return true;
} catch (error) {
log_info('Not authenticated', dn);
log_debug('error', error);
return false;
}
}
disconnect() {
this.connected = false;
this.domainBinded = false;
log_info('Disconecting');
this.client.unbind();
}
}
+163
View File
@@ -0,0 +1,163 @@
Oidc = {};
OAuth.registerService('oidc', 2, null, function(query) {
var debug = process.env.DEBUG || false;
var token = getToken(query);
if (debug) console.log('XXX: register token:', token);
var accessToken = token.access_token || token.id_token;
var expiresAt = +new Date() + 1000 * parseInt(token.expires_in, 10);
var userinfo = getUserInfo(accessToken);
if (debug) console.log('XXX: userinfo:', userinfo);
var serviceData = {};
serviceData.id = userinfo[process.env.OAUTH2_ID_MAP]; // || userinfo["id"];
serviceData.username = userinfo[process.env.OAUTH2_USERNAME_MAP]; // || userinfo["uid"];
serviceData.fullname = userinfo[process.env.OAUTH2_FULLNAME_MAP]; // || userinfo["displayName"];
serviceData.accessToken = accessToken;
serviceData.expiresAt = expiresAt;
serviceData.email = userinfo[process.env.OAUTH2_EMAIL_MAP]; // || userinfo["email"];
if (accessToken) {
var tokenContent = getTokenContent(accessToken);
var fields = _.pick(
tokenContent,
getConfiguration().idTokenWhitelistFields,
);
_.extend(serviceData, fields);
}
if (token.refresh_token) serviceData.refreshToken = token.refresh_token;
if (debug) console.log('XXX: serviceData:', serviceData);
var profile = {};
profile.name = userinfo[process.env.OAUTH2_FULLNAME_MAP]; // || userinfo["displayName"];
profile.email = userinfo[process.env.OAUTH2_EMAIL_MAP]; // || userinfo["email"];
if (debug) console.log('XXX: profile:', profile);
return {
serviceData: serviceData,
options: { profile: profile },
};
});
var userAgent = 'Meteor';
if (Meteor.release) {
userAgent += '/' + Meteor.release;
}
var getToken = function(query) {
var debug = process.env.DEBUG || false;
var config = getConfiguration();
if (config.tokenEndpoint.includes('https://')) {
var serverTokenEndpoint = config.tokenEndpoint;
} else {
var serverTokenEndpoint = config.serverUrl + config.tokenEndpoint;
}
var requestPermissions = config.requestPermissions;
var response;
try {
response = HTTP.post(serverTokenEndpoint, {
headers: {
Accept: 'application/json',
'User-Agent': userAgent,
},
params: {
code: query.code,
client_id: config.clientId,
client_secret: OAuth.openSecret(config.secret),
redirect_uri: OAuth._redirectUri('oidc', config),
grant_type: 'authorization_code',
scope: requestPermissions,
state: query.state,
},
});
} catch (err) {
throw _.extend(
new Error(
'Failed to get token from OIDC ' +
serverTokenEndpoint +
': ' +
err.message,
),
{ response: err.response },
);
}
if (response.data.error) {
// if the http response was a json object with an error attribute
throw new Error(
'Failed to complete handshake with OIDC ' +
serverTokenEndpoint +
': ' +
response.data.error,
);
} else {
if (debug) console.log('XXX: getToken response: ', response.data);
return response.data;
}
};
var getUserInfo = function(accessToken) {
var debug = process.env.DEBUG || false;
var config = getConfiguration();
// Some userinfo endpoints use a different base URL than the authorization or token endpoints.
// This logic allows the end user to override the setting by providing the full URL to userinfo in their config.
if (config.userinfoEndpoint.includes('https://')) {
var serverUserinfoEndpoint = config.userinfoEndpoint;
} else {
var serverUserinfoEndpoint = config.serverUrl + config.userinfoEndpoint;
}
var response;
try {
response = HTTP.get(serverUserinfoEndpoint, {
headers: {
'User-Agent': userAgent,
Authorization: 'Bearer ' + accessToken,
},
});
} catch (err) {
throw _.extend(
new Error(
'Failed to fetch userinfo from OIDC ' +
serverUserinfoEndpoint +
': ' +
err.message,
),
{ response: err.response },
);
}
if (debug) console.log('XXX: getUserInfo response: ', response.data);
return response.data;
};
var getConfiguration = function() {
var config = ServiceConfiguration.configurations.findOne({ service: 'oidc' });
if (!config) {
throw new ServiceConfiguration.ConfigError('Service oidc not configured.');
}
return config;
};
var getTokenContent = function(token) {
var content = null;
if (token) {
try {
var parts = token.split('.');
var header = JSON.parse(new Buffer(parts[0], 'base64').toString());
content = JSON.parse(new Buffer(parts[1], 'base64').toString());
var signature = new Buffer(parts[2], 'base64');
var signed = parts[0] + '.' + parts[1];
} catch (err) {
this.content = {
exp: 0,
};
}
}
return content;
};
Oidc.retrieveCredential = function(credentialToken, credentialSecret) {
return OAuth.retrieveCredential(credentialToken, credentialSecret);
};
File diff suppressed because it is too large. Load diff
+73
View File
@@ -0,0 +1,73 @@
{
"name": "wekan",
"version": "v3.87.0",
"description": "Open-Source kanban",
"private": true,
"scripts": {
"lint": "eslint --cache --ext .js --ignore-path .eslintignore .",
"lint:eslint:fix": "eslint --ext .js --ignore-path .eslintignore --fix .",
"lint:staged": "lint-staged",
"prettify": "prettier --write '**/*.js' '**/*.jsx'",
"test": "npm run lint"
},
"lint-staged": {
"*.js": [
"meteor npm run prettify",
"meteor npm run lint:eslint:fix",
"git add --force"
],
"*.jsx": [
"meteor npm run prettify",
"meteor npm run lint:eslint:fix",
"git add --force"
],
"*.json": [
"prettier --write",
"git add --force"
]
},
"pre-commit": "lint:staged",
"eslintConfig": {
"extends": "@meteorjs/eslint-config-meteor"
},
"repository": {
"type": "git",
"url": "git+https://github.com/wekan/wekan.git"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/wekan/wekan/issues"
},
"homepage": "https://wekan.github.io",
"devDependencies": {
"eslint": "^6.8.0",
"eslint-config-meteor": "^0.1.1",
"eslint-config-prettier": "^6.10.0",
"eslint-import-resolver-meteor": "^0.4.0",
"eslint-plugin-import": "^2.20.1",
"eslint-plugin-meteor": "^6.0.0",
"eslint-plugin-prettier": "^3.1.2",
"lint-staged": "^10.0.8",
"pre-commit": "^1.2.2",
"prettier": "^1.19.1",
"prettier-eslint": "^9.0.1"
},
"dependencies": {
"@babel/runtime": "^7.8.7",
"ajv": "^6.12.0",
"babel-runtime": "^6.26.0",
"bcrypt": "^4.0.1",
"bson": "^4.0.3",
"bunyan": "^1.8.12",
"es6-promise": "^4.2.8",
"gridfs-stream": "^1.1.1",
"ldapjs": "^1.0.2",
"meteor-node-stubs": "^1.0.0",
"mongodb": "^3.5.5",
"os": "^0.1.1",
"page": "^1.11.5",
"qs": "^6.9.1",
"source-map-support": "^0.5.16",
"xss": "^1.0.6"
}
}
+244
View File
@@ -0,0 +1,244 @@
name: wekan
version: 0
version-script: git describe --tags | cut -c 2-
summary: The open-source kanban
description: |
Wekan is an open-source and collaborative kanban board application.
Whether you’re maintaining a personal todo list, planning your holidays with some friends, or working in a team on your next revolutionary idea, Kanban boards are an unbeatable tool to keep your things organized. They give you a visual overview of the current state of your project, and make you productive by allowing you to focus on the few items that matter the most.
Depending on target environment, some configuration settings might need to be adjusted.
For full list of configuration options call:
$ wekan.help
confinement: strict
grade: stable
architectures:
- amd64
plugs:
mongodb-plug:
interface: content
target: $SNAP_DATA/shared
hooks:
configure:
plugs:
- network
- network-bind
slots:
mongodb-slot:
interface: content
write:
- $SNAP_DATA/share
apps:
wekan:
command: wekan-control
daemon: simple
plugs: [network, network-bind]
mongodb:
command: mongodb-control
daemon: simple
plugs: [network, network-bind]
caddy:
command: caddy-control
daemon: simple
plugs: [network, network-bind]
help:
command: wekan-help
database-backup:
command: mongodb-backup
plugs: [network, network-bind]
database-list-backups:
command: ls -al $SNAP_COMMON/db-backups/
database-restore:
command: mongodb-restore
plugs: [network, network-bind]
parts:
mongodb:
source: https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1604-3.2.22.tgz
plugin: dump
stage-packages: [libssl1.0.0]
filesets:
mongo:
- usr
- bin
- lib
stage:
- $mongo
prime:
- $mongo
wekan:
source: .
plugin: nodejs
node-engine: 8.17.0
node-packages:
- node-gyp
- node-pre-gyp
- fibers@2.0.0
build-packages:
- ca-certificates
- apt-utils
- python
# - python3
- g++
- capnproto
- curl
- execstack
- nodejs
- npm
stage-packages:
- libfontconfig1
override-build: |
echo "Cleaning environment first"
rm -rf ~/.meteor ~/.npm /usr/local/lib/node_modules
# Create the OpenAPI specification
rm -rf .build
#mkdir -p .build/python
#cd .build/python
#git clone --depth 1 -b master https://github.com/Kronuz/esprima-python
#cd esprima-python
#python3 setup.py install
#cd ../../..
#mkdir -p ./public/api
#python3 ./openapi/generate_openapi.py --release $(git describe --tags --abbrev=0) > ./public/api/wekan.yml
# we temporary need api2html and mkdirp
#npm install -g api2html@0.3.0
#npm install -g mkdirp
#api2html -c ./public/logo-header.png -o ./public/api/wekan.html ./public/api/wekan.yml
#npm uninstall -g mkdirp
#npm uninstall -g api2html
# Node Fibers 100% CPU usage issue:
# https://github.com/wekan/wekan-mongodb/issues/2#issuecomment-381453161
# https://github.com/meteor/meteor/issues/9796#issuecomment-381676326
# https://github.com/sandstorm-io/sandstorm/blob/0f1fec013fe7208ed0fd97eb88b31b77e3c61f42/shell/server/00-startup.js#L99-L129
# Also see beginning of wekan/server/authentication.js
# import Fiber from "fibers";
# Fiber.poolSize = 1e9;
# OLD: Download node version 8.12.0 prerelease build => Official node 8.12.0 has been released
# Description at https://releases.wekan.team/node.txt
##echo "375bd8db50b9c692c0bbba6e96d4114cd29bee3770f901c1ff2249d1038f1348 node" >> node-SHASUMS256.txt.asc
##curl https://releases.wekan.team/node -o node
# Verify Fibers patched node authenticity
##echo "Fibers 100% CPU issue patched node authenticity:"
##grep node node-SHASUMS256.txt.asc | shasum -a 256 -c -
##rm -f node-SHASUMS256.txt.asc
##chmod +x node
##mv node `which node`
# DOES NOT WORK: paxctl fix.
# Removed from build-packages: - paxctl
#echo "Applying paxctl fix for alpine linux: https://github.com/wekan/wekan/issues/1303"
#paxctl -mC `which node`
#echo "Installing npm"
#curl -L https://www.npmjs.com/install.sh | sh
echo "Installing meteor"
curl https://install.meteor.com/ -o install_meteor.sh
#sed -i "s|RELEASE=.*|RELEASE=\"1.8.1-beta.0\"|g" install_meteor.sh
chmod +x install_meteor.sh
sh install_meteor.sh
rm install_meteor.sh
# REPOS BELOW ARE INCLUDED TO WEKAN REPO
#if [ ! -d "packages" ]; then
# mkdir packages
#fi
#if [ ! -d "packages/kadira-flow-router" ]; then
# cd packages
# git clone --depth 1 -b master https://github.com/wekan/flow-router.git kadira-flow-router
# cd ..
#fi
#if [ ! -d "packages/meteor-useraccounts-core" ]; then
# cd packages
# git clone --depth 1 -b master https://github.com/meteor-useraccounts/core.git meteor-useraccounts-core
# sed -i 's/api\.versionsFrom/\/\/api.versionsFrom/' meteor-useraccounts-core/package.js
# cd ..
#fi
#if [ ! -d "packages/meteor-accounts-cas" ]; then
# cd packages
# git clone --depth 1 -b master https://github.com/wekan/meteor-accounts-cas.git meteor-accounts-cas
# cd ..
#fi
#if [ ! -d "packages/wekan-ldap" ]; then
# cd packages
# git clone --depth 1 -b master https://github.com/wekan/wekan-ldap.git
# cd ..
#fi
#if [ ! -d "packages/wekan-scrollbar" ]; then
# cd packages
# git clone --depth 1 -b master https://github.com/wekan/wekan-scrollbar.git
# cd ..
#fi
#if [ ! -d "packages/wekan_accounts-oidc" ]; then
# cd packages
# git clone --depth 1 -b master https://github.com/wekan/meteor-accounts-oidc.git
# mv meteor-accounts-oidc/packages/switch_accounts-oidc wekan-accounts-oidc
# mv meteor-accounts-oidc/packages/switch_oidc wekan-oidc
# rm -rf meteor-accounts-oidc
# cd ..
#fi
#if [ ! -d "packages/markdown" ]; then
# cd packages
# git clone --depth 1 -b master --recurse-submodules https://github.com/wekan/markdown.git
# cd ..
#fi
rm -rf .build
meteor add standard-minifier-js --allow-superuser
meteor npm install --allow-superuser
meteor npm install --allow-superuser --save babel-runtime
meteor build .build --directory --allow-superuser
cp -f fix-download-unicode/cfs_access-point.txt .build/bundle/programs/server/packages/cfs_access-point.js
#Removed binary version of bcrypt because of security vulnerability that is not fixed yet.
#https://github.com/wekan/wekan/commit/4b2010213907c61b0e0482ab55abb06f6a668eac
#https://github.com/wekan/wekan/commit/7eeabf14be3c63fae2226e561ef8a0c1390c8d3c
#cd .build/bundle/programs/server/npm/node_modules/meteor/npm-bcrypt
#rm -rf node_modules/bcrypt
#meteor npm install --save bcrypt
# Change from npm-bcrypt directory back to .build/bundle/programs/server directory.
#cd ../../../../
# Change to directory .build/bundle/programs/server
cd .build/bundle/programs/server
npm install
npm install --allow-superuser --save babel-runtime
#meteor npm install --save bcrypt
# Change back to Wekan source directory
cd ../../../..
cp -r .build/bundle/* $SNAPCRAFT_PART_INSTALL/
cp .build/bundle/.node_version.txt $SNAPCRAFT_PART_INSTALL/
rm -f $SNAPCRAFT_PART_INSTALL/lib/node_modules/wekan
rm -f $SNAPCRAFT_PART_INSTALL/programs/server/npm/node_modules/meteor/rajit_bootstrap3-datepicker/lib/bootstrap-datepicker/node_modules/phantomjs-prebuilt/lib/phantom/bin/phantomjs
rm -f $SNAPCRAFT_PART_INSTALL/programs/server/npm/node_modules/tar/lib/.mkdir.js.swp
rm -f $SNAPCRAFT_PART_INSTALL/lib/node_modules/node-pre-gyp/node_modules/tar/lib/.mkdir.js.swp
rm -f $SNAPCRAFT_PART_INSTALL/lib/node_modules/node-gyp/node_modules/tar/lib/.mkdir.js.swp
# Meteor 1.8.x additional .swp remove
rm -f $SNAPCRAFT_PART_INSTALL/programs/server/node_modules/node-pre-gyp/node_modules/tar/lib/.mkdir.js.swp
organize:
README: README.wekan
prime:
- -lib/node_modules/node-pre-gyp/node_modules/tar/lib/.unpack.js.swp
helpers:
source: snap-src
plugin: dump
caddy:
plugin: dump
source: https://caddyserver.com/download/linux/amd64?license=personal&telemetry=off
source-type: tar
organize:
caddy: bin/caddy
CHANGES.txt: CADDY_CHANGES.txt
EULA.txt: CADDY_EULA.txt
LICENSES.txt: CADDY_LICENSES.txt
README.txt: CADDY_README.txt
stage:
- -init
+853
View File
@@ -0,0 +1,853 @@
const DateString = Match.Where(function(dateAsString) {
check(dateAsString, String);
return moment(dateAsString, moment.ISO_8601).isValid();
});
export class WekanCreator {
constructor(data) {
// we log current date, to use the same timestamp for all our actions.
// this helps to retrieve all elements performed by the same import.
this._nowDate = new Date();
// The object creation dates, indexed by Wekan id
// (so we only parse actions once!)
this.createdAt = {
board: null,
cards: {},
lists: {},
swimlanes: {},
};
// The object creator Wekan Id, indexed by the object Wekan id
// (so we only parse actions once!)
this.createdBy = {
cards: {}, // only cards have a field for that
};
// Map of labels Wekan ID => Wekan ID
this.labels = {};
// Map of swimlanes Wekan ID => Wekan ID
this.swimlanes = {};
// Map of lists Wekan ID => Wekan ID
this.lists = {};
// Map of cards Wekan ID => Wekan ID
this.cards = {};
// Map of comments Wekan ID => Wekan ID
this.commentIds = {};
// Map of attachments Wekan ID => Wekan ID
this.attachmentIds = {};
// Map of checklists Wekan ID => Wekan ID
this.checklists = {};
// Map of checklistItems Wekan ID => Wekan ID
this.checklistItems = {};
// The comments, indexed by Wekan card id (to map when importing cards)
this.comments = {};
// Map of rules Wekan ID => Wekan ID
this.rules = {};
// the members, indexed by Wekan member id => Wekan user ID
this.members = data.membersMapping ? data.membersMapping : {};
// Map of triggers Wekan ID => Wekan ID
this.triggers = {};
// Map of actions Wekan ID => Wekan ID
this.actions = {};
// maps a wekanCardId to an array of wekanAttachments
this.attachments = {};
}
/**
* If dateString is provided,
* return the Date it represents.
* If not, will return the date when it was first called.
* This is useful for us, as we want all import operations to
* have the exact same date for easier later retrieval.
*
* @param {String} dateString a properly formatted Date
*/
_now(dateString) {
if (dateString) {
return new Date(dateString);
}
if (!this._nowDate) {
this._nowDate = new Date();
}
return this._nowDate;
}
/**
* if wekanUserId is provided and we have a mapping,
* return it.
* Otherwise return current logged user.
* @param wekanUserId
* @private
*/
_user(wekanUserId) {
if (wekanUserId && this.members[wekanUserId]) {
return this.members[wekanUserId];
}
return Meteor.userId();
}
checkActivities(wekanActivities) {
check(wekanActivities, [
Match.ObjectIncluding({
activityType: String,
createdAt: DateString,
}),
]);
// XXX we could perform more thorough checks based on action type
}
checkBoard(wekanBoard) {
check(
wekanBoard,
Match.ObjectIncluding({
archived: Boolean,
title: String,
// XXX refine control by validating 'color' against a list of
// allowed values (is it worth the maintenance?)
color: String,
permission: Match.Where(value => {
return ['private', 'public'].indexOf(value) >= 0;
}),
}),
);
}
checkCards(wekanCards) {
check(wekanCards, [
Match.ObjectIncluding({
archived: Boolean,
dateLastActivity: DateString,
labelIds: [String],
title: String,
sort: Number,
}),
]);
}
checkLabels(wekanLabels) {
check(wekanLabels, [
Match.ObjectIncluding({
// XXX refine control by validating 'color' against a list of allowed
// values (is it worth the maintenance?)
color: String,
}),
]);
}
checkLists(wekanLists) {
check(wekanLists, [
Match.ObjectIncluding({
archived: Boolean,
title: String,
}),
]);
}
checkSwimlanes(wekanSwimlanes) {
check(wekanSwimlanes, [
Match.ObjectIncluding({
archived: Boolean,
title: String,
}),
]);
}
checkChecklists(wekanChecklists) {
check(wekanChecklists, [
Match.ObjectIncluding({
cardId: String,
title: String,
}),
]);
}
checkChecklistItems(wekanChecklistItems) {
check(wekanChecklistItems, [
Match.ObjectIncluding({
cardId: String,
title: String,
}),
]);
}
checkRules(wekanRules) {
check(wekanRules, [
Match.ObjectIncluding({
triggerId: String,
actionId: String,
title: String,
}),
]);
}
checkTriggers(wekanTriggers) {
// XXX More check based on trigger type
check(wekanTriggers, [
Match.ObjectIncluding({
activityType: String,
desc: String,
}),
]);
}
getMembersToMap(data) {
// we will work on the list itself (an ordered array of objects) when a
// mapping is done, we add a 'wekan' field to the object representing the
// imported member
const membersToMap = data.members;
const users = data.users;
// auto-map based on username
membersToMap.forEach(importedMember => {
importedMember.id = importedMember.userId;
delete importedMember.userId;
const user = users.filter(user => {
return user._id === importedMember.id;
})[0];
if (user.profile && user.profile.fullname) {
importedMember.fullName = user.profile.fullname;
}
importedMember.username = user.username;
const wekanUser = Users.findOne({ username: importedMember.username });
if (wekanUser) {
importedMember.wekanId = wekanUser._id;
}
});
return membersToMap;
}
checkActions(wekanActions) {
// XXX More check based on action type
check(wekanActions, [
Match.ObjectIncluding({
actionType: String,
desc: String,
}),
]);
}
// You must call parseActions before calling this one.
createBoardAndLabels(boardToImport) {
const boardToCreate = {
archived: boardToImport.archived,
color: boardToImport.color,
// very old boards won't have a creation activity so no creation date
createdAt: this._now(boardToImport.createdAt),
labels: [],
members: [
{
userId: Meteor.userId(),
wekanId: Meteor.userId(),
isActive: true,
isAdmin: true,
isNoComments: false,
isCommentOnly: false,
swimlaneId: false,
},
],
// Standalone Export has modifiedAt missing, adding modifiedAt to fix it
modifiedAt: this._now(boardToImport.modifiedAt),
permission: boardToImport.permission,
slug: getSlug(boardToImport.title) || 'board',
stars: 0,
title: boardToImport.title,
};
// now add other members
if (boardToImport.members) {
boardToImport.members.forEach(wekanMember => {
// do we already have it in our list?
if (
!boardToCreate.members.some(
member => member.wekanId === wekanMember.wekanId,
)
)
boardToCreate.members.push({
...wekanMember,
userId: wekanMember.wekanId,
});
});
}
boardToImport.labels.forEach(label => {
const labelToCreate = {
_id: Random.id(6),
color: label.color,
name: label.name,
};
// We need to remember them by Wekan ID, as this is the only ref we have
// when importing cards.
this.labels[label._id] = labelToCreate._id;
boardToCreate.labels.push(labelToCreate);
});
const boardId = Boards.direct.insert(boardToCreate);
Boards.direct.update(boardId, {
$set: {
modifiedAt: this._now(),
},
});
// log activity
Activities.direct.insert({
activityType: 'importBoard',
boardId,
createdAt: this._now(),
source: {
id: boardToImport.id,
system: 'Wekan',
},
// We attribute the import to current user,
// not the author from the original object.
userId: this._user(),
});
return boardId;
}
/**
* Create the Wekan cards corresponding to the supplied Wekan cards,
* as well as all linked data: activities, comments, and attachments
* @param wekanCards
* @param boardId
* @returns {Array}
*/
createCards(wekanCards, boardId) {
const result = [];
wekanCards.forEach(card => {
const cardToCreate = {
archived: card.archived,
boardId,
// very old boards won't have a creation activity so no creation date
createdAt: this._now(this.createdAt.cards[card._id]),
dateLastActivity: this._now(),
description: card.description,
listId: this.lists[card.listId],
swimlaneId: this.swimlanes[card.swimlaneId],
sort: card.sort,
title: card.title,
// we attribute the card to its creator if available
userId: this._user(this.createdBy.cards[card._id]),
isOvertime: card.isOvertime || false,
startAt: card.startAt ? this._now(card.startAt) : null,
dueAt: card.dueAt ? this._now(card.dueAt) : null,
spentTime: card.spentTime || null,
};
// add labels
if (card.labelIds) {
cardToCreate.labelIds = card.labelIds.map(wekanId => {
return this.labels[wekanId];
});
}
// add members {
if (card.members) {
const wekanMembers = [];
// we can't just map, as some members may not have been mapped
card.members.forEach(sourceMemberId => {
if (this.members[sourceMemberId]) {
const wekanId = this.members[sourceMemberId];
// we may map multiple Wekan members to the same wekan user
// in which case we risk adding the same user multiple times
if (!wekanMembers.find(wId => wId === wekanId)) {
wekanMembers.push(wekanId);
}
}
return true;
});
if (wekanMembers.length > 0) {
cardToCreate.members = wekanMembers;
}
}
// set color
if (card.color) {
cardToCreate.color = card.color;
}
// insert card
const cardId = Cards.direct.insert(cardToCreate);
// keep track of Wekan id => Wekan id
this.cards[card._id] = cardId;
// // log activity
// Activities.direct.insert({
// activityType: 'importCard',
// boardId,
// cardId,
// createdAt: this._now(),
// listId: cardToCreate.listId,
// source: {
// id: card._id,
// system: 'Wekan',
// },
// // we attribute the import to current user,
// // not the author of the original card
// userId: this._user(),
// });
// add comments
const comments = this.comments[card._id];
if (comments) {
comments.forEach(comment => {
const commentToCreate = {
boardId,
cardId,
createdAt: this._now(comment.createdAt),
text: comment.text,
// we attribute the comment to the original author, default to current user
userId: this._user(comment.userId),
};
// dateLastActivity will be set from activity insert, no need to
// update it ourselves
const commentId = CardComments.direct.insert(commentToCreate);
this.commentIds[comment._id] = commentId;
// Activities.direct.insert({
// activityType: 'addComment',
// boardId: commentToCreate.boardId,
// cardId: commentToCreate.cardId,
// commentId,
// createdAt: this._now(commentToCreate.createdAt),
// // we attribute the addComment (not the import)
// // to the original author - it is needed by some UI elements.
// userId: commentToCreate.userId,
// });
});
}
const attachments = this.attachments[card._id];
const wekanCoverId = card.coverId;
if (attachments) {
attachments.forEach(att => {
const file = new FS.File();
// Simulating file.attachData on the client generates multiple errors
// - HEAD returns null, which causes exception down the line
// - the template then tries to display the url to the attachment which causes other errors
// so we make it server only, and let UI catch up once it is done, forget about latency comp.
const self = this;
if (Meteor.isServer) {
if (att.url) {
file.attachData(att.url, function(error) {
file.boardId = boardId;
file.cardId = cardId;
file.userId = self._user(att.userId);
// The field source will only be used to prevent adding
// attachments' related activities automatically
file.source = 'import';
if (error) {
throw error;
} else {
const wekanAtt = Attachments.insert(file, () => {
// we do nothing
});
self.attachmentIds[att._id] = wekanAtt._id;
//
if (wekanCoverId === att._id) {
Cards.direct.update(cardId, {
$set: {
coverId: wekanAtt._id,
},
});
}
}
});
} else if (att.file) {
file.attachData(
new Buffer(att.file, 'base64'),
{
type: att.type,
},
error => {
file.name(att.name);
file.boardId = boardId;
file.cardId = cardId;
file.userId = self._user(att.userId);
// The field source will only be used to prevent adding
// attachments' related activities automatically
file.source = 'import';
if (error) {
throw error;
} else {
const wekanAtt = Attachments.insert(file, () => {
// we do nothing
});
this.attachmentIds[att._id] = wekanAtt._id;
//
if (wekanCoverId === att._id) {
Cards.direct.update(cardId, {
$set: {
coverId: wekanAtt._id,
},
});
}
}
},
);
}
}
// todo XXX set cover - if need be
});
}
result.push(cardId);
});
return result;
}
// Create labels if they do not exist and load this.labels.
createLabels(wekanLabels, board) {
wekanLabels.forEach(label => {
const color = label.color;
const name = label.name;
const existingLabel = board.getLabel(name, color);
if (existingLabel) {
this.labels[label.id] = existingLabel._id;
} else {
const idLabelCreated = board.pushLabel(name, color);
this.labels[label.id] = idLabelCreated;
}
});
}
createLists(wekanLists, boardId) {
wekanLists.forEach((list, listIndex) => {
const listToCreate = {
archived: list.archived,
boardId,
// We are being defensing here by providing a default date (now) if the
// creation date wasn't found on the action log. This happen on old
// Wekan boards (eg from 2013) that didn't log the 'createList' action
// we require.
createdAt: this._now(this.createdAt.lists[list.id]),
title: list.title,
sort: list.sort ? list.sort : listIndex,
};
const listId = Lists.direct.insert(listToCreate);
Lists.direct.update(listId, {
$set: {
updatedAt: this._now(),
},
});
this.lists[list._id] = listId;
// // log activity
// Activities.direct.insert({
// activityType: 'importList',
// boardId,
// createdAt: this._now(),
// listId,
// source: {
// id: list._id,
// system: 'Wekan',
// },
// // We attribute the import to current user,
// // not the creator of the original object
// userId: this._user(),
// });
});
}
createSwimlanes(wekanSwimlanes, boardId) {
wekanSwimlanes.forEach((swimlane, swimlaneIndex) => {
const swimlaneToCreate = {
archived: swimlane.archived,
boardId,
// We are being defensing here by providing a default date (now) if the
// creation date wasn't found on the action log. This happen on old
// Wekan boards (eg from 2013) that didn't log the 'createList' action
// we require.
createdAt: this._now(this.createdAt.swimlanes[swimlane._id]),
title: swimlane.title,
sort: swimlane.sort ? swimlane.sort : swimlaneIndex,
};
// set color
if (swimlane.color) {
swimlaneToCreate.color = swimlane.color;
}
const swimlaneId = Swimlanes.direct.insert(swimlaneToCreate);
Swimlanes.direct.update(swimlaneId, {
$set: {
updatedAt: this._now(),
},
});
this.swimlanes[swimlane._id] = swimlaneId;
});
}
createChecklists(wekanChecklists) {
const result = [];
wekanChecklists.forEach((checklist, checklistIndex) => {
// Create the checklist
const checklistToCreate = {
cardId: this.cards[checklist.cardId],
title: checklist.title,
createdAt: checklist.createdAt,
sort: checklist.sort ? checklist.sort : checklistIndex,
};
const checklistId = Checklists.direct.insert(checklistToCreate);
this.checklists[checklist._id] = checklistId;
result.push(checklistId);
});
return result;
}
createTriggers(wekanTriggers, boardId) {
wekanTriggers.forEach(trigger => {
if (trigger.hasOwnProperty('labelId')) {
trigger.labelId = this.labels[trigger.labelId];
}
if (trigger.hasOwnProperty('memberId')) {
trigger.memberId = this.members[trigger.memberId];
}
trigger.boardId = boardId;
const oldId = trigger._id;
delete trigger._id;
this.triggers[oldId] = Triggers.direct.insert(trigger);
});
}
createActions(wekanActions, boardId) {
wekanActions.forEach(action => {
if (action.hasOwnProperty('labelId')) {
action.labelId = this.labels[action.labelId];
}
if (action.hasOwnProperty('memberId')) {
action.memberId = this.members[action.memberId];
}
action.boardId = boardId;
const oldId = action._id;
delete action._id;
this.actions[oldId] = Actions.direct.insert(action);
});
}
createRules(wekanRules, boardId) {
wekanRules.forEach(rule => {
// Create the rule
rule.boardId = boardId;
rule.triggerId = this.triggers[rule.triggerId];
rule.actionId = this.actions[rule.actionId];
delete rule._id;
Rules.direct.insert(rule);
});
}
createChecklistItems(wekanChecklistItems) {
wekanChecklistItems.forEach((checklistitem, checklistitemIndex) => {
// Create the checklistItem
const checklistItemTocreate = {
title: checklistitem.title,
checklistId: this.checklists[checklistitem.checklistId],
cardId: this.cards[checklistitem.cardId],
sort: checklistitem.sort ? checklistitem.sort : checklistitemIndex,
isFinished: checklistitem.isFinished,
};
const checklistItemId = ChecklistItems.direct.insert(
checklistItemTocreate,
);
this.checklistItems[checklistitem._id] = checklistItemId;
});
}
parseActivities(wekanBoard) {
wekanBoard.activities.forEach(activity => {
switch (activity.activityType) {
case 'addAttachment': {
// We have to be cautious, because the attachment could have been removed later.
// In that case Wekan still reports its addition, but removes its 'url' field.
// So we test for that
const wekanAttachment = wekanBoard.attachments.filter(attachment => {
return attachment._id === activity.attachmentId;
})[0];
if (typeof wekanAttachment !== 'undefined' && wekanAttachment) {
if (wekanAttachment.url || wekanAttachment.file) {
// we cannot actually create the Wekan attachment, because we don't yet
// have the cards to attach it to, so we store it in the instance variable.
const wekanCardId = activity.cardId;
if (!this.attachments[wekanCardId]) {
this.attachments[wekanCardId] = [];
}
this.attachments[wekanCardId].push(wekanAttachment);
}
}
break;
}
case 'addComment': {
const wekanComment = wekanBoard.comments.filter(comment => {
return comment._id === activity.commentId;
})[0];
const id = activity.cardId;
if (!this.comments[id]) {
this.comments[id] = [];
}
this.comments[id].push(wekanComment);
break;
}
case 'createBoard': {
this.createdAt.board = activity.createdAt;
break;
}
case 'createCard': {
const cardId = activity.cardId;
this.createdAt.cards[cardId] = activity.createdAt;
this.createdBy.cards[cardId] = activity.userId;
break;
}
case 'createList': {
const listId = activity.listId;
this.createdAt.lists[listId] = activity.createdAt;
break;
}
case 'createSwimlane': {
const swimlaneId = activity.swimlaneId;
this.createdAt.swimlanes[swimlaneId] = activity.createdAt;
break;
}
}
});
}
importActivities(activities, boardId) {
activities.forEach(activity => {
switch (activity.activityType) {
// Board related activities
// TODO: addBoardMember, removeBoardMember
case 'createBoard': {
Activities.direct.insert({
userId: this._user(activity.userId),
type: 'board',
activityTypeId: boardId,
activityType: activity.activityType,
boardId,
createdAt: this._now(activity.createdAt),
});
break;
}
// List related activities
// TODO: removeList, archivedList
case 'createList': {
Activities.direct.insert({
userId: this._user(activity.userId),
type: 'list',
activityType: activity.activityType,
listId: this.lists[activity.listId],
boardId,
createdAt: this._now(activity.createdAt),
});
break;
}
// Card related activities
// TODO: archivedCard, restoredCard, joinMember, unjoinMember
case 'createCard': {
Activities.direct.insert({
userId: this._user(activity.userId),
activityType: activity.activityType,
listId: this.lists[activity.listId],
cardId: this.cards[activity.cardId],
boardId,
createdAt: this._now(activity.createdAt),
});
break;
}
case 'moveCard': {
Activities.direct.insert({
userId: this._user(activity.userId),
oldListId: this.lists[activity.oldListId],
activityType: activity.activityType,
listId: this.lists[activity.listId],
cardId: this.cards[activity.cardId],
boardId,
createdAt: this._now(activity.createdAt),
});
break;
}
// Comment related activities
case 'addComment': {
Activities.direct.insert({
userId: this._user(activity.userId),
activityType: activity.activityType,
cardId: this.cards[activity.cardId],
commentId: this.commentIds[activity.commentId],
boardId,
createdAt: this._now(activity.createdAt),
});
break;
}
// Attachment related activities
case 'addAttachment': {
Activities.direct.insert({
userId: this._user(activity.userId),
type: 'card',
activityType: activity.activityType,
attachmentId: this.attachmentIds[activity.attachmentId],
cardId: this.cards[activity.cardId],
boardId,
createdAt: this._now(activity.createdAt),
});
break;
}
// Checklist related activities
case 'addChecklist': {
Activities.direct.insert({
userId: this._user(activity.userId),
activityType: activity.activityType,
cardId: this.cards[activity.cardId],
checklistId: this.checklists[activity.checklistId],
boardId,
createdAt: this._now(activity.createdAt),
});
break;
}
case 'addChecklistItem': {
Activities.direct.insert({
userId: this._user(activity.userId),
activityType: activity.activityType,
cardId: this.cards[activity.cardId],
checklistId: this.checklists[activity.checklistId],
checklistItemId: activity.checklistItemId.replace(
activity.checklistId,
this.checklists[activity.checklistId],
),
boardId,
createdAt: this._now(activity.createdAt),
});
break;
}
}
});
}
//check(board) {
check() {
//try {
// check(data, {
// membersMapping: Match.Optional(Object),
// });
// this.checkActivities(board.activities);
// this.checkBoard(board);
// this.checkLabels(board.labels);
// this.checkLists(board.lists);
// this.checkSwimlanes(board.swimlanes);
// this.checkCards(board.cards);
//this.checkChecklists(board.checklists);
// this.checkRules(board.rules);
// this.checkActions(board.actions);
//this.checkTriggers(board.triggers);
//this.checkChecklistItems(board.checklistItems);
//} catch (e) {
// throw new Meteor.Error('error-json-schema');
// }
}
create(board, currentBoardId) {
// TODO : Make isSandstorm variable global
const isSandstorm =
Meteor.settings &&
Meteor.settings.public &&
Meteor.settings.public.sandstorm;
if (isSandstorm && currentBoardId) {
const currentBoard = Boards.findOne(currentBoardId);
currentBoard.archive();
}
this.parseActivities(board);
const boardId = this.createBoardAndLabels(board);
this.createLists(board.lists, boardId);
this.createSwimlanes(board.swimlanes, boardId);
this.createCards(board.cards, boardId);
this.createChecklists(board.checklists);
this.createChecklistItems(board.checklistItems);
this.importActivities(board.activities, boardId);
this.createTriggers(board.triggers, boardId);
this.createActions(board.actions, boardId);
this.createRules(board.rules, boardId);
// XXX add members
return boardId;
}
}
+2 -2
View File
@@ -1,9 +1,9 @@
dist: disco
dist: eoan
sudo: required
env:
TRAVIS_DOCKER_COMPOSE_VERSION: 1.24.0
TRAVIS_NODE_VERSION: 8.17.0
TRAVIS_NODE_VERSION: 12.15.0
TRAVIS_NPM_VERSION: latest
before_install:
+399 -1
View File
@@ -1,3 +1,401 @@
# v3.87 2020-04-01 Wekan release
This release makes the following UI changes:
- [Move "Rules" from "Board View" to "Board Settings"](https://github.com/wekan/wekan/issues/2973).
Thanks to helioguardabaxo and xet7.
- [Improvements on card details visualization](https://github.com/wekan/wekan/issues/2974).
Thanks to helioguardabaxo and xet7.
- [Hide duplicate "Hide system messages" at Change Settings/Member Settings, because it's also on card
slider](https://github.com/wekan/wekan/issues/2837).
Thanks to notohiro and xet7.
and fixes the following bugs:
- [Fix Browser always reload the whole page when I change one of the card
color](https://github.com/wekan/wekan/commit/3546d7aa02bc65cf1183cb493adeb543ba51945d).
Fixed by making label colors and text again editable.
Regression from [Wekan v3.86 2)](https://github.com/wekan/wekan/commit/b9099a8b7ea6f63c79bdcbb871cb993b2cb7e325).
Thanks to javen9881 and xet7.
- [Fix richer editor submit did not clear edit area](https://github.com/wekan/wekan/commit/033d6710470b2ecd7a0ec0b2f0741ff459e68b32).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.86 2020-03-24 Wekan release
This release fixes the following bugs:
- [Fix Rich editor can not be disabled, regression from changes yesterday at Wekan v3.85](https://github.com/wekan/wekan/commit/12ab8fac5db9c5ac8069d0ca2bca340d6004a25b).
Thanks to uusijani, vjrj and xet7.
- [1) Fix Pasting text into a card is adding a line before and after
(and multiplies by pasting more) by changing paste "p" to "br".
2) Fixes to summernote and markdown comment editors, related
to keeping them open when adding comments, having
@member mention not close card, and disabling clicking of
@member mention](https://github.com/wekan/wekan/commit/b9099a8b7ea6f63c79bdcbb871cb993b2cb7e325).
Thanks to xet7 !
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.85 2020-03-23 Wekan release
This release fixes the following CRITICAL SECURITY VULNERABILITIES:
- [Fix XSS bug reported today 4 hours ago by Cyb3rjunky](https://github.com/wekan/wekan/commit/482682e50079d70c5113169020d6834013b57c11).
Logged in users could run javascript in input fields.
This affects Wekan versions v3.12-v3.84.
In [Wekan v3.12](https://github.com/wekan/wekan/blob/master/CHANGELOG.md#v312-2019-08-09-wekan-release)
there was [changes for XSS filter to allow inserting images, videos etc
on comment WYSIWYG editor](https://github.com/wekan/wekan/pull/2593)
so features related to that are now removed.
After this fix, Javascript in input fields is not executed.
Thanks to Cyb3rjunky and xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.84 2020-03-16 Wekan release
This release adds the following features:
- Add settings for mouse wheel scroll inertia and scroll
amount [Part1](https://github.com/wekan/wekan/commit/9d13001b903f9ec50f5fa3a4bdbacae32b27ac65)
and [Part2](https://github.com/wekan/wekan/commit/aaecac091209e90c0c2123830728f5e7a835ccb4).
For example: sudo snap set wekan scrollinertia='200' , sudo snap set wekan scrollamount='200' .
Thanks to danger89 and xet7.
and adds the following updates:
- [Upgrade to Meteor 1.10.1](https://github.com/wekan/wekan/commit/e16c65babc1f021c35a3d46bc61e649ec94d1e82).
Thanks to xet7.
- [Update markdown](https://github.com/wekan/wekan/commit/6e0fa78022ea487176eb0a32ec5a4a441f8e0c3c).
Thanks to xet7.
- [Update minimist](https://github.com/wekan/wekan/commit/ea6baa5c2b956ee28b0a7e63f988e2fc1998201a).
Thanks to xet7.
- [Update acorn](https://github.com/wekan/wekan/commit/369a29707bbec3bf89717c16e8b698fb4666087a).
Thanks to xet7.
- [Update prettier-eslint](https://github.com/wekan/wekan/commit/8183b7bdaa01d2ce53ac7215beafd5efe21373e8).
Thanks to xet7.
- [Update ostrio:cookies](https://github.com/wekan/wekan/commit/14b8610837117616d436e2bac6a9dc653e315662).
Thanks to xet7.
- [Add build time profiling to build script](https://github.com/wekan/wekan/commit/f968109e7390139e50375ee29bc7bc3cf1e1ab41).
Thanks to zodern.
and fixes the following bugs:
- [Downgrade stylus to v1.1.0 to speed up building Wekan](https://github.com/wekan/wekan/commit/fca4cdcebf1cc6642aefeb78b911cb5b95ebe473).
This is because building newer stylus v2 takes 52 minutes. After this change, building Wekan takes 3 minutes.
Thanks to zodern.
- [Fix: Error when retrieve token from some OIDC due to not necessary scope
parameter](https://github.com/wekan/wekan/pull/2955).
Thanks to benoitm76.
- [Fix: img tag did not allow width and height. Removed swipebox from markdown editor
img tag and updated marked markdown to newest version](https://github.com/wekan/wekan/commit/2b26bbe78a1a2b8b427963a6c44c3853efdb737e).
Thanks to hradec and xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.83 2020-03-01 Wekan release
This release tries to revert remaining the following changes:
- [Revert](https://github.com/wekan/wekan/88573ad2cdb8596b795a82ef40a0662180e8a7d7) change made at Wekan v3.81,
because building did not work: [Try to make Meteor build time shorter
by excluding legacy and cordova. This was made possible by
Meteor 1.10-rc.2](https://github.com/wekan/wekan/commit/0d3002f69d97e646fa7368bfdade4f78c51e9884).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.82 2020-03-01 Wekan release
This release reverts the following changes:
- Revert change made at Wekan v3.81, because building did not work: [Try to make Meteor build time shorter
by excluding legacy and cordova. This was made possible by
Meteor 1.10-rc.2](https://github.com/wekan/wekan/commit/0d3002f69d97e646fa7368bfdade4f78c51e9884).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.81 2020-03-01 Wekan release
This release [fixes](https://github.com/wekan/wekan/commit/aac7c380c8c389b0683b2bd64e2cc856993f0e30) the following CRITICAL SECURITY VULNERABILITIES and other bugs:
- Fix critical and moderate security vulnerabilities reported at 2020-02-26 with
responsible disclosure by [Dejan Zelic](https://twitter.com/dejandayoff),
Justin Benjamin and others at [Offensive Security](https://twitter.com/offsectraining),
that follow standard 90 days before public disclosure.
Thanks to xet7.
- Fix webhook error that prevented some card etc deleting from web UI of board.
Thanks to xet7.
- Add missing Font Awesome icon to Board Settings Menu.
Thanks to xet7.
- Remove autofocus from many form input boxes so that they would not cause warnings.
Thanks to xet7.
and does the following upgrades:
- [Upgrade Meteor to 1.10-rc.2](https://github.com/wekan/wekan/commit/26b521e86e6ac40b7ba25bbe8dac7bf4d48d43ce).
Thanks to xet7.
- [Try to make Meteor build time shorter by excluding legacy and cordova. This was made possible by
Meteor 1.10-rc.2](https://github.com/wekan/wekan/commit/0d3002f69d97e646fa7368bfdade4f78c51e9884).
Thanks to xet7.
and fixes the following bugs:
- [Try to fix afterwards loading of cards by adding fallback when requestIdleCallback is not
available](https://github.com/wekan/wekan/commit/2b9540ce02de604bf84ea082f2dcb1d01673708c).
Thanks to xet7.
- [Make profile.initials available in publications](https://github.com/wekan/wekan/pull/2948).
Thanks to NicoP-S.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.80 2020-02-22 Wekan release
This release adds the following features:
- [Create New User in Admin Panel](https://github.com/wekan/wekan/commit/e0ca960a35cf006880019ba28fc82aa30f289a71).
Works, but does not save fullname yet, so currently it's needed to edit add fullname later.
Thanks to xet7.
and adds the following updates:
- [Update to Meteor 1.9.1, Node 12.16.1 etc newest dependencies](https://github.com/wekan/wekan/commit/cbbb5deff7d84a91c40becc9caaf70f5b6738b63).
Thanks to xet7.
- [Update to Meteor 1.9.2](https://github.com/wekan/wekan/commit/9be3f3714ae680ff9fc1855c960c9831e84c2b07).
Thanks to xet7.
and fixes the following bugs:
- [Update Sandstorm release build script](https://github.com/wekan/wekan/commit/a4ff6cc0af8545ca4d3e97fa2cabbe7981c025b2).
Thanks to xet7.
- [Fix docker-compose link](https://github.com/wekan/wekan/pull/2937).
Thanks to pbek.
- [Remove alethes:pages package, that had some indentation error.
Package is about pagination, but I did not find any pagination related code in Wekan
yet](https://github.com/wekan/wekan/commit/ec012060305bc16fbf8d2ac218f5c847e02c4301).
Thanks to xet7 !
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.79 2020-02-13 Wekan release
This release fixes the following bugs:
- [Fix Card Opened Webhook can not be disabled](https://github.com/wekan/wekan/commit/178f376e2138b5522c2e92ddfd2babb113df8d9f).
Thanks to mvanvoorden and xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.78 2020-02-12 Wekan release
This release adds the following features:
- [Card Settings / Show on Card: Description Title and Description Text](https://github.com/wekan/wekan/commit/e89965f6422fd95b4ad2112ae407b1dde4853510).
Thanks to e-stoniauk, 2020product and xet7.
and fixes the following bugs:
- [Remove card element grouping to create compact card layout](https://github.com/wekan/wekan/commit/e89965f6422fd95b4ad2112ae407b1dde4853510).
Thanks to e-stoniauk, 2020product and xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.77 2020-02-10 Wekan release
This release removes the following features:
- [Remove hiding comments and activities](https://github.com/wekan/wekan/commit/2a54218f3f68547032bd53a04a968b233be21e15).
Thanks to xet7.
and fixes the following bugs:
- Fix Copy Card Link to Clipboard button at card title did not
work [Part 1](https://github.com/wekan/wekan/commit/9a21b0a1c933e7f778e4e57a8258e150ccea1620)
and [Part2](https://github.com/wekan/wekan/commit/4467a68b97a3fbf0fbae7f05177d978f2aa80287).
Thanks to 2020product and xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.76 2020-02-07 Wekan release
This release adds the following updates:
- [Use Meteor 1.9 and Node.js 12.15.0 on Snap and Docker](https://github.com/wekan/wekan/commit/8384d68a060ef8f2c202ce2fa6064c5c823d28dc).
This also fixes bug that exporting some boards was not possible, downloading export file failed.
Thanks to xet7.
and fixes the following bugs:
- [Fix Bug enable/disable Comments in Card Settings](https://github.com/wekan/wekan/issues/2923).
Thanks to warnt, mdurokov and xet7.
- [Try to disable dragging Swimlanes/Lists/Cards/Checklists/Subtasks on small mobile smartphones webbrowsers,
and hide drag handles on mobile web](https://github.com/wekan/wekan/commit/bf78b093bad7d463ee325ad96e8b485264d4a3be).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.75 2020-02-05 Wekan release
This release adds the following new features:
- [Fix](https://github.com/wekan/wekan/commit/f22785dbcde42e425c9ead209ec224aef6e11c16)
[adding comments](https://github.com/wekan/wekan/issues/2918).
Thanks to xet7.
and fixes the following bugs:
- [Added some working layout changes like activities using less space from https://github.com/wekan/wekan/pull/2920](https://github.com/wekan/wekan/commit/f22785dbcde42e425c9ead209ec224aef6e11c16).
Thanks to 2020product.
- [Fixed Card Settings not working at Sandstorm](https://github.com/wekan/wekan/commit/f22785dbcde42e425c9ead209ec224aef6e11c16).
Thanks to xet7.
- Add [Card Description title](https://github.com/wekan/wekan/issues/2918#issuecomment-582346577)
[back](https://github.com/wekan/wekan/commit/f22785dbcde42e425c9ead209ec224aef6e11c16).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.74 2020-02-05 Wekan release
This release adds the following new features:
- [For BoardAdmin, add way to hide parts of a card, at Board Settings/Card Settings/Show on Card: Received, Start, ... etc.
Add to card title bar Copy card to Clipboard button](https://github.com/wekan/wekan/pull/2915).
Thanks to 2020product and xet7.
- [Set default to RICHER_CARD_COMMENT_EDITOR=false](https://github.com/wekan/wekan/commit/65fa2f626f503b8089e0d982901cffb3990426cb).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.73 2020-01-29 Wekan release
This release adds the following new features:
- [Login to Wekan with Nextcloud](https://github.com/wekan/wekan/pull/2897).
Thanks to bogie.
- [Add rule action to move cards to other boards](https://github.com/wekan/wekan/pull/2899).
Thanks to peterverraedt.
and fixes the following bugs:
- [Show System Wide Announcement in one line](https://github.com/wekan/wekan/pull/2891).
Thanks to tsia.
- [Fixed board export with attachment in Wekan Meteor 1.9.x version](https://github.com/wekan/wekan/pull/2898).
Thanks to izadpoor.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.72 2020-01-19 Sandstorm-only Wekan release
This release fixes the following bugs:
- Try to fix Wekan at Sandstorm.
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.71 2020-01-18 Sandstorm-only Wekan release
This release fixes the following bugs:
- [Try to fix Wekan at Sandstorm by using Meteor 1.8.x and Node 8.17.0 at Sandstorm](https://github.com/wekan/wekan/commit/5e5ab95410c715a4379631456fc5547c497898b0).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.70 2020-01-18 Wekan release
This release fixes the following bugs:
- [Add missing LD_LIBRARY_PATH to use libssl and libcurl](https://github.com/wekan/wekan/10f142a1a05acb98a175ccb0326fb0c1d3e3713f).
Thanks to xet7.
- [Use Meteor 1.8.x](https://github.com/wekan/wekan/commit/55a2aa90cbbf44200e9b0b9f4bd08b6177f1bb95)
[on Snap](https://github.com/wekan/wekan/commit/6a01170d8696322462c4065ce0cf4a637a058975), because
Snap builds do not work yet for Meteor 1.9, Node 12.14.1 and MongoDB 4.2.2.
Docker version works with Meteor 1.9.
Thanks to xet7.
- [Try to fix Node 12 Buffer() deprecation errors](https://github.com/wekan/wekan/commit/9b905c2833d54cf34d1875148075b2bf756d943a).
Thanks to xet7.
- [Add Snap Meteor 1.8.x files to lint ignore files](https://github.com/wekan/wekan/commit/48f8050c25e40f737dfdd3a98923cb87cd4e77e2).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.69 2020-01-10 Wekan release
This release fixes the following bugs:
- [Fix docker-compose.yml to not use --smallfiles that is not supported in
MongoDB 4.x](https://github.com/wekan/wekan/commit/ecb76842fcbd81701afcab8db0ed106e6be0fdec).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.68 2020-01-10 Wekan release
This release tries to fix the following bugs:
- [Try to fix Snap by removing MongoDB option --smallfiles that is not supported
in MongoDB 4.x](https://github.com/wekan/wekan/commit/031df54a2e0a03dcb7a2586667e60e5bd4eef706)
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.67 2020-01-10 Wekan release
This release tries to fix the following bugs:
- [Try to fix Snap](https://github.com/wekan/wekan/commit/2b382b940be9af575fab4c2e955702d8cde55ae9).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.66 2020-01-10 Wekan release
This release tries to fix the following bugs:
- [Try to fix Snap](https://github.com/wekan/wekan/commit/39bf1e375e2962f824e6f8cfa425ea51aa4efa24).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.65 2020-01-10 Wekan release
This release adds the following features:
- [More keyboard shortcuts: c for archive card](https://github.com/wekan/wekan/commit/d16a601c04aeb1d3550c5c541be02a67276a34cf).
Thanks to xet7.
and adds the following updates:
- [Upgrade to Meteor 1.9, Node 12.14.1 and MongoDB 4.2.2](https://github.com/wekan/wekan/commit/785f3cf88b61f687ef5ad4a529768221d1a54c86).
Thanks to xet7.
- [Add more issue repo links to GitHub issue template](https://github.com/wekan/wekan/commit/5724674e73246f4e52843a6d6906c0ecdd85cccc).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.64 2020-01-06 Wekan release
This release adds the following warning for CentOS 7 users:
- [WARNING: DO NOT USE SNAP ON CENTOS 7, THERE IS UPDATE BUG](https://github.com/wekan/wekan-snap/wiki/CentOS-7).
Thanks to andy-twosticks and xet7.
and adds the following features:
- [Wider sidebar](https://github.com/wekan/wekan/commit/5058233509e44916296e38fb8a6c5dd591c46d8b).
Thanks to vjrj.
and removes the following features:
- [Removed Custom HTML feature that does not work](https://github.com/wekan/wekan/commit/ddce0ada094e6450be260b4cda21fdfa09ae0133).
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v3.63 2020-01-06 Wekan release
This release fixes the following bugs:
@@ -52,7 +450,7 @@ Thanks to above GitHub users for their contributions and translators for their t
This release fixes the following bugs:
- [Fix not being able to edit received date](https://github.com/wekan/wekan/commit/5376bc7b7905c0dd99fae1aeae3f63b4583a3e3f).
Thanks to xet7.
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
+6 -4
View File
@@ -6,8 +6,8 @@ LABEL maintainer="wekan"
# ENV BUILD_DEPS="paxctl"
ENV BUILD_DEPS="apt-utils libarchive-tools gnupg gosu wget curl bzip2 g++ build-essential git ca-certificates python3" \
DEBUG=false \
NODE_VERSION=v8.17.0 \
METEOR_RELEASE=1.8.1 \
NODE_VERSION=v12.16.1 \
METEOR_RELEASE=1.10-rc.2 \
USE_EDGE=false \
METEOR_EDGE=1.5-beta.17 \
NPM_VERSION=latest \
@@ -21,7 +21,7 @@ ENV BUILD_DEPS="apt-utils libarchive-tools gnupg gosu wget curl bzip2 g++ build-
ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURES_BERORE=3 \
ACCOUNTS_LOCKOUT_UNKNOWN_USERS_LOCKOUT_PERIOD=60 \
ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURE_WINDOW=15 \
RICHER_CARD_COMMENT_EDITOR=true \
RICHER_CARD_COMMENT_EDITOR=false \
CARD_OPENED_WEBHOOK_ENABLED=false \
ATTACHMENTS_STORE_PATH="" \
MAX_IMAGE_PIXEL="" \
@@ -110,7 +110,9 @@ ENV BUILD_DEPS="apt-utils libarchive-tools gnupg gosu wget curl bzip2 g++ build-
CORS="" \
CORS_ALLOW_HEADERS="" \
CORS_EXPOSE_HEADERS="" \
DEFAULT_AUTHENTICATION_METHOD=""
DEFAULT_AUTHENTICATION_METHOD="" \
SCROLLINERTIA="0" \
SCROLLAMOUNT="auto"
# Copy the app to the image
COPY ${SRC_PATH} /home/wekan/app
+1 -1
View File
@@ -61,7 +61,7 @@ that by providing one-click installation on various platforms.
[Mac](https://github.com/wekan/wekan/wiki/Mac) / [Windows](https://github.com/wekan/wekan/wiki/Install-Wekan-from-source-on-Windows).
[More Platforms](https://github.com/wekan/wekan/wiki/Platforms), bundle for RasPi3 ARM and other CPUs where Node.js and MongoDB exists.
- 1 GB RAM minimum free for Wekan. Production server should have minimum total 4 GB RAM.
For thousands of users, for example with [Docker](https://github.com/wekan/wekan/blob/devel/docker-compose.yml): 3 frontend servers,
For thousands of users, for example with [Docker](https://github.com/wekan/wekan/blob/master/docker-compose.yml): 3 frontend servers,
each having 2 CPU and 2 wekan-app containers. One backend wekan-db server with many CPUs.
- Enough disk space and alerts about low disk space. If you run out disk space, MongoDB database gets corrupted.
- SECURITY: Updating to newest Wekan version very often. Please check you do not have automatic updates of Sandstorm or Snap turned off.
+1 -1
View File
@@ -1,5 +1,5 @@
appId: wekan-public/apps/77b94f60-dec9-0136-304e-16ff53095928
appVersion: "v3.63.0"
appVersion: "v3.87.0"
files:
userUploads:
- README.md
+3 -3
View File
@@ -201,20 +201,20 @@ template(name="cardActivities")
.activity-checklist(href="{{ card.absoluteUrl }}")
+viewer
= checklistItem.title
if(currentData.timeKey)
| {{{_ activityType }}}
= ' '
i(title=currentData.timeValue).activity-meta {{ moment currentData.timeValue 'LLL' }}
if (currentData.timeOldValue)
= ' '
= ' '
| {{{_ "previous_as" }}}
= ' '
i(title=currentData.timeOldValue).activity-meta {{ moment currentData.timeOldValue 'LLL' }}
= ' @'
else if(currentData.timeValue)
| {{{_ activityType currentData.timeValue}}}
if($eq activityType 'deleteComment')
| {{{_ 'activity-deleteComment' currentData.commentId}}}.
+1 -1
View File
@@ -9,7 +9,7 @@
clear: both
.activity
margin: 10px 0
margin: 0.5px 0
display: flex
.member
@@ -46,3 +46,23 @@
&:is-open
cursor: auto
.comment-item
background-color: #fff
border: 0
box-shadow: 0 1px 2px rgba(0, 0, 0, .23)
color: #8c8c8c
height: 36px
margin: 4px 4px 6px 0
width: 92%
&:hover
background: darken(white, 12%)
&.add-comment
display: flex
margin: 5px
a
display: block
margin: auto
+4 -6
View File
@@ -205,21 +205,19 @@ BlazeComponent.extendComponent({
} else {
showDesktopDragHandles = false;
}
if (
Utils.isMiniScreen() ||
(!Utils.isMiniScreen() && showDesktopDragHandles)
) {
if (!Utils.isMiniScreen() && showDesktopDragHandles) {
$swimlanesDom.sortable({
handle: '.js-swimlane-header-handle',
});
} else {
} else if (!Utils.isMiniScreen() && !showDesktopDragHandles) {
$swimlanesDom.sortable({
handle: '.swimlane-header',
});
}
// Disable drag-dropping if the current user is not a board member or is comment only
// Disable drag-dropping if the current user is not a board member or is miniscreen
$swimlanesDom.sortable('option', 'disabled', !userIsMember());
$swimlanesDom.sortable('option', 'disabled', Utils.isMiniScreen());
});
function userIsMember() {
-14
View File
@@ -193,20 +193,6 @@ template(name="boardChangeViewPopup")
| {{_ 'board-view-cal'}}
if $eq Utils.boardView "board-view-cal"
i.fa.fa-check
if currentUser.isAdmin
hr
li
with "board-view-rules"
a.js-open-rules-view(title="{{_ 'rules'}}")
i.fa.fa-magic
| {{_ 'rules'}}
else if currentUser.isBoardAdmin
hr
li
with "board-view-rules"
a.js-open-rules-view(title="{{_ 'rules'}}")
i.fa.fa-magic
| {{_ 'rules'}}
template(name="createBoard")
form
+1 -4
View File
@@ -30,6 +30,7 @@ Template.boardMenuPopup.events({
'click .js-outgoing-webhooks': Popup.open('outgoingWebhooks'),
'click .js-import-board': Popup.open('chooseBoardSource'),
'click .js-subtask-settings': Popup.open('boardSubtaskSettings'),
'click .js-card-settings': Popup.open('boardCardSettings'),
});
Template.boardMenuPopup.helpers({
@@ -190,10 +191,6 @@ Template.boardChangeViewPopup.events({
Utils.setBoardView('board-view-cal');
Popup.close();
},
'click .js-open-rules-view'() {
Modal.openWide('rulesMain');
Popup.close();
},
});
const CreateBoard = BlazeComponent.extendComponent({
+4 -2
View File
@@ -53,5 +53,7 @@ template(name="attachmentsGalery")
if currentUser.isBoardMember
unless currentUser.isCommentOnly
unless currentUser.isWorker
li.attachment-item.add-attachment
a.js-add-attachment {{_ 'add-attachment' }}
//li.attachment-item.add-attachment
a.js-add-attachment
i.fa.fa-plus
| {{_ 'add-attachment' }}
+190 -160
View File
@@ -8,10 +8,17 @@ template(name="cardDetails")
a.fa.fa-times-thin.close-card-details.js-close-card-details
if currentUser.isBoardMember
a.fa.fa-navicon.card-details-menu.js-open-card-details-menu
input.inline-input(type="text" id="cardURL_copy" value="{{ absoluteUrl }}")
a.fa.fa-link.card-copy-button.js-copy-link(
class="fa-link"
title="{{_ 'copy-card-link-to-clipboard'}}"
value="{{ absoluteUrl }}"
)
if isMiniScreen
a.fa.fa-times-thin.close-card-details-mobile-web.js-close-card-details
if currentUser.isBoardMember
a.fa.fa-navicon.card-details-menu-mobile-web.js-open-card-details-menu
a.fa.fa-link.card-copy-mobile-button
h2.card-details-title.js-card-title(
class="{{#if canModifyCard}}js-open-inlined-form is-editable{{/if}}")
+viewer
@@ -36,94 +43,105 @@ template(name="cardDetails")
p.warning {{_ 'card-archived'}}
.card-details-items
.card-details-item.card-details-item-received
h3
i.fa.fa-sign-out
card-details-item-title {{_ 'card-received'}}
if getReceived
+cardReceivedDate
else
if currentBoard.allowsReceivedDate
.card-details-item.card-details-item-received
h3
i.fa.fa-sign-out
card-details-item-title {{_ 'card-received'}}
if getReceived
+cardReceivedDate
else
if canModifyCard
unless currentUser.isWorker
a.card-label.add-label.js-received-date
i.fa.fa-plus
if currentBoard.allowsStartDate
.card-details-item.card-details-item-start
h3
i.fa.fa-hourglass-start
card-details-item-title {{_ 'card-start'}}
if getStart
+cardStartDate
else
if canModifyCard
unless currentUser.isWorker
a.card-label.add-label.js-start-date
i.fa.fa-plus
if currentBoard.allowsDueDate
.card-details-item.card-details-item-due
h3
i.fa.fa-sign-in
card-details-item-title {{_ 'card-due'}}
if getDue
+cardDueDate
else
if canModifyCard
unless currentUser.isWorker
a.card-label.add-label.js-due-date
i.fa.fa-plus
if currentBoard.allowsEndDate
.card-details-item.card-details-item-end
h3
i.fa.fa-hourglass-end
card-details-item-title {{_ 'card-end'}}
if getEnd
+cardEndDate
else
if canModifyCard
unless currentUser.isWorker
a.card-label.add-label.js-end-date
i.fa.fa-plus
//.card-details-items
if currentBoard.allowsMembers
.card-details-item.card-details-item-members
h3
i.fa.fa-users
card-details-item-title {{_ 'members'}}
each getMembers
+userAvatar(userId=this cardId=../_id)
| {{! XXX Hack to hide syntaxic coloration /// }}
if canModifyCard
unless currentUser.isWorker
a.js-received-date {{_ 'add'}}
a.member.add-member.card-details-item-add-button.js-add-members(title="{{_ 'card-members-title'}}")
i.fa.fa-plus
.card-details-item.card-details-item-start
h3
i.fa.fa-hourglass-start
card-details-item-title {{_ 'card-start'}}
if getStart
+cardStartDate
else
//if assigneeSelected
if currentBoard.allowsAssignee
.card-details-item.card-details-item-assignees
h3
i.fa.fa-user
card-details-item-title {{_ 'assignee'}}
each getAssignees
+userAvatarAssignee(userId=this cardId=../_id)
| {{! XXX Hack to hide syntaxic coloration /// }}
if canModifyCard
unless currentUser.isWorker
a.js-start-date {{_ 'add'}}
.card-details-item.card-details-item-due
h3
i.fa.fa-sign-in
card-details-item-title {{_ 'card-due'}}
if getDue
+cardDueDate
else
if canModifyCard
unless currentUser.isWorker
a.js-due-date {{_ 'add'}}
.card-details-item.card-details-item-end
h3
i.fa.fa-hourglass-end
card-details-item-title {{_ 'card-end'}}
if getEnd
+cardEndDate
else
if canModifyCard
unless currentUser.isWorker
a.js-end-date {{_ 'add'}}
.card-details-items
.card-details-item.card-details-item-members
h3
i.fa.fa-users
card-details-item-title {{_ 'members'}}
each getMembers
+userAvatar(userId=this cardId=../_id)
| {{! XXX Hack to hide syntaxic coloration /// }}
if canModifyCard
unless currentUser.isWorker
a.member.add-member.card-details-item-add-button.js-add-members(title="{{_ 'card-members-title'}}")
i.fa.fa-plus
.card-details-item.card-details-item-assignees
h3
i.fa.fa-user
card-details-item-title {{_ 'assignee'}}
each getAssignees
+userAvatarAssignee(userId=this cardId=../_id)
| {{! XXX Hack to hide syntaxic coloration /// }}
if canModifyCard
a.assignee.add-assignee.card-details-item-add-button.js-add-assignees(title="{{_ 'assignee'}}")
i.fa.fa-plus
if currentUser.isWorker
unless assigneeSelected
a.assignee.add-assignee.card-details-item-add-button.js-add-assignees(title="{{_ 'assignee'}}")
i.fa.fa-plus
if currentUser.isWorker
unless assigneeSelected
a.assignee.add-assignee.card-details-item-add-button.js-add-assignees(title="{{_ 'assignee'}}")
i.fa.fa-plus
if currentBoard.allowsLabels
.card-details-item.card-details-item-labels
h3
i.fa.fa-tags
card-details-item-title {{_ 'labels'}}
a(class="{{#if canModifyCard}}js-add-labels{{else}}is-disabled{{/if}}" title="{{_ 'card-labels-title'}}")
each labels
span.card-label(class="card-label-{{color}}" title=name)
+viewer
= name
if canModifyCard
unless currentUser.isWorker
a.card-label.add-label.js-add-labels(title="{{_ 'card-labels-title'}}")
i.fa.fa-plus
.card-details-item.card-details-item-labels
h3
i.fa.fa-tags
card-details-item-title {{_ 'labels'}}
a(class="{{#if canModifyCard}}js-add-labels{{else}}is-disabled{{/if}}" title="{{_ 'card-labels-title'}}")
each labels
span.card-label(class="card-label-{{color}}" title=name)
+viewer
= name
if canModifyCard
unless currentUser.isWorker
a.card-label.add-label.js-add-labels(title="{{_ 'card-labels-title'}}")
i.fa.fa-plus
.card-details-items
//.card-details-items
each customFieldsWD
.card-details-item.card-details-item-customfield
h3.card-details-item-title
@@ -131,7 +149,7 @@ template(name="cardDetails")
= definition.name
+cardCustomField
.card-details-items
//.card-details-items
if getSpentTime
.card-details-item.card-details-item-spent
if getIsOvertime
@@ -140,88 +158,99 @@ template(name="cardDetails")
h3.card-details-item-title {{_ 'spent-time-hours'}}
+cardSpentTime
//.card-details-items
if currentBoard.allowsRequestedBy
.card-details-item.card-details-item-name
h3
i.fa.fa-shopping-cart
card-details-item-title {{_ 'requested-by'}}
if canModifyCard
unless currentUser.isWorker
+inlinedForm(classNames="js-card-details-requester")
+editCardRequesterForm
else
a.js-open-inlined-form
if getRequestedBy
+viewer
= getRequestedBy
else
| {{_ 'add'}}
else if getRequestedBy
+viewer
= getRequestedBy
if currentBoard.allowsAssignedBy
.card-details-item.card-details-item-name
h3
i.fa.fa-user-plus
card-details-item-title {{_ 'assigned-by'}}
if canModifyCard
unless currentUser.isWorker
+inlinedForm(classNames="js-card-details-assigner")
+editCardAssignerForm
else
a.js-open-inlined-form
if getAssignedBy
+viewer
= getAssignedBy
else
| {{_ 'add'}}
else if getRequestedBy
+viewer
= getAssignedBy
//- XXX We should use "editable" to avoid repetiting ourselves
if canModifyCard
unless currentUser.isWorker
h3
i.fa.fa-align-left
card-details-item-title {{_ 'description'}}
+inlinedCardDescription(classNames="card-description js-card-description")
+editor(autofocus=true)
| {{getUnsavedValue 'cardDescription' _id getDescription}}
.edit-controls.clearfix
button.primary(type="submit") {{_ 'save'}}
a.fa.fa-times-thin.js-close-inlined-form
else
a.js-open-inlined-form
if getDescription
+viewer
= getDescription
else
| {{_ 'edit'}}
if (hasUnsavedValue 'cardDescription' _id)
p.quiet
| {{_ 'unsaved-description'}}
a.js-open-inlined-form {{_ 'view-it'}}
= ' - '
a.js-close-inlined-form {{_ 'discard'}}
if currentBoard.allowsDescriptionTitle
hr
h3
i.fa.fa-align-left
card-details-item-title {{_ 'description'}}
if currentBoard.allowsDescriptionText
+inlinedCardDescription(classNames="card-description js-card-description")
+editor(autofocus=true)
| {{getUnsavedValue 'cardDescription' _id getDescription}}
.edit-controls.clearfix
button.primary(type="submit") {{_ 'save'}}
a.fa.fa-times-thin.js-close-inlined-form
else
if currentBoard.allowsDescriptionText
a.js-open-inlined-form
if getDescription
+viewer
= getDescription
else
| {{_ 'edit'}}
if (hasUnsavedValue 'cardDescription' _id)
p.quiet
| {{_ 'unsaved-description'}}
a.js-open-inlined-form {{_ 'view-it'}}
= ' - '
a.js-close-inlined-form {{_ 'discard'}}
else if getDescription
h3.card-details-item-title {{_ 'description'}}
+viewer
= getDescription
if currentBoard.allowsDescriptionTitle
hr
h3.card-details-item-title {{_ 'description'}}
if currentBoard.allowsDescriptionText
+viewer
= getDescription
.card-details-items
.card-details-item.card-details-item-name
.card-checklist-attachmentGalerys
.card-checklist-attachmentGalery.card-checklists
if currentBoard.allowsChecklists
hr
+checklists(cardId = _id)
if currentBoard.allowsSubtasks
hr
+subtasks(cardId = _id)
if currentBoard.allowsAttachments
hr
h3
i.fa.fa-shopping-cart
card-details-item-title {{_ 'requested-by'}}
if canModifyCard
unless currentUser.isWorker
+inlinedForm(classNames="js-card-details-requester")
+editCardRequesterForm
else
a.js-open-inlined-form
if getRequestedBy
+viewer
= getRequestedBy
else
| {{_ 'add'}}
else if getRequestedBy
+viewer
= getRequestedBy
.card-details-item.card-details-item-name
h3
i.fa.fa-user-plus
card-details-item-title {{_ 'assigned-by'}}
if canModifyCard
unless currentUser.isWorker
+inlinedForm(classNames="js-card-details-assigner")
+editCardAssignerForm
else
a.js-open-inlined-form
if getAssignedBy
+viewer
= getAssignedBy
else
| {{_ 'add'}}
else if getRequestedBy
+viewer
= getAssignedBy
hr
+checklists(cardId = _id)
if currentBoard.allowsSubtasks
hr
+subtasks(cardId = _id)
hr
h3
i.fa.fa-paperclip
| {{_ 'attachments'}}
+attachmentsGalery
i.fa.fa-paperclip
| {{_ 'attachments'}}
.card-checklist-attachmentGalery.card-attachmentGalery
+attachmentsGalery
hr
unless currentUser.isNoComments
@@ -237,9 +266,10 @@ template(name="cardDetails")
else
input.toggle-switch(type="checkbox" id="toggleButton")
label.toggle-label(for="toggleButton")
if currentUser.isBoardMember
unless currentUser.isNoComments
+commentForm
if currentBoard.allowsComments
if currentUser.isBoardMember
unless currentUser.isNoComments
+commentForm
unless currentUser.isNoComments
if isLoaded.get
if isLinkedCard
+93
View File
@@ -253,6 +253,12 @@ BlazeComponent.extendComponent({
if ($subtasksDom.data('sortable')) {
$subtasksDom.sortable('option', 'disabled', !userIsMember());
}
if ($checklistsDom.data('sortable')) {
$checklistsDom.sortable('option', 'disabled', Utils.isMiniScreen());
}
if ($subtasksDom.data('sortable')) {
$subtasksDom.sortable('option', 'disabled', Utils.isMiniScreen());
}
});
},
@@ -279,6 +285,29 @@ BlazeComponent.extendComponent({
'click .js-close-card-details'() {
Utils.goBoardId(this.data().boardId);
},
'click .js-copy-link'() {
StringToCopyElement = document.getElementById('cardURL_copy');
StringToCopyElement.select();
if (document.execCommand('copy')) {
StringToCopyElement.blur();
} else {
document.getElementById('cardURL_copy').selectionStart = 0;
document.getElementById('cardURL_copy').selectionEnd = 999;
document.execCommand('copy');
if (window.getSelection) {
if (window.getSelection().empty) {
// Chrome
window.getSelection().empty();
} else if (window.getSelection().removeAllRanges) {
// Firefox
window.getSelection().removeAllRanges();
}
} else if (document.selection) {
// IE?
document.selection.empty();
}
}
},
'click .js-open-card-details-menu': Popup.open('cardDetailsActions'),
'submit .js-card-description'(event) {
event.preventDefault();
@@ -371,6 +400,54 @@ Template.cardDetails.helpers({
});
},
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;
@@ -379,6 +456,22 @@ Template.cardDetails.helpers({
}
},
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';
+18
View File
@@ -4,6 +4,12 @@
avatar-radius = 50%
#cardURL_copy
// Have clipboard text not visible by moving it to far left
position: absolute
left: -2000px
top: 0px
.assignee
border-radius: 3px
display: block
@@ -108,6 +114,8 @@ avatar-radius = 50%
.close-card-details,
.card-details-menu,
.card-copy-button,
.card-copy-mobile-button,
.close-card-details-mobile-web,
.card-details-menu-mobile-web
float: right
@@ -122,6 +130,16 @@ avatar-radius = 50%
padding: 5px
margin-right: 40px
.card-copy-button
font-size: 17px
padding: 10px
margin-right: 10px
.card-copy-mobile-button
font-size: 17px
padding: 10px
margin-right: 10px
.card-details-menu
font-size: 17px
padding: 10px
+3
View File
@@ -60,6 +60,9 @@ BlazeComponent.extendComponent({
if ($itemsDom.data('sortable')) {
$(self.itemsDom).sortable('option', 'disabled', !userIsMember());
}
if ($itemsDom.data('sortable')) {
$(self.itemsDom).sortable('option', 'disabled', Utils.isMiniScreen());
}
});
},
+2 -2
View File
@@ -4,8 +4,8 @@ template(name="minicard")
class="{{#if isLinkedBoard}}linked-board{{/if}}"
class="minicard-{{colorClass}}")
if isMiniScreen
.handle
.fa.fa-arrows
//.handle
// .fa.fa-arrows
unless isMiniScreen
if showDesktopDragHandles
.handle
+22 -3
View File
@@ -133,14 +133,33 @@ BlazeComponent.extendComponent({
$cards.sortable({
handle: '.handle',
});
} else {
} else if (!Utils.isMiniScreen() && !showDesktopDragHandles) {
$cards.sortable({
handle: '.minicard',
});
}
// Disable drag-dropping if the current user is not a board member or is comment only
$cards.sortable('option', 'disabled', !userIsMember());
if ($cards.data('sortable')) {
$cards.sortable(
'option',
'disabled',
// Disable drag-dropping when user is not member/is miniscreen
!userIsMember(),
// Not disable drag-dropping while in multi-selection mode
// MultiSelection.isActive() || !userIsMember(),
);
}
if ($cards.data('sortable')) {
$cards.sortable(
'option',
'disabled',
// Disable drag-dropping when user is not member/is miniscreen
Utils.isMiniScreen(),
// Not disable drag-dropping while in multi-selection mode
// MultiSelection.isActive() || !userIsMember(),
);
}
});
// We want to re-run this function any time a card is added.
+17 -1
View File
@@ -743,9 +743,25 @@ BlazeComponent.extendComponent({
},
updateList() {
// Use fallback when requestIdleCallback is not available on iOS and Safari
// https://www.afasterweb.com/2017/11/20/utilizing-idle-moments/
checkIdleTime =
window.requestIdleCallback ||
function(handler) {
const startTime = Date.now();
return setTimeout(function() {
handler({
didTimeout: false,
timeRemaining() {
return Math.max(0, 50.0 - (Date.now() - startTime));
},
});
}, 1);
};
if (this.spinnerInView()) {
this.cardlimit.set(this.cardlimit.get() + InfiniteScrollIter);
window.requestIdleCallback(() => this.updateList());
checkIdleTime(() => this.updateList());
}
},
+2 -2
View File
@@ -30,10 +30,10 @@ template(name="listHeader")
if canSeeAddCard
a.js-add-card.fa.fa-plus.list-header-plus-icon
a.fa.fa-navicon.js-open-list-menu
a.list-header-handle.handle.fa.fa-arrows.js-list-handle
//a.list-header-handle.handle.fa.fa-arrows.js-list-handle
else
a.list-header-menu-icon.fa.fa-angle-right.js-select-list
a.list-header-handle.handle.fa.fa-arrows.js-list-handle
//a.list-header-handle.handle.fa.fa-arrows.js-list-handle
else if currentUser.isBoardMember
if isWatching
i.list-header-watch-icon.fa.fa-eye
+64 -128
View File
@@ -1,87 +1,3 @@
import _sanitizeXss from 'xss';
const ASIS = 'asis';
const sanitizeXss = (input, options) => {
const defaultAllowedIframeSrc = /^(https:){0,1}\/\/.*?(youtube|vimeo|dailymotion|youku)/i;
const allowedIframeSrcRegex = (function() {
let reg = defaultAllowedIframeSrc;
const SAFE_IFRAME_SRC_PATTERN =
Meteor.settings.public.SAFE_IFRAME_SRC_PATTERN;
try {
if (SAFE_IFRAME_SRC_PATTERN !== undefined) {
reg = new RegExp(SAFE_IFRAME_SRC_PATTERN, 'i');
}
} catch (e) {
/*eslint no-console: ["error", { allow: ["warn", "error"] }] */
console.error('Wrong pattern specified', SAFE_IFRAM_SRC_PATTERN, e);
}
return reg;
})();
const targetWindow = '_blank';
const getHtmlDOM = html => {
const i = document.createElement('i');
i.innerHTML = html;
return i.firstChild;
};
options = {
onTag(tag, html, options) {
const htmlDOM = getHtmlDOM(html);
const getAttr = attr => {
return htmlDOM && attr && htmlDOM.getAttribute(attr);
};
if (tag === 'iframe') {
const clipCls = 'note-vide-clip';
if (!options.isClosing) {
const iframeCls = getAttr('class');
let safe = iframeCls.indexOf(clipCls) > -1;
const src = getAttr('src');
if (allowedIframeSrcRegex.exec(src)) {
safe = true;
}
if (safe)
return `<iframe src='${src}' class="${clipCls}" width=100% height=auto allowfullscreen></iframe>`;
} else {
// remove </iframe> tag
return '';
}
} else if (tag === 'a') {
if (!options.isClosing) {
if (getAttr(ASIS) === 'true') {
// if has a ASIS attribute, don't do anything, it's a member id
return html;
} else {
const href = getAttr('href');
if (href.match(/^((http(s){0,1}:){0,1}\/\/|\/)/)) {
// a valid url
return `<a href=${href} target=${targetWindow}>`;
}
}
}
} else if (tag === 'img') {
if (!options.isClosing) {
const src = getAttr('src');
if (src) {
return `<a href='${src}' class='swipebox'><img src='${src}' class="attachment-image-preview mCS_img_loaded"></a>`;
}
}
}
return undefined;
},
onTagAttr(tag, name, value) {
if (tag === 'img' && name === 'src') {
if (value && value.substr(0, 5) === 'data:') {
// allow image with dataURI src
return `${name}='${value}'`;
}
} else if (tag === 'a' && name === 'target') {
return `${name}='${targetWindow}'`; // always change a href target to a new window
}
return undefined;
},
...options,
};
return _sanitizeXss(input, options);
};
Template.editor.onRendered(() => {
const textareaSelector = 'textarea';
const mentions = [
@@ -94,13 +10,7 @@ Template.editor.onRendered(() => {
currentBoard
.activeMembers()
.map(member => {
const user = Users.findOne(member.userId);
if (user._id === Meteor.userId()) {
return null;
}
const value = user.username;
const username =
value && value.match(/\s+/) ? `"${value}"` : value;
const username = Users.findOne(member.userId).username;
return username.includes(term) ? username : null;
})
.filter(Boolean),
@@ -126,10 +36,9 @@ Template.editor.onRendered(() => {
? [
['view', ['fullscreen']],
['table', ['table']],
['font', ['bold']],
['color', ['color']],
['insert', ['video']], // iframe tag will be sanitized TODO if iframe[class=note-video-clip] can be added into safe list, insert video can be enabled
['font', ['bold', 'underline']],
//['fontsize', ['fontsize']],
['color', ['color']],
]
: [
['style', ['style']],
@@ -139,11 +48,47 @@ Template.editor.onRendered(() => {
['color', ['color']],
['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', '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 :(
['view', ['fullscreen', 'help']],
];
const cleanPastedHTML = sanitizeXss;
const cleanPastedHTML = function(input) {
const badTags = [
'style',
'script',
'applet',
'embed',
'noframes',
'noscript',
'meta',
'link',
'button',
'form',
].join('|');
const badPatterns = new RegExp(
`(?:${[
`<(${badTags})s*[^>][\\s\\S]*?<\\/\\1>`,
`<(${badTags})[^>]*?\\/>`,
].join('|')})`,
'gi',
);
let output = input;
// remove bad Tags
output = output.replace(badPatterns, '');
// remove attributes ' style="..."'
const badAttributes = new RegExp(
`(?:${[
'on\\S+=([\'"]?).*?\\1',
'href=([\'"]?)javascript:.*?\\2',
'style=([\'"]?).*?\\3',
'target=\\S+',
].join('|')})`,
'gi',
);
output = output.replace(badAttributes, '');
output = output.replace(/(<a )/gi, '$1target=_ '); // always to new target
return output;
};
const editor = '.editor';
const selectors = [
`.js-new-comment-form ${editor}`,
@@ -163,37 +108,14 @@ Template.editor.onRendered(() => {
}
return undefined;
};
let popupShown = false;
inputs.each(function(idx, input) {
mSummernotes[idx] = $(input).summernote({
placeholder,
callbacks: {
onKeydown(e) {
if (popupShown) {
e.preventDefault();
}
},
onKeyup(e) {
if (popupShown) {
e.preventDefault();
}
},
onInit(object) {
const originalInput = this;
const setAutocomplete = function(jEditor) {
if (jEditor !== undefined) {
jEditor.escapeableTextComplete(mentions).on({
'textComplete:show'() {
popupShown = true;
},
'textComplete:hide'() {
popupShown = false;
},
});
}
};
$(originalInput).on('submitted', function() {
// resetCommentInput has been called
// when comment is submitted, the original textarea will be set to '', so shall we
if (!this.value) {
const sn = getSummernote(this);
sn && sn.summernote('code', '');
@@ -201,7 +123,9 @@ Template.editor.onRendered(() => {
});
const jEditor = object && object.editable;
const toolbar = object && object.toolbar;
setAutocomplete(jEditor);
if (jEditor !== undefined) {
jEditor.escapeableTextComplete(mentions);
}
if (toolbar !== undefined) {
const fBtn = toolbar.find('.btn-fullscreen');
fBtn.on('click', function() {
@@ -211,6 +135,7 @@ Template.editor.onRendered(() => {
});
}
},
onImageUpload(files) {
const $summernote = getSummernote(this);
if (files && files.length > 0) {
@@ -289,6 +214,12 @@ Template.editor.onRendered(() => {
const thisNote = this;
const updatePastedText = function(object) {
const someNote = getSummernote(object);
// Fix Pasting text into a card is adding a line before and after
// (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');
// == 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.
someNote.summernote('code', ''); //clear original
@@ -331,6 +262,8 @@ Template.editor.onRendered(() => {
}
});
import sanitizeXss from 'xss';
// XXX I believe we should compute a HTML rendered field on the server that
// would handle markdown and user mentions. We can simply have two
// fields, one source, and one compiled version (in HTML) and send only the
@@ -352,7 +285,7 @@ Blaze.Template.registerHelper(
}
return member;
});
const mentionRegex = /\B@(?:(?:"([\w.\s]*)")|([\w.]+))/gi; // including space in username
const mentionRegex = /\B@([\w.]*)/gi;
let currentMention;
while ((currentMention = mentionRegex.exec(content)) !== null) {
@@ -368,7 +301,12 @@ Blaze.Template.registerHelper(
if (knowedUser.userId === Meteor.userId()) {
linkClass += ' me';
}
const link = HTML.A(
// This @user mention link generation did open same Wekan
// window in new tab, so now A is changed to U so it's
// underlined and there is no link popup. This way also
// text can be selected more easily.
//const link = HTML.A(
const link = HTML.U(
{
class: linkClass,
// XXX Hack. Since we stringify this render function result below with
@@ -376,16 +314,17 @@ Blaze.Template.registerHelper(
// `userId` to the popup as usual, and we need to store it in the DOM
// using a data attribute.
'data-userId': knowedUser.userId,
[ASIS]: 'true',
},
linkValue,
);
content = content.replace(fullMention, Blaze.toHTML(link));
}
return HTML.Raw(sanitizeXss(content));
}),
);
Template.viewer.events({
// Viewer sometimes have click-able wrapper around them (for instance to edit
// the corresponding text). Clicking a link shouldn't fire these actions, stop
@@ -397,10 +336,7 @@ Template.viewer.events({
Popup.open('member').call({ userId }, event, templateInstance);
} else {
const href = event.currentTarget.href;
const child = event.currentTarget.firstElementChild;
if (child && child.tagName === 'IMG') {
prevent = false;
} else if (href) {
if (href) {
window.open(href, '_blank');
}
}
+3
View File
@@ -218,6 +218,9 @@
padding: 10px
margin: -10px 0 -10px -10px
.announcement .viewer
display: inline-block
.announcement,
.offline-warning
width: 100%
@@ -1,29 +1,42 @@
template(name="boardActions")
div.trigger-item
div.trigger-content
div.trigger-text
div.trigger-text
| {{_'r-move-card-to'}}
div.trigger-dropdown
select(id="move-gen-action")
option(value="top") {{_'r-top-of'}}
option(value="bottom") {{_'r-bottom-of'}}
div.trigger-text
div.trigger-text
| {{_'r-its-list'}}
div.trigger-button.js-add-gen-move-action.js-goto-rules
i.fa.fa-plus
div.trigger-item
div.trigger-content
div.trigger-text
div.trigger-text
| {{_'r-move-card-to'}}
div.trigger-dropdown
select(id="move-spec-action")
option(value="top") {{_'r-top-of'}}
option(value="bottom") {{_'r-bottom-of'}}
div.trigger-text
| {{_'r-list'}}
div.trigger-text
| {{_'r-the-board'}}
div.trigger-dropdown
select(id="board-id")
each boards
if $eq _id currentBoard._id
option(value="{{_id}}" selected) {{_ 'current'}}
else
option(value="{{_id}}") {{title}}
div.trigger-text
| {{_'r-in-list'}}
div.trigger-dropdown
input(id="listName",type=text,placeholder="{{_'r-name'}}")
div.trigger-text
| {{_'r-in-swimlane'}}
div.trigger-dropdown
input(id="swimlaneName",type=text,placeholder="{{_'r-name'}}")
div.trigger-button.js-add-spec-move-action.js-goto-rules
i.fa.fa-plus
@@ -33,14 +46,14 @@ template(name="boardActions")
select(id="arch-action")
option(value="archive") {{_'r-archive'}}
option(value="unarchive") {{_'r-unarchive'}}
div.trigger-text
div.trigger-text
| {{_'r-card'}}
div.trigger-button.js-add-arch-action.js-goto-rules
i.fa.fa-plus
div.trigger-item
div.trigger-content
div.trigger-text
div.trigger-text
| {{_'r-add-swimlane'}}
div.trigger-dropdown
input(id="swimlane-name",type=text,placeholder="{{_'r-name'}}")
@@ -49,15 +62,15 @@ template(name="boardActions")
div.trigger-item
div.trigger-content
div.trigger-text
div.trigger-text
| {{_'r-create-card'}}
div.trigger-dropdown
input(id="card-name",type=text,placeholder="{{_'r-name'}}")
div.trigger-text
div.trigger-text
| {{_'r-in-list'}}
div.trigger-dropdown
input(id="list-name",type=text,placeholder="{{_'r-name'}}")
div.trigger-text
div.trigger-text
| {{_'r-in-swimlane'}}
div.trigger-dropdown
input(id="swimlane-name2",type=text,placeholder="{{_'r-name'}}")
@@ -65,8 +78,8 @@ template(name="boardActions")
i.fa.fa-plus
@@ -1,6 +1,22 @@
BlazeComponent.extendComponent({
onCreated() {},
boards() {
const boards = Boards.find(
{
archived: false,
'members.userId': Meteor.userId(),
_id: {
$ne: Meteor.user().getTemplatesBoardId(),
},
},
{
sort: ['title'],
},
);
return boards;
},
events() {
return [
{
@@ -52,15 +68,18 @@ BlazeComponent.extendComponent({
const ruleName = this.data().ruleName.get();
const trigger = this.data().triggerVar.get();
const actionSelected = this.find('#move-spec-action').value;
const listTitle = this.find('#listName').value;
const swimlaneName = this.find('#swimlaneName').value;
const listName = this.find('#listName').value;
const boardId = Session.get('currentBoard');
const destBoardId = this.find('#board-id').value;
const desc = Utils.getTriggerActionDesc(event, this);
if (actionSelected === 'top') {
const triggerId = Triggers.insert(trigger);
const actionId = Actions.insert({
actionType: 'moveCardToTop',
listTitle,
boardId,
listName,
swimlaneName,
boardId: destBoardId,
desc,
});
Rules.insert({
@@ -74,8 +93,9 @@ BlazeComponent.extendComponent({
const triggerId = Triggers.insert(trigger);
const actionId = Actions.insert({
actionType: 'moveCardToBottom',
listTitle,
boardId,
listName,
swimlaneName,
boardId: destBoardId,
desc,
});
Rules.insert({
+53 -1
View File
@@ -40,9 +40,15 @@ template(name="peopleGeneral")
th {{_ 'active'}}
th {{_ 'authentication-method'}}
th
+newUserRow
each user in peopleList
+peopleRow(userId=user._id)
template(name="newUserRow")
a.new-user
i.fa.fa-edit
| {{_ 'new'}}
template(name="peopleRow")
tr
if userData.loginDisabled
@@ -104,7 +110,7 @@ template(name="editUserPopup")
label.hide.userId(type="text" value=user._id)
label
| {{_ 'fullname'}}
input.js-profile-fullname(type="text" value=user.profile.fullname autofocus)
input.js-profile-fullname(type="text" value=user.profile.fullname)
label
| {{_ 'username'}}
span.error.hide.username-taken
@@ -148,3 +154,49 @@ template(name="editUserPopup")
// div
// input#deleteButton.primary.wide(type="button" value="{{_ 'delete'}}")
template(name="newUserPopup")
form
//label.hide.userId(type="text" value=user._id)
label
| {{_ 'fullname'}}
input.js-profile-fullname(type="text" value="")
label
| {{_ 'username'}}
span.error.hide.username-taken
| {{_ 'error-username-taken'}}
//if isLdap
// input.js-profile-username(type="text" value=user.username readonly)
//else
input.js-profile-username(type="text" value="")
label
| {{_ 'email'}}
span.error.hide.email-taken
| {{_ 'error-email-taken'}}
//if isLdap
// input.js-profile-email(type="email" value="{{user.emails.[0].address}}" readonly)
//else
input.js-profile-email(type="email" value="")
label
| {{_ 'admin'}}
select.select-role.js-profile-isadmin
option(value="false" selected="selected") {{_ 'no'}}
option(value="true") {{_ 'yes'}}
label
| {{_ 'active'}}
select.select-active.js-profile-isactive
option(value="false" selected="selected") {{_ 'yes'}}
option(value="true") {{_ 'no'}}
label
| {{_ 'authentication-type'}}
select.select-authenticationMethod.js-authenticationMethod
each authentications
if isSelected value
option(value="{{value}}" selected) {{_ value}}
else
option(value="{{value}}") {{_ value}}
hr
label
| {{_ 'password'}}
input.js-profile-password(type="password")
div.buttonsContainer
input.primary.wide(type="submit" value="{{_ 'save'}}")
+95
View File
@@ -39,6 +39,9 @@ BlazeComponent.extendComponent({
this.filterPeople();
}
},
'click #newUserButton'() {
Popup.open('newUser');
},
},
];
},
@@ -141,6 +144,47 @@ Template.editUserPopup.helpers({
},
});
Template.newUserPopup.onCreated(function() {
this.authenticationMethods = new ReactiveVar([]);
this.errorMessage = new ReactiveVar('');
Meteor.call('getAuthenticationsEnabled', (_, result) => {
if (result) {
// TODO : add a management of different languages
// (ex {value: ldap, text: TAPi18n.__('ldap', {}, T9n.getLanguage() || 'en')})
this.authenticationMethods.set([
{ value: 'password' },
// Gets only the authentication methods availables
...Object.entries(result)
.filter(e => e[1])
.map(e => ({ value: e[0] })),
]);
}
});
});
Template.newUserPopup.helpers({
//user() {
// return Users.findOne(this.userId);
//},
authentications() {
return Template.instance().authenticationMethods.get();
},
//isSelected(match) {
// const userId = Template.instance().data.userId;
// const selected = Users.findOne(userId).authenticationMethod;
// return selected === match;
//},
//isLdap() {
// const userId = Template.instance().data.userId;
// const selected = Users.findOne(userId).authenticationMethod;
// return selected === 'ldap';
//},
errorMessage() {
return Template.instance().errorMessage.get();
},
});
BlazeComponent.extendComponent({
onCreated() {},
user() {
@@ -155,6 +199,16 @@ BlazeComponent.extendComponent({
},
}).register('peopleRow');
BlazeComponent.extendComponent({
events() {
return [
{
'click a.new-user': Popup.open('newUser'),
},
];
},
}).register('newUserRow');
Template.editUserPopup.events({
submit(event, templateInstance) {
event.preventDefault();
@@ -248,3 +302,44 @@ Template.editUserPopup.events({
Popup.close();
}),
});
Template.newUserPopup.events({
submit(event, templateInstance) {
event.preventDefault();
const fullname = templateInstance.find('.js-profile-fullname').value.trim();
const username = templateInstance.find('.js-profile-username').value.trim();
const password = templateInstance.find('.js-profile-password').value;
const isAdmin = templateInstance.find('.js-profile-isadmin').value.trim();
const isActive = templateInstance.find('.js-profile-isactive').value.trim();
const email = templateInstance.find('.js-profile-email').value.trim();
Meteor.call(
'setCreateUser',
fullname,
username,
password,
isAdmin,
isActive,
email.toLowerCase(),
function(error) {
const usernameMessageElement = templateInstance.$('.username-taken');
const emailMessageElement = templateInstance.$('.email-taken');
if (error) {
const errorElement = error.error;
if (errorElement === 'username-already-taken') {
usernameMessageElement.show();
emailMessageElement.hide();
} else if (errorElement === 'email-already-taken') {
usernameMessageElement.hide();
emailMessageElement.show();
}
} else {
usernameMessageElement.hide();
emailMessageElement.hide();
Popup.close();
}
},
);
Popup.close();
},
});
@@ -184,12 +184,6 @@ template(name='layoutSettings')
.title {{_ 'custom-product-name'}}
.form-group
input.wekan-form-control#product-name(type="text", placeholder="" value="{{currentSetting.productName}}")
li.layout-form
.title {{_ 'add-custom-html-after-body-start'}}
textarea#customHTMLafterBodyStart.wekan-form-control= currentSetting.customHTMLafterBodyStart
li.layout-form
.title {{_ 'add-custom-html-before-body-end'}}
textarea#customHTMLbeforeBodyEnd.wekan-form-control= currentSetting.customHTMLbeforeBodyEnd
li
button.js-save-layout.primary {{_ 'save'}}
@@ -171,20 +171,12 @@ BlazeComponent.extendComponent({
const displayAuthenticationMethod =
$('input[name=displayAuthenticationMethod]:checked').val() === 'true';
const defaultAuthenticationMethod = $('#defaultAuthenticationMethod').val();
const customHTMLafterBodyStart = $('#customHTMLafterBodyStart')
.val()
.trim();
const customHTMLbeforeBodyEnd = $('#customHTMLbeforeBodyEnd')
.val()
.trim();
try {
Settings.update(Settings.findOne()._id, {
$set: {
productName,
hideLogo: hideLogoChange,
customHTMLafterBodyStart,
customHTMLbeforeBodyEnd,
displayAuthenticationMethod,
defaultAuthenticationMethod,
},
+122 -3
View File
@@ -72,6 +72,108 @@ template(name="boardChangeColorPopup")
if isSelected
i.fa.fa-check
template(name="boardCardSettingsPopup")
form.board-card-settings
h3 {{_ 'show-on-card'}}
div.check-div
a.flex.js-field-has-receiveddate(class="{{#if allowsReceivedDate}}is-checked{{/if}}")
.materialCheckBox(class="{{#if allowsReceivedDate}}is-checked{{/if}}")
span
i.fa.fa-sign-out
| {{_ 'card-received'}}
div.check-div
a.flex.js-field-has-startdate(class="{{#if allowsStartDate}}is-checked{{/if}}")
.materialCheckBox(class="{{#if allowsStartDate}}is-checked{{/if}}")
span
i.fa.fa-hourglass-start
| {{_ 'card-start'}}
div.check-div
a.flex.js-field-has-duedate(class="{{#if allowsDueDate}}is-checked{{/if}}")
.materialCheckBox(class="{{#if allowsDueDate}}is-checked{{/if}}")
span
i.fa.fa-sign-in
| {{_ 'card-due'}}
div.check-div
a.flex.js-field-has-enddate(class="{{#if allowsEndDate}}is-checked{{/if}}")
.materialCheckBox(class="{{#if allowsEndDate}}is-checked{{/if}}")
span
i.fa.fa-hourglass-end
| {{_ 'card-end'}}
div.check-div
a.flex.js-field-has-members(class="{{#if allowsMembers}}is-checked{{/if}}")
.materialCheckBox(class="{{#if allowsMembers}}is-checked{{/if}}")
span
i.fa.fa-users
| {{_ 'members'}}
div.check-div
a.flex.js-field-has-assignee(class="{{#if allowsAssignee}}is-checked{{/if}}")
.materialCheckBox(class="{{#if allowsAssignee}}is-checked{{/if}}")
span
i.fa.fa-user
| {{_ 'assignee'}}
div.check-div
a.flex.js-field-has-assigned-by(class="{{#if allowsAssignedBy}}is-checked{{/if}}")
.materialCheckBox(class="{{#if allowsAssignedBy}}is-checked{{/if}}")
span
i.fa.fa-shopping-cart
| {{_ 'assigned-by'}}
div.check-div
a.flex.js-field-has-requested-by(class="{{#if allowsRequestedBy}}is-checked{{/if}}")
.materialCheckBox(class="{{#if allowsRequestedBy}}is-checked{{/if}}")
span
i.fa.fa-user-plus
| {{_ 'requested-by'}}
div.check-div
a.flex.js-field-has-labels(class="{{#if allowsLabels}}is-checked{{/if}}")
.materialCheckBox(class="{{#if allowsLabels}}is-checked{{/if}}")
span
i.fa.fa-tags
| {{_ 'labels'}}
div.check-div
a.flex.js-field-has-description-title(class="{{#if allowsDescriptionTitle}}is-checked{{/if}}")
.materialCheckBox(class="{{#if allowsDescriptionTitle}}is-checked{{/if}}")
span
i.fa.fa-align-left
| {{_ 'description'}}
| {{_ 'title'}}
div.check-div
a.flex.js-field-has-description-text(class="{{#if allowsDescriptionText}}is-checked{{/if}}")
.materialCheckBox(class="{{#if allowsDescriptionText}}is-checked{{/if}}")
span
i.fa.fa-align-left
| {{_ 'description'}}
| {{_ 'custom-field-text'}}
div.check-div
a.flex.js-field-has-checklists(class="{{#if allowsChecklists}}is-checked{{/if}}")
.materialCheckBox(class="{{#if allowsChecklists}}is-checked{{/if}}")
span
i.fa.fa-check
| {{_ 'checklists'}}
div.check-div
a.flex.js-field-has-subtasks(class="{{#if allowsSubtasks}}is-checked{{/if}}")
.materialCheckBox(class="{{#if allowsSubtasks}}is-checked{{/if}}")
span
i.fa.fa-sitemap
| {{_ 'subtasks'}}
div.check-div
a.flex.js-field-has-attachments(class="{{#if allowsAttachments}}is-checked{{/if}}")
.materialCheckBox(class="{{#if allowsAttachments}}is-checked{{/if}}")
span
i.fa.fa-paperclip
| {{_ 'attachments'}}
//div.check-div
// a.flex.js-field-has-comments(class="{{#if allowsComments}}is-checked{{/if}}")
// .materialCheckBox(class="{{#if allowsComments}}is-checked{{/if}}")
// span
// i.fa.fa-comment-o
// | {{_ 'comment'}}
//div.check-div
// a.flex.js-field-has-activities(class="{{#if allowsActivities}}is-checked{{/if}}")
// .materialCheckBox(class="{{#if allowsActivities}}is-checked{{/if}}")
// span
// i.fa.fa-history
// | {{_ 'activities'}}
template(name="boardSubtaskSettingsPopup")
form.board-subtask-settings
h3 {{_ 'show-parent-in-minicard'}}
@@ -143,7 +245,7 @@ template(name="outgoingWebhooksPopup")
b &nbsp;
.materialCheckBox(class="{{#unless enabled}}is-checked{{/unless}}")
input.js-outgoing-webhooks-title(placeholder="{{_ 'webhook-title'}}" type="text" name="title" value=title)
input.js-outgoing-webhooks-url(type="text" name="url" value=url autofocus)
input.js-outgoing-webhooks-url(type="text" name="url" value=url)
input.js-outgoing-webhooks-token(placeholder="{{_ 'webhook-token' }}" type="text" value=token name="token")
select.js-outgoing-webhooks-type(name="type")
each _type in types
@@ -155,7 +257,7 @@ template(name="outgoingWebhooksPopup")
input(type="hidden" value=_id name="id")
input.primary.wide(type="submit" value="{{_ 'save'}}")
form.integration-form
input.js-outgoing-webhooks-title(placeholder="{{_ 'webhook-title'}}" type="text" name="title" autofocus)
input.js-outgoing-webhooks-title(placeholder="{{_ 'webhook-title'}}" type="text" name="title")
input.js-outgoing-webhooks-url(placeholder="{{_ 'URL' }}" type="text" name="url")
input.js-outgoing-webhooks-token(placeholder="{{_ 'webhook-token' }}" type="text" name="token")
select.js-outgoing-webhooks-type(name="type")
@@ -165,7 +267,14 @@ template(name="outgoingWebhooksPopup")
template(name="boardMenuPopup")
ul.pop-over-list
li: a.js-custom-fields {{_ 'custom-fields'}}
li
a.js-open-rules-view(title="{{_ 'rules'}}")
i.fa.fa-magic
| {{_ 'rules'}}
li
a.js-custom-fields
i.fa.fa-list-alt
| {{_ 'custom-fields'}}
li
a.js-open-archives
i.fa.fa-archive
@@ -197,6 +306,10 @@ template(name="boardMenuPopup")
a.js-outgoing-webhooks
i.fa.fa-globe
| {{_ 'outgoing-webhooks'}}
li
a.js-card-settings
i.fa.fa-id-card-o
| {{_ 'card-settings'}}
li
a.js-subtask-settings
i.fa.fa-sitemap
@@ -233,6 +346,12 @@ template(name="boardMenuPopup")
i.fa.fa-globe
| {{_ 'outgoing-webhooks'}}
hr
ul.pop-over-list
li
a.js-card-settings
i.fa.fa-id-card-o
| {{_ 'card-settings'}}
hr
ul.pop-over-list
li
a.js-subtask-settings
+362
View File
@@ -182,6 +182,10 @@ Template.memberPopup.helpers({
Template.boardMenuPopup.events({
'click .js-rename-board': Popup.open('boardChangeTitle'),
'click .js-open-rules-view'() {
Modal.openWide('rulesMain');
Popup.close();
},
'click .js-custom-fields'() {
Sidebar.setView('customFields');
Popup.close();
@@ -208,6 +212,7 @@ Template.boardMenuPopup.events({
'click .js-outgoing-webhooks': Popup.open('outgoingWebhooks'),
'click .js-import-board': Popup.open('chooseBoardSource'),
'click .js-subtask-settings': Popup.open('boardSubtaskSettings'),
'click .js-card-settings': Popup.open('boardCardSettings'),
});
Template.boardMenuPopup.helpers({
@@ -472,6 +477,10 @@ BlazeComponent.extendComponent({
return this.currentBoard.allowsSubtasks;
},
allowsReceivedDate() {
return this.currentBoard.allowsReceivedDate;
},
isBoardSelected() {
return this.currentBoard.subtasksDefaultBoardId === this.currentData()._id;
},
@@ -585,6 +594,359 @@ BlazeComponent.extendComponent({
},
}).register('boardSubtaskSettingsPopup');
BlazeComponent.extendComponent({
onCreated() {
this.currentBoard = Boards.findOne(Session.get('currentBoard'));
},
allowsReceivedDate() {
return this.currentBoard.allowsReceivedDate;
},
allowsStartDate() {
return this.currentBoard.allowsStartDate;
},
allowsDueDate() {
return this.currentBoard.allowsDueDate;
},
allowsEndDate() {
return this.currentBoard.allowsEndDate;
},
allowsSubtasks() {
return this.currentBoard.allowsSubtasks;
},
allowsMembers() {
return this.currentBoard.allowsMembers;
},
allowsAssignee() {
return this.currentBoard.allowsAssignee;
},
allowsAssignedBy() {
return this.currentBoard.allowsAssignedBy;
},
allowsRequestedBy() {
return this.currentBoard.allowsRequestedBy;
},
allowsLabels() {
return this.currentBoard.allowsLabels;
},
allowsChecklists() {
return this.currentBoard.allowsChecklists;
},
allowsAttachments() {
return this.currentBoard.allowsAttachments;
},
allowsComments() {
return this.currentBoard.allowsComments;
},
allowsDescriptionTitle() {
return this.currentBoard.allowsDescriptionTitle;
},
allowsDescriptionText() {
return this.currentBoard.allowsDescriptionText;
},
isBoardSelected() {
return this.currentBoard.dateSettingsDefaultBoardID;
},
isNullBoardSelected() {
return (
this.currentBoard.dateSettingsDefaultBoardId === null ||
this.currentBoard.dateSettingsDefaultBoardId === undefined
);
},
boards() {
return Boards.find(
{
archived: false,
'members.userId': Meteor.userId(),
},
{
sort: ['title'],
},
);
},
lists() {
return Lists.find(
{
boardId: this.currentBoard._id,
archived: false,
},
{
sort: ['title'],
},
);
},
hasLists() {
return this.lists().count() > 0;
},
isListSelected() {
return (
this.currentBoard.dateSettingsDefaultBoardId === this.currentData()._id
);
},
events() {
return [
{
'click .js-field-has-receiveddate'(evt) {
evt.preventDefault();
this.currentBoard.allowsReceivedDate = !this.currentBoard
.allowsReceivedDate;
this.currentBoard.setAllowsReceivedDate(
this.currentBoard.allowsReceivedDate,
);
$(`.js-field-has-receiveddate ${MCB}`).toggleClass(
CKCLS,
this.currentBoard.allowsReceivedDate,
);
$('.js-field-has-receiveddate').toggleClass(
CKCLS,
this.currentBoard.allowsReceivedDate,
);
},
'click .js-field-has-startdate'(evt) {
evt.preventDefault();
this.currentBoard.allowsStartDate = !this.currentBoard
.allowsStartDate;
this.currentBoard.setAllowsStartDate(
this.currentBoard.allowsStartDate,
);
$(`.js-field-has-startdate ${MCB}`).toggleClass(
CKCLS,
this.currentBoard.allowsStartDate,
);
$('.js-field-has-startdate').toggleClass(
CKCLS,
this.currentBoard.allowsStartDate,
);
},
'click .js-field-has-enddate'(evt) {
evt.preventDefault();
this.currentBoard.allowsEndDate = !this.currentBoard.allowsEndDate;
this.currentBoard.setAllowsEndDate(this.currentBoard.allowsEndDate);
$(`.js-field-has-enddate ${MCB}`).toggleClass(
CKCLS,
this.currentBoard.allowsEndDate,
);
$('.js-field-has-enddate').toggleClass(
CKCLS,
this.currentBoard.allowsEndDate,
);
},
'click .js-field-has-duedate'(evt) {
evt.preventDefault();
this.currentBoard.allowsDueDate = !this.currentBoard.allowsDueDate;
this.currentBoard.setAllowsDueDate(this.currentBoard.allowsDueDate);
$(`.js-field-has-duedate ${MCB}`).toggleClass(
CKCLS,
this.currentBoard.allowsDueDate,
);
$('.js-field-has-duedate').toggleClass(
CKCLS,
this.currentBoard.allowsDueDate,
);
},
'click .js-field-has-subtasks'(evt) {
evt.preventDefault();
this.currentBoard.allowsSubtasks = !this.currentBoard.allowsSubtasks;
this.currentBoard.setAllowsSubtasks(this.currentBoard.allowsSubtasks);
$(`.js-field-has-subtasks ${MCB}`).toggleClass(
CKCLS,
this.currentBoard.allowsSubtasks,
);
$('.js-field-has-subtasks').toggleClass(
CKCLS,
this.currentBoard.allowsSubtasks,
);
},
'click .js-field-has-members'(evt) {
evt.preventDefault();
this.currentBoard.allowsMembers = !this.currentBoard.allowsMembers;
this.currentBoard.setAllowsMembers(this.currentBoard.allowsMembers);
$(`.js-field-has-members ${MCB}`).toggleClass(
CKCLS,
this.currentBoard.allowsMembers,
);
$('.js-field-has-members').toggleClass(
CKCLS,
this.currentBoard.allowsMembers,
);
},
'click .js-field-has-assignee'(evt) {
evt.preventDefault();
this.currentBoard.allowsAssignee = !this.currentBoard.allowsAssignee;
this.currentBoard.setAllowsAssignee(this.currentBoard.allowsAssignee);
$(`.js-field-has-assignee ${MCB}`).toggleClass(
CKCLS,
this.currentBoard.allowsAssignee,
);
$('.js-field-has-assignee').toggleClass(
CKCLS,
this.currentBoard.allowsAssignee,
);
},
'click .js-field-has-assigned-by'(evt) {
evt.preventDefault();
this.currentBoard.allowsAssignedBy = !this.currentBoard
.allowsAssignedBy;
this.currentBoard.setAllowsAssignedBy(
this.currentBoard.allowsAssignedBy,
);
$(`.js-field-has-assigned-by ${MCB}`).toggleClass(
CKCLS,
this.currentBoard.allowsAssignedBy,
);
$('.js-field-has-assigned-by').toggleClass(
CKCLS,
this.currentBoard.allowsAssignedBy,
);
},
'click .js-field-has-requested-by'(evt) {
evt.preventDefault();
this.currentBoard.allowsRequestedBy = !this.currentBoard
.allowsRequestedBy;
this.currentBoard.setAllowsRequestedBy(
this.currentBoard.allowsRequestedBy,
);
$(`.js-field-has-requested-by ${MCB}`).toggleClass(
CKCLS,
this.currentBoard.allowsRequestedBy,
);
$('.js-field-has-requested-by').toggleClass(
CKCLS,
this.currentBoard.allowsRequestedBy,
);
},
'click .js-field-has-labels'(evt) {
evt.preventDefault();
this.currentBoard.allowsLabels = !this.currentBoard.allowsLabels;
this.currentBoard.setAllowsLabels(this.currentBoard.allowsLabels);
$(`.js-field-has-labels ${MCB}`).toggleClass(
CKCLS,
this.currentBoard.allowsAssignee,
);
$('.js-field-has-labels').toggleClass(
CKCLS,
this.currentBoard.allowsLabels,
);
},
'click .js-field-has-description-title'(evt) {
evt.preventDefault();
this.currentBoard.allowsDescriptionTitle = !this.currentBoard
.allowsDescriptionTitle;
this.currentBoard.setAllowsDescriptionTitle(
this.currentBoard.allowsDescriptionTitle,
);
$(`.js-field-has-description-title ${MCB}`).toggleClass(
CKCLS,
this.currentBoard.allowsDescriptionTitle,
);
$('.js-field-has-description-title').toggleClass(
CKCLS,
this.currentBoard.allowsDescriptionTitle,
);
},
'click .js-field-has-description-text'(evt) {
evt.preventDefault();
this.currentBoard.allowsDescriptionText = !this.currentBoard
.allowsDescriptionText;
this.currentBoard.setAllowsDescriptionText(
this.currentBoard.allowsDescriptionText,
);
$(`.js-field-has-description-text ${MCB}`).toggleClass(
CKCLS,
this.currentBoard.allowsDescriptionText,
);
$('.js-field-has-description-text').toggleClass(
CKCLS,
this.currentBoard.allowsDescriptionText,
);
},
'click .js-field-has-checklists'(evt) {
evt.preventDefault();
this.currentBoard.allowsChecklists = !this.currentBoard
.allowsChecklists;
this.currentBoard.setAllowsChecklists(
this.currentBoard.allowsChecklists,
);
$(`.js-field-has-checklists ${MCB}`).toggleClass(
CKCLS,
this.currentBoard.allowsChecklists,
);
$('.js-field-has-checklists').toggleClass(
CKCLS,
this.currentBoard.allowsChecklists,
);
},
'click .js-field-has-attachments'(evt) {
evt.preventDefault();
this.currentBoard.allowsAttachments = !this.currentBoard
.allowsAttachments;
this.currentBoard.setAllowsAttachments(
this.currentBoard.allowsAttachments,
);
$(`.js-field-has-attachments ${MCB}`).toggleClass(
CKCLS,
this.currentBoard.allowsAttachments,
);
$('.js-field-has-attachments').toggleClass(
CKCLS,
this.currentBoard.allowsAttachments,
);
},
'click .js-field-has-comments'(evt) {
evt.preventDefault();
this.currentBoard.allowsComments = !this.currentBoard.allowsComments;
this.currentBoard.setAllowsComments(this.currentBoard.allowsComments);
$(`.js-field-has-comments ${MCB}`).toggleClass(
CKCLS,
this.currentBoard.allowsComments,
);
$('.js-field-has-comments').toggleClass(
CKCLS,
this.currentBoard.allowsComments,
);
},
'click .js-field-has-activities'(evt) {
evt.preventDefault();
this.currentBoard.allowsActivities = !this.currentBoard
.allowsActivities;
this.currentBoard.setAllowsActivities(
this.currentBoard.allowsActivities,
);
$(`.js-field-has-activities ${MCB}`).toggleClass(
CKCLS,
this.currentBoard.allowsActivities,
);
$('.js-field-has-activities').toggleClass(
CKCLS,
this.currentBoard.allowsActivities,
);
},
},
];
},
}).register('boardCardSettingsPopup');
BlazeComponent.extendComponent({
onCreated() {
this.error = new ReactiveVar('');
+1 -1
View File
@@ -109,7 +109,7 @@
color: darken(white, 40%)
.board-sidebar
width: 248px
width: 548px
right: -@width
transition: top .1s, right .1s, width .1s
+23 -10
View File
@@ -115,7 +115,7 @@ function initSortable(boardComponent, $listsDom) {
$listsDom.sortable({
handle: '.js-list-handle',
});
} else {
} else if (!Utils.isMiniScreen() && !showDesktopDragHandles) {
$listsDom.sortable({
handle: '.js-list-header',
});
@@ -126,21 +126,34 @@ function initSortable(boardComponent, $listsDom) {
$listsDom.sortable(
'option',
'disabled',
// Disable drag-dropping when user is not member
// Disable drag-dropping when user is not member/is worker/is miniscreen
!userIsMember(),
// Not disable drag-dropping while in multi-selection mode
// MultiSelection.isActive() || !userIsMember(),
);
}
$listsDom.sortable(
'option',
'disabled',
// Disable drag-dropping when user is not member
Meteor.user().isWorker(),
// Not disable drag-dropping while in multi-selection mode
// MultiSelection.isActive() || !userIsMember(),
);
if ($listDom.data('sortable')) {
$listsDom.sortable(
'option',
'disabled',
// Disable drag-dropping when user is not member/is worker/is miniscreen
Meteor.user().isWorker(),
// Not disable drag-dropping while in multi-selection mode
// MultiSelection.isActive() || !userIsMember(),
);
}
if ($listDom.data('sortable')) {
$listsDom.sortable(
'option',
'disabled',
// Disable drag-dropping when user is not member/is worker/is miniscreen
Utils.isMiniScreen(),
// Not disable drag-dropping while in multi-selection mode
// MultiSelection.isActive() || !userIsMember(),
);
}
});
}
+6 -6
View File
@@ -98,12 +98,12 @@ template(name="changeLanguagePopup")
template(name="changeSettingsPopup")
ul.pop-over-list
li
a.js-toggle-system-messages
i.fa.fa-comments-o
| {{_ 'hide-system-messages'}}
if hiddenSystemMessages
i.fa.fa-check
//li
// a.js-toggle-system-messages
// i.fa.fa-comments-o
// | {{_ 'hide-system-messages'}}
// if hiddenSystemMessages
// i.fa.fa-check
li
a.js-toggle-desktop-drag-handles
i.fa.fa-arrows
+28
View File
@@ -70,6 +70,30 @@ Mousetrap.bind('space', evt => {
}
});
// XXX This shortcut should also work when hovering over a card in board view
Mousetrap.bind('c', evt => {
if (!Session.get('currentCard')) {
return;
}
const currentUserId = Meteor.userId();
if (currentUserId === null) {
return;
}
if (
Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly() &&
!Meteor.user().isWorker()
) {
const card = Cards.findOne(Session.get('currentCard'));
card.archive();
// We should prevent scrolling in card when spacebar is clicked
// This should do it according to Mousetrap docs, but it doesn't
evt.preventDefault();
}
});
Template.keyboardShortcuts.helpers({
mapping: [
{
@@ -104,5 +128,9 @@ Template.keyboardShortcuts.helpers({
keys: ['SPACE'],
action: 'shortcut-assign-self',
},
{
keys: ['C'],
action: 'archive-card',
},
],
});
+8 -3
View File
@@ -48,6 +48,11 @@ $.fn.escapeableTextComplete = function(strategies, options, ...otherArgs) {
return this;
};
EscapeActions.register('textcomplete', () => {}, () => dropdownMenuIsOpened, {
noClickEscapeOn: '.textcomplete-dropdown',
});
EscapeActions.register(
'textcomplete',
() => {},
() => dropdownMenuIsOpened,
{
noClickEscapeOn: '.textcomplete-dropdown',
},
);
+30
View File
@@ -147,8 +147,38 @@ Utils = {
// in a small window (even on desktop), Wekan run in compact mode.
// we can easily debug with a small window of desktop browser. :-)
isMiniScreen() {
// OLD WINDOW WIDTH DETECTION:
this.windowResizeDep.depend();
return $(window).width() <= 800;
// NEW TOUCH DEVICE DETECTION:
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent
/*
var hasTouchScreen = false;
if ("maxTouchPoints" in navigator) {
hasTouchScreen = navigator.maxTouchPoints > 0;
} else if ("msMaxTouchPoints" in navigator) {
hasTouchScreen = navigator.msMaxTouchPoints > 0;
} else {
var mQ = window.matchMedia && matchMedia("(pointer:coarse)");
if (mQ && mQ.media === "(pointer:coarse)") {
hasTouchScreen = !!mQ.matches;
} else if ('orientation' in window) {
hasTouchScreen = true; // deprecated, but good fallback
} else {
// Only as a last resort, fall back to user agent sniffing
var UA = navigator.userAgent;
hasTouchScreen = (
/\b(BlackBerry|webOS|iPhone|IEMobile)\b/i.test(UA) ||
/\b(Android|Windows Phone|iPad|iPod)\b/i.test(UA)
);
}
}
*/
//if (hasTouchScreen)
// document.getElementById("exampleButton").style.padding="1em";
//return false;
},
calculateIndexData(prevData, nextData, nItems = 1) {
+7 -3
View File
@@ -38,9 +38,13 @@ AccountsTemplates.configure({
},
});
['signIn', 'signUp', 'resetPwd', 'forgotPwd', 'enrollAccount'].forEach(
routeName => AccountsTemplates.configureRoute(routeName),
);
[
'signIn',
'signUp',
'resetPwd',
'forgotPwd',
'enrollAccount',
].forEach(routeName => AccountsTemplates.configureRoute(routeName));
// We display the form to change the password in a popup window that already
// have a title, so we unset the title automatically displayed by useraccounts.
+38 -4
View File
@@ -93,14 +93,14 @@ services:
#-------------------------------------------------------------------------------------
# ==== MONGODB AND METEOR VERSION ====
# a) For Wekan Meteor 1.8.x version at master branch, use mongo 4.x
image: mongo:4.0.12
image: mongo:latest
# b) For Wekan Meteor 1.6.x version at devel branch.
# Only for Snap and Sandstorm while they are not upgraded yet to Meteor 1.8.x
#image: mongo:3.2.21
#-------------------------------------------------------------------------------------
container_name: wekan-db
restart: always
command: mongod --smallfiles --oplogSize 128
command: mongod --oplogSize 128
networks:
- wekan-tier
expose:
@@ -238,7 +238,12 @@ services:
#---------------------------------------------------------------
# ==== RICH TEXT EDITOR IN CARD COMMENTS ====
# https://github.com/wekan/wekan/pull/2560
- RICHER_CARD_COMMENT_EDITOR=true
- RICHER_CARD_COMMENT_EDITOR=false
#---------------------------------------------------------------
# ==== MOUSE SCROLL ====
# https://github.com/wekan/wekan/issues/2949
- SCROLLINERTIA=0
- SCROLLAMOUNT=auto
#---------------------------------------------------------------
# ==== CARD OPENED, SEND WEBHOOK MESSAGE ====
# https://github.com/wekan/wekan/issues/2518
@@ -342,6 +347,31 @@ services:
# Tthe claim name you want to map to the email field:
#- OAUTH2_EMAIL_MAP=email
#-----------------------------------------------------------------
# ==== OAUTH2 Nextcloud ====
# 1) Register the application with Nextcloud: https://your.nextcloud/settings/admin/security
# Make sure you capture the application ID as well as generate a secret key.
# 2) Configure the environment variables. This differs slightly
# by installation type, but make sure you have the following:
#- OAUTH2_ENABLED=true
# OAuth2 login style: popup or redirect.
#- OAUTH2_LOGIN_STYLE=redirect
# Application GUID captured during app registration:
#- OAUTH2_CLIENT_ID=xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx
# Secret key generated during app registration:
#- OAUTH2_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
#- OAUTH2_SERVER_URL=https://your-nextcloud.tld
#- OAUTH2_AUTH_ENDPOINT=/index.php/apps/oauth2/authorize
#- OAUTH2_USERINFO_ENDPOINT=/ocs/v2.php/cloud/user?format=json
#- OAUTH2_TOKEN_ENDPOINT=/index.php/apps/oauth2/api/v1/token
# The claim name you want to map to the unique ID field:
#- OAUTH2_ID_MAP=id
# The claim name you want to map to the username field:
#- OAUTH2_USERNAME_MAP=id
# The claim name you want to map to the full name field:
#- OAUTH2_FULLNAME_MAP=display-name
# Tthe claim name you want to map to the email field:
#- OAUTH2_EMAIL_MAP=email
#-----------------------------------------------------------------
# ==== OAUTH2 KEYCLOAK ====
# https://github.com/wekan/wekan/wiki/Keycloak <== MAPPING INFO, REQUIRED
#- OAUTH2_ENABLED=true
@@ -479,18 +509,22 @@ services:
# The limit number of entries (0=unlimited)
#- LDAP_SEARCH_SIZE_LIMIT=0
#
# Enable group filtering
# Enable group filtering. Note the authenticated ldap user must be able to query all relevant group data with own login data from ldap.
#- LDAP_GROUP_FILTER_ENABLE=false
#
# The object class for filtering. Example: group
#- LDAP_GROUP_FILTER_OBJECTCLASS=
#
# The attribute of a group identifying it. Example: cn
#- LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE=
#
# The attribute inside a group object listing its members. Example: member
#- LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE=
#
# The format of the value of LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE. Example: 'dn' if the users dn ist saved as value into the attribute.
#- LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT=
#
# The group name (id) that matches all users.
#- LDAP_GROUP_FILTER_GROUP_NAME=
#
# LDAP_UNIQUE_IDENTIFIER_FIELD : This field is sometimes class GUID (Globally Unique Identifier). Example: guid
+4 -4
View File
@@ -451,14 +451,14 @@ FS.HTTP.Handlers.Get = function (ref) {
if(userAgent.indexOf('msie') >= 0 || userAgent.indexOf('trident') >= 0 || userAgent.indexOf('chrome') >= 0) {
ref.filename = encodeURIComponent(ref.filename);
} else if(userAgent.indexOf('firefox') >= 0) {
ref.filename = new Buffer(ref.filename).toString('binary');
ref.filename = Buffer.from(ref.filename).toString('binary');
} else {
/* safari*/
ref.filename = new Buffer(ref.filename).toString('binary');
}
ref.filename = Buffer.from(ref.filename).toString('binary');
}
} catch (ex){
ref.filename = 'tempfix';
}
}
return originalHandler.call(this, ref);
};
// 221
+8 -2
View File
@@ -583,8 +583,9 @@
"default": "Default",
"queue": "Queue",
"subtask-settings": "Subtasks Settings",
"card-settings": "Card Settings",
"boardSubtaskSettingsPopup-title": "Board Subtasks Settings",
"show-subtasks-field": "Cards can have subtasks",
"boardCardSettingsPopup-title": "Card Settings",
"deposit-subtasks-board": "Deposit subtasks to this board:",
"deposit-subtasks-list": "Landing list for subtasks deposited here:",
"show-parent-in-minicard": "Show parent in minicard:",
@@ -753,5 +754,10 @@
"hide-minicard-label-text": "Hide minicard label text",
"show-desktop-drag-handles": "Show desktop drag handles",
"assignee": "Assignee",
"cardAssigneesPopup-title": "Assignee"
"cardAssigneesPopup-title": "Assignee",
"addmore-detail": "Add a more detailed description",
"show-on-card": "Show on Card",
"new": "New",
"editUserPopup-title": "Edit User",
"newUserPopup-title": "New User"
}
+8 -2
View File
@@ -583,8 +583,9 @@
"default": "по подразбиране",
"queue": "Опашка",
"subtask-settings": "Настройки на Подзадачите",
"card-settings": "Card Settings",
"boardSubtaskSettingsPopup-title": "Настройки за Подзадачите за това Табло",
"show-subtasks-field": "Картата може да има подзадачи",
"boardCardSettingsPopup-title": "Card Settings",
"deposit-subtasks-board": "Deposit subtasks to this board:",
"deposit-subtasks-list": "Landing list for subtasks deposited here:",
"show-parent-in-minicard": "Show parent in minicard:",
@@ -753,5 +754,10 @@
"hide-minicard-label-text": "Hide minicard label text",
"show-desktop-drag-handles": "Show desktop drag handles",
"assignee": "Assignee",
"cardAssigneesPopup-title": "Assignee"
"cardAssigneesPopup-title": "Assignee",
"addmore-detail": "Add a more detailed description",
"show-on-card": "Show on Card",
"new": "New",
"editUserPopup-title": "Edit User",
"newUserPopup-title": "New User"
}
+8 -2
View File
@@ -583,8 +583,9 @@
"default": "Default",
"queue": "Queue",
"subtask-settings": "Subtasks Settings",
"card-settings": "Card Settings",
"boardSubtaskSettingsPopup-title": "Board Subtasks Settings",
"show-subtasks-field": "Cards can have subtasks",
"boardCardSettingsPopup-title": "Card Settings",
"deposit-subtasks-board": "Deposit subtasks to this board:",
"deposit-subtasks-list": "Landing list for subtasks deposited here:",
"show-parent-in-minicard": "Show parent in minicard:",
@@ -753,5 +754,10 @@
"hide-minicard-label-text": "Hide minicard label text",
"show-desktop-drag-handles": "Show desktop drag handles",
"assignee": "Assignee",
"cardAssigneesPopup-title": "Assignee"
"cardAssigneesPopup-title": "Assignee",
"addmore-detail": "Add a more detailed description",
"show-on-card": "Show on Card",
"new": "New",
"editUserPopup-title": "Edit User",
"newUserPopup-title": "New User"
}
+17 -11
View File
@@ -111,8 +111,8 @@
"restore-board": "Restaura Tauler",
"no-archived-boards": "No hi han Taulers al Arxiu.",
"archives": "Desa",
"template": "Template",
"templates": "Templates",
"template": "Plantilla",
"templates": "Plantilles",
"assign-member": "Assignar membre",
"attached": "adjuntat",
"attachment": "Adjunt",
@@ -131,13 +131,13 @@
"boardChangeTitlePopup-title": "Canvia el nom tauler",
"boardChangeVisibilityPopup-title": "Canvia visibilitat",
"boardChangeWatchPopup-title": "Canvia seguiment",
"boardMenuPopup-title": "Board Settings",
"boardMenuPopup-title": "Configuració del tauler",
"boardChangeViewPopup-title": "Visió del tauler",
"boards": "Taulers",
"board-view": "Visió del tauler",
"board-view-cal": "Calendari",
"board-view-swimlanes": "Carrils de Natació",
"board-view-collapse": "Collapse",
"board-view-collapse": "Contraure",
"board-view-lists": "Llistes",
"bucket-example": "Igual que “Bucket List”, per exemple",
"cancel": "Cancel·la",
@@ -583,8 +583,9 @@
"default": "Default",
"queue": "Queue",
"subtask-settings": "Subtasks Settings",
"card-settings": "Configuració de targeta",
"boardSubtaskSettingsPopup-title": "Board Subtasks Settings",
"show-subtasks-field": "Cards can have subtasks",
"boardCardSettingsPopup-title": "Configuració de targeta",
"deposit-subtasks-board": "Deposit subtasks to this board:",
"deposit-subtasks-list": "Landing list for subtasks deposited here:",
"show-parent-in-minicard": "Show parent in minicard:",
@@ -625,7 +626,7 @@
"r-moved-from": "Moved from",
"r-archived": "Moved to Archive",
"r-unarchived": "Restored from Archive",
"r-a-card": "a card",
"r-a-card": "una targeta",
"r-when-a-label-is": "When a label is",
"r-when-the-label": "When the label",
"r-list-name": "list name",
@@ -647,7 +648,7 @@
"r-its-list": "its list",
"r-archive": "Moure al arxiu",
"r-unarchive": "Restore from Archive",
"r-card": "card",
"r-card": "targeta",
"r-add": "Afegeix",
"r-remove": "Remove",
"r-label": "label",
@@ -678,7 +679,7 @@
"r-d-unarchive": "Restore card from Archive",
"r-d-add-label": "Add label",
"r-d-remove-label": "Remove label",
"r-create-card": "Create new card",
"r-create-card": "Crea una targeta nova",
"r-in-list": "in list",
"r-in-swimlane": "in swimlane",
"r-d-add-member": "Add member",
@@ -699,7 +700,7 @@
"r-swimlane-name": "swimlane name",
"r-board-note": "Note: leave a field empty to match every possible value.",
"r-checklist-note": "Note: checklist's items have to be written as comma separated values.",
"r-when-a-card-is-moved": "When a card is moved to another list",
"r-when-a-card-is-moved": "Quan una targeta es mou a una altra llista",
"r-set": "Set",
"r-update": "Update",
"r-datefield": "date field",
@@ -752,6 +753,11 @@
"accounts-allowUserDelete": "Allow users to self delete their account",
"hide-minicard-label-text": "Hide minicard label text",
"show-desktop-drag-handles": "Show desktop drag handles",
"assignee": "Assignee",
"cardAssigneesPopup-title": "Assignee"
"assignee": "Assignat",
"cardAssigneesPopup-title": "Assignat",
"addmore-detail": "Afegiu una descripció més detallada",
"show-on-card": "Show on Card",
"new": "New",
"editUserPopup-title": "Edit User",
"newUserPopup-title": "New User"
}
+8 -2
View File
@@ -583,8 +583,9 @@
"default": "Výchozí",
"queue": "Fronta",
"subtask-settings": "Nastavení podúkolů",
"card-settings": "Card Settings",
"boardSubtaskSettingsPopup-title": "Nastavení podúkolů tabla",
"show-subtasks-field": "Karty mohou mít podúkoly",
"boardCardSettingsPopup-title": "Card Settings",
"deposit-subtasks-board": "Vložit podúkoly do tohoto tabla",
"deposit-subtasks-list": "Landing list for subtasks deposited here:",
"show-parent-in-minicard": "Ukázat předka na minikartě",
@@ -753,5 +754,10 @@
"hide-minicard-label-text": "Hide minicard label text",
"show-desktop-drag-handles": "Show desktop drag handles",
"assignee": "Assignee",
"cardAssigneesPopup-title": "Assignee"
"cardAssigneesPopup-title": "Assignee",
"addmore-detail": "Add a more detailed description",
"show-on-card": "Show on Card",
"new": "New",
"editUserPopup-title": "Edit User",
"newUserPopup-title": "New User"
}
+8 -2
View File
@@ -583,8 +583,9 @@
"default": "Default",
"queue": "Queue",
"subtask-settings": "Subtasks Settings",
"card-settings": "Card Settings",
"boardSubtaskSettingsPopup-title": "Board Subtasks Settings",
"show-subtasks-field": "Cards can have subtasks",
"boardCardSettingsPopup-title": "Card Settings",
"deposit-subtasks-board": "Deposit subtasks to this board:",
"deposit-subtasks-list": "Landing list for subtasks deposited here:",
"show-parent-in-minicard": "Show parent in minicard:",
@@ -753,5 +754,10 @@
"hide-minicard-label-text": "Hide minicard label text",
"show-desktop-drag-handles": "Show desktop drag handles",
"assignee": "Assignee",
"cardAssigneesPopup-title": "Assignee"
"cardAssigneesPopup-title": "Assignee",
"addmore-detail": "Add a more detailed description",
"show-on-card": "Show on Card",
"new": "New",
"editUserPopup-title": "Edit User",
"newUserPopup-title": "New User"
}
+9 -3
View File
@@ -224,7 +224,7 @@
"no-comments": "Keine Kommentare",
"no-comments-desc": "Kann keine Kommentare und Aktivitäten sehen.",
"worker": "Arbeiter",
"worker-desc": "Kann nur Karten verschieben, sich selbst zuweisen und kommentieren.",
"worker-desc": "Kann Karten nur verschieben, sich selbst zuweisen und kommentieren.",
"computer": "Computer",
"confirm-subtask-delete-dialog": "Wollen Sie die Teilaufgabe wirklich löschen?",
"confirm-checklist-delete-dialog": "Wollen Sie die Checkliste wirklich löschen?",
@@ -583,8 +583,9 @@
"default": "Standard",
"queue": "Warteschlange",
"subtask-settings": "Einstellungen für Teilaufgaben",
"card-settings": "Karten-Einstellungen",
"boardSubtaskSettingsPopup-title": "Boardeinstellungen für Teilaufgaben",
"show-subtasks-field": "Karten können Teilaufgaben haben",
"boardCardSettingsPopup-title": "Karten-Einstellungen",
"deposit-subtasks-board": "Teilaufgaben in diesem Board ablegen:",
"deposit-subtasks-list": "Zielliste für hier abgelegte Teilaufgaben:",
"show-parent-in-minicard": "Übergeordnetes Element auf Minikarte anzeigen:",
@@ -753,5 +754,10 @@
"hide-minicard-label-text": "Labeltext auf Minikarte ausblenden",
"show-desktop-drag-handles": "Desktop-Ziehpunkte anzeigen",
"assignee": "Zugewiesen",
"cardAssigneesPopup-title": "Zugewiesen"
"cardAssigneesPopup-title": "Zugewiesen",
"addmore-detail": "Eine detailliertere Beschreibung hinzufügen",
"show-on-card": "Zeige auf Karte",
"new": "Neu",
"editUserPopup-title": "Benutzer ändern",
"newUserPopup-title": "Neuer Benutzer"
}
+8 -2
View File
@@ -583,8 +583,9 @@
"default": "Default",
"queue": "Queue",
"subtask-settings": "Subtasks Settings",
"card-settings": "Card Settings",
"boardSubtaskSettingsPopup-title": "Board Subtasks Settings",
"show-subtasks-field": "Cards can have subtasks",
"boardCardSettingsPopup-title": "Card Settings",
"deposit-subtasks-board": "Deposit subtasks to this board:",
"deposit-subtasks-list": "Landing list for subtasks deposited here:",
"show-parent-in-minicard": "Show parent in minicard:",
@@ -753,5 +754,10 @@
"hide-minicard-label-text": "Hide minicard label text",
"show-desktop-drag-handles": "Show desktop drag handles",
"assignee": "Assignee",
"cardAssigneesPopup-title": "Assignee"
"cardAssigneesPopup-title": "Assignee",
"addmore-detail": "Add a more detailed description",
"show-on-card": "Show on Card",
"new": "New",
"editUserPopup-title": "Edit User",
"newUserPopup-title": "New User"
}
+8 -2
View File
@@ -583,8 +583,9 @@
"default": "Default",
"queue": "Queue",
"subtask-settings": "Subtasks Settings",
"card-settings": "Card Settings",
"boardSubtaskSettingsPopup-title": "Board Subtasks Settings",
"show-subtasks-field": "Cards can have subtasks",
"boardCardSettingsPopup-title": "Card Settings",
"deposit-subtasks-board": "Deposit subtasks to this board:",
"deposit-subtasks-list": "Landing list for subtasks deposited here:",
"show-parent-in-minicard": "Show parent in minicard:",
@@ -753,5 +754,10 @@
"hide-minicard-label-text": "Hide minicard label text",
"show-desktop-drag-handles": "Show desktop drag handles",
"assignee": "Assignee",
"cardAssigneesPopup-title": "Assignee"
"cardAssigneesPopup-title": "Assignee",
"addmore-detail": "Add a more detailed description",
"show-on-card": "Show on Card",
"new": "New",
"editUserPopup-title": "Edit User",
"newUserPopup-title": "New User"
}
+8 -2
View File
@@ -583,8 +583,9 @@
"default": "Default",
"queue": "Queue",
"subtask-settings": "Subtasks Settings",
"card-settings": "Card Settings",
"boardSubtaskSettingsPopup-title": "Board Subtasks Settings",
"show-subtasks-field": "Cards can have subtasks",
"boardCardSettingsPopup-title": "Card Settings",
"deposit-subtasks-board": "Deposit subtasks to this board:",
"deposit-subtasks-list": "Landing list for subtasks deposited here:",
"show-parent-in-minicard": "Show parent in minicard:",
@@ -756,5 +757,10 @@
"hide-minicard-label-text": "Hide minicard label text",
"show-desktop-drag-handles": "Show desktop drag handles",
"assignee": "Assignee",
"cardAssigneesPopup-title": "Assignee"
"cardAssigneesPopup-title": "Assignee",
"addmore-detail": "Add a more detailed description",
"show-on-card": "Show on Card",
"new": "New",
"editUserPopup-title": "Edit User",
"newUserPopup-title": "New User"
}
+36 -30
View File
@@ -128,7 +128,7 @@
"board-private-info": "This board will be <strong>private</strong>.",
"board-public-info": "This board will be <strong>public</strong>.",
"boardChangeColorPopup-title": "Change Board Background",
"boardChangeTitlePopup-title": "Rename Board",
"boardChangeTitlePopup-title": "Renomi tavolon",
"boardChangeVisibilityPopup-title": "Change Visibility",
"boardChangeWatchPopup-title": "Change Watch",
"boardMenuPopup-title": "Board Settings",
@@ -250,8 +250,8 @@
"custom-field-dropdown-options": "List Options",
"custom-field-dropdown-options-placeholder": "Press enter to add more options",
"custom-field-dropdown-unknown": "(unknown)",
"custom-field-number": "Number",
"custom-field-text": "Text",
"custom-field-number": "Nombro",
"custom-field-text": "Teksto",
"custom-fields": "Custom Fields",
"date": "Dato",
"decline": "Decline",
@@ -327,13 +327,13 @@
"filter-to-selection": "Filter to selection",
"advanced-filter-label": "Advanced Filter",
"advanced-filter-description": "Advanced Filter allows to write a string containing following operators: == != <= >= && || ( ) A space is used as a separator between the Operators. You can filter for all Custom Fields by typing their names and values. For Example: Field1 == Value1. Note: If fields or values contains spaces, you need to encapsulate them into single quotes. For Example: 'Field 1' == 'Value 1'. For single control characters (' \\/) to be skipped, you can use \\. For example: Field1 == I\\'m. Also you can combine multiple conditions. For Example: F1 == V1 || F1 == V2. Normally all operators are interpreted from left to right. You can change the order by placing brackets. For Example: F1 == V1 && ( F2 == V2 || F2 == V3 ). Also you can search text fields using regex: F1 == /Tes.*/i",
"fullname": "Full Name",
"fullname": "Plena nomo",
"header-logo-title": "Go back to your boards page.",
"hide-system-messages": "Hide system messages",
"headerBarCreateBoardPopup-title": "Krei tavolon",
"home": "Hejmo",
"import": "Importi",
"link": "Link",
"link": "Ligilo",
"import-board": "import board",
"import-board-c": "Import board",
"import-board-title-trello": "Import board from Trello",
@@ -435,11 +435,11 @@
"remove-member-pop": "Remove __name__ (__username__) from __boardTitle__? The member will be removed from all cards on this board. They will receive a notification.",
"removeMemberPopup-title": "Remove Member?",
"rename": "Renomi",
"rename-board": "Rename Board",
"rename-board": "Renomi tavolon",
"restore": "Forigi",
"save": "Savi",
"search": "Serĉi",
"rules": "Rules",
"rules": "Reguloj",
"search-cards": "Search from card/list titles and descriptions on this board",
"search-example": "Text to search for?",
"select-color": "Select Color",
@@ -474,7 +474,7 @@
"title": "Titolo",
"tracking": "Tracking",
"tracking-info": "You will be notified of any changes to those cards you are involved as creator or member.",
"type": "Type",
"type": "Tipo",
"unassign-member": "Unassign member",
"unsaved-description": "You have an unsaved description.",
"unwatch": "Unwatch",
@@ -555,8 +555,8 @@
"show-field-on-card": "Show this field on card",
"automatically-field-on-card": "Auto create field to all cards",
"showLabel-field-on-card": "Show field label on minicard",
"yes": "Yes",
"no": "No",
"yes": "Jes",
"no": "Ne",
"accounts": "Accounts",
"accounts-allowEmailChange": "Allow Email Change",
"accounts-allowUserNameChange": "Allow Username Change",
@@ -580,11 +580,12 @@
"boardDeletePopup-title": "Delete Board?",
"delete-board": "Delete Board",
"default-subtasks-board": "Subtasks for __board__ board",
"default": "Default",
"default": "Defaŭlto",
"queue": "Queue",
"subtask-settings": "Subtasks Settings",
"card-settings": "Card Settings",
"boardSubtaskSettingsPopup-title": "Board Subtasks Settings",
"show-subtasks-field": "Cards can have subtasks",
"boardCardSettingsPopup-title": "Card Settings",
"deposit-subtasks-board": "Deposit subtasks to this board:",
"deposit-subtasks-list": "Landing list for subtasks deposited here:",
"show-parent-in-minicard": "Show parent in minicard:",
@@ -604,13 +605,13 @@
"activity-delete-attach-card": "deleted an attachment",
"activity-set-customfield": "set custom field '%s' to '%s' in %s",
"activity-unset-customfield": "unset custom field '%s' in %s",
"r-rule": "Rule",
"r-rule": "Regulo",
"r-add-trigger": "Add trigger",
"r-add-action": "Add action",
"r-board-rules": "Board rules",
"r-add-rule": "Add rule",
"r-add-rule": "Aldoni regulon",
"r-view-rule": "View rule",
"r-delete-rule": "Delete rule",
"r-delete-rule": "Forigi regulon",
"r-new-rule-name": "New rule title",
"r-no-rules": "No rules",
"r-when-a-card": "When a card",
@@ -619,7 +620,7 @@
"r-added-to": "added to",
"r-removed-from": "Removed from",
"r-the-board": "the board",
"r-list": "list",
"r-list": "listo",
"set-filter": "Set Filter",
"r-moved-to": "Moved to",
"r-moved-from": "Moved from",
@@ -631,7 +632,7 @@
"r-list-name": "list name",
"r-when-a-member": "When a member is",
"r-when-the-member": "When the member",
"r-name": "name",
"r-name": "nomo",
"r-when-a-attach": "When an attachment",
"r-when-a-checklist": "When a checklist is",
"r-when-the-checklist": "When the checklist",
@@ -649,12 +650,12 @@
"r-unarchive": "Restore from Archive",
"r-card": "card",
"r-add": "Aldoni",
"r-remove": "Remove",
"r-remove": "Forigi",
"r-label": "label",
"r-member": "member",
"r-member": "membro",
"r-remove-all": "Remove all members from the card",
"r-set-color": "Set color to",
"r-checklist": "checklist",
"r-checklist": "kontrololisto",
"r-check-all": "Check all",
"r-uncheck-all": "Uncheck all",
"r-items-check": "items of checklist",
@@ -664,7 +665,7 @@
"r-of-checklist": "of checklist",
"r-send-email": "Send an email",
"r-to": "to",
"r-subject": "subject",
"r-subject": "temo",
"r-rule-details": "Rule details",
"r-d-move-to-top-gen": "Move card to top of its list",
"r-d-move-to-top-spec": "Move card to top of list",
@@ -672,8 +673,8 @@
"r-d-move-to-bottom-spec": "Move card to bottom of list",
"r-d-send-email": "Send email",
"r-d-send-email-to": "to",
"r-d-send-email-subject": "subject",
"r-d-send-email-message": "message",
"r-d-send-email-subject": "temo",
"r-d-send-email-message": "mesaĝo",
"r-d-archive": "Move card to Archive",
"r-d-unarchive": "Restore card from Archive",
"r-d-add-label": "Add label",
@@ -681,18 +682,18 @@
"r-create-card": "Create new card",
"r-in-list": "in list",
"r-in-swimlane": "in swimlane",
"r-d-add-member": "Add member",
"r-d-remove-member": "Remove member",
"r-d-remove-all-member": "Remove all member",
"r-d-add-member": "Aldoni membron",
"r-d-remove-member": "Forigi membron",
"r-d-remove-all-member": "Forigi ĉiujn membrojn",
"r-d-check-all": "Check all items of a list",
"r-d-uncheck-all": "Uncheck all items of a list",
"r-d-check-one": "Check item",
"r-d-uncheck-one": "Uncheck item",
"r-d-check-of-list": "of checklist",
"r-d-add-checklist": "Add checklist",
"r-d-remove-checklist": "Remove checklist",
"r-d-add-checklist": "Aldoni kontrololiston",
"r-d-remove-checklist": "Forigi kontrololiston",
"r-by": "by",
"r-add-checklist": "Add checklist",
"r-add-checklist": "Aldoni kontrololiston",
"r-with-items": "with items",
"r-items-list": "item1,item2,item3",
"r-add-swimlane": "Add swimlane",
@@ -753,5 +754,10 @@
"hide-minicard-label-text": "Hide minicard label text",
"show-desktop-drag-handles": "Show desktop drag handles",
"assignee": "Assignee",
"cardAssigneesPopup-title": "Assignee"
"cardAssigneesPopup-title": "Assignee",
"addmore-detail": "Add a more detailed description",
"show-on-card": "Show on Card",
"new": "New",
"editUserPopup-title": "Edit User",
"newUserPopup-title": "New User"
}
+8 -2
View File
@@ -583,8 +583,9 @@
"default": "Default",
"queue": "Queue",
"subtask-settings": "Subtasks Settings",
"card-settings": "Card Settings",
"boardSubtaskSettingsPopup-title": "Board Subtasks Settings",
"show-subtasks-field": "Cards can have subtasks",
"boardCardSettingsPopup-title": "Card Settings",
"deposit-subtasks-board": "Deposit subtasks to this board:",
"deposit-subtasks-list": "Landing list for subtasks deposited here:",
"show-parent-in-minicard": "Show parent in minicard:",
@@ -753,5 +754,10 @@
"hide-minicard-label-text": "Hide minicard label text",
"show-desktop-drag-handles": "Show desktop drag handles",
"assignee": "Assignee",
"cardAssigneesPopup-title": "Assignee"
"cardAssigneesPopup-title": "Assignee",
"addmore-detail": "Add a more detailed description",
"show-on-card": "Show on Card",
"new": "New",
"editUserPopup-title": "Edit User",
"newUserPopup-title": "New User"
}
+10 -4
View File
@@ -223,8 +223,8 @@
"comment-only-desc": "Solo puedes comentar en las tarjetas.",
"no-comments": "No hay comentarios",
"no-comments-desc": "No se pueden mostrar comentarios ni actividades.",
"worker": "Worker",
"worker-desc": "Can only move cards, assign itself to card and comment.",
"worker": "Trabajador",
"worker-desc": "Solo puede mover tarjetas, asignarse a la tarjeta y comentar.",
"computer": "el ordenador",
"confirm-subtask-delete-dialog": "¿Seguro que quieres eliminar la subtarea?",
"confirm-checklist-delete-dialog": "¿Seguro que quieres eliminar la lista de verificación?",
@@ -583,8 +583,9 @@
"default": "Por defecto",
"queue": "Cola",
"subtask-settings": "Preferencias de las subtareas",
"card-settings": "Preferencias de la tarjeta",
"boardSubtaskSettingsPopup-title": "Preferencias de las subtareas del tablero",
"show-subtasks-field": "Las tarjetas pueden tener subtareas",
"boardCardSettingsPopup-title": "Preferencias de la tarjeta",
"deposit-subtasks-board": "Depositar subtareas en este tablero:",
"deposit-subtasks-list": "Lista de destino para subtareas depositadas aquí:",
"show-parent-in-minicard": "Mostrar el padre en una minitarjeta:",
@@ -753,5 +754,10 @@
"hide-minicard-label-text": "Ocultar el texto de la etiqueta de la minitarjeta",
"show-desktop-drag-handles": "Mostrar los controles de arrastre del escritorio",
"assignee": "Asignado",
"cardAssigneesPopup-title": "Asignado"
"cardAssigneesPopup-title": "Asignado",
"addmore-detail": "Añadir una descripción detallada",
"show-on-card": "Mostrar en la tarjeta",
"new": "Nuevo",
"editUserPopup-title": "Editar el usuario",
"newUserPopup-title": "Nuevo usuario"
}
+8 -2
View File
@@ -583,8 +583,9 @@
"default": "Default",
"queue": "Queue",
"subtask-settings": "Subtasks Settings",
"card-settings": "Card Settings",
"boardSubtaskSettingsPopup-title": "Board Subtasks Settings",
"show-subtasks-field": "Cards can have subtasks",
"boardCardSettingsPopup-title": "Card Settings",
"deposit-subtasks-board": "Deposit subtasks to this board:",
"deposit-subtasks-list": "Landing list for subtasks deposited here:",
"show-parent-in-minicard": "Show parent in minicard:",
@@ -753,5 +754,10 @@
"hide-minicard-label-text": "Hide minicard label text",
"show-desktop-drag-handles": "Show desktop drag handles",
"assignee": "Assignee",
"cardAssigneesPopup-title": "Assignee"
"cardAssigneesPopup-title": "Assignee",
"addmore-detail": "Add a more detailed description",
"show-on-card": "Show on Card",
"new": "New",
"editUserPopup-title": "Edit User",
"newUserPopup-title": "New User"
}
+8 -2
View File
@@ -583,8 +583,9 @@
"default": "پیش‌فرض",
"queue": "صف",
"subtask-settings": "تنظیمات ریزکارها",
"card-settings": "Card Settings",
"boardSubtaskSettingsPopup-title": "تنظیمات ریزکار های برد",
"show-subtasks-field": "کارت می تواند ریزکار داشته باشد",
"boardCardSettingsPopup-title": "Card Settings",
"deposit-subtasks-board": "افزودن ریزکار به برد:",
"deposit-subtasks-list": "لیست برای ریزکار های افزوده شده",
"show-parent-in-minicard": "نمایش خانواده در ریز کارت",
@@ -753,5 +754,10 @@
"hide-minicard-label-text": "Hide minicard label text",
"show-desktop-drag-handles": "Show desktop drag handles",
"assignee": "Assignee",
"cardAssigneesPopup-title": "Assignee"
"cardAssigneesPopup-title": "Assignee",
"addmore-detail": "Add a more detailed description",
"show-on-card": "Show on Card",
"new": "New",
"editUserPopup-title": "Edit User",
"newUserPopup-title": "New User"
}
+8 -2
View File
@@ -583,8 +583,9 @@
"default": "Oletus",
"queue": "Jono",
"subtask-settings": "Alitehtävä-asetukset",
"card-settings": "Kortin asetukset",
"boardSubtaskSettingsPopup-title": "Taulualitehtävien asetukset",
"show-subtasks-field": "Korteilla voi olla alitehtäviä",
"boardCardSettingsPopup-title": "Kortin asetukset",
"deposit-subtasks-board": "Talleta alitehtävät tälle taululle:",
"deposit-subtasks-list": "Laskeutumislista alatehtäville tallennettu tänne:",
"show-parent-in-minicard": "Näytä ylätehtävä minikortilla:",
@@ -753,5 +754,10 @@
"hide-minicard-label-text": "Piilota minikortin nimilappu teksti",
"show-desktop-drag-handles": "Näytä työpöydän vedon kahvat",
"assignee": "Käsittelijä",
"cardAssigneesPopup-title": "Käsittelijä"
"cardAssigneesPopup-title": "Käsittelijä",
"addmore-detail": "Lisää tarkempi kuvaus",
"show-on-card": "Näytä kortilla",
"new": "Uusi",
"editUserPopup-title": "Muokkaa käyttäjää",
"newUserPopup-title": "Uusi käyttäjä"
}
+8 -2
View File
@@ -583,8 +583,9 @@
"default": "Défaut",
"queue": "Queue",
"subtask-settings": "Paramètres des sous-tâches",
"card-settings": "Paramètres de la carte",
"boardSubtaskSettingsPopup-title": "Paramètres des sous-tâches du tableau",
"show-subtasks-field": "Les cartes peuvent avoir des sous-tâches",
"boardCardSettingsPopup-title": "Paramètres de la carte",
"deposit-subtasks-board": "Déposer des sous-tâches dans ce tableau :",
"deposit-subtasks-list": "Liste de destination pour les sous-tâches déposées ici :",
"show-parent-in-minicard": "Voir la carte parente dans la mini-carte :",
@@ -753,5 +754,10 @@
"hide-minicard-label-text": "Cacher le label de la minicarte",
"show-desktop-drag-handles": "Voir les poignées de déplacement du bureau",
"assignee": "Cessionnaire",
"cardAssigneesPopup-title": "Cessionnaire"
"cardAssigneesPopup-title": "Cessionnaire",
"addmore-detail": "Ajouter une description plus détaillée",
"show-on-card": "Afficher sur la carte",
"new": "Nouveau",
"editUserPopup-title": "Éditer l'utilisateur",
"newUserPopup-title": "Nouvel utilisateur"
}
+8 -2
View File
@@ -583,8 +583,9 @@
"default": "Default",
"queue": "Queue",
"subtask-settings": "Subtasks Settings",
"card-settings": "Card Settings",
"boardSubtaskSettingsPopup-title": "Board Subtasks Settings",
"show-subtasks-field": "Cards can have subtasks",
"boardCardSettingsPopup-title": "Card Settings",
"deposit-subtasks-board": "Deposit subtasks to this board:",
"deposit-subtasks-list": "Landing list for subtasks deposited here:",
"show-parent-in-minicard": "Show parent in minicard:",
@@ -753,5 +754,10 @@
"hide-minicard-label-text": "Hide minicard label text",
"show-desktop-drag-handles": "Show desktop drag handles",
"assignee": "Assignee",
"cardAssigneesPopup-title": "Assignee"
"cardAssigneesPopup-title": "Assignee",
"addmore-detail": "Add a more detailed description",
"show-on-card": "Show on Card",
"new": "New",
"editUserPopup-title": "Edit User",
"newUserPopup-title": "New User"
}
+8 -2
View File
@@ -583,8 +583,9 @@
"default": "בררת מחדל",
"queue": "תור",
"subtask-settings": "הגדרות תתי משימות",
"card-settings": "הגדרות כרטיס",
"boardSubtaskSettingsPopup-title": "הגדרות תת־משימות בלוח",
"show-subtasks-field": "לכרטיסים יכולות להיות תת־משימות",
"boardCardSettingsPopup-title": "הגדרות כרטיס",
"deposit-subtasks-board": "הפקדת תת־משימות ללוח הזה:",
"deposit-subtasks-list": "רשימות נחיתה עבור תת־משימות שהופקדו כאן:",
"show-parent-in-minicard": "הצגת ההורה במיני כרטיס:",
@@ -753,5 +754,10 @@
"hide-minicard-label-text": "הסתרת טקסט התווית של מיני כרטיס",
"show-desktop-drag-handles": "הצגת ידיות גרירה של שולחן העבודה",
"assignee": "גורם אחראי",
"cardAssigneesPopup-title": "גורם אחראי"
"cardAssigneesPopup-title": "גורם אחראי",
"addmore-detail": "הוספת תיאור מפורט",
"show-on-card": "הצגה על הכרטיס",
"new": "חדש",
"editUserPopup-title": "עריכת משתמש",
"newUserPopup-title": "משתמש חדש"
}
+8 -2
View File
@@ -583,8 +583,9 @@
"default": "Default",
"queue": "Queue",
"subtask-settings": "Subtasks व्यवस्था",
"card-settings": "Card Settings",
"boardSubtaskSettingsPopup-title": "बोर्ड Subtasks व्यवस्था",
"show-subtasks-field": "Cards can have subtasks",
"boardCardSettingsPopup-title": "Card Settings",
"deposit-subtasks-board": "Deposit subtasks तक यह बोर्ड:",
"deposit-subtasks-list": "Landing सूची for subtasks deposited here:",
"show-parent-in-minicard": "Show parent अंदर में minicard:",
@@ -753,5 +754,10 @@
"hide-minicard-label-text": "Hide minicard label text",
"show-desktop-drag-handles": "Show desktop drag handles",
"assignee": "Assignee",
"cardAssigneesPopup-title": "Assignee"
"cardAssigneesPopup-title": "Assignee",
"addmore-detail": "Add a more detailed description",
"show-on-card": "Show on Card",
"new": "New",
"editUserPopup-title": "Edit User",
"newUserPopup-title": "New User"
}
+8 -2
View File
@@ -583,8 +583,9 @@
"default": "Default",
"queue": "Queue",
"subtask-settings": "Alfeladat beállítások",
"card-settings": "Card Settings",
"boardSubtaskSettingsPopup-title": "Tábla alfeladat beállítások",
"show-subtasks-field": "Cards can have subtasks",
"boardCardSettingsPopup-title": "Card Settings",
"deposit-subtasks-board": "Deposit subtasks to this board:",
"deposit-subtasks-list": "Landing list for subtasks deposited here:",
"show-parent-in-minicard": "Show parent in minicard:",
@@ -753,5 +754,10 @@
"hide-minicard-label-text": "Hide minicard label text",
"show-desktop-drag-handles": "Show desktop drag handles",
"assignee": "Assignee",
"cardAssigneesPopup-title": "Assignee"
"cardAssigneesPopup-title": "Assignee",
"addmore-detail": "Add a more detailed description",
"show-on-card": "Show on Card",
"new": "New",
"editUserPopup-title": "Edit User",
"newUserPopup-title": "New User"
}
+8 -2
View File
@@ -583,8 +583,9 @@
"default": "Default",
"queue": "Queue",
"subtask-settings": "Subtasks Settings",
"card-settings": "Card Settings",
"boardSubtaskSettingsPopup-title": "Board Subtasks Settings",
"show-subtasks-field": "Cards can have subtasks",
"boardCardSettingsPopup-title": "Card Settings",
"deposit-subtasks-board": "Deposit subtasks to this board:",
"deposit-subtasks-list": "Landing list for subtasks deposited here:",
"show-parent-in-minicard": "Show parent in minicard:",
@@ -753,5 +754,10 @@
"hide-minicard-label-text": "Hide minicard label text",
"show-desktop-drag-handles": "Show desktop drag handles",
"assignee": "Assignee",
"cardAssigneesPopup-title": "Assignee"
"cardAssigneesPopup-title": "Assignee",
"addmore-detail": "Add a more detailed description",
"show-on-card": "Show on Card",
"new": "New",
"editUserPopup-title": "Edit User",
"newUserPopup-title": "New User"
}
+8 -2
View File
@@ -583,8 +583,9 @@
"default": "Default",
"queue": "Queue",
"subtask-settings": "Subtasks Settings",
"card-settings": "Card Settings",
"boardSubtaskSettingsPopup-title": "Board Subtasks Settings",
"show-subtasks-field": "Cards can have subtasks",
"boardCardSettingsPopup-title": "Card Settings",
"deposit-subtasks-board": "Deposit subtasks to this board:",
"deposit-subtasks-list": "Landing list for subtasks deposited here:",
"show-parent-in-minicard": "Show parent in minicard:",
@@ -753,5 +754,10 @@
"hide-minicard-label-text": "Hide minicard label text",
"show-desktop-drag-handles": "Show desktop drag handles",
"assignee": "Assignee",
"cardAssigneesPopup-title": "Assignee"
"cardAssigneesPopup-title": "Assignee",
"addmore-detail": "Add a more detailed description",
"show-on-card": "Show on Card",
"new": "New",
"editUserPopup-title": "Edit User",
"newUserPopup-title": "New User"
}
+8 -2
View File
@@ -583,8 +583,9 @@
"default": "Default",
"queue": "Queue",
"subtask-settings": "Subtasks Settings",
"card-settings": "Card Settings",
"boardSubtaskSettingsPopup-title": "Board Subtasks Settings",
"show-subtasks-field": "Cards can have subtasks",
"boardCardSettingsPopup-title": "Card Settings",
"deposit-subtasks-board": "Deposit subtasks to this board:",
"deposit-subtasks-list": "Landing list for subtasks deposited here:",
"show-parent-in-minicard": "Show parent in minicard:",
@@ -753,5 +754,10 @@
"hide-minicard-label-text": "Hide minicard label text",
"show-desktop-drag-handles": "Show desktop drag handles",
"assignee": "Assignee",
"cardAssigneesPopup-title": "Assignee"
"cardAssigneesPopup-title": "Assignee",
"addmore-detail": "Add a more detailed description",
"show-on-card": "Show on Card",
"new": "New",
"editUserPopup-title": "Edit User",
"newUserPopup-title": "New User"
}
+24 -18
View File
@@ -24,7 +24,7 @@
"act-createCard": "scheda __card__ creata nella lista __list__ della corsia __swimlane__ della bacheca __board__",
"act-createCustomField": "creato campo personalizzato __customField__ nella bacheca __board",
"act-deleteCustomField": "campo personalizzato eliminato __customField__ nella bacheca __board",
"act-setCustomField": "edited custom field __customField__: __customFieldValue__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
"act-setCustomField": "campo personalizzato modificato __customField__: __customFieldValue__ sulla scheda __card__ sulla lista __list__ sulla swimlane __swimlane__ sulla bacheca __board__",
"act-createList": "aggiunta lista __list__ alla bacheca __board__",
"act-addBoardMember": "aggiunto membro __member__ alla bacheca __board__",
"act-archivedBoard": "Bacheca __board__ archiviata",
@@ -137,7 +137,7 @@
"board-view": "Visualizza bacheca",
"board-view-cal": "Calendario",
"board-view-swimlanes": "Diagramma Swimlane",
"board-view-collapse": "Collapse",
"board-view-collapse": "Collassa",
"board-view-lists": "Liste",
"bucket-example": "Per esempio come \"una lista di cose da fare\"",
"cancel": "Cancella",
@@ -223,7 +223,7 @@
"comment-only-desc": "Puoi commentare solo le schede.",
"no-comments": "Non ci sono commenti.",
"no-comments-desc": "Impossibile visualizzare commenti o attività.",
"worker": "Worker",
"worker": "Lavoratore",
"worker-desc": "Può solo spostare schede, assegnarsi una scheda e commentare.",
"computer": "Computer",
"confirm-subtask-delete-dialog": "Sei sicuro di voler eliminare il sotto-compito?",
@@ -309,7 +309,7 @@
"list-sort-by": "Ordina la lista per:",
"list-label-modifiedAt": "Orario ultimo accesso",
"list-label-title": "Nome della lista",
"list-label-sort": "Your Manual Order",
"list-label-sort": "Il tuo ordine manuale",
"list-label-short-modifiedAt": "(L)",
"list-label-short-title": "(N)",
"list-label-short-sort": "(M)",
@@ -527,11 +527,11 @@
"webhook-title": "Nome Webhook",
"webhook-token": "Token (facoltativo per l'autenticazione)",
"outgoing-webhooks": "Server esterni",
"bidirectional-webhooks": "Two-Way Webhooks",
"bidirectional-webhooks": "Webhook a due vie",
"outgoingWebhooksPopup-title": "Server esterni",
"boardCardTitlePopup-title": "Filtro per Titolo Scheda",
"disable-webhook": "Disattiva questo Webhook",
"global-webhook": "Global Webhooks",
"global-webhook": "Webhook globali",
"new-outgoing-webhook": "Nuovo webhook in uscita",
"no-name": "(Sconosciuto)",
"Node_version": "Versione di Node",
@@ -583,8 +583,9 @@
"default": "Predefinito",
"queue": "Coda",
"subtask-settings": "Impostazioni sotto-compiti",
"card-settings": "Impostazioni della scheda",
"boardSubtaskSettingsPopup-title": "Impostazioni sotto-compiti della bacheca",
"show-subtasks-field": "Le schede posso avere dei sotto-compiti",
"boardCardSettingsPopup-title": "Impostazioni della scheda",
"deposit-subtasks-board": "Deposita i sotto compiti in questa bacheca",
"deposit-subtasks-list": "Lista di destinaizoni per questi sotto-compiti",
"show-parent-in-minicard": "Mostra genirotri nelle mini schede:",
@@ -707,7 +708,7 @@
"r-df-due-at": "scadenza",
"r-df-end-at": "fine",
"r-df-received-at": "ricevuta",
"r-to-current-datetime": "to current date/time",
"r-to-current-datetime": "a data/ora corrente",
"r-remove-value-from": "Rimuovi valore da",
"ldap": "LDAP",
"oauth2": "Oauth2",
@@ -730,8 +731,8 @@
"restore-all": "Restore all",
"delete-all": "Cancella tutto",
"loading": "Loading, please wait.",
"previous_as": "last time was",
"act-a-dueAt": "modified due time to \nWhen: __timeValue__\nWhere: __card__\n previous due was __timeOldValue__",
"previous_as": "l'ultima volta è stata",
"act-a-dueAt": "Scadenza modificata in __timeValue__\nData precedente: __timeOldValue__",
"act-a-endAt": "modified ending time to __timeValue__ from (__timeOldValue__)",
"act-a-startAt": "modified starting time to __timeValue__ from (__timeOldValue__)",
"act-a-receivedAt": "modified received time to __timeValue__ from (__timeOldValue__)",
@@ -739,19 +740,24 @@
"a-endAt": "modified ending time to be",
"a-startAt": "modified starting time to be",
"a-receivedAt": "modified received time to be",
"almostdue": "current due time %s is approaching",
"pastdue": "current due time %s is past",
"duenow": "current due time %s is today",
"almostdue": "la data di scadenza attuale %s si sta avvicinando",
"pastdue": "la data di scadenza attuale %s è scaduta",
"duenow": "la data di scadenza attuale %s è oggi",
"act-newDue": "__list__/__card__ has 1st due reminder [__board__]",
"act-withDue": "__list__/__card__ due reminders [__board__]",
"act-almostdue": "was reminding the current due (__timeValue__) of __card__ is approaching",
"act-pastdue": "was reminding the current due (__timeValue__) of __card__ is past",
"act-duenow": "was reminding the current due (__timeValue__) of __card__ is now",
"act-atUserComment": "You were mentioned in [__board__] __list__/__card__",
"act-atUserComment": "Sei stato menzionato in [__board__] __list__/__card__",
"delete-user-confirm-popup": "Sei sicuro di voler cancellare questo profilo? Non sarà possibile ripristinarlo.",
"accounts-allowUserDelete": "Permetti agli utenti di cancellare il loro profilo",
"hide-minicard-label-text": "Hide minicard label text",
"show-desktop-drag-handles": "Show desktop drag handles",
"assignee": "Assignee",
"cardAssigneesPopup-title": "Assignee"
"hide-minicard-label-text": "Nascondi etichetta minicard",
"show-desktop-drag-handles": "Mostra maniglie di trascinamento del desktop",
"assignee": "Assegnatario",
"cardAssigneesPopup-title": "Assegnatario",
"addmore-detail": "Aggiungi una descrizione più dettagliata",
"show-on-card": "Mostra sulla scheda",
"new": "Nuovo",
"editUserPopup-title": "Modifica utente",
"newUserPopup-title": "Nuovo utente"
}
+327 -321
View File
@@ -1,45 +1,45 @@
{
"accept": "受け入れ",
"act-activity-notify": "アクティビティ通知",
"act-addAttachment": "added attachment __attachment__ to card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
"act-deleteAttachment": "deleted attachment __attachment__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
"act-addSubtask": "added subtask __subtask__ to card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
"act-addLabel": "Added label __label__ to card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
"act-addedLabel": "Added label __label__ to card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
"act-removeLabel": "Removed label __label__ from card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
"act-removedLabel": "Removed label __label__ from card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
"act-addChecklist": "added checklist __checklist__ to card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
"act-addChecklistItem": "added checklist item __checklistItem__ to checklist __checklist__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
"act-removeChecklist": "removed checklist __checklist__ from card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
"act-removeChecklistItem": "removed checklist item __checklistItem__ from checklist __checkList__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
"act-checkedItem": "checked __checklistItem__ of checklist __checklist__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
"act-uncheckedItem": "unchecked __checklistItem__ of checklist __checklist__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
"act-completeChecklist": "completed checklist __checklist__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
"act-uncompleteChecklist": "uncompleted checklist __checklist__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
"act-addComment": "commented on card __card__: __comment__ at list __list__ at swimlane __swimlane__ at board __board__",
"act-editComment": "edited comment on card __card__: __comment__ at list __list__ at swimlane __swimlane__ at board __board__",
"act-deleteComment": "deleted comment on card __card__: __comment__ at list __list__ at swimlane __swimlane__ at board __board__",
"act-createBoard": "created board __board__",
"act-createSwimlane": "created swimlane __swimlane__ to board __board__",
"act-createCard": "created card __card__ to list __list__ at swimlane __swimlane__ at board __board__",
"act-createCustomField": "created custom field __customField__ at board __board__",
"act-deleteCustomField": "deleted custom field __customField__ at board __board__",
"act-setCustomField": "edited custom field __customField__: __customFieldValue__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
"act-createList": "added list __list__ to board __board__",
"act-addBoardMember": "added member __member__ to board __board__",
"act-archivedBoard": "Board __board__ moved to Archive",
"act-archivedCard": "Card __card__ at list __list__ at swimlane __swimlane__ at board __board__ moved to Archive",
"act-archivedList": "List __list__ at swimlane __swimlane__ at board __board__ moved to Archive",
"act-archivedSwimlane": "Swimlane __swimlane__ at board __board__ moved to Archive",
"act-importBoard": "imported board __board__",
"act-importCard": "imported card __card__ to list __list__ at swimlane __swimlane__ at board __board__",
"act-importList": "imported list __list__ to swimlane __swimlane__ at board __board__",
"act-joinMember": "added member __member__ to card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
"act-moveCard": "moved card __card__ at board __board__ from list __oldList__ at swimlane __oldSwimlane__ to list __list__ at swimlane __swimlane__",
"act-moveCardToOtherBoard": "moved card __card__ from list __oldList__ at swimlane __oldSwimlane__ at board __oldBoard__ to list __list__ at swimlane __swimlane__ at board __board__",
"act-removeBoardMember": "removed member __member__ from board __board__",
"act-restoredCard": "restored card __card__ to list __list__ at swimlane __swimlane__ at board __board__",
"act-unjoinMember": "removed member __member__ from card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
"act-addAttachment": "添付ファイル __attachment__ を ボード __board__, スイムレーン __swimlane__, リスト __list__, カード __card__ に追加しました",
"act-deleteAttachment": "添付ファイル __attachment__ を ボード __board__, スイムレーン __swimlane__, リスト __list__, カード __card__ から削除しました",
"act-addSubtask": "サブタスク __subtask__ を ボード __board__, スイムレーン __swimlane__, リスト __list__, カード __card__ に追加しました",
"act-addLabel": "ラベル __label__ を ボード __board__, スイムレーン __swimlane__, リスト __list__, カード __card__ に追加しました",
"act-addedLabel": "ラベル __label__ を ボード __board__, スイムレーン __swimlane__, リスト __list__, カード __card__ に追加しました",
"act-removeLabel": "ラベル __label__ を ボード __board__, スイムレーン __swimlane__, リスト __list__, カード __card__ から削除しました",
"act-removedLabel": "ラベル __label__ を ボード __board__, スイムレーン __swimlane__, リスト __list__, カード __card__ から削除しました",
"act-addChecklist": "チェックリスト __checklist__ を ボード __board__, スイムレーン __swimlane__, リスト __list__, カード __card__ に追加しました",
"act-addChecklistItem": "チェックリスト項目 __checklistItem__ を ボード __board__, スイムレーン __swimlane__, リスト __list__, カード __card__, チェックリスト __checkList__ に追加しました",
"act-removeChecklist": "チェックリスト __checklist__ を ボード __board__, スイムレーン __swimlane__, リスト __list__, カード __card__ から削除しました",
"act-removeChecklistItem": "チェックリスト項目 __checklistItem__ を ボード __board__, スイムレーン __swimlane__, リスト __list__, カード __card__, チェックリスト __checkList__ から削除しました",
"act-checkedItem": "__checklistItem__ がチェックされました / ボード __board__, スイムレーン __swimlane__, リスト __list__, カード __card__, チェックリスト __checklist__",
"act-uncheckedItem": "__checklistItem__ が未チェックになりました / ボード __board__, スイムレーン __swimlane__, リスト __list__, カード __card__, チェックリスト __checklist__",
"act-completeChecklist": "チェックリスト __checklist__ ボード __board__, スイムレーン __swimlane__, リスト __list__, カード __card__ が完了しました",
"act-uncompleteChecklist": "チェックリスト __checklist__ ボード __board__, スイムレーン __swimlane__, リスト __list__, カード __card__ が未完了になりました",
"act-addComment": "カード __card__ にコメントが追加されました: __comment__ / ボード __board__, スイムレーン __swimlane__, リスト __list__",
"act-editComment": "カード __card__ のコメントが編集されました: __comment__ / ボード __board__, スイムレーン __swimlane__, リスト __list__",
"act-deleteComment": "コメント __comment__ を ボード __board__, スイムレーン __swimlane__, リスト __list__, カード __card__ から削除しました",
"act-createBoard": "ボード __board__ を作成しました",
"act-createSwimlane": "スイムレーン __swimlane__ を ボード __board__ に作成しました",
"act-createCard": "カード __card__ をボード __board__, スイムレーン __swimlane__, リスト __list__ に作成しました",
"act-createCustomField": "カスタムフィールド __customField__ を ボード __board__ に作成しました",
"act-deleteCustomField": "カスタムフィールド __customField__ を ボード __board__ から削除しました",
"act-setCustomField": "カスタムフィールド __customField__ が編集されました: __customFieldValue__ / ボード __board__, スイムレーン __swimlane__, リスト __list__, カード __card__",
"act-createList": "リスト __list__ を ボード __board__ に追加しました",
"act-addBoardMember": "メンバー __member__ を ボード __board__ に追加しました",
"act-archivedBoard": "ボード __board__ をアーカイブしました",
"act-archivedCard": "カード __card__ を ボード __board__, スイムレーン __swimlane__, リスト __list__ からアーカイブしました",
"act-archivedList": "リスト __list__ を ボード __board__, スイムレーン __swimlane__ からアーカイブしました",
"act-archivedSwimlane": "スイムレーン __swimlane__ を ボード __board__ からアーカイブしました",
"act-importBoard": "ボード __board__ をインポートしました",
"act-importCard": "カード __card__ が ボード __board__, スイムレーン __swimlane__, リスト __list__ にインポートされました",
"act-importList": "リスト __list__ が ボード __board__, スイムレーン __swimlane__ にインポートされました",
"act-joinMember": "メンバー __member__ を ボード __board__, スイムレーン __swimlane__, リスト __list__, カード __card__ に追加しました",
"act-moveCard": "カード __card__ / ボード __board__ が スイムレーン __oldSwimlane__, リスト __oldList__ から スイムレーン __swimlane__, リスト __list__ に移動されました",
"act-moveCardToOtherBoard": "カード __card__ が ボード __oldBoard__, スイムレーン __oldSwimlane__, リスト __oldList__ から ボード __board__, スイムレーン __swimlane__, リスト __list__ に移動されました",
"act-removeBoardMember": "メンバー __member__ を ボード __board__ から削除しました",
"act-restoredCard": "カード __card__ が ボード __board__, スイムレーン __swimlane__, リスト __list__ に復元されました",
"act-unjoinMember": "メンバー __member__ を ボード __board__, スイムレーン __swimlane__, リスト __list__, カード __card__ から削除しました",
"act-withBoardTitle": "__board__",
"act-withCardTitle": "[__board__] __card__",
"actions": "操作",
@@ -49,7 +49,7 @@
"activity-archived": "%sをアーカイブしました",
"activity-attached": "%s を %s に添付しました",
"activity-created": "%s を作成しました",
"activity-customfield-created": "%s を作成しました",
"activity-customfield-created": "カスタムフィールド %s を作成しました",
"activity-excluded": "%s を %s から除外しました",
"activity-imported": "imported %s into %s from %s",
"activity-imported-board": "imported %s from %s",
@@ -60,21 +60,21 @@
"activity-sent": "%s を %s に送りました",
"activity-unjoined": "%s への参加を止めました",
"activity-subtask-added": "%sにサブタスクを追加しました",
"activity-checked-item": "checked %s in checklist %s of %s",
"activity-unchecked-item": "unchecked %s in checklist %s of %s",
"activity-checked-item": "チェックされました %s チェックリスト %s / %s",
"activity-unchecked-item": "チェックが外されました %s チェックリスト %s / %s",
"activity-checklist-added": "%s にチェックリストを追加しました",
"activity-checklist-removed": "removed a checklist from %s",
"activity-checklist-completed": "completed checklist __checklist__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
"activity-checklist-uncompleted": "uncompleted the checklist %s of %s",
"activity-checklist-item-added": "added checklist item to '%s' in %s",
"activity-checklist-item-removed": "removed a checklist item from '%s' in %s",
"activity-checklist-removed": "チェックリストを %s から削除しました",
"activity-checklist-completed": "チェックリスト __checklist__ ボード __board__, スイムレーン __swimlane__, リスト __list__, カード __card__ が完了しました",
"activity-checklist-uncompleted": "チェックリスト %s / %s が未完了になりました",
"activity-checklist-item-added": "チェックリスト項目を '%s' / %s に追加しました",
"activity-checklist-item-removed": "'%s' の チェックリスト %s の項目を削除しました",
"add": "追加",
"activity-checked-item-card": "checked %s in checklist %s",
"activity-unchecked-item-card": "unchecked %s in checklist %s",
"activity-checklist-completed-card": "completed checklist __checklist__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
"activity-checklist-uncompleted-card": "uncompleted the checklist %s",
"activity-editComment": "edited comment %s",
"activity-deleteComment": "deleted comment %s",
"activity-checked-item-card": "チェックされました %s チェックリスト %s",
"activity-unchecked-item-card": "チェックが外されました %s チェックリスト %s",
"activity-checklist-completed-card": "チェックリスト __checklist__ ボード __board__, スイムレーン __swimlane__, リスト __list__, カード __card__ が完了しました",
"activity-checklist-uncompleted-card": "チェックリスト %s が未完了になりました",
"activity-editComment": "コメント %s を編集しました",
"activity-deleteComment": "コメント %s を削除しました",
"add-attachment": "添付ファイルを追加",
"add-board": "ボードを追加",
"add-card": "カードを追加",
@@ -90,26 +90,26 @@
"addMemberPopup-title": "メンバー",
"admin": "管理",
"admin-desc": "カードの閲覧と編集、メンバーの削除、ボードの設定変更が可能",
"admin-announcement": "Announcement",
"admin-announcement-active": "Active System-Wide Announcement",
"admin-announcement-title": "Announcement from Administrator",
"admin-announcement": "アナウンス",
"admin-announcement-active": "システム全体アナウンスを有効化",
"admin-announcement-title": "管理者からのアナウンス",
"all-boards": "全てのボード",
"and-n-other-card": "And __count__ other card",
"and-n-other-card_plural": "And __count__ other cards",
"apply": "適用",
"app-is-offline": "Loading, please wait. Refreshing the page will cause data loss. If loading does not work, please check that server has not stopped.",
"archive": "Move to Archive",
"archive-all": "Move All to Archive",
"archive-board": "Move Board to Archive",
"archive-card": "Move Card to Archive",
"archive-list": "Move List to Archive",
"archive-swimlane": "Move Swimlane to Archive",
"archive-selection": "Move selection to Archive",
"archiveBoardPopup-title": "Move Board to Archive?",
"app-is-offline": "ロード中です、お待ちください。ページを更新するとデータが失われます。ロードが終わらない場合、サーバーが停止していないかどうかを確認してください。",
"archive": "アーカイブする",
"archive-all": "すべてアーカイブする",
"archive-board": "ボードをアーカイブする",
"archive-card": "カードをアーカイブする",
"archive-list": "リストをアーカイブする",
"archive-swimlane": "スイムレーンをアーカイブする",
"archive-selection": "選択をアーカイブする",
"archiveBoardPopup-title": "ボードをアーカイブしますか?",
"archived-items": "アーカイブ",
"archived-boards": "アーカイブ済みボード",
"restore-board": "ボードをリストア",
"no-archived-boards": "No Boards in Archive.",
"no-archived-boards": "アーカイブ済みボードはありません。",
"archives": "アーカイブ",
"template": "テンプレート",
"templates": "テンプレート",
@@ -132,24 +132,24 @@
"boardChangeVisibilityPopup-title": "公開範囲の変更",
"boardChangeWatchPopup-title": "ウォッチの変更",
"boardMenuPopup-title": "ボード設定",
"boardChangeViewPopup-title": "Board View",
"boardChangeViewPopup-title": "ボード表示",
"boards": "ボード",
"board-view": "Board View",
"board-view": "ボード表示",
"board-view-cal": "カレンダー",
"board-view-swimlanes": "スイムレーン",
"board-view-collapse": "Collapse",
"board-view-collapse": "折りたたむ",
"board-view-lists": "リスト",
"bucket-example": "例:バケットリスト",
"cancel": "キャンセル",
"card-archived": "This card is moved to Archive.",
"board-archived": "This board is moved to Archive.",
"card-archived": "このカードをアーカイブしました。",
"board-archived": "このボードをアーカイブしました。",
"card-comments-title": "%s 件のコメントがあります。",
"card-delete-notice": "削除は取り消しできません。このカードに関係するすべてのアクションがなくなります。",
"card-delete-pop": "すべての内容がアクティビティから削除されます。この削除は元に戻すことができません。",
"card-delete-suggest-archive": "You can move a card to Archive to remove it from the board and preserve the activity.",
"card-delete-suggest-archive": "カードをアーカイブするとボードから削除され、アクティビティに保持されます。",
"card-due": "期限",
"card-due-on": "期限日",
"card-spent": "Spent Time",
"card-spent": "作業時間",
"card-edit-attachments": "添付ファイルの編集",
"card-edit-custom-fields": "カスタムフィールドの編集",
"card-edit-labels": "ラベルの編集",
@@ -158,8 +158,8 @@
"card-members-title": "カードからボードメンバーを追加・削除する",
"card-start": "開始",
"card-start-on": "開始日",
"cardAttachmentsPopup-title": "Attach From",
"cardCustomField-datePopup-title": "Change date",
"cardAttachmentsPopup-title": "添付元",
"cardCustomField-datePopup-title": "日時変更",
"cardCustomFieldsPopup-title": "カスタムフィールドの編集",
"cardDeletePopup-title": "カードを削除しますか?",
"cardDetailsActionsPopup-title": "カード操作",
@@ -169,10 +169,10 @@
"cardTemplatePopup-title": "テンプレートの作成",
"cards": "カード",
"cards-count": "カード",
"casSignIn": "Sign In with CAS",
"casSignIn": "CASでサインインする",
"cardType-card": "カード",
"cardType-linkedCard": "Linked Card",
"cardType-linkedBoard": "Linked Board",
"cardType-linkedCard": "リンクされたカード",
"cardType-linkedBoard": "リンクされたボード",
"change": "変更",
"change-avatar": "アバターの変更",
"change-password": "パスワードの変更",
@@ -190,7 +190,7 @@
"clipboard": "クリップボードもしくはドラッグ&ドロップ",
"close": "閉じる",
"close-board": "ボードを閉じる",
"close-board-pop": "You will be able to restore the board by clicking the “Archive” button from the home header.",
"close-board-pop": "ホームのヘッダ上にあるアーカイブボタンをクリックするとボードをリストアできます。",
"color-black": "黒",
"color-blue": "青",
"color-crimson": "濃赤",
@@ -222,18 +222,18 @@
"comment-only": "コメントのみ",
"comment-only-desc": "カードにのみコメント可能",
"no-comments": "コメントなし",
"no-comments-desc": "Can not see comments and activities.",
"worker": "Worker",
"worker-desc": "Can only move cards, assign itself to card and comment.",
"no-comments-desc": "コメントとアクティビティの閲覧不可。",
"worker": "作業者",
"worker-desc": "カードの移動、自分への割り当て、コメントが可能。",
"computer": "コンピューター",
"confirm-subtask-delete-dialog": "本当にサブタスクを削除してもよろしいでしょうか?",
"confirm-checklist-delete-dialog": "本当にチェックリストを削除してもよろしいでしょうか?",
"copy-card-link-to-clipboard": "カードへのリンクをクリップボードにコピー",
"linkCardPopup-title": "Link Card",
"linkCardPopup-title": "カードをリンク",
"searchElementPopup-title": "検索",
"copyCardPopup-title": "カードをコピー",
"copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
"copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
"copyChecklistToManyCardsPopup-title": "テンプレートから複数カードにコピー",
"copyChecklistToManyCardsPopup-instructions": "作成されるカードのタイトルと説明をJSON形式で指定",
"copyChecklistToManyCardsPopup-format": "[ {\"title\": \"1つ目のカードタイトル\", \"description\":\"1つ目のカードの説明\"}, {\"title\":\"2つ目のカードタイトル\",\"description\":\"2つ目のカードの説明\"},{\"title\":\"最後のカードタイトル\",\"description\":\"最後のカードの説明\"} ]",
"create": "作成",
"createBoardPopup-title": "ボードの作成",
@@ -242,15 +242,15 @@
"createCustomField": "フィールドを作成",
"createCustomFieldPopup-title": "フィールドを作成",
"current": "現在",
"custom-field-delete-pop": "There is no undo. This will remove this custom field from all cards and destroy its history.",
"custom-field-delete-pop": "この操作は取り消しできません。このカスタムフィールドはすべてのカードから外され履歴からも見えなくなります。",
"custom-field-checkbox": "チェックボックス",
"custom-field-date": "日付",
"custom-field-dropdown": "ドロップダウンリスト",
"custom-field-dropdown-none": "(none)",
"custom-field-dropdown-options": "List Options",
"custom-field-dropdown-options-placeholder": "Press enter to add more options",
"custom-field-dropdown-options": "リストの選択肢",
"custom-field-dropdown-options-placeholder": "Enterキーを押して選択肢を追加します",
"custom-field-dropdown-unknown": "(unknown)",
"custom-field-number": "Number",
"custom-field-number": "数値",
"custom-field-text": "テキスト",
"custom-fields": "カスタムフィールド",
"date": "日付",
@@ -268,12 +268,12 @@
"edit": "編集",
"edit-avatar": "アバターの変更",
"edit-profile": "プロフィールの編集",
"edit-wip-limit": "Edit WIP Limit",
"soft-wip-limit": "Soft WIP Limit",
"edit-wip-limit": "仕掛中制限編集",
"soft-wip-limit": "警告のみの仕掛中制限",
"editCardStartDatePopup-title": "開始日の変更",
"editCardDueDatePopup-title": "期限の変更",
"editCustomFieldPopup-title": "フィールドを編集",
"editCardSpentTimePopup-title": "Change spent time",
"editCardSpentTimePopup-title": "作業時間の変更",
"editLabelPopup-title": "ラベルの変更",
"editNotificationPopup-title": "通知の変更",
"editProfilePopup-title": "プロフィールの編集",
@@ -291,7 +291,7 @@
"email-sent": "メールを送信しました",
"email-verifyEmail-subject": "あなたの __siteName__ のメールアドレスを確認する",
"email-verifyEmail-text": "こんにちは、__user__さん。\n\nメールアドレスを認証するために、以下のリンクをクリックしてください。\n\n__url__\n\nよろしくお願いします。",
"enable-wip-limit": "Enable WIP Limit",
"enable-wip-limit": "仕掛中制限を有効化",
"error-board-doesNotExist": "ボードがありません",
"error-board-notAdmin": "操作にはボードの管理者権限が必要です",
"error-board-notAMember": "操作にはボードメンバーである必要があります",
@@ -304,29 +304,29 @@
"error-username-taken": "このユーザ名は既に使用されています",
"error-email-taken": "メールは既に受け取られています",
"export-board": "ボードのエクスポート",
"sort": "Sort",
"sort-desc": "Click to Sort List",
"list-sort-by": "Sort the List By:",
"list-label-modifiedAt": "Last Access Time",
"list-label-title": "Name of the List",
"list-label-sort": "Your Manual Order",
"sort": "並べ替え",
"sort-desc": "クリックでリストをソート",
"list-sort-by": "次によりリストを並べ替え:",
"list-label-modifiedAt": "最終アクセス日時",
"list-label-title": "リストの名前",
"list-label-sort": "手動による並べ替え",
"list-label-short-modifiedAt": "(L)",
"list-label-short-title": "(N)",
"list-label-short-sort": "(M)",
"filter": "フィルター",
"filter-cards": "Filter Cards or Lists",
"list-filter-label": "Filter List by Title",
"filter-cards": "カードまたはリストをフィルタ",
"list-filter-label": "タイトルでリストをフィルタ",
"filter-clear": "フィルターの解除",
"filter-no-label": "ラベルなし",
"filter-no-member": "メンバーなし",
"filter-no-custom-fields": "No Custom Fields",
"filter-show-archive": "Show archived lists",
"filter-hide-empty": "Hide empty lists",
"filter-no-custom-fields": "カスタムフィールドなし",
"filter-show-archive": "アーカイブされたリストを表示",
"filter-hide-empty": "空のリストを隠す",
"filter-on": "フィルター有効",
"filter-on-desc": "このボードのカードをフィルターしています。フィルターを編集するにはこちらをクリックしてください。",
"filter-to-selection": "フィルターした項目を全選択",
"advanced-filter-label": "高度なフィルター",
"advanced-filter-description": "Advanced Filter allows to write a string containing following operators: == != <= >= && || ( ) A space is used as a separator between the Operators. You can filter for all Custom Fields by typing their names and values. For Example: Field1 == Value1. Note: If fields or values contains spaces, you need to encapsulate them into single quotes. For Example: 'Field 1' == 'Value 1'. For single control characters (' \\/) to be skipped, you can use \\. For example: Field1 == I\\'m. Also you can combine multiple conditions. For Example: F1 == V1 || F1 == V2. Normally all operators are interpreted from left to right. You can change the order by placing brackets. For Example: F1 == V1 && ( F2 == V2 || F2 == V3 ). Also you can search text fields using regex: F1 == /Tes.*/i",
"advanced-filter-description": "高度なフィルタでは次のような演算子を使用できます:== != <= >= && || ( )\n半角スペースは演算子の区切り文字として使用します。\n\nフィールド名や値を使用したフィルタが可能です。\n例:Field1 == Value1\n\n注意:フィールド名や値にスペースが含まれる場合、それらをシングルクォーテーションで囲む必要があります。\n例:'Field 1' == 'Value 1'\n\n単体の制御文字 (' \\/) は無視されますので、\\を使用することができます。\n例:Field1 == I\\'m\n\n複数の条件を組み合わせることもできます。\n例:F1 == V1 || F1 == V2\n\n基本的にすべての演算子は左から右に評価されます。\n丸カッコを使用することで順序を変更できます。\n例:F1 == V1 && ( F2 == V2 || F2 == V3 )\n\nテキストフィールドでは正規表現を使用した検索もできます。\n例:F1 == /Tes.*/i",
"fullname": "フルネーム",
"header-logo-title": "自分のボードページに戻る。",
"hide-system-messages": "システムメッセージを隠す",
@@ -337,19 +337,19 @@
"import-board": "ボードをインポート",
"import-board-c": "ボードをインポート",
"import-board-title-trello": "Trelloからボードをインポート",
"import-board-title-wekan": "Import board from previous export",
"import-sandstorm-backup-warning": "Do not delete data you import from original exported board or Trello before checking does this grain close and open again, or do you get Board not found error, that means data loss.",
"import-board-title-wekan": "以前のエクスポートからボードをインポート",
"import-sandstorm-backup-warning": "この操作が完了して再度開くことができるのを確認するまでインポート元のボードまたはTrelloのデータを削除しないでください。「ボードが見つかりません」が表示された場合、データが失われたことを意味します。",
"import-sandstorm-warning": "ボードのインポートは、既存ボードのすべてのデータを置き換えます。",
"from-trello": "Trelloから",
"from-wekan": "From previous export",
"from-wekan": "以前のエクスポートから",
"import-board-instruction-trello": "Trelloボードの、 'Menu' → 'More' → 'Print and Export' → 'Export JSON'を選択し、テキストをコピーしてください。",
"import-board-instruction-wekan": "In your board, go to 'Menu', then 'Export board', and copy the text in the downloaded file.",
"import-board-instruction-about-errors": "If you get errors when importing board, sometimes importing still works, and board is at All Boards page.",
"import-board-instruction-wekan": "ボードの、 'メニュー' → 'ボードのエクスポート'を選択し、ダウンロードされたファイルの中のテキストをコピーしてください。",
"import-board-instruction-about-errors": "ボードのインポート中にエラーが発生した場合、インポートがまだ進行中のまま、全てのボードページに表示されている場合があります。",
"import-json-placeholder": "JSONデータをここに貼り付けする",
"import-map-members": "メンバーを紐付け",
"import-members-map": "Your imported board has some members. Please map the members you want to import to your users",
"import-members-map": "インポートしたボードにはいくつかのメンバーが含まれています。インポートしたいメンバーをユーザーにマッピングしてください",
"import-show-user-mapping": "メンバー紐付けの確認",
"import-user-select": "Pick your existing user you want to use as this member",
"import-user-select": "このメンバーとして使用したいユーザーを選択してください",
"importMapMembersAddPopup-title": "メンバーを選択",
"info": "バージョン",
"initials": "イニシャル",
@@ -366,22 +366,22 @@
"language": "言語",
"last-admin-desc": "最低でも1人以上の管理者が必要なためロールを変更できません。",
"leave-board": "ボードから退出する",
"leave-board-pop": "Are you sure you want to leave __boardTitle__? You will be removed from all cards on this board.",
"leave-board-pop": "本当に __boardTitle__ から退出しますか?このボードのすべてのカードからあなたは削除されます。",
"leaveBoardPopup-title": "ボードから退出しますか?",
"link-card": "このカードへのリンク",
"list-archive-cards": "リスト内の全カードをアーカイブする",
"list-archive-cards-pop": "This will remove all the cards in this list from the board. To view cards in Archive and bring them back to the board, click “Menu” > “Archive”.",
"list-archive-cards-pop": "このボード上のリストから削除されたカードは メニュー > アーカイブ をクリックすると見ることができ、元のボードに戻すことができます。",
"list-move-cards": "リストの全カードを移動する",
"list-select-cards": "リストの全カードを選択",
"set-color-list": "色を選択",
"listActionPopup-title": "操作一覧",
"swimlaneActionPopup-title": "スイムレーン操作",
"swimlaneAddPopup-title": "Add a Swimlane below",
"swimlaneAddPopup-title": "直下にスイムレーンを追加",
"listImportCardPopup-title": "Trelloのカードをインポート",
"listMorePopup-title": "さらに見る",
"link-list": "このリストへのリンク",
"list-delete-pop": "すべての内容がアクティビティから削除されます。この削除は元に戻すことができません。",
"list-delete-suggest-archive": "You can move a list to Archive to remove it from the board and preserve the activity.",
"list-delete-suggest-archive": "リストをアーカイブするとボードから削除され、アクティビティに保持されます。",
"lists": "リスト",
"swimlanes": "スイムレーン",
"log-out": "ログアウト",
@@ -401,9 +401,9 @@
"muted-info": "このボードの変更は通知されません",
"my-boards": "自分のボード",
"name": "名前",
"no-archived-cards": "カードをアーカイブする",
"no-archived-lists": "リストをアーカイブする",
"no-archived-swimlanes": "No swimlanes in Archive.",
"no-archived-cards": "アーカイブ済みカードはありません。",
"no-archived-lists": "アーカイブ済みリストはありません。",
"no-archived-swimlanes": "アーカイブ済みスイムレーンはありません。",
"no-results": "該当するものはありません",
"normal": "通常",
"normal-desc": "カードの閲覧と編集が可能。設定変更不可。",
@@ -436,22 +436,22 @@
"removeMemberPopup-title": "メンバーを外しますか?",
"rename": "名前変更",
"rename-board": "ボード名の変更",
"restore": "復元",
"restore": "リストア",
"save": "保存",
"search": "検索",
"rules": "Rules",
"search-cards": "Search from card/list titles and descriptions on this board",
"rules": "ルール",
"search-cards": "ボードのカード/リストのタイトルと詳細から検索",
"search-example": "検索文字",
"select-color": "色を選択",
"set-wip-limit-value": "Set a limit for the maximum number of tasks in this list",
"setWipLimitPopup-title": "Set WIP Limit",
"set-wip-limit-value": "このリスト中のタスクの最大数を設定",
"setWipLimitPopup-title": "仕掛中制限設定",
"shortcut-assign-self": "自分をこのカードに割り当てる",
"shortcut-autocomplete-emoji": "絵文字の補完",
"shortcut-autocomplete-members": "メンバーの補完",
"shortcut-clear-filters": "すべてのフィルターを解除する",
"shortcut-close-dialog": "ダイアログを閉じる",
"shortcut-filter-my-cards": "カードをフィルター",
"shortcut-show-shortcuts": "Bring up this shortcuts list",
"shortcut-show-shortcuts": "このショートカットリストを表示する",
"shortcut-toggle-filterbar": "フィルターサイドバーの切り替え",
"shortcut-toggle-sidebar": "ボードサイドバーの切り替え",
"show-cards-minimum-count": "以下より多い場合、リストにカード数を表示",
@@ -465,16 +465,16 @@
"team": "チーム",
"this-board": "このボード",
"this-card": "このカード",
"spent-time-hours": "Spent time (hours)",
"overtime-hours": "Overtime (hours)",
"overtime": "Overtime",
"has-overtime-cards": "Has overtime cards",
"has-spenttime-cards": "Has spent time cards",
"spent-time-hours": "作業時間(時)",
"overtime-hours": "超過時間(時)",
"overtime": "超過時間",
"has-overtime-cards": "超過時間ありのカード",
"has-spenttime-cards": "作業時間ありのカード",
"time": "時間",
"title": "タイトル",
"tracking": "トラッキング",
"tracking-info": "これらのカードへの変更が通知されるようになります。",
"type": "Type",
"type": "タイプ",
"unassign-member": "未登録のメンバー",
"unsaved-description": "未保存の変更があります。",
"unwatch": "アンウォッチ",
@@ -483,21 +483,21 @@
"uploaded-avatar": "アップロードされたアバター",
"username": "ユーザー名",
"view-it": "見る",
"warn-list-archived": "warning: this card is in an list at Archive",
"warn-list-archived": "警告:このカードはアーカイブされたリスト内にあります",
"watch": "ウォッチ",
"watching": "ウォッチしています",
"watching-info": "このボードの変更が通知されます",
"welcome-board": "ウェルカムボード",
"welcome-swimlane": "Milestone 1",
"welcome-swimlane": "マイルストーン 1",
"welcome-list1": "基本",
"welcome-list2": "高度",
"card-templates-swimlane": "カードのテンプレート",
"list-templates-swimlane": "リストのテンプレート",
"board-templates-swimlane": "ボードのテンプレート",
"what-to-do": "何をしたいですか?",
"wipLimitErrorPopup-title": "Invalid WIP Limit",
"wipLimitErrorPopup-dialog-pt1": "The number of tasks in this list is higher than the WIP limit you've defined.",
"wipLimitErrorPopup-dialog-pt2": "Please move some tasks out of this list, or set a higher WIP limit.",
"wipLimitErrorPopup-title": "無効な仕掛中制限",
"wipLimitErrorPopup-dialog-pt1": "入力した仕掛中制限よりこのリスト中のタスクの数が大きいです。",
"wipLimitErrorPopup-dialog-pt2": "いくつかのタスクをこのリストから移動するか、仕掛中制限の最大値を変更してください。",
"admin-panel": "管理パネル",
"settings": "設定",
"people": "メンバー",
@@ -516,29 +516,29 @@
"smtp-password": "パスワード",
"smtp-tls": "TLSサポート",
"send-from": "送信元",
"send-smtp-test": "Send a test email to yourself",
"send-smtp-test": "テストメールを自分に送信",
"invitation-code": "招待コード",
"email-invite-register-subject": "__inviter__さんがあなたを招待しています",
"email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to kanban board for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
"email-smtp-test-subject": "SMTP Test Email",
"email-smtp-test-text": "You have successfully sent an email",
"email-invite-register-text": "__user__ 様\n\n__inviter__ によりカンバンへ招待されています。\n\n以下のリンクへ移動し、招待コード __icode__ を入力してください。\n__url__",
"email-smtp-test-subject": "SMTP経由でテストメールを送信",
"email-smtp-test-text": "メールを送信しました",
"error-invitation-code-not-exist": "招待コードが存在しません",
"error-notAuthorized": "このページを参照する権限がありません。",
"webhook-title": "Webhook Name",
"webhook-token": "Token (Optional for Authentication)",
"webhook-title": "Webフック名",
"webhook-token": "トークン(認証用オプション)",
"outgoing-webhooks": "発信Webフック",
"bidirectional-webhooks": "Two-Way Webhooks",
"bidirectional-webhooks": "双方向Webフック",
"outgoingWebhooksPopup-title": "発信Webフック",
"boardCardTitlePopup-title": "Card Title Filter",
"disable-webhook": "Disable This Webhook",
"global-webhook": "Global Webhooks",
"boardCardTitlePopup-title": "カード名フィルタ",
"disable-webhook": "このWebフックを無効化",
"global-webhook": "グローバルWebフック",
"new-outgoing-webhook": "発信Webフックの作成",
"no-name": "(Unknown)",
"no-name": "(名前なし)",
"Node_version": "Nodeバージョン",
"Meteor_version": "Meteor version",
"MongoDB_version": "MongoDB version",
"MongoDB_storage_engine": "MongoDB storage engine",
"MongoDB_Oplog_enabled": "MongoDB Oplog enabled",
"Meteor_version": "Meteor バージョン",
"MongoDB_version": "MongoDB バージョン",
"MongoDB_storage_engine": "MongoDB ストレージエンジン",
"MongoDB_Oplog_enabled": "MongoDB Oplog 有効状態",
"OS_Arch": "OSアーキテクチャ",
"OS_Cpus": "OS CPU数",
"OS_Freemem": "OSフリーメモリ",
@@ -548,210 +548,216 @@
"OS_Totalmem": "OSトータルメモリ",
"OS_Type": "OS種類",
"OS_Uptime": "OSアップタイム",
"days": "days",
"hours": "時",
"days": "日",
"hours": "時間",
"minutes": "分",
"seconds": "秒",
"show-field-on-card": "Show this field on card",
"automatically-field-on-card": "Auto create field to all cards",
"showLabel-field-on-card": "Show field label on minicard",
"show-field-on-card": "このフィールドをカードに表示",
"automatically-field-on-card": "全カードにフィールドを自動作成",
"showLabel-field-on-card": "ミニカード上のフィールドラベル表示",
"yes": "はい",
"no": "いいえ",
"accounts": "アカウント",
"accounts-allowEmailChange": "メールアドレスの変更を許可",
"accounts-allowUserNameChange": "Allow Username Change",
"createdAt": "Created at",
"verified": "Verified",
"active": "Active",
"accounts-allowUserNameChange": "ユーザー名の変更を許可",
"createdAt": "作成日時",
"verified": "認証状況",
"active": "有効状態",
"card-received": "受付",
"card-received-on": "受付日",
"card-end": "終了",
"card-end-on": "終了日",
"editCardReceivedDatePopup-title": "受付日の変更",
"editCardEndDatePopup-title": "終了日の変更",
"setCardColorPopup-title": "Set color",
"setCardActionsColorPopup-title": "Choose a color",
"setSwimlaneColorPopup-title": "Choose a color",
"setListColorPopup-title": "Choose a color",
"assigned-by": "Assigned By",
"requested-by": "Requested By",
"board-delete-notice": "Deleting is permanent. You will lose all lists, cards and actions associated with this board.",
"setCardColorPopup-title": "色を選択",
"setCardActionsColorPopup-title": "色を選択",
"setSwimlaneColorPopup-title": "色を選択",
"setListColorPopup-title": "色を選択",
"assigned-by": "任命者",
"requested-by": "依頼者",
"board-delete-notice": "永久に削除されます。このボードに関連するリスト、カード、アクションはすべて失われます。",
"delete-board-confirm-popup": "すべてのリスト、カード、ラベル、アクティビティは削除され、ボードの内容を元に戻すことができません。",
"boardDeletePopup-title": "ボードを削除しますか?",
"delete-board": "ボードを削除",
"default-subtasks-board": "Subtasks for __board__ board",
"default": "Default",
"queue": "Queue",
"subtask-settings": "Subtasks Settings",
"boardSubtaskSettingsPopup-title": "Board Subtasks Settings",
"show-subtasks-field": "Cards can have subtasks",
"deposit-subtasks-board": "Deposit subtasks to this board:",
"deposit-subtasks-list": "Landing list for subtasks deposited here:",
"show-parent-in-minicard": "Show parent in minicard:",
"prefix-with-full-path": "Prefix with full path",
"prefix-with-parent": "Prefix with parent",
"subtext-with-full-path": "Subtext with full path",
"subtext-with-parent": "Subtext with parent",
"change-card-parent": "Change card's parent",
"parent-card": "Parent card",
"source-board": "Source board",
"no-parent": "Don't show parent",
"activity-added-label": "added label '%s' to %s",
"activity-removed-label": "removed label '%s' from %s",
"activity-delete-attach": "deleted an attachment from %s",
"activity-added-label-card": "added label '%s'",
"activity-removed-label-card": "removed label '%s'",
"activity-delete-attach-card": "deleted an attachment",
"activity-set-customfield": "set custom field '%s' to '%s' in %s",
"activity-unset-customfield": "unset custom field '%s' in %s",
"r-rule": "Rule",
"r-add-trigger": "Add trigger",
"r-add-action": "Add action",
"r-board-rules": "Board rules",
"r-add-rule": "Add rule",
"r-view-rule": "View rule",
"r-delete-rule": "Delete rule",
"r-new-rule-name": "New rule title",
"r-no-rules": "No rules",
"r-when-a-card": "When a card",
"r-is": "is",
"r-is-moved": "is moved",
"r-added-to": "added to",
"r-removed-from": "Removed from",
"r-the-board": "the board",
"r-list": "list",
"set-filter": "Set Filter",
"r-moved-to": "Moved to",
"r-moved-from": "Moved from",
"r-archived": "Moved to Archive",
"r-unarchived": "Restored from Archive",
"r-a-card": "a card",
"r-when-a-label-is": "When a label is",
"r-when-the-label": "When the label",
"r-list-name": "list name",
"r-when-a-member": "When a member is",
"r-when-the-member": "When the member",
"r-name": "name",
"r-when-a-attach": "When an attachment",
"r-when-a-checklist": "When a checklist is",
"r-when-the-checklist": "When the checklist",
"r-completed": "Completed",
"r-made-incomplete": "Made incomplete",
"r-when-a-item": "When a checklist item is",
"r-when-the-item": "When the checklist item",
"r-checked": "Checked",
"r-unchecked": "Unchecked",
"r-move-card-to": "Move card to",
"r-top-of": "Top of",
"r-bottom-of": "Bottom of",
"r-its-list": "its list",
"r-archive": "Move to Archive",
"r-unarchive": "Restore from Archive",
"r-card": "card",
"default-subtasks-board": "__board__ ボードのサブタスク",
"default": "デフォルト",
"queue": "キュー",
"subtask-settings": "サブタスク設定",
"card-settings": "カード設定",
"boardSubtaskSettingsPopup-title": "ボードのサブタスク設定",
"boardCardSettingsPopup-title": "カード設定",
"deposit-subtasks-board": "サブタスクの作成先ボード:",
"deposit-subtasks-list": "サブタスクの作成先リスト:",
"show-parent-in-minicard": "ミニカード上の親表示",
"prefix-with-full-path": "タイトルの前に親をフル表示",
"prefix-with-parent": "タイトルの前に親を表示",
"subtext-with-full-path": "タイトルの後ろに親をフル表示",
"subtext-with-parent": "タイトルの後ろに親を表示",
"change-card-parent": "親カードを変更",
"parent-card": "親カード",
"source-board": "元ボード",
"no-parent": "親を表示しない",
"activity-added-label": "ラベル '%s' を %s に追加しました",
"activity-removed-label": "ラベル '%s' を %s から削除しました",
"activity-delete-attach": "添付ファイルを %s から削除しました",
"activity-added-label-card": "ラベル '%s' を追加しました",
"activity-removed-label-card": "ラベル '%s' を削除しました",
"activity-delete-attach-card": "添付ファイルを削除しました",
"activity-set-customfield": "カスタムフィールド '%s' を %s / %s に設定",
"activity-unset-customfield": "カスタムフィールド '%s' を %s から削除",
"r-rule": "ルール",
"r-add-trigger": "トリガーの追加",
"r-add-action": "アクションの追加",
"r-board-rules": "ボードのルール",
"r-add-rule": "ルールを追加",
"r-view-rule": "ルールを表示",
"r-delete-rule": "ルールを削除",
"r-new-rule-name": "新しいルールのタイトル",
"r-no-rules": "ルールなし",
"r-when-a-card": "カード:",
"r-is": "が",
"r-is-moved": "が移動された時",
"r-added-to": "次に追加された時",
"r-removed-from": "次から削除された時",
"r-the-board": "ボード:",
"r-list": "リスト:",
"set-filter": "フィルタを設定",
"r-moved-to": "次に移動した時",
"r-moved-from": "次から移動された時",
"r-archived": "アーカイブされた時",
"r-unarchived": "アーカイブから復元された時",
"r-a-card": "カード",
"r-when-a-label-is": "ラベルが",
"r-when-the-label": "ラベル:",
"r-list-name": "リスト名",
"r-when-a-member": "メンバーが",
"r-when-the-member": "メンバー:",
"r-name": "名前",
"r-when-a-attach": "添付ファイルが",
"r-when-a-checklist": "チェックリストが",
"r-when-the-checklist": "チェックリスト:",
"r-completed": "完了した時",
"r-made-incomplete": "未完了になった時",
"r-when-a-item": "チェックリスト項目が",
"r-when-the-item": "チェックリスト項目:",
"r-checked": "チェックされた時",
"r-unchecked": "チェックが外された時",
"r-move-card-to": "カードを次に移動",
"r-top-of": "先頭",
"r-bottom-of": "最下部",
"r-its-list": "所属リスト内の",
"r-archive": "アーカイブする",
"r-unarchive": "アーカイブから復元する",
"r-card": "カードを",
"r-add": "追加",
"r-remove": "Remove",
"r-label": "label",
"r-member": "member",
"r-remove-all": "Remove all members from the card",
"r-set-color": "Set color to",
"r-checklist": "checklist",
"r-check-all": "Check all",
"r-uncheck-all": "Uncheck all",
"r-items-check": "items of checklist",
"r-check": "Check",
"r-uncheck": "Uncheck",
"r-item": "item",
"r-of-checklist": "of checklist",
"r-send-email": "Send an email",
"r-to": "to",
"r-subject": "subject",
"r-rule-details": "Rule details",
"r-d-move-to-top-gen": "Move card to top of its list",
"r-d-move-to-top-spec": "Move card to top of list",
"r-d-move-to-bottom-gen": "Move card to bottom of its list",
"r-d-move-to-bottom-spec": "Move card to bottom of list",
"r-d-send-email": "Send email",
"r-d-send-email-to": "to",
"r-d-send-email-subject": "subject",
"r-d-send-email-message": "message",
"r-d-archive": "Move card to Archive",
"r-d-unarchive": "Restore card from Archive",
"r-d-add-label": "Add label",
"r-d-remove-label": "Remove label",
"r-create-card": "Create new card",
"r-remove": "削除",
"r-label": "ラベル",
"r-member": "メンバー",
"r-remove-all": "カードのすべてのメンバーを削除",
"r-set-color": "色を設定",
"r-checklist": "チェックリスト",
"r-check-all": "すべてチェック",
"r-uncheck-all": "すべてチェックを外す",
"r-items-check": "チェックリストの項目",
"r-check": "チェック",
"r-uncheck": "チェックを外す",
"r-item": "項目",
"r-of-checklist": "チェックリスト",
"r-send-email": "メールを送る",
"r-to": "宛先",
"r-subject": "件名",
"r-rule-details": "ルール詳細",
"r-d-move-to-top-gen": "カードを自身のリストの先頭に移動",
"r-d-move-to-top-spec": "カードをリストの先頭に移動",
"r-d-move-to-bottom-gen": "カードを自身のリストの最下部に移動",
"r-d-move-to-bottom-spec": "カードをリストの最下部に移動",
"r-d-send-email": "メールを送信",
"r-d-send-email-to": "宛先",
"r-d-send-email-subject": "件名",
"r-d-send-email-message": "メッセージ本文",
"r-d-archive": "カードをアーカイブする",
"r-d-unarchive": "アーカイブからカードを復元する",
"r-d-add-label": "ラベルを追加",
"r-d-remove-label": "ラベルを削除",
"r-create-card": "カードを新規作成",
"r-in-list": "リスト:",
"r-in-swimlane": "in swimlane",
"r-d-add-member": "Add member",
"r-d-remove-member": "Remove member",
"r-d-remove-all-member": "Remove all member",
"r-d-check-all": "Check all items of a list",
"r-d-uncheck-all": "Uncheck all items of a list",
"r-d-check-one": "Check item",
"r-d-uncheck-one": "Uncheck item",
"r-d-check-of-list": "of checklist",
"r-in-swimlane": "スイムレーン:",
"r-d-add-member": "メンバーを追加",
"r-d-remove-member": "メンバーを削除",
"r-d-remove-all-member": "すべてのメンバーを削除",
"r-d-check-all": "リストのアイテムすべてをチェックする",
"r-d-uncheck-all": "リストのアイテムのすべてのチェックを外す",
"r-d-check-one": "アイテムをチェックする",
"r-d-uncheck-one": "アイテムのチェックを外す",
"r-d-check-of-list": "チェックリスト",
"r-d-add-checklist": "チェックリストを追加",
"r-d-remove-checklist": "チェックリストを削除",
"r-by": "by",
"r-by": "ユーザー:",
"r-add-checklist": "チェックリストを追加",
"r-with-items": "with items",
"r-with-items": "チェックリスト項目",
"r-items-list": "アイテム1、アイテム2、アイテム3",
"r-add-swimlane": "スイムレーンを追加",
"r-swimlane-name": "スイムレーン名",
"r-board-note": "Note: leave a field empty to match every possible value.",
"r-checklist-note": "Note: checklist's items have to be written as comma separated values.",
"r-board-note": "注意:フィールドを空にすると全ての値に一致します",
"r-checklist-note": "注意:チェックリスト項目はカンマ区切りで記述する必要があります。",
"r-when-a-card-is-moved": "カードが別のリストに移動したとき",
"r-set": "Set",
"r-update": "Update",
"r-datefield": "date field",
"r-df-start-at": "start",
"r-df-due-at": "due",
"r-df-end-at": "end",
"r-df-received-at": "received",
"r-to-current-datetime": "to current date/time",
"r-remove-value-from": "Remove value from",
"r-set": "設定する",
"r-update": "更新する",
"r-datefield": "日時項目",
"r-df-start-at": "開始",
"r-df-due-at": "期限",
"r-df-end-at": "終了",
"r-df-received-at": "受付",
"r-to-current-datetime": "現在日時に",
"r-remove-value-from": "次の値を削除:",
"ldap": "LDAP",
"oauth2": "OAuth2",
"cas": "CAS",
"authentication-method": "認証方式",
"authentication-type": "認証タイプ",
"custom-product-name": "Custom Product Name",
"custom-product-name": "カスタム製品名",
"layout": "レイアウト",
"hide-logo": "ロゴを隠す",
"add-custom-html-after-body-start": "Add Custom HTML after <body> start",
"add-custom-html-before-body-end": "Add Custom HTML before </body> end",
"error-undefined": "Something went wrong",
"error-ldap-login": "An error occurred while trying to login",
"display-authentication-method": "Display Authentication Method",
"default-authentication-method": "Default Authentication Method",
"add-custom-html-after-body-start": "<body> の開始直後にカスタムHTMLを追加する",
"add-custom-html-before-body-end": "</body> の終了直前にカスタムHTMLを追加する",
"error-undefined": "何らかの問題が発生しています",
"error-ldap-login": "ログイン中にエラーが発生しました",
"display-authentication-method": "認証方式を表示",
"default-authentication-method": "デフォルトの認証方式",
"duplicate-board": "ボードの複製",
"people-number": "The number of people is:",
"people-number": "メンバー数:",
"swimlaneDeletePopup-title": "スイムレーンを削除しますか?",
"swimlane-delete-pop": "All actions will be removed from the activity feed and you won't be able to recover the swimlane. There is no undo.",
"swimlane-delete-pop": "すべての内容がアクティビティから削除されます。この削除は元に戻すことができません。",
"restore-all": "全てをリストアする",
"delete-all": "全てを削除する",
"loading": "ローディング中です、しばらくお待ちください。",
"previous_as": "last time was",
"act-a-dueAt": "modified due time to \nWhen: __timeValue__\nWhere: __card__\n previous due was __timeOldValue__",
"act-a-endAt": "modified ending time to __timeValue__ from (__timeOldValue__)",
"act-a-startAt": "modified starting time to __timeValue__ from (__timeOldValue__)",
"act-a-receivedAt": "modified received time to __timeValue__ from (__timeOldValue__)",
"a-dueAt": "modified due time to be",
"a-endAt": "modified ending time to be",
"a-startAt": "modified starting time to be",
"a-receivedAt": "modified received time to be",
"almostdue": "current due time %s is approaching",
"pastdue": "current due time %s is past",
"duenow": "current due time %s is today",
"act-newDue": "__list__/__card__ has 1st due reminder [__board__]",
"act-withDue": "__list__/__card__ due reminders [__board__]",
"act-almostdue": "was reminding the current due (__timeValue__) of __card__ is approaching",
"act-pastdue": "was reminding the current due (__timeValue__) of __card__ is past",
"act-duenow": "was reminding the current due (__timeValue__) of __card__ is now",
"act-atUserComment": "You were mentioned in [__board__] __list__/__card__",
"delete-user-confirm-popup": "Are you sure you want to delete this account? There is no undo.",
"accounts-allowUserDelete": "Allow users to self delete their account",
"hide-minicard-label-text": "Hide minicard label text",
"show-desktop-drag-handles": "Show desktop drag handles",
"assignee": "Assignee",
"cardAssigneesPopup-title": "Assignee"
"previous_as": "最終日時",
"act-a-dueAt": "期限の時刻が変更されました\n変更後:__timeValue__\n場所:__card__\n変更前の期限の時刻は __timeOldValue__ でした",
"act-a-endAt": "終了を (__timeOldValue__)から __timeValue__ に変更しました",
"act-a-startAt": "開始を (__timeOldValue__)から __timeValue__ に変更しました",
"act-a-receivedAt": "期限を (__timeOldValue__)から __timeValue__ に変更しました",
"a-dueAt": "期限を変更しました",
"a-endAt": "終了を変更しました",
"a-startAt": "開始を変更しました",
"a-receivedAt": "受付を変更しました",
"almostdue": "期限 %s が近づいています",
"pastdue": "期限 %s が過ぎています",
"duenow": "期限 %s は本日です",
"act-newDue": "__list __ / __ card__に1回目の有効期限アラートがあります [__board__]",
"act-withDue": "__list __ / __ card__ の有効期限のアラート [__board__]",
"act-almostdue": "__card__ の期限日時 (__timeValue__) が近づいています",
"act-pastdue": "__card__ の期限日時 (__timeValue__) が過ぎています",
"act-duenow": "__card__ の期限日時 (__timeValue__) になりました",
"act-atUserComment": "あなたが [__board__] __list__/__card__ に追記しました",
"delete-user-confirm-popup": "本当にこのアカウントを削除しますか?この操作は取り消しできません。",
"accounts-allowUserDelete": "ユーザー自身のアカウント削除を許可",
"hide-minicard-label-text": "ミニカードのラベル名を隠す",
"show-desktop-drag-handles": "デスクトップへのドラッグハンドルを表示",
"assignee": "担当者",
"cardAssigneesPopup-title": "担当者",
"addmore-detail": "詳細説明の追加",
"show-on-card": "カードに表示する項目",
"new": "新規作成",
"editUserPopup-title": "ユーザーを編集",
"newUserPopup-title": "新規ユーザー"
}
+8 -2
View File
@@ -583,8 +583,9 @@
"default": "Default",
"queue": "რიგი",
"subtask-settings": "ქვესაქმიანობების პარამეტრები",
"card-settings": "Card Settings",
"boardSubtaskSettingsPopup-title": "Board Subtasks Settings",
"show-subtasks-field": "ბარათებს შესაძლოა ჰქონდეს ქვესაქმიანობები",
"boardCardSettingsPopup-title": "Card Settings",
"deposit-subtasks-board": "Deposit subtasks to this board:",
"deposit-subtasks-list": "Landing list for subtasks deposited here:",
"show-parent-in-minicard": "Show parent in minicard:",
@@ -753,5 +754,10 @@
"hide-minicard-label-text": "Hide minicard label text",
"show-desktop-drag-handles": "Show desktop drag handles",
"assignee": "Assignee",
"cardAssigneesPopup-title": "Assignee"
"cardAssigneesPopup-title": "Assignee",
"addmore-detail": "Add a more detailed description",
"show-on-card": "Show on Card",
"new": "New",
"editUserPopup-title": "Edit User",
"newUserPopup-title": "New User"
}
+8 -2
View File
@@ -583,8 +583,9 @@
"default": "Default",
"queue": "Queue",
"subtask-settings": "Subtasks Settings",
"card-settings": "Card Settings",
"boardSubtaskSettingsPopup-title": "Board Subtasks Settings",
"show-subtasks-field": "Cards can have subtasks",
"boardCardSettingsPopup-title": "Card Settings",
"deposit-subtasks-board": "Deposit subtasks to this board:",
"deposit-subtasks-list": "Landing list for subtasks deposited here:",
"show-parent-in-minicard": "Show parent in minicard:",
@@ -753,5 +754,10 @@
"hide-minicard-label-text": "Hide minicard label text",
"show-desktop-drag-handles": "Show desktop drag handles",
"assignee": "Assignee",
"cardAssigneesPopup-title": "Assignee"
"cardAssigneesPopup-title": "Assignee",
"addmore-detail": "Add a more detailed description",
"show-on-card": "Show on Card",
"new": "New",
"editUserPopup-title": "Edit User",
"newUserPopup-title": "New User"
}
+8 -2
View File
@@ -583,8 +583,9 @@
"default": "Default",
"queue": "Queue",
"subtask-settings": "Subtasks Settings",
"card-settings": "Card Settings",
"boardSubtaskSettingsPopup-title": "Board Subtasks Settings",
"show-subtasks-field": "Cards can have subtasks",
"boardCardSettingsPopup-title": "Card Settings",
"deposit-subtasks-board": "Deposit subtasks to this board:",
"deposit-subtasks-list": "Landing list for subtasks deposited here:",
"show-parent-in-minicard": "Show parent in minicard:",
@@ -753,5 +754,10 @@
"hide-minicard-label-text": "Hide minicard label text",
"show-desktop-drag-handles": "Show desktop drag handles",
"assignee": "Assignee",
"cardAssigneesPopup-title": "Assignee"
"cardAssigneesPopup-title": "Assignee",
"addmore-detail": "Add a more detailed description",
"show-on-card": "Show on Card",
"new": "New",
"editUserPopup-title": "Edit User",
"newUserPopup-title": "New User"
}
+8 -2
View File
@@ -583,8 +583,9 @@
"default": "Default",
"queue": "Queue",
"subtask-settings": "Subtasks Settings",
"card-settings": "Card Settings",
"boardSubtaskSettingsPopup-title": "Board Subtasks Settings",
"show-subtasks-field": "Cards can have subtasks",
"boardCardSettingsPopup-title": "Card Settings",
"deposit-subtasks-board": "Deposit subtasks to this board:",
"deposit-subtasks-list": "Landing list for subtasks deposited here:",
"show-parent-in-minicard": "Show parent in minicard:",
@@ -753,5 +754,10 @@
"hide-minicard-label-text": "Hide minicard label text",
"show-desktop-drag-handles": "Show desktop drag handles",
"assignee": "Assignee",
"cardAssigneesPopup-title": "Assignee"
"cardAssigneesPopup-title": "Assignee",
"addmore-detail": "Add a more detailed description",
"show-on-card": "Show on Card",
"new": "New",
"editUserPopup-title": "Edit User",
"newUserPopup-title": "New User"
}
+8 -2
View File
@@ -583,8 +583,9 @@
"default": "по подразбиране",
"queue": "Опашка",
"subtask-settings": "Настройки на Подзадачите",
"card-settings": "Card Settings",
"boardSubtaskSettingsPopup-title": "Настройки за Подзадачите за това Табло",
"show-subtasks-field": "Картата може да има подзадачи",
"boardCardSettingsPopup-title": "Card Settings",
"deposit-subtasks-board": "Deposit subtasks to this board:",
"deposit-subtasks-list": "Landing list for subtasks deposited here:",
"show-parent-in-minicard": "Show parent in minicard:",
@@ -753,5 +754,10 @@
"hide-minicard-label-text": "Hide minicard label text",
"show-desktop-drag-handles": "Show desktop drag handles",
"assignee": "Assignee",
"cardAssigneesPopup-title": "Assignee"
"cardAssigneesPopup-title": "Assignee",
"addmore-detail": "Add a more detailed description",
"show-on-card": "Show on Card",
"new": "New",
"editUserPopup-title": "Edit User",
"newUserPopup-title": "New User"
}
+8 -2
View File
@@ -583,8 +583,9 @@
"default": "Default",
"queue": "Queue",
"subtask-settings": "Subtasks Settings",
"card-settings": "Card Settings",
"boardSubtaskSettingsPopup-title": "Board Subtasks Settings",
"show-subtasks-field": "Cards can have subtasks",
"boardCardSettingsPopup-title": "Card Settings",
"deposit-subtasks-board": "Deposit subtasks to this board:",
"deposit-subtasks-list": "Landing list for subtasks deposited here:",
"show-parent-in-minicard": "Show parent in minicard:",
@@ -753,5 +754,10 @@
"hide-minicard-label-text": "Hide minicard label text",
"show-desktop-drag-handles": "Show desktop drag handles",
"assignee": "Assignee",
"cardAssigneesPopup-title": "Assignee"
"cardAssigneesPopup-title": "Assignee",
"addmore-detail": "Add a more detailed description",
"show-on-card": "Show on Card",
"new": "New",
"editUserPopup-title": "Edit User",
"newUserPopup-title": "New User"
}
+8 -2
View File
@@ -583,8 +583,9 @@
"default": "Default",
"queue": "Queue",
"subtask-settings": "Subtasks Settings",
"card-settings": "Card Settings",
"boardSubtaskSettingsPopup-title": "Board Subtasks Settings",
"show-subtasks-field": "Cards can have subtasks",
"boardCardSettingsPopup-title": "Card Settings",
"deposit-subtasks-board": "Deposit subtasks to this board:",
"deposit-subtasks-list": "Landing list for subtasks deposited here:",
"show-parent-in-minicard": "Show parent in minicard:",
@@ -753,5 +754,10 @@
"hide-minicard-label-text": "Hide minicard label text",
"show-desktop-drag-handles": "Show desktop drag handles",
"assignee": "Assignee",
"cardAssigneesPopup-title": "Assignee"
"cardAssigneesPopup-title": "Assignee",
"addmore-detail": "Add a more detailed description",
"show-on-card": "Show on Card",
"new": "New",
"editUserPopup-title": "Edit User",
"newUserPopup-title": "New User"
}
+8 -2
View File
@@ -583,8 +583,9 @@
"default": "Standaard",
"queue": "Rij",
"subtask-settings": "Subtaak Instellingen",
"card-settings": "Kaart Instellingen",
"boardSubtaskSettingsPopup-title": "Bord Subtaak Instellingen",
"show-subtasks-field": "Kaarten kunnen subtaken hebben",
"boardCardSettingsPopup-title": "Kaart Instellingen",
"deposit-subtasks-board": "Plaats subtaken op dit bord:",
"deposit-subtasks-list": "Plaats subtaken in deze lijst:",
"show-parent-in-minicard": "Toon bron in minikaart:",
@@ -753,5 +754,10 @@
"hide-minicard-label-text": "Verberg minikaart labeltekst",
"show-desktop-drag-handles": "Toon sleep gereedschap op werkblad",
"assignee": "Toegewezen aan",
"cardAssigneesPopup-title": "Toegewezen aan"
"cardAssigneesPopup-title": "Toegewezen aan",
"addmore-detail": "Voeg een meer gedetailleerde beschrijving toe",
"show-on-card": "Toon op kaart",
"new": "Nieuw",
"editUserPopup-title": "Wijzig gebruiker",
"newUserPopup-title": "Nieuwe gebruiker"
}
+8 -2
View File
@@ -583,8 +583,9 @@
"default": "Default",
"queue": "Queue",
"subtask-settings": "Subtasks Settings",
"card-settings": "Card Settings",
"boardSubtaskSettingsPopup-title": "Board Subtasks Settings",
"show-subtasks-field": "Cards can have subtasks",
"boardCardSettingsPopup-title": "Card Settings",
"deposit-subtasks-board": "Deposit subtasks to this board:",
"deposit-subtasks-list": "Landing list for subtasks deposited here:",
"show-parent-in-minicard": "Show parent in minicard:",
@@ -753,5 +754,10 @@
"hide-minicard-label-text": "Hide minicard label text",
"show-desktop-drag-handles": "Show desktop drag handles",
"assignee": "Assignee",
"cardAssigneesPopup-title": "Assignee"
"cardAssigneesPopup-title": "Assignee",
"addmore-detail": "Add a more detailed description",
"show-on-card": "Show on Card",
"new": "New",
"editUserPopup-title": "Edit User",
"newUserPopup-title": "New User"
}
+31 -25
View File
@@ -2,44 +2,44 @@
"accept": "Akceptuj",
"act-activity-notify": "Powiadomienia aktywności",
"act-addAttachment": "dodał(a) załącznik __attachment__ do karty __card__ na liście __list__ w diagramie czynności __swimlane__ na tablicy __board__",
"act-deleteAttachment": "usunął/usunęła załącznik __attachment__ na karcie __card__ na liście __list__ w diagramie czynności __swimlane__ na tablicy __board__",
"act-deleteAttachment": "usunął załącznik __attachment__ na karcie __card__ na liście __list__ w diagramie czynności __swimlane__ na tablicy __board__",
"act-addSubtask": "dodał(a) podzadanie __subtask__ na karcie __card__ na liście __list__ w diagramie czynności __swimlane__ na tablicy __board__",
"act-addLabel": "dodał(a) etykietę __label__ do karty __card__ na liście __list__ w diagramie czynności __swimlane__ na tablicy __board__",
"act-addedLabel": "dodał(a) etykietę __label__ do karty __card__ na liście __list__ w diagramie czynności __swimlane__ na tablicy __board__",
"act-removeLabel": "usunął/usunęła etykietę __label__ z karty __card__ na liście __list__ w diagramie czynności __swimlane__ na tablicy __board__",
"act-removedLabel": "usunął/usunęła etykietę __label__ z karty __card__ na liście __list__ w diagramie czynności __swimlane__ na tablicy __board__",
"act-removeLabel": "usunął etykietę __label__ z karty __card__ na liście __list__ w diagramie czynności __swimlane__ na tablicy __board__",
"act-removedLabel": "usunął etykietę __label__ z karty __card__ na liście __list__ w diagramie czynności __swimlane__ na tablicy __board__",
"act-addChecklist": "dodał(a) listę zadań __checklist__ do karty __card__ na liście __list__ w diagramie czynności __swimlane__ na tablicy __board__",
"act-addChecklistItem": "dodał(a) element listy zadań __checklistItem__ do listy zadań __checklist__ na karcie __card__ na liście __list__ na diagramie czynności __swimlane__ na tablicy __board__",
"act-removeChecklist": "usunął/usunęła listę zadań __checklist__ z karty __card__ na liście __list__ na diagramie czynności __swimlane__ na tablicy __board__",
"act-removeChecklistItem": "usunął/usunęła element listy zadań __checklistItem__ z listy zadań __checkList__ na karcie __card__ na liście __list__ na diagramie czynności __swimlane__ na tablicy __board__",
"act-removeChecklist": "usunął listę zadań __checklist__ z karty __card__ na liście __list__ na diagramie czynności __swimlane__ na tablicy __board__",
"act-removeChecklistItem": "usunął element listy zadań __checklistItem__ z listy zadań __checkList__ na karcie __card__ na liście __list__ na diagramie czynności __swimlane__ na tablicy __board__",
"act-checkedItem": "zaznaczył(a) __checklistItem__ na liście zadań __checklist__ na karcie __card__ na liście __list__ na diagramie czynności __swimlane__ na tablicy __board__",
"act-uncheckedItem": "odznaczył(a) __checklistItem__ na liście __checklist__ na karcie __card__ na liście __list__ na diagramie czynności __swimlane__ na tablicy __board__",
"act-completeChecklist": "wykonał(a) wszystkie zadania z listy __checklist__ na karcie __card__ na liście __list__ na diagramie czynności__ na tablicy __board__",
"act-uncompleteChecklist": "wycofał(a) ukończenie wykonania listy __checklist__ na karcie __card__ na liście __list__ na diagramie czynności__ na tablicy __board__",
"act-addComment": "dodał(a) komentarz na karcie __card__: __comment__ na liście __list__ na diagramie czynności __swimlane__ na tablicy __board__",
"act-editComment": "edytował(a) komentarz na karcie __card__: __comment__ na liście __list__ na diagramie czynności __swimlane__ na tablicy __board__",
"act-deleteComment": "usunął/usunęła komentarz na karcie __card__: __comment__ na liście __list__ na diagramie czynności __swimlane__ na tablicy __board__",
"act-deleteComment": "usunął komentarz na karcie __card__: __comment__ na liście __list__ na diagramie czynności __swimlane__ na tablicy __board__",
"act-createBoard": "utworzył(a) tablicę __board__",
"act-createSwimlane": "utworzył(a) diagram czynności __swimlane__ na tablicy __board__",
"act-createCard": "utworzył(a) kartę __card__ na liście __list__ na diagramie czynności __swimlane__ na tablicy __board__",
"act-createCustomField": "utworzył(a) niestandardowe pole __customField__ na tablicy __board__",
"act-deleteCustomField": "usunął/usunęła niestandardowe pole __customField__ na tablicy __board__",
"act-deleteCustomField": "usunął niestandardowe pole __customField__ na tablicy __board__",
"act-setCustomField": "zmienił(a) niestandardowe pole __customField__: __customFieldValue__ na karcie __card__ na liście __list__ na diagramie czynności __swimlane__ na tablicy __board__",
"act-createList": "dodał(a) listę __list__ do tablicy __board__",
"act-addBoardMember": "dodał(a) użytykownika __member__ do tablicy __board__",
"act-archivedBoard": "Tablica __board__ została przeniesiona do Archiwum",
"act-archivedCard": "przeniósł/przeniosła kartę __card__ na liście __list__ na diagramie czynności __swimlane__ na tablicy __board__ do Archiwum",
"act-archivedList": "przeniósł/przeniosła listę __list__ na diagramie czynności __swimlane__ na tablicy __board__ do Archiwum",
"act-archivedSwimlane": "przeniósł/przeniosła diagram czynności __swimlane__ na tablicy __board__ do Archiwum",
"act-archivedCard": "przeniósł kartę __card__ na liście __list__ na diagramie czynności __swimlane__ na tablicy __board__ do Archiwum",
"act-archivedList": "przeniósł listę __list__ na diagramie czynności __swimlane__ na tablicy __board__ do Archiwum",
"act-archivedSwimlane": "przeniósł diagram czynności __swimlane__ na tablicy __board__ do Archiwum",
"act-importBoard": "zaimportował(a) tablicę __board__",
"act-importCard": "zaimportował(a) kartę __card__ do listy __list__ na diagramie czynności __swimlane__ na tablicy __board__",
"act-importList": "zaimportował(a) listę __list__ na diagram czynności __swimlane__ do tablicy __board__",
"act-joinMember": "dodał(a) użytkownika __member__ do karty __card__ na liście __list__ na diagramie czynności __swimlane__ na tablicy __board__",
"act-moveCard": "przeniósł/a kartę __card__ na tablicy __board__ z listy __oldList__ na diagramie czynności __oldSwimlane__ na listę __list__ na diagramie czynności __swimlane__",
"act-moveCardToOtherBoard": "przeniósł/a kartę __card__ z listy __oldList__ na diagramie czynności __oldSwimlane__ na tablicy __oldBoard__ do listy __listy__ na diagramie czynności __swimlane__ na tablicy __board__",
"act-removeBoardMember": "usunął/usunęła użytkownika __member__ z tablicy __board__",
"act-moveCard": "przeniósł kartę __card__ na tablicy __board__ z listy __oldList__ na diagramie czynności __oldSwimlane__ na listę __list__ na diagramie czynności __swimlane__",
"act-moveCardToOtherBoard": "przeniósł kartę __card__ z listy __oldList__ na diagramie czynności __oldSwimlane__ na tablicy __oldBoard__ do listy __listy__ na diagramie czynności __swimlane__ na tablicy __board__",
"act-removeBoardMember": "usunął użytkownika __member__ z tablicy __board__",
"act-restoredCard": "przywrócił(a) kartę __card__ na listę __list__ na diagram czynności__ na tablicy __board__",
"act-unjoinMember": "usunął/usunęła użytkownika __member__ z karty __card__ na liście __list__ na diagramie czynności __swimlane__ na tablicy __board__",
"act-unjoinMember": "usunął użytkownika __member__ z karty __card__ na liście __list__ na diagramie czynności __swimlane__ na tablicy __board__",
"act-withBoardTitle": "__board__",
"act-withCardTitle": "[__board__] __card__",
"actions": "Akcje",
@@ -63,18 +63,18 @@
"activity-checked-item": "zaznaczono %s w liście zadań%s z %s",
"activity-unchecked-item": "odznaczono %s w liście zadań %s z %s",
"activity-checklist-added": "dodał(a) listę zadań do %s",
"activity-checklist-removed": "usunął/usunęła listę zadań z %s",
"activity-checklist-removed": "usunął listę zadań z %s",
"activity-checklist-completed": "wykonał(a) wszystkie zadania z listy __checklist__ na karcie __card__ na liście __list__ na diagramie czynności __swimlane__ na tablicy __board__",
"activity-checklist-uncompleted": "nieukończono listy zadań %s z %s",
"activity-checklist-item-added": "dodał(a) zadanie '%s' do %s",
"activity-checklist-item-removed": "usunął/usunęła element z listy zadań '%s' w %s",
"activity-checklist-item-removed": "usunął element z listy zadań '%s' w %s",
"add": "Dodaj",
"activity-checked-item-card": "zaznaczono %s w liście zadań %s",
"activity-unchecked-item-card": "odznaczono %s w liście zadań %s",
"activity-checklist-completed-card": "wykonał(a) wszystkie zadania z listy __checklist__ na karcie __card__ na liście __list__ na diagramie czynności __swimlane__ na tablicy __board__",
"activity-checklist-uncompleted-card": "wycofano ukończenie listy zadań %s",
"activity-editComment": "edytował(a) komentarz %s",
"activity-deleteComment": "usunął/ęła komentarz %s",
"activity-deleteComment": "usunął komentarz %s",
"add-attachment": "Dodaj załącznik",
"add-board": "Dodaj tablicę",
"add-card": "Dodaj kartę",
@@ -583,8 +583,9 @@
"default": "Domyślny",
"queue": "Kolejka",
"subtask-settings": "Ustawienia podzadań",
"card-settings": "Ustawienia karty",
"boardSubtaskSettingsPopup-title": "Ustawienia tablicy podzadań",
"show-subtasks-field": "Karty mogą mieć podzadania",
"boardCardSettingsPopup-title": "Ustawienia kart",
"deposit-subtasks-board": "Przechowuj podzadania na tablicy:",
"deposit-subtasks-list": "Początkowa lista dla podzadań jest przechowywana w:",
"show-parent-in-minicard": "Pokaż rodzica w minikarcie:",
@@ -597,11 +598,11 @@
"source-board": "Tablica źródłowa",
"no-parent": "Nie pokazuj rodzica",
"activity-added-label": "dodał(a) etykietę '%s' z '%s'",
"activity-removed-label": "usunął/usunęła etykietę '%s' z '%s'",
"activity-delete-attach": "usunął/usunęła załącznik z %s",
"activity-removed-label": "usunął etykietę '%s' z '%s'",
"activity-delete-attach": "usunął załącznik z %s",
"activity-added-label-card": "dodał(a) etykietę '%s'",
"activity-removed-label-card": "usunął/usunęła etykietę '%s'",
"activity-delete-attach-card": "usunął/usunęła załącznik",
"activity-removed-label-card": "usunął etykietę '%s'",
"activity-delete-attach-card": "usunął załącznik",
"activity-set-customfield": "ustawiono niestandardowe pole '%s' do '%s' na '%s'",
"activity-unset-customfield": "wyczyszczono niestandardowe pole '%s' na '%s'",
"r-rule": "Reguła",
@@ -617,7 +618,7 @@
"r-is": "jest",
"r-is-moved": "jest przenoszona",
"r-added-to": "dodana do",
"r-removed-from": "usunął/usunęła z",
"r-removed-from": "usunął z",
"r-the-board": "tablicy",
"r-list": "lista",
"set-filter": "Ustaw filtr",
@@ -708,7 +709,7 @@
"r-df-end-at": "zakończenie",
"r-df-received-at": "odebrano",
"r-to-current-datetime": "o aktualnej dacie/godzinie",
"r-remove-value-from": "usunął/usunęła wartość z",
"r-remove-value-from": "usunął wartość z",
"ldap": "LDAP",
"oauth2": "OAuth2",
"cas": "CAS",
@@ -753,5 +754,10 @@
"hide-minicard-label-text": "Ukryj opisy etykiet minikart",
"show-desktop-drag-handles": "Pokaż przeciągnięcia na pulpit",
"assignee": "Przypisujący",
"cardAssigneesPopup-title": "Przypisujący"
"cardAssigneesPopup-title": "Przypisujący",
"addmore-detail": "Dodaj bardziej szczegółowy opis",
"show-on-card": "Pokaż na karcie",
"new": "Nowy",
"editUserPopup-title": "Edytuj użytkownika",
"newUserPopup-title": "Nowy użytkownik"
}
+9 -3
View File
@@ -182,7 +182,7 @@
"changeLanguagePopup-title": "Alterar Idioma",
"changePasswordPopup-title": "Alterar Senha",
"changePermissionsPopup-title": "Alterar Permissões",
"changeSettingsPopup-title": "Altera configurações",
"changeSettingsPopup-title": "Alterar configurações",
"subtasks": "Subtarefas",
"checklists": "Listas de verificação",
"click-to-star": "Marcar quadro como favorito.",
@@ -583,8 +583,9 @@
"default": "Padrão",
"queue": "Fila",
"subtask-settings": "Configurações de subtarefas",
"card-settings": "Configurações do Cartão",
"boardSubtaskSettingsPopup-title": "Configurações das subtarefas do quadro",
"show-subtasks-field": "Cartões podem ter subtarefas",
"boardCardSettingsPopup-title": "Configurações do Cartão",
"deposit-subtasks-board": "Inserir subtarefas a este quadro:",
"deposit-subtasks-list": "Listas de subtarefas inseridas aqui:",
"show-parent-in-minicard": "Mostrar Pai do mini cartão:",
@@ -753,5 +754,10 @@
"hide-minicard-label-text": "Esconder rótulo da etiqueta do mini cartão",
"show-desktop-drag-handles": "Mostrar alças de arrasto da área de trabalho",
"assignee": "Administrador",
"cardAssigneesPopup-title": "Administrador"
"cardAssigneesPopup-title": "Administrador",
"addmore-detail": "Adicionar descrição detalhada",
"show-on-card": "Mostrar no Cartão",
"new": "Novo",
"editUserPopup-title": "Editar usuário",
"newUserPopup-title": "Novo usuário"
}
+8 -2
View File
@@ -583,8 +583,9 @@
"default": "Omissão",
"queue": "Fila",
"subtask-settings": "Configurações de Sub-tarefas",
"card-settings": "Card Settings",
"boardSubtaskSettingsPopup-title": "Configurações das Sub-tarefas do Quadro",
"show-subtasks-field": "Cartões podem ter sub-tarefas",
"boardCardSettingsPopup-title": "Card Settings",
"deposit-subtasks-board": "Depositar sub-tarefas neste quadro:",
"deposit-subtasks-list": "Lista de destino para sub-tarefas depositadas aqui:",
"show-parent-in-minicard": "Mostrar pai no mini-cartão:",
@@ -753,5 +754,10 @@
"hide-minicard-label-text": "Ocultar texto das etiquetas dos mini-cartões",
"show-desktop-drag-handles": "Show desktop drag handles",
"assignee": "Assignee",
"cardAssigneesPopup-title": "Assignee"
"cardAssigneesPopup-title": "Assignee",
"addmore-detail": "Add a more detailed description",
"show-on-card": "Show on Card",
"new": "New",
"editUserPopup-title": "Edit User",
"newUserPopup-title": "New User"
}
Loaded 100 of 366 files, more files were not shown because too many files have changed in this diff. Show more