Compare commits

...
90 Commits
Author SHA1 Message Date
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
126 changed files with 11431 additions and 837 deletions

No files matched your search

+1
View File
@@ -1 +1,2 @@
packages/*
.snap-meteor-1.8/*
+8 -8
View File
@@ -1,7 +1,7 @@
3stack:presence@1.1.2
accounts-base@1.5.0
accounts-oauth@1.1.16
accounts-password@1.5.2
accounts-password@1.5.3
aldeed:collection2@2.10.0
aldeed:collection2-core@1.2.0
aldeed:schema-deny@1.1.0
@@ -12,7 +12,7 @@ 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.5.0
babel-compiler@7.5.1
babel-runtime@1.5.0
base64@1.0.12
binary-heap@1.0.11
@@ -52,12 +52,12 @@ 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.14.0
ecmascript@0.14.1
ecmascript-runtime@0.7.0
ecmascript-runtime-client@0.10.0
ecmascript-runtime-server@0.9.0
@@ -106,11 +106,11 @@ 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
modern-browsers@0.1.5
modules@0.15.0
modules-runtime@0.12.0
momentjs:moment@2.24.0
mongo@1.8.0
mongo@1.8.1
mongo-decimal@0.1.1
mongo-dev-server@1.1.0
mongo-id@1.0.7
@@ -162,7 +162,7 @@ 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
@@ -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.8.0
webapp@1.8.2
webapp-hashing@1.0.9
wekan-accounts-cas@0.1.0
wekan-accounts-oidc@1.0.10
+1
View File
@@ -5,3 +5,4 @@ node_modules/
.vscode/
.tx/
.github/
.snap-meteor-1.8/
@@ -0,0 +1,20 @@
# This file contains information which helps Meteor properly upgrade your
# app when you run 'meteor update'. You should check it into version control
# with your project.
notices-for-0.9.0
notices-for-0.9.1
0.9.4-platform-file
notices-for-facebook-graph-api-2
1.2.0-standard-minifiers-package
1.2.0-meteor-platform-split
1.2.0-cordova-changes
1.2.0-breaking-changes
1.3.0-split-minifiers-package
1.3.5-remove-old-dev-bundle-link
1.4.0-remove-old-dev-bundle-link
1.4.1-add-shell-server-package
1.4.3-split-account-service-packages
1.5-add-dynamic-import-package
1.7-split-underscore-from-meteor-base
1.8.3-split-jquery-from-blaze
+2
View File
@@ -0,0 +1,2 @@
dev_bundle
local
+7
View File
@@ -0,0 +1,7 @@
# This file contains a token that is unique to your project.
# Check it into your repository along with the rest of this directory.
# It can be used for purposes such as:
# - ensuring you don't accidentally deploy one app on top of another
# - providing package authors with aggregated statistics
dvyihgykyzec6y1dpg
Whitespace-only changes.
+99
View File
@@ -0,0 +1,99 @@
# Meteor packages used by this project, one per line.
#
# 'meteor add' and 'meteor remove' will edit this file for you,
# but you can also edit it by hand.
meteor-base@1.4.0
# Build system
ecmascript@0.13.2
standard-minifier-css@1.5.4
standard-minifier-js@2.5.2
mquandalle:jade
# Polyfills
es5-shim@4.8.0
# Collections
aldeed:collection2
cfs:standard-packages
cottz:publish-relations
dburles:collection-helpers
idmontie:migrations
matb33:collection-hooks
matteodem:easy-search
mongo@1.7.0
mquandalle:collection-mutations
# Account system
kenton:accounts-sandstorm
service-configuration@1.0.11
useraccounts:unstyled
useraccounts:flow-routing
wekan-ldap
wekan-accounts-cas
wekan-accounts-oidc
# Utilities
check@1.3.1
jquery@1.11.10
random@1.1.0
reactive-dict@1.3.0
session@1.2.0
tracker@1.2.0
underscore@1.0.10
3stack:presence
alethes:pages
arillo:flow-router-helpers
audit-argument-checks@1.0.7
kadira:blaze-layout
kadira:dochead
mquandalle:autofocus
ongoworks:speakingurl
raix:handlebar-helpers
tap:i18n
http@1.4.2
# UI components
blaze
reactive-var@1.0.11
fortawesome:fontawesome
mousetrap:mousetrap
mquandalle:jquery-textcomplete
mquandalle:jquery-ui-drag-drop-sort
mquandalle:mousetrap-bindglobal
peerlibrary:blaze-components@=0.15.1
templates:tabs
verron:autosize
simple:json-routes
rajit:bootstrap3-datepicker
shell-server@0.4.0
simple:rest-accounts-password
useraccounts:core
email@1.2.3
horka:swipebox
dynamic-import@0.5.1
staringatlights:fast-render
accounts-password@1.5.2
cfs:gridfs
rzymek:fullcalendar
momentjs:moment@2.22.2
browser-policy-framing@1.1.0
mquandalle:moment
msavin:usercache
wekan-scrollbar
mquandalle:perfect-scrollbar
mdg:meteor-apm-agent@3.2.0-rc.0!
coagmano:stylus
lucasantoniassi:accounts-lockout
meteorhacks:subs-manager
meteorhacks:picker
lamhieu:unblock
meteorhacks:aggregate@1.3.0
wekan-markdown
konecty:mongo-counter
percolate:synced-cron
easylogic:summernote
cfs:filesystem
ostrio:cookies
+2
View File
@@ -0,0 +1,2 @@
server
browser
+1
View File
@@ -0,0 +1 @@
METEOR@1.8.3
+198
View File
@@ -0,0 +1,198 @@
3stack:presence@1.1.2
accounts-base@1.4.5
accounts-oauth@1.1.16
accounts-password@1.5.2
aldeed:collection2@2.10.0
aldeed:collection2-core@1.2.0
aldeed:schema-deny@1.1.0
aldeed:schema-index@1.1.1
aldeed:simple-schema@1.5.4
alethes:pages@1.8.6
allow-deny@1.1.0
arillo:flow-router-helpers@0.5.2
audit-argument-checks@1.0.7
autoupdate@1.6.0
babel-compiler@7.4.2
babel-runtime@1.4.0
base64@1.0.12
binary-heap@1.0.11
blaze@2.3.4
blaze-tools@1.0.10
boilerplate-generator@1.6.0
browser-policy-common@1.0.11
browser-policy-framing@1.1.0
caching-compiler@1.2.1
caching-html-compiler@1.1.3
callback-hook@1.2.0
cfs:access-point@0.1.49
cfs:base-package@0.0.30
cfs:collection@0.5.5
cfs:collection-filters@0.2.4
cfs:data-man@0.0.6
cfs:file@0.1.17
cfs:filesystem@0.1.2
cfs:gridfs@0.0.34
cfs:http-methods@0.0.32
cfs:http-publish@0.0.13
cfs:power-queue@0.9.11
cfs:reactive-list@0.0.9
cfs:reactive-property@0.0.4
cfs:standard-packages@0.5.10
cfs:storage-adapter@0.2.4
cfs:tempstore@0.1.6
cfs:upload-http@0.0.20
cfs:worker@0.1.5
check@1.3.1
chuangbo:cookie@1.1.0
coagmano:stylus@2.0.0
coffeescript@1.0.17
cottz:publish-relations@2.0.8
dburles:collection-helpers@1.1.0
ddp@1.4.0
ddp-client@2.3.3
ddp-common@1.4.0
ddp-rate-limiter@1.0.7
ddp-server@2.3.0
deps@1.0.12
diff-sequence@1.1.1
dynamic-import@0.5.1
easylogic:summernote@0.8.8
ecmascript@0.13.2
ecmascript-runtime@0.7.0
ecmascript-runtime-client@0.9.0
ecmascript-runtime-server@0.8.0
ejson@1.1.1
email@1.2.3
es5-shim@4.8.0
fastclick@1.0.13
fetch@0.1.1
fortawesome:fontawesome@4.7.0
geojson-utils@1.0.10
horka:swipebox@1.0.2
hot-code-push@1.0.4
html-tools@1.0.11
htmljs@1.0.11
http@1.4.2
id-map@1.1.0
idmontie:migrations@1.0.3
inter-process-messaging@0.1.0
jquery@1.11.11
kadira:blaze-layout@2.3.0
kadira:dochead@1.5.0
kadira:flow-router@2.12.1
kenton:accounts-sandstorm@0.7.0
konecty:mongo-counter@0.0.5_3
lamhieu:meteorx@2.1.1
lamhieu:unblock@1.0.0
launch-screen@1.1.1
livedata@1.0.18
localstorage@1.2.0
logging@1.1.20
lucasantoniassi:accounts-lockout@1.0.0
matb33:collection-hooks@0.9.1
matteodem:easy-search@1.6.4
mdg:meteor-apm-agent@3.2.5
mdg:validation-error@0.5.1
meteor@1.9.3
meteor-base@1.4.0
meteor-platform@1.2.6
meteorhacks:aggregate@1.3.0
meteorhacks:collection-utils@1.2.0
meteorhacks:picker@1.0.3
meteorhacks:subs-manager@1.6.4
meteorspark:util@0.2.0
minifier-css@1.4.3
minifier-js@2.5.1
minifiers@1.1.8-faster-rebuild.0
minimongo@1.4.5
mobile-status-bar@1.0.14
modern-browsers@0.1.4
modules@0.14.0
modules-runtime@0.11.0
momentjs:moment@2.24.0
mongo@1.7.0
mongo-decimal@0.1.1
mongo-dev-server@1.1.0
mongo-id@1.0.7
mongo-livedata@1.0.12
mousetrap:mousetrap@1.4.6_1
mquandalle:autofocus@1.0.0
mquandalle:collection-mutations@0.1.0
mquandalle:jade@0.4.9
mquandalle:jade-compiler@0.4.5
mquandalle:jquery-textcomplete@0.8.0_1
mquandalle:jquery-ui-drag-drop-sort@0.2.0
mquandalle:moment@1.0.1
mquandalle:mousetrap-bindglobal@0.0.1
mquandalle:perfect-scrollbar@0.6.5_2
msavin:usercache@1.8.0
npm-bcrypt@0.9.3
npm-mongo@3.2.0
oauth@1.2.8
oauth2@1.2.1
observe-sequence@1.0.16
ongoworks:speakingurl@1.1.0
ordered-dict@1.1.0
ostrio:cookies@2.5.0
peerlibrary:assert@0.3.0
peerlibrary:base-component@0.16.0
peerlibrary:blaze-components@0.15.1
peerlibrary:computed-field@0.10.0
peerlibrary:reactive-field@0.6.0
percolate:synced-cron@1.3.2
promise@0.11.2
raix:eventemitter@0.1.3
raix:handlebar-helpers@0.2.5
rajit:bootstrap3-datepicker@1.7.1_1
random@1.1.0
rate-limit@1.0.9
reactive-dict@1.3.0
reactive-var@1.0.11
reload@1.3.0
retry@1.1.0
routepolicy@1.1.0
rzymek:fullcalendar@3.8.0
server-render@0.3.1
service-configuration@1.0.11
session@1.2.0
sha@1.0.9
shell-server@0.4.0
simple:authenticate-user-by-token@1.0.1
simple:json-routes@2.1.0
simple:rest-accounts-password@1.1.2
simple:rest-bearer-token-parser@1.0.1
simple:rest-json-error-handler@1.0.1
socket-stream-client@0.2.2
softwarerero:accounts-t9n@1.3.11
spacebars@1.0.15
spacebars-compiler@1.1.3
srp@1.0.12
standard-minifier-css@1.5.4
standard-minifier-js@2.5.2
staringatlights:fast-render@3.2.0
staringatlights:inject-data@2.3.0
tap:i18n@1.8.2
templates:tabs@2.3.0
templating@1.3.2
templating-compiler@1.3.3
templating-runtime@1.3.2
templating-tools@1.1.2
tracker@1.2.0
twbs:bootstrap@3.3.6
ui@1.0.13
underscore@1.0.10
url@1.2.0
useraccounts:core@1.14.2
useraccounts:flow-routing@1.14.2
useraccounts:unstyled@1.14.2
verron:autosize@3.0.8
webapp@1.7.5
webapp-hashing@1.0.9
wekan-accounts-cas@0.1.0
wekan-accounts-oidc@1.0.10
wekan-ldap@0.0.2
wekan-markdown@1.0.7
wekan-oidc@1.0.12
wekan-scrollbar@3.1.3
yasaricli:slugify@0.0.7
zimme:active-route@2.3.2
+914
View File
@@ -0,0 +1,914 @@
(function () {
/* Imports */
var Meteor = Package.meteor.Meteor;
var global = Package.meteor.global;
var meteorEnv = Package.meteor.meteorEnv;
var FS = Package['cfs:base-package'].FS;
var check = Package.check.check;
var Match = Package.check.Match;
var EJSON = Package.ejson.EJSON;
var HTTP = Package['cfs:http-methods'].HTTP;
/* Package-scope variables */
var rootUrlPathPrefix, baseUrl, getHeaders, getHeadersByCollection, _existingMountPoints, mountUrls;
(function(){
///////////////////////////////////////////////////////////////////////
// //
// packages/cfs_access-point/packages/cfs_access-point.js //
// //
///////////////////////////////////////////////////////////////////////
//
(function () {
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// packages/cfs:access-point/access-point-common.js //
// //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
rootUrlPathPrefix = __meteor_runtime_config__.ROOT_URL_PATH_PREFIX || ""; // 1
// Adjust the rootUrlPathPrefix if necessary // 2
if (rootUrlPathPrefix.length > 0) { // 3
if (rootUrlPathPrefix.slice(0, 1) !== '/') { // 4
rootUrlPathPrefix = '/' + rootUrlPathPrefix; // 5
} // 6
if (rootUrlPathPrefix.slice(-1) === '/') { // 7
rootUrlPathPrefix = rootUrlPathPrefix.slice(0, -1); // 8
} // 9
} // 10
// 11
// prepend ROOT_URL when isCordova // 12
if (Meteor.isCordova) { // 13
rootUrlPathPrefix = Meteor.absoluteUrl(rootUrlPathPrefix.replace(/^\/+/, '')).replace(/\/+$/, ''); // 14
} // 15
// 16
baseUrl = '/cfs'; // 17
FS.HTTP = FS.HTTP || {}; // 18
// 19
// Note the upload URL so that client uploader packages know what it is // 20
FS.HTTP.uploadUrl = rootUrlPathPrefix + baseUrl + '/files'; // 21
// 22
/** // 23
* @method FS.HTTP.setBaseUrl // 24
* @public // 25
* @param {String} newBaseUrl - Change the base URL for the HTTP GET and DELETE endpoints. // 26
* @returns {undefined} // 27
*/ // 28
FS.HTTP.setBaseUrl = function setBaseUrl(newBaseUrl) { // 29
// 30
// Adjust the baseUrl if necessary // 31
if (newBaseUrl.slice(0, 1) !== '/') { // 32
newBaseUrl = '/' + newBaseUrl; // 33
} // 34
if (newBaseUrl.slice(-1) === '/') { // 35
newBaseUrl = newBaseUrl.slice(0, -1); // 36
} // 37
// 38
// Update the base URL // 39
baseUrl = newBaseUrl; // 40
// 41
// Change the upload URL so that client uploader packages know what it is // 42
FS.HTTP.uploadUrl = rootUrlPathPrefix + baseUrl + '/files'; // 43
// 44
// Remount URLs with the new baseUrl, unmounting the old, on the server only. // 45
// If existingMountPoints is empty, then we haven't run the server startup // 46
// code yet, so this new URL will be used at that point for the initial mount. // 47
if (Meteor.isServer && !FS.Utility.isEmpty(_existingMountPoints)) { // 48
mountUrls(); // 49
} // 50
}; // 51
// 52
/* // 53
* FS.File extensions // 54
*/ // 55
// 56
/** // 57
* @method FS.File.prototype.url Construct the file url // 58
* @public // 59
* @param {Object} [options] // 60
* @param {String} [options.store] Name of the store to get from. If not defined, the first store defined in `options.stores` for the collection on the client is used.
* @param {Boolean} [options.auth=null] Add authentication token to the URL query string? By default, a token for the current logged in user is added on the client. Set this to `false` to omit the token. Set this to a string to provide your own token. Set this to a number to specify an expiration time for the token in seconds.
* @param {Boolean} [options.download=false] Should headers be set to force a download? Typically this means that clicking the link with this URL will download the file to the user's Downloads folder instead of displaying the file in the browser.
* @param {Boolean} [options.brokenIsFine=false] Return the URL even if we know it's currently a broken link because the file hasn't been saved in the requested store yet.
* @param {Boolean} [options.metadata=false] Return the URL for the file metadata access point rather than the file itself.
* @param {String} [options.uploading=null] A URL to return while the file is being uploaded. // 66
* @param {String} [options.storing=null] A URL to return while the file is being stored. // 67
* @param {String} [options.filename=null] Override the filename that should appear at the end of the URL. By default it is the name of the file in the requested store.
* // 69
* Returns the HTTP URL for getting the file or its metadata. // 70
*/ // 71
FS.File.prototype.url = function(options) { // 72
var self = this; // 73
options = options || {}; // 74
options = FS.Utility.extend({ // 75
store: null, // 76
auth: null, // 77
download: false, // 78
metadata: false, // 79
brokenIsFine: false, // 80
uploading: null, // return this URL while uploading // 81
storing: null, // return this URL while storing // 82
filename: null // override the filename that is shown to the user // 83
}, options.hash || options); // check for "hash" prop if called as helper // 84
// 85
// Primarily useful for displaying a temporary image while uploading an image // 86
if (options.uploading && !self.isUploaded()) { // 87
return options.uploading; // 88
} // 89
// 90
if (self.isMounted()) { // 91
// See if we've stored in the requested store yet // 92
var storeName = options.store || self.collection.primaryStore.name; // 93
if (!self.hasStored(storeName)) { // 94
if (options.storing) { // 95
return options.storing; // 96
} else if (!options.brokenIsFine) { // 97
// We want to return null if we know the URL will be a broken // 98
// link because then we can avoid rendering broken links, broken // 99
// images, etc. // 100
return null; // 101
} // 102
} // 103
// 104
// Add filename to end of URL if we can determine one // 105
var filename = options.filename || self.name({store: storeName}); // 106
if (typeof filename === "string" && filename.length) { // 107
filename = '/' + filename; // 108
} else { // 109
filename = ''; // 110
} // 111
// 112
// TODO: Could we somehow figure out if the collection requires login? // 113
var authToken = ''; // 114
if (Meteor.isClient && typeof Accounts !== "undefined" && typeof Accounts._storedLoginToken === "function") { // 115
if (options.auth !== false) { // 116
// Add reactive deps on the user // 117
Meteor.userId(); // 118
// 119
var authObject = { // 120
authToken: Accounts._storedLoginToken() || '' // 121
}; // 122
// 123
// If it's a number, we use that as the expiration time (in seconds) // 124
if (options.auth === +options.auth) { // 125
authObject.expiration = FS.HTTP.now() + options.auth * 1000; // 126
} // 127
// 128
// Set the authToken // 129
var authString = JSON.stringify(authObject); // 130
authToken = FS.Utility.btoa(authString); // 131
} // 132
} else if (typeof options.auth === "string") { // 133
// If the user supplies auth token the user will be responsible for // 134
// updating // 135
authToken = options.auth; // 136
} // 137
// 138
// Construct query string // 139
var params = {}; // 140
if (authToken !== '') { // 141
params.token = authToken; // 142
} // 143
if (options.download) { // 144
params.download = true; // 145
} // 146
if (options.store) { // 147
// We use options.store here instead of storeName because we want to omit the queryString // 148
// whenever possible, allowing users to have "clean" URLs if they want. The server will // 149
// assume the first store defined on the server, which means that we are assuming that // 150
// the first on the client is also the first on the server. If that's not the case, the // 151
// store option should be supplied. // 152
params.store = options.store; // 153
} // 154
var queryString = FS.Utility.encodeParams(params); // 155
if (queryString.length) { // 156
queryString = '?' + queryString; // 157
} // 158
// 159
// Determine which URL to use // 160
var area; // 161
if (options.metadata) { // 162
area = '/record'; // 163
} else { // 164
area = '/files'; // 165
} // 166
// 167
// Construct and return the http method url // 168
return rootUrlPathPrefix + baseUrl + area + '/' + self.collection.name + '/' + self._id + filename + queryString; // 169
} // 170
// 171
}; // 172
// 173
// 174
// 175
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}).call(this);
(function () {
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// packages/cfs:access-point/access-point-handlers.js //
// //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
getHeaders = []; // 1
getHeadersByCollection = {}; // 2
// 3
FS.HTTP.Handlers = {}; // 4
// 5
/** // 6
* @method FS.HTTP.Handlers.Del // 7
* @public // 8
* @returns {any} response // 9
* // 10
* HTTP DEL request handler // 11
*/ // 12
FS.HTTP.Handlers.Del = function httpDelHandler(ref) { // 13
var self = this; // 14
var opts = FS.Utility.extend({}, self.query || {}, self.params || {}); // 15
// 16
// If DELETE request, validate with 'remove' allow/deny, delete the file, and return // 17
FS.Utility.validateAction(ref.collection.files._validators['remove'], ref.file, self.userId); // 18
// 19
/* // 20
* From the DELETE spec: // 21
* A successful response SHOULD be 200 (OK) if the response includes an // 22
* entity describing the status, 202 (Accepted) if the action has not // 23
* yet been enacted, or 204 (No Content) if the action has been enacted // 24
* but the response does not include an entity. // 25
*/ // 26
self.setStatusCode(200); // 27
// 28
return { // 29
deleted: !!ref.file.remove() // 30
}; // 31
}; // 32
// 33
/** // 34
* @method FS.HTTP.Handlers.GetList // 35
* @public // 36
* @returns {Object} response // 37
* // 38
* HTTP GET file list request handler // 39
*/ // 40
FS.HTTP.Handlers.GetList = function httpGetListHandler() { // 41
// Not Yet Implemented // 42
// Need to check publications and return file list based on // 43
// what user is allowed to see // 44
}; // 45
// 46
/* // 47
requestRange will parse the range set in request header - if not possible it // 48
will throw fitting errors and autofill range for both partial and full ranges // 49
// 50
throws error or returns the object: // 51
{ // 52
start // 53
end // 54
length // 55
unit // 56
partial // 57
} // 58
*/ // 59
var requestRange = function(req, fileSize) { // 60
if (req) { // 61
if (req.headers) { // 62
var rangeString = req.headers.range; // 63
// 64
// Make sure range is a string // 65
if (rangeString === ''+rangeString) { // 66
// 67
// range will be in the format "bytes=0-32767" // 68
var parts = rangeString.split('='); // 69
var unit = parts[0]; // 70
// 71
// Make sure parts consists of two strings and range is of type "byte" // 72
if (parts.length == 2 && unit == 'bytes') { // 73
// Parse the range // 74
var range = parts[1].split('-'); // 75
var start = Number(range[0]); // 76
var end = Number(range[1]); // 77
// 78
// Fix invalid ranges? // 79
if (range[0] != start) start = 0; // 80
if (range[1] != end || !end) end = fileSize - 1; // 81
// 82
// Make sure range consists of a start and end point of numbers and start is less than end // 83
if (start < end) { // 84
// 85
var partSize = 0 - start + end + 1; // 86
// 87
// Return the parsed range // 88
return { // 89
start: start, // 90
end: end, // 91
length: partSize, // 92
size: fileSize, // 93
unit: unit, // 94
partial: (partSize < fileSize) // 95
}; // 96
// 97
} else { // 98
throw new Meteor.Error(416, "Requested Range Not Satisfiable"); // 99
} // 100
// 101
} else { // 102
// The first part should be bytes // 103
throw new Meteor.Error(416, "Requested Range Unit Not Satisfiable"); // 104
} // 105
// 106
} else { // 107
// No range found // 108
} // 109
// 110
} else { // 111
// throw new Error('No request headers set for _parseRange function'); // 112
} // 113
} else { // 114
throw new Error('No request object passed to _parseRange function'); // 115
} // 116
// 117
return { // 118
start: 0, // 119
end: fileSize - 1, // 120
length: fileSize, // 121
size: fileSize, // 122
unit: 'bytes', // 123
partial: false // 124
}; // 125
}; // 126
// 127
/** // 128
* @method FS.HTTP.Handlers.Get // 129
* @public // 130
* @returns {any} response // 131
* // 132
* HTTP GET request handler // 133
*/ // 134
FS.HTTP.Handlers.Get = function httpGetHandler(ref) { // 135
var self = this; // 136
// Once we have the file, we can test allow/deny validators // 137
// XXX: pass on the "share" query eg. ?share=342hkjh23ggj for shared url access? // 138
FS.Utility.validateAction(ref.collection._validators['download'], ref.file, self.userId /*, self.query.shareId*/); // 139
// 140
var storeName = ref.storeName; // 141
// 142
// If no storeName was specified, use the first defined storeName // 143
if (typeof storeName !== "string") { // 144
// No store handed, we default to primary store // 145
storeName = ref.collection.primaryStore.name; // 146
} // 147
// 148
// Get the storage reference // 149
var storage = ref.collection.storesLookup[storeName]; // 150
// 151
if (!storage) { // 152
throw new Meteor.Error(404, "Not Found", 'There is no store "' + storeName + '"'); // 153
} // 154
// 155
// Get the file // 156
var copyInfo = ref.file.copies[storeName]; // 157
// 158
if (!copyInfo) { // 159
throw new Meteor.Error(404, "Not Found", 'This file was not stored in the ' + storeName + ' store'); // 160
} // 161
// 162
// Set the content type for file // 163
if (typeof copyInfo.type === "string") { // 164
self.setContentType(copyInfo.type); // 165
} else { // 166
self.setContentType('application/octet-stream'); // 167
} // 168
// 169
// Add 'Content-Disposition' header if requested a download/attachment URL // 170
if (typeof ref.download !== "undefined") { // 171
var filename = ref.filename || copyInfo.name; // 172
self.addHeader('Content-Disposition', 'attachment; filename="' + filename + '"'); // 173
} else { // 174
self.addHeader('Content-Disposition', 'inline'); // 175
} // 176
// 177
// Get the contents range from request // 178
var range = requestRange(self.request, copyInfo.size); // 179
// 180
// Some browsers cope better if the content-range header is // 181
// still included even for the full file being returned. // 182
self.addHeader('Content-Range', range.unit + ' ' + range.start + '-' + range.end + '/' + range.size); // 183
// 184
// If a chunk/range was requested instead of the whole file, serve that' // 185
if (range.partial) { // 186
self.setStatusCode(206, 'Partial Content'); // 187
} else { // 188
self.setStatusCode(200, 'OK'); // 189
} // 190
// 191
// Add any other global custom headers and collection-specific custom headers // 192
FS.Utility.each(getHeaders.concat(getHeadersByCollection[ref.collection.name] || []), function(header) { // 193
self.addHeader(header[0], header[1]); // 194
}); // 195
// 196
// Inform clients about length (or chunk length in case of ranges) // 197
self.addHeader('Content-Length', range.length); // 198
// 199
// Last modified header (updatedAt from file info) // 200
self.addHeader('Last-Modified', copyInfo.updatedAt.toUTCString()); // 201
// 202
// Inform clients that we accept ranges for resumable chunked downloads // 203
self.addHeader('Accept-Ranges', range.unit); // 204
// 205
if (FS.debug) console.log('Read file "' + (ref.filename || copyInfo.name) + '" ' + range.unit + ' ' + range.start + '-' + range.end + '/' + range.size);
// 207
var readStream = storage.adapter.createReadStream(ref.file, {start: range.start, end: range.end}); // 208
// 209
readStream.on('error', function(err) { // 210
// Send proper error message on get error // 211
if (err.message && err.statusCode) { // 212
self.Error(new Meteor.Error(err.statusCode, err.message)); // 213
} else { // 214
self.Error(new Meteor.Error(503, 'Service unavailable')); // 215
} // 216
}); // 217
// 218
readStream.pipe(self.createWriteStream()); // 219
}; // 220
const originalHandler = FS.HTTP.Handlers.Get;
FS.HTTP.Handlers.Get = function (ref) {
//console.log(ref.filename);
try {
var userAgent = (this.requestHeaders['user-agent']||'').toLowerCase();
if(userAgent.indexOf('msie') >= 0 || userAgent.indexOf('trident') >= 0 || userAgent.indexOf('chrome') >= 0) {
ref.filename = encodeURIComponent(ref.filename);
} else if(userAgent.indexOf('firefox') >= 0) {
ref.filename = new Buffer(ref.filename).toString('binary');
} else {
/* safari*/
ref.filename = new Buffer(ref.filename).toString('binary');
}
} catch (ex){
ref.filename = 'tempfix';
}
return originalHandler.call(this, ref);
};
// 221
/** // 222
* @method FS.HTTP.Handlers.PutInsert // 223
* @public // 224
* @returns {Object} response object with _id property // 225
* // 226
* HTTP PUT file insert request handler // 227
*/ // 228
FS.HTTP.Handlers.PutInsert = function httpPutInsertHandler(ref) { // 229
var self = this; // 230
var opts = FS.Utility.extend({}, self.query || {}, self.params || {}); // 231
// 232
FS.debug && console.log("HTTP PUT (insert) handler"); // 233
// 234
// Create the nice FS.File // 235
var fileObj = new FS.File(); // 236
// 237
// Set its name // 238
fileObj.name(opts.filename || null); // 239
// 240
// Attach the readstream as the file's data // 241
fileObj.attachData(self.createReadStream(), {type: self.requestHeaders['content-type'] || 'application/octet-stream'});
// 243
// Validate with insert allow/deny // 244
FS.Utility.validateAction(ref.collection.files._validators['insert'], fileObj, self.userId); // 245
// 246
// Insert file into collection, triggering readStream storage // 247
ref.collection.insert(fileObj); // 248
// 249
// Send response // 250
self.setStatusCode(200); // 251
// 252
// Return the new file id // 253
return {_id: fileObj._id}; // 254
}; // 255
// 256
/** // 257
* @method FS.HTTP.Handlers.PutUpdate // 258
* @public // 259
* @returns {Object} response object with _id and chunk properties // 260
* // 261
* HTTP PUT file update chunk request handler // 262
*/ // 263
FS.HTTP.Handlers.PutUpdate = function httpPutUpdateHandler(ref) { // 264
var self = this; // 265
var opts = FS.Utility.extend({}, self.query || {}, self.params || {}); // 266
// 267
var chunk = parseInt(opts.chunk, 10); // 268
if (isNaN(chunk)) chunk = 0; // 269
// 270
FS.debug && console.log("HTTP PUT (update) handler received chunk: ", chunk); // 271
// 272
// Validate with insert allow/deny; also mounts and retrieves the file // 273
FS.Utility.validateAction(ref.collection.files._validators['insert'], ref.file, self.userId); // 274
// 275
self.createReadStream().pipe( FS.TempStore.createWriteStream(ref.file, chunk) ); // 276
// 277
// Send response // 278
self.setStatusCode(200); // 279
// 280
return { _id: ref.file._id, chunk: chunk }; // 281
}; // 282
// 283
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}).call(this);
(function () {
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// packages/cfs:access-point/access-point-server.js //
// //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
var path = Npm.require("path"); // 1
// 2
HTTP.publishFormats({ // 3
fileRecordFormat: function (input) { // 4
// Set the method scope content type to json // 5
this.setContentType('application/json'); // 6
if (FS.Utility.isArray(input)) { // 7
return EJSON.stringify(FS.Utility.map(input, function (obj) { // 8
return FS.Utility.cloneFileRecord(obj); // 9
})); // 10
} else { // 11
return EJSON.stringify(FS.Utility.cloneFileRecord(input)); // 12
} // 13
} // 14
}); // 15
// 16
/** // 17
* @method FS.HTTP.setHeadersForGet // 18
* @public // 19
* @param {Array} headers - List of headers, where each is a two-item array in which item 1 is the header name and item 2 is the header value.
* @param {Array|String} [collections] - Which collections the headers should be added for. Omit this argument to add the header for all collections.
* @returns {undefined} // 22
*/ // 23
FS.HTTP.setHeadersForGet = function setHeadersForGet(headers, collections) { // 24
if (typeof collections === "string") { // 25
collections = [collections]; // 26
} // 27
if (collections) { // 28
FS.Utility.each(collections, function(collectionName) { // 29
getHeadersByCollection[collectionName] = headers || []; // 30
}); // 31
} else { // 32
getHeaders = headers || []; // 33
} // 34
}; // 35
// 36
/** // 37
* @method FS.HTTP.publish // 38
* @public // 39
* @param {FS.Collection} collection // 40
* @param {Function} func - Publish function that returns a cursor. // 41
* @returns {undefined} // 42
* // 43
* Publishes all documents returned by the cursor at a GET URL // 44
* with the format baseUrl/record/collectionName. The publish // 45
* function `this` is similar to normal `Meteor.publish`. // 46
*/ // 47
FS.HTTP.publish = function fsHttpPublish(collection, func) { // 48
var name = baseUrl + '/record/' + collection.name; // 49
// Mount collection listing URL using http-publish package // 50
HTTP.publish({ // 51
name: name, // 52
defaultFormat: 'fileRecordFormat', // 53
collection: collection, // 54
collectionGet: true, // 55
collectionPost: false, // 56
documentGet: true, // 57
documentPut: false, // 58
documentDelete: false // 59
}, func); // 60
// 61
FS.debug && console.log("Registered HTTP method GET URLs:\n\n" + name + '\n' + name + '/:id\n'); // 62
}; // 63
// 64
/** // 65
* @method FS.HTTP.unpublish // 66
* @public // 67
* @param {FS.Collection} collection // 68
* @returns {undefined} // 69
* // 70
* Unpublishes a restpoint created by a call to `FS.HTTP.publish` // 71
*/ // 72
FS.HTTP.unpublish = function fsHttpUnpublish(collection) { // 73
// Mount collection listing URL using http-publish package // 74
HTTP.unpublish(baseUrl + '/record/' + collection.name); // 75
}; // 76
// 77
_existingMountPoints = {}; // 78
// 79
/** // 80
* @method defaultSelectorFunction // 81
* @private // 82
* @returns { collection, file } // 83
* // 84
* This is the default selector function // 85
*/ // 86
var defaultSelectorFunction = function() { // 87
var self = this; // 88
// Selector function // 89
// // 90
// This function will have to return the collection and the // 91
// file. If file not found undefined is returned - if null is returned the // 92
// search was not possible // 93
var opts = FS.Utility.extend({}, self.query || {}, self.params || {}); // 94
// 95
// Get the collection name from the url // 96
var collectionName = opts.collectionName; // 97
// 98
// Get the id from the url // 99
var id = opts.id; // 100
// 101
// Get the collection // 102
var collection = FS._collections[collectionName]; // 103
// 104
// Get the file if possible else return null // 105
var file = (id && collection)? collection.findOne({ _id: id }): null; // 106
// 107
// Return the collection and the file // 108
return { // 109
collection: collection, // 110
file: file, // 111
storeName: opts.store, // 112
download: opts.download, // 113
filename: opts.filename // 114
}; // 115
}; // 116
// 117
/* // 118
* @method FS.HTTP.mount // 119
* @public // 120
* @param {array of string} mountPoints mount points to map rest functinality on // 121
* @param {function} selector_f [selector] function returns `{ collection, file }` for mount points to work with // 122
* // 123
*/ // 124
FS.HTTP.mount = function(mountPoints, selector_f) { // 125
// We take mount points as an array and we get a selector function // 126
var selectorFunction = selector_f || defaultSelectorFunction; // 127
// 128
var accessPoint = { // 129
'stream': true, // 130
'auth': expirationAuth, // 131
'post': function(data) { // 132
// Use the selector for finding the collection and file reference // 133
var ref = selectorFunction.call(this); // 134
// 135
// We dont support post - this would be normal insert eg. of filerecord? // 136
throw new Meteor.Error(501, "Not implemented", "Post is not supported"); // 137
}, // 138
'put': function(data) { // 139
// Use the selector for finding the collection and file reference // 140
var ref = selectorFunction.call(this); // 141
// 142
// Make sure we have a collection reference // 143
if (!ref.collection) // 144
throw new Meteor.Error(404, "Not Found", "No collection found"); // 145
// 146
// Make sure we have a file reference // 147
if (ref.file === null) { // 148
// No id supplied so we will create a new FS.File instance and // 149
// insert the supplied data. // 150
return FS.HTTP.Handlers.PutInsert.apply(this, [ref]); // 151
} else { // 152
if (ref.file) { // 153
return FS.HTTP.Handlers.PutUpdate.apply(this, [ref]); // 154
} else { // 155
throw new Meteor.Error(404, "Not Found", 'No file found'); // 156
} // 157
} // 158
}, // 159
'get': function(data) { // 160
// Use the selector for finding the collection and file reference // 161
var ref = selectorFunction.call(this); // 162
// 163
// Make sure we have a collection reference // 164
if (!ref.collection) // 165
throw new Meteor.Error(404, "Not Found", "No collection found"); // 166
// 167
// Make sure we have a file reference // 168
if (ref.file === null) { // 169
// No id supplied so we will return the published list of files ala // 170
// http.publish in json format // 171
return FS.HTTP.Handlers.GetList.apply(this, [ref]); // 172
} else { // 173
if (ref.file) { // 174
return FS.HTTP.Handlers.Get.apply(this, [ref]); // 175
} else { // 176
throw new Meteor.Error(404, "Not Found", 'No file found'); // 177
} // 178
} // 179
}, // 180
'delete': function(data) { // 181
// Use the selector for finding the collection and file reference // 182
var ref = selectorFunction.call(this); // 183
// 184
// Make sure we have a collection reference // 185
if (!ref.collection) // 186
throw new Meteor.Error(404, "Not Found", "No collection found"); // 187
// 188
// Make sure we have a file reference // 189
if (ref.file) { // 190
return FS.HTTP.Handlers.Del.apply(this, [ref]); // 191
} else { // 192
throw new Meteor.Error(404, "Not Found", 'No file found'); // 193
} // 194
} // 195
}; // 196
// 197
var accessPoints = {}; // 198
// 199
// Add debug message // 200
FS.debug && console.log('Registered HTTP method URLs:'); // 201
// 202
FS.Utility.each(mountPoints, function(mountPoint) { // 203
// Couple mountpoint and accesspoint // 204
accessPoints[mountPoint] = accessPoint; // 205
// Remember our mountpoints // 206
_existingMountPoints[mountPoint] = mountPoint; // 207
// Add debug message // 208
FS.debug && console.log(mountPoint); // 209
}); // 210
// 211
// XXX: HTTP:methods should unmount existing mounts in case of overwriting? // 212
HTTP.methods(accessPoints); // 213
// 214
}; // 215
// 216
/** // 217
* @method FS.HTTP.unmount // 218
* @public // 219
* @param {string | array of string} [mountPoints] Optional, if not specified all mountpoints are unmounted // 220
* // 221
*/ // 222
FS.HTTP.unmount = function(mountPoints) { // 223
// The mountPoints is optional, can be string or array if undefined then // 224
// _existingMountPoints will be used // 225
var unmountList; // 226
// Container for the mount points to unmount // 227
var unmountPoints = {}; // 228
// 229
if (typeof mountPoints === 'undefined') { // 230
// Use existing mount points - unmount all // 231
unmountList = _existingMountPoints; // 232
} else if (mountPoints === ''+mountPoints) { // 233
// Got a string // 234
unmountList = [mountPoints]; // 235
} else if (mountPoints.length) { // 236
// Got an array // 237
unmountList = mountPoints; // 238
} // 239
// 240
// If we have a list to unmount // 241
if (unmountList) { // 242
// Iterate over each item // 243
FS.Utility.each(unmountList, function(mountPoint) { // 244
// Check _existingMountPoints to make sure the mount point exists in our // 245
// context / was created by the FS.HTTP.mount // 246
if (_existingMountPoints[mountPoint]) { // 247
// Mark as unmount // 248
unmountPoints[mountPoint] = false; // 249
// Release // 250
delete _existingMountPoints[mountPoint]; // 251
} // 252
}); // 253
FS.debug && console.log('FS.HTTP.unmount:'); // 254
FS.debug && console.log(unmountPoints); // 255
// Complete unmount // 256
HTTP.methods(unmountPoints); // 257
} // 258
}; // 259
// 260
// ### FS.Collection maps on HTTP pr. default on the following restpoints: // 261
// * // 262
// baseUrl + '/files/:collectionName/:id/:filename', // 263
// baseUrl + '/files/:collectionName/:id', // 264
// baseUrl + '/files/:collectionName' // 265
// // 266
// Change/ replace the existing mount point by: // 267
// ```js // 268
// // unmount all existing // 269
// FS.HTTP.unmount(); // 270
// // Create new mount point // 271
// FS.HTTP.mount([ // 272
// '/cfs/files/:collectionName/:id/:filename', // 273
// '/cfs/files/:collectionName/:id', // 274
// '/cfs/files/:collectionName' // 275
// ]); // 276
// ``` // 277
// // 278
mountUrls = function mountUrls() { // 279
// We unmount first in case we are calling this a second time // 280
FS.HTTP.unmount(); // 281
// 282
FS.HTTP.mount([ // 283
baseUrl + '/files/:collectionName/:id/:filename', // 284
baseUrl + '/files/:collectionName/:id', // 285
baseUrl + '/files/:collectionName' // 286
]); // 287
}; // 288
// 289
// Returns the userId from URL token // 290
var expirationAuth = function expirationAuth() { // 291
var self = this; // 292
// 293
// Read the token from '/hello?token=base64' // 294
var encodedToken = self.query.token; // 295
// 296
FS.debug && console.log("token: "+encodedToken); // 297
// 298
if (!encodedToken || !Meteor.users) return false; // 299
// 300
// Check the userToken before adding it to the db query // 301
// Set the this.userId // 302
var tokenString = FS.Utility.atob(encodedToken); // 303
// 304
var tokenObject; // 305
try { // 306
tokenObject = JSON.parse(tokenString); // 307
} catch(err) { // 308
throw new Meteor.Error(400, 'Bad Request'); // 309
} // 310
// 311
// XXX: Do some check here of the object // 312
var userToken = tokenObject.authToken; // 313
if (userToken !== ''+userToken) { // 314
throw new Meteor.Error(400, 'Bad Request'); // 315
} // 316
// 317
// If we have an expiration token we should check that it's still valid // 318
if (tokenObject.expiration != null) { // 319
// check if its too old // 320
var now = Date.now(); // 321
if (tokenObject.expiration < now) { // 322
FS.debug && console.log('Expired token: ' + tokenObject.expiration + ' is less than ' + now); // 323
throw new Meteor.Error(500, 'Expired token'); // 324
} // 325
} // 326
// 327
// We are not on a secure line - so we have to look up the user... // 328
var user = Meteor.users.findOne({ // 329
$or: [ // 330
{'services.resume.loginTokens.hashedToken': Accounts._hashLoginToken(userToken)}, // 331
{'services.resume.loginTokens.token': userToken} // 332
] // 333
}); // 334
// 335
// Set the userId in the scope // 336
return user && user._id; // 337
}; // 338
// 339
HTTP.methods( // 340
{'/cfs/servertime': { // 341
get: function(data) { // 342
return Date.now().toString(); // 343
} // 344
} // 345
}); // 346
// 347
// Unify client / server api // 348
FS.HTTP.now = function() { // 349
return Date.now(); // 350
}; // 351
// 352
// Start up the basic mount points // 353
Meteor.startup(function () { // 354
mountUrls(); // 355
}); // 356
// 357
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}).call(this);
///////////////////////////////////////////////////////////////////////
}).call(this);
/* Exports */
if (typeof Package === 'undefined') Package = {};
Package['cfs:access-point'] = {};
})();
+238
View File
@@ -0,0 +1,238 @@
/* global JsonRoutes */
if (Meteor.isServer) {
// todo XXX once we have a real API in place, move that route there
// todo XXX also share the route definition between the client and the server
// so that we could use something like
// `ApiRoutes.path('boards/export', boardId)``
// on the client instead of copy/pasting the route path manually between the
// client and the server.
/**
* @operation export
* @tag Boards
*
* @summary This route is used to export the board.
*
* @description If user is already logged-in, pass loginToken as param
* "authToken": '/api/boards/:boardId/export?authToken=:token'
*
* See https://blog.kayla.com.au/server-side-route-authentication-in-meteor/
* for detailed explanations
*
* @param {string} boardId the ID of the board we are exporting
* @param {string} authToken the loginToken
*/
JsonRoutes.add('get', '/api/boards/:boardId/export', function(req, res) {
const boardId = req.params.boardId;
let user = null;
const loginToken = req.query.authToken;
if (loginToken) {
const hashToken = Accounts._hashLoginToken(loginToken);
user = Meteor.users.findOne({
'services.resume.loginTokens.hashedToken': hashToken,
});
} else if (!Meteor.settings.public.sandstorm) {
Authentication.checkUserId(req.userId);
user = Users.findOne({ _id: req.userId, isAdmin: true });
}
const exporter = new Exporter(boardId);
if (exporter.canExport(user)) {
JsonRoutes.sendResult(res, {
code: 200,
data: exporter.build(),
});
} else {
// we could send an explicit error message, but on the other hand the only
// way to get there is by hacking the UI so let's keep it raw.
JsonRoutes.sendResult(res, 403);
}
});
}
// exporter maybe is broken since Gridfs introduced, add fs and path
export class Exporter {
constructor(boardId) {
this._boardId = boardId;
}
build() {
const fs = Npm.require('fs');
const os = Npm.require('os');
const path = Npm.require('path');
const byBoard = { boardId: this._boardId };
const byBoardNoLinked = {
boardId: this._boardId,
linkedId: { $in: ['', null] },
};
// we do not want to retrieve boardId in related elements
const noBoardId = {
fields: {
boardId: 0,
},
};
const result = {
_format: 'wekan-board-1.0.0',
};
_.extend(
result,
Boards.findOne(this._boardId, {
fields: {
stars: 0,
},
}),
);
result.lists = Lists.find(byBoard, noBoardId).fetch();
result.cards = Cards.find(byBoardNoLinked, noBoardId).fetch();
result.swimlanes = Swimlanes.find(byBoard, noBoardId).fetch();
result.customFields = CustomFields.find(
{ boardIds: { $in: [this.boardId] } },
{ fields: { boardId: 0 } },
).fetch();
result.comments = CardComments.find(byBoard, noBoardId).fetch();
result.activities = Activities.find(byBoard, noBoardId).fetch();
result.rules = Rules.find(byBoard, noBoardId).fetch();
result.checklists = [];
result.checklistItems = [];
result.subtaskItems = [];
result.triggers = [];
result.actions = [];
result.cards.forEach(card => {
result.checklists.push(
...Checklists.find({
cardId: card._id,
}).fetch(),
);
result.checklistItems.push(
...ChecklistItems.find({
cardId: card._id,
}).fetch(),
);
result.subtaskItems.push(
...Cards.find({
parentId: card._id,
}).fetch(),
);
});
result.rules.forEach(rule => {
result.triggers.push(
...Triggers.find(
{
_id: rule.triggerId,
},
noBoardId,
).fetch(),
);
result.actions.push(
...Actions.find(
{
_id: rule.actionId,
},
noBoardId,
).fetch(),
);
});
// [Old] for attachments we only export IDs and absolute url to original doc
// [New] Encode attachment to base64
const getBase64Data = function(doc, callback) {
let buffer = new Buffer(0);
// callback has the form function (err, res) {}
const tmpFile = path.join(
os.tmpdir(),
`tmpexport${process.pid}${Math.random()}`,
);
const tmpWriteable = fs.createWriteStream(tmpFile);
const readStream = doc.createReadStream();
readStream.on('data', function(chunk) {
buffer = Buffer.concat([buffer, chunk]);
});
readStream.on('error', function(err) {
callback(err, null);
});
readStream.on('end', function() {
// done
fs.unlink(tmpFile, () => {
//ignored
});
callback(null, buffer.toString('base64'));
});
readStream.pipe(tmpWriteable);
};
const getBase64DataSync = Meteor.wrapAsync(getBase64Data);
result.attachments = Attachments.find(byBoard)
.fetch()
.map(attachment => {
return {
_id: attachment._id,
cardId: attachment.cardId,
// url: FlowRouter.url(attachment.url()),
file: getBase64DataSync(attachment),
name: attachment.original.name,
type: attachment.original.type,
};
});
// we also have to export some user data - as the other elements only
// include id but we have to be careful:
// 1- only exports users that are linked somehow to that board
// 2- do not export any sensitive information
const users = {};
result.members.forEach(member => {
users[member.userId] = true;
});
result.lists.forEach(list => {
users[list.userId] = true;
});
result.cards.forEach(card => {
users[card.userId] = true;
if (card.members) {
card.members.forEach(memberId => {
users[memberId] = true;
});
}
});
result.comments.forEach(comment => {
users[comment.userId] = true;
});
result.activities.forEach(activity => {
users[activity.userId] = true;
});
result.checklists.forEach(checklist => {
users[checklist.userId] = true;
});
const byUserIds = {
_id: {
$in: Object.getOwnPropertyNames(users),
},
};
// we use whitelist to be sure we do not expose inadvertently
// some secret fields that gets added to User later.
const userFields = {
fields: {
_id: 1,
username: 1,
'profile.fullname': 1,
'profile.initials': 1,
'profile.avatarUrl': 1,
},
};
result.users = Users.find(byUserIds, userFields)
.fetch()
.map(user => {
// user avatar is stored as a relative url, we export absolute
if ((user.profile || {}).avatarUrl) {
user.profile.avatarUrl = FlowRouter.url(user.profile.avatarUrl);
}
return user;
});
return result;
}
canExport(user) {
const board = Boards.findOne(this._boardId);
return board && board.isVisibleBy(user);
}
}
+155
View File
@@ -0,0 +1,155 @@
name: wekan
version: git
summary: The open-source kanban
description: |
Wekan is an open-source and collaborative kanban board application.
Whether you’re maintaining a personal todo list, planning your holidays with some friends, or working in a team on your next revolutionary idea, Kanban boards are an unbeatable tool to keep your things organized. They give you a visual overview of the current state of your project, and make you productive by allowing you to focus on the few items that matter the most.
Depending on target environment, some configuration settings might need to be adjusted.
For full list of configuration options call:
$ wekan.help
confinement: strict
grade: stable
base: core18
architectures:
- amd64
plugs:
mongodb-plug:
interface: content
target: $SNAP_DATA/shared
hooks:
configure:
plugs:
- network
- network-bind
slots:
mongodb-slot:
interface: content
write:
- $SNAP_DATA/share
apps:
wekan:
command: wekan-control
daemon: simple
plugs: [network, network-bind]
mongodb:
command: mongodb-control
daemon: simple
plugs: [network, network-bind]
caddy:
command: caddy-control
daemon: simple
plugs: [network, network-bind]
help:
command: wekan-help
database-backup:
command: mongodb-backup
plugs: [network, network-bind]
database-list-backups:
command: ls -al $SNAP_COMMON/db-backups/
database-restore:
command: mongodb-restore
plugs: [network, network-bind]
parts:
mongodb:
source: https://repo.mongodb.org/apt/ubuntu/dists/xenial/mongodb-org/4.2/multiverse/binary-amd64/mongodb-org-server_4.2.2_amd64.deb
#https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-4.0.14.tgz
#https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1604-3.2.22.tgz
plugin: dump
stage-packages: [libssl1.0.0, libcurl3]
filesets:
mongo:
- usr
- bin
- lib
stage:
- $mongo
prime:
- $mongo
wekan:
source: .
plugin: nodejs
node-engine: 12.14.1
node-packages:
- node-gyp
- node-pre-gyp
- fibers
build-packages:
- ca-certificates
- apt-utils
- build-essential
- python
- python3
- g++
- capnproto
- curl
- libcurl3
- execstack
- nodejs
- npm
stage-packages:
- libfontconfig1
override-build: |
echo "Cleaning environment first"
rm -rf ~/.meteor ~/.npm /usr/local/lib/node_modules
rm -rf .build
echo "Installing meteor"
curl https://install.meteor.com/ -o install_meteor.sh
chmod +x install_meteor.sh
sh install_meteor.sh
rm install_meteor.sh
rm -rf .build
meteor add standard-minifier-js --allow-superuser
meteor npm install --allow-superuser
meteor npm install --allow-superuser --save babel-runtime
meteor build .build --directory --allow-superuser
cp -f fix-download-unicode/cfs_access-point.txt .build/bundle/programs/server/packages/cfs_access-point.js
cd .build/bundle/programs/server
npm install
npm install --allow-superuser --save babel-runtime
# Change back to Wekan source directory
cd ../../../..
cp -r .build/bundle/* $SNAPCRAFT_PART_INSTALL/
cp .build/bundle/.node_version.txt $SNAPCRAFT_PART_INSTALL/
rm -f $SNAPCRAFT_PART_INSTALL/lib/node_modules/wekan
rm -f $SNAPCRAFT_PART_INSTALL/programs/server/npm/node_modules/meteor/rajit_bootstrap3-datepicker/lib/bootstrap-datepicker/node_modules/phantomjs-prebuilt/lib/phantom/bin/phantomjs
rm -f $SNAPCRAFT_PART_INSTALL/programs/server/npm/node_modules/tar/lib/.mkdir.js.swp
rm -f $SNAPCRAFT_PART_INSTALL/lib/node_modules/node-pre-gyp/node_modules/tar/lib/.mkdir.js.swp
rm -f $SNAPCRAFT_PART_INSTALL/lib/node_modules/node-gyp/node_modules/tar/lib/.mkdir.js.swp
rm -f $SNAPCRAFT_PART_INSTALL/programs/server/node_modules/node-pre-gyp/node_modules/tar/lib/.mkdir.js.swp
organize:
README: README.wekan
prime:
- -lib/node_modules/node-pre-gyp/node_modules/tar/lib/.unpack.js.swp
helpers:
source: snap-src
plugin: dump
caddy:
plugin: dump
source: https://caddyserver.com/download/linux/amd64?license=personal&telemetry=off
source-type: tar
organize:
caddy: bin/caddy
CHANGES.txt: CADDY_CHANGES.txt
EULA.txt: CADDY_EULA.txt
LICENSES.txt: CADDY_LICENSES.txt
README.txt: CADDY_README.txt
stage:
- -init
+584
View File
@@ -0,0 +1,584 @@
import ldapjs from 'ldapjs';
import util from 'util';
import Bunyan from 'bunyan';
import {log_debug, log_info, log_warn, log_error} from './logger';
export default class LDAP {
constructor() {
this.ldapjs = ldapjs;
this.connected = false;
this.options = {
host : this.constructor.settings_get('LDAP_HOST'),
port : this.constructor.settings_get('LDAP_PORT'),
Reconnect : this.constructor.settings_get('LDAP_RECONNECT'),
timeout : this.constructor.settings_get('LDAP_TIMEOUT'),
connect_timeout : this.constructor.settings_get('LDAP_CONNECT_TIMEOUT'),
idle_timeout : this.constructor.settings_get('LDAP_IDLE_TIMEOUT'),
encryption : this.constructor.settings_get('LDAP_ENCRYPTION'),
ca_cert : this.constructor.settings_get('LDAP_CA_CERT'),
reject_unauthorized : this.constructor.settings_get('LDAP_REJECT_UNAUTHORIZED') || false,
Authentication : this.constructor.settings_get('LDAP_AUTHENTIFICATION'),
Authentication_UserDN : this.constructor.settings_get('LDAP_AUTHENTIFICATION_USERDN'),
Authentication_Password : this.constructor.settings_get('LDAP_AUTHENTIFICATION_PASSWORD'),
Authentication_Fallback : this.constructor.settings_get('LDAP_LOGIN_FALLBACK'),
BaseDN : this.constructor.settings_get('LDAP_BASEDN'),
Internal_Log_Level : this.constructor.settings_get('INTERNAL_LOG_LEVEL'),
User_Authentication : this.constructor.settings_get('LDAP_USER_AUTHENTICATION'),
User_Authentication_Field : this.constructor.settings_get('LDAP_USER_AUTHENTICATION_FIELD'),
User_Attributes : this.constructor.settings_get('LDAP_USER_ATTRIBUTES'),
User_Search_Filter : this.constructor.settings_get('LDAP_USER_SEARCH_FILTER'),
User_Search_Scope : this.constructor.settings_get('LDAP_USER_SEARCH_SCOPE'),
User_Search_Field : this.constructor.settings_get('LDAP_USER_SEARCH_FIELD'),
Search_Page_Size : this.constructor.settings_get('LDAP_SEARCH_PAGE_SIZE'),
Search_Size_Limit : this.constructor.settings_get('LDAP_SEARCH_SIZE_LIMIT'),
group_filter_enabled : this.constructor.settings_get('LDAP_GROUP_FILTER_ENABLE'),
group_filter_object_class : this.constructor.settings_get('LDAP_GROUP_FILTER_OBJECTCLASS'),
group_filter_group_id_attribute : this.constructor.settings_get('LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE'),
group_filter_group_member_attribute: this.constructor.settings_get('LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE'),
group_filter_group_member_format : this.constructor.settings_get('LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT'),
group_filter_group_name : this.constructor.settings_get('LDAP_GROUP_FILTER_GROUP_NAME'),
};
}
static settings_get(name, ...args) {
let value = process.env[name];
if (value !== undefined) {
if (value === 'true' || value === 'false') {
value = JSON.parse(value);
} else if (value !== '' && !isNaN(value)) {
value = Number(value);
}
return value;
} else {
log_warn(`Lookup for unset variable: ${name}`);
}
}
connectSync(...args) {
if (!this._connectSync) {
this._connectSync = Meteor.wrapAsync(this.connectAsync, this);
}
return this._connectSync(...args);
}
searchAllSync(...args) {
if (!this._searchAllSync) {
this._searchAllSync = Meteor.wrapAsync(this.searchAllAsync, this);
}
return this._searchAllSync(...args);
}
connectAsync(callback) {
log_info('Init setup');
let replied = false;
const connectionOptions = {
url : `${this.options.host}:${this.options.port}`,
timeout : this.options.timeout,
connectTimeout: this.options.connect_timeout,
idleTimeout : this.options.idle_timeout,
reconnect : this.options.Reconnect,
};
if (this.options.Internal_Log_Level !== 'disabled') {
connectionOptions.log = new Bunyan({
name : 'ldapjs',
component: 'client',
stream : process.stderr,
level : this.options.Internal_Log_Level,
});
}
const tlsOptions = {
rejectUnauthorized: this.options.reject_unauthorized,
};
if (this.options.ca_cert && this.options.ca_cert !== '') {
// Split CA cert into array of strings
const chainLines = this.constructor.settings_get('LDAP_CA_CERT').split('\n');
let cert = [];
const ca = [];
chainLines.forEach((line) => {
cert.push(line);
if (line.match(/-END CERTIFICATE-/)) {
ca.push(cert.join('\n'));
cert = [];
}
});
tlsOptions.ca = ca;
}
if (this.options.encryption === 'ssl') {
connectionOptions.url = `ldaps://${connectionOptions.url}`;
connectionOptions.tlsOptions = tlsOptions;
} else {
connectionOptions.url = `ldap://${connectionOptions.url}`;
}
log_info('Connecting', connectionOptions.url);
log_debug(`connectionOptions${util.inspect(connectionOptions)}`);
this.client = ldapjs.createClient(connectionOptions);
this.bindSync = Meteor.wrapAsync(this.client.bind, this.client);
this.client.on('error', (error) => {
log_error('connection', error);
if (replied === false) {
replied = true;
callback(error, null);
}
});
this.client.on('idle', () => {
log_info('Idle');
this.disconnect();
});
this.client.on('close', () => {
log_info('Closed');
});
if (this.options.encryption === 'tls') {
// Set host parameter for tls.connect which is used by ldapjs starttls. This shouldn't be needed in newer nodejs versions (e.g v5.6.0).
// https://github.com/RocketChat/Rocket.Chat/issues/2035
// https://github.com/mcavage/node-ldapjs/issues/349
tlsOptions.host = this.options.host;
log_info('Starting TLS');
log_debug('tlsOptions', tlsOptions);
this.client.starttls(tlsOptions, null, (error, response) => {
if (error) {
log_error('TLS connection', error);
if (replied === false) {
replied = true;
callback(error, null);
}
return;
}
log_info('TLS connected');
this.connected = true;
if (replied === false) {
replied = true;
callback(null, response);
}
});
} else {
this.client.on('connect', (response) => {
log_info('LDAP connected');
this.connected = true;
if (replied === false) {
replied = true;
callback(null, response);
}
});
}
setTimeout(() => {
if (replied === false) {
log_error('connection time out', connectionOptions.connectTimeout);
replied = true;
callback(new Error('Timeout'));
}
}, connectionOptions.connectTimeout);
}
getUserFilter(username) {
const filter = [];
if (this.options.User_Search_Filter !== '') {
if (this.options.User_Search_Filter[0] === '(') {
filter.push(`${this.options.User_Search_Filter}`);
} else {
filter.push(`(${this.options.User_Search_Filter})`);
}
}
const usernameFilter = this.options.User_Search_Field.split(',').map((item) => `(${item}=${username})`);
if (usernameFilter.length === 0) {
log_error('LDAP_LDAP_User_Search_Field not defined');
} else if (usernameFilter.length === 1) {
filter.push(`${usernameFilter[0]}`);
} else {
filter.push(`(|${usernameFilter.join('')})`);
}
return `(&${filter.join('')})`;
}
bindUserIfNecessary(username, password) {
if (this.domainBinded === true) {
return;
}
if (!this.options.User_Authentication) {
return;
}
if (!this.options.BaseDN) throw new Error('BaseDN is not provided');
const userDn = `${this.options.User_Authentication_Field}=${username},${this.options.BaseDN}`;
this.bindSync(userDn, password);
this.domainBinded = true;
}
bindIfNecessary() {
if (this.domainBinded === true) {
return;
}
if (this.options.Authentication !== true) {
return;
}
log_info('Binding UserDN', this.options.Authentication_UserDN);
this.bindSync(this.options.Authentication_UserDN, this.options.Authentication_Password);
this.domainBinded = true;
}
searchUsersSync(username, page) {
this.bindIfNecessary();
const searchOptions = {
filter : this.getUserFilter(username),
scope : this.options.User_Search_Scope || 'sub',
sizeLimit: this.options.Search_Size_Limit,
};
if (!!this.options.User_Attributes) searchOptions.attributes = this.options.User_Attributes.split(',');
if (this.options.Search_Page_Size > 0) {
searchOptions.paged = {
pageSize : this.options.Search_Page_Size,
pagePause: !!page,
};
}
log_info('Searching user', username);
log_debug('searchOptions', searchOptions);
log_debug('BaseDN', this.options.BaseDN);
if (page) {
return this.searchAllPaged(this.options.BaseDN, searchOptions, page);
}
return this.searchAllSync(this.options.BaseDN, searchOptions);
}
getUserByIdSync(id, attribute) {
this.bindIfNecessary();
const Unique_Identifier_Field = this.constructor.settings_get('LDAP_UNIQUE_IDENTIFIER_FIELD').split(',');
let filter;
if (attribute) {
filter = new this.ldapjs.filters.EqualityFilter({
attribute,
value: new Buffer(id, 'hex'),
});
} else {
const filters = [];
Unique_Identifier_Field.forEach((item) => {
filters.push(new this.ldapjs.filters.EqualityFilter({
attribute: item,
value : new Buffer(id, 'hex'),
}));
});
filter = new this.ldapjs.filters.OrFilter({ filters });
}
const searchOptions = {
filter,
scope: 'sub',
};
log_info('Searching by id', id);
log_debug('search filter', searchOptions.filter.toString());
log_debug('BaseDN', this.options.BaseDN);
const result = this.searchAllSync(this.options.BaseDN, searchOptions);
if (!Array.isArray(result) || result.length === 0) {
return;
}
if (result.length > 1) {
log_error('Search by id', id, 'returned', result.length, 'records');
}
return result[0];
}
getUserByUsernameSync(username) {
this.bindIfNecessary();
const searchOptions = {
filter: this.getUserFilter(username),
scope : this.options.User_Search_Scope || 'sub',
};
log_info('Searching user', username);
log_debug('searchOptions', searchOptions);
log_debug('BaseDN', this.options.BaseDN);
const result = this.searchAllSync(this.options.BaseDN, searchOptions);
if (!Array.isArray(result) || result.length === 0) {
return;
}
if (result.length > 1) {
log_error('Search by username', username, 'returned', result.length, 'records');
}
return result[0];
}
getUserGroups(username, ldapUser) {
if (!this.options.group_filter_enabled) {
return true;
}
const filter = ['(&'];
if (this.options.group_filter_object_class !== '') {
filter.push(`(objectclass=${this.options.group_filter_object_class})`);
}
if (this.options.group_filter_group_member_attribute !== '') {
const format_value = ldapUser[this.options.group_filter_group_member_format];
if (format_value) {
filter.push(`(${this.options.group_filter_group_member_attribute}=${format_value})`);
}
}
filter.push(')');
const searchOptions = {
filter: filter.join('').replace(/#{username}/g, username),
scope : 'sub',
};
log_debug('Group list filter LDAP:', searchOptions.filter);
const result = this.searchAllSync(this.options.BaseDN, searchOptions);
if (!Array.isArray(result) || result.length === 0) {
return [];
}
const grp_identifier = this.options.group_filter_group_id_attribute || 'cn';
const groups = [];
result.map((item) => {
groups.push(item[grp_identifier]);
});
log_debug(`Groups: ${groups.join(', ')}`);
return groups;
}
isUserInGroup(username, ldapUser) {
if (!this.options.group_filter_enabled) {
return true;
}
const grps = this.getUserGroups(username, ldapUser);
const filter = ['(&'];
if (this.options.group_filter_object_class !== '') {
filter.push(`(objectclass=${this.options.group_filter_object_class})`);
}
if (this.options.group_filter_group_member_attribute !== '') {
const format_value = ldapUser[this.options.group_filter_group_member_format];
if (format_value) {
filter.push(`(${this.options.group_filter_group_member_attribute}=${format_value})`);
}
}
if (this.options.group_filter_group_id_attribute !== '') {
filter.push(`(${this.options.group_filter_group_id_attribute}=${this.options.group_filter_group_name})`);
}
filter.push(')');
const searchOptions = {
filter: filter.join('').replace(/#{username}/g, username),
scope : 'sub',
};
log_debug('Group filter LDAP:', searchOptions.filter);
const result = this.searchAllSync(this.options.BaseDN, searchOptions);
if (!Array.isArray(result) || result.length === 0) {
return false;
}
return true;
}
extractLdapEntryData(entry) {
const values = {
_raw: entry.raw,
};
Object.keys(values._raw).forEach((key) => {
const value = values._raw[key];
if (!['thumbnailPhoto', 'jpegPhoto'].includes(key)) {
if (value instanceof Buffer) {
values[key] = value.toString();
} else {
values[key] = value;
}
}
});
return values;
}
searchAllPaged(BaseDN, options, page) {
this.bindIfNecessary();
const processPage = ({ entries, title, end, next }) => {
log_info(title);
// Force LDAP idle to wait the record processing
this.client._updateIdle(true);
page(null, entries, {
end, next: () => {
// Reset idle timer
this.client._updateIdle();
next && next();
}
});
};
this.client.search(BaseDN, options, (error, res) => {
if (error) {
log_error(error);
page(error);
return;
}
res.on('error', (error) => {
log_error(error);
page(error);
return;
});
let entries = [];
const internalPageSize = options.paged && options.paged.pageSize > 0 ? options.paged.pageSize * 2 : 500;
res.on('searchEntry', (entry) => {
entries.push(this.extractLdapEntryData(entry));
if (entries.length >= internalPageSize) {
processPage({
entries,
title: 'Internal Page',
end : false,
});
entries = [];
}
});
res.on('page', (result, next) => {
if (!next) {
this.client._updateIdle(true);
processPage({
entries,
title: 'Final Page',
end : true,
});
} else if (entries.length) {
log_info('Page');
processPage({
entries,
title: 'Page',
end : false,
next,
});
entries = [];
}
});
res.on('end', () => {
if (entries.length) {
processPage({
entries,
title: 'Final Page',
end : true,
});
entries = [];
}
});
});
}
searchAllAsync(BaseDN, options, callback) {
this.bindIfNecessary();
this.client.search(BaseDN, options, (error, res) => {
if (error) {
log_error(error);
callback(error);
return;
}
res.on('error', (error) => {
log_error(error);
callback(error);
return;
});
const entries = [];
res.on('searchEntry', (entry) => {
entries.push(this.extractLdapEntryData(entry));
});
res.on('end', () => {
log_info('Search result count', entries.length);
callback(null, entries);
});
});
}
authSync(dn, password) {
log_info('Authenticating', dn);
try {
if (password === '') {
throw new Error('Password is not provided');
}
this.bindSync(dn, password);
log_info('Authenticated', dn);
return true;
} catch (error) {
log_info('Not authenticated', dn);
log_debug('error', error);
return false;
}
}
disconnect() {
this.connected = false;
this.domainBinded = false;
log_info('Disconecting');
this.client.unbind();
}
}
+149
View File
@@ -0,0 +1,149 @@
Oidc = {};
OAuth.registerService('oidc', 2, null, function (query) {
var debug = process.env.DEBUG || false;
var token = getToken(query);
if (debug) console.log('XXX: register token:', token);
var accessToken = token.access_token || token.id_token;
var expiresAt = (+new Date) + (1000 * parseInt(token.expires_in, 10));
var userinfo = getUserInfo(accessToken);
if (debug) console.log('XXX: userinfo:', userinfo);
var serviceData = {};
serviceData.id = userinfo[process.env.OAUTH2_ID_MAP]; // || userinfo["id"];
serviceData.username = userinfo[process.env.OAUTH2_USERNAME_MAP]; // || userinfo["uid"];
serviceData.fullname = userinfo[process.env.OAUTH2_FULLNAME_MAP]; // || userinfo["displayName"];
serviceData.accessToken = accessToken;
serviceData.expiresAt = expiresAt;
serviceData.email = userinfo[process.env.OAUTH2_EMAIL_MAP]; // || userinfo["email"];
if (accessToken) {
var tokenContent = getTokenContent(accessToken);
var fields = _.pick(tokenContent, getConfiguration().idTokenWhitelistFields);
_.extend(serviceData, fields);
}
if (token.refresh_token)
serviceData.refreshToken = token.refresh_token;
if (debug) console.log('XXX: serviceData:', serviceData);
var profile = {};
profile.name = userinfo[process.env.OAUTH2_FULLNAME_MAP]; // || userinfo["displayName"];
profile.email = userinfo[process.env.OAUTH2_EMAIL_MAP]; // || userinfo["email"];
if (debug) console.log('XXX: profile:', profile);
return {
serviceData: serviceData,
options: { profile: profile }
};
});
var userAgent = "Meteor";
if (Meteor.release) {
userAgent += "/" + Meteor.release;
}
var getToken = function (query) {
var debug = process.env.DEBUG || false;
var config = getConfiguration();
if(config.tokenEndpoint.includes('https://')){
var serverTokenEndpoint = config.tokenEndpoint;
}else{
var serverTokenEndpoint = config.serverUrl + config.tokenEndpoint;
}
var requestPermissions = config.requestPermissions;
var response;
try {
response = HTTP.post(
serverTokenEndpoint,
{
headers: {
Accept: 'application/json',
"User-Agent": userAgent
},
params: {
code: query.code,
client_id: config.clientId,
client_secret: OAuth.openSecret(config.secret),
redirect_uri: OAuth._redirectUri('oidc', config),
grant_type: 'authorization_code',
scope: requestPermissions,
state: query.state
}
}
);
} catch (err) {
throw _.extend(new Error("Failed to get token from OIDC " + serverTokenEndpoint + ": " + err.message),
{ response: err.response });
}
if (response.data.error) {
// if the http response was a json object with an error attribute
throw new Error("Failed to complete handshake with OIDC " + serverTokenEndpoint + ": " + response.data.error);
} else {
if (debug) console.log('XXX: getToken response: ', response.data);
return response.data;
}
};
var getUserInfo = function (accessToken) {
var debug = process.env.DEBUG || false;
var config = getConfiguration();
// Some userinfo endpoints use a different base URL than the authorization or token endpoints.
// This logic allows the end user to override the setting by providing the full URL to userinfo in their config.
if (config.userinfoEndpoint.includes("https://")) {
var serverUserinfoEndpoint = config.userinfoEndpoint;
} else {
var serverUserinfoEndpoint = config.serverUrl + config.userinfoEndpoint;
}
var response;
try {
response = HTTP.get(
serverUserinfoEndpoint,
{
headers: {
"User-Agent": userAgent,
"Authorization": "Bearer " + accessToken
}
}
);
} catch (err) {
throw _.extend(new Error("Failed to fetch userinfo from OIDC " + serverUserinfoEndpoint + ": " + err.message),
{response: err.response});
}
if (debug) console.log('XXX: getUserInfo response: ', response.data);
return response.data;
};
var getConfiguration = function () {
var config = ServiceConfiguration.configurations.findOne({ service: 'oidc' });
if (!config) {
throw new ServiceConfiguration.ConfigError('Service oidc not configured.');
}
return config;
};
var getTokenContent = function (token) {
var content = null;
if (token) {
try {
var parts = token.split('.');
var header = JSON.parse(new Buffer(parts[0], 'base64').toString());
content = JSON.parse(new Buffer(parts[1], 'base64').toString());
var signature = new Buffer(parts[2], 'base64');
var signed = parts[0] + '.' + parts[1];
} catch (err) {
this.content = {
exp: 0
};
}
}
return content;
}
Oidc.retrieveCredential = function (credentialToken, credentialSecret) {
return OAuth.retrieveCredential(credentialToken, credentialSecret);
};
+5184
View File
File diff suppressed because it is too large. Load diff
+73
View File
@@ -0,0 +1,73 @@
{
"name": "wekan",
"version": "v3.78.0",
"description": "Open-Source kanban",
"private": true,
"scripts": {
"lint": "eslint --cache --ext .js --ignore-path .eslintignore .",
"lint:eslint:fix": "eslint --ext .js --ignore-path .eslintignore --fix .",
"lint:staged": "lint-staged",
"prettify": "prettier --write '**/*.js' '**/*.jsx'",
"test": "npm run lint"
},
"lint-staged": {
"*.js": [
"meteor npm run prettify",
"meteor npm run lint:eslint:fix",
"git add --force"
],
"*.jsx": [
"meteor npm run prettify",
"meteor npm run lint:eslint:fix",
"git add --force"
],
"*.json": [
"prettier --write",
"git add --force"
]
},
"pre-commit": "lint:staged",
"eslintConfig": {
"extends": "@meteorjs/eslint-config-meteor"
},
"repository": {
"type": "git",
"url": "git+https://github.com/wekan/wekan.git"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/wekan/wekan/issues"
},
"homepage": "https://wekan.github.io",
"devDependencies": {
"eslint": "^5.16.0",
"eslint-config-meteor": "0.0.9",
"eslint-config-prettier": "^3.6.0",
"eslint-import-resolver-meteor": "^0.4.0",
"eslint-plugin-import": "^2.18.0",
"eslint-plugin-meteor": "^5.1.0",
"eslint-plugin-prettier": "^3.1.0",
"lint-staged": "^7.3.0",
"pre-commit": "^1.2.2",
"prettier": "^1.18.2",
"prettier-eslint": "^8.8.2"
},
"dependencies": {
"@babel/runtime": "^7.6.2",
"ajv": "^5.0.0",
"babel-runtime": "^6.26.0",
"bcrypt": "^3.0.2",
"bson": "^4.0.0",
"bunyan": "^1.8.12",
"es6-promise": "^4.2.4",
"gridfs-stream": "^0.5.3",
"ldapjs": "^1.0.2",
"meteor-node-stubs": "^0.4.1",
"mongodb": "^3.3.3",
"os": "^0.1.1",
"page": "^1.8.6",
"qs": "^6.8.0",
"source-map-support": "^0.5.12",
"xss": "^1.0.6"
}
}
+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:
+146 -2
View File
@@ -1,3 +1,147 @@
# 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:
@@ -53,7 +197,7 @@ 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).
@@ -115,7 +259,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.
+2 -2
View File
@@ -6,7 +6,7 @@ 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=v12.14.1 \
NODE_VERSION=v12.15.0 \
METEOR_RELEASE=1.9.0 \
USE_EDGE=false \
METEOR_EDGE=1.5-beta.17 \
@@ -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="" \
+1 -1
View File
@@ -1,5 +1,5 @@
appId: wekan-public/apps/77b94f60-dec9-0136-304e-16ff53095928
appVersion: "v3.68.0"
appVersion: "v3.78.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() {
+1
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({
+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-paperclip
| {{_ 'add-attachment' }}
+188 -161
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,96 @@ 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
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
h3.card-details-item-title {{_ 'description'}}
if currentBoard.allowsDescriptionText
+viewer
= getDescription
.card-details-items
.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
.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
.card-checklist-attachmentGalerys
.card-checklist-attachmentGalery.card-checklists
if currentBoard.allowsChecklists
+checklists(cardId = _id)
if currentBoard.allowsSubtasks
hr
+subtasks(cardId = _id)
if currentBoard.allowsAttachments
//- hr
//- h3
//- i.fa.fa-paperclip
//- | {{_ 'attachments'}}
.card-checklist-attachmentGalery.card-attachmentGalery
+attachmentsGalery
hr
unless currentUser.isNoComments
@@ -237,9 +263,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.
+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
+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({
+112
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'}}
@@ -197,6 +299,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 +339,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
+358
View File
@@ -208,6 +208,7 @@ Template.boardMenuPopup.events({
'click .js-outgoing-webhooks': Popup.open('outgoingWebhooks'),
'click .js-import-board': Popup.open('chooseBoardSource'),
'click .js-subtask-settings': Popup.open('boardSubtaskSettings'),
'click .js-card-settings': Popup.open('boardCardSettings'),
});
Template.boardMenuPopup.helpers({
@@ -472,6 +473,10 @@ BlazeComponent.extendComponent({
return this.currentBoard.allowsSubtasks;
},
allowsReceivedDate() {
return this.currentBoard.allowsReceivedDate;
},
isBoardSelected() {
return this.currentBoard.subtasksDefaultBoardId === this.currentData()._id;
},
@@ -585,6 +590,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('');
+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(),
);
}
});
}
+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.
+27 -2
View File
@@ -100,7 +100,7 @@ services:
#-------------------------------------------------------------------------------------
container_name: wekan-db
restart: always
command: mongod --smallfiles --oplogSize 128
command: mongod --oplogSize 128
networks:
- wekan-tier
expose:
@@ -238,7 +238,7 @@ services:
#---------------------------------------------------------------
# ==== RICH TEXT EDITOR IN CARD COMMENTS ====
# https://github.com/wekan/wekan/pull/2560
- RICHER_CARD_COMMENT_EDITOR=true
- RICHER_CARD_COMMENT_EDITOR=false
#---------------------------------------------------------------
# ==== CARD OPENED, SEND WEBHOOK MESSAGE ====
# https://github.com/wekan/wekan/issues/2518
@@ -342,6 +342,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
+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
+5 -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,7 @@
"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"
}
+5 -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,7 @@
"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"
}
+5 -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,7 @@
"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"
}
+5 -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,7 @@
"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"
}
+5 -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,7 @@
"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"
}
+5 -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,7 @@
"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"
}
+6 -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,7 @@
"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"
}
+5 -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,7 @@
"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"
}
+5 -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,7 @@
"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"
}
+5 -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,7 @@
"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"
}
+33 -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,7 @@
"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"
}
+5 -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,7 @@
"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"
}
+7 -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,7 @@
"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"
}
+5 -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,7 @@
"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"
}
+5 -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,7 @@
"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"
}
+5 -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,7 @@
"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"
}
+5 -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,7 @@
"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"
}
+5 -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,7 @@
"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"
}
+5 -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,7 @@
"hide-minicard-label-text": "הסתרת טקסט התווית של מיני כרטיס",
"show-desktop-drag-handles": "הצגת ידיות גרירה של שולחן העבודה",
"assignee": "גורם אחראי",
"cardAssigneesPopup-title": "גורם אחראי"
"cardAssigneesPopup-title": "גורם אחראי",
"addmore-detail": "הוספת תיאור מפורט",
"show-on-card": "הצגה על הכרטיס"
}
+5 -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,7 @@
"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"
}
+5 -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,7 @@
"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"
}
+5 -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,7 @@
"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"
}
+5 -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,7 @@
"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"
}
+5 -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,7 @@
"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"
}
+14 -11
View File
@@ -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": "Card Settings",
"boardSubtaskSettingsPopup-title": "Impostazioni sotto-compiti della bacheca",
"show-subtasks-field": "Le schede posso avere dei sotto-compiti",
"boardCardSettingsPopup-title": "Card Settings",
"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",
@@ -750,8 +751,10 @@
"act-atUserComment": "You were mentioned 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": "Add a more detailed description",
"show-on-card": "Show on Card"
}
+302 -299
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-removed": "チェックリストを %s から削除しました",
"activity-checklist-completed": "チェックリスト __checklist__ ボード __board__, スイムレーン __swimlane__, リスト __list__, カード __card__ が完了しました",
"activity-checklist-uncompleted": "チェックリスト %s / %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-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": "操作にはボードメンバーである必要があります",
@@ -305,28 +305,28 @@
"error-email-taken": "メールは既に受け取られています",
"export-board": "ボードのエクスポート",
"sort": "Sort",
"sort-desc": "Click to Sort List",
"sort-desc": "クリックでリストをソート",
"list-sort-by": "Sort the List By:",
"list-label-modifiedAt": "Last Access Time",
"list-label-title": "Name of the List",
"list-label-title": "リストの名前",
"list-label-sort": "Your Manual Order",
"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,13 +337,13 @@
"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-wekan": "ボードの、 'メニュー' → 'ボードのエクスポート'を選択し、ダウンロードされたファイルの中のテキストをコピーしてください。",
"import-board-instruction-about-errors": "If you get errors when importing board, sometimes importing still works, and board is at All Boards page.",
"import-json-placeholder": "JSONデータをここに貼り付けする",
"import-map-members": "メンバーを紐付け",
@@ -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",
"spent-time-hours": "作業時間(時)",
"overtime-hours": "超過時間(時)",
"overtime": "超過時間",
"has-overtime-cards": "Has overtime cards",
"has-spenttime-cards": "Has spent time 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-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,213 @@
"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",
"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-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__ 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.",
"act-atUserComment": "あなたが [__board__] __list__/__card__ に追記しました",
"delete-user-confirm-popup": "本当にこのアカウントを削除しますか?この操作は取り消しできません。",
"accounts-allowUserDelete": "ユーザー自身のアカウント削除を許可",
"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": "ミニカードのラベル名を隠す",
"show-desktop-drag-handles": "デスクトップへのドラッグハンドルを表示",
"assignee": "担当者",
"cardAssigneesPopup-title": "担当者",
"addmore-detail": "詳細説明の追加",
"show-on-card": "カードに表示する項目"
}
+5 -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,7 @@
"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"
}
+5 -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,7 @@
"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"
}
+5 -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,7 @@
"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"
}
+5 -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,7 @@
"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"
}
+5 -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,7 @@
"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"
}
+5 -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,7 @@
"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"
}
+5 -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,7 @@
"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"
}
+5 -2
View File
@@ -583,8 +583,9 @@
"default": "Standaard",
"queue": "Rij",
"subtask-settings": "Subtaak Instellingen",
"card-settings": "Card Settings",
"boardSubtaskSettingsPopup-title": "Bord Subtaak Instellingen",
"show-subtasks-field": "Kaarten kunnen subtaken hebben",
"boardCardSettingsPopup-title": "Card Settings",
"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,7 @@
"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": "Add a more detailed description",
"show-on-card": "Show on Card"
}
+5 -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,7 @@
"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"
}
+5 -2
View File
@@ -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:",
@@ -753,5 +754,7 @@
"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"
}
+5 -2
View File
@@ -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,7 @@
"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"
}
+5 -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,7 @@
"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"
}
+5 -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,7 @@
"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"
}
+5 -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,7 @@
"hide-minicard-label-text": "Скрыть текст меток на карточках",
"show-desktop-drag-handles": "Show desktop drag handles",
"assignee": "Исполнитель",
"cardAssigneesPopup-title": "Исполнитель"
"cardAssigneesPopup-title": "Исполнитель",
"addmore-detail": "Добавить более детальное описание",
"show-on-card": "Показать на карточке"
}
+5 -2
View File
@@ -583,8 +583,9 @@
"default": "Privzeto",
"queue": "Čakalna vrsta",
"subtask-settings": "Nastavitve podopravil",
"card-settings": "Nastavitve kartice",
"boardSubtaskSettingsPopup-title": "Nastavitve podopravil table",
"show-subtasks-field": "Dovoli podopravila na karticah",
"boardCardSettingsPopup-title": "Nastavitve kartice",
"deposit-subtasks-board": "Deponiraj podopravila na tablo:",
"deposit-subtasks-list": "Ciljni seznam za deponirana podopravila:",
"show-parent-in-minicard": "Pokaži starša na mini-kartici:",
@@ -753,5 +754,7 @@
"hide-minicard-label-text": "Skrij besedilo oznak na karticah",
"show-desktop-drag-handles": "Pokaži ročke za povleko na namizju",
"assignee": "Dodeljen član",
"cardAssigneesPopup-title": "Dodeljen član"
"cardAssigneesPopup-title": "Dodeljen član",
"addmore-detail": "Dodaj podrobnejši opis",
"show-on-card": "Prikaži na kartici"
}
+78 -75
View File
@@ -1,7 +1,7 @@
{
"accept": "Prihvati",
"act-activity-notify": "Activity Notification",
"act-addAttachment": "added attachment __attachment__ to card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
"act-activity-notify": "Obaveštenje o aktivnosti",
"act-addAttachment": "dodao attachment __prilog__ na kartici __kartica__ na listi __lista__ na swimlane dijagramu toka __swimlane dijagram toka__ na tabli __tabla__",
"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__",
@@ -17,14 +17,14 @@
"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-editComment": "izmenio komentar na kartici__kartica__:__komentar__na listi__lista__na swimlane dijagramu toka__swimlane dijagram toka__na tabli__tabla",
"act-deleteComment": "izbrisan komentar na kartici__kartica__:__komentar__na listi__lista__na swimlane dijagramu toka__swimlane dijagram toka__na tabli__tabla",
"act-createBoard": "kreirana tabla __tabla__",
"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-createCustomField": "kreirano prilagodjeno polje__prilagodjenoPolje__na tabli__tabla",
"act-deleteCustomField": "izbrisano prilagodjeno polje__prilagodjenoPolje__na tabli__tabla",
"act-setCustomField": "izmenjeno prilagodjeno polje__prilagodjenoPolje__:__vrednostPilagodjenogPolja__na kartici__kartica__na listi__lista__na swimlane dijagramu toka__swimlane dijagram toka__na tabli__tabla",
"act-createList": "added list __list__ to board __board__",
"act-addBoardMember": "added member __member__ to board __board__",
"act-archivedBoard": "Board __board__ moved to Archive",
@@ -46,7 +46,7 @@
"activities": "Aktivnosti",
"activity": "Aktivnost",
"activity-added": "dodao %s u %s",
"activity-archived": "%s moved to Archive",
"activity-archived": "%s premešteno u Arhivu",
"activity-attached": "prikačio %s u %s",
"activity-created": "kreirao %s",
"activity-customfield-created": "created custom field %s",
@@ -73,8 +73,8 @@
"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-editComment": "izmenjen komentar",
"activity-deleteComment": "izbrisan komentar",
"add-attachment": "Add Attachment",
"add-board": "Add Board",
"add-card": "Add Card",
@@ -84,7 +84,7 @@
"add-checklist-item": "Dodaj novu stavku u listu",
"add-cover": "Dodaj zaglavlje",
"add-label": "Add Label",
"add-list": "Add List",
"add-list": "Dodaj Listu",
"add-members": "Dodaj Članove",
"added": "Dodao",
"addMemberPopup-title": "Članovi",
@@ -111,8 +111,8 @@
"restore-board": "Restore Board",
"no-archived-boards": "No Boards in Archive.",
"archives": "Arhiviraj",
"template": "Template",
"templates": "Templates",
"template": "Obrazac",
"templates": "Obrasci",
"assign-member": "Dodeli člana",
"attached": "Prikačeno",
"attachment": "Prikačeni dokument",
@@ -120,7 +120,7 @@
"attachmentDeletePopup-title": "Obrisati prikačeni dokument ?",
"attachments": "Prikačeni dokumenti",
"auto-watch": "Automatically watch boards when they are created",
"avatar-too-big": "The avatar is too large (70KB max)",
"avatar-too-big": "Avatar je prevelik (maksimum je 70KB)",
"back": "Nazad",
"board-change-color": "Promeni boju",
"board-nb-stars": "%s zvezdice",
@@ -137,7 +137,7 @@
"board-view": "Board View",
"board-view-cal": "Calendar",
"board-view-swimlanes": "Swimlanes",
"board-view-collapse": "Collapse",
"board-view-collapse": "Sažmi",
"board-view-lists": "Lists",
"bucket-example": "Na primer \"Lista zadataka\"",
"cancel": "Otkaži",
@@ -223,8 +223,8 @@
"comment-only-desc": "Can comment on cards only.",
"no-comments": "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.",
"worker": "Radnik",
"worker-desc": "Može samo da pomera kartice, dodeljuje sebe kartici i da komentariše. ",
"computer": "Computer",
"confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
"confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
@@ -304,24 +304,24 @@
"error-username-taken": "Korisničko ime je već zauzeto",
"error-email-taken": "Email has already been taken",
"export-board": "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",
"list-label-short-modifiedAt": "(L)",
"sort": "Sortiraj",
"sort-desc": "Kliknite da biste sortirali listu",
"list-sort-by": "Poredaj listu po:",
"list-label-modifiedAt": "Poslednje vreme pristupa",
"list-label-title": "Naziv liste",
"list-label-sort": "Vaš ručni nalog",
"list-label-short-modifiedAt": "(P)",
"list-label-short-title": "(N)",
"list-label-short-sort": "(M)",
"list-label-short-sort": "(R)",
"filter": "Filter",
"filter-cards": "Filter Cards or Lists",
"list-filter-label": "Filter List by Title",
"filter-cards": "Filtriraj kartice ili liste",
"list-filter-label": "Filtriraj liste po naslovu",
"filter-clear": "Clear filter",
"filter-no-label": "Nema oznake",
"filter-no-member": "Nema člana",
"filter-no-custom-fields": "No Custom Fields",
"filter-show-archive": "Show archived lists",
"filter-hide-empty": "Hide empty lists",
"filter-show-archive": "Prikaži arhivirane liste",
"filter-hide-empty": "Sakrij prazne liste",
"filter-on": "Filter is on",
"filter-on-desc": "You are filtering cards on this board. Click here to edit filter.",
"filter-to-selection": "Filter to selection",
@@ -440,7 +440,7 @@
"save": "Snimi",
"search": "Pretraga",
"rules": "Rules",
"search-cards": "Search from card/list titles and descriptions on this board",
"search-cards": "Pretraži naslove sa kartica/listi i opisa na ovoj tabli",
"search-example": "Text to search for?",
"select-color": "Select Color",
"set-wip-limit-value": "Set a limit for the maximum number of tasks in this list",
@@ -524,21 +524,21 @@
"email-smtp-test-text": "You have successfully sent an email",
"error-invitation-code-not-exist": "Invitation code doesn't exist",
"error-notAuthorized": "You are not authorized to view this page.",
"webhook-title": "Webhook Name",
"webhook-token": "Token (Optional for Authentication)",
"webhook-title": "Naziv mrežne kuke",
"webhook-token": "Token (neobavezno za autentifikaciju)",
"outgoing-webhooks": "Outgoing Webhooks",
"bidirectional-webhooks": "Two-Way Webhooks",
"bidirectional-webhooks": "Dvosmerne mrežne kuke",
"outgoingWebhooksPopup-title": "Outgoing Webhooks",
"boardCardTitlePopup-title": "Card Title Filter",
"disable-webhook": "Disable This Webhook",
"global-webhook": "Global Webhooks",
"disable-webhook": "Onesposobi ovu mrežnu kuku",
"global-webhook": "Globalna mrežna kuka",
"new-outgoing-webhook": "New Outgoing Webhook",
"no-name": "(Unknown)",
"Node_version": "Node version",
"Meteor_version": "Meteor version",
"MongoDB_version": "MongoDB version",
"MongoDB_storage_engine": "MongoDB storage engine",
"MongoDB_Oplog_enabled": "MongoDB Oplog enabled",
"Meteor_version": "Verzija Meteor-a",
"MongoDB_version": "Verzija MongoDB-a",
"MongoDB_storage_engine": "MongoDB mehanizam za skladištenje",
"MongoDB_Oplog_enabled": "MongoDB Oplog omogućen",
"OS_Arch": "OS Arch",
"OS_Cpus": "OS CPU Count",
"OS_Freemem": "OS Free Memory",
@@ -573,7 +573,7 @@
"setCardActionsColorPopup-title": "Choose a color",
"setSwimlaneColorPopup-title": "Choose a color",
"setListColorPopup-title": "Choose a color",
"assigned-by": "Assigned By",
"assigned-by": "Dodeljeno od strane",
"requested-by": "Requested By",
"board-delete-notice": "Deleting is permanent. You will lose all lists, cards and actions associated with this board.",
"delete-board-confirm-popup": "All lists, cards, labels, and activities will be deleted and you won't be able to recover the board contents. There is no undo.",
@@ -583,8 +583,9 @@
"default": "Default",
"queue": "Queue",
"subtask-settings": "Subtasks Settings",
"card-settings": "Podešavanja kartice",
"boardSubtaskSettingsPopup-title": "Board Subtasks Settings",
"show-subtasks-field": "Cards can have subtasks",
"boardCardSettingsPopup-title": "Podešavanja kartice",
"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:",
@@ -700,15 +701,15 @@
"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-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": "Postavi",
"r-update": "Ažuriraj",
"r-datefield": "polje za datum",
"r-df-start-at": "početak",
"r-df-due-at": "krajnji datum",
"r-df-end-at": "kraj",
"r-df-received-at": "primljen",
"r-to-current-datetime": "do trenutnog datuma/vremena",
"r-remove-value-from": "Ukloni vrednost sa",
"ldap": "LDAP",
"oauth2": "OAuth2",
"cas": "CAS",
@@ -730,28 +731,30 @@
"restore-all": "Restore all",
"delete-all": "Delete all",
"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__",
"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": "prošli put je bio",
"act-a-dueAt": "modifikovano u vreme\nKada:__vremenskaVrednost__\nGde:__kartica__\nprethodni rok je bio__StaraVremenskaVrednost",
"act-a-endAt": "izmenjeno vreme završetaka za__vremenskaVrednost__od (__StaraVremenskaVrednost__)",
"act-a-startAt": "izmenjeno vreme početka u__vremenskaVrednost__od (__StaraVremenskaVrednost__)",
"act-a-receivedAt": "izmenjeno primljeno vreme u__vremenskaVrednost__od (__StaraVremenskaVrednost__)",
"a-dueAt": "izmenjen krajnji datum da bude",
"a-endAt": "izmenjeno vreme završetka",
"a-startAt": "izmenjeno vreme početka",
"a-receivedAt": "izmenjeno primljeno vreme da bude",
"almostdue": "trenutni krajnji datum %s se približava",
"pastdue": "trenutni krajnji datum %s je prošao",
"duenow": "trenutni krajnji datum %s je danas",
"act-newDue": "__lista__/__kartica__ima 1. podsetnik za krajnji datum [__tabla__]",
"act-withDue": "__lista__/__kartica__ podsetnici za krajnji datum [__tabla__]",
"act-almostdue": "podsećao da se trenutni krajnji rok (__vremenskaVrednost__) __kartica__ približava",
"act-pastdue": "podsećao da je trenutni krajnji rok (__vremenskaVrednost__) __kartica__ prošao",
"act-duenow": "podsećao da je trenutni krajnji rok (__vremenskaVrednost__) __kartica__ trenutno",
"act-atUserComment": "Spomenuti ste na [__tabla__] __lista__/__kartica__",
"delete-user-confirm-popup": "Da li ste sigurni da želite da izbrišete nalog? Nema poništenja akcije. ",
"accounts-allowUserDelete": "Dozvoli korisnicima da sami brišu svoj nalog",
"hide-minicard-label-text": "Sakrij tekst nalepnice minikartice",
"show-desktop-drag-handles": "Prikaži kvake za povlačenje sa radne površine",
"assignee": "Asignat",
"cardAssigneesPopup-title": "Asignat",
"addmore-detail": "Dodaj detaljaniji opis",
"show-on-card": "Prikaži na kartici"
}
+5 -2
View File
@@ -583,8 +583,9 @@
"default": "Standard",
"queue": "Kö",
"subtask-settings": "Deluppgift inställningar",
"card-settings": "Card Settings",
"boardSubtaskSettingsPopup-title": "Deluppgiftsinställningar för anslagstavla",
"show-subtasks-field": "Kort kan ha deluppgifter",
"boardCardSettingsPopup-title": "Card Settings",
"deposit-subtasks-board": "Insättnings deluppgifter på denna anslagstavla:",
"deposit-subtasks-list": "Landningslista för deluppgifter deponerade här:",
"show-parent-in-minicard": "Visa förälder i minikort:",
@@ -753,5 +754,7 @@
"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"
}
+5 -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,7 @@
"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"
}
+5 -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,7 @@
"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"
}
+5 -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,7 @@
"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"
}
+5 -2
View File
@@ -583,8 +583,9 @@
"default": "Varsayılan",
"queue": "Sıra",
"subtask-settings": "Alt Görev ayarları",
"card-settings": "Card Settings",
"boardSubtaskSettingsPopup-title": "Pano alt görev ayarları",
"show-subtasks-field": "Kartların alt görevleri olabilir",
"boardCardSettingsPopup-title": "Card Settings",
"deposit-subtasks-board": "Deposit subtasks to this board:",
"deposit-subtasks-list": "Alt görevlerin açılacağı liste:",
"show-parent-in-minicard": "Mini kart içinde üst kartı göster",
@@ -753,5 +754,7 @@
"hide-minicard-label-text": "Mini kart etiklerini gizle",
"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"
}
+11 -8
View File
@@ -27,11 +27,11 @@
"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-archivedBoard": "Дошку __board__ переміщено до Архіву",
"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-importBoard": "Імпортовано дошку __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__",
@@ -73,13 +73,13 @@
"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",
"add-attachment": "Add Attachment",
"activity-editComment": "відредаговано коментар %s",
"activity-deleteComment": "видалено коментар %s",
"add-attachment": "Додати вкладення",
"add-board": "Додати дошку",
"add-card": "Додати картку",
"add-swimlane": "Add Swimlane",
"add-subtask": "Add Subtask",
"add-subtask": "Додати підзадачу",
"add-checklist": "Add Checklist",
"add-checklist-item": "Додати елемент в список",
"add-cover": "Додати обкладинку",
@@ -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,7 @@
"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"
}
+5 -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,7 @@
"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"
}
+5 -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,7 @@
"hide-minicard-label-text": "隐藏迷你卡片标签文本",
"show-desktop-drag-handles": "显示桌面拖放手柄",
"assignee": "被指派人",
"cardAssigneesPopup-title": "被指派人"
"cardAssigneesPopup-title": "被指派人",
"addmore-detail": "Add a more detailed description",
"show-on-card": "Show on Card"
}
+5 -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,7 @@
"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"
}
+5 -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,7 @@
"hide-minicard-label-text": "隱藏迷你卡片標籤內文",
"show-desktop-drag-handles": "顯示桌面拖曳工具",
"assignee": "代理人",
"cardAssigneesPopup-title": "代理人"
"cardAssigneesPopup-title": "代理人",
"addmore-detail": "Add a more detailed description",
"show-on-card": "Show on Card"
}
Loaded 100 of 126 files, more files were not shown because too many files have changed in this diff. Show more