Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a7cb809f4 | ||
|
|
3244c3c32b | ||
|
|
09666585e2 | ||
|
|
ee106d1cb4 | ||
|
|
8ef435c83e | ||
|
|
3a74b88294 | ||
|
|
c5079096b5 | ||
|
|
927e43ab3f | ||
|
|
eddcb2260b | ||
|
|
ff73aa4566 | ||
|
|
de993a079d | ||
|
|
7bb0aa7488 | ||
|
|
fe7398faef | ||
|
|
8e709c1b79 | ||
|
|
3908fe0ab8 | ||
|
|
5d2ff89e9b | ||
|
|
7f38bc7b95 | ||
|
|
ed0c64a270 | ||
|
|
e63e7b1131 | ||
|
|
3f7d9fd42c | ||
|
|
867344f070 | ||
|
|
a84195179e | ||
|
|
f1b18d79cd | ||
|
|
6d1cdebfe2 | ||
|
|
7cf8487f57 | ||
|
|
5403027c9d | ||
|
|
daf397a1ba | ||
|
|
6104c1711f | ||
|
|
db41e41d90 | ||
|
|
1235363465 | ||
|
|
7263d7497f | ||
|
|
666186fee8 | ||
|
|
27d1b5acca | ||
|
|
5eb5ca5752 | ||
|
|
16d371b814 | ||
|
|
df9b4db49d | ||
|
|
2e8e58004d | ||
|
|
a83aa6162b | ||
|
|
07c915ccf0 | ||
|
|
405f176bbb | ||
|
|
6476503137 | ||
|
|
981ed546f1 | ||
|
|
c1287248a6 | ||
|
|
52f884f2b1 | ||
|
|
4550e10fbe | ||
|
|
2efe974195 | ||
|
|
6b808dd74b | ||
|
|
8a917e0efc | ||
|
|
d1f98d0c47 | ||
|
|
323de77a83 | ||
|
|
738e7f9ebc | ||
|
|
b5b4af73a9 | ||
|
|
ab1014b7d8 | ||
|
|
12964aa742 | ||
|
|
e4b1106acd | ||
|
|
d0524532a4 | ||
|
|
7de18eccea | ||
|
|
f9747bc908 | ||
|
|
6fd827792a | ||
|
|
9e95c06415 | ||
|
|
e7603298d7 | ||
|
|
8e14459cff | ||
|
|
3ac5dba243 | ||
|
|
9969c89b1c | ||
|
|
5a5b19d4fc | ||
|
|
4d48fa4d95 | ||
|
|
57545f66d2 | ||
|
|
8586c843a4 | ||
|
|
b2acc3ba45 | ||
|
|
9f0273aa44 | ||
|
|
3407b0e65b | ||
|
|
6cd92ab2c6 | ||
|
|
e92ebe5151 | ||
|
|
8cb838c0de | ||
|
|
6cae14d6e9 | ||
|
|
b42d8346cd | ||
|
|
1a065ff351 | ||
|
|
3565ff2700 | ||
|
|
e354715a9d | ||
|
|
e7002f417b | ||
|
|
ef5f38f431 | ||
|
|
b3efa71d13 | ||
|
|
10fcc19b7f | ||
|
|
948f05fecb | ||
|
|
927bb83813 | ||
|
|
9f396e9038 | ||
|
|
2400c91013 | ||
|
|
f583645744 | ||
|
|
42b3cff99f | ||
|
|
960fe5163b | ||
|
|
41665c079d | ||
|
|
8a0defc988 | ||
|
|
d7e0350ecf | ||
|
|
bf15fa988d | ||
|
|
db2d59181c | ||
|
|
dcc9a0d498 | ||
|
|
936dea591a | ||
|
|
cf7e5472ba | ||
|
|
5c61119716 | ||
|
|
3558d711ff | ||
|
|
72c1c49da3 | ||
|
|
93f8ff946f | ||
|
|
61d582ffd9 | ||
|
|
60384be5ff | ||
|
|
8ac305778a | ||
|
|
5be93741ee | ||
|
|
c2f91baaf9 | ||
|
|
9df5c0e2de | ||
|
|
f1a9e30956 | ||
|
|
4b202aaa08 | ||
|
|
5ef1a92366 | ||
|
|
1a836969e1 | ||
|
|
079b3da6ef | ||
|
|
13563e20e7 | ||
|
|
4f1330c777 | ||
|
|
94b558cf43 | ||
|
|
e22e24d917 | ||
|
|
55545a52ca | ||
|
|
e0cb5a0c7a | ||
|
|
efd3faf9ef | ||
|
|
2fd577d2c0 | ||
|
|
659e8418f7 | ||
|
|
18610d2fe6 | ||
|
|
1389ed0fce | ||
|
|
c39364af83 | ||
|
|
3e817257ef | ||
|
|
c5f782976b | ||
|
|
b841f798f8 | ||
|
|
35ae07e2a6 | ||
|
|
2c792363bf | ||
|
|
a68ecc079d | ||
|
|
b2d546579c |
No files matched your search
@@ -11,6 +11,7 @@
|
||||
"browser": true,
|
||||
"meteor": true
|
||||
},
|
||||
"parser": "babel-eslint",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2018,
|
||||
"sourceType": "module"
|
||||
|
||||
@@ -65,9 +65,9 @@ apps:
|
||||
|
||||
parts:
|
||||
mongodb:
|
||||
source: https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1604-3.2.22.tgz
|
||||
source: https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1604-4.2.6.tgz
|
||||
plugin: dump
|
||||
stage-packages: [libssl1.0.0]
|
||||
stage-packages: [libssl1.0.0, libcurl3]
|
||||
filesets:
|
||||
mongo:
|
||||
- usr
|
||||
@@ -81,19 +81,20 @@ parts:
|
||||
wekan:
|
||||
source: .
|
||||
plugin: nodejs
|
||||
node-engine: 8.17.0
|
||||
node-engine: 12.16.2
|
||||
node-packages:
|
||||
- node-gyp
|
||||
- node-pre-gyp
|
||||
- fibers@2.0.0
|
||||
- fibers
|
||||
build-packages:
|
||||
- ca-certificates
|
||||
- apt-utils
|
||||
- python
|
||||
# - python3
|
||||
- python3
|
||||
- g++
|
||||
- capnproto
|
||||
- curl
|
||||
- libcurl3
|
||||
- execstack
|
||||
- nodejs
|
||||
- npm
|
||||
@@ -104,6 +105,18 @@ parts:
|
||||
rm -rf ~/.meteor ~/.npm /usr/local/lib/node_modules
|
||||
# Create the OpenAPI specification
|
||||
rm -rf .build
|
||||
## Use Meteor 1.8.x on Snap
|
||||
#rm -rf .meteor
|
||||
#mv .snap-meteor-1.8/.meteor .
|
||||
#mv .snap-meteor-1.8/package.json .
|
||||
#mv .snap-meteor-1.8/package-lock.json .
|
||||
## Meteor 1.9.x has changes to Buffer() => Buffer.alloc(), so reverting those
|
||||
#mv .snap-meteor-1.8/cfs_access-point.txt fix-download-unicode/
|
||||
#mv .snap-meteor-1.8/export.js models/
|
||||
#mv .snap-meteor-1.8/wekanCreator.js models/
|
||||
#mv .snap-meteor-1.8/ldap.js packages/wekan-ldap/server/ldap.js
|
||||
#mv .snap-meteor-1.8/oidc_server.js packages/wekan-oidc/oidc_server.js
|
||||
rm -rf .snap-meteor-1.8
|
||||
#mkdir -p .build/python
|
||||
#cd .build/python
|
||||
#git clone --depth 1 -b master https://github.com/Kronuz/esprima-python
|
||||
File renamed without changes.
+3
-3
@@ -6,7 +6,7 @@
|
||||
meteor-base@1.4.0
|
||||
|
||||
# Build system
|
||||
ecmascript@0.14.2
|
||||
ecmascript@0.14.3
|
||||
standard-minifier-css@1.6.0
|
||||
standard-minifier-js@2.6.0
|
||||
mquandalle:jade
|
||||
@@ -23,7 +23,7 @@ dburles:collection-helpers
|
||||
idmontie:migrations
|
||||
matb33:collection-hooks
|
||||
matteodem:easy-search
|
||||
mongo@1.9.0
|
||||
mongo@1.10.0
|
||||
mquandalle:collection-mutations
|
||||
|
||||
# Account system
|
||||
@@ -72,7 +72,7 @@ simple:rest-accounts-password
|
||||
useraccounts:core
|
||||
email@1.2.3
|
||||
horka:swipebox
|
||||
dynamic-import@0.5.1
|
||||
dynamic-import@0.5.2
|
||||
staringatlights:fast-render
|
||||
|
||||
accounts-password@1.6.0
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
METEOR@1.10.1
|
||||
METEOR@1.10.2
|
||||
+5
-5
@@ -104,13 +104,13 @@ meteorspark:util@0.2.0
|
||||
minifier-css@1.5.0
|
||||
minifier-js@2.6.0
|
||||
minifiers@1.1.8-faster-rebuild.0
|
||||
minimongo@1.5.0
|
||||
minimongo@1.6.0
|
||||
mobile-status-bar@1.1.0
|
||||
modern-browsers@0.1.5
|
||||
modules@0.15.0
|
||||
modules-runtime@0.12.0
|
||||
momentjs:moment@2.24.0
|
||||
mongo@1.9.1
|
||||
mongo@1.10.0
|
||||
mongo-decimal@0.1.1
|
||||
mongo-dev-server@1.1.0
|
||||
mongo-id@1.0.7
|
||||
@@ -162,11 +162,11 @@ 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.3
|
||||
socket-stream-client@0.3.0
|
||||
softwarerero:accounts-t9n@1.3.11
|
||||
spacebars@1.0.15
|
||||
spacebars-compiler@1.1.3
|
||||
srp@1.0.12
|
||||
srp@1.1.0
|
||||
standard-minifier-css@1.6.0
|
||||
standard-minifier-js@2.6.0
|
||||
staringatlights:fast-render@3.2.0
|
||||
@@ -181,7 +181,7 @@ tracker@1.2.0
|
||||
twbs:bootstrap@3.3.6
|
||||
ui@1.0.13
|
||||
underscore@1.0.10
|
||||
url@1.2.0
|
||||
url@1.3.0
|
||||
useraccounts:core@1.14.2
|
||||
useraccounts:flow-routing@1.14.2
|
||||
useraccounts:unstyled@1.14.2
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
# 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
|
||||
@@ -1,2 +0,0 @@
|
||||
dev_bundle
|
||||
local
|
||||
@@ -1,7 +0,0 @@
|
||||
# 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.
@@ -1,100 +0,0 @@
|
||||
# Meteor packages used by this project, one per line.
|
||||
#
|
||||
# 'meteor add' and 'meteor remove' will edit this file for you,
|
||||
# but you can also edit it by hand.
|
||||
|
||||
meteor-base@1.4.0
|
||||
|
||||
# Build system
|
||||
ecmascript@0.13.2
|
||||
standard-minifier-css@1.5.4
|
||||
standard-minifier-js@2.5.2
|
||||
mquandalle:jade
|
||||
|
||||
# Polyfills
|
||||
es5-shim@4.8.0
|
||||
|
||||
# Collections
|
||||
aldeed:collection2
|
||||
cfs:standard-packages
|
||||
cottz:publish-relations
|
||||
dburles:collection-helpers
|
||||
idmontie:migrations
|
||||
matb33:collection-hooks
|
||||
matteodem:easy-search
|
||||
mongo@1.7.0
|
||||
mquandalle:collection-mutations
|
||||
|
||||
# Account system
|
||||
kenton:accounts-sandstorm
|
||||
service-configuration@1.0.11
|
||||
useraccounts:unstyled
|
||||
useraccounts:flow-routing
|
||||
wekan-ldap
|
||||
wekan-accounts-cas
|
||||
wekan-accounts-oidc
|
||||
|
||||
# Utilities
|
||||
check@1.3.1
|
||||
jquery@1.11.10
|
||||
random@1.1.0
|
||||
reactive-dict@1.3.0
|
||||
session@1.2.0
|
||||
tracker@1.2.0
|
||||
underscore@1.0.10
|
||||
3stack:presence
|
||||
alethes:pages
|
||||
arillo:flow-router-helpers
|
||||
audit-argument-checks@1.0.7
|
||||
kadira:blaze-layout
|
||||
kadira:dochead
|
||||
mquandalle:autofocus
|
||||
ongoworks:speakingurl
|
||||
raix:handlebar-helpers
|
||||
tap:i18n
|
||||
http@1.4.2
|
||||
|
||||
# UI components
|
||||
blaze
|
||||
reactive-var@1.0.11
|
||||
fortawesome:fontawesome
|
||||
mousetrap:mousetrap
|
||||
mquandalle:jquery-textcomplete
|
||||
mquandalle:jquery-ui-drag-drop-sort
|
||||
mquandalle:mousetrap-bindglobal
|
||||
peerlibrary:blaze-components@=0.15.1
|
||||
templates:tabs
|
||||
verron:autosize
|
||||
simple:json-routes
|
||||
rajit:bootstrap3-datepicker
|
||||
shell-server@0.4.0
|
||||
simple:rest-accounts-password
|
||||
useraccounts:core
|
||||
email@1.2.3
|
||||
horka:swipebox
|
||||
dynamic-import@0.5.1
|
||||
staringatlights:fast-render
|
||||
|
||||
accounts-password@1.5.2
|
||||
cfs:gridfs
|
||||
rzymek:fullcalendar
|
||||
momentjs:moment@2.22.2
|
||||
browser-policy-framing@1.1.0
|
||||
mquandalle:moment
|
||||
msavin:usercache
|
||||
wekan-scrollbar
|
||||
mquandalle:perfect-scrollbar
|
||||
mdg:meteor-apm-agent@3.2.0-rc.0!
|
||||
# Keep stylus in 1.1.0, because building v2 takes extra 52 minutes.
|
||||
coagmano:stylus@1.1.0!
|
||||
lucasantoniassi:accounts-lockout
|
||||
meteorhacks:subs-manager
|
||||
meteorhacks:picker
|
||||
lamhieu:unblock
|
||||
meteorhacks:aggregate@1.3.0
|
||||
wekan-markdown
|
||||
konecty:mongo-counter
|
||||
percolate:synced-cron
|
||||
easylogic:summernote
|
||||
cfs:filesystem
|
||||
ostrio:cookies
|
||||
@@ -1,2 +0,0 @@
|
||||
server
|
||||
browser
|
||||
@@ -1 +0,0 @@
|
||||
METEOR@1.8.3
|
||||
@@ -1,198 +0,0 @@
|
||||
3stack:presence@1.1.2
|
||||
accounts-base@1.4.5
|
||||
accounts-oauth@1.1.16
|
||||
accounts-password@1.5.2
|
||||
aldeed:collection2@2.10.0
|
||||
aldeed:collection2-core@1.2.0
|
||||
aldeed:schema-deny@1.1.0
|
||||
aldeed:schema-index@1.1.1
|
||||
aldeed:simple-schema@1.5.4
|
||||
alethes:pages@1.8.6
|
||||
allow-deny@1.1.0
|
||||
arillo:flow-router-helpers@0.5.2
|
||||
audit-argument-checks@1.0.7
|
||||
autoupdate@1.6.0
|
||||
babel-compiler@7.4.2
|
||||
babel-runtime@1.4.0
|
||||
base64@1.0.12
|
||||
binary-heap@1.0.11
|
||||
blaze@2.3.4
|
||||
blaze-tools@1.0.10
|
||||
boilerplate-generator@1.6.0
|
||||
browser-policy-common@1.0.11
|
||||
browser-policy-framing@1.1.0
|
||||
caching-compiler@1.2.1
|
||||
caching-html-compiler@1.1.3
|
||||
callback-hook@1.2.0
|
||||
cfs:access-point@0.1.49
|
||||
cfs:base-package@0.0.30
|
||||
cfs:collection@0.5.5
|
||||
cfs:collection-filters@0.2.4
|
||||
cfs:data-man@0.0.6
|
||||
cfs:file@0.1.17
|
||||
cfs:filesystem@0.1.2
|
||||
cfs:gridfs@0.0.34
|
||||
cfs:http-methods@0.0.32
|
||||
cfs:http-publish@0.0.13
|
||||
cfs:power-queue@0.9.11
|
||||
cfs:reactive-list@0.0.9
|
||||
cfs:reactive-property@0.0.4
|
||||
cfs:standard-packages@0.5.10
|
||||
cfs:storage-adapter@0.2.4
|
||||
cfs:tempstore@0.1.6
|
||||
cfs:upload-http@0.0.20
|
||||
cfs:worker@0.1.5
|
||||
check@1.3.1
|
||||
chuangbo:cookie@1.1.0
|
||||
coagmano:stylus@1.1.0
|
||||
coffeescript@1.0.17
|
||||
cottz:publish-relations@2.0.8
|
||||
dburles:collection-helpers@1.1.0
|
||||
ddp@1.4.0
|
||||
ddp-client@2.3.3
|
||||
ddp-common@1.4.0
|
||||
ddp-rate-limiter@1.0.7
|
||||
ddp-server@2.3.0
|
||||
deps@1.0.12
|
||||
diff-sequence@1.1.1
|
||||
dynamic-import@0.5.1
|
||||
easylogic:summernote@0.8.8
|
||||
ecmascript@0.13.2
|
||||
ecmascript-runtime@0.7.0
|
||||
ecmascript-runtime-client@0.9.0
|
||||
ecmascript-runtime-server@0.8.0
|
||||
ejson@1.1.1
|
||||
email@1.2.3
|
||||
es5-shim@4.8.0
|
||||
fastclick@1.0.13
|
||||
fetch@0.1.1
|
||||
fortawesome:fontawesome@4.7.0
|
||||
geojson-utils@1.0.10
|
||||
horka:swipebox@1.0.2
|
||||
hot-code-push@1.0.4
|
||||
html-tools@1.0.11
|
||||
htmljs@1.0.11
|
||||
http@1.4.2
|
||||
id-map@1.1.0
|
||||
idmontie:migrations@1.0.3
|
||||
inter-process-messaging@0.1.0
|
||||
jquery@1.11.11
|
||||
kadira:blaze-layout@2.3.0
|
||||
kadira:dochead@1.5.0
|
||||
kadira:flow-router@2.12.1
|
||||
kenton:accounts-sandstorm@0.7.0
|
||||
konecty:mongo-counter@0.0.5_3
|
||||
lamhieu:meteorx@2.1.1
|
||||
lamhieu:unblock@1.0.0
|
||||
launch-screen@1.1.1
|
||||
livedata@1.0.18
|
||||
localstorage@1.2.0
|
||||
logging@1.1.20
|
||||
lucasantoniassi:accounts-lockout@1.0.0
|
||||
matb33:collection-hooks@0.9.1
|
||||
matteodem:easy-search@1.6.4
|
||||
mdg:meteor-apm-agent@3.2.5
|
||||
mdg:validation-error@0.5.1
|
||||
meteor@1.9.3
|
||||
meteor-base@1.4.0
|
||||
meteor-platform@1.2.6
|
||||
meteorhacks:aggregate@1.3.0
|
||||
meteorhacks:collection-utils@1.2.0
|
||||
meteorhacks:picker@1.0.3
|
||||
meteorhacks:subs-manager@1.6.4
|
||||
meteorspark:util@0.2.0
|
||||
minifier-css@1.4.3
|
||||
minifier-js@2.5.1
|
||||
minifiers@1.1.8-faster-rebuild.0
|
||||
minimongo@1.4.5
|
||||
mobile-status-bar@1.0.14
|
||||
modern-browsers@0.1.4
|
||||
modules@0.14.0
|
||||
modules-runtime@0.11.0
|
||||
momentjs:moment@2.24.0
|
||||
mongo@1.7.0
|
||||
mongo-decimal@0.1.1
|
||||
mongo-dev-server@1.1.0
|
||||
mongo-id@1.0.7
|
||||
mongo-livedata@1.0.12
|
||||
mousetrap:mousetrap@1.4.6_1
|
||||
mquandalle:autofocus@1.0.0
|
||||
mquandalle:collection-mutations@0.1.0
|
||||
mquandalle:jade@0.4.9
|
||||
mquandalle:jade-compiler@0.4.5
|
||||
mquandalle:jquery-textcomplete@0.8.0_1
|
||||
mquandalle:jquery-ui-drag-drop-sort@0.2.0
|
||||
mquandalle:moment@1.0.1
|
||||
mquandalle:mousetrap-bindglobal@0.0.1
|
||||
mquandalle:perfect-scrollbar@0.6.5_2
|
||||
msavin:usercache@1.8.0
|
||||
npm-bcrypt@0.9.3
|
||||
npm-mongo@3.2.0
|
||||
oauth@1.2.8
|
||||
oauth2@1.2.1
|
||||
observe-sequence@1.0.16
|
||||
ongoworks:speakingurl@1.1.0
|
||||
ordered-dict@1.1.0
|
||||
ostrio:cookies@2.5.0
|
||||
peerlibrary:assert@0.3.0
|
||||
peerlibrary:base-component@0.16.0
|
||||
peerlibrary:blaze-components@0.15.1
|
||||
peerlibrary:computed-field@0.10.0
|
||||
peerlibrary:reactive-field@0.6.0
|
||||
percolate:synced-cron@1.3.2
|
||||
promise@0.11.2
|
||||
raix:eventemitter@0.1.3
|
||||
raix:handlebar-helpers@0.2.5
|
||||
rajit:bootstrap3-datepicker@1.7.1_1
|
||||
random@1.1.0
|
||||
rate-limit@1.0.9
|
||||
reactive-dict@1.3.0
|
||||
reactive-var@1.0.11
|
||||
reload@1.3.0
|
||||
retry@1.1.0
|
||||
routepolicy@1.1.0
|
||||
rzymek:fullcalendar@3.8.0
|
||||
server-render@0.3.1
|
||||
service-configuration@1.0.11
|
||||
session@1.2.0
|
||||
sha@1.0.9
|
||||
shell-server@0.4.0
|
||||
simple:authenticate-user-by-token@1.0.1
|
||||
simple:json-routes@2.1.0
|
||||
simple:rest-accounts-password@1.1.2
|
||||
simple:rest-bearer-token-parser@1.0.1
|
||||
simple:rest-json-error-handler@1.0.1
|
||||
socket-stream-client@0.2.2
|
||||
softwarerero:accounts-t9n@1.3.11
|
||||
spacebars@1.0.15
|
||||
spacebars-compiler@1.1.3
|
||||
srp@1.0.12
|
||||
standard-minifier-css@1.5.4
|
||||
standard-minifier-js@2.5.2
|
||||
staringatlights:fast-render@3.2.0
|
||||
staringatlights:inject-data@2.3.0
|
||||
tap:i18n@1.8.2
|
||||
templates:tabs@2.3.0
|
||||
templating@1.3.2
|
||||
templating-compiler@1.3.3
|
||||
templating-runtime@1.3.2
|
||||
templating-tools@1.1.2
|
||||
tracker@1.2.0
|
||||
twbs:bootstrap@3.3.6
|
||||
ui@1.0.13
|
||||
underscore@1.0.10
|
||||
url@1.2.0
|
||||
useraccounts:core@1.14.2
|
||||
useraccounts:flow-routing@1.14.2
|
||||
useraccounts:unstyled@1.14.2
|
||||
verron:autosize@3.0.8
|
||||
webapp@1.7.5
|
||||
webapp-hashing@1.0.9
|
||||
wekan-accounts-cas@0.1.0
|
||||
wekan-accounts-oidc@1.0.10
|
||||
wekan-ldap@0.0.2
|
||||
wekan-markdown@1.0.7
|
||||
wekan-oidc@1.0.12
|
||||
wekan-scrollbar@3.1.3
|
||||
yasaricli:slugify@0.0.7
|
||||
zimme:active-route@2.3.2
|
||||
@@ -1,914 +0,0 @@
|
||||
(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'] = {};
|
||||
|
||||
})();
|
||||
@@ -1,238 +0,0 @@
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
@@ -1,640 +0,0 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
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);
|
||||
};
|
||||
Generated
-4361
File diff suppressed because it is too large.
Load diff
@@ -1,73 +0,0 @@
|
||||
{
|
||||
"name": "wekan",
|
||||
"version": "v3.95.0",
|
||||
"description": "Open-Source kanban",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"lint": "eslint --cache --ext .js --ignore-path .eslintignore .",
|
||||
"lint:eslint:fix": "eslint --ext .js --ignore-path .eslintignore --fix .",
|
||||
"lint:staged": "lint-staged",
|
||||
"prettify": "prettier --write '**/*.js' '**/*.jsx'",
|
||||
"test": "npm run lint"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.js": [
|
||||
"meteor npm run prettify",
|
||||
"meteor npm run lint:eslint:fix",
|
||||
"git add --force"
|
||||
],
|
||||
"*.jsx": [
|
||||
"meteor npm run prettify",
|
||||
"meteor npm run lint:eslint:fix",
|
||||
"git add --force"
|
||||
],
|
||||
"*.json": [
|
||||
"prettier --write",
|
||||
"git add --force"
|
||||
]
|
||||
},
|
||||
"pre-commit": "lint:staged",
|
||||
"eslintConfig": {
|
||||
"extends": "@meteorjs/eslint-config-meteor"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/wekan/wekan.git"
|
||||
},
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/wekan/wekan/issues"
|
||||
},
|
||||
"homepage": "https://wekan.github.io",
|
||||
"devDependencies": {
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-config-meteor": "^0.1.1",
|
||||
"eslint-config-prettier": "^6.10.0",
|
||||
"eslint-import-resolver-meteor": "^0.4.0",
|
||||
"eslint-plugin-import": "^2.20.1",
|
||||
"eslint-plugin-meteor": "^6.0.0",
|
||||
"eslint-plugin-prettier": "^3.1.2",
|
||||
"lint-staged": "^10.0.8",
|
||||
"pre-commit": "^1.2.2",
|
||||
"prettier": "^1.19.1",
|
||||
"prettier-eslint": "^9.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.8.7",
|
||||
"ajv": "^6.12.0",
|
||||
"babel-runtime": "^6.26.0",
|
||||
"bcrypt": "^4.0.1",
|
||||
"bson": "^4.0.3",
|
||||
"bunyan": "^1.8.12",
|
||||
"es6-promise": "^4.2.8",
|
||||
"gridfs-stream": "^1.1.1",
|
||||
"ldapjs": "^1.0.2",
|
||||
"meteor-node-stubs": "^1.0.0",
|
||||
"mongodb": "^3.5.5",
|
||||
"os": "^0.1.1",
|
||||
"page": "^1.11.5",
|
||||
"qs": "^6.9.1",
|
||||
"source-map-support": "^0.5.16",
|
||||
"xss": "^1.0.6"
|
||||
}
|
||||
}
|
||||
@@ -1,853 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
+121
-2
@@ -1,3 +1,124 @@
|
||||
# v3.99 2020-04-27 Wekan release
|
||||
|
||||
This release fixes the following bugs:
|
||||
|
||||
- [Fix Boards are very hard to tap in mobile](https://github.com/wekan/wekan/pull/3051).
|
||||
Thanks to marc1006.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v3.98 2020-04-25 Wekan release
|
||||
|
||||
News:
|
||||
|
||||
- There is now many mobile and desktop webbrowser fixes. Please test does your
|
||||
favourite Javascript enabled webbrowser work, and add issues if something
|
||||
does not work, and there is no existing issue about that yet.
|
||||
- Desktop browser mode has setting for Show/Hide drag handles:
|
||||
top right click username / Change Settings / Show desktop drag handles.
|
||||
You can request desktop website also at mobile webbrowsers on Android.
|
||||
At iOS requesting desktop website did not seem to work yet.
|
||||
- At iOS Safari and Chrome, to see swimlane buttons you need to scroll to right.
|
||||
Fixes to this and other issues are welcome as pull request.
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- [Pre-fill the title of checklists (Trello-style)](https://github.com/wekan/wekan/pull/3030).
|
||||
Thanks to boeserwolf.
|
||||
- [Implement option to change the first day of the week in user settings](https://github.com/wekan/wekan/pull/3032).
|
||||
Thanks to marc1006.
|
||||
- [Add babel to build chain and linter. Enables fancy Javascript language
|
||||
features like optional chaining, for developer happiness](https://github.com/wekan/wekan/pull/3034).
|
||||
Thanks to boeserwolf.
|
||||
- [Use only one 'Apply' button for applying the user settings](https://github.com/wekan/wekan/pull/3039).
|
||||
Thanks to marc1006.
|
||||
- [Allow variable height for board list items. Allow words in title/description to be able to break
|
||||
and wrap onto the next line](https://github.com/wekan/wekan/pull/3046).
|
||||
Thanks to marc1006.
|
||||
|
||||
and adds the following updates:
|
||||
|
||||
- [Upgrade to Meteor 1.10.2](https://github.com/wekan/wekan/commit/d1f98d0c472fb41e25fb29a9a6f6dae7db003f6f).
|
||||
Thanks to Meteor developers and xet7.
|
||||
- [Set Snap MongoDB compatibility to 4.2 according to Meteor ChangeLog](https://github.com/wekan/wekan/commit/7de18eccea3854db3be6197bf21afbfd3ddb65a6).
|
||||
Thanks to xet7.
|
||||
|
||||
and fixes the following bugs:
|
||||
|
||||
- [Multiple lint issue fixes](https://github.com/wekan/wekan/pull/3031).
|
||||
Thanks to marc1006.
|
||||
- [Fix lint errors in lint error fix](https://github.com/wekan/wekan/commit/9e95c06415e614e587d684ff9660cc53c5f8c8d3).
|
||||
Thanks to xet7.
|
||||
- [Fix getStartDayOfWeek function](https://github.com/wekan/wekan/pull/3038).
|
||||
Thanks to marc1006 and boeserwolf.
|
||||
- Improve mobile devices support [Part1](https://github.com/wekan/wekan/pull/3040) and [Part2](https://github.com/wekan/wekan/pull/3045).
|
||||
Thanks to marc1006.
|
||||
- [Fix Wekan not load at all in Firefox v.68 for Android](https://github.com/wekan/wekan/commit/1235363465b824d26129d4aa74a4445f362c1a73).
|
||||
Thanks to xet7.
|
||||
- [Fix comment typo in docker-compose.yml](https://github.com/wekan/wekan/pull/3044).
|
||||
Thanks to VictorioBerra.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v3.97 2020-04-19 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- [Sortable boards](https://github.com/wekan/wekan/pull/3027).
|
||||
Thanks to boeserwolf.
|
||||
- [Added dockerfiles for multi-arch builds and manifest](https://github.com/wekan/wekan/pull/3023).
|
||||
[In Progress](https://github.com/wekan/wekan/issues/2999).
|
||||
Thanks to brokencode64.
|
||||
- [Make linked card clickable](https://github.com/wekan/wekan/pull/3025).
|
||||
Thanks to boeserwolf.
|
||||
|
||||
and fixes the following bugs:
|
||||
|
||||
- [Fix using checklists on mobile and iPad](https://github.com/wekan/wekan/pull/3019).
|
||||
Thanks to devinsm.
|
||||
- [Improve card layout on mobile devices](https://github.com/wekan/wekan/pull/3024).
|
||||
Thanks to marc1006.
|
||||
- [Make OCP OAuth work with Openshift 4.x](https://github.com/wekan/wekan/pull/3020).
|
||||
Thanks to ckavili.
|
||||
- [Remove old warning from Sandstorm import board data loss, because bug has been already
|
||||
fixed](https://github.com/wekan/wekan/commit/960fe5163b6a2f7c3dca03b5e31d69611b49f079).
|
||||
Thanks to aputsiaq and xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v3.96 2020-04-15 Wekan release
|
||||
|
||||
This release adds the following Sandstorm updates:
|
||||
|
||||
- This is the first Sandstorm Wekan release that uses newest Meteor 1.10.1 and Node 12.x.
|
||||
Now all Wekan platforms use newest Meteor and Node 12.x LTS.
|
||||
Thanks to kentonv and xet7.
|
||||
- [Fix capnp workaround to work with newest Meteor and
|
||||
Node 12.x](https://github.com/wekan/wekan/commit/b2d546579c4957352c29b36c0c8a4a08b944dbb4).
|
||||
Thanks to kentonv.
|
||||
- [Update Sandstorm release script for newest Meteor and
|
||||
Node 12.x](https://github.com/wekan/wekan/commit/c5f782976b971fa3f2323e80a013bbf6a49c0596).
|
||||
Thanks to xet7.
|
||||
- [Remove Meteor 1.8.x files because Sandstorm Wekan now uses newest
|
||||
Meteor](https://github.com/wekan/wekan/commit/1a836969e10215bad47ac56a9b0d9de801b66fd2).
|
||||
Thanks to xet7.
|
||||
|
||||
and adds the following new features:
|
||||
|
||||
- [Hide password auth with environment variable PASSWORD_LOGIN_ENABLED=false](https://github.com/wekan/wekan/pull/3014).
|
||||
Snap example: `sudo snap set wekan password-login-enabled='false'` .
|
||||
Thanks to salleman33.
|
||||
|
||||
and fixes the following bugs:
|
||||
|
||||
- [Fix Board admins can not clone or archive their boards at All Boards
|
||||
page](https://github.com/wekan/wekan/pull/3013).
|
||||
Thanks to salleman33.
|
||||
- [Fix `<p>` margin in card labels](https://github.com/wekan/wekan/pull/3015).
|
||||
Thanks to boeserwolf.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v3.95 2020-04-12 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
@@ -10,8 +131,6 @@ This release adds the following new features:
|
||||
Thanks to juniormendonca.
|
||||
- [Public boards overview](https://github.com/wekan/wekan/pull/3008).
|
||||
Thanks to NicoP-S.
|
||||
- Try to upgrade Sandstorm Wekan to Meteor 1.10.1 and Node 12.x.
|
||||
Thanks to kentonv and xet7.
|
||||
|
||||
and fixes the following bugs:
|
||||
|
||||
|
||||
+5
-2
@@ -7,7 +7,7 @@ LABEL maintainer="wekan"
|
||||
ENV BUILD_DEPS="apt-utils libarchive-tools gnupg gosu wget curl bzip2 g++ build-essential git ca-certificates python3" \
|
||||
DEBUG=false \
|
||||
NODE_VERSION=v12.16.2 \
|
||||
METEOR_RELEASE=1.10-rc.2 \
|
||||
METEOR_RELEASE=1.10.2 \
|
||||
USE_EDGE=false \
|
||||
METEOR_EDGE=1.5-beta.17 \
|
||||
NPM_VERSION=latest \
|
||||
@@ -113,7 +113,8 @@ ENV BUILD_DEPS="apt-utils libarchive-tools gnupg gosu wget curl bzip2 g++ build-
|
||||
CORS_EXPOSE_HEADERS="" \
|
||||
DEFAULT_AUTHENTICATION_METHOD="" \
|
||||
SCROLLINERTIA="0" \
|
||||
SCROLLAMOUNT="auto"
|
||||
SCROLLAMOUNT="auto" \
|
||||
PASSWORD_LOGIN_ENABLED=true
|
||||
|
||||
# Copy the app to the image
|
||||
COPY ${SRC_PATH} /home/wekan/app
|
||||
@@ -270,6 +271,8 @@ RUN \
|
||||
cd /home/wekan/app_build/bundle/programs/server/ && \
|
||||
gosu wekan:wekan npm install && \
|
||||
#gosu wekan:wekan npm install bcrypt && \
|
||||
# Remove legacy webbroser bundle, so that Wekan works also at Android Firefox, iOS Safari, etc.
|
||||
rm -rf /home/wekan/app_build/bundle/programs/web.browser.legacy && \
|
||||
mv /home/wekan/app_build/bundle /build && \
|
||||
\
|
||||
# Put back the original tar
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
FROM amd64/alpine:3.7 AS builder
|
||||
|
||||
# Set the environment variables for builder
|
||||
ENV QEMU_VERSION=v4.2.0-6 \
|
||||
QEMU_ARCHITECTURE=aarch64 \
|
||||
NODE_ARCHITECTURE=linux-arm64 \
|
||||
NODE_VERSION=v12.16.1 \
|
||||
WEKAN_VERSION=3.96 \
|
||||
WEKAN_ARCHITECTURE=arm64
|
||||
|
||||
# Install dependencies
|
||||
RUN apk update && apk add ca-certificates outils-sha1 && \
|
||||
\
|
||||
# Download qemu static for our architecture
|
||||
wget https://github.com/multiarch/qemu-user-static/releases/download/${QEMU_VERSION}/qemu-${QEMU_ARCHITECTURE}-static.tar.gz -O - | tar -xz && \
|
||||
\
|
||||
# Download wekan and shasum
|
||||
wget https://releases.wekan.team/raspi3/wekan-${WEKAN_VERSION}-${WEKAN_ARCHITECTURE}.zip && \
|
||||
wget https://releases.wekan.team/raspi3/SHA256SUMS.txt && \
|
||||
# Verify wekan
|
||||
grep wekan-${WEKAN_VERSION}-${WEKAN_ARCHITECTURE}.zip SHA256SUMS.txt | sha256sum -c - && \
|
||||
\
|
||||
# Unzip wekan
|
||||
unzip wekan-${WEKAN_VERSION}-${WEKAN_ARCHITECTURE}.zip && \
|
||||
\
|
||||
# Download node and shasums
|
||||
wget https://nodejs.org/dist/${NODE_VERSION}/node-${NODE_VERSION}-${NODE_ARCHITECTURE}.tar.gz && \
|
||||
wget https://nodejs.org/dist/${NODE_VERSION}/SHASUMS256.txt.asc && \
|
||||
\
|
||||
# Verify nodejs authenticity
|
||||
grep node-${NODE_VERSION}-${NODE_ARCHITECTURE}.tar.gz SHASUMS256.txt.asc | sha256sum -c - && \
|
||||
\
|
||||
# Extract node and remove tar.gz
|
||||
tar xvzf node-${NODE_VERSION}-${NODE_ARCHITECTURE}.tar.gz
|
||||
|
||||
# Build wekan dockerfile
|
||||
FROM arm64v8/ubuntu:19.10
|
||||
LABEL maintainer="wekan"
|
||||
|
||||
# Set the environment variables (defaults where required)
|
||||
ENV QEMU_ARCHITECTURE=aarch64 \
|
||||
NODE_ARCHITECTURE=linux-arm64 \
|
||||
NODE_VERSION=v12.16.1 \
|
||||
NODE_ENV=production \
|
||||
NPM_VERSION=latest \
|
||||
WITH_API=true \
|
||||
PORT=8080 \
|
||||
ROOT_URL=http://localhost \
|
||||
MONGO_URL=mongodb://127.0.0.1:27017/wekan
|
||||
|
||||
# Copy qemu-static to image
|
||||
COPY --from=builder qemu-${QEMU_ARCHITECTURE}-static /usr/bin
|
||||
|
||||
# Copy the app to the image
|
||||
COPY --from=builder bundle /home/wekan/bundle
|
||||
|
||||
# Copy
|
||||
COPY --from=builder node-${NODE_VERSION}-${NODE_ARCHITECTURE} /opt/nodejs
|
||||
|
||||
RUN \
|
||||
set -o xtrace && \
|
||||
# Add non-root user wekan
|
||||
useradd --user-group --system --home-dir /home/wekan wekan && \
|
||||
\
|
||||
# Install Node
|
||||
ln -s /opt/nodejs/bin/node /usr/bin/node && \
|
||||
ln -s /opt/nodejs/bin/npm /usr/bin/npm && \
|
||||
mkdir -p /opt/nodejs/lib/node_modules/fibers/.node-gyp /root/.node-gyp/8.16.1 /home/wekan/.config && \
|
||||
chown wekan --recursive /home/wekan/.config && \
|
||||
\
|
||||
# Install Node dependencies
|
||||
npm install -g npm@${NPM_VERSION}
|
||||
|
||||
EXPOSE $PORT
|
||||
USER wekan
|
||||
|
||||
CMD ["node", "/home/wekan/bundle/main.js"]
|
||||
@@ -34,7 +34,7 @@ and PWA app that can be added as icon on Android and bookmark on iOS, used like
|
||||
|
||||
**NOTE**:
|
||||
- Please read the [FAQ](https://github.com/wekan/wekan/wiki/FAQ) first
|
||||
- Please don't feed the trolls and spammers that are mentioned in the FAQ :)
|
||||
- Please don't feed the [trolls](https://github.com/wekan/wekan/wiki/FAQ#why-am-i-called-a-troll) and [spammers](https://github.com/wekan/wekan/wiki/FAQ#why-am-i-called-a-spammer) that are mentioned in the FAQ :)
|
||||
|
||||
## About Wekan
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
appId: wekan-public/apps/77b94f60-dec9-0136-304e-16ff53095928
|
||||
appVersion: "v3.95.0"
|
||||
appVersion: "v3.99.0"
|
||||
files:
|
||||
userUploads:
|
||||
- README.md
|
||||
|
||||
@@ -7,7 +7,7 @@ BlazeComponent.extendComponent({
|
||||
return Boards.find(
|
||||
{ archived: true },
|
||||
{
|
||||
sort: ['title'],
|
||||
sort: { sort: 1 /* boards default sorting */ },
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Cookies } from 'meteor/ostrio:cookies';
|
||||
const cookies = new Cookies();
|
||||
const subManager = new SubsManager();
|
||||
const { calculateIndex, enableClickOnTouch } = Utils;
|
||||
const { calculateIndex } = Utils;
|
||||
const swimlaneWhileSortingHeight = 150;
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
@@ -191,9 +191,6 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
});
|
||||
|
||||
// ugly touch event hotfix
|
||||
enableClickOnTouch('.js-swimlane:not(.placeholder)');
|
||||
|
||||
this.autorun(() => {
|
||||
let showDesktopDragHandles = false;
|
||||
currentUser = Meteor.user();
|
||||
@@ -205,7 +202,7 @@ BlazeComponent.extendComponent({
|
||||
} else {
|
||||
showDesktopDragHandles = false;
|
||||
}
|
||||
if (!Utils.isMiniScreen() && showDesktopDragHandles) {
|
||||
if (Utils.isMiniScreen() || showDesktopDragHandles) {
|
||||
$swimlanesDom.sortable({
|
||||
handle: '.js-swimlane-header-handle',
|
||||
});
|
||||
@@ -215,9 +212,8 @@ BlazeComponent.extendComponent({
|
||||
});
|
||||
}
|
||||
|
||||
// Disable drag-dropping if the current user is not a board member or is miniscreen
|
||||
// Disable drag-dropping if the current user is not a board member
|
||||
$swimlanesDom.sortable('option', 'disabled', !userIsMember());
|
||||
$swimlanesDom.sortable('option', 'disabled', Utils.isMiniScreen());
|
||||
});
|
||||
|
||||
function userIsMember() {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
template(name="boardList")
|
||||
.wrapper
|
||||
ul.board-list.clearfix
|
||||
ul.board-list.clearfix.js-boards
|
||||
li.js-add-board
|
||||
a.board-list-item.label {{_ 'add-board'}}
|
||||
each boards
|
||||
li(class="{{#if isStarred}}starred{{/if}}" class=colorClass)
|
||||
li(class="{{#if isStarred}}starred{{/if}}" class=colorClass).js-board
|
||||
if isInvited
|
||||
.board-list-item
|
||||
span.details
|
||||
@@ -39,7 +39,7 @@ template(name="boardList")
|
||||
i.fa.js-archive-board(
|
||||
class="fa-archive"
|
||||
title="{{_ 'archive-board'}}")
|
||||
else if currentUser.isBoardAdmin
|
||||
else if isAdministrable
|
||||
i.fa.js-clone-board(
|
||||
class="fa-clone"
|
||||
title="{{_ 'duplicate-board'}}")
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
const subManager = new SubsManager();
|
||||
const { calculateIndex, enableClickOnTouch } = Utils;
|
||||
|
||||
Template.boardListHeaderBar.events({
|
||||
'click .js-open-archived-board'() {
|
||||
@@ -7,8 +8,8 @@ Template.boardListHeaderBar.events({
|
||||
});
|
||||
|
||||
Template.boardListHeaderBar.helpers({
|
||||
title(){
|
||||
return FlowRouter.getRouteName() == 'home' ? 'my-boards' :'public';
|
||||
title() {
|
||||
return FlowRouter.getRouteName() === 'home' ? 'my-boards' : 'public';
|
||||
},
|
||||
templatesBoardId() {
|
||||
return Meteor.user() && Meteor.user().getTemplatesBoardId();
|
||||
@@ -23,25 +24,80 @@ BlazeComponent.extendComponent({
|
||||
Meteor.subscribe('setting');
|
||||
},
|
||||
|
||||
onRendered() {
|
||||
const self = this;
|
||||
function userIsAllowedToMove() {
|
||||
return Meteor.user();
|
||||
}
|
||||
|
||||
const itemsSelector = '.js-board:not(.placeholder)';
|
||||
|
||||
const $boards = this.$('.js-boards');
|
||||
$boards.sortable({
|
||||
connectWith: '.js-boards',
|
||||
tolerance: 'pointer',
|
||||
appendTo: '.board-list',
|
||||
helper: 'clone',
|
||||
distance: 7,
|
||||
items: itemsSelector,
|
||||
placeholder: 'board-wrapper placeholder',
|
||||
start(evt, ui) {
|
||||
ui.helper.css('z-index', 1000);
|
||||
ui.placeholder.height(ui.helper.height());
|
||||
EscapeActions.executeUpTo('popup-close');
|
||||
},
|
||||
stop(evt, ui) {
|
||||
// To attribute the new index number, we need to get the DOM element
|
||||
// of the previous and the following card -- if any.
|
||||
const prevBoardDom = ui.item.prev('.js-board').get(0);
|
||||
const nextBoardBom = ui.item.next('.js-board').get(0);
|
||||
const sortIndex = calculateIndex(prevBoardDom, nextBoardBom, 1);
|
||||
|
||||
const boardDomElement = ui.item.get(0);
|
||||
const board = Blaze.getData(boardDomElement);
|
||||
// Normally the jquery-ui sortable library moves the dragged DOM element
|
||||
// to its new position, which disrupts Blaze reactive updates mechanism
|
||||
// (especially when we move the last card of a list, or when multiple
|
||||
// users move some cards at the same time). To prevent these UX glitches
|
||||
// we ask sortable to gracefully cancel the move, and to put back the
|
||||
// DOM in its initial state. The card move is then handled reactively by
|
||||
// Blaze with the below query.
|
||||
$boards.sortable('cancel');
|
||||
|
||||
board.move(sortIndex.base);
|
||||
},
|
||||
});
|
||||
|
||||
// ugly touch event hotfix
|
||||
enableClickOnTouch(itemsSelector);
|
||||
|
||||
// Disable drag-dropping if the current user is not a board member or is comment only
|
||||
this.autorun(() => {
|
||||
$boards.sortable('option', 'disabled', !userIsAllowedToMove());
|
||||
});
|
||||
},
|
||||
|
||||
boards() {
|
||||
let query = {
|
||||
archived: false,
|
||||
type: 'board',
|
||||
}
|
||||
if (FlowRouter.getRouteName() == 'home')
|
||||
query['members.userId'] = Meteor.userId()
|
||||
else
|
||||
query.permission = 'public'
|
||||
};
|
||||
if (FlowRouter.getRouteName() === 'home')
|
||||
query['members.userId'] = Meteor.userId();
|
||||
else query.permission = 'public';
|
||||
|
||||
return Boards.find(
|
||||
query,
|
||||
{ sort: ['title'] },
|
||||
);
|
||||
return Boards.find(query, {
|
||||
sort: { sort: 1 /* boards default sorting */ },
|
||||
});
|
||||
},
|
||||
isStarred() {
|
||||
const user = Meteor.user();
|
||||
return user && user.hasStarred(this.currentData()._id);
|
||||
},
|
||||
isAdministrable() {
|
||||
const user = Meteor.user();
|
||||
return user && user.isBoardAdmin(this.currentData()._id);
|
||||
},
|
||||
|
||||
hasOvertimeCards() {
|
||||
subManager.subscribe('board', this.currentData()._id, false);
|
||||
|
||||
@@ -11,6 +11,19 @@ $spaceBetweenTiles = 16px
|
||||
box-sizing: border-box
|
||||
position: relative
|
||||
|
||||
&.placeholder:after
|
||||
content: '';
|
||||
display: block;
|
||||
background: darken(white, 20%)
|
||||
border-radius: 3px;
|
||||
height: 106px;
|
||||
margin: 8px;
|
||||
|
||||
&.ui-sortable-helper
|
||||
cursor: grabbing
|
||||
transform: rotate(4deg)
|
||||
display: block !important
|
||||
|
||||
&.starred
|
||||
.fa-star,
|
||||
.fa-star-o
|
||||
@@ -20,7 +33,7 @@ $spaceBetweenTiles = 16px
|
||||
overflow: hidden;
|
||||
background-color: #999
|
||||
color: #f6f6f6
|
||||
height: 90px
|
||||
height: auto
|
||||
font-size: 16px
|
||||
line-height: 22px
|
||||
border-radius: 3px
|
||||
@@ -31,6 +44,7 @@ $spaceBetweenTiles = 16px
|
||||
margin: ($spaceBetweenTiles/2)
|
||||
position: relative
|
||||
text-decoration: none
|
||||
word-wrap: break-word
|
||||
|
||||
&.tile
|
||||
background-size: auto
|
||||
@@ -183,7 +197,7 @@ $spaceBetweenTiles = 16px
|
||||
overflow: scroll
|
||||
|
||||
li
|
||||
width: 50%
|
||||
width: 50%
|
||||
|
||||
.board-list-item
|
||||
overflow: hidden
|
||||
|
||||
@@ -32,7 +32,7 @@ template(name="cardDetails")
|
||||
// else
|
||||
{{_ 'top-level-card'}}
|
||||
if isLinkedCard
|
||||
h3.linked-card-location
|
||||
a.linked-card-location.js-go-to-linked-card
|
||||
+viewer
|
||||
| {{getBoardTitle}} > {{getTitle}}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const subManager = new SubsManager();
|
||||
const { calculateIndexData, enableClickOnTouch } = Utils;
|
||||
const { calculateIndexData } = Utils;
|
||||
|
||||
let cardColors;
|
||||
Meteor.startup(() => {
|
||||
@@ -56,9 +56,8 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
votePublic() {
|
||||
const card = this.currentData();
|
||||
if (card.vote)
|
||||
return card.vote.public
|
||||
return null
|
||||
if (card.vote) return card.vote.public;
|
||||
return null;
|
||||
},
|
||||
voteCountPositive() {
|
||||
const card = this.currentData();
|
||||
@@ -232,9 +231,6 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
});
|
||||
|
||||
// ugly touch event hotfix
|
||||
enableClickOnTouch('.card-checklist-items .js-checklist');
|
||||
|
||||
const $subtasksDom = this.$('.card-subtasks-items');
|
||||
|
||||
$subtasksDom.sortable({
|
||||
@@ -270,26 +266,21 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
});
|
||||
|
||||
// ugly touch event hotfix
|
||||
enableClickOnTouch('.card-subtasks-items .js-subtasks');
|
||||
|
||||
function userIsMember() {
|
||||
return Meteor.user() && Meteor.user().isBoardMember();
|
||||
}
|
||||
|
||||
// Disable sorting if the current user is not a board member
|
||||
this.autorun(() => {
|
||||
if ($checklistsDom.data('sortable')) {
|
||||
$checklistsDom.sortable('option', 'disabled', !userIsMember());
|
||||
const disabled = !userIsMember() || Utils.isMiniScreen();
|
||||
if (
|
||||
$checklistsDom.data('uiSortable') ||
|
||||
$checklistsDom.data('sortable')
|
||||
) {
|
||||
$checklistsDom.sortable('option', 'disabled', disabled);
|
||||
}
|
||||
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());
|
||||
if ($subtasksDom.data('uiSortable') || $subtasksDom.data('sortable')) {
|
||||
$subtasksDom.sortable('option', 'disabled', disabled);
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -379,6 +370,9 @@ BlazeComponent.extendComponent({
|
||||
this.data().setRequestedBy('');
|
||||
}
|
||||
},
|
||||
'click .js-go-to-linked-card'() {
|
||||
Utils.goCardId(this.data().linkedId);
|
||||
},
|
||||
'click .js-member': Popup.open('cardMember'),
|
||||
'click .js-add-members': Popup.open('cardMembers'),
|
||||
'click .js-assignee': Popup.open('cardAssignee'),
|
||||
@@ -388,7 +382,7 @@ BlazeComponent.extendComponent({
|
||||
'click .js-start-date': Popup.open('editCardStartDate'),
|
||||
'click .js-due-date': Popup.open('editCardDueDate'),
|
||||
'click .js-end-date': Popup.open('editCardEndDate'),
|
||||
'click .js-show-positive-votes':Popup.open('positiveVoteMembers'),
|
||||
'click .js-show-positive-votes': Popup.open('positiveVoteMembers'),
|
||||
'click .js-show-negative-votes': Popup.open('negativeVoteMembers'),
|
||||
'mouseenter .js-card-details'() {
|
||||
const parentComponent = this.parentComponent().parentComponent();
|
||||
@@ -417,9 +411,9 @@ BlazeComponent.extendComponent({
|
||||
const forIt = $(e.target).hasClass('js-vote-positive');
|
||||
let newState = null;
|
||||
if (
|
||||
this.voteState() == null ||
|
||||
(this.voteState() == false && forIt) ||
|
||||
(this.voteState() == true && !forIt)
|
||||
this.voteState() === null ||
|
||||
(this.voteState() === false && forIt) ||
|
||||
(this.voteState() === true && !forIt)
|
||||
) {
|
||||
newState = forIt;
|
||||
}
|
||||
@@ -655,7 +649,7 @@ Template.cardDetailsActionsPopup.events({
|
||||
},
|
||||
});
|
||||
|
||||
Template.editCardTitleForm.onRendered(function () {
|
||||
Template.editCardTitleForm.onRendered(function() {
|
||||
autosize(this.$('.js-edit-card-title'));
|
||||
});
|
||||
|
||||
@@ -669,7 +663,7 @@ Template.editCardTitleForm.events({
|
||||
},
|
||||
});
|
||||
|
||||
Template.editCardRequesterForm.onRendered(function () {
|
||||
Template.editCardRequesterForm.onRendered(function() {
|
||||
autosize(this.$('.js-edit-card-requester'));
|
||||
});
|
||||
|
||||
@@ -682,7 +676,7 @@ Template.editCardRequesterForm.events({
|
||||
},
|
||||
});
|
||||
|
||||
Template.editCardAssignerForm.onRendered(function () {
|
||||
Template.editCardAssignerForm.onRendered(function() {
|
||||
autosize(this.$('.js-edit-card-assigner'));
|
||||
});
|
||||
|
||||
@@ -724,7 +718,7 @@ BlazeComponent.extendComponent({
|
||||
_id: { $ne: Meteor.user().getTemplatesBoardId() },
|
||||
},
|
||||
{
|
||||
sort: ['title'],
|
||||
sort: { sort: 1 /* boards default sorting */ },
|
||||
},
|
||||
);
|
||||
return boards;
|
||||
@@ -822,7 +816,7 @@ Template.copyChecklistToManyCardsPopup.events({
|
||||
|
||||
// copy subtasks
|
||||
cursor = Cards.find({ parentId: oldId });
|
||||
cursor.forEach(function () {
|
||||
cursor.forEach(function() {
|
||||
'use strict';
|
||||
const subtask = arguments[0];
|
||||
subtask.parentId = _id;
|
||||
@@ -900,7 +894,7 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
},
|
||||
{
|
||||
sort: ['title'],
|
||||
sort: { sort: 1 /* boards default sorting */ },
|
||||
},
|
||||
);
|
||||
return boards;
|
||||
@@ -971,7 +965,7 @@ BlazeComponent.extendComponent({
|
||||
}
|
||||
}
|
||||
},
|
||||
'click .js-delete': Popup.afterConfirm('cardDelete', function () {
|
||||
'click .js-delete': Popup.afterConfirm('cardDelete', function() {
|
||||
Popup.close();
|
||||
Cards.remove(this._id);
|
||||
Utils.goBoardId(this.boardId);
|
||||
|
||||
@@ -94,6 +94,7 @@ avatar-radius = 50%
|
||||
animation: flexGrowIn 0.1s
|
||||
box-shadow: 0 0 7px 0 darken(white, 30%)
|
||||
transition: flex-basis 0.1s
|
||||
box-sizing: border-box
|
||||
|
||||
.mCustomScrollBox
|
||||
padding-left: 0
|
||||
|
||||
@@ -88,7 +88,8 @@ template(name="checklistItems")
|
||||
template(name='checklistItemDetail')
|
||||
.js-checklist-item.checklist-item
|
||||
if canModifyCard
|
||||
.check-box.materialCheckBox(class="{{#if item.isFinished }}is-checked{{/if}}")
|
||||
.check-box-container
|
||||
.check-box.materialCheckBox(class="{{#if item.isFinished }}is-checked{{/if}}")
|
||||
.item-title.js-open-inlined-form.is-editable(class="{{#if item.isFinished }}is-checked{{/if}}")
|
||||
+viewer
|
||||
= item.title
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const { calculateIndexData, enableClickOnTouch } = Utils;
|
||||
const { calculateIndexData, capitalize } = Utils;
|
||||
|
||||
function initSorting(items) {
|
||||
items.sortable({
|
||||
@@ -36,9 +36,6 @@ function initSorting(items) {
|
||||
checklistItem.move(checklistId, sortIndex.base);
|
||||
},
|
||||
});
|
||||
|
||||
// ugly touch event hotfix
|
||||
enableClickOnTouch('.js-checklist-item:not(.placeholder)');
|
||||
}
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
@@ -54,14 +51,15 @@ BlazeComponent.extendComponent({
|
||||
return Meteor.user() && Meteor.user().isBoardMember();
|
||||
}
|
||||
|
||||
// Disable sorting if the current user is not a board member
|
||||
// Disable sorting if the current user is not a board member or is a miniscreen
|
||||
self.autorun(() => {
|
||||
const $itemsDom = $(self.itemsDom);
|
||||
if ($itemsDom.data('sortable')) {
|
||||
$(self.itemsDom).sortable('option', 'disabled', !userIsMember());
|
||||
}
|
||||
if ($itemsDom.data('sortable')) {
|
||||
$(self.itemsDom).sortable('option', 'disabled', Utils.isMiniScreen());
|
||||
if ($itemsDom.data('uiSortable') || $itemsDom.data('sortable')) {
|
||||
$(self.itemsDom).sortable(
|
||||
'option',
|
||||
'disabled',
|
||||
!userIsMember() || Utils.isMiniScreen(),
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -177,6 +175,16 @@ BlazeComponent.extendComponent({
|
||||
}
|
||||
},
|
||||
|
||||
focusChecklistItem(event) {
|
||||
// If a new checklist is created, pre-fill the title and select it.
|
||||
const checklist = this.currentData().checklist;
|
||||
if (!checklist) {
|
||||
const textarea = event.target;
|
||||
textarea.value = capitalize(TAPi18n.__('r-checklist'));
|
||||
textarea.select();
|
||||
}
|
||||
},
|
||||
|
||||
events() {
|
||||
const events = {
|
||||
'click .toggle-delete-checklist-dialog'(event) {
|
||||
@@ -196,6 +204,7 @@ BlazeComponent.extendComponent({
|
||||
'submit .js-edit-checklist-item': this.editChecklistItem,
|
||||
'click .js-delete-checklist-item': this.deleteItem,
|
||||
'click .confirm-checklist-delete': this.deleteChecklist,
|
||||
'focus .js-add-checklist-item': this.focusChecklistItem,
|
||||
keydown: this.pressKey,
|
||||
},
|
||||
];
|
||||
@@ -250,7 +259,7 @@ BlazeComponent.extendComponent({
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click .js-checklist-item .check-box': this.toggleItem,
|
||||
'click .js-checklist-item .check-box-container': this.toggleItem,
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
@@ -113,6 +113,9 @@ textarea.js-add-checklist-item, textarea.js-edit-checklist-item
|
||||
&:hover
|
||||
background-color: darken(white, 8%)
|
||||
|
||||
.check-box-container
|
||||
padding-right: 1px;
|
||||
|
||||
.check-box
|
||||
margin: 0.1em 0 0 0;
|
||||
&.is-checked
|
||||
@@ -121,7 +124,7 @@ textarea.js-add-checklist-item, textarea.js-edit-checklist-item
|
||||
|
||||
.item-title
|
||||
flex: 1
|
||||
padding-left: 10px;
|
||||
margin-left: 10px;
|
||||
&.is-checked
|
||||
color: #8c8c8c
|
||||
font-style: italic
|
||||
|
||||
@@ -158,6 +158,8 @@
|
||||
|
||||
.edit-labels-pop-over
|
||||
margin-bottom: 8px
|
||||
.card-label .viewer p
|
||||
margin: 0
|
||||
|
||||
.edit-labels-pop-over .shortcut
|
||||
display: inline-block
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -15,9 +15,6 @@ template(name="importTextarea")
|
||||
p: label(for='import-textarea') {{_ instruction}} {{_ 'import-board-instruction-about-errors'}}
|
||||
textarea.js-import-json(placeholder="{{_ 'import-json-placeholder'}}" autofocus)
|
||||
| {{jsonText}}
|
||||
if isSandstorm
|
||||
h1.warning {{_ 'import-sandstorm-backup-warning'}}
|
||||
p.warning {{_ 'import-sandstorm-warning'}}
|
||||
input.primary.wide(type="submit" value="{{_ 'import'}}")
|
||||
|
||||
template(name="importMapMembers")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Cookies } from 'meteor/ostrio:cookies';
|
||||
const cookies = new Cookies();
|
||||
const { calculateIndex, enableClickOnTouch } = Utils;
|
||||
const { calculateIndex } = Utils;
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
// Proxy
|
||||
@@ -114,9 +114,6 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
});
|
||||
|
||||
// ugly touch event hotfix
|
||||
enableClickOnTouch(itemsSelector);
|
||||
|
||||
this.autorun(() => {
|
||||
let showDesktopDragHandles = false;
|
||||
currentUser = Meteor.user();
|
||||
@@ -129,7 +126,7 @@ BlazeComponent.extendComponent({
|
||||
showDesktopDragHandles = false;
|
||||
}
|
||||
|
||||
if (!Utils.isMiniScreen() && showDesktopDragHandles) {
|
||||
if (Utils.isMiniScreen() || showDesktopDragHandles) {
|
||||
$cards.sortable({
|
||||
handle: '.handle',
|
||||
});
|
||||
@@ -139,27 +136,16 @@ BlazeComponent.extendComponent({
|
||||
});
|
||||
}
|
||||
|
||||
if ($cards.data('sortable')) {
|
||||
if ($cards.data('uiSortable') || $cards.data('sortable')) {
|
||||
$cards.sortable(
|
||||
'option',
|
||||
'disabled',
|
||||
// Disable drag-dropping when user is not member/is miniscreen
|
||||
// Disable drag-dropping when user is not member
|
||||
!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.
|
||||
|
||||
@@ -411,7 +411,7 @@ BlazeComponent.extendComponent({
|
||||
type: 'board',
|
||||
},
|
||||
{
|
||||
sort: ['title'],
|
||||
sort: { sort: 1 /* boards default sorting */ },
|
||||
},
|
||||
);
|
||||
return boards;
|
||||
@@ -597,7 +597,7 @@ BlazeComponent.extendComponent({
|
||||
type: 'board',
|
||||
},
|
||||
{
|
||||
sort: ['title'],
|
||||
sort: { sort: 1 /* boards default sorting */ },
|
||||
},
|
||||
);
|
||||
return boards;
|
||||
|
||||
@@ -30,10 +30,9 @@ template(name="listHeader")
|
||||
if canSeeAddCard
|
||||
a.js-add-card.fa.fa-plus.list-header-plus-icon
|
||||
a.fa.fa-navicon.js-open-list-menu
|
||||
//a.list-header-handle.handle.fa.fa-arrows.js-list-handle
|
||||
else
|
||||
a.list-header-menu-icon.fa.fa-angle-right.js-select-list
|
||||
//a.list-header-handle.handle.fa.fa-arrows.js-list-handle
|
||||
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
|
||||
|
||||
@@ -31,6 +31,11 @@ Template.userFormsLayout.onCreated(function() {
|
||||
return this.stop();
|
||||
},
|
||||
});
|
||||
Meteor.call('isPasswordLoginDisabled', (_, result) => {
|
||||
if (result) {
|
||||
$('.at-pwd-form').hide();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Template.userFormsLayout.onRendered(() => {
|
||||
|
||||
@@ -135,6 +135,10 @@ $popupWidth = 300px
|
||||
margin-bottom: 8px
|
||||
|
||||
.pop-over-list
|
||||
li
|
||||
display: block
|
||||
clear: both
|
||||
|
||||
li > a
|
||||
clear: both
|
||||
cursor: pointer
|
||||
@@ -316,6 +320,7 @@ $popupWidth = 300px
|
||||
input[type="file"]
|
||||
margin: 4px 0 12px
|
||||
width: 100%
|
||||
box-sizing: border-box
|
||||
|
||||
.pop-over-list
|
||||
li > a
|
||||
|
||||
@@ -11,7 +11,7 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
},
|
||||
{
|
||||
sort: ['title'],
|
||||
sort: { sort: 1 /* boards default sorting */ },
|
||||
},
|
||||
);
|
||||
return boards;
|
||||
|
||||
@@ -48,7 +48,7 @@ BlazeComponent.extendComponent({
|
||||
'members.isAdmin': true,
|
||||
},
|
||||
{
|
||||
sort: ['title'],
|
||||
sort: { sort: 1 /* boards default sorting */ },
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
@@ -196,14 +196,14 @@ Template.boardMenuPopup.events({
|
||||
},
|
||||
'click .js-change-board-color': Popup.open('boardChangeColor'),
|
||||
'click .js-change-language': Popup.open('changeLanguage'),
|
||||
'click .js-archive-board ': Popup.afterConfirm('archiveBoard', function () {
|
||||
'click .js-archive-board ': Popup.afterConfirm('archiveBoard', function() {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
currentBoard.archive();
|
||||
// XXX We should have some kind of notification on top of the page to
|
||||
// confirm that the board was successfully archived.
|
||||
FlowRouter.go('home');
|
||||
}),
|
||||
'click .js-delete-board': Popup.afterConfirm('deleteBoard', function () {
|
||||
'click .js-delete-board': Popup.afterConfirm('deleteBoard', function() {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
Popup.close();
|
||||
Boards.remove(currentBoard._id);
|
||||
@@ -215,17 +215,16 @@ Template.boardMenuPopup.events({
|
||||
'click .js-card-settings': Popup.open('boardCardSettings'),
|
||||
});
|
||||
|
||||
|
||||
Template.boardMenuPopup.onCreated(function () {
|
||||
Template.boardMenuPopup.onCreated(function() {
|
||||
this.apiEnabled = new ReactiveVar(false);
|
||||
Meteor.call('_isApiEnabled', (e, result) => {
|
||||
this.apiEnabled.set(result)
|
||||
})
|
||||
})
|
||||
this.apiEnabled.set(result);
|
||||
});
|
||||
});
|
||||
|
||||
Template.boardMenuPopup.helpers({
|
||||
withApi() {
|
||||
return Template.instance().apiEnabled.get()
|
||||
return Template.instance().apiEnabled.get();
|
||||
},
|
||||
exportUrl() {
|
||||
const params = {
|
||||
@@ -248,7 +247,7 @@ Template.memberPopup.events({
|
||||
Popup.close();
|
||||
},
|
||||
'click .js-change-role': Popup.open('changePermissions'),
|
||||
'click .js-remove-member': Popup.afterConfirm('removeMember', function () {
|
||||
'click .js-remove-member': Popup.afterConfirm('removeMember', function() {
|
||||
const boardId = Session.get('currentBoard');
|
||||
const memberId = this.userId;
|
||||
Cards.find({ boardId, members: memberId }).forEach(card => {
|
||||
@@ -510,7 +509,7 @@ BlazeComponent.extendComponent({
|
||||
'members.userId': Meteor.userId(),
|
||||
},
|
||||
{
|
||||
sort: ['title'],
|
||||
sort: { sort: 1 /* boards default sorting */ },
|
||||
},
|
||||
);
|
||||
},
|
||||
@@ -589,7 +588,7 @@ BlazeComponent.extendComponent({
|
||||
'subtext-with-parent',
|
||||
'no-parent',
|
||||
];
|
||||
options.forEach(function (element) {
|
||||
options.forEach(function(element) {
|
||||
if (element !== value) {
|
||||
$(`#${element} ${MCB}`).toggleClass(CKCLS, false);
|
||||
$(`#${element}`).toggleClass(CKCLS, false);
|
||||
@@ -688,7 +687,7 @@ BlazeComponent.extendComponent({
|
||||
'members.userId': Meteor.userId(),
|
||||
},
|
||||
{
|
||||
sort: ['title'],
|
||||
sort: { sort: 1 /* boards default sorting */ },
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Cookies } from 'meteor/ostrio:cookies';
|
||||
const cookies = new Cookies();
|
||||
const { calculateIndex, enableClickOnTouch } = Utils;
|
||||
const { calculateIndex } = Utils;
|
||||
|
||||
function currentListIsInThisSwimlane(swimlaneId) {
|
||||
const currentList = Lists.findOne(Session.get('currentList'));
|
||||
@@ -87,9 +87,6 @@ function initSortable(boardComponent, $listsDom) {
|
||||
},
|
||||
});
|
||||
|
||||
// ugly touch event hotfix
|
||||
enableClickOnTouch('.js-list:not(.js-list-composer)');
|
||||
|
||||
function userIsMember() {
|
||||
return (
|
||||
Meteor.user() &&
|
||||
@@ -111,7 +108,7 @@ function initSortable(boardComponent, $listsDom) {
|
||||
showDesktopDragHandles = false;
|
||||
}
|
||||
|
||||
if (!Utils.isMiniScreen() && showDesktopDragHandles) {
|
||||
if (Utils.isMiniScreen() || showDesktopDragHandles) {
|
||||
$listsDom.sortable({
|
||||
handle: '.js-list-handle',
|
||||
});
|
||||
@@ -122,34 +119,12 @@ function initSortable(boardComponent, $listsDom) {
|
||||
}
|
||||
|
||||
const $listDom = $listsDom;
|
||||
if ($listDom.data('sortable')) {
|
||||
if ($listDom.data('uiSortable') || $listDom.data('sortable')) {
|
||||
$listsDom.sortable(
|
||||
'option',
|
||||
'disabled',
|
||||
// Disable drag-dropping when user is not member/is worker/is miniscreen
|
||||
!userIsMember(),
|
||||
// Not disable drag-dropping while in multi-selection mode
|
||||
// MultiSelection.isActive() || !userIsMember(),
|
||||
);
|
||||
}
|
||||
|
||||
if ($listDom.data('sortable')) {
|
||||
$listsDom.sortable(
|
||||
'option',
|
||||
'disabled',
|
||||
// Disable drag-dropping when user is not member/is worker/is miniscreen
|
||||
Meteor.user().isWorker(),
|
||||
// Not disable drag-dropping while in multi-selection mode
|
||||
// MultiSelection.isActive() || !userIsMember(),
|
||||
);
|
||||
}
|
||||
|
||||
if ($listDom.data('sortable')) {
|
||||
$listsDom.sortable(
|
||||
'option',
|
||||
'disabled',
|
||||
// Disable drag-dropping when user is not member/is worker/is miniscreen
|
||||
Utils.isMiniScreen(),
|
||||
// Disable drag-dropping when user is not member/is worker
|
||||
!userIsMember() || Meteor.user().isWorker(),
|
||||
// Not disable drag-dropping while in multi-selection mode
|
||||
// MultiSelection.isActive() || !userIsMember(),
|
||||
);
|
||||
@@ -210,8 +185,7 @@ BlazeComponent.extendComponent({
|
||||
}
|
||||
|
||||
const noDragInside = ['a', 'input', 'textarea', 'p'].concat(
|
||||
Utils.isMiniScreen() ||
|
||||
(!Utils.isMiniScreen() && showDesktopDragHandles)
|
||||
Utils.isMiniScreen() || showDesktopDragHandles
|
||||
? ['.js-list-handle', '.js-swimlane-header-handle']
|
||||
: ['.js-list-header'],
|
||||
);
|
||||
|
||||
@@ -112,11 +112,20 @@ template(name="changeSettingsPopup")
|
||||
i.fa.fa-check
|
||||
unless currentUser.isWorker
|
||||
li
|
||||
label.bold
|
||||
label.bold.clear
|
||||
i.fa.fa-sort-numeric-asc
|
||||
| {{_ 'show-cards-minimum-count'}}
|
||||
input#show-cards-count-at.inline-input.left(type="number" value="#{showCardsCountAt}" min="0" max="99" onkeydown="return false")
|
||||
input.js-apply-show-cards-at.left(type="submit" value="{{_ 'apply'}}")
|
||||
label.bold.clear
|
||||
i.fa.fa-calendar
|
||||
| {{_ 'start-day-of-week'}}
|
||||
select#start-day-of-week.inline-input.left
|
||||
each day in weekDays startDayOfWeek
|
||||
if day.isSelected
|
||||
option(selected="true", value="#{day.value}") #{day.name}
|
||||
else
|
||||
option(value="#{day.value}") #{day.name}
|
||||
input.js-apply-user-settings.left(type="submit" value="{{_ 'apply'}}")
|
||||
|
||||
template(name="userDeletePopup")
|
||||
unless currentUser.isWorker
|
||||
|
||||
@@ -224,6 +224,27 @@ Template.changeSettingsPopup.helpers({
|
||||
return cookies.get('limitToShowCardsCount');
|
||||
}
|
||||
},
|
||||
weekDays(startDay) {
|
||||
return [
|
||||
TAPi18n.__('sunday'),
|
||||
TAPi18n.__('monday'),
|
||||
TAPi18n.__('tuesday'),
|
||||
TAPi18n.__('wednesday'),
|
||||
TAPi18n.__('thursday'),
|
||||
TAPi18n.__('friday'),
|
||||
TAPi18n.__('saturday'),
|
||||
].map(function(day, index) {
|
||||
return { name: day, value: index, isSelected: index === startDay };
|
||||
});
|
||||
},
|
||||
startDayOfWeek() {
|
||||
currentUser = Meteor.user();
|
||||
if (currentUser) {
|
||||
return currentUser.getStartDayOfWeek();
|
||||
} else {
|
||||
return cookies.get('startDayOfWeek');
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Template.changeSettingsPopup.events({
|
||||
@@ -247,20 +268,31 @@ Template.changeSettingsPopup.events({
|
||||
cookies.set('hasHiddenSystemMessages', 'true');
|
||||
}
|
||||
},
|
||||
'click .js-apply-show-cards-at'(event, templateInstance) {
|
||||
'click .js-apply-user-settings'(event, templateInstance) {
|
||||
event.preventDefault();
|
||||
const minLimit = parseInt(
|
||||
templateInstance.$('#show-cards-count-at').val(),
|
||||
10,
|
||||
);
|
||||
const startDay = parseInt(
|
||||
templateInstance.$('#start-day-of-week').val(),
|
||||
10,
|
||||
);
|
||||
const currentUser = Meteor.user();
|
||||
if (!isNaN(minLimit)) {
|
||||
currentUser = Meteor.user();
|
||||
if (currentUser) {
|
||||
Meteor.call('changeLimitToShowCardsCount', minLimit);
|
||||
} else {
|
||||
cookies.set('limitToShowCardsCount', minLimit);
|
||||
}
|
||||
Popup.back();
|
||||
}
|
||||
if (!isNaN(startDay)) {
|
||||
if (currentUser) {
|
||||
Meteor.call('changeStartDayOfWeek', startDay);
|
||||
} else {
|
||||
cookies.set('startDayOfWeek', startDay);
|
||||
}
|
||||
}
|
||||
Popup.back();
|
||||
},
|
||||
});
|
||||
@@ -10,12 +10,22 @@ DatePicker = BlazeComponent.extendComponent({
|
||||
this.defaultTime = defaultTime;
|
||||
},
|
||||
|
||||
startDayOfWeek() {
|
||||
const currentUser = Meteor.user();
|
||||
if (currentUser) {
|
||||
return currentUser.getStartDayOfWeek();
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
},
|
||||
|
||||
onRendered() {
|
||||
const $picker = this.$('.js-datepicker')
|
||||
.datepicker({
|
||||
todayHighlight: true,
|
||||
todayBtn: 'linked',
|
||||
language: TAPi18n.getLanguage(),
|
||||
weekStart: this.startDayOfWeek(),
|
||||
})
|
||||
.on(
|
||||
'changeDate',
|
||||
|
||||
+4
-1
@@ -38,7 +38,7 @@ version: '2'
|
||||
# sudo service docker start
|
||||
# ----------------------------------------------------------------------------------
|
||||
# ==== USAGE OF THIS docker-compose.yml ====
|
||||
# 1) For seeing does Wekan work, try this and check with your webbroser:
|
||||
# 1) For seeing does Wekan work, try this and check with your web browser:
|
||||
# docker-compose up
|
||||
# 2) Stop Wekan and start Wekan in background:
|
||||
# docker-compose stop
|
||||
@@ -598,6 +598,9 @@ services:
|
||||
# example : LOGOUT_ON_MINUTES=55
|
||||
#- LOGOUT_ON_MINUTES=
|
||||
#-------------------------------------------------------------------
|
||||
# Hide password login form
|
||||
# - PASSWORD_LOGIN_ENABLED=true
|
||||
#-------------------------------------------------------------------
|
||||
depends_on:
|
||||
- wekandb
|
||||
|
||||
|
||||
@@ -37,6 +37,12 @@ spec:
|
||||
value: {{ .Values.root_url | default "https://wekan.local" | quote }}
|
||||
- name: MONGO_URL
|
||||
value: "{{ template "mongodb-replicaset.url" . }}"
|
||||
{{- range $key := .Values.env }}
|
||||
{{- if .value }}
|
||||
- name: {{ .name }}
|
||||
value: {{ .value | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
{{- if .Values.serviceAccounts.annotations }}
|
||||
annotations:
|
||||
{{ .Values.serviceAccounts.annotations | indent 4}}
|
||||
{{- end }}
|
||||
labels:
|
||||
app: {{ template "wekan.name" . }}
|
||||
chart: {{ template "wekan.chart" . }}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
serviceAccounts:
|
||||
create: true
|
||||
name: ""
|
||||
annotations: ""
|
||||
|
||||
## Wekan image configuration
|
||||
##
|
||||
@@ -29,7 +30,9 @@ credentials:
|
||||
|
||||
## Specify additional environmental variables for the Deployment
|
||||
##
|
||||
env: {}
|
||||
env:
|
||||
- name: ""
|
||||
value: ""
|
||||
|
||||
service:
|
||||
type: NodePort
|
||||
@@ -59,10 +62,10 @@ ingress:
|
||||
# hosts:
|
||||
# - wekan-example.local
|
||||
|
||||
route:
|
||||
enabled: false
|
||||
route:
|
||||
enabled: false
|
||||
|
||||
resources:
|
||||
resources:
|
||||
requests:
|
||||
memory: 128Mi
|
||||
cpu: 300m
|
||||
|
||||
+42
-34
@@ -74,12 +74,12 @@
|
||||
"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-deleteComment": "تعليق محذوف %s",
|
||||
"add-attachment": "إضافة مرفق",
|
||||
"add-board": "إضافة لوحة",
|
||||
"add-card": "إضافة بطاقة",
|
||||
"add-swimlane": "Add Swimlane",
|
||||
"add-subtask": "Add Subtask",
|
||||
"add-subtask": "إضافة مهمة فرعية",
|
||||
"add-checklist": "إضافة قائمة تدقيق",
|
||||
"add-checklist-item": "إضافة عنصر إلى قائمة التحقق",
|
||||
"add-cover": "إضافة غلاف",
|
||||
@@ -111,8 +111,8 @@
|
||||
"restore-board": "استعادة اللوحة",
|
||||
"no-archived-boards": "لا توجد لوحات في الأرشيف.",
|
||||
"archives": "أرشيف",
|
||||
"template": "Template",
|
||||
"templates": "Templates",
|
||||
"template": "نموذج",
|
||||
"templates": "نماذج",
|
||||
"assign-member": "تعيين عضو",
|
||||
"attached": "أُرفق)",
|
||||
"attachment": "مرفق",
|
||||
@@ -152,8 +152,8 @@
|
||||
"card-spent": "امضى وقتا",
|
||||
"card-edit-attachments": "تعديل المرفقات",
|
||||
"card-edit-custom-fields": "تعديل الحقل المعدل",
|
||||
"card-start-voting": "Start voting",
|
||||
"card-cancel-voting": "Delete voting and all votes",
|
||||
"card-start-voting": "ابدأ التصويت",
|
||||
"card-cancel-voting": "حذف التصويت وجميع الأصوات",
|
||||
"card-edit-labels": "تعديل العلامات",
|
||||
"card-edit-members": "تعديل الأعضاء",
|
||||
"card-labels-title": "تعديل علامات البطاقة.",
|
||||
@@ -163,13 +163,14 @@
|
||||
"cardAttachmentsPopup-title": "إرفاق من",
|
||||
"cardCustomField-datePopup-title": "تغير التاريخ",
|
||||
"cardCustomFieldsPopup-title": "تعديل الحقل المعدل",
|
||||
"cardStartVotingPopup-title": "Start a vote",
|
||||
"cardStartVotingPopup-title": "ابدأ تصويت",
|
||||
"positiveVoteMembersPopup-title": "Proponents",
|
||||
"negativeVoteMembersPopup-title": "Opponents",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "Voting question",
|
||||
"vote-public": "Public vote",
|
||||
"vote-for-it": "for it",
|
||||
"vote-against": "against",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "مع",
|
||||
"vote-against": "ضد",
|
||||
"cardDeletePopup-title": "حذف البطاقة ?",
|
||||
"cardDetailsActionsPopup-title": "إجراءات على البطاقة",
|
||||
"cardLabelsPopup-title": "علامات",
|
||||
@@ -192,7 +193,7 @@
|
||||
"changePasswordPopup-title": "تغيير كلمة المرور",
|
||||
"changePermissionsPopup-title": "تعديل الصلاحيات",
|
||||
"changeSettingsPopup-title": "تغيير الاعدادات",
|
||||
"subtasks": "Subtasks",
|
||||
"subtasks": "المهمات الفرعية",
|
||||
"checklists": "قوائم التّدقيق",
|
||||
"click-to-star": "اضغط لإضافة اللوحة للمفضلة.",
|
||||
"click-to-unstar": "اضغط لحذف اللوحة من المفضلة.",
|
||||
@@ -203,9 +204,9 @@
|
||||
"color-black": "black",
|
||||
"color-blue": "blue",
|
||||
"color-crimson": "crimson",
|
||||
"color-darkgreen": "darkgreen",
|
||||
"color-gold": "gold",
|
||||
"color-gray": "gray",
|
||||
"color-darkgreen": "اخضر غامق",
|
||||
"color-gold": "ذهبي",
|
||||
"color-gray": "رمادي",
|
||||
"color-green": "green",
|
||||
"color-indigo": "indigo",
|
||||
"color-lime": "lime",
|
||||
@@ -220,17 +221,17 @@
|
||||
"color-purple": "purple",
|
||||
"color-red": "red",
|
||||
"color-saddlebrown": "saddlebrown",
|
||||
"color-silver": "silver",
|
||||
"color-silver": "فضي",
|
||||
"color-sky": "sky",
|
||||
"color-slateblue": "slateblue",
|
||||
"color-white": "white",
|
||||
"color-white": "أبيض",
|
||||
"color-yellow": "yellow",
|
||||
"unset-color": "Unset",
|
||||
"comment": "تعليق",
|
||||
"comment-placeholder": "أكتب تعليق",
|
||||
"comment-only": "التعليق فقط",
|
||||
"comment-only-desc": "يمكن التعليق على بطاقات فقط.",
|
||||
"no-comments": "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.",
|
||||
@@ -248,8 +249,8 @@
|
||||
"createBoardPopup-title": "إنشاء لوحة",
|
||||
"chooseBoardSourcePopup-title": "استيراد لوحة",
|
||||
"createLabelPopup-title": "إنشاء علامة",
|
||||
"createCustomField": "Create Field",
|
||||
"createCustomFieldPopup-title": "Create Field",
|
||||
"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-checkbox": "Checkbox",
|
||||
@@ -259,8 +260,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": "رقم",
|
||||
"custom-field-text": "نص",
|
||||
"custom-fields": "Custom Fields",
|
||||
"date": "تاريخ",
|
||||
"decline": "Decline",
|
||||
@@ -343,13 +344,11 @@
|
||||
"headerBarCreateBoardPopup-title": "إنشاء لوحة",
|
||||
"home": "الرئيسية",
|
||||
"import": "Import",
|
||||
"link": "Link",
|
||||
"link": "رابط",
|
||||
"import-board": "استيراد لوحة",
|
||||
"import-board-c": "استيراد لوحة",
|
||||
"import-board-title-trello": "Import board from 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-sandstorm-warning": "Imported board will delete all existing data on board and replace it with imported board.",
|
||||
"from-trello": "من تريلو",
|
||||
"from-wekan": "From previous export",
|
||||
"import-board-instruction-trello": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text",
|
||||
@@ -476,15 +475,15 @@
|
||||
"this-board": "هذه اللوحة",
|
||||
"this-card": "هذه البطاقة",
|
||||
"spent-time-hours": "Spent time (hours)",
|
||||
"overtime-hours": "Overtime (hours)",
|
||||
"overtime": "Overtime",
|
||||
"overtime-hours": "وقت اضافي (ساعات)",
|
||||
"overtime": "وقت اضافي",
|
||||
"has-overtime-cards": "Has overtime cards",
|
||||
"has-spenttime-cards": "Has spent time cards",
|
||||
"time": "الوقت",
|
||||
"title": "عنوان",
|
||||
"tracking": "تتبع",
|
||||
"tracking-info": "You will be notified of any changes to those cards you are involved as creator or member.",
|
||||
"type": "Type",
|
||||
"type": "النوع",
|
||||
"unassign-member": "إلغاء تعيين العضو",
|
||||
"unsaved-description": "لديك وصف غير محفوظ",
|
||||
"unwatch": "غير مُشاهد",
|
||||
@@ -558,7 +557,7 @@
|
||||
"OS_Totalmem": "الذاكرة الكلية لنظام التشغيل",
|
||||
"OS_Type": "نوع نظام التشغيل",
|
||||
"OS_Uptime": "مدة تشغيل نظام التشغيل",
|
||||
"days": "days",
|
||||
"days": "أيام",
|
||||
"hours": "الساعات",
|
||||
"minutes": "الدقائق",
|
||||
"seconds": "الثواني",
|
||||
@@ -572,17 +571,17 @@
|
||||
"accounts-allowUserNameChange": "Allow Username Change",
|
||||
"createdAt": "Created at",
|
||||
"verified": "Verified",
|
||||
"active": "Active",
|
||||
"active": "نشط",
|
||||
"card-received": "Received",
|
||||
"card-received-on": "Received on",
|
||||
"card-end": "End",
|
||||
"card-end-on": "Ends on",
|
||||
"editCardReceivedDatePopup-title": "Change received date",
|
||||
"editCardEndDatePopup-title": "Change end date",
|
||||
"setCardColorPopup-title": "Set color",
|
||||
"setCardActionsColorPopup-title": "Choose a color",
|
||||
"setSwimlaneColorPopup-title": "Choose a color",
|
||||
"setListColorPopup-title": "Choose a color",
|
||||
"setCardColorPopup-title": "حدد اللون",
|
||||
"setCardActionsColorPopup-title": "اختر لوناً",
|
||||
"setSwimlaneColorPopup-title": "اختر لوناً",
|
||||
"setListColorPopup-title": "اختر لوناً",
|
||||
"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.",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "of checklist",
|
||||
"r-send-email": "Send an email",
|
||||
"r-to": "to",
|
||||
"r-of": "of",
|
||||
"r-subject": "subject",
|
||||
"r-rule-details": "Rule details",
|
||||
"r-d-move-to-top-gen": "Move card to top of its list",
|
||||
@@ -776,5 +776,13 @@
|
||||
"mark-all-as-read": "Mark all as read",
|
||||
"remove-all-read": "Remove all read",
|
||||
"allow-rename": "Allow Rename",
|
||||
"allowRenamePopup-title": "Allow Rename"
|
||||
"allowRenamePopup-title": "Allow Rename",
|
||||
"start-day-of-week": "Set day of the week start",
|
||||
"monday": "Monday",
|
||||
"tuesday": "Tuesday",
|
||||
"wednesday": "Wednesday",
|
||||
"thursday": "Thursday",
|
||||
"friday": "Friday",
|
||||
"saturday": "Saturday",
|
||||
"sunday": "Sunday"
|
||||
}
|
||||
+12
-4
@@ -166,8 +166,9 @@
|
||||
"cardStartVotingPopup-title": "Start a vote",
|
||||
"positiveVoteMembersPopup-title": "Proponents",
|
||||
"negativeVoteMembersPopup-title": "Opponents",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "Voting question",
|
||||
"vote-public": "Public vote",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "for it",
|
||||
"vote-against": "against",
|
||||
"cardDeletePopup-title": "Желаете да изтриете картата?",
|
||||
@@ -348,8 +349,6 @@
|
||||
"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-sandstorm-warning": "Импортирането ще изтрие всичката налична информация в таблото и ще я замени с нова.",
|
||||
"from-trello": "От Trello",
|
||||
"from-wekan": "From previous export",
|
||||
"import-board-instruction-trello": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text.",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "of checklist",
|
||||
"r-send-email": "Send an email",
|
||||
"r-to": "to",
|
||||
"r-of": "of",
|
||||
"r-subject": "subject",
|
||||
"r-rule-details": "Детайли за правилото",
|
||||
"r-d-move-to-top-gen": "Move card to top of its list",
|
||||
@@ -776,5 +776,13 @@
|
||||
"mark-all-as-read": "Mark all as read",
|
||||
"remove-all-read": "Remove all read",
|
||||
"allow-rename": "Allow Rename",
|
||||
"allowRenamePopup-title": "Allow Rename"
|
||||
"allowRenamePopup-title": "Allow Rename",
|
||||
"start-day-of-week": "Set day of the week start",
|
||||
"monday": "Monday",
|
||||
"tuesday": "Tuesday",
|
||||
"wednesday": "Wednesday",
|
||||
"thursday": "Thursday",
|
||||
"friday": "Friday",
|
||||
"saturday": "Saturday",
|
||||
"sunday": "Sunday"
|
||||
}
|
||||
+12
-4
@@ -166,8 +166,9 @@
|
||||
"cardStartVotingPopup-title": "Start a vote",
|
||||
"positiveVoteMembersPopup-title": "Proponents",
|
||||
"negativeVoteMembersPopup-title": "Opponents",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "Voting question",
|
||||
"vote-public": "Public vote",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "for it",
|
||||
"vote-against": "against",
|
||||
"cardDeletePopup-title": "Diverkañ ar gartenn ?",
|
||||
@@ -348,8 +349,6 @@
|
||||
"import-board-c": "Import board",
|
||||
"import-board-title-trello": "Import board from 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-sandstorm-warning": "Imported board will delete all existing data on board and replace it with imported board.",
|
||||
"from-trello": "From Trello",
|
||||
"from-wekan": "From previous export",
|
||||
"import-board-instruction-trello": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "of checklist",
|
||||
"r-send-email": "Send an email",
|
||||
"r-to": "to",
|
||||
"r-of": "of",
|
||||
"r-subject": "subject",
|
||||
"r-rule-details": "Rule details",
|
||||
"r-d-move-to-top-gen": "Move card to top of its list",
|
||||
@@ -776,5 +776,13 @@
|
||||
"mark-all-as-read": "Mark all as read",
|
||||
"remove-all-read": "Remove all read",
|
||||
"allow-rename": "Allow Rename",
|
||||
"allowRenamePopup-title": "Allow Rename"
|
||||
"allowRenamePopup-title": "Allow Rename",
|
||||
"start-day-of-week": "Set day of the week start",
|
||||
"monday": "Monday",
|
||||
"tuesday": "Tuesday",
|
||||
"wednesday": "Wednesday",
|
||||
"thursday": "Thursday",
|
||||
"friday": "Friday",
|
||||
"saturday": "Saturday",
|
||||
"sunday": "Sunday"
|
||||
}
|
||||
+12
-4
@@ -166,8 +166,9 @@
|
||||
"cardStartVotingPopup-title": "Start a vote",
|
||||
"positiveVoteMembersPopup-title": "Proponents",
|
||||
"negativeVoteMembersPopup-title": "Opponents",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "Voting question",
|
||||
"vote-public": "Public vote",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "for it",
|
||||
"vote-against": "against",
|
||||
"cardDeletePopup-title": "Esborrar fitxa?",
|
||||
@@ -348,8 +349,6 @@
|
||||
"import-board-c": "Importa tauler",
|
||||
"import-board-title-trello": "Importa tauler des de 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-sandstorm-warning": "Estau segur que voleu esborrar aquesta checklist?",
|
||||
"from-trello": "Des de Trello",
|
||||
"from-wekan": "From previous export",
|
||||
"import-board-instruction-trello": "En el teu tauler Trello, ves a 'Menú', 'Més'.' Imprimir i Exportar', 'Exportar JSON', i copia el text resultant.",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "of checklist",
|
||||
"r-send-email": "Send an email",
|
||||
"r-to": "to",
|
||||
"r-of": "of",
|
||||
"r-subject": "subject",
|
||||
"r-rule-details": "Rule details",
|
||||
"r-d-move-to-top-gen": "Move card to top of its list",
|
||||
@@ -776,5 +776,13 @@
|
||||
"mark-all-as-read": "Mark all as read",
|
||||
"remove-all-read": "Remove all read",
|
||||
"allow-rename": "Allow Rename",
|
||||
"allowRenamePopup-title": "Allow Rename"
|
||||
"allowRenamePopup-title": "Allow Rename",
|
||||
"start-day-of-week": "Set day of the week start",
|
||||
"monday": "Monday",
|
||||
"tuesday": "Tuesday",
|
||||
"wednesday": "Wednesday",
|
||||
"thursday": "Thursday",
|
||||
"friday": "Friday",
|
||||
"saturday": "Saturday",
|
||||
"sunday": "Sunday"
|
||||
}
|
||||
+12
-4
@@ -166,8 +166,9 @@
|
||||
"cardStartVotingPopup-title": "Start a vote",
|
||||
"positiveVoteMembersPopup-title": "Proponents",
|
||||
"negativeVoteMembersPopup-title": "Opponents",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "Voting question",
|
||||
"vote-public": "Public vote",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "for it",
|
||||
"vote-against": "against",
|
||||
"cardDeletePopup-title": "Smazat kartu?",
|
||||
@@ -348,8 +349,6 @@
|
||||
"import-board-c": "Importovat tablo",
|
||||
"import-board-title-trello": "Import board from Trello",
|
||||
"import-board-title-wekan": "Importovat tablo z předchozího exportu",
|
||||
"import-sandstorm-backup-warning": "Nemažte data, která importujete z původního exportovaného tabla nebo Trello předtím, nežli zkontrolujete, jestli lze tuto část zavřít a znovu otevřít nebo jestli se Vám nezobrazuje chyba tabla, což znamená ztrátu dat.",
|
||||
"import-sandstorm-warning": "Importované tablo spaže všechny existující data v tablu a nahradí je importovaným tablem.",
|
||||
"from-trello": "Z Trella",
|
||||
"from-wekan": "Z předchozího exportu",
|
||||
"import-board-instruction-trello": "Na svém Trello tablu, otevři 'Menu', pak 'More', 'Print and Export', 'Export JSON', a zkopíruj výsledný text",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "ze zaškrtávacího seznamu",
|
||||
"r-send-email": "Odeslat e-mail",
|
||||
"r-to": "komu",
|
||||
"r-of": "of",
|
||||
"r-subject": "předmět",
|
||||
"r-rule-details": "Podrobnosti pravidla",
|
||||
"r-d-move-to-top-gen": "Přesunout kartu na začátek toho sloupce",
|
||||
@@ -776,5 +776,13 @@
|
||||
"mark-all-as-read": "Označit vše jako přečtené",
|
||||
"remove-all-read": "Remove all read",
|
||||
"allow-rename": "Povolit přejmenování",
|
||||
"allowRenamePopup-title": "Povolit přejmenování"
|
||||
"allowRenamePopup-title": "Povolit přejmenování",
|
||||
"start-day-of-week": "Set day of the week start",
|
||||
"monday": "Monday",
|
||||
"tuesday": "Tuesday",
|
||||
"wednesday": "Wednesday",
|
||||
"thursday": "Thursday",
|
||||
"friday": "Friday",
|
||||
"saturday": "Saturday",
|
||||
"sunday": "Sunday"
|
||||
}
|
||||
+761
-753
File diff suppressed because it is too large.
Load diff
+13
-5
@@ -166,8 +166,9 @@
|
||||
"cardStartVotingPopup-title": "Abstimmung starten",
|
||||
"positiveVoteMembersPopup-title": "Befürworter",
|
||||
"negativeVoteMembersPopup-title": "Gegner",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "Abstimmen über",
|
||||
"vote-public": "Öffentliche Abstimmung",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "Dafür",
|
||||
"vote-against": "Dagegen",
|
||||
"cardDeletePopup-title": "Karte löschen?",
|
||||
@@ -328,7 +329,7 @@
|
||||
"filter-clear": "Filter entfernen",
|
||||
"filter-no-label": "Kein Label",
|
||||
"filter-no-member": "Kein Mitglied",
|
||||
"filter-no-assignee": "No assignee",
|
||||
"filter-no-assignee": "Nicht zugewiesen",
|
||||
"filter-no-custom-fields": "Keine benutzerdefinierten Felder",
|
||||
"filter-show-archive": "Archivierte Listen anzeigen",
|
||||
"filter-hide-empty": "Leere Listen verstecken",
|
||||
@@ -348,8 +349,6 @@
|
||||
"import-board-c": "Board importieren",
|
||||
"import-board-title-trello": "Board von Trello importieren",
|
||||
"import-board-title-wekan": "Board aus vorherigem Export importieren",
|
||||
"import-sandstorm-backup-warning": "Löschen Sie keine Daten, die Sie aus einem ursprünglich exportierten oder Trelloboard importieren, bevor Sie geprüft haben, ob alles funktioniert. Andernfalls kann es zu Datenverlust kommen, falls es zu einem \"Board nicht gefunden\"-Fehler kommt.",
|
||||
"import-sandstorm-warning": "Das importierte Board wird alle bereits existierenden Daten löschen und mit den importierten Daten überschreiben.",
|
||||
"from-trello": "Von Trello",
|
||||
"from-wekan": "Aus vorherigem Export",
|
||||
"import-board-instruction-trello": "Gehen Sie in ihrem Trello-Board auf 'Menü', dann 'Mehr', 'Drucken und Exportieren', 'JSON-Export' und kopieren Sie den dort angezeigten Text",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "der Checkliste",
|
||||
"r-send-email": "Eine E-Mail senden",
|
||||
"r-to": "an",
|
||||
"r-of": "of",
|
||||
"r-subject": "Betreff",
|
||||
"r-rule-details": "Regeldetails",
|
||||
"r-d-move-to-top-gen": "Karte nach oben in die Liste verschieben",
|
||||
@@ -776,5 +776,13 @@
|
||||
"mark-all-as-read": "Alle als gelesen markieren",
|
||||
"remove-all-read": "Alle gelesenen entfernen",
|
||||
"allow-rename": "Umbenennen erlauben",
|
||||
"allowRenamePopup-title": "Umbenennen erlauben"
|
||||
"allowRenamePopup-title": "Umbenennen erlauben",
|
||||
"start-day-of-week": "Set day of the week start",
|
||||
"monday": "Monday",
|
||||
"tuesday": "Tuesday",
|
||||
"wednesday": "Wednesday",
|
||||
"thursday": "Thursday",
|
||||
"friday": "Friday",
|
||||
"saturday": "Saturday",
|
||||
"sunday": "Sunday"
|
||||
}
|
||||
+12
-4
@@ -166,8 +166,9 @@
|
||||
"cardStartVotingPopup-title": "Start a vote",
|
||||
"positiveVoteMembersPopup-title": "Proponents",
|
||||
"negativeVoteMembersPopup-title": "Opponents",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "Voting question",
|
||||
"vote-public": "Public vote",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "for it",
|
||||
"vote-against": "against",
|
||||
"cardDeletePopup-title": "Διαγραφή Κάρτας;",
|
||||
@@ -348,8 +349,6 @@
|
||||
"import-board-c": "Εισαγωγή πίνακα",
|
||||
"import-board-title-trello": "Import board from 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-sandstorm-warning": "Imported board will delete all existing data on board and replace it with imported board.",
|
||||
"from-trello": "Από το Trello",
|
||||
"from-wekan": "From previous export",
|
||||
"import-board-instruction-trello": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text.",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "of checklist",
|
||||
"r-send-email": "Send an email",
|
||||
"r-to": "to",
|
||||
"r-of": "of",
|
||||
"r-subject": "subject",
|
||||
"r-rule-details": "Rule details",
|
||||
"r-d-move-to-top-gen": "Μετακίνηση κάρτας στην αρχή της λίστας της",
|
||||
@@ -776,5 +776,13 @@
|
||||
"mark-all-as-read": "Mark all as read",
|
||||
"remove-all-read": "Remove all read",
|
||||
"allow-rename": "Allow Rename",
|
||||
"allowRenamePopup-title": "Allow Rename"
|
||||
"allowRenamePopup-title": "Allow Rename",
|
||||
"start-day-of-week": "Set day of the week start",
|
||||
"monday": "Monday",
|
||||
"tuesday": "Tuesday",
|
||||
"wednesday": "Wednesday",
|
||||
"thursday": "Thursday",
|
||||
"friday": "Friday",
|
||||
"saturday": "Saturday",
|
||||
"sunday": "Sunday"
|
||||
}
|
||||
+12
-4
@@ -166,8 +166,9 @@
|
||||
"cardStartVotingPopup-title": "Start a vote",
|
||||
"positiveVoteMembersPopup-title": "Proponents",
|
||||
"negativeVoteMembersPopup-title": "Opponents",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "Voting question",
|
||||
"vote-public": "Public vote",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "for it",
|
||||
"vote-against": "against",
|
||||
"cardDeletePopup-title": "Delete Card?",
|
||||
@@ -348,8 +349,6 @@
|
||||
"import-board-c": "Import board",
|
||||
"import-board-title-trello": "Import board from 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-sandstorm-warning": "Imported board will delete all existing data on board and replace it with imported board.",
|
||||
"from-trello": "From Trello",
|
||||
"from-wekan": "From previous export",
|
||||
"import-board-instruction-trello": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text.",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "of checklist",
|
||||
"r-send-email": "Send an email",
|
||||
"r-to": "to",
|
||||
"r-of": "of",
|
||||
"r-subject": "subject",
|
||||
"r-rule-details": "Rule details",
|
||||
"r-d-move-to-top-gen": "Move card to top of its list",
|
||||
@@ -776,5 +776,13 @@
|
||||
"mark-all-as-read": "Mark all as read",
|
||||
"remove-all-read": "Remove all read",
|
||||
"allow-rename": "Allow Rename",
|
||||
"allowRenamePopup-title": "Allow Rename"
|
||||
"allowRenamePopup-title": "Allow Rename",
|
||||
"start-day-of-week": "Set day of the week start",
|
||||
"monday": "Monday",
|
||||
"tuesday": "Tuesday",
|
||||
"wednesday": "Wednesday",
|
||||
"thursday": "Thursday",
|
||||
"friday": "Friday",
|
||||
"saturday": "Saturday",
|
||||
"sunday": "Sunday"
|
||||
}
|
||||
+12
-4
@@ -166,8 +166,9 @@
|
||||
"cardStartVotingPopup-title": "Start a vote",
|
||||
"positiveVoteMembersPopup-title": "Proponents",
|
||||
"negativeVoteMembersPopup-title": "Opponents",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "Voting question",
|
||||
"vote-public": "Public vote",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "for it",
|
||||
"vote-against": "against",
|
||||
"cardDeletePopup-title": "Delete Card?",
|
||||
@@ -348,8 +349,6 @@
|
||||
"import-board-c": "Import board",
|
||||
"import-board-title-trello": "Import board from 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-sandstorm-warning": "Imported board will delete all existing data on board and replace it with imported board.",
|
||||
"from-trello": "From Trello",
|
||||
"from-wekan": "From previous export",
|
||||
"import-board-instruction-trello": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text.",
|
||||
@@ -677,6 +676,7 @@
|
||||
"r-of-checklist": "of checklist",
|
||||
"r-send-email": "Send an email",
|
||||
"r-to": "to",
|
||||
"r-of": "of",
|
||||
"r-subject": "subject",
|
||||
"r-rule-details": "Rule details",
|
||||
"r-d-move-to-top-gen": "Move card to top of its list",
|
||||
@@ -779,5 +779,13 @@
|
||||
"mark-all-as-read": "Mark all as read",
|
||||
"remove-all-read": "Remove all read",
|
||||
"allow-rename": "Allow Rename",
|
||||
"allowRenamePopup-title": "Allow Rename"
|
||||
"allowRenamePopup-title": "Allow Rename",
|
||||
"start-day-of-week": "Set day of the week start",
|
||||
"monday": "Monday",
|
||||
"tuesday": "Tuesday",
|
||||
"wednesday": "Wednesday",
|
||||
"thursday": "Thursday",
|
||||
"friday": "Friday",
|
||||
"saturday": "Saturday",
|
||||
"sunday": "Sunday"
|
||||
}
|
||||
+12
-4
@@ -166,8 +166,9 @@
|
||||
"cardStartVotingPopup-title": "Start a vote",
|
||||
"positiveVoteMembersPopup-title": "Proponents",
|
||||
"negativeVoteMembersPopup-title": "Opponents",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "Voting question",
|
||||
"vote-public": "Public vote",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "for it",
|
||||
"vote-against": "against",
|
||||
"cardDeletePopup-title": "Delete Card?",
|
||||
@@ -348,8 +349,6 @@
|
||||
"import-board-c": "Import board",
|
||||
"import-board-title-trello": "Import board from 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-sandstorm-warning": "Imported board will delete all existing data on board and replace it with imported board.",
|
||||
"from-trello": "From Trello",
|
||||
"from-wekan": "From previous export",
|
||||
"import-board-instruction-trello": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text.",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "of checklist",
|
||||
"r-send-email": "Send an email",
|
||||
"r-to": "to",
|
||||
"r-of": "of",
|
||||
"r-subject": "temo",
|
||||
"r-rule-details": "Rule details",
|
||||
"r-d-move-to-top-gen": "Move card to top of its list",
|
||||
@@ -776,5 +776,13 @@
|
||||
"mark-all-as-read": "Mark all as read",
|
||||
"remove-all-read": "Remove all read",
|
||||
"allow-rename": "Allow Rename",
|
||||
"allowRenamePopup-title": "Allow Rename"
|
||||
"allowRenamePopup-title": "Allow Rename",
|
||||
"start-day-of-week": "Set day of the week start",
|
||||
"monday": "Monday",
|
||||
"tuesday": "Tuesday",
|
||||
"wednesday": "Wednesday",
|
||||
"thursday": "Thursday",
|
||||
"friday": "Friday",
|
||||
"saturday": "Saturday",
|
||||
"sunday": "Sunday"
|
||||
}
|
||||
+16
-8
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"accept": "Aceptar",
|
||||
"act-activity-notify": "Notificación de Actividad",
|
||||
"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-addAttachment": "agregado archivo adjunto __attachment__ a tarjeta __card__ en la lista __list__ en el swimlane __swimlane__ en el tablero __board__",
|
||||
"act-deleteAttachment": "eliminado archivo adjunto __attachment__ de la tarjeta __card__ en la lista __list__ en el swimlane __swimlane__ en el tablero __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__",
|
||||
@@ -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": "comentario %s editado",
|
||||
"activity-deleteComment": "comentario %s eliminado",
|
||||
"add-attachment": "Agregar Adjunto",
|
||||
"add-board": "Agregar Tablero",
|
||||
"add-card": "Agregar Tarjeta",
|
||||
@@ -166,8 +166,9 @@
|
||||
"cardStartVotingPopup-title": "Start a vote",
|
||||
"positiveVoteMembersPopup-title": "Proponents",
|
||||
"negativeVoteMembersPopup-title": "Opponents",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "Voting question",
|
||||
"vote-public": "Public vote",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "for it",
|
||||
"vote-against": "against",
|
||||
"cardDeletePopup-title": "¿Borrar Tarjeta?",
|
||||
@@ -348,8 +349,6 @@
|
||||
"import-board-c": "Importar tablero",
|
||||
"import-board-title-trello": "Importar tablero de 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-sandstorm-warning": "El tablero importado va a borrar todos los datos existentes en el tablero y reemplazarlos con los del tablero en cuestión.",
|
||||
"from-trello": "De Trello",
|
||||
"from-wekan": "From previous export",
|
||||
"import-board-instruction-trello": "En tu tablero de Trello, ve a 'Menú', luego a 'Más', 'Imprimir y Exportar', 'Exportar JSON', y copia el texto resultante.",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "of checklist",
|
||||
"r-send-email": "Send an email",
|
||||
"r-to": "to",
|
||||
"r-of": "of",
|
||||
"r-subject": "subject",
|
||||
"r-rule-details": "Rule details",
|
||||
"r-d-move-to-top-gen": "Move card to top of its list",
|
||||
@@ -776,5 +776,13 @@
|
||||
"mark-all-as-read": "Mark all as read",
|
||||
"remove-all-read": "Remove all read",
|
||||
"allow-rename": "Allow Rename",
|
||||
"allowRenamePopup-title": "Allow Rename"
|
||||
"allowRenamePopup-title": "Allow Rename",
|
||||
"start-day-of-week": "Set day of the week start",
|
||||
"monday": "Monday",
|
||||
"tuesday": "Tuesday",
|
||||
"wednesday": "Wednesday",
|
||||
"thursday": "Thursday",
|
||||
"friday": "Friday",
|
||||
"saturday": "Saturday",
|
||||
"sunday": "Sunday"
|
||||
}
|
||||
+13
-5
@@ -166,10 +166,11 @@
|
||||
"cardStartVotingPopup-title": "Start a vote",
|
||||
"positiveVoteMembersPopup-title": "Proponents",
|
||||
"negativeVoteMembersPopup-title": "Opponents",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "Voting question",
|
||||
"vote-public": "Public vote",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "for it",
|
||||
"vote-against": "against",
|
||||
"vote-against": "contrarios",
|
||||
"cardDeletePopup-title": "¿Eliminar la tarjeta?",
|
||||
"cardDetailsActionsPopup-title": "Acciones de la tarjeta",
|
||||
"cardLabelsPopup-title": "Etiquetas",
|
||||
@@ -348,8 +349,6 @@
|
||||
"import-board-c": "Importar un tablero",
|
||||
"import-board-title-trello": "Importar un tablero desde Trello",
|
||||
"import-board-title-wekan": "Importar tablero desde una exportación previa",
|
||||
"import-sandstorm-backup-warning": "No elimine los datos que está importando del tablero o Trello original antes de verificar que la semilla pueda cerrarse y abrirse nuevamente, o que ocurra un error de \"Tablero no encontrado\", de lo contrario perderá sus datos.",
|
||||
"import-sandstorm-warning": "El tablero importado eliminará todos los datos existentes en este tablero y los reemplazará con los datos del tablero importado.",
|
||||
"from-trello": "Desde Trello",
|
||||
"from-wekan": "Desde exportación previa",
|
||||
"import-board-instruction-trello": "En tu tablero de Trello, ve a 'Menú', luego 'Más' > 'Imprimir y exportar' > 'Exportar JSON', y copia el texto resultante.",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "de la lista de verificación",
|
||||
"r-send-email": "Enviar un email",
|
||||
"r-to": "a",
|
||||
"r-of": "of",
|
||||
"r-subject": "asunto",
|
||||
"r-rule-details": "Detalle de la regla",
|
||||
"r-d-move-to-top-gen": "Mover la tarjeta al inicio de su lista",
|
||||
@@ -776,5 +776,13 @@
|
||||
"mark-all-as-read": "Marcar todo como leido",
|
||||
"remove-all-read": "Remove all read",
|
||||
"allow-rename": "Permitir renombrar",
|
||||
"allowRenamePopup-title": "Permitir renombrar"
|
||||
"allowRenamePopup-title": "Permitir renombrar",
|
||||
"start-day-of-week": "Set day of the week start",
|
||||
"monday": "Monday",
|
||||
"tuesday": "Tuesday",
|
||||
"wednesday": "Wednesday",
|
||||
"thursday": "Thursday",
|
||||
"friday": "Friday",
|
||||
"saturday": "Saturday",
|
||||
"sunday": "Sunday"
|
||||
}
|
||||
+13
-5
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"accept": "Onartu",
|
||||
"act-activity-notify": "Activity Notification",
|
||||
"act-activity-notify": "Jardueraren jakinarazpena",
|
||||
"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__",
|
||||
@@ -166,8 +166,9 @@
|
||||
"cardStartVotingPopup-title": "Start a vote",
|
||||
"positiveVoteMembersPopup-title": "Proponents",
|
||||
"negativeVoteMembersPopup-title": "Opponents",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "Voting question",
|
||||
"vote-public": "Public vote",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "for it",
|
||||
"vote-against": "against",
|
||||
"cardDeletePopup-title": "Ezabatu txartela?",
|
||||
@@ -348,8 +349,6 @@
|
||||
"import-board-c": "Inportatu arbela",
|
||||
"import-board-title-trello": "Inportatu arbela Trellotik",
|
||||
"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-sandstorm-warning": "Inportatutako arbelak oraingo arbeleko informazio guztia ezabatuko du eta inportatutako arbeleko informazioarekin ordeztu.",
|
||||
"from-trello": "Trellotik",
|
||||
"from-wekan": "From previous export",
|
||||
"import-board-instruction-trello": "Zure Trello arbelean, aukeratu 'Menu\", 'More', 'Print and Export', 'Export JSON', eta kopiatu jasotako testua hemen.",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "of checklist",
|
||||
"r-send-email": "Send an email",
|
||||
"r-to": "to",
|
||||
"r-of": "of",
|
||||
"r-subject": "subject",
|
||||
"r-rule-details": "Rule details",
|
||||
"r-d-move-to-top-gen": "Move card to top of its list",
|
||||
@@ -776,5 +776,13 @@
|
||||
"mark-all-as-read": "Mark all as read",
|
||||
"remove-all-read": "Remove all read",
|
||||
"allow-rename": "Allow Rename",
|
||||
"allowRenamePopup-title": "Allow Rename"
|
||||
"allowRenamePopup-title": "Allow Rename",
|
||||
"start-day-of-week": "Set day of the week start",
|
||||
"monday": "Monday",
|
||||
"tuesday": "Tuesday",
|
||||
"wednesday": "Wednesday",
|
||||
"thursday": "Thursday",
|
||||
"friday": "Friday",
|
||||
"saturday": "Saturday",
|
||||
"sunday": "Sunday"
|
||||
}
|
||||
+12
-4
@@ -166,8 +166,9 @@
|
||||
"cardStartVotingPopup-title": "شروع به رای",
|
||||
"positiveVoteMembersPopup-title": "Proponents",
|
||||
"negativeVoteMembersPopup-title": "Opponents",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "سوال رای گیری",
|
||||
"vote-public": "Public vote",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "for it",
|
||||
"vote-against": "against",
|
||||
"cardDeletePopup-title": "آیا می خواهید کارت را حذف کنید؟",
|
||||
@@ -348,8 +349,6 @@
|
||||
"import-board-c": "وارد کردن برد",
|
||||
"import-board-title-trello": "وارد کردن برد از Trello",
|
||||
"import-board-title-wekan": "بارگذاری برد ها از آخرین خروجی",
|
||||
"import-sandstorm-backup-warning": "قبل از بررسی این داده ها را از صفحه اصلی صادر شده یا Trello وارد نمیکنید این دانه دوباره باز می شود و یا دوباره باز می شود، یا برد را پیدا نمی کنید، این بدان معنی است که از دست دادن اطلاعات.",
|
||||
"import-sandstorm-warning": "Imported board will delete all existing data on board and replace it with imported board.",
|
||||
"from-trello": "از Trello",
|
||||
"from-wekan": "از آخرین خروجی",
|
||||
"import-board-instruction-trello": "در Trello-ی خود به 'Menu'، 'More'، 'Print'، 'Export to JSON رفته و متن نهایی را دراینجا وارد نمایید.",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "از چک لیست",
|
||||
"r-send-email": "ارسال ایمیل",
|
||||
"r-to": "به",
|
||||
"r-of": "of",
|
||||
"r-subject": "عنوان",
|
||||
"r-rule-details": "جزئیات قوانین",
|
||||
"r-d-move-to-top-gen": "انتقال کارت به ابتدای لیست خود",
|
||||
@@ -776,5 +776,13 @@
|
||||
"mark-all-as-read": "علامت همه به خوانده شده",
|
||||
"remove-all-read": "حذف همه خوانده شده",
|
||||
"allow-rename": "Allow Rename",
|
||||
"allowRenamePopup-title": "Allow Rename"
|
||||
"allowRenamePopup-title": "Allow Rename",
|
||||
"start-day-of-week": "Set day of the week start",
|
||||
"monday": "Monday",
|
||||
"tuesday": "Tuesday",
|
||||
"wednesday": "Wednesday",
|
||||
"thursday": "Thursday",
|
||||
"friday": "Friday",
|
||||
"saturday": "Saturday",
|
||||
"sunday": "Sunday"
|
||||
}
|
||||
+12
-4
@@ -166,8 +166,9 @@
|
||||
"cardStartVotingPopup-title": "Äänestä",
|
||||
"positiveVoteMembersPopup-title": "Kannattajat",
|
||||
"negativeVoteMembersPopup-title": "Vastustajat",
|
||||
"allowNonBoardMembers": "Salli anonyymi äänestys julkisella taululla",
|
||||
"vote-question": "Äänestys kysymys",
|
||||
"vote-public": "Julkinen äänestys",
|
||||
"vote-public": "Näytä kuka äänesti mitäkin",
|
||||
"vote-for-it": "puolesta",
|
||||
"vote-against": "vastaan",
|
||||
"cardDeletePopup-title": "Poista kortti?",
|
||||
@@ -348,8 +349,6 @@
|
||||
"import-board-c": "Tuo taulu",
|
||||
"import-board-title-trello": "Tuo taulu Trellosta",
|
||||
"import-board-title-wekan": "Tuo taulu edellisestä viennistä",
|
||||
"import-sandstorm-backup-warning": "Älä poista tietoja joita toit alkuperäisestä viennistä tai Trellosta ennen kuin tarkistat onnistuuko sulkea ja avata tämä jyvä uudelleen, vai näkyykö Board not found -virhe, joka tarkoittaa tietojen häviämistä.",
|
||||
"import-sandstorm-warning": "Tuotu taulu poistaa kaikki olemassa olevan taulun tiedot ja korvaa ne tuodulla taululla.",
|
||||
"from-trello": "Trellosta",
|
||||
"from-wekan": "Edellisestä viennistä",
|
||||
"import-board-instruction-trello": "Mene Trello-taulullasi 'Menu', sitten 'More', 'Print and Export', 'Export JSON', ja kopioi tuloksena saamasi teksti",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "tarkistuslistasta",
|
||||
"r-send-email": "Lähetä sähköposti",
|
||||
"r-to": "vastaanottajalle",
|
||||
"r-of": "/",
|
||||
"r-subject": "aihe",
|
||||
"r-rule-details": "Säännön yksityiskohdat",
|
||||
"r-d-move-to-top-gen": "Siirrä kortti listansa alkuun",
|
||||
@@ -776,5 +776,13 @@
|
||||
"mark-all-as-read": "Merkkaa kaikki luetuksi",
|
||||
"remove-all-read": "Poista kaikki luetut",
|
||||
"allow-rename": "Salli uudelleennimeäminen",
|
||||
"allowRenamePopup-title": "Salli uudelleennimeäminen"
|
||||
"allowRenamePopup-title": "Salli uudelleennimeäminen",
|
||||
"start-day-of-week": "Aseta viikon alkamispäivä",
|
||||
"monday": "Maanantai",
|
||||
"tuesday": "Tiistai",
|
||||
"wednesday": "Keskiviikko",
|
||||
"thursday": "Torstai",
|
||||
"friday": "Perjantai",
|
||||
"saturday": "Lauantai",
|
||||
"sunday": "Sunnuntai"
|
||||
}
|
||||
+12
-4
@@ -166,8 +166,9 @@
|
||||
"cardStartVotingPopup-title": "Commencer un vote",
|
||||
"positiveVoteMembersPopup-title": "Pour",
|
||||
"negativeVoteMembersPopup-title": "Contre",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "Question du vote",
|
||||
"vote-public": "Vote public",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "pour",
|
||||
"vote-against": "contre",
|
||||
"cardDeletePopup-title": "Supprimer la carte ?",
|
||||
@@ -348,8 +349,6 @@
|
||||
"import-board-c": "Importer un tableau",
|
||||
"import-board-title-trello": "Importer un tableau depuis Trello",
|
||||
"import-board-title-wekan": "Importer un tableau depuis un export précédent",
|
||||
"import-sandstorm-backup-warning": "Ne supprimez pas les données que vous importez d'un tableau exporté d'origine ou de Trello avant de vérifier que la graine peut se fermer et s'ouvrir à nouveau ou qu'une erreur \"Tableau introuvable\" survient, sinon vous perdrez vos données.",
|
||||
"import-sandstorm-warning": "Le tableau importé supprimera toutes les données du tableau et les remplacera avec celles du tableau importé.",
|
||||
"from-trello": "Depuis Trello",
|
||||
"from-wekan": "Depuis un export précédent",
|
||||
"import-board-instruction-trello": "Dans votre tableau Trello, allez sur 'Menu', puis sur 'Plus', 'Imprimer et exporter', 'Exporter en JSON' et copiez le texte du résultat",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "de la checklist",
|
||||
"r-send-email": "Envoyer un email",
|
||||
"r-to": "à",
|
||||
"r-of": "of",
|
||||
"r-subject": "sujet",
|
||||
"r-rule-details": "Détails de la règle",
|
||||
"r-d-move-to-top-gen": "Déplacer la carte en haut de sa liste",
|
||||
@@ -776,5 +776,13 @@
|
||||
"mark-all-as-read": "Marquer comme lus",
|
||||
"remove-all-read": "Supprimer les lus",
|
||||
"allow-rename": "Autoriser le renommage",
|
||||
"allowRenamePopup-title": "Autoriser le renommage"
|
||||
"allowRenamePopup-title": "Autoriser le renommage",
|
||||
"start-day-of-week": "Définir le jour de début de semaine",
|
||||
"monday": "Lundi",
|
||||
"tuesday": "Mardi",
|
||||
"wednesday": "Mercredi",
|
||||
"thursday": "Jeudi",
|
||||
"friday": "Vendredi",
|
||||
"saturday": "Samedi",
|
||||
"sunday": "Dimanche"
|
||||
}
|
||||
+12
-4
@@ -166,8 +166,9 @@
|
||||
"cardStartVotingPopup-title": "Start a vote",
|
||||
"positiveVoteMembersPopup-title": "Proponents",
|
||||
"negativeVoteMembersPopup-title": "Opponents",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "Voting question",
|
||||
"vote-public": "Public vote",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "for it",
|
||||
"vote-against": "against",
|
||||
"cardDeletePopup-title": "Delete Card?",
|
||||
@@ -348,8 +349,6 @@
|
||||
"import-board-c": "Importar taboleiro",
|
||||
"import-board-title-trello": "Importar taboleiro de 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-sandstorm-warning": "Imported board will delete all existing data on board and replace it with imported board.",
|
||||
"from-trello": "De Trello",
|
||||
"from-wekan": "From previous export",
|
||||
"import-board-instruction-trello": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text.",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "of checklist",
|
||||
"r-send-email": "Send an email",
|
||||
"r-to": "to",
|
||||
"r-of": "of",
|
||||
"r-subject": "subject",
|
||||
"r-rule-details": "Rule details",
|
||||
"r-d-move-to-top-gen": "Move card to top of its list",
|
||||
@@ -776,5 +776,13 @@
|
||||
"mark-all-as-read": "Mark all as read",
|
||||
"remove-all-read": "Remove all read",
|
||||
"allow-rename": "Allow Rename",
|
||||
"allowRenamePopup-title": "Allow Rename"
|
||||
"allowRenamePopup-title": "Allow Rename",
|
||||
"start-day-of-week": "Set day of the week start",
|
||||
"monday": "Monday",
|
||||
"tuesday": "Tuesday",
|
||||
"wednesday": "Wednesday",
|
||||
"thursday": "Thursday",
|
||||
"friday": "Friday",
|
||||
"saturday": "Saturday",
|
||||
"sunday": "Sunday"
|
||||
}
|
||||
+12
-4
@@ -166,8 +166,9 @@
|
||||
"cardStartVotingPopup-title": "התחלת הצבעה",
|
||||
"positiveVoteMembersPopup-title": "תומכים",
|
||||
"negativeVoteMembersPopup-title": "יריבים",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "שאלת הסקר",
|
||||
"vote-public": "הצבעה ציבורית",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "בעד",
|
||||
"vote-against": "נגד",
|
||||
"cardDeletePopup-title": "למחוק כרטיס?",
|
||||
@@ -348,8 +349,6 @@
|
||||
"import-board-c": "יבוא לוח",
|
||||
"import-board-title-trello": "ייבוא לוח מטרלו",
|
||||
"import-board-title-wekan": "ייבוא לוח מייצוא קודם",
|
||||
"import-sandstorm-backup-warning": "עדיף לא למחוק נתונים שייובאו מייצוא מקורי או מ־Trello בטרם בדיקה האם הגרעין הזה נסגר ונפתח שוב או אם מתקבלת שגיאה על כך שהלוח לא נמצא, משמעות הדבר היא אבדן מידע.",
|
||||
"import-sandstorm-warning": "הלוח שייובא ימחק את כל הנתונים הקיימים בלוח ויחליף אותם בלוח שייובא.",
|
||||
"from-trello": "מ־Trello",
|
||||
"from-wekan": "מייצוא קודם",
|
||||
"import-board-instruction-trello": "בלוח הטרלו שלך, עליך ללחוץ על ‚תפריט‘, ואז על ‚עוד‘, ‚הדפסה וייצוא‘, ‚יצוא JSON‘ ולהעתיק את הטקסט שנוצר.",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "של רשימת משימות",
|
||||
"r-send-email": "שליחת דוא״ל",
|
||||
"r-to": "אל",
|
||||
"r-of": "of",
|
||||
"r-subject": "נושא",
|
||||
"r-rule-details": "פרטי הכלל",
|
||||
"r-d-move-to-top-gen": "העברת כרטיס לראש הרשימה שלו",
|
||||
@@ -776,5 +776,13 @@
|
||||
"mark-all-as-read": "לסמן הכול כאילו שנקראו",
|
||||
"remove-all-read": "הסרת כל אלו שנקראו",
|
||||
"allow-rename": "לאפשר שינוי שם",
|
||||
"allowRenamePopup-title": "לאפשר שינוי שם"
|
||||
"allowRenamePopup-title": "לאפשר שינוי שם",
|
||||
"start-day-of-week": "הגדרת יום תחילת השבוע",
|
||||
"monday": "יום שני",
|
||||
"tuesday": "יום שלישי",
|
||||
"wednesday": "יום רביעי",
|
||||
"thursday": "יום חמישי",
|
||||
"friday": "יום שישי",
|
||||
"saturday": "שבת",
|
||||
"sunday": "יום ראשון"
|
||||
}
|
||||
+12
-4
@@ -166,8 +166,9 @@
|
||||
"cardStartVotingPopup-title": "Start a vote",
|
||||
"positiveVoteMembersPopup-title": "Proponents",
|
||||
"negativeVoteMembersPopup-title": "Opponents",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "Voting question",
|
||||
"vote-public": "Public vote",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "for it",
|
||||
"vote-against": "against",
|
||||
"cardDeletePopup-title": "मिटाएँ कार्ड?",
|
||||
@@ -348,8 +349,6 @@
|
||||
"import-board-c": "Import बोर्ड",
|
||||
"import-board-title-trello": "Import बोर्ड से 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-sandstorm-warning": "सूचित कर बोर्ड will मिटाएँ संपूर्ण existing data on बोर्ड और replace it साथ में सूचित कर बोर्ड.",
|
||||
"from-trello": "From Trello",
|
||||
"from-wekan": "From previous export",
|
||||
"import-board-instruction-trello": "In your Trello बोर्ड, go तक 'Menu', then 'More', 'Print और Export', 'Export JSON', और copy the resulting text.",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "of checklist",
|
||||
"r-send-email": "Send an email",
|
||||
"r-to": "to",
|
||||
"r-of": "of",
|
||||
"r-subject": "subject",
|
||||
"r-rule-details": "Rule details",
|
||||
"r-d-move-to-top-gen": "स्थानांतरित कार्ड तक top of its list",
|
||||
@@ -776,5 +776,13 @@
|
||||
"mark-all-as-read": "Mark all as read",
|
||||
"remove-all-read": "Remove all read",
|
||||
"allow-rename": "Allow Rename",
|
||||
"allowRenamePopup-title": "Allow Rename"
|
||||
"allowRenamePopup-title": "Allow Rename",
|
||||
"start-day-of-week": "Set day of the week start",
|
||||
"monday": "Monday",
|
||||
"tuesday": "Tuesday",
|
||||
"wednesday": "Wednesday",
|
||||
"thursday": "Thursday",
|
||||
"friday": "Friday",
|
||||
"saturday": "Saturday",
|
||||
"sunday": "Sunday"
|
||||
}
|
||||
+12
-4
@@ -166,8 +166,9 @@
|
||||
"cardStartVotingPopup-title": "Start a vote",
|
||||
"positiveVoteMembersPopup-title": "Proponents",
|
||||
"negativeVoteMembersPopup-title": "Opponents",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "Voting question",
|
||||
"vote-public": "Public vote",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "for it",
|
||||
"vote-against": "against",
|
||||
"cardDeletePopup-title": "Törli a kártyát?",
|
||||
@@ -348,8 +349,6 @@
|
||||
"import-board-c": "Tábla importálása",
|
||||
"import-board-title-trello": "Tábla importálása a Trello oldalról",
|
||||
"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-sandstorm-warning": "Az importált tábla törölni fogja a táblán lévő összes meglévő adatot, és kicseréli az importált táblával.",
|
||||
"from-trello": "A Trello oldalról",
|
||||
"from-wekan": "From previous export",
|
||||
"import-board-instruction-trello": "A Trello tábláján menjen a „Menü”, majd a „Több”, „Nyomtatás és exportálás”, „JSON exportálása” menüpontokra, és másolja ki az eredményül kapott szöveget.",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "ellenőrzőlistából",
|
||||
"r-send-email": "Send an email",
|
||||
"r-to": "to",
|
||||
"r-of": "of",
|
||||
"r-subject": "subject",
|
||||
"r-rule-details": "Rule details",
|
||||
"r-d-move-to-top-gen": "Move card to top of its list",
|
||||
@@ -776,5 +776,13 @@
|
||||
"mark-all-as-read": "Mark all as read",
|
||||
"remove-all-read": "Remove all read",
|
||||
"allow-rename": "Allow Rename",
|
||||
"allowRenamePopup-title": "Allow Rename"
|
||||
"allowRenamePopup-title": "Allow Rename",
|
||||
"start-day-of-week": "Set day of the week start",
|
||||
"monday": "Monday",
|
||||
"tuesday": "Tuesday",
|
||||
"wednesday": "Wednesday",
|
||||
"thursday": "Thursday",
|
||||
"friday": "Friday",
|
||||
"saturday": "Saturday",
|
||||
"sunday": "Sunday"
|
||||
}
|
||||
+12
-4
@@ -166,8 +166,9 @@
|
||||
"cardStartVotingPopup-title": "Start a vote",
|
||||
"positiveVoteMembersPopup-title": "Proponents",
|
||||
"negativeVoteMembersPopup-title": "Opponents",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "Voting question",
|
||||
"vote-public": "Public vote",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "for it",
|
||||
"vote-against": "against",
|
||||
"cardDeletePopup-title": "Delete Card?",
|
||||
@@ -348,8 +349,6 @@
|
||||
"import-board-c": "Import board",
|
||||
"import-board-title-trello": "Import board from 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-sandstorm-warning": "Imported board will delete all existing data on board and replace it with imported board.",
|
||||
"from-trello": "From Trello",
|
||||
"from-wekan": "From previous export",
|
||||
"import-board-instruction-trello": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text.",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "of checklist",
|
||||
"r-send-email": "Send an email",
|
||||
"r-to": "to",
|
||||
"r-of": "of",
|
||||
"r-subject": "subject",
|
||||
"r-rule-details": "Rule details",
|
||||
"r-d-move-to-top-gen": "Move card to top of its list",
|
||||
@@ -776,5 +776,13 @@
|
||||
"mark-all-as-read": "Mark all as read",
|
||||
"remove-all-read": "Remove all read",
|
||||
"allow-rename": "Allow Rename",
|
||||
"allowRenamePopup-title": "Allow Rename"
|
||||
"allowRenamePopup-title": "Allow Rename",
|
||||
"start-day-of-week": "Set day of the week start",
|
||||
"monday": "Monday",
|
||||
"tuesday": "Tuesday",
|
||||
"wednesday": "Wednesday",
|
||||
"thursday": "Thursday",
|
||||
"friday": "Friday",
|
||||
"saturday": "Saturday",
|
||||
"sunday": "Sunday"
|
||||
}
|
||||
+12
-4
@@ -166,8 +166,9 @@
|
||||
"cardStartVotingPopup-title": "Start a vote",
|
||||
"positiveVoteMembersPopup-title": "Proponents",
|
||||
"negativeVoteMembersPopup-title": "Opponents",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "Voting question",
|
||||
"vote-public": "Public vote",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "for it",
|
||||
"vote-against": "against",
|
||||
"cardDeletePopup-title": "Hapus kartu",
|
||||
@@ -348,8 +349,6 @@
|
||||
"import-board-c": "Import board",
|
||||
"import-board-title-trello": "Impor panel dari 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-sandstorm-warning": "Imported board will delete all existing data on board and replace it with imported board.",
|
||||
"from-trello": "From Trello",
|
||||
"from-wekan": "From previous export",
|
||||
"import-board-instruction-trello": "Di panel Trello anda, ke 'Menu', terus 'More', 'Print and Export','Export JSON', dan salin hasilnya",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "of checklist",
|
||||
"r-send-email": "Send an email",
|
||||
"r-to": "to",
|
||||
"r-of": "of",
|
||||
"r-subject": "subject",
|
||||
"r-rule-details": "Rule details",
|
||||
"r-d-move-to-top-gen": "Move card to top of its list",
|
||||
@@ -776,5 +776,13 @@
|
||||
"mark-all-as-read": "Mark all as read",
|
||||
"remove-all-read": "Remove all read",
|
||||
"allow-rename": "Allow Rename",
|
||||
"allowRenamePopup-title": "Allow Rename"
|
||||
"allowRenamePopup-title": "Allow Rename",
|
||||
"start-day-of-week": "Set day of the week start",
|
||||
"monday": "Monday",
|
||||
"tuesday": "Tuesday",
|
||||
"wednesday": "Wednesday",
|
||||
"thursday": "Thursday",
|
||||
"friday": "Friday",
|
||||
"saturday": "Saturday",
|
||||
"sunday": "Sunday"
|
||||
}
|
||||
+12
-4
@@ -166,8 +166,9 @@
|
||||
"cardStartVotingPopup-title": "Start a vote",
|
||||
"positiveVoteMembersPopup-title": "Proponents",
|
||||
"negativeVoteMembersPopup-title": "Opponents",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "Voting question",
|
||||
"vote-public": "Public vote",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "for it",
|
||||
"vote-against": "against",
|
||||
"cardDeletePopup-title": "Delete Card?",
|
||||
@@ -348,8 +349,6 @@
|
||||
"import-board-c": "Import board",
|
||||
"import-board-title-trello": "Import board from 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-sandstorm-warning": "Imported board will delete all existing data on board and replace it with imported board.",
|
||||
"from-trello": "From Trello",
|
||||
"from-wekan": "From previous export",
|
||||
"import-board-instruction-trello": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text.",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "of checklist",
|
||||
"r-send-email": "Send an email",
|
||||
"r-to": "to",
|
||||
"r-of": "of",
|
||||
"r-subject": "subject",
|
||||
"r-rule-details": "Rule details",
|
||||
"r-d-move-to-top-gen": "Move card to top of its list",
|
||||
@@ -776,5 +776,13 @@
|
||||
"mark-all-as-read": "Mark all as read",
|
||||
"remove-all-read": "Remove all read",
|
||||
"allow-rename": "Allow Rename",
|
||||
"allowRenamePopup-title": "Allow Rename"
|
||||
"allowRenamePopup-title": "Allow Rename",
|
||||
"start-day-of-week": "Set day of the week start",
|
||||
"monday": "Monday",
|
||||
"tuesday": "Tuesday",
|
||||
"wednesday": "Wednesday",
|
||||
"thursday": "Thursday",
|
||||
"friday": "Friday",
|
||||
"saturday": "Saturday",
|
||||
"sunday": "Sunday"
|
||||
}
|
||||
+14
-6
@@ -164,10 +164,11 @@
|
||||
"cardCustomField-datePopup-title": "Cambia data",
|
||||
"cardCustomFieldsPopup-title": "Modifica campo personalizzato",
|
||||
"cardStartVotingPopup-title": "Inizia una votazione",
|
||||
"positiveVoteMembersPopup-title": "Proponents",
|
||||
"negativeVoteMembersPopup-title": "Opponents",
|
||||
"positiveVoteMembersPopup-title": "Favorevoli",
|
||||
"negativeVoteMembersPopup-title": "Contrari",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "Domanda di votazione",
|
||||
"vote-public": "Public vote",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "a favore",
|
||||
"vote-against": "contro",
|
||||
"cardDeletePopup-title": "Elimina scheda?",
|
||||
@@ -348,8 +349,6 @@
|
||||
"import-board-c": "Importa bacheca",
|
||||
"import-board-title-trello": "Importa una bacheca da Trello",
|
||||
"import-board-title-wekan": "Importa bacheca dall'esportazione precedente",
|
||||
"import-sandstorm-backup-warning": "Non cancellare i dati che importi dalla bacheca esportata in origine o da Trello prima che il controllo finisca e si riapra ancora, altrimenti otterrai un messaggio di errore Bacheca non trovata, che significa che i dati sono perduti.",
|
||||
"import-sandstorm-warning": "La bacheca importata cancellerà tutti i dati esistenti su questa bacheca e li rimpiazzerà con quelli della bacheca importata.",
|
||||
"from-trello": "Da Trello",
|
||||
"from-wekan": "Dall'esportazione precedente",
|
||||
"import-board-instruction-trello": "Nella tua bacheca Trello vai a 'Menu', poi 'Altro', 'Stampa ed esporta', 'Esporta JSON', e copia il testo che compare.",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "della lista di cose da fare",
|
||||
"r-send-email": "Invia un e-mail",
|
||||
"r-to": "a",
|
||||
"r-of": "of",
|
||||
"r-subject": "soggetto",
|
||||
"r-rule-details": "Dettagli della regola",
|
||||
"r-d-move-to-top-gen": "Sposta la scheda al di sopra del suo elenco",
|
||||
@@ -776,5 +776,13 @@
|
||||
"mark-all-as-read": "Segna come letto",
|
||||
"remove-all-read": "Rimuovi tutti i già letti",
|
||||
"allow-rename": "Consenti Rinomina",
|
||||
"allowRenamePopup-title": "Consenti Rinomina"
|
||||
"allowRenamePopup-title": "Consenti Rinomina",
|
||||
"start-day-of-week": "Imposta l'inizio del giorno della settimana",
|
||||
"monday": "Lunedi",
|
||||
"tuesday": "Martedi",
|
||||
"wednesday": "Mercoledi",
|
||||
"thursday": "Giovedi",
|
||||
"friday": "Venerdi",
|
||||
"saturday": "Sabato",
|
||||
"sunday": "Domenica"
|
||||
}
|
||||
+12
-4
@@ -166,8 +166,9 @@
|
||||
"cardStartVotingPopup-title": "投票を開始",
|
||||
"positiveVoteMembersPopup-title": "支持者",
|
||||
"negativeVoteMembersPopup-title": "反対者",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "投票の質問事項",
|
||||
"vote-public": "投票を公開",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "賛成",
|
||||
"vote-against": "反対",
|
||||
"cardDeletePopup-title": "カードを削除しますか?",
|
||||
@@ -348,8 +349,6 @@
|
||||
"import-board-c": "ボードをインポート",
|
||||
"import-board-title-trello": "Trelloからボードをインポート",
|
||||
"import-board-title-wekan": "以前のエクスポートからボードをインポート",
|
||||
"import-sandstorm-backup-warning": "この操作が完了して再度開くことができるのを確認するまでインポート元のボードまたはTrelloのデータを削除しないでください。「ボードが見つかりません」が表示された場合、データが失われたことを意味します。",
|
||||
"import-sandstorm-warning": "ボードのインポートは、既存ボードのすべてのデータを置き換えます。",
|
||||
"from-trello": "Trelloから",
|
||||
"from-wekan": "以前のエクスポートから",
|
||||
"import-board-instruction-trello": "Trelloボードの、 'Menu' → 'More' → 'Print and Export' → 'Export JSON'を選択し、テキストをコピーしてください。",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "チェックリスト",
|
||||
"r-send-email": "メールを送る",
|
||||
"r-to": "宛先",
|
||||
"r-of": "of",
|
||||
"r-subject": "件名",
|
||||
"r-rule-details": "ルール詳細",
|
||||
"r-d-move-to-top-gen": "カードを自身のリストの先頭に移動",
|
||||
@@ -776,5 +776,13 @@
|
||||
"mark-all-as-read": "全て既読にする",
|
||||
"remove-all-read": "全ての既読を削除",
|
||||
"allow-rename": "リネームを許可する",
|
||||
"allowRenamePopup-title": "リネームを許可する"
|
||||
"allowRenamePopup-title": "リネームを許可する",
|
||||
"start-day-of-week": "週の始まりを設定",
|
||||
"monday": "月曜",
|
||||
"tuesday": "火曜",
|
||||
"wednesday": "水曜",
|
||||
"thursday": "木曜",
|
||||
"friday": "金曜",
|
||||
"saturday": "土曜",
|
||||
"sunday": "日曜"
|
||||
}
|
||||
+12
-4
@@ -166,8 +166,9 @@
|
||||
"cardStartVotingPopup-title": "Start a vote",
|
||||
"positiveVoteMembersPopup-title": "Proponents",
|
||||
"negativeVoteMembersPopup-title": "Opponents",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "Voting question",
|
||||
"vote-public": "Public vote",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "for it",
|
||||
"vote-against": "against",
|
||||
"cardDeletePopup-title": "წავშალოთ ბარათი? ",
|
||||
@@ -348,8 +349,6 @@
|
||||
"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-sandstorm-warning": "იმპორტირებული დაფა წაშლის ყველა არსებულ მონაცემს დაფაზე და შეანაცვლებს მას იმპორტირებული დაფა. ",
|
||||
"from-trello": "Trello-დან",
|
||||
"from-wekan": "From previous export",
|
||||
"import-board-instruction-trello": "თქვენს Trello დაფაზე, შედით \"მენიუ\"-ში, შემდეგ დააკლიკეთ \"მეტი\", \"ამოპრინტერება და ექსპორტი\", \"JSON-ის ექსპორტი\" და დააკოპირეთ შედეგი. ",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "of checklist",
|
||||
"r-send-email": "Send an email",
|
||||
"r-to": "to",
|
||||
"r-of": "of",
|
||||
"r-subject": "subject",
|
||||
"r-rule-details": "Rule details",
|
||||
"r-d-move-to-top-gen": "Move card to top of its list",
|
||||
@@ -776,5 +776,13 @@
|
||||
"mark-all-as-read": "Mark all as read",
|
||||
"remove-all-read": "Remove all read",
|
||||
"allow-rename": "Allow Rename",
|
||||
"allowRenamePopup-title": "Allow Rename"
|
||||
"allowRenamePopup-title": "Allow Rename",
|
||||
"start-day-of-week": "Set day of the week start",
|
||||
"monday": "Monday",
|
||||
"tuesday": "Tuesday",
|
||||
"wednesday": "Wednesday",
|
||||
"thursday": "Thursday",
|
||||
"friday": "Friday",
|
||||
"saturday": "Saturday",
|
||||
"sunday": "Sunday"
|
||||
}
|
||||
+12
-4
@@ -166,8 +166,9 @@
|
||||
"cardStartVotingPopup-title": "Start a vote",
|
||||
"positiveVoteMembersPopup-title": "Proponents",
|
||||
"negativeVoteMembersPopup-title": "Opponents",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "Voting question",
|
||||
"vote-public": "Public vote",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "for it",
|
||||
"vote-against": "against",
|
||||
"cardDeletePopup-title": "Delete Card?",
|
||||
@@ -348,8 +349,6 @@
|
||||
"import-board-c": "Import board",
|
||||
"import-board-title-trello": "Import board from 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-sandstorm-warning": "Imported board will delete all existing data on board and replace it with imported board.",
|
||||
"from-trello": "From Trello",
|
||||
"from-wekan": "From previous export",
|
||||
"import-board-instruction-trello": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text.",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "of checklist",
|
||||
"r-send-email": "Send an email",
|
||||
"r-to": "to",
|
||||
"r-of": "of",
|
||||
"r-subject": "subject",
|
||||
"r-rule-details": "Rule details",
|
||||
"r-d-move-to-top-gen": "Move card to top of its list",
|
||||
@@ -776,5 +776,13 @@
|
||||
"mark-all-as-read": "Mark all as read",
|
||||
"remove-all-read": "Remove all read",
|
||||
"allow-rename": "Allow Rename",
|
||||
"allowRenamePopup-title": "Allow Rename"
|
||||
"allowRenamePopup-title": "Allow Rename",
|
||||
"start-day-of-week": "Set day of the week start",
|
||||
"monday": "Monday",
|
||||
"tuesday": "Tuesday",
|
||||
"wednesday": "Wednesday",
|
||||
"thursday": "Thursday",
|
||||
"friday": "Friday",
|
||||
"saturday": "Saturday",
|
||||
"sunday": "Sunday"
|
||||
}
|
||||
+12
-4
@@ -166,8 +166,9 @@
|
||||
"cardStartVotingPopup-title": "Start a vote",
|
||||
"positiveVoteMembersPopup-title": "Proponents",
|
||||
"negativeVoteMembersPopup-title": "Opponents",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "Voting question",
|
||||
"vote-public": "Public vote",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "for it",
|
||||
"vote-against": "against",
|
||||
"cardDeletePopup-title": "카드를 삭제합니까?",
|
||||
@@ -348,8 +349,6 @@
|
||||
"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-sandstorm-warning": "Imported board will delete all existing data on board and replace it with imported board.",
|
||||
"from-trello": "From Trello",
|
||||
"from-wekan": "From previous export",
|
||||
"import-board-instruction-trello": "Trello 게시판에서 'Menu' -> 'More' -> 'Print and Export', 'Export JSON' 선택하여 텍스트 결과값 복사",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "of checklist",
|
||||
"r-send-email": "Send an email",
|
||||
"r-to": "to",
|
||||
"r-of": "of",
|
||||
"r-subject": "subject",
|
||||
"r-rule-details": "Rule details",
|
||||
"r-d-move-to-top-gen": "Move card to top of its list",
|
||||
@@ -776,5 +776,13 @@
|
||||
"mark-all-as-read": "Mark all as read",
|
||||
"remove-all-read": "Remove all read",
|
||||
"allow-rename": "Allow Rename",
|
||||
"allowRenamePopup-title": "Allow Rename"
|
||||
"allowRenamePopup-title": "Allow Rename",
|
||||
"start-day-of-week": "Set day of the week start",
|
||||
"monday": "Monday",
|
||||
"tuesday": "Tuesday",
|
||||
"wednesday": "Wednesday",
|
||||
"thursday": "Thursday",
|
||||
"friday": "Friday",
|
||||
"saturday": "Saturday",
|
||||
"sunday": "Sunday"
|
||||
}
|
||||
+12
-4
@@ -166,8 +166,9 @@
|
||||
"cardStartVotingPopup-title": "Start a vote",
|
||||
"positiveVoteMembersPopup-title": "Proponents",
|
||||
"negativeVoteMembersPopup-title": "Opponents",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "Voting question",
|
||||
"vote-public": "Public vote",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "for it",
|
||||
"vote-against": "against",
|
||||
"cardDeletePopup-title": "Delete Card?",
|
||||
@@ -348,8 +349,6 @@
|
||||
"import-board-c": "Import board",
|
||||
"import-board-title-trello": "Import board from 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-sandstorm-warning": "Imported board will delete all existing data on board and replace it with imported board.",
|
||||
"from-trello": "From Trello",
|
||||
"from-wekan": "From previous export",
|
||||
"import-board-instruction-trello": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text.",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "of checklist",
|
||||
"r-send-email": "Send an email",
|
||||
"r-to": "to",
|
||||
"r-of": "of",
|
||||
"r-subject": "subject",
|
||||
"r-rule-details": "Rule details",
|
||||
"r-d-move-to-top-gen": "Move card to top of its list",
|
||||
@@ -776,5 +776,13 @@
|
||||
"mark-all-as-read": "Mark all as read",
|
||||
"remove-all-read": "Remove all read",
|
||||
"allow-rename": "Allow Rename",
|
||||
"allowRenamePopup-title": "Allow Rename"
|
||||
"allowRenamePopup-title": "Allow Rename",
|
||||
"start-day-of-week": "Set day of the week start",
|
||||
"monday": "Monday",
|
||||
"tuesday": "Tuesday",
|
||||
"wednesday": "Wednesday",
|
||||
"thursday": "Thursday",
|
||||
"friday": "Friday",
|
||||
"saturday": "Saturday",
|
||||
"sunday": "Sunday"
|
||||
}
|
||||
+12
-4
@@ -166,8 +166,9 @@
|
||||
"cardStartVotingPopup-title": "Start a vote",
|
||||
"positiveVoteMembersPopup-title": "Proponents",
|
||||
"negativeVoteMembersPopup-title": "Opponents",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "Voting question",
|
||||
"vote-public": "Public vote",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "for it",
|
||||
"vote-against": "against",
|
||||
"cardDeletePopup-title": "Желаете да изтриете картата?",
|
||||
@@ -348,8 +349,6 @@
|
||||
"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-sandstorm-warning": "Импортирането ще изтрие всичката налична информация в таблото и ще я замени с нова.",
|
||||
"from-trello": "От Trello",
|
||||
"from-wekan": "From previous export",
|
||||
"import-board-instruction-trello": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text.",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "of checklist",
|
||||
"r-send-email": "Send an email",
|
||||
"r-to": "to",
|
||||
"r-of": "of",
|
||||
"r-subject": "subject",
|
||||
"r-rule-details": "Детайли за правилото",
|
||||
"r-d-move-to-top-gen": "Move card to top of its list",
|
||||
@@ -776,5 +776,13 @@
|
||||
"mark-all-as-read": "Mark all as read",
|
||||
"remove-all-read": "Remove all read",
|
||||
"allow-rename": "Allow Rename",
|
||||
"allowRenamePopup-title": "Allow Rename"
|
||||
"allowRenamePopup-title": "Allow Rename",
|
||||
"start-day-of-week": "Set day of the week start",
|
||||
"monday": "Monday",
|
||||
"tuesday": "Tuesday",
|
||||
"wednesday": "Wednesday",
|
||||
"thursday": "Thursday",
|
||||
"friday": "Friday",
|
||||
"saturday": "Saturday",
|
||||
"sunday": "Sunday"
|
||||
}
|
||||
+12
-4
@@ -166,8 +166,9 @@
|
||||
"cardStartVotingPopup-title": "Start a vote",
|
||||
"positiveVoteMembersPopup-title": "Proponents",
|
||||
"negativeVoteMembersPopup-title": "Opponents",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "Voting question",
|
||||
"vote-public": "Public vote",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "for it",
|
||||
"vote-against": "against",
|
||||
"cardDeletePopup-title": "Delete Card?",
|
||||
@@ -348,8 +349,6 @@
|
||||
"import-board-c": "Import board",
|
||||
"import-board-title-trello": "Import board from 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-sandstorm-warning": "Imported board will delete all existing data on board and replace it with imported board.",
|
||||
"from-trello": "From Trello",
|
||||
"from-wekan": "From previous export",
|
||||
"import-board-instruction-trello": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text.",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "of checklist",
|
||||
"r-send-email": "Send an email",
|
||||
"r-to": "to",
|
||||
"r-of": "of",
|
||||
"r-subject": "subject",
|
||||
"r-rule-details": "Rule details",
|
||||
"r-d-move-to-top-gen": "Move card to top of its list",
|
||||
@@ -776,5 +776,13 @@
|
||||
"mark-all-as-read": "Mark all as read",
|
||||
"remove-all-read": "Remove all read",
|
||||
"allow-rename": "Allow Rename",
|
||||
"allowRenamePopup-title": "Allow Rename"
|
||||
"allowRenamePopup-title": "Allow Rename",
|
||||
"start-day-of-week": "Set day of the week start",
|
||||
"monday": "Monday",
|
||||
"tuesday": "Tuesday",
|
||||
"wednesday": "Wednesday",
|
||||
"thursday": "Thursday",
|
||||
"friday": "Friday",
|
||||
"saturday": "Saturday",
|
||||
"sunday": "Sunday"
|
||||
}
|
||||
+12
-4
@@ -166,8 +166,9 @@
|
||||
"cardStartVotingPopup-title": "Start a vote",
|
||||
"positiveVoteMembersPopup-title": "Proponents",
|
||||
"negativeVoteMembersPopup-title": "Opponents",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "Voting question",
|
||||
"vote-public": "Public vote",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "for it",
|
||||
"vote-against": "against",
|
||||
"cardDeletePopup-title": "Slett kort?",
|
||||
@@ -348,8 +349,6 @@
|
||||
"import-board-c": "Import board",
|
||||
"import-board-title-trello": "Import board from 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-sandstorm-warning": "Imported board will delete all existing data on board and replace it with imported board.",
|
||||
"from-trello": "From Trello",
|
||||
"from-wekan": "From previous export",
|
||||
"import-board-instruction-trello": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text.",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "of checklist",
|
||||
"r-send-email": "Send an email",
|
||||
"r-to": "til",
|
||||
"r-of": "of",
|
||||
"r-subject": "Emne",
|
||||
"r-rule-details": "Rule details",
|
||||
"r-d-move-to-top-gen": "Move card to top of its list",
|
||||
@@ -776,5 +776,13 @@
|
||||
"mark-all-as-read": "Mark all as read",
|
||||
"remove-all-read": "Remove all read",
|
||||
"allow-rename": "Allow Rename",
|
||||
"allowRenamePopup-title": "Allow Rename"
|
||||
"allowRenamePopup-title": "Allow Rename",
|
||||
"start-day-of-week": "Set day of the week start",
|
||||
"monday": "Monday",
|
||||
"tuesday": "Tuesday",
|
||||
"wednesday": "Wednesday",
|
||||
"thursday": "Thursday",
|
||||
"friday": "Friday",
|
||||
"saturday": "Saturday",
|
||||
"sunday": "Sunday"
|
||||
}
|
||||
+12
-4
@@ -166,8 +166,9 @@
|
||||
"cardStartVotingPopup-title": "Start een stemming",
|
||||
"positiveVoteMembersPopup-title": "Proponents",
|
||||
"negativeVoteMembersPopup-title": "Opponents",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "Stemvraag",
|
||||
"vote-public": "Public vote",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "Voor",
|
||||
"vote-against": "tegen",
|
||||
"cardDeletePopup-title": "Kaart verwijderen?",
|
||||
@@ -348,8 +349,6 @@
|
||||
"import-board-c": "Importeer bord",
|
||||
"import-board-title-trello": "Importeer bord vanuit Trello",
|
||||
"import-board-title-wekan": "Importeer bord vanuit eerdere export",
|
||||
"import-sandstorm-backup-warning": "Verwijder nog niet de data van je geëxporteerde Trello-bord totdat je vastgesteld hebt dat het Wekan-bord werkt. Doe dit door het nieuwe bord te sluiten en opnieuw te openen. Als er dan een foutmelding krijgt of het nieuwe bord opent niet dan kun je nog terugvallen op het originele bord. ",
|
||||
"import-sandstorm-warning": "Het geïmporteerde bord verwijdert alle huidige data op dit bord, om het daarna te vervangen.",
|
||||
"from-trello": "Vanuit Trello",
|
||||
"from-wekan": "Vanuit eerdere export",
|
||||
"import-board-instruction-trello": "Op jouw Trello bord, ga naar 'Menu', dan naar 'Meer', 'Print en Exporteer', 'Exporteer JSON', en kopieer de tekst.",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "van checklist",
|
||||
"r-send-email": "Verzend een email",
|
||||
"r-to": "naar",
|
||||
"r-of": "of",
|
||||
"r-subject": "onderwerp",
|
||||
"r-rule-details": "Regel details",
|
||||
"r-d-move-to-top-gen": "Verplaats kaart helemaal naar boven op de lijst",
|
||||
@@ -776,5 +776,13 @@
|
||||
"mark-all-as-read": "Markeer alles als gelezen",
|
||||
"remove-all-read": "verwijder alle gelezen",
|
||||
"allow-rename": "Sta Hernoemen toe",
|
||||
"allowRenamePopup-title": "Sta Hernoemen toe"
|
||||
"allowRenamePopup-title": "Sta Hernoemen toe",
|
||||
"start-day-of-week": "Set day of the week start",
|
||||
"monday": "Monday",
|
||||
"tuesday": "Tuesday",
|
||||
"wednesday": "Wednesday",
|
||||
"thursday": "Thursday",
|
||||
"friday": "Friday",
|
||||
"saturday": "Saturday",
|
||||
"sunday": "Sunday"
|
||||
}
|
||||
+12
-4
@@ -166,8 +166,9 @@
|
||||
"cardStartVotingPopup-title": "Start a vote",
|
||||
"positiveVoteMembersPopup-title": "Proponents",
|
||||
"negativeVoteMembersPopup-title": "Opponents",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "Voting question",
|
||||
"vote-public": "Public vote",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "for it",
|
||||
"vote-against": "against",
|
||||
"cardDeletePopup-title": "Suprimir la carta?",
|
||||
@@ -348,8 +349,6 @@
|
||||
"import-board-c": "Importar un tablèu",
|
||||
"import-board-title-trello": "Importar un tablèu dempuèi Trello",
|
||||
"import-board-title-wekan": "Importar un tablèu dempuèi un export passat",
|
||||
"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-sandstorm-warning": "Importar lo tablèu va quitar totes las donadas del tablèu e lo va remplaçar amb las donadas del tablèu importat.",
|
||||
"from-trello": "Dempuèi Trello",
|
||||
"from-wekan": "Dempuèi un export passat",
|
||||
"import-board-instruction-trello": "Dins vòstre tablèu Trello, vos cal anar dins \"Menut\", puèi \"Mai\", \"Export\", \"Export JSON\", e copiar lo tèxte balhat.",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "of checklist",
|
||||
"r-send-email": "Send an email",
|
||||
"r-to": "to",
|
||||
"r-of": "of",
|
||||
"r-subject": "subject",
|
||||
"r-rule-details": "Rule details",
|
||||
"r-d-move-to-top-gen": "Move card to top of its list",
|
||||
@@ -776,5 +776,13 @@
|
||||
"mark-all-as-read": "Mark all as read",
|
||||
"remove-all-read": "Remove all read",
|
||||
"allow-rename": "Allow Rename",
|
||||
"allowRenamePopup-title": "Allow Rename"
|
||||
"allowRenamePopup-title": "Allow Rename",
|
||||
"start-day-of-week": "Set day of the week start",
|
||||
"monday": "Monday",
|
||||
"tuesday": "Tuesday",
|
||||
"wednesday": "Wednesday",
|
||||
"thursday": "Thursday",
|
||||
"friday": "Friday",
|
||||
"saturday": "Saturday",
|
||||
"sunday": "Sunday"
|
||||
}
|
||||
+23
-15
@@ -152,8 +152,8 @@
|
||||
"card-spent": "Spędzony czas",
|
||||
"card-edit-attachments": "Edytuj załączniki",
|
||||
"card-edit-custom-fields": "Edytuj niestandardowe pola",
|
||||
"card-start-voting": "Start voting",
|
||||
"card-cancel-voting": "Delete voting and all votes",
|
||||
"card-start-voting": "Rozpocznij głosowanie",
|
||||
"card-cancel-voting": "Usuń głosowanie i wszystkie głosy",
|
||||
"card-edit-labels": "Edytuj etykiety",
|
||||
"card-edit-members": "Edytuj członków",
|
||||
"card-labels-title": "Zmień etykiety karty",
|
||||
@@ -163,13 +163,14 @@
|
||||
"cardAttachmentsPopup-title": "Dodaj załącznik z",
|
||||
"cardCustomField-datePopup-title": "Zmień datę",
|
||||
"cardCustomFieldsPopup-title": "Edytuj niestandardowe pola",
|
||||
"cardStartVotingPopup-title": "Start a vote",
|
||||
"positiveVoteMembersPopup-title": "Proponents",
|
||||
"negativeVoteMembersPopup-title": "Opponents",
|
||||
"vote-question": "Voting question",
|
||||
"vote-public": "Public vote",
|
||||
"vote-for-it": "for it",
|
||||
"vote-against": "against",
|
||||
"cardStartVotingPopup-title": "Zacznij głosowanie",
|
||||
"positiveVoteMembersPopup-title": "Zwolennicy",
|
||||
"negativeVoteMembersPopup-title": "Przeciwnicy",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "Pytanie do głosowania",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "za",
|
||||
"vote-against": "przeciwko",
|
||||
"cardDeletePopup-title": "Usunąć kartę?",
|
||||
"cardDetailsActionsPopup-title": "Czynności kart",
|
||||
"cardLabelsPopup-title": "Etykiety",
|
||||
@@ -328,7 +329,7 @@
|
||||
"filter-clear": "Usuń filter",
|
||||
"filter-no-label": "Brak etykiety",
|
||||
"filter-no-member": "Brak członków",
|
||||
"filter-no-assignee": "No assignee",
|
||||
"filter-no-assignee": "Nieprzypisane ",
|
||||
"filter-no-custom-fields": "Brak niestandardowych pól",
|
||||
"filter-show-archive": "Pokaż zarchiwizowane listy",
|
||||
"filter-hide-empty": "Ukryj puste listy",
|
||||
@@ -348,8 +349,6 @@
|
||||
"import-board-c": "Import tablicy",
|
||||
"import-board-title-trello": "Importuj tablicę z Trello",
|
||||
"import-board-title-wekan": "Importuj tablicę z poprzedniego eksportu",
|
||||
"import-sandstorm-backup-warning": "Nie usuwaj danych, które importujesz ze źródłowej tablicy lub Trello zanim upewnisz się, że wszystko zostało prawidłowo przeniesione przy czym brane jest pod uwagę ponowne uruchomienie strony, ponieważ w przypadku błędu braku tablicy stracisz dane.",
|
||||
"import-sandstorm-warning": "Zaimportowana tablica usunie wszystkie istniejące dane na aktualnej tablicy oraz zastąpi ją danymi z tej importowanej.",
|
||||
"from-trello": "Z Trello",
|
||||
"from-wekan": "Z poprzedniego eksportu",
|
||||
"import-board-instruction-trello": "W twojej tablicy na Trello przejdź do 'Menu', następnie 'Więcej', 'Drukuj i eksportuj', 'Eksportuj jako JSON' i skopiuj wynik",
|
||||
@@ -450,7 +449,7 @@
|
||||
"save": "Zapisz",
|
||||
"search": "Wyszukaj",
|
||||
"rules": "Reguły",
|
||||
"search-cards": "Search from card/list titles, descriptions and custom fields on this board",
|
||||
"search-cards": "Szukaj w tytułach kart/list oraz opisach i niestandardowych polach na tej tablicy",
|
||||
"search-example": "Czego mam szukać?",
|
||||
"select-color": "Wybierz kolor",
|
||||
"set-wip-limit-value": "Ustaw maksymalny limit zadań na tej liście",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "z listy zadań",
|
||||
"r-send-email": "Wyślij wiadomość email",
|
||||
"r-to": "do",
|
||||
"r-of": "of",
|
||||
"r-subject": "temat",
|
||||
"r-rule-details": "Szczegóły reguł",
|
||||
"r-d-move-to-top-gen": "Przenieś kartę na górę tej listy",
|
||||
@@ -774,7 +774,15 @@
|
||||
"view-all": "Wyświetl wszystko",
|
||||
"filter-by-unread": "Filtruj nieprzeczytane",
|
||||
"mark-all-as-read": "Zaznacz wszystkie jako przeczytane",
|
||||
"remove-all-read": "Remove all read",
|
||||
"remove-all-read": "Usuń wszystkie przeczytane",
|
||||
"allow-rename": "Zezwól na zmianę nazwy",
|
||||
"allowRenamePopup-title": "Zezwól na zmianę nazwy"
|
||||
"allowRenamePopup-title": "Zezwól na zmianę nazwy",
|
||||
"start-day-of-week": "Set day of the week start",
|
||||
"monday": "Monday",
|
||||
"tuesday": "Tuesday",
|
||||
"wednesday": "Wednesday",
|
||||
"thursday": "Thursday",
|
||||
"friday": "Friday",
|
||||
"saturday": "Saturday",
|
||||
"sunday": "Sunday"
|
||||
}
|
||||
+13
-5
@@ -163,11 +163,12 @@
|
||||
"cardAttachmentsPopup-title": "Anexar a partir de",
|
||||
"cardCustomField-datePopup-title": "Mudar data",
|
||||
"cardCustomFieldsPopup-title": "Editar campos customizados",
|
||||
"cardStartVotingPopup-title": "Iniciar um voto",
|
||||
"cardStartVotingPopup-title": "Iniciar uma votação",
|
||||
"positiveVoteMembersPopup-title": "Proponentes",
|
||||
"negativeVoteMembersPopup-title": "Oponentes",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "Questão em votação",
|
||||
"vote-public": "Voto público",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "a favor",
|
||||
"vote-against": "contra",
|
||||
"cardDeletePopup-title": "Excluir Cartão?",
|
||||
@@ -348,8 +349,6 @@
|
||||
"import-board-c": "Importar quadro",
|
||||
"import-board-title-trello": "Importar quadro do Trello",
|
||||
"import-board-title-wekan": "Importar quadro a partir de exportação prévia",
|
||||
"import-sandstorm-backup-warning": "Não exclua os dados importados do quadro original exportado ou do Trello antes de verificar se esse item fecha e abre novamente, ou se você receber o erro Quadro não encontrado, que significa perda de dados.",
|
||||
"import-sandstorm-warning": "O quadro importado irá excluir todos os dados existentes no quadro e irá sobrescrever com o quadro importado.",
|
||||
"from-trello": "Do Trello",
|
||||
"from-wekan": "A partir de exportação prévia",
|
||||
"import-board-instruction-trello": "No seu quadro do Trello, vá em 'Menu', depois em 'Mais', 'Imprimir e Exportar', 'Exportar JSON', então copie o texto emitido",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "da lista de verificação",
|
||||
"r-send-email": "Enviar um e-mail",
|
||||
"r-to": "para",
|
||||
"r-of": "of",
|
||||
"r-subject": "assunto",
|
||||
"r-rule-details": "Detalhes da regra",
|
||||
"r-d-move-to-top-gen": "Mover cartão para o topo da sua lista",
|
||||
@@ -776,5 +776,13 @@
|
||||
"mark-all-as-read": "Marcar todas como lidas",
|
||||
"remove-all-read": "Remover todas lidas",
|
||||
"allow-rename": "Permitir renomear",
|
||||
"allowRenamePopup-title": "Permitir renomear"
|
||||
"allowRenamePopup-title": "Permitir renomear",
|
||||
"start-day-of-week": "Definir dia em que a semana começa",
|
||||
"monday": "Segunda",
|
||||
"tuesday": "Terça",
|
||||
"wednesday": "Quarta",
|
||||
"thursday": "Quinta",
|
||||
"friday": "Sexta",
|
||||
"saturday": "Sábado",
|
||||
"sunday": "Domingo"
|
||||
}
|
||||
+12
-4
@@ -166,8 +166,9 @@
|
||||
"cardStartVotingPopup-title": "Start a vote",
|
||||
"positiveVoteMembersPopup-title": "Proponents",
|
||||
"negativeVoteMembersPopup-title": "Opponents",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "Voting question",
|
||||
"vote-public": "Public vote",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "for it",
|
||||
"vote-against": "against",
|
||||
"cardDeletePopup-title": "Apagar Cartão?",
|
||||
@@ -348,8 +349,6 @@
|
||||
"import-board-c": "Importar quadro",
|
||||
"import-board-title-trello": "Importar quadro do Trello",
|
||||
"import-board-title-wekan": "Importar quadro a partir de exportação prévia",
|
||||
"import-sandstorm-backup-warning": "Não apague os dados importados do quadro original exportado ou do Trello antes de verificar se esse item fecha e abre novamente, ou se receber o erro Quadro não encontrado, que significa perda de dados.",
|
||||
"import-sandstorm-warning": "O quadro importado irá apagar todos os dados existentes no quadro e irá sobrescrever com o quadro importado.",
|
||||
"from-trello": "Do Trello",
|
||||
"from-wekan": "A partir de exportação prévia",
|
||||
"import-board-instruction-trello": "No seu quadro do Trello, vá em 'Menu', depois em 'Mais', 'Imprimir e Exportar', 'Exportar JSON', e copie o texto resultante.",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "da lista de verificação",
|
||||
"r-send-email": "Enviar um e-mail",
|
||||
"r-to": "para",
|
||||
"r-of": "of",
|
||||
"r-subject": "assunto",
|
||||
"r-rule-details": "Detalhes da regra",
|
||||
"r-d-move-to-top-gen": "Mover cartão para o topo da sua lista",
|
||||
@@ -776,5 +776,13 @@
|
||||
"mark-all-as-read": "Mark all as read",
|
||||
"remove-all-read": "Remove all read",
|
||||
"allow-rename": "Allow Rename",
|
||||
"allowRenamePopup-title": "Allow Rename"
|
||||
"allowRenamePopup-title": "Allow Rename",
|
||||
"start-day-of-week": "Set day of the week start",
|
||||
"monday": "Monday",
|
||||
"tuesday": "Tuesday",
|
||||
"wednesday": "Wednesday",
|
||||
"thursday": "Thursday",
|
||||
"friday": "Friday",
|
||||
"saturday": "Saturday",
|
||||
"sunday": "Sunday"
|
||||
}
|
||||
+12
-4
@@ -166,8 +166,9 @@
|
||||
"cardStartVotingPopup-title": "Start a vote",
|
||||
"positiveVoteMembersPopup-title": "Proponents",
|
||||
"negativeVoteMembersPopup-title": "Opponents",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "Voting question",
|
||||
"vote-public": "Public vote",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "for it",
|
||||
"vote-against": "against",
|
||||
"cardDeletePopup-title": "Delete Card?",
|
||||
@@ -348,8 +349,6 @@
|
||||
"import-board-c": "Import board",
|
||||
"import-board-title-trello": "Import board from 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-sandstorm-warning": "Imported board will delete all existing data on board and replace it with imported board.",
|
||||
"from-trello": "From Trello",
|
||||
"from-wekan": "From previous export",
|
||||
"import-board-instruction-trello": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "of checklist",
|
||||
"r-send-email": "Send an email",
|
||||
"r-to": "to",
|
||||
"r-of": "of",
|
||||
"r-subject": "subject",
|
||||
"r-rule-details": "Rule details",
|
||||
"r-d-move-to-top-gen": "Move card to top of its list",
|
||||
@@ -776,5 +776,13 @@
|
||||
"mark-all-as-read": "Mark all as read",
|
||||
"remove-all-read": "Remove all read",
|
||||
"allow-rename": "Allow Rename",
|
||||
"allowRenamePopup-title": "Allow Rename"
|
||||
"allowRenamePopup-title": "Allow Rename",
|
||||
"start-day-of-week": "Set day of the week start",
|
||||
"monday": "Monday",
|
||||
"tuesday": "Tuesday",
|
||||
"wednesday": "Wednesday",
|
||||
"thursday": "Thursday",
|
||||
"friday": "Friday",
|
||||
"saturday": "Saturday",
|
||||
"sunday": "Sunday"
|
||||
}
|
||||
+14
-6
@@ -164,10 +164,11 @@
|
||||
"cardCustomField-datePopup-title": "Изменить дату",
|
||||
"cardCustomFieldsPopup-title": "редактировать настраиваемые поля",
|
||||
"cardStartVotingPopup-title": "Голосовать",
|
||||
"positiveVoteMembersPopup-title": "Proponents",
|
||||
"negativeVoteMembersPopup-title": "Opponents",
|
||||
"positiveVoteMembersPopup-title": "Сторонники",
|
||||
"negativeVoteMembersPopup-title": "Противники",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "Вопрос для голосования",
|
||||
"vote-public": "Public vote",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "за",
|
||||
"vote-against": "против",
|
||||
"cardDeletePopup-title": "Удалить карточку?",
|
||||
@@ -348,8 +349,6 @@
|
||||
"import-board-c": "Импортировать доску",
|
||||
"import-board-title-trello": "Импортировать доску из Trello",
|
||||
"import-board-title-wekan": "Импортировать доску, сохраненную ранее.",
|
||||
"import-sandstorm-backup-warning": "Не удаляйте импортируемые данные из ранее сохраненной доски или Trello, пока не убедитесь, что импорт завершился успешно – удается закрыть и снова открыть доску, и не появляется ошибка «Доска не найдена», что означает потерю данных.",
|
||||
"import-sandstorm-warning": "Импортированная доска удалит все существующие данные на текущей доске и заменит её импортированной доской.",
|
||||
"from-trello": "Из Trello",
|
||||
"from-wekan": "Сохраненную ранее",
|
||||
"import-board-instruction-trello": "На вашей Trello доске нажмите “Menu” - “More” - “Print and export - “Export JSON” и скопируйте полученный текст",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "контрольного списка",
|
||||
"r-send-email": "Отправить письмо",
|
||||
"r-to": "кому",
|
||||
"r-of": "of",
|
||||
"r-subject": "тема",
|
||||
"r-rule-details": "Содержание правила",
|
||||
"r-d-move-to-top-gen": "Переместить карточку в начало текущего списка",
|
||||
@@ -776,5 +776,13 @@
|
||||
"mark-all-as-read": "Отметить все как прочитанные",
|
||||
"remove-all-read": "Удалить все прочитанные",
|
||||
"allow-rename": "Разрешить переименование",
|
||||
"allowRenamePopup-title": "Разрешить переименование"
|
||||
"allowRenamePopup-title": "Разрешить переименование",
|
||||
"start-day-of-week": "Установить день начала недели",
|
||||
"monday": "Понедельник",
|
||||
"tuesday": "Вторник",
|
||||
"wednesday": "Среда",
|
||||
"thursday": "Четверг",
|
||||
"friday": "Пятница",
|
||||
"saturday": "Суббота",
|
||||
"sunday": "Воскресенье"
|
||||
}
|
||||
+12
-4
@@ -166,8 +166,9 @@
|
||||
"cardStartVotingPopup-title": "Start a vote",
|
||||
"positiveVoteMembersPopup-title": "Proponents",
|
||||
"negativeVoteMembersPopup-title": "Opponents",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "Voting question",
|
||||
"vote-public": "Public vote",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "for it",
|
||||
"vote-against": "against",
|
||||
"cardDeletePopup-title": "Briši kartico?",
|
||||
@@ -348,8 +349,6 @@
|
||||
"import-board-c": "Uvozi tablo",
|
||||
"import-board-title-trello": "Uvozi tablo iz orodja Trello",
|
||||
"import-board-title-wekan": "Uvozi tablo iz prejšnjega izvoza",
|
||||
"import-sandstorm-backup-warning": "Ne zbrišite podatkov, ki jih uvozite z originalne izvožene table ali Trello, preden preverite ali se tabla uspešno zapre in odpre ali pa boste dobili sporočilo Tabla ni najdena, kar pomeni izgubo podatkov.",
|
||||
"import-sandstorm-warning": "Uvožena tabla bo izbrisala vse obstoječe podatke na tabli in jih zamenjala z uvoženo tablo.",
|
||||
"from-trello": "Iz orodja Trello",
|
||||
"from-wekan": "Od prejšnjega izvoza",
|
||||
"import-board-instruction-trello": "V vaši Trello tabli pojdite na 'Meni', 'Več', 'Natisni in Izvozi', 'Izvozi JSON', in kopirajte prikazano besedilo.",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "kontrolnega seznama",
|
||||
"r-send-email": "Pošlji e-pošto",
|
||||
"r-to": "naslovnik",
|
||||
"r-of": "of",
|
||||
"r-subject": "zadeva",
|
||||
"r-rule-details": "Podrobnosti pravila",
|
||||
"r-d-move-to-top-gen": "Premakni kartico na vrh pripadajočega sezama",
|
||||
@@ -776,5 +776,13 @@
|
||||
"mark-all-as-read": "Mark all as read",
|
||||
"remove-all-read": "Remove all read",
|
||||
"allow-rename": "Allow Rename",
|
||||
"allowRenamePopup-title": "Allow Rename"
|
||||
"allowRenamePopup-title": "Allow Rename",
|
||||
"start-day-of-week": "Set day of the week start",
|
||||
"monday": "Monday",
|
||||
"tuesday": "Tuesday",
|
||||
"wednesday": "Wednesday",
|
||||
"thursday": "Thursday",
|
||||
"friday": "Friday",
|
||||
"saturday": "Saturday",
|
||||
"sunday": "Sunday"
|
||||
}
|
||||
+12
-4
@@ -166,8 +166,9 @@
|
||||
"cardStartVotingPopup-title": "Novo glasanje",
|
||||
"positiveVoteMembersPopup-title": "Proponents",
|
||||
"negativeVoteMembersPopup-title": "Opponents",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "Pitanje za glasanje",
|
||||
"vote-public": "Public vote",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "za",
|
||||
"vote-against": "protiv",
|
||||
"cardDeletePopup-title": "Obrisati karticu?",
|
||||
@@ -348,8 +349,6 @@
|
||||
"import-board-c": "Import board",
|
||||
"import-board-title-trello": "Uvezi tablu iz Trella",
|
||||
"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-sandstorm-warning": "Imported board will delete all existing data on board and replace it with imported board.",
|
||||
"from-trello": "From Trello",
|
||||
"from-wekan": "From previous export",
|
||||
"import-board-instruction-trello": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text.",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "of checklist",
|
||||
"r-send-email": "Pošalji epoštu",
|
||||
"r-to": "za",
|
||||
"r-of": "of",
|
||||
"r-subject": "naslov",
|
||||
"r-rule-details": "Detalji pravila",
|
||||
"r-d-move-to-top-gen": "Move card to top of its list",
|
||||
@@ -776,5 +776,13 @@
|
||||
"mark-all-as-read": "Označi sve kao pročitano",
|
||||
"remove-all-read": "Ukloni sve pročitano",
|
||||
"allow-rename": "Dozvoli preimenovanje",
|
||||
"allowRenamePopup-title": "Dozvoli preimenovanje"
|
||||
"allowRenamePopup-title": "Dozvoli preimenovanje",
|
||||
"start-day-of-week": "Set day of the week start",
|
||||
"monday": "Monday",
|
||||
"tuesday": "Tuesday",
|
||||
"wednesday": "Wednesday",
|
||||
"thursday": "Thursday",
|
||||
"friday": "Friday",
|
||||
"saturday": "Saturday",
|
||||
"sunday": "Sunday"
|
||||
}
|
||||
+12
-4
@@ -166,8 +166,9 @@
|
||||
"cardStartVotingPopup-title": "Start a vote",
|
||||
"positiveVoteMembersPopup-title": "Proponents",
|
||||
"negativeVoteMembersPopup-title": "Opponents",
|
||||
"allowNonBoardMembers": "Allow anonymous vote on public board",
|
||||
"vote-question": "Voting question",
|
||||
"vote-public": "Public vote",
|
||||
"vote-public": "Show who voted what",
|
||||
"vote-for-it": "for it",
|
||||
"vote-against": "against",
|
||||
"cardDeletePopup-title": "Ta bort kort?",
|
||||
@@ -348,8 +349,6 @@
|
||||
"import-board-c": "Importera anslagstavla",
|
||||
"import-board-title-trello": "Importera anslagstavla från Trello",
|
||||
"import-board-title-wekan": "Importera anslagstavla från tidigare export",
|
||||
"import-sandstorm-backup-warning": "Ta inte bort data som du importerar från exporterad original-tavla eller Trello innan du kontrollerar att det här spannet stänger och öppnas igen, eller får du felmeddelandet Anslagstavla hittades inte, det vill säga dataförlust.",
|
||||
"import-sandstorm-warning": "Importerad anslagstavla raderar all befintlig data på anslagstavla och ersätter den med importerat anslagstavla.",
|
||||
"from-trello": "Från Trello",
|
||||
"from-wekan": "Från tidigare export",
|
||||
"import-board-instruction-trello": "I din Trello-anslagstavla, gå till 'Meny', sedan 'Mera', 'Skriv ut och exportera', 'Exportera JSON' och kopiera den resulterande text.",
|
||||
@@ -675,6 +674,7 @@
|
||||
"r-of-checklist": "av checklistan",
|
||||
"r-send-email": "Skicka ett e-postmeddelande",
|
||||
"r-to": "till",
|
||||
"r-of": "of",
|
||||
"r-subject": "änme",
|
||||
"r-rule-details": "Regeldetaljer",
|
||||
"r-d-move-to-top-gen": "Flytta kort till toppen av sin lista",
|
||||
@@ -776,5 +776,13 @@
|
||||
"mark-all-as-read": "Mark all as read",
|
||||
"remove-all-read": "Remove all read",
|
||||
"allow-rename": "Allow Rename",
|
||||
"allowRenamePopup-title": "Allow Rename"
|
||||
"allowRenamePopup-title": "Allow Rename",
|
||||
"start-day-of-week": "Set day of the week start",
|
||||
"monday": "Monday",
|
||||
"tuesday": "Tuesday",
|
||||
"wednesday": "Wednesday",
|
||||
"thursday": "Thursday",
|
||||
"friday": "Friday",
|
||||
"saturday": "Saturday",
|
||||
"sunday": "Sunday"
|
||||
}
|
||||
Loaded 100 of 136 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user