summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.eslintrc.json4
-rw-r--r--.meteor/packages3
-rw-r--r--.meteor/versions2
-rw-r--r--.travis.yml4
-rw-r--r--CHANGELOG.md34
-rw-r--r--Dockerfile37
-rw-r--r--README.md16
-rw-r--r--client/components/boards/boardHeader.jade19
-rw-r--r--client/components/boards/boardHeader.js50
-rw-r--r--client/components/cards/checklists.jade4
-rw-r--r--client/components/cards/checklists.js24
-rw-r--r--client/components/cards/checklists.styl3
-rw-r--r--client/components/import/import.jade4
-rw-r--r--client/components/import/import.js48
-rw-r--r--client/components/import/trelloMembersMapper.js14
-rw-r--r--client/components/import/wekanMembersMapper.js24
-rw-r--r--config/router.js20
-rw-r--r--docker-compose.yml2
-rw-r--r--i18n/ar.i18n.json103
-rw-r--r--i18n/br.i18n.json17
-rw-r--r--i18n/ca.i18n.json17
-rw-r--r--i18n/cs.i18n.json17
-rw-r--r--i18n/de.i18n.json17
-rw-r--r--i18n/en-GB.i18n.json17
-rw-r--r--i18n/en.i18n.json17
-rw-r--r--i18n/eo.i18n.json17
-rw-r--r--i18n/es.i18n.json17
-rw-r--r--i18n/eu.i18n.json17
-rw-r--r--i18n/fa.i18n.json17
-rw-r--r--i18n/fi.i18n.json17
-rw-r--r--i18n/fr.i18n.json17
-rw-r--r--i18n/he.i18n.json23
-rw-r--r--i18n/hu.i18n.json17
-rw-r--r--i18n/id.i18n.json17
-rw-r--r--i18n/it.i18n.json17
-rw-r--r--i18n/ja.i18n.json17
-rw-r--r--i18n/ko.i18n.json63
-rw-r--r--i18n/nb.i18n.json17
-rw-r--r--i18n/pl.i18n.json17
-rw-r--r--i18n/pt-BR.i18n.json39
-rw-r--r--i18n/ro.i18n.json17
-rw-r--r--i18n/ru.i18n.json51
-rw-r--r--i18n/sr.i18n.json17
-rw-r--r--i18n/sv.i18n.json35
-rw-r--r--i18n/ta.i18n.json17
-rw-r--r--i18n/th.i18n.json17
-rw-r--r--i18n/tr.i18n.json85
-rw-r--r--i18n/uk.i18n.json17
-rw-r--r--i18n/vi.i18n.json17
-rw-r--r--i18n/zh-CN.i18n.json47
-rw-r--r--i18n/zh-TW.i18n.json17
-rw-r--r--models/activities.js5
-rw-r--r--models/export.js24
-rw-r--r--models/import.js513
-rw-r--r--models/integrations.js54
-rw-r--r--models/trelloCreator.js500
-rw-r--r--models/wekanCreator.js493
-rw-r--r--package.json2
-rw-r--r--server/notifications/outgoing.js47
-rw-r--r--server/publications/boards.js1
-rw-r--r--snapcraft.yaml11
61 files changed, 1942 insertions, 891 deletions
diff --git a/.eslintrc.json b/.eslintrc.json
index 64e2b702..c1ee03b8 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -127,6 +127,8 @@
"InvitationCodes": true,
"Winston":true,
"JsonRoutes": true,
- "Authentication": true
+ "Authentication": true,
+ "Integrations": true,
+ "HTTP": true
}
}
diff --git a/.meteor/packages b/.meteor/packages
index 1705935f..7c04f207 100644
--- a/.meteor/packages
+++ b/.meteor/packages
@@ -33,7 +33,6 @@ kenton:accounts-sandstorm
service-configuration@1.0.11
useraccounts:unstyled
useraccounts:flow-routing
-email@1.2.0
# Utilities
check@1.2.5
@@ -57,6 +56,7 @@ mquandalle:moment
ongoworks:speakingurl
raix:handlebar-helpers
tap:i18n
+http
# UI components
blaze
@@ -78,3 +78,4 @@ kadira:flow-router
shell-server@0.2.3
simple:rest-accounts-password
useraccounts:core
+email@1.2.3
diff --git a/.meteor/versions b/.meteor/versions
index 4c1fccf4..416093aa 100644
--- a/.meteor/versions
+++ b/.meteor/versions
@@ -53,7 +53,7 @@ diff-sequence@1.0.7
ecmascript@0.7.3
ecmascript-runtime@0.3.15
ejson@1.0.13
-email@1.2.1
+email@1.2.3
es5-shim@4.6.15
fastclick@1.0.13
fortawesome:fontawesome@4.7.0
diff --git a/.travis.yml b/.travis.yml
index 1bdc5b25..bad4d10e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,8 +3,8 @@ sudo: required
env:
TRAVIS_DOCKER_COMPOSE_VERSION: 1.12.0
- TRAVIS_NODE_VERSION: 4.8.1
- TRAVIS_NPM_VERSION: 4.4.4
+ TRAVIS_NODE_VERSION: 4.8.4
+ TRAVIS_NPM_VERSION: 4.6.1
before_install:
- sudo apt-get update -y
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b4e4a3ec..4d9acf35 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,37 @@
+# Upcoming Wekan release
+
+This release adds the following new features:
+
+* [Export and import attachments as base64 encoded files](https://github.com/wekan/wekan/pull/1134);
+
+Thanks to GitHub user GhassenRjab for contributions.
+
+# v0.28 2017-07-15 Wekan release
+
+SECURITY ISSUE [Files accessible without authentication](https://github.com/wekan/wekan/issues/1105)
+IS NOT FIXED YET.
+
+This release adds the following new features:
+
+* [REST API: Add PUT method to update a card](https://github.com/wekan/wekan/pull/1095) and
+ [related fix](https://github.com/wekan/wekan/pull/1097);
+* [When finished input of checklist item, open new checklist
+ item](https://github.com/wekan/wekan/pull/1099);
+* [Improve UI design of checklist items](https://github.com/wekan/wekan/pull/1108);
+* [Import Wekan board](https://github.com/wekan/wekan/pull/1117);
+* [Outgoing Webhooks](https://github.com/wekan/wekan/pull/1119);
+* [Wekan wiki now has menu with categories](https://github.com/wekan/wekan/wiki).
+
+and fixes the following bugs:
+
+* [SECURITY: Upgrade Node.js, MongoDB and Debian on Docker and Ubuntu snap edge](https://github.com/wekan/wekan/pull/1132);
+* [Possible to add empty item to checklist](https://github.com/wekan/wekan/pull/1107);
+* [Double-slash issue](https://github.com/wekan/wekan/pull/1114);
+* [Node.js crash when adding new user to board](https://github.com/wekan/wekan/issues/1131).
+
+Thanks to GitHub users GhassenRjab, johnleeming, jtickle, nztqa, xet7 and zarnifoulette
+for their contributions.
+
# v0.27 2017-06-28 Wekan release
This release adds the following new features:
diff --git a/Dockerfile b/Dockerfile
index f7e76ee8..77f55ff3 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM debian:8.7
+FROM debian:8.8
MAINTAINER wekan
# Declare Arguments
@@ -14,7 +14,7 @@ ARG SRC_PATH
# Set the environment variables (defaults where required)
ENV BUILD_DEPS="wget curl bzip2 build-essential python git ca-certificates gcc-4.9"
ENV GOSU_VERSION=1.10
-ENV NODE_VERSION ${NODE_VERSION:-v4.8.1}
+ENV NODE_VERSION ${NODE_VERSION:-v4.8.4}
ENV METEOR_RELEASE ${METEOR_RELEASE:-1.4.4.1}
ENV USE_EDGE ${USE_EDGE:-false}
ENV METEOR_EDGE ${METEOR_EDGE:-1.5-beta.17}
@@ -95,27 +95,32 @@ RUN \
if [ "$USE_EDGE" = false ]; then \
gosu wekan:wekan sh ./install_meteor.sh; \
else \
- gosu wekan:wekan git clone --recursive --depth 1 -b release/METEOR@${METEOR_EDGE} git://github.com/meteor/meteor.git /home/wekan/.meteor && \
- cd /home/wekan/packages && \
- gosu wekan:wekan git clone --depth 1 -b master git://github.com/wekan/flow-router.git kadira-flow-router && \
- gosu wekan:wekan git clone --depth 1 -b master git://github.com/meteor-useraccounts/core.git meteor-useraccounts-core && \
- cd /home/wekan/.meteor && \
- gosu wekan /home/wekan/.meteor/meteor -- help; \
- fi && \
+ gosu wekan:wekan git clone --recursive --depth 1 -b release/METEOR@${METEOR_EDGE} git://github.com/meteor/meteor.git /home/wekan/.meteor; \
+ fi; \
+ \
+ # Get additional packages
+ mkdir -p /home/wekan/.meteor/packages && \
+ chown wekan:wekan --recursive /home/wekan/.meteor && \
+ cd /home/wekan/.meteor/packages && \
+ gosu wekan:wekan git clone --depth 1 -b master git://github.com/wekan/flow-router.git kadira-flow-router && \
+ gosu wekan:wekan git clone --depth 1 -b master git://github.com/meteor-useraccounts/core.git meteor-useraccounts-core && \
+ sed -i 's/api\.versionsFrom/\/\/api.versionsFrom/' /home/wekan/.meteor/packages/meteor-useraccounts-core/package.js && \
+ cd /home/wekan/.meteor && \
+ gosu wekan:wekan /home/wekan/.meteor/meteor -- help; \
\
# Build app
cd /home/wekan/app && \
- gosu wekan /home/wekan/.meteor/meteor add standard-minifier-js && \
- gosu wekan /home/wekan/.meteor/meteor npm install && \
- gosu wekan /home/wekan/.meteor/meteor build --directory /home/wekan/app_build && \
+ gosu wekan:wekan /home/wekan/.meteor/meteor add standard-minifier-js && \
+ gosu wekan:wekan /home/wekan/.meteor/meteor npm install && \
+ gosu wekan:wekan /home/wekan/.meteor/meteor build --directory /home/wekan/app_build && \
cp /home/wekan/app/fix-download-unicode/cfs_access-point.txt /home/wekan/app_build/bundle/programs/server/packages/cfs_access-point.js && \
chown wekan:wekan /home/wekan/app_build/bundle/programs/server/packages/cfs_access-point.js && \
- gosu wekan sed -i "s|build\/Release\/bson|browser_build\/bson|g" /home/wekan/app_build/bundle/programs/server/npm/node_modules/meteor/cfs_gridfs/node_modules/mongodb/node_modules/bson/ext/index.js && \
+ gosu wekan:wekan sed -i "s|build\/Release\/bson|browser_build\/bson|g" /home/wekan/app_build/bundle/programs/server/npm/node_modules/meteor/cfs_gridfs/node_modules/mongodb/node_modules/bson/ext/index.js && \
cd /home/wekan/app_build/bundle/programs/server/npm/node_modules/meteor/npm-bcrypt && \
- gosu wekan rm -rf node_modules/bcrypt && \
- gosu wekan npm install bcrypt && \
+ gosu wekan:wekan rm -rf node_modules/bcrypt && \
+ gosu wekan:wekan npm install bcrypt && \
cd /home/wekan/app_build/bundle/programs/server/ && \
- gosu wekan npm install && \
+ gosu wekan:wekan npm install && \
mv /home/wekan/app_build/bundle /build && \
\
# Cleanup
diff --git a/README.md b/README.md
index 522cca93..a49f138e 100644
--- a/README.md
+++ b/README.md
@@ -50,20 +50,20 @@ We also welcome sponsors for features, although we don't have any yet.
By working directly with Wekan you get the benefit of active maintenance
and new features added by growing Wekan developer community.
-[Roadmap is self-hosted on Wekan][roadmap_wefork]
+Actual work happens at [Wekan GitHub issues][wekan_issues].
-Roadmaps is not currently up-to-date. You can find more up-to-date
-info from [Features][features] and [Integrations][integrations] pages,
-where is links to [Wekan GitHub issues][wekan_issues] where actual
-work happens.
+See [Development links on Wekan
+wiki](https://github.com/wekan/wekan/wiki#Development)
+bottom of the page for more info.
+
+## Demo
+
+[Wekan demo][roadmap_wefork]
## Screenshot
[![Screenshot of Wekan][screenshot_wefork]][roadmap_wefork]
-Content is being copied from [old Wekan roadmap][roadmap_wekan] to
-new one in process of merging Wefork back to Wekan.
-
Since Wekan is a free software, you don’t have to trust us with your data and can
install Wekan on your own computer or server. In fact we encourage you to do
that by providing one-click installation on various platforms.
diff --git a/client/components/boards/boardHeader.jade b/client/components/boards/boardHeader.jade
index e61aea35..d33ee11b 100644
--- a/client/components/boards/boardHeader.jade
+++ b/client/components/boards/boardHeader.jade
@@ -112,6 +112,7 @@ template(name="boardMenuPopup")
ul.pop-over-list
li: a(href="{{exportUrl}}", download="{{exportFilename}}") {{_ 'export-board'}}
li: a.js-archive-board {{_ 'archive-board'}}
+ li: a.js-outgoing-webhooks {{_ 'outgoing-webhooks'}}
template(name="boardVisibilityList")
ul.pop-over-list
@@ -191,8 +192,14 @@ template(name="createBoard")
input.primary.wide(type="submit" value="{{_ 'create'}}")
span.quiet
| {{_ 'or'}}
- a(href="{{pathFor 'import'}}") {{_ 'import-board'}}
+ a.js-import-board {{_ 'import-board'}}
+template(name="chooseBoardSource")
+ ul.pop-over-list
+ li
+ a(href="{{pathFor 'import/trello'}}") {{_ 'from-trello'}}
+ li
+ a(href="{{pathFor 'import/wekan'}}") {{_ 'from-wekan'}}
template(name="boardChangeTitlePopup")
form
@@ -207,3 +214,13 @@ template(name="boardChangeTitlePopup")
template(name="archiveBoardPopup")
p {{_ 'close-board-pop'}}
button.js-confirm.negate.full(type="submit") {{_ 'archive'}}
+
+template(name="outgoingWebhooksPopup")
+ form
+ label
+ | URL
+ if integration.enabled
+ input.js-outgoing-webhooks-url(type="text" value=integration.url autofocus)
+ else
+ input.js-outgoing-webhooks-url(type="text" autofocus)
+ input.primary.wide(type="submit" value="{{_ 'save'}}")
diff --git a/client/components/boards/boardHeader.js b/client/components/boards/boardHeader.js
index 06defbfa..dafbfd30 100644
--- a/client/components/boards/boardHeader.js
+++ b/client/components/boards/boardHeader.js
@@ -13,6 +13,7 @@ Template.boardMenuPopup.events({
// confirm that the board was successfully archived.
FlowRouter.go('home');
}),
+ 'click .js-outgoing-webhooks': Popup.open('outgoingWebhooks'),
});
Template.boardMenuPopup.helpers({
@@ -174,10 +175,17 @@ const CreateBoard = BlazeComponent.extendComponent({
'click .js-change-visibility': this.toggleVisibilityMenu,
'click .js-import': Popup.open('boardImportBoard'),
submit: this.onSubmit,
+ 'click .js-import-board': Popup.open('chooseBoardSource'),
}];
},
}).register('createBoardPopup');
+BlazeComponent.extendComponent({
+ template() {
+ return 'chooseBoardSource';
+ },
+}).register('chooseBoardSourcePopup');
+
(class HeaderBarCreateBoard extends CreateBoard {
onSubmit(evt) {
super.onSubmit(evt);
@@ -227,3 +235,45 @@ BlazeComponent.extendComponent({
}];
},
}).register('boardChangeWatchPopup');
+
+BlazeComponent.extendComponent({
+ integration() {
+ const boardId = Session.get('currentBoard');
+ return Integrations.findOne({ boardId: `${boardId}` });
+ },
+
+ events() {
+ return [{
+ 'submit'(evt) {
+ evt.preventDefault();
+ const url = this.find('.js-outgoing-webhooks-url').value.trim();
+ const boardId = Session.get('currentBoard');
+ const integration = this.integration();
+ if (integration) {
+ if (url) {
+ Integrations.update(integration._id, {
+ $set: {
+ enabled: true,
+ url: `${url}`,
+ },
+ });
+ } else {
+ Integrations.update(integration._id, {
+ $set: {
+ enabled: false,
+ },
+ });
+ }
+ } else if (url) {
+ Integrations.insert({
+ enabled: true,
+ type: 'outgoing-webhooks',
+ url: `${url}`,
+ boardId: `${boardId}`,
+ });
+ }
+ Popup.close();
+ },
+ }];
+ },
+}).register('outgoingWebhooksPopup');
diff --git a/client/components/cards/checklists.jade b/client/components/cards/checklists.jade
index a0d89351..d04a9b60 100644
--- a/client/components/cards/checklists.jade
+++ b/client/components/cards/checklists.jade
@@ -4,7 +4,7 @@ template(name="checklists")
each checklist in currentCard.checklists
+checklistDetail(checklist = checklist)
if canModifyCard
- +inlinedForm(classNames="js-add-checklist" cardId = cardId)
+ +inlinedForm(autoclose=false classNames="js-add-checklist" cardId = cardId)
+addChecklistItemForm
else
a.js-open-inlined-form
@@ -53,7 +53,7 @@ template(name="checklistItems")
else
+itemDetail(item = item checklist = checklist)
if canModifyCard
- +inlinedForm(classNames="js-add-checklist-item" checklist = checklist)
+ +inlinedForm(autoclose=false classNames="js-add-checklist-item" checklist = checklist)
+addChecklistItemForm
else
a.add-checklist-item.js-open-inlined-form
diff --git a/client/components/cards/checklists.js b/client/components/cards/checklists.js
index b8f5e443..24a78035 100644
--- a/client/components/cards/checklists.js
+++ b/client/components/cards/checklists.js
@@ -4,10 +4,18 @@ BlazeComponent.extendComponent({
const textarea = this.find('textarea.js-add-checklist-item');
const title = textarea.value.trim();
const cardId = this.currentData().cardId;
- Checklists.insert({
- cardId,
- title,
- });
+
+ if (title) {
+ Checklists.insert({
+ cardId,
+ title,
+ });
+ setTimeout(() => {
+ this.$('.add-checklist-item').last().click();
+ }, 100);
+ }
+ textarea.value = '';
+ textarea.focus();
},
addChecklistItem(event) {
@@ -15,7 +23,13 @@ BlazeComponent.extendComponent({
const textarea = this.find('textarea.js-add-checklist-item');
const title = textarea.value.trim();
const checklist = this.currentData().checklist;
- checklist.addItem(title);
+
+ if (title) {
+ checklist.addItem(title);
+ }
+ // We keep the form opened, empty it.
+ textarea.value = '';
+ textarea.focus();
},
editChecklist(event) {
diff --git a/client/components/cards/checklists.styl b/client/components/cards/checklists.styl
index 885d7528..1156c577 100644
--- a/client/components/cards/checklists.styl
+++ b/client/components/cards/checklists.styl
@@ -46,6 +46,8 @@ textarea.js-add-checklist-item, textarea.js-edit-checklist-item
font-size: 1.1em
margin-top: 3px
display: flex
+ &:hover
+ background-color: darken(white, 8%)
.check-box
margin-top: 5px
@@ -54,6 +56,7 @@ textarea.js-add-checklist-item, textarea.js-edit-checklist-item
border-right: 2px solid #3cb500
.item-title
+ flex-grow: 1
padding-left: 10px;
&.is-checked
color: #8c8c8c
diff --git a/client/components/import/import.jade b/client/components/import/import.jade
index d4def7d8..d1b3489a 100644
--- a/client/components/import/import.jade
+++ b/client/components/import/import.jade
@@ -2,7 +2,7 @@ template(name="importHeaderBar")
h1
a.back-btn(href="{{pathFor 'home'}}")
i.fa.fa-chevron-left
- | {{_ 'import-board-title'}}
+ | {{_ title}}
template(name="import")
.wrapper
@@ -12,7 +12,7 @@ template(name="import")
template(name="importTextarea")
form
- p: label(for='import-textarea') {{_ 'import-board-trello-instruction'}}
+ p: label(for='import-textarea') {{_ instruction}}
textarea.js-import-json(placeholder="{{_ 'import-json-placeholder'}}" autofocus)
| {{jsonText}}
input.primary.wide(type="submit" value="{{_ 'import'}}")
diff --git a/client/components/import/import.js b/client/components/import/import.js
index 11a5308a..d72a02dd 100644
--- a/client/components/import/import.js
+++ b/client/components/import/import.js
@@ -1,3 +1,12 @@
+import trelloMembersMapper from './trelloMembersMapper';
+import wekanMembersMapper from './wekanMembersMapper';
+
+BlazeComponent.extendComponent({
+ title() {
+ return `import-board-title-${Session.get('importSource')}`;
+ },
+}).register('importHeaderBar');
+
BlazeComponent.extendComponent({
onCreated() {
this.error = new ReactiveVar('');
@@ -5,6 +14,7 @@ BlazeComponent.extendComponent({
this._currentStepIndex = new ReactiveVar(0);
this.importedData = new ReactiveVar();
this.membersToMap = new ReactiveVar([]);
+ this.importSource = Session.get('importSource');
},
currentTemplate() {
@@ -27,7 +37,10 @@ BlazeComponent.extendComponent({
const dataObject = JSON.parse(dataJson);
this.setError('');
this.importedData.set(dataObject);
- this._prepareAdditionalData(dataObject);
+ const membersToMap = this._prepareAdditionalData(dataObject);
+ // store members data and mapping in Session
+ // (we go deep and 2-way, so storing in data context is not a viable option)
+ this.membersToMap.set(membersToMap);
this.nextStep();
} catch (e) {
this.setError('error-json-malformed');
@@ -51,7 +64,10 @@ BlazeComponent.extendComponent({
additionalData.membersMapping = mappingById;
}
this.membersToMap.set([]);
- Meteor.call('importTrelloBoard', this.importedData.get(), additionalData,
+ Meteor.call('importBoard',
+ this.importedData.get(),
+ additionalData,
+ this.importSource,
(err, res) => {
if (err) {
this.setError(err.error);
@@ -63,20 +79,16 @@ BlazeComponent.extendComponent({
},
_prepareAdditionalData(dataObject) {
- // 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 = dataObject.members;
- // auto-map based on username
- membersToMap.forEach((importedMember) => {
- const wekanUser = Users.findOne({ username: importedMember.username });
- if (wekanUser) {
- importedMember.wekanId = wekanUser._id;
- }
- });
- // store members data and mapping in Session
- // (we go deep and 2-way, so storing in data context is not a viable option)
- this.membersToMap.set(membersToMap);
+ const importSource = Session.get('importSource');
+ let membersToMap;
+ switch (importSource) {
+ case 'trello':
+ membersToMap = trelloMembersMapper.getMembersToMap(dataObject);
+ break;
+ case 'wekan':
+ membersToMap = wekanMembersMapper.getMembersToMap(dataObject);
+ break;
+ }
return membersToMap;
},
@@ -90,6 +102,10 @@ BlazeComponent.extendComponent({
return 'importTextarea';
},
+ instruction() {
+ return `import-board-instruction-${Session.get('importSource')}`;
+ },
+
events() {
return [{
submit(evt) {
diff --git a/client/components/import/trelloMembersMapper.js b/client/components/import/trelloMembersMapper.js
new file mode 100644
index 00000000..0f353bf1
--- /dev/null
+++ b/client/components/import/trelloMembersMapper.js
@@ -0,0 +1,14 @@
+export function 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;
+ // auto-map based on username
+ membersToMap.forEach((importedMember) => {
+ const wekanUser = Users.findOne({ username: importedMember.username });
+ if (wekanUser) {
+ importedMember.wekanId = wekanUser._id;
+ }
+ });
+ return membersToMap;
+}
diff --git a/client/components/import/wekanMembersMapper.js b/client/components/import/wekanMembersMapper.js
new file mode 100644
index 00000000..f4c110f7
--- /dev/null
+++ b/client/components/import/wekanMembersMapper.js
@@ -0,0 +1,24 @@
+export function 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;
+}
diff --git a/config/router.js b/config/router.js
index 75f6ab58..d4d13be5 100644
--- a/config/router.js
+++ b/config/router.js
@@ -80,19 +80,16 @@ FlowRouter.route('/shortcuts', {
},
});
-FlowRouter.route('/import', {
+FlowRouter.route('/import/:source', {
name: 'import',
- triggersEnter: [
- AccountsTemplates.ensureSignedIn,
- () => {
- Session.set('currentBoard', null);
- Session.set('currentCard', null);
+ triggersEnter: [AccountsTemplates.ensureSignedIn],
+ action(params) {
+ Session.set('currentBoard', null);
+ Session.set('currentCard', null);
+ Session.set('importSource', params.source);
- Filter.reset();
- EscapeActions.executeAll();
- },
- ],
- action() {
+ Filter.reset();
+ EscapeActions.executeAll();
BlazeLayout.render('defaultLayout', {
headerBar: 'importHeaderBar',
content: 'import',
@@ -132,6 +129,7 @@ const redirections = {
'/boards': '/',
'/boards/:id/:slug': '/b/:id/:slug',
'/boards/:id/:slug/:cardId': '/b/:id/:slug/:cardId',
+ '/import': '/import/trello',
};
_.each(redirections, (newPath, oldPath) => {
diff --git a/docker-compose.yml b/docker-compose.yml
index 22ef2679..22ccb71c 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -3,7 +3,7 @@ version: '2'
services:
wekandb:
- image: mongo:3.2.14
+ image: mongo:3.2.15
container_name: wekan-db
restart: always
command: mongod --smallfiles --oplogSize 128
diff --git a/i18n/ar.i18n.json b/i18n/ar.i18n.json
index cd565776..08cbd579 100644
--- a/i18n/ar.i18n.json
+++ b/i18n/ar.i18n.json
@@ -1,5 +1,5 @@
{
- "accept": "اقبل",
+ "accept": "اقبلboard",
"act-activity-notify": "[Wekan] اشعار عن نشاط",
"act-addAttachment": "ربط __attachment__ الى __card__",
"act-addComment": "علق على __comment__ : __card__",
@@ -39,7 +39,7 @@
"activity-checklist-added": "أضاف قائمة تحقق إلى %s",
"add": "أضف",
"add-attachment": "إضافة مرفق",
- "add-board": "إضافة طاولة",
+ "add-board": "إضافة لوحة",
"add-card": "إضافة بطاقة",
"add-checklist": "إضافة قائمة تدقيق",
"add-checklist-item": "إضافة عنصر إلى قائمة التحقق",
@@ -55,18 +55,18 @@
"and-n-other-card": "And __count__ other بطاقة",
"and-n-other-card_plural": "And __count__ other بطاقات",
"apply": "طبق",
- "app-is-offline": "التطبيق غير متصل بالانترنت حاليا، إعادة تحميل الصفحة سوف يسبب فقدان البيانات.",
- "archive": "أرشف",
- "archive-all": "أرشف الكل",
- "archive-board": "أرشف اللوحة",
- "archive-card": "أرشف البطاقة",
- "archive-list": "Archive List",
+ "app-is-offline": "يتمّ تحميل ويكان، يرجى الانتظار. سيؤدي تحديث الصفحة إلى فقدان البيانات. إذا لم يتم تحميل ويكان، يرجى التحقق من أن خادم ويكان لم يتوقف. ",
+ "archive": "أرشيف",
+ "archive-all": "أرشيف الكل",
+ "archive-board": "أرشيف اللوحة",
+ "archive-card": "أرشيف البطاقة",
+ "archive-list": "قائمة الأرشيفات",
"archive-selection": "أرشف المُحدّد",
- "archiveBoardPopup-title": "Archive Board?",
- "archived-items": "عناصر في الأرشيف",
- "archived-boards": "Archived Boards",
- "restore-board": "Restore Board",
- "no-archived-boards": "No Archived Boards.",
+ "archiveBoardPopup-title": "أرشيف اللوحة ؟",
+ "archived-items": "عناصر مؤرشفة",
+ "archived-boards": "لوحات مؤرشفة",
+ "restore-board": "استعادة اللوحة",
+ "no-archived-boards": "لا توجد لوحات مؤرشفة.",
"archives": "أرشيفات",
"assign-member": "تعيين عضو",
"attached": "أُرفق)",
@@ -74,8 +74,8 @@
"attachment-delete-pop": "حذف المرق هو حذف نهائي . لا يمكن التراجع إذا حذف.",
"attachmentDeletePopup-title": "تريد حذف المرفق ?",
"attachments": "المرفقات",
- "auto-watch": "Automatically watch boards when they are created",
- "avatar-too-big": "The avatar is too large (70KB max)",
+ "auto-watch": "مراقبة لوحات تلقائيا عندما يتم إنشاؤها",
+ "avatar-too-big": "الصورة الرمزية كبيرة جدا (70 كيلوبايت كحد أقصى)",
"back": "رجوع",
"board-change-color": "تغيير اللومr",
"board-nb-stars": "%s نجوم",
@@ -85,7 +85,7 @@
"boardChangeColorPopup-title": "تعديل خلفية الشاشة",
"boardChangeTitlePopup-title": "إعادة تسمية اللوحة",
"boardChangeVisibilityPopup-title": "تعديل وضوح الرؤية",
- "boardChangeWatchPopup-title": "Change Watch",
+ "boardChangeWatchPopup-title": "تغيير المتابعة",
"boardMenuPopup-title": "قائمة اللوحة",
"boards": "لوحات",
"bucket-example": "مثل « todo list » على سبيل المثال",
@@ -95,15 +95,15 @@
"card-delete-notice": "هذا حذف أبديّ . سوف تفقد كل الإجراءات المنوطة بهذه البطاقة",
"card-delete-pop": "سيتم إزالة جميع الإجراءات من تبعات النشاط، وأنك لن تكون قادرا على إعادة فتح البطاقة. لا يوجد التراجع.",
"card-delete-suggest-archive": "يمكنك أرشفة بطاقة لحذفها من اللوحة والمحافظة على النشاط.",
- "card-due": "Due",
- "card-due-on": "Due on",
+ "card-due": "مستحق",
+ "card-due-on": "مستحق في",
"card-edit-attachments": "تعديل المرفقات",
"card-edit-labels": "تعديل العلامات",
"card-edit-members": "تعديل الأعضاء",
"card-labels-title": "تعديل علامات البطاقة.",
"card-members-title": "إضافة او حذف أعضاء للبطاقة.",
- "card-start": "Start",
- "card-start-on": "Starts on",
+ "card-start": "بداية",
+ "card-start-on": "يبدأ في",
"cardAttachmentsPopup-title": "إرفاق من",
"cardDeletePopup-title": "حذف البطاقة ?",
"cardDetailsActionsPopup-title": "إجراءات على البطاقة",
@@ -115,13 +115,13 @@
"change-avatar": "تعديل الصورة الشخصية",
"change-password": "تغيير كلمة المرور",
"change-permissions": "تعديل الصلاحيات",
- "change-settings": "Change Settings",
+ "change-settings": "تغيير الاعدادات",
"changeAvatarPopup-title": "تعديل الصورة الشخصية",
"changeLanguagePopup-title": "تغيير اللغة",
"changePasswordPopup-title": "تغيير كلمة المرور",
"changePermissionsPopup-title": "تعديل الصلاحيات",
- "changeSettingsPopup-title": "Change Settings",
- "checklists": "Checklists",
+ "changeSettingsPopup-title": "تغيير الاعدادات",
+ "checklists": "قوائم التّدقيق",
"click-to-star": "اضغط لإضافة اللوحة للمفضلة.",
"click-to-unstar": "اضغط لحذف اللوحة من المفضلة.",
"clipboard": "Clipboard or drag & drop",
@@ -139,15 +139,16 @@
"color-sky": "sky",
"color-yellow": "yellow",
"comment": "تعليق",
- "comment-placeholder": "Write Comment",
- "comment-only": "Comment only",
- "comment-only-desc": "Can comment on cards only.",
+ "comment-placeholder": "أكتب تعليق",
+ "comment-only": "التعليق فقط",
+ "comment-only-desc": "يمكن التعليق على بطاقات فقط.",
"computer": "حاسوب",
"create": "إنشاء",
"createBoardPopup-title": "إنشاء لوحة",
+ "chooseBoardSourcePopup-title": "استيراد لوحة",
"createLabelPopup-title": "إنشاء علامة",
"current": "الحالي",
- "date": "Date",
+ "date": "تاريخ",
"decline": "Decline",
"default-avatar": "صورة شخصية افتراضية",
"delete": "حذف",
@@ -161,10 +162,10 @@
"edit": "تعديل",
"edit-avatar": "تعديل الصورة الشخصية",
"edit-profile": "تعديل الملف الشخصي",
- "editCardStartDatePopup-title": "Change start date",
- "editCardDueDatePopup-title": "Change due date",
+ "editCardStartDatePopup-title": "تغيير تاريخ البدء",
+ "editCardDueDatePopup-title": "تغيير تاريخ الاستحقاق",
"editLabelPopup-title": "تعديل العلامة",
- "editNotificationPopup-title": "Edit Notification",
+ "editNotificationPopup-title": "تصحيح الإشعار",
"editProfilePopup-title": "تعديل الملف الشخصي",
"email": "البريد الإلكتروني",
"email-enrollAccount-subject": "An account created for you on __siteName__",
@@ -186,36 +187,40 @@
"error-json-schema": "Your JSON data does not include the proper information in the correct format",
"error-list-doesNotExist": "This list does not exist",
"error-user-doesNotExist": "This user does not exist",
- "error-user-notAllowSelf": "You can not invite yourself",
+ "error-user-notAllowSelf": "لا يمكنك دعوة نفسك",
"error-user-notCreated": "This user is not created",
- "error-username-taken": "This username is already taken",
+ "error-username-taken": "إسم المستخدم مأخوذ مسبقا",
"export-board": "Export board",
"filter": "تصفية",
"filter-cards": "تصفية البطاقات",
"filter-clear": "مسح التصفية",
- "filter-no-label": "No label",
- "filter-no-member": "No member",
+ "filter-no-label": "لا يوجد ملصق",
+ "filter-no-member": "ليس هناك أي عضو",
"filter-on": "التصفية تشتغل",
"filter-on-desc": "أنت بصدد تصفية بطاقات هذه اللوحة. اضغط هنا لتعديل التصفية.",
"filter-to-selection": "تصفية بالتحديد",
"fullname": "الإسم الكامل",
"header-logo-title": "الرجوع إلى صفحة اللوحات",
- "hide-system-messages": "Hide system messages",
+ "hide-system-messages": "إخفاء رسائل النظام",
"headerBarCreateBoardPopup-title": "إنشاء لوحة",
"home": "الرئيسية",
"import": "Import",
- "import-board": "import from Trello",
- "import-board-title": "Import board from Trello",
- "import-board-trello-instruction": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text",
+ "import-board": "استيراد لوحة",
+ "import-board-title-trello": "Import board from Trello",
+ "import-board-title-wekan": "استيراد لوحة من ويكان",
+ "from-trello": "من تريلو",
+ "from-wekan": "من ويكان",
+ "import-board-instruction-trello": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text",
+ "import-board-instruction-wekan": "في لوحة ويكان الخاصة بك، انتقل إلى 'القائمة'، ثم 'تصدير اللوحة'، ونسخ النص في الملف الذي تم تنزيله.",
"import-json-placeholder": "Paste your valid JSON data here",
- "import-map-members": "Map members",
+ "import-map-members": "رسم خريطة الأعضاء",
"import-members-map": "Your imported board has some members. Please map the members you want to import to Wekan users",
"import-show-user-mapping": "Review members mapping",
"import-user-select": "Pick the Wekan user you want to use as this member",
- "importMapMembersAddPopup-title": "Select Wekan member",
+ "importMapMembersAddPopup-title": "حدّد عضو ويكان",
"info": "معلومات",
"initials": "أولية",
- "invalid-date": "Invalid date",
+ "invalid-date": "تاريخ غير صالح",
"joined": "انضمّ",
"just-invited": "You are just invited to this board",
"keyboard-shortcuts": "اختصار لوحة المفاتيح",
@@ -234,9 +239,9 @@
"listActionPopup-title": "قائمة الإجراءات",
"listImportCardPopup-title": "Import a Trello card",
"listMorePopup-title": "المزيد",
- "link-list": "Link to this list",
+ "link-list": "رابط إلى هذه القائمة",
"list-delete-pop": "All actions will be removed from the activity feed and you won't be able to recover the list. There is no undo.",
- "list-delete-suggest-archive": "You can archive a list to remove it from the board and preserve the activity.",
+ "list-delete-suggest-archive": "يمكنك أرشفة قائمة لحذفها من اللوحة والمحافظة على النشاط.",
"lists": "القائمات",
"log-out": "تسجيل الخروج",
"log-in": "تسجيل الدخول",
@@ -255,8 +260,8 @@
"muted-info": "You will never be notified of any changes in this board",
"my-boards": "لوحاتي",
"name": "اسم",
- "no-archived-cards": "لا يوجد بطاقة في الأرشيف.",
- "no-archived-lists": "لا يوجد قائمة في الأرشيف.",
+ "no-archived-cards": "لا توجد بطاقات مؤرشفة.",
+ "no-archived-lists": "لا توجد قوائم مؤرشفة.",
"no-results": "لا توجد نتائج",
"normal": "عادي",
"normal-desc": "يمكن مشاهدة و تعديل البطاقات. لا يمكن تغيير إعدادات الضبط.",
@@ -329,7 +334,7 @@
"watch": "Watch",
"watching": "Watching",
"watching-info": "You will be notified of any change in this board",
- "welcome-board": "Welcome Board",
+ "welcome-board": "لوحة التّرحيب",
"welcome-list1": "Basics",
"welcome-list2": "Advanced",
"what-to-do": "ماذا تريد أن تنجز?",
@@ -340,7 +345,7 @@
"disable-self-registration": "Disable Self-Registration",
"invite": "Invite",
"invite-people": "Invite People",
- "to-boards": "To board(s)",
+ "to-boards": "إلى اللوحات",
"email-addresses": "Email Addresses",
"smtp-host-description": "The address of the SMTP server that handles your emails.",
"smtp-port-description": "The port your SMTP server uses for outgoing emails.",
@@ -355,5 +360,7 @@
"email-invite-register-subject": "__inviter__ sent you an invitation",
"email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
"error-invitation-code-not-exist": "Invitation code doesn't exist",
- "error-notAuthorized": "You are not authorized to view this page."
+ "error-notAuthorized": "You are not authorized to view this page.",
+ "outgoing-webhooks": "Outgoing Webhooks",
+ "outgoingWebhooksPopup-title": "Outgoing Webhooks"
} \ No newline at end of file
diff --git a/i18n/br.i18n.json b/i18n/br.i18n.json
index 497bc003..f7f83c98 100644
--- a/i18n/br.i18n.json
+++ b/i18n/br.i18n.json
@@ -55,7 +55,7 @@
"and-n-other-card": "And __count__ other card",
"and-n-other-card_plural": "And __count__ other cards",
"apply": "Apply",
- "app-is-offline": "The application is currently offline, refreshing the page will cause data loss.",
+ "app-is-offline": "Wekan is loading, please wait. Refreshing the page will cause data loss. If Wekan does not load, please check that Wekan server has not stopped.",
"archive": "Archive",
"archive-all": "Archive All",
"archive-board": "Archive Board",
@@ -145,6 +145,7 @@
"computer": "Computer",
"create": "Krouiñ",
"createBoardPopup-title": "Create Board",
+ "chooseBoardSourcePopup-title": "Import board",
"createLabelPopup-title": "Create Label",
"current": "current",
"date": "Date",
@@ -204,9 +205,13 @@
"headerBarCreateBoardPopup-title": "Create Board",
"home": "Home",
"import": "Import",
- "import-board": "import from Trello",
- "import-board-title": "Import board from Trello",
- "import-board-trello-instruction": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text",
+ "import-board": "import board",
+ "import-board-title-trello": "Import board from Trello",
+ "import-board-title-wekan": "Import board from Wekan",
+ "from-trello": "From Trello",
+ "from-wekan": "From Wekan",
+ "import-board-instruction-trello": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text",
+ "import-board-instruction-wekan": "In your Wekan board, go to 'Menu', then 'Export board', and copy the text in the downloaded file.",
"import-json-placeholder": "Paste your valid JSON data here",
"import-map-members": "Map members",
"import-members-map": "Your imported board has some members. Please map the members you want to import to Wekan users",
@@ -355,5 +360,7 @@
"email-invite-register-subject": "__inviter__ sent you an invitation",
"email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
"error-invitation-code-not-exist": "Invitation code doesn't exist",
- "error-notAuthorized": "You are not authorized to view this page."
+ "error-notAuthorized": "You are not authorized to view this page.",
+ "outgoing-webhooks": "Outgoing Webhooks",
+ "outgoingWebhooksPopup-title": "Outgoing Webhooks"
} \ No newline at end of file
diff --git a/i18n/ca.i18n.json b/i18n/ca.i18n.json
index df3d5053..72f23947 100644
--- a/i18n/ca.i18n.json
+++ b/i18n/ca.i18n.json
@@ -55,7 +55,7 @@
"and-n-other-card": "And __count__ other card",
"and-n-other-card_plural": "And __count__ other cards",
"apply": "Aplica",
- "app-is-offline": "L'aplicació éstà fora de línia. Si refresca la pàgina perdrà les dades.",
+ "app-is-offline": "Wekan is loading, please wait. Refreshing the page will cause data loss. If Wekan does not load, please check that Wekan server has not stopped.",
"archive": "Desa",
"archive-all": "Desa Tot",
"archive-board": "Arxiva tauler",
@@ -145,6 +145,7 @@
"computer": "Ordinador",
"create": "Crea",
"createBoardPopup-title": "Crea tauler",
+ "chooseBoardSourcePopup-title": "Import board",
"createLabelPopup-title": "Crea etiqueta",
"current": "Actual",
"date": "Data",
@@ -204,9 +205,13 @@
"headerBarCreateBoardPopup-title": "Crea tauler",
"home": "Inici",
"import": "importa",
- "import-board": "Importa des de Trello",
- "import-board-title": "Importa tauler des de Trello",
- "import-board-trello-instruction": "En el teu tauler Trello, ves a 'Menú', 'Més'.' Imprimir i Exportar', 'Exportar JSON', i copia el text resultant.",
+ "import-board": "import board",
+ "import-board-title-trello": "Importa tauler des de Trello",
+ "import-board-title-wekan": "Import board from Wekan",
+ "from-trello": "From Trello",
+ "from-wekan": "From Wekan",
+ "import-board-instruction-trello": "En el teu tauler Trello, ves a 'Menú', 'Més'.' Imprimir i Exportar', 'Exportar JSON', i copia el text resultant.",
+ "import-board-instruction-wekan": "In your Wekan board, go to 'Menu', then 'Export board', and copy the text in the downloaded file.",
"import-json-placeholder": "Aferra codi JSON vàlid aquí",
"import-map-members": "Mapeja el membres",
"import-members-map": "El tauler importat conté membres. Assigna els membres que vulguis importar a usuaris Wekan",
@@ -355,5 +360,7 @@
"email-invite-register-subject": "__inviter__ t'ha convidat",
"email-invite-register-text": " __user__,\n\n __inviter__ us ha convidat a col·laborar a Wekan.\n\n Clicau l'enllaç següent per acceptar l'invitació:\n __url__\n\n El vostre codi d'invitació és: __icode__\n\n Gràcies",
"error-invitation-code-not-exist": "El codi d'invitació no existeix",
- "error-notAuthorized": "You are not authorized to view this page."
+ "error-notAuthorized": "You are not authorized to view this page.",
+ "outgoing-webhooks": "Outgoing Webhooks",
+ "outgoingWebhooksPopup-title": "Outgoing Webhooks"
} \ No newline at end of file
diff --git a/i18n/cs.i18n.json b/i18n/cs.i18n.json
index 36d46ec0..3c8cadb6 100644
--- a/i18n/cs.i18n.json
+++ b/i18n/cs.i18n.json
@@ -55,7 +55,7 @@
"and-n-other-card": "A __count__ další karta(y)",
"and-n-other-card_plural": "A __count__ dalších karet",
"apply": "Apply",
- "app-is-offline": "The application is currently offline, refreshing the page will cause data loss.",
+ "app-is-offline": "Wekan is loading, please wait. Refreshing the page will cause data loss. If Wekan does not load, please check that Wekan server has not stopped.",
"archive": "Archiv",
"archive-all": "Archivovat vše",
"archive-board": "Archivovat tablo",
@@ -145,6 +145,7 @@
"computer": "Počítač",
"create": "Vytvořit",
"createBoardPopup-title": "Vytvořit tablo",
+ "chooseBoardSourcePopup-title": "Import board",
"createLabelPopup-title": "Vytvořit štítek",
"current": "Aktuální",
"date": "Date",
@@ -204,9 +205,13 @@
"headerBarCreateBoardPopup-title": "Vytvořit tablo",
"home": "Domů",
"import": "Import",
- "import-board": "Importovat ze služby Trello",
- "import-board-title": "Import board from Trello",
- "import-board-trello-instruction": "Na svém Trello tablu, otevři 'Menu', pak 'More', 'Print and Export', 'Export JSON', a zkopíruj výsledný text",
+ "import-board": "import board",
+ "import-board-title-trello": "Import board from Trello",
+ "import-board-title-wekan": "Import board from Wekan",
+ "from-trello": "From Trello",
+ "from-wekan": "From Wekan",
+ "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",
+ "import-board-instruction-wekan": "In your Wekan board, go to 'Menu', then 'Export board', and copy the text in the downloaded file.",
"import-json-placeholder": "Sem vlož validní JSON data",
"import-map-members": "Map members",
"import-members-map": "Toto importované tablo obsahuje několik členů. Namapuj členy z importu na uživatelské účty Wekan.",
@@ -355,5 +360,7 @@
"email-invite-register-subject": "__inviter__ odeslal pozvánku",
"email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
"error-invitation-code-not-exist": "Invitation code doesn't exist",
- "error-notAuthorized": "You are not authorized to view this page."
+ "error-notAuthorized": "You are not authorized to view this page.",
+ "outgoing-webhooks": "Outgoing Webhooks",
+ "outgoingWebhooksPopup-title": "Outgoing Webhooks"
} \ No newline at end of file
diff --git a/i18n/de.i18n.json b/i18n/de.i18n.json
index 912b9597..83efdabc 100644
--- a/i18n/de.i18n.json
+++ b/i18n/de.i18n.json
@@ -55,7 +55,7 @@
"and-n-other-card": "und eine andere Karte",
"and-n-other-card_plural": "und __count__ andere Karten",
"apply": "Übernehmen",
- "app-is-offline": "Die Anwendung ist derzeit offline. Aktualisieren der Seite führt zu Datenverlust.",
+ "app-is-offline": "Wekan is loading, please wait. Refreshing the page will cause data loss. If Wekan does not load, please check that Wekan server has not stopped.",
"archive": "Archiv",
"archive-all": "Alles archivieren",
"archive-board": "Board archivieren",
@@ -145,6 +145,7 @@
"computer": "Computer",
"create": "Erstellen",
"createBoardPopup-title": "Board erstellen",
+ "chooseBoardSourcePopup-title": "Import board",
"createLabelPopup-title": "Label erstellen",
"current": "aktuell",
"date": "Datum",
@@ -204,9 +205,13 @@
"headerBarCreateBoardPopup-title": "Board erstellen",
"home": "Home",
"import": "Importieren",
- "import-board": "von Trello importieren",
- "import-board-title": "Board von Trello importieren",
- "import-board-trello-instruction": "Gehen Sie in ihrem Trello-Board auf 'Menü', dann 'Mehr', 'Drucken und Exportieren', 'JSON-Export' und kopieren Sie den dort angezeigten Text",
+ "import-board": "import board",
+ "import-board-title-trello": "Board von Trello importieren",
+ "import-board-title-wekan": "Import board from Wekan",
+ "from-trello": "From Trello",
+ "from-wekan": "From Wekan",
+ "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",
+ "import-board-instruction-wekan": "In your Wekan board, go to 'Menu', then 'Export board', and copy the text in the downloaded file.",
"import-json-placeholder": "Fügen Sie die korrekten JSON-Daten hier ein",
"import-map-members": "Mitglieder zuordnen",
"import-members-map": "Das importierte Board hat einige Mitglieder. Bitte ordnen Sie die Mitglieder, die importiert werden sollen, Wekan-Nutzern zu",
@@ -355,5 +360,7 @@
"email-invite-register-subject": "__inviter__ hat Ihnen eine Einladung geschickt",
"email-invite-register-text": "Hallo __user__,\n\n__inviter__ hat Sie für Ihre Zusammenarbeit zu Wekan eingeladen.\n\nBitte klicken Sie auf folgenden Link:\n__url__\n\nIhr Einladungscode lautet: __icode__\n\nDanke.",
"error-invitation-code-not-exist": "Ungültiger Einladungscode",
- "error-notAuthorized": "Sie sind nicht berechtigt diese Seite zu sehen."
+ "error-notAuthorized": "Sie sind nicht berechtigt diese Seite zu sehen.",
+ "outgoing-webhooks": "Outgoing Webhooks",
+ "outgoingWebhooksPopup-title": "Outgoing Webhooks"
} \ No newline at end of file
diff --git a/i18n/en-GB.i18n.json b/i18n/en-GB.i18n.json
index 718b7bed..e27671cb 100644
--- a/i18n/en-GB.i18n.json
+++ b/i18n/en-GB.i18n.json
@@ -55,7 +55,7 @@
"and-n-other-card": "And __count__ other card",
"and-n-other-card_plural": "And __count__ other cards",
"apply": "Apply",
- "app-is-offline": "The application is currently offline, refreshing the page will cause data loss.",
+ "app-is-offline": "Wekan is loading, please wait. Refreshing the page will cause data loss. If Wekan does not load, please check that Wekan server has not stopped.",
"archive": "Archive",
"archive-all": "Archive All",
"archive-board": "Archive Board",
@@ -145,6 +145,7 @@
"computer": "Computer",
"create": "Create",
"createBoardPopup-title": "Create Board",
+ "chooseBoardSourcePopup-title": "Import board",
"createLabelPopup-title": "Create Label",
"current": "current",
"date": "Date",
@@ -204,9 +205,13 @@
"headerBarCreateBoardPopup-title": "Create Board",
"home": "Home",
"import": "Import",
- "import-board": "import from Trello",
- "import-board-title": "Import board from Trello",
- "import-board-trello-instruction": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text.",
+ "import-board": "import board",
+ "import-board-title-trello": "Import board from Trello",
+ "import-board-title-wekan": "Import board from Wekan",
+ "from-trello": "From Trello",
+ "from-wekan": "From Wekan",
+ "import-board-instruction-trello": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text.",
+ "import-board-instruction-wekan": "In your Wekan board, go to 'Menu', then 'Export board', and copy the text in the downloaded file.",
"import-json-placeholder": "Paste your valid JSON data here",
"import-map-members": "Map members",
"import-members-map": "Your imported board has some members. Please map the members you want to import to Wekan users",
@@ -355,5 +360,7 @@
"email-invite-register-subject": "__inviter__ sent you an invitation",
"email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaboration.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
"error-invitation-code-not-exist": "Invitation code doesn't exist",
- "error-notAuthorized": "You are not authorised to view this page."
+ "error-notAuthorized": "You are not authorised to view this page.",
+ "outgoing-webhooks": "Outgoing Webhooks",
+ "outgoingWebhooksPopup-title": "Outgoing Webhooks"
} \ No newline at end of file
diff --git a/i18n/en.i18n.json b/i18n/en.i18n.json
index 122d0b8d..05d86ff6 100644
--- a/i18n/en.i18n.json
+++ b/i18n/en.i18n.json
@@ -55,7 +55,7 @@
"and-n-other-card": "And __count__ other card",
"and-n-other-card_plural": "And __count__ other cards",
"apply": "Apply",
- "app-is-offline": "The application is currently offline, refreshing the page will cause data loss.",
+ "app-is-offline": "Wekan is loading, please wait. Refreshing the page will cause data loss. If Wekan does not load, please check that Wekan server has not stopped.",
"archive": "Archive",
"archive-all": "Archive All",
"archive-board": "Archive Board",
@@ -145,6 +145,7 @@
"computer": "Computer",
"create": "Create",
"createBoardPopup-title": "Create Board",
+ "chooseBoardSourcePopup-title": "Import board",
"createLabelPopup-title": "Create Label",
"current": "current",
"date": "Date",
@@ -204,9 +205,13 @@
"headerBarCreateBoardPopup-title": "Create Board",
"home": "Home",
"import": "Import",
- "import-board": "import from Trello",
- "import-board-title": "Import board from Trello",
- "import-board-trello-instruction": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text.",
+ "import-board": "import board",
+ "import-board-title-trello": "Import board from Trello",
+ "import-board-title-wekan": "Import board from Wekan",
+ "from-trello": "From Trello",
+ "from-wekan": "From Wekan",
+ "import-board-instruction-trello": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text.",
+ "import-board-instruction-wekan": "In your Wekan board, go to 'Menu', then 'Export board', and copy the text in the downloaded file.",
"import-json-placeholder": "Paste your valid JSON data here",
"import-map-members": "Map members",
"import-members-map": "Your imported board has some members. Please map the members you want to import to Wekan users",
@@ -355,5 +360,7 @@
"email-invite-register-subject": "__inviter__ sent you an invitation",
"email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
"error-invitation-code-not-exist": "Invitation code doesn't exist",
- "error-notAuthorized": "You are not authorized to view this page."
+ "error-notAuthorized": "You are not authorized to view this page.",
+ "outgoing-webhooks": "Outgoing Webhooks",
+ "outgoingWebhooksPopup-title": "Outgoing Webhooks"
}
diff --git a/i18n/eo.i18n.json b/i18n/eo.i18n.json
index 17c758c2..58ff0f03 100644
--- a/i18n/eo.i18n.json
+++ b/i18n/eo.i18n.json
@@ -55,7 +55,7 @@
"and-n-other-card": "And __count__ other card",
"and-n-other-card_plural": "And __count__ other cards",
"apply": "Apliki",
- "app-is-offline": "The application is currently offline, refreshing the page will cause data loss.",
+ "app-is-offline": "Wekan is loading, please wait. Refreshing the page will cause data loss. If Wekan does not load, please check that Wekan server has not stopped.",
"archive": "Arkivi",
"archive-all": "Arkivi ĉion",
"archive-board": "Archive Board",
@@ -145,6 +145,7 @@
"computer": "Komputilo",
"create": "Krei",
"createBoardPopup-title": "Krei ",
+ "chooseBoardSourcePopup-title": "Import board",
"createLabelPopup-title": "Create Label",
"current": "current",
"date": "Dato",
@@ -204,9 +205,13 @@
"headerBarCreateBoardPopup-title": "Krei ",
"home": "Hejmo",
"import": "Importi",
- "import-board": "Importi de Trello",
- "import-board-title": "Import board from Trello",
- "import-board-trello-instruction": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text.",
+ "import-board": "import board",
+ "import-board-title-trello": "Import board from Trello",
+ "import-board-title-wekan": "Import board from Wekan",
+ "from-trello": "From Trello",
+ "from-wekan": "From Wekan",
+ "import-board-instruction-trello": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text.",
+ "import-board-instruction-wekan": "In your Wekan board, go to 'Menu', then 'Export board', and copy the text in the downloaded file.",
"import-json-placeholder": "Paste your valid JSON data here",
"import-map-members": "Map members",
"import-members-map": "Your imported board has some members. Please map the members you want to import to Wekan users",
@@ -355,5 +360,7 @@
"email-invite-register-subject": "__inviter__ sent you an invitation",
"email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
"error-invitation-code-not-exist": "Invitation code doesn't exist",
- "error-notAuthorized": "You are not authorized to view this page."
+ "error-notAuthorized": "You are not authorized to view this page.",
+ "outgoing-webhooks": "Outgoing Webhooks",
+ "outgoingWebhooksPopup-title": "Outgoing Webhooks"
} \ No newline at end of file
diff --git a/i18n/es.i18n.json b/i18n/es.i18n.json
index 4076f79e..22820a49 100644
--- a/i18n/es.i18n.json
+++ b/i18n/es.i18n.json
@@ -55,7 +55,7 @@
"and-n-other-card": "And __count__ other card",
"and-n-other-card_plural": "And __count__ other cards",
"apply": "Aplicar",
- "app-is-offline": "La aplicación esta actualmente fuera de servicio, refrescar la página causará pérdida de datos",
+ "app-is-offline": "Wekan is loading, please wait. Refreshing the page will cause data loss. If Wekan does not load, please check that Wekan server has not stopped.",
"archive": "Guardar",
"archive-all": "Guardar Todo",
"archive-board": "Archive Board",
@@ -145,6 +145,7 @@
"computer": "Ordenador",
"create": "Crear",
"createBoardPopup-title": "Crear tablero",
+ "chooseBoardSourcePopup-title": "Import board",
"createLabelPopup-title": "Crear etiqueta",
"current": "actual",
"date": "Fecha",
@@ -204,9 +205,13 @@
"headerBarCreateBoardPopup-title": "Crear tablero",
"home": "Inicio",
"import": "Importar",
- "import-board": "importar desde Trello",
- "import-board-title": "Importar tablero desde Trello",
- "import-board-trello-instruction": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text",
+ "import-board": "import board",
+ "import-board-title-trello": "Importar tablero desde Trello",
+ "import-board-title-wekan": "Import board from Wekan",
+ "from-trello": "From Trello",
+ "from-wekan": "From Wekan",
+ "import-board-instruction-trello": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text",
+ "import-board-instruction-wekan": "In your Wekan board, go to 'Menu', then 'Export board', and copy the text in the downloaded file.",
"import-json-placeholder": "Paste your valid JSON data here",
"import-map-members": "Mapa de miembros",
"import-members-map": "Your imported board has some members. Please map the members you want to import to Wekan users",
@@ -355,5 +360,7 @@
"email-invite-register-subject": "__inviter__ sent you an invitation",
"email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
"error-invitation-code-not-exist": "Invitation code doesn't exist",
- "error-notAuthorized": "You are not authorized to view this page."
+ "error-notAuthorized": "You are not authorized to view this page.",
+ "outgoing-webhooks": "Outgoing Webhooks",
+ "outgoingWebhooksPopup-title": "Outgoing Webhooks"
} \ No newline at end of file
diff --git a/i18n/eu.i18n.json b/i18n/eu.i18n.json
index 2ac1c6da..2f9342d4 100644
--- a/i18n/eu.i18n.json
+++ b/i18n/eu.i18n.json
@@ -55,7 +55,7 @@
"and-n-other-card": "Eta beste txartel __count__",
"and-n-other-card_plural": "Eta beste __count__ txartel",
"apply": "Aplikatu",
- "app-is-offline": "Aplikazio hau lineaz kanpo dago, orria freskatzeak datuen galera ekarriko du.",
+ "app-is-offline": "Wekan kargatzen ari da, itxaron mesedez. Orria freskatzeak datuen galera ekarriko luke. Wekan kargatzen ez bada, egiaztatu Wekan zerbitzaria gelditu ez dela.",
"archive": "Artxibatu",
"archive-all": "Artxibatu guztiak",
"archive-board": "Artxibatu arbela",
@@ -145,6 +145,7 @@
"computer": "Ordenagailua",
"create": "Sortu",
"createBoardPopup-title": "Sortu arbela",
+ "chooseBoardSourcePopup-title": "Inportatu arbela",
"createLabelPopup-title": "Sortu etiketa",
"current": "unekoa",
"date": "Data",
@@ -204,9 +205,13 @@
"headerBarCreateBoardPopup-title": "Sortu arbela",
"home": "Hasiera",
"import": "Inportatu",
- "import-board": "Inportatu Trellotik",
- "import-board-title": "Inportatu arbela Trellotik",
- "import-board-trello-instruction": "Zure Trello arbelean, aukeratu 'Menu\", 'More', 'Print and Export', 'Export JSON', eta kopiatu jasotako testua hemen.",
+ "import-board": "inportatu arbela",
+ "import-board-title-trello": "Inportatu arbela Trellotik",
+ "import-board-title-wekan": "Inportatu arbela Wekanetik",
+ "from-trello": "Trellotik",
+ "from-wekan": "Wekanetik",
+ "import-board-instruction-trello": "Zure Trello arbelean, aukeratu 'Menu\", 'More', 'Print and Export', 'Export JSON', eta kopiatu jasotako testua hemen.",
+ "import-board-instruction-wekan": "Zure Wekan arbelean, aukeratu 'Menua' - 'Esportatu arbela', eta kopiatu testua deskargatutako fitxategian.",
"import-json-placeholder": "Isatsi baliozko JSON datuak hemen",
"import-map-members": "Kideen mapa",
"import-members-map": "Inportatu duzun arbela kide batzuk ditu, mesedez lotu inportatu nahi dituzun kideak Wekan erabiltzaileekin",
@@ -355,5 +360,7 @@
"email-invite-register-subject": "__inviter__ erabiltzaileak gonbidapen bat bidali dizu",
"email-invite-register-text": "Kaixo __user__,\n\n__inviter__ erabiltzaileak Wekanera gonbidatu zaitu elkar-lanean aritzeko.\n\nJarraitu mesedez lotura hau:\n__url__\n\nZure gonbidapen kodea hau da: __icode__\n\nEskerrik asko.",
"error-invitation-code-not-exist": "Gonbidapen kodea ez da existitzen",
- "error-notAuthorized": "Ez duzu orri hau ikusteko baimenik."
+ "error-notAuthorized": "Ez duzu orri hau ikusteko baimenik.",
+ "outgoing-webhooks": "Outgoing Webhooks",
+ "outgoingWebhooksPopup-title": "Outgoing Webhooks"
} \ No newline at end of file
diff --git a/i18n/fa.i18n.json b/i18n/fa.i18n.json
index 9044b79f..2a892f07 100644
--- a/i18n/fa.i18n.json
+++ b/i18n/fa.i18n.json
@@ -55,7 +55,7 @@
"and-n-other-card": "و __count__ کارت دیگر",
"and-n-other-card_plural": "و __count__ کارت دیگر",
"apply": "اجرا",
- "app-is-offline": "برنامه به شبکه متصل نیست، تازه کردن(f5) صفحه اطلاعات نوشته شده را پاک می کند",
+ "app-is-offline": "Wekan is loading, please wait. Refreshing the page will cause data loss. If Wekan does not load, please check that Wekan server has not stopped.",
"archive": "بایگانی",
"archive-all": "تمامی بایگانی ها",
"archive-board": "بایگانی برد",
@@ -145,6 +145,7 @@
"computer": "رایانه",
"create": "ایجاد",
"createBoardPopup-title": "ایجاد تخته",
+ "chooseBoardSourcePopup-title": "Import board",
"createLabelPopup-title": "ایجاد برچسب",
"current": "جاری",
"date": "تاریخ",
@@ -204,9 +205,13 @@
"headerBarCreateBoardPopup-title": "ایجاد تخته",
"home": "خانه",
"import": "وارد کردن",
- "import-board": "وارد کردن از Trello",
- "import-board-title": "وارد کردن تخته ها از Trello",
- "import-board-trello-instruction": "در Trello-ی خود به 'Menu'، 'More'، 'Print'، 'Export to JSON رفته و متن نهایی را دراینجا وارد نمایید.",
+ "import-board": "import board",
+ "import-board-title-trello": "وارد کردن تخته ها از Trello",
+ "import-board-title-wekan": "Import board from Wekan",
+ "from-trello": "From Trello",
+ "from-wekan": "From Wekan",
+ "import-board-instruction-trello": "در Trello-ی خود به 'Menu'، 'More'، 'Print'، 'Export to JSON رفته و متن نهایی را دراینجا وارد نمایید.",
+ "import-board-instruction-wekan": "In your Wekan board, go to 'Menu', then 'Export board', and copy the text in the downloaded file.",
"import-json-placeholder": "اطلاعات Json معتبر خود را اینجا وارد کنید.",
"import-map-members": "نگاشت اعضا",
"import-members-map": "تعدادی عضو در تخته وارد شده می باشد. لطفا کاربرانی که باید وارد نرم افزار بشوند را مشخص کنید.",
@@ -355,5 +360,7 @@
"email-invite-register-subject": "__inviter__ برای شما دعوت نامه ارسال کرده است",
"email-invite-register-text": "__User__ عزیز \nکاربر __inviter__ شما را به عضویت در Wekan برای همکاری دعوت کرده است.\nلطفا لینک زیر را دنبال کنید،\n __url__\nکد دعوت شما __icode__ می باشد.\n باتشکر",
"error-invitation-code-not-exist": "چنین کد دعوتی یافت نشد",
- "error-notAuthorized": "You are not authorized to view this page."
+ "error-notAuthorized": "You are not authorized to view this page.",
+ "outgoing-webhooks": "Outgoing Webhooks",
+ "outgoingWebhooksPopup-title": "Outgoing Webhooks"
} \ No newline at end of file
diff --git a/i18n/fi.i18n.json b/i18n/fi.i18n.json
index dd1f51ab..ea8cd374 100644
--- a/i18n/fi.i18n.json
+++ b/i18n/fi.i18n.json
@@ -55,7 +55,7 @@
"and-n-other-card": "Ja __count__ muu kortti",
"and-n-other-card_plural": "Ja __count__ muuta korttia",
"apply": "Käytä",
- "app-is-offline": "Sovellus ei ole tällä hetkellä linjoilla, sivun lataaminen uudelleen voi aiheuttaa muutettujen tietojen menettämisen.",
+ "app-is-offline": "Wekan latautuu, odota. Sivun uudelleenlataus aiheuttaa tietojen menettämisen. Jos Wekan ei lataudu, tarkista että Wekan palvelin ei ole pysähtynyt.",
"archive": "Arkistoi",
"archive-all": "Arkistoi kaikki",
"archive-board": "Arkistoi taulu",
@@ -145,6 +145,7 @@
"computer": "Tietokone",
"create": "Luo",
"createBoardPopup-title": "Luo taulu",
+ "chooseBoardSourcePopup-title": "Tuo taulu",
"createLabelPopup-title": "Luo tunniste",
"current": "nykyinen",
"date": "Päivämäärä",
@@ -204,9 +205,13 @@
"headerBarCreateBoardPopup-title": "Luo taulu",
"home": "Koti",
"import": "Tuo",
- "import-board": "tuo Trellosta",
- "import-board-title": "Tuo taulu Trellosta",
- "import-board-trello-instruction": "Trello taulullasi, mene 'Menu', sitten 'More', 'Print and Export', 'Export JSON', ja kopioi tuloksena saamasi teksti",
+ "import-board": "tuo taulu",
+ "import-board-title-trello": "Tuo taulu Trellosta",
+ "import-board-title-wekan": "Tuo taulu Wekanista",
+ "from-trello": "Trellosta",
+ "from-wekan": "Wekanista",
+ "import-board-instruction-trello": "Trello taulullasi, mene 'Menu', sitten 'More', 'Print and Export', 'Export JSON', ja kopioi tuloksena saamasi teksti",
+ "import-board-instruction-wekan": "Wekan taulullasi, mene 'Valikko', sitten 'Vie taulu', ja kopioi teksti ladatusta tiedostosta.",
"import-json-placeholder": "Liitä kelvollinen JSON tietosi tähän",
"import-map-members": "Vastaavat jäsenet",
"import-members-map": "Tuomallasi taululla on muutamia jäseniä. Ole hyvä ja valitse tuomiasi jäseniä vastaavat Wekan käyttäjät",
@@ -355,5 +360,7 @@
"email-invite-register-subject": "__inviter__ lähetti sinulle kutsun",
"email-invite-register-text": "Hei __user__,\n\n__inviter__ kutsuu sinut mukaan Wekan ohjelman käyttöön.\n\nOle hyvä ja seuraa allaolevaa linkkiä:\n__url__\n\nJa kutsukoodisi on: __icode__\n\nKiitos.",
"error-invitation-code-not-exist": "Kutsukoodi ei ole olemassa",
- "error-notAuthorized": "Sinulla ei ole oikeutta tarkastella tätä sivua."
+ "error-notAuthorized": "Sinulla ei ole oikeutta tarkastella tätä sivua.",
+ "outgoing-webhooks": "Lähtevät Webkoukut",
+ "outgoingWebhooksPopup-title": "Lähtevät Webkoukut"
} \ No newline at end of file
diff --git a/i18n/fr.i18n.json b/i18n/fr.i18n.json
index d27aa1d7..dc323c8b 100644
--- a/i18n/fr.i18n.json
+++ b/i18n/fr.i18n.json
@@ -55,7 +55,7 @@
"and-n-other-card": "Et __count__ autre carte",
"and-n-other-card_plural": "Et __count__ autres cartes",
"apply": "Appliquer",
- "app-is-offline": "L'application est actuellement déconnectée, recharger la page provoquera des pertes de données.",
+ "app-is-offline": "Chargement en cours de Wekan. Veuillez patienter. Vous risquez de perdre des données si vous recharger la page. Si Wekan ne se charge pas, veuillez vérifier si le serveur Wekan est arrêté.",
"archive": "Archiver",
"archive-all": "Tout archiver",
"archive-board": "Archiver le tableau",
@@ -145,6 +145,7 @@
"computer": "Ordinateur",
"create": "Créer",
"createBoardPopup-title": "Créer un tableau",
+ "chooseBoardSourcePopup-title": "Importer un tableau",
"createLabelPopup-title": "Créer une étiquette",
"current": "actuel",
"date": "Date",
@@ -204,9 +205,13 @@
"headerBarCreateBoardPopup-title": "Créer un tableau",
"home": "Accueil",
"import": "Importer",
- "import-board": "Importer depuis Trello",
- "import-board-title": "Importer le tableau depuis Trello",
- "import-board-trello-instruction": "Dans votre tableau Trello, allez sur 'Menu', puis sur 'Plus', 'Imprimer et exporter', 'Exporter en JSON' et copiez le texte du résultat",
+ "import-board": "importer un tableau",
+ "import-board-title-trello": "Importer le tableau depuis Trello",
+ "import-board-title-wekan": "Importer un tableau depuis Wekan",
+ "from-trello": "Depuis Trello",
+ "from-wekan": "Depuis Wekan",
+ "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",
+ "import-board-instruction-wekan": "Dans votre tableau Wekan, allez dans 'Menu', puis 'Exporter un tableau', et copier le texte du fichier téléchargé.",
"import-json-placeholder": "Collez ici les données JSON valides",
"import-map-members": "Faire correspondre aux membres",
"import-members-map": "Le tableau que vous venez d'importer contient des membres. Veuillez associer les membres que vous souhaitez importer à des utilisateurs de Wekan.",
@@ -355,5 +360,7 @@
"email-invite-register-subject": "__inviter__ vous a envoyé une invitation",
"email-invite-register-text": "Cher __user__,\n\n__inviter__ vous invite à le rejoindre sur Wekan pour collaborer.\n\nVeuillez suivre le lien ci-dessous :\n__url__\n\nVotre code d'invitation est : __icode__\n\nMerci.",
"error-invitation-code-not-exist": "Ce code d'invitation n'existe pas.",
- "error-notAuthorized": "Vous n'êtes pas autorisé à accéder à cette page."
+ "error-notAuthorized": "Vous n'êtes pas autorisé à accéder à cette page.",
+ "outgoing-webhooks": "Outgoing Webhooks",
+ "outgoingWebhooksPopup-title": "Outgoing Webhooks"
} \ No newline at end of file
diff --git a/i18n/he.i18n.json b/i18n/he.i18n.json
index 736b29c4..8893f26d 100644
--- a/i18n/he.i18n.json
+++ b/i18n/he.i18n.json
@@ -36,7 +36,7 @@
"activity-removed": "%s הוסר מ%s",
"activity-sent": "%s נשלח ל%s",
"activity-unjoined": "בטל צירוף %s",
- "activity-checklist-added": "נוספה רשימת משימות ל%s",
+ "activity-checklist-added": "נוספה רשימת משימות אל %s",
"add": "הוספה",
"add-attachment": "הוספת קובץ מצורף",
"add-board": "הוספת לוח",
@@ -55,7 +55,7 @@
"and-n-other-card": "וכרטיס אחר",
"and-n-other-card_plural": "ו־__count__ כרטיסים אחרים",
"apply": "החלה",
- "app-is-offline": "המערכת מושבתת כרגע, רענון הדף יגרום לאבדן מידע.",
+ "app-is-offline": "Wekan בטעינה, נא להמתין. רענון העמוד עשוי להוביל לאובדן מידע. אם הטעינה של Wekan נעצרה, נא לבדוק ששרת ה־Wekan לא נעצר.",
"archive": "להעביר לארכיון",
"archive-all": "להעביר הכול לארכיון",
"archive-board": "להעביר לוח לארכיון",
@@ -145,6 +145,7 @@
"computer": "מחשב",
"create": "יצירה",
"createBoardPopup-title": "יצירת לוח",
+ "chooseBoardSourcePopup-title": "יבוא לוח",
"createLabelPopup-title": "יצירת תווית",
"current": "נוכחי",
"date": "תאריך",
@@ -203,10 +204,14 @@
"hide-system-messages": "הסתרת הודעות מערכת",
"headerBarCreateBoardPopup-title": "יצירת לוח",
"home": "בית",
- "import": "ייבוא",
- "import-board": "ייבוא מטרלו",
- "import-board-title": "ייבוא לוח מטרלו",
- "import-board-trello-instruction": "בלוח הטרלו שלך, עליך ללחוץ על 'Menu', ואז על 'More',‏ 'Print and Export',‏ 'Export JSON' ולהעתיק את הטקסט שנוצר.",
+ "import": "יבוא",
+ "import-board": "יבוא לוח",
+ "import-board-title-trello": "ייבוא לוח מטרלו",
+ "import-board-title-wekan": "יבוא לוח מ־Wekan",
+ "from-trello": "מ־Trello",
+ "from-wekan": "מ־Wekan",
+ "import-board-instruction-trello": "בלוח הטרלו שלך, עליך ללחוץ על ‚תפריט‘, ואז על ‚עוד‘, ‚הדפסה וייצוא‘, ‚יצוא JSON‘ ולהעתיק את הטקסט שנוצר.",
+ "import-board-instruction-wekan": "בלוח ה־Wekan, יש לגשת אל ‚תפריט‘, לאחר מכן ‚יצוא לוח‘ ולהעתיק את הטקסט בקובץ שהתקבל.",
"import-json-placeholder": "יש להדביק את נתוני ה־JSON התקינים לכאן",
"import-map-members": "מיפוי חברים",
"import-members-map": "הלוחות המיובאים שלך מכילים חברים. נא למפות את החברים שברצונך לייבא למשתמשי Wekan",
@@ -232,7 +237,7 @@
"list-move-cards": "העברת כל הכרטיסים שברשימה זו",
"list-select-cards": "בחירת כל הכרטיסים שברשימה זו",
"listActionPopup-title": "פעולות רשימה",
- "listImportCardPopup-title": "ייבוא כרטיס מטרלו",
+ "listImportCardPopup-title": "יבוא כרטיס מ־Trello",
"listMorePopup-title": "עוד",
"link-list": "קישור לרשימה זו",
"list-delete-pop": "כל הפעולות תוסרנה מרצף הפעילות ולא תהיה לך אפשרות לשחזר את הרשימה. אין ביטול.",
@@ -355,5 +360,7 @@
"email-invite-register-subject": "נשלחה אליך הזמנה מאת __inviter__",
"email-invite-register-text": "__user__ יקר,\n\nקיבלת הזמנה מאת __inviter__ לשתף פעולה ב־Wekan.\n\nנא ללחוץ על הקישור:\n__url__\n\nקוד ההזמנה שלך הוא: __icode__\n\nתודה.",
"error-invitation-code-not-exist": "קוד ההזמנה אינו קיים",
- "error-notAuthorized": "אין לך הרשאה לצפות בעמוד זה."
+ "error-notAuthorized": "אין לך הרשאה לצפות בעמוד זה.",
+ "outgoing-webhooks": "Outgoing Webhooks",
+ "outgoingWebhooksPopup-title": "Outgoing Webhooks"
} \ No newline at end of file
diff --git a/i18n/hu.i18n.json b/i18n/hu.i18n.json
index b9cf5585..9b2114a8 100644
--- a/i18n/hu.i18n.json
+++ b/i18n/hu.i18n.json
@@ -55,7 +55,7 @@
"and-n-other-card": "And __count__ other card",
"and-n-other-card_plural": "And __count__ other cards",
"apply": "Alkalmaz",
- "app-is-offline": "Az alkalmazás jelenleg nem elérhető, az oldal frissítése adatvesztéssel járhat.",
+ "app-is-offline": "Wekan is loading, please wait. Refreshing the page will cause data loss. If Wekan does not load, please check that Wekan server has not stopped.",
"archive": "Archív",
"archive-all": "Összes archivált",
"archive-board": "Archívált tábla",
@@ -145,6 +145,7 @@
"computer": "Számítógép",
"create": "Létrehoz",
"createBoardPopup-title": "Új tábla",
+ "chooseBoardSourcePopup-title": "Import board",
"createLabelPopup-title": "Új cimke",
"current": "aktuális",
"date": "Dátum",
@@ -204,9 +205,13 @@
"headerBarCreateBoardPopup-title": "Új tábla",
"home": "Kezdőlap",
"import": "Importálás",
- "import-board": "importálás a Trello-ról",
- "import-board-title": "Tábla importálása a Trello-ról",
- "import-board-trello-instruction": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text.",
+ "import-board": "import board",
+ "import-board-title-trello": "Tábla importálása a Trello-ról",
+ "import-board-title-wekan": "Import board from Wekan",
+ "from-trello": "From Trello",
+ "from-wekan": "From Wekan",
+ "import-board-instruction-trello": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text.",
+ "import-board-instruction-wekan": "In your Wekan board, go to 'Menu', then 'Export board', and copy the text in the downloaded file.",
"import-json-placeholder": "Paste your valid JSON data here",
"import-map-members": "Tagok megjelenítése",
"import-members-map": "Your imported board has some members. Please map the members you want to import to Wekan users",
@@ -355,5 +360,7 @@
"email-invite-register-subject": "__inviter__ küldött neked egy meghívót",
"email-invite-register-text": "Kedves __user__,\n\n__inviter__ meghívott közreműködésre a Wekanba.\n\nKérlek kövesd az alábbi linket:\n__url__\n\nA meghívókódod: __icode__\n\nKöszönjük.",
"error-invitation-code-not-exist": "A meghívó kódja nem érvényes",
- "error-notAuthorized": "You are not authorized to view this page."
+ "error-notAuthorized": "You are not authorized to view this page.",
+ "outgoing-webhooks": "Outgoing Webhooks",
+ "outgoingWebhooksPopup-title": "Outgoing Webhooks"
} \ No newline at end of file
diff --git a/i18n/id.i18n.json b/i18n/id.i18n.json
index 45c6187e..10f50359 100644
--- a/i18n/id.i18n.json
+++ b/i18n/id.i18n.json
@@ -55,7 +55,7 @@
"and-n-other-card": "Dan__menghitung__kartu lain",
"and-n-other-card_plural": "Dan__menghitung__kartu lain",
"apply": "Terapkan",
- "app-is-offline": "Aplikasi sedang offline, menyegarkan halaman dapat menyebabkan kehilangan data.",
+ "app-is-offline": "Wekan is loading, please wait. Refreshing the page will cause data loss. If Wekan does not load, please check that Wekan server has not stopped.",
"archive": "Arsip",
"archive-all": "Arsipkan Semua",
"archive-board": "Arsip Panel",
@@ -145,6 +145,7 @@
"computer": "Komputer",
"create": "Buat",
"createBoardPopup-title": "Buat Panel",
+ "chooseBoardSourcePopup-title": "Import board",
"createLabelPopup-title": "Buat Label",
"current": "sekarang",
"date": "Tanggal",
@@ -204,9 +205,13 @@
"headerBarCreateBoardPopup-title": "Buat Panel",
"home": "Beranda",
"import": "Impor",
- "import-board": "Impor dari Trello",
- "import-board-title": "Impor panel dari Trello",
- "import-board-trello-instruction": "Di panel Trello anda, ke 'Menu', terus 'More', 'Print and Export','Export JSON', dan salin hasilnya",
+ "import-board": "import board",
+ "import-board-title-trello": "Impor panel dari Trello",
+ "import-board-title-wekan": "Import board from Wekan",
+ "from-trello": "From Trello",
+ "from-wekan": "From Wekan",
+ "import-board-instruction-trello": "Di panel Trello anda, ke 'Menu', terus 'More', 'Print and Export','Export JSON', dan salin hasilnya",
+ "import-board-instruction-wekan": "In your Wekan board, go to 'Menu', then 'Export board', and copy the text in the downloaded file.",
"import-json-placeholder": "Tempelkan data JSON yang sah disini",
"import-map-members": "Petakan partisipan",
"import-members-map": "Panel yang anda impor punya partisipan. Silahkan petakan anggota yang anda ingin impor ke user [Wekan]",
@@ -355,5 +360,7 @@
"email-invite-register-subject": "__inviter__ mengirim undangan ke Anda",
"email-invite-register-text": "Halo __user__,\n\n__inviter__ mengundang Anda untuk berkolaborasi menggunakan Wekan.\n\nMohon ikuti tautan berikut:\n__url__\n\nDan kode undangan Anda adalah: __icode__\n\nTerima kasih.",
"error-invitation-code-not-exist": "Kode undangan tidak ada",
- "error-notAuthorized": "You are not authorized to view this page."
+ "error-notAuthorized": "You are not authorized to view this page.",
+ "outgoing-webhooks": "Outgoing Webhooks",
+ "outgoingWebhooksPopup-title": "Outgoing Webhooks"
} \ No newline at end of file
diff --git a/i18n/it.i18n.json b/i18n/it.i18n.json
index 1214dd82..35acbdd7 100644
--- a/i18n/it.i18n.json
+++ b/i18n/it.i18n.json
@@ -55,7 +55,7 @@
"and-n-other-card": "E __count__ altra scheda",
"and-n-other-card_plural": "E __count__ altre schede",
"apply": "Applica",
- "app-is-offline": "L'applicazione è al momento offline, ricaricando la pagina perderai i dati.",
+ "app-is-offline": "Wekan è in caricamento, attendi per favore. Ricaricare la pagina causerà una perdita di dati. Se Wekan non si carica, controlla per favore che non ci siano problemi al server.",
"archive": "Archivia",
"archive-all": "Archivia tutto",
"archive-board": "Archivia bacheca",
@@ -145,6 +145,7 @@
"computer": "Computer",
"create": "Crea",
"createBoardPopup-title": "Crea bacheca",
+ "chooseBoardSourcePopup-title": "Importa bacheca",
"createLabelPopup-title": "Crea etichetta",
"current": "corrente",
"date": "Data",
@@ -204,9 +205,13 @@
"headerBarCreateBoardPopup-title": "Crea bacheca",
"home": "Home",
"import": "Importa",
- "import-board": "importa da Trello",
- "import-board-title": "Importa una bacheca da Trello",
- "import-board-trello-instruction": "Nella tua bacheca Trello vai a 'Menu', poi 'Altro', 'Stampa ed esporta', 'Esporta JSON', e copia il testo che compare.",
+ "import-board": "Importa bacheca",
+ "import-board-title-trello": "Importa una bacheca da Trello",
+ "import-board-title-wekan": "Importa bacheca da Wekan",
+ "from-trello": "Da Trello",
+ "from-wekan": "Da Wekan",
+ "import-board-instruction-trello": "Nella tua bacheca Trello vai a 'Menu', poi 'Altro', 'Stampa ed esporta', 'Esporta JSON', e copia il testo che compare.",
+ "import-board-instruction-wekan": "Nella tua bacheca Wekan, vai su 'Menu', poi 'Esporta bacheca', e copia il testo nel file scaricato.",
"import-json-placeholder": "Incolla un JSON valido qui",
"import-map-members": "Mappatura dei membri",
"import-members-map": "La bacheca che hai importato ha alcuni membri. Per favore scegli i membri che vuoi vengano importati negli utenti di Wekan",
@@ -355,5 +360,7 @@
"email-invite-register-subject": "__inviter__ ti ha inviato un invito",
"email-invite-register-text": "Gentile __user__,\n\n__inviter__ ti ha invitato su Wekan per collaborare.\n\nPer favore segui il link qui sotto:\n__url__\n\nIl tuo codice d'invito è: __icode__\n\nGrazie.",
"error-invitation-code-not-exist": "Il codice d'invito non esiste",
- "error-notAuthorized": "Non sei autorizzato ad accedere a questa pagina."
+ "error-notAuthorized": "Non sei autorizzato ad accedere a questa pagina.",
+ "outgoing-webhooks": "Server esterni",
+ "outgoingWebhooksPopup-title": "Server esterni"
} \ No newline at end of file
diff --git a/i18n/ja.i18n.json b/i18n/ja.i18n.json
index c7b5cbd7..b30b9e7f 100644
--- a/i18n/ja.i18n.json
+++ b/i18n/ja.i18n.json
@@ -55,7 +55,7 @@
"and-n-other-card": "And __count__ other card",
"and-n-other-card_plural": "And __count__ other cards",
"apply": "適用",
- "app-is-offline": "現在オフラインです。ページを更新すると保存していないデータは失われます。",
+ "app-is-offline": "Wekan is loading, please wait. Refreshing the page will cause data loss. If Wekan does not load, please check that Wekan server has not stopped.",
"archive": "アーカイブ",
"archive-all": "すべてをアーカイブ",
"archive-board": "ボードをアーカイブ",
@@ -145,6 +145,7 @@
"computer": "コンピューター",
"create": "作成",
"createBoardPopup-title": "ボードの作成",
+ "chooseBoardSourcePopup-title": "Import board",
"createLabelPopup-title": "ラベルの作成",
"current": "現在",
"date": "日付",
@@ -204,9 +205,13 @@
"headerBarCreateBoardPopup-title": "ボードの作成",
"home": "ホーム",
"import": "インポート",
- "import-board": "Trelloからインポート",
- "import-board-title": "Trelloからボードをインポート",
- "import-board-trello-instruction": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text",
+ "import-board": "import board",
+ "import-board-title-trello": "Trelloからボードをインポート",
+ "import-board-title-wekan": "Import board from Wekan",
+ "from-trello": "From Trello",
+ "from-wekan": "From Wekan",
+ "import-board-instruction-trello": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text",
+ "import-board-instruction-wekan": "In your Wekan board, go to 'Menu', then 'Export board', and copy the text in the downloaded file.",
"import-json-placeholder": "Paste your valid JSON data here",
"import-map-members": "メンバーを紐付け",
"import-members-map": "Your imported board has some members. Please map the members you want to import to Wekan users",
@@ -355,5 +360,7 @@
"email-invite-register-subject": "__inviter__ sent you an invitation",
"email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
"error-invitation-code-not-exist": "招待コードが存在しません",
- "error-notAuthorized": "You are not authorized to view this page."
+ "error-notAuthorized": "You are not authorized to view this page.",
+ "outgoing-webhooks": "Outgoing Webhooks",
+ "outgoingWebhooksPopup-title": "Outgoing Webhooks"
} \ No newline at end of file
diff --git a/i18n/ko.i18n.json b/i18n/ko.i18n.json
index c3abf6d1..25c43277 100644
--- a/i18n/ko.i18n.json
+++ b/i18n/ko.i18n.json
@@ -38,14 +38,14 @@
"activity-unjoined": "%s에서 멤버 해제",
"activity-checklist-added": "%s에 체크리스트를 추가함",
"add": "추가",
- "add-attachment": "Add Attachment",
- "add-board": "Add Board",
- "add-card": "Add Card",
- "add-checklist": "Add Checklist",
+ "add-attachment": "첨부파일 추가",
+ "add-board": "보드 추가",
+ "add-card": "카드 추가",
+ "add-checklist": "체크리스트 추가",
"add-checklist-item": "체크리스트에 항목 추가",
"add-cover": "커버 추가",
- "add-label": "Add Label",
- "add-list": "Add List",
+ "add-label": "라벨 추가",
+ "add-list": "리스트 추가",
"add-members": "멤버 추가",
"added": "추가됨",
"addMemberPopup-title": "멤버",
@@ -55,18 +55,18 @@
"and-n-other-card": "__count__ 개의 다른 카드",
"and-n-other-card_plural": "__count__ 개의 다른 카드들",
"apply": "적용",
- "app-is-offline": "현재 페이지는 오프라인 상태입니다. 저장되지 않은 데이터는 손실됩니다. 새로고침 하세요.",
+ "app-is-offline": "Wekan is loading, please wait. Refreshing the page will cause data loss. If Wekan does not load, please check that Wekan server has not stopped.",
"archive": "보관",
"archive-all": "모두 보관",
"archive-board": "보드 저장소 보관",
"archive-card": "카드 저장소 보관",
- "archive-list": "Archive List",
+ "archive-list": "리스트 저장소 보관",
"archive-selection": "저장소 선택",
"archiveBoardPopup-title": "보드를 저장소에 보관하시겠습니까?",
"archived-items": "보관된 아이템",
- "archived-boards": "Archived Boards",
- "restore-board": "Restore Board",
- "no-archived-boards": "No Archived Boards.",
+ "archived-boards": "저장소 보관된 보드들",
+ "restore-board": "보드 복구",
+ "no-archived-boards": "보관된 보드가 없습니다.",
"archives": "저장소",
"assign-member": "멤버 지정",
"attached": "첨부됨",
@@ -74,8 +74,8 @@
"attachment-delete-pop": "영구 첨부파일을 삭제합니다. 되돌릴 수 없습니다.",
"attachmentDeletePopup-title": "첨부 파일을 삭제합니까?",
"attachments": "첨부 파일",
- "auto-watch": "Automatically watch boards when they are created",
- "avatar-too-big": "The avatar is too large (70KB max)",
+ "auto-watch": "생성한 보드를 자동으로 감시합니다.",
+ "avatar-too-big": "아바타 파일이 너무 큽니다. (최대 70KB)",
"back": "뒤로",
"board-change-color": "보드 색 변경",
"board-nb-stars": "%s개의 별",
@@ -139,12 +139,13 @@
"color-sky": "스카이",
"color-yellow": "옐로우",
"comment": "댓글",
- "comment-placeholder": "Write Comment",
- "comment-only": "Comment only",
+ "comment-placeholder": "댓글 입력",
+ "comment-only": "댓글만 입력 가능",
"comment-only-desc": "카드에 댓글만 달수 있습니다.",
"computer": "내 컴퓨터",
"create": "생성",
"createBoardPopup-title": "보드 생성",
+ "chooseBoardSourcePopup-title": "Import board",
"createLabelPopup-title": "라벨 생성",
"current": "경향",
"date": "날짜",
@@ -186,7 +187,7 @@
"error-json-schema": "JSON 데이터에 정보가 올바른 형식으로 포함되어 있지 않습니다.",
"error-list-doesNotExist": "목록이 없습니다.",
"error-user-doesNotExist": "멤버의 정보가 없습니다.",
- "error-user-notAllowSelf": "You can not invite yourself",
+ "error-user-notAllowSelf": "자기 자신을 초대할 수 없습니다.",
"error-user-notCreated": "유저가 생성되지 않았습니다.",
"error-username-taken": "중복된 아이디 입니다.",
"export-board": "보드 내보내기",
@@ -204,9 +205,13 @@
"headerBarCreateBoardPopup-title": "보드 생성",
"home": "홈",
"import": "가져오기",
- "import-board": "Trello에서 가져오기",
- "import-board-title": "Trello에서 보드 가져오기",
- "import-board-trello-instruction": "Trello 게시판에서 'Menu' -> 'More' -> 'Print and Export', 'Export JSON' 선택하여 텍스트 결과값 복사",
+ "import-board": "import board",
+ "import-board-title-trello": "Trello에서 보드 가져오기",
+ "import-board-title-wekan": "Import board from Wekan",
+ "from-trello": "From Trello",
+ "from-wekan": "From Wekan",
+ "import-board-instruction-trello": "Trello 게시판에서 'Menu' -> 'More' -> 'Print and Export', 'Export JSON' 선택하여 텍스트 결과값 복사",
+ "import-board-instruction-wekan": "In your Wekan board, go to 'Menu', then 'Export board', and copy the text in the downloaded file.",
"import-json-placeholder": "유효한 JSON 데이터를 여기에 붙여 넣으십시오.",
"import-map-members": "보드 멤버들",
"import-members-map": "가져온 보드에는 멤버가 있습니다. 원하는 멤버를 Wekan 멤버로 매핑하세요.",
@@ -234,9 +239,9 @@
"listActionPopup-title": "동작 목록",
"listImportCardPopup-title": "Trello 카드 가져 오기",
"listMorePopup-title": "더보기",
- "link-list": "Link to this list",
- "list-delete-pop": "All actions will be removed from the activity feed and you won't be able to recover the list. There is no undo.",
- "list-delete-suggest-archive": "You can archive a list to remove it from the board and preserve the activity.",
+ "link-list": "이 리스트에 링크",
+ "list-delete-pop": "모든 작업이 활동내역에서 제거되며 리스트를 복구 할 수 없습니다. 실행 취소는 불가능 합니다.",
+ "list-delete-suggest-archive": "리스트를 보관하여 보드에서 삭제하고 활동내역을 보존 할 수 있습니다.",
"lists": "목록들",
"log-out": "로그아웃",
"log-in": "로그인",
@@ -281,8 +286,8 @@
"quick-access-description": "여기에 바로 가기를 추가하려면 보드에 별 표시를 체크하세요.",
"remove-cover": "커버 제거",
"remove-from-board": "보드에서 제거",
- "remove-label": "Remove Label",
- "listDeletePopup-title": "Delete List ?",
+ "remove-label": "라벨 제거",
+ "listDeletePopup-title": "리스트를 삭제합니까?",
"remove-member": "멤버 제거",
"remove-member-from-card": "카드에서 제거",
"remove-member-pop": "__boardTitle__에서 __name__(__username__) 을 제거합니까? 이 보드의 모든 카드에서 제거됩니다. 해당 내용을 __name__(__username__) 은(는) 알림으로 받게됩니다.",
@@ -292,7 +297,7 @@
"restore": "복구",
"save": "저장",
"search": "검색",
- "select-color": "Select Color",
+ "select-color": "색 선택",
"shortcut-assign-self": "현재 카드에 자신을 지정하세요.",
"shortcut-autocomplete-emoji": "이모티콘 자동완성",
"shortcut-autocomplete-members": "멤버 자동완성",
@@ -344,16 +349,18 @@
"email-addresses": "이메일 주소",
"smtp-host-description": "이메일을 처리하는 SMTP 서버의 주소입니다.",
"smtp-port-description": "SMTP 서버가 보내는 전자 메일에 사용하는 포트입니다.",
- "smtp-tls-description": "Enable TLS support for SMTP server",
+ "smtp-tls-description": "SMTP 서버에 TLS 지원 사용",
"smtp-host": "SMTP 호스트",
"smtp-port": "SMTP 포트",
"smtp-username": "사용자 이름",
"smtp-password": "암호",
- "smtp-tls": "TLS support",
+ "smtp-tls": "TLS 지원",
"send-from": "보낸 사람",
"invitation-code": "초대 코드",
"email-invite-register-subject": "\"__inviter__ 님이 당신에게 초대장을 보냈습니다.",
"email-invite-register-text": "\"__user__ 님, \n\n__inviter__ 님이 Wekan 보드에 협업을 위하여 초대합니다.\n\n아래 링크를 클릭해주세요 : \n__url__\n\n그리고 초대 코드는 __icode__ 입니다.\n\n감사합니다.",
"error-invitation-code-not-exist": "초대 코드가 존재하지 않습니다.",
- "error-notAuthorized": "You are not authorized to view this page."
+ "error-notAuthorized": "이 페이지를 볼 수있는 권한이 없습니다.",
+ "outgoing-webhooks": "Outgoing Webhooks",
+ "outgoingWebhooksPopup-title": "Outgoing Webhooks"
} \ No newline at end of file
diff --git a/i18n/nb.i18n.json b/i18n/nb.i18n.json
index 02547da3..3cfda1e0 100644
--- a/i18n/nb.i18n.json
+++ b/i18n/nb.i18n.json
@@ -55,7 +55,7 @@
"and-n-other-card": "Og __count__ andre kort",
"and-n-other-card_plural": "Og __count__ andre kort",
"apply": "Lagre",
- "app-is-offline": "Programmet er ikke på nett, å laste siden på nytt vil medføre tap av data.",
+ "app-is-offline": "Wekan is loading, please wait. Refreshing the page will cause data loss. If Wekan does not load, please check that Wekan server has not stopped.",
"archive": "Arkiv",
"archive-all": "Arkiver alle",
"archive-board": "Arkiver tavle",
@@ -145,6 +145,7 @@
"computer": "Computer",
"create": "Create",
"createBoardPopup-title": "Create Board",
+ "chooseBoardSourcePopup-title": "Import board",
"createLabelPopup-title": "Create Label",
"current": "current",
"date": "Date",
@@ -204,9 +205,13 @@
"headerBarCreateBoardPopup-title": "Create Board",
"home": "Home",
"import": "Import",
- "import-board": "import from Trello",
- "import-board-title": "Import board from Trello",
- "import-board-trello-instruction": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text.",
+ "import-board": "import board",
+ "import-board-title-trello": "Import board from Trello",
+ "import-board-title-wekan": "Import board from Wekan",
+ "from-trello": "From Trello",
+ "from-wekan": "From Wekan",
+ "import-board-instruction-trello": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text.",
+ "import-board-instruction-wekan": "In your Wekan board, go to 'Menu', then 'Export board', and copy the text in the downloaded file.",
"import-json-placeholder": "Paste your valid JSON data here",
"import-map-members": "Map members",
"import-members-map": "Your imported board has some members. Please map the members you want to import to Wekan users",
@@ -355,5 +360,7 @@
"email-invite-register-subject": "__inviter__ sent you an invitation",
"email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
"error-invitation-code-not-exist": "Invitation code doesn't exist",
- "error-notAuthorized": "You are not authorized to view this page."
+ "error-notAuthorized": "You are not authorized to view this page.",
+ "outgoing-webhooks": "Outgoing Webhooks",
+ "outgoingWebhooksPopup-title": "Outgoing Webhooks"
} \ No newline at end of file
diff --git a/i18n/pl.i18n.json b/i18n/pl.i18n.json
index 4e3a33c8..b698418a 100644
--- a/i18n/pl.i18n.json
+++ b/i18n/pl.i18n.json
@@ -55,7 +55,7 @@
"and-n-other-card": "And __count__ other card",
"and-n-other-card_plural": "And __count__ other cards",
"apply": "Apply",
- "app-is-offline": "The application is currently offline, refreshing the page will cause data loss.",
+ "app-is-offline": "Wekan is loading, please wait. Refreshing the page will cause data loss. If Wekan does not load, please check that Wekan server has not stopped.",
"archive": "Zarchiwizuj",
"archive-all": "Zarchiwizuj wszystkie",
"archive-board": "Zarchiwizuj tablicę",
@@ -145,6 +145,7 @@
"computer": "Komputer",
"create": "Utwórz",
"createBoardPopup-title": "Utwórz tablicę",
+ "chooseBoardSourcePopup-title": "Import board",
"createLabelPopup-title": "Utwórz etykietę",
"current": "obecny",
"date": "Date",
@@ -204,9 +205,13 @@
"headerBarCreateBoardPopup-title": "Utwórz tablicę",
"home": "Strona główna",
"import": "Importu",
- "import-board": "zaimportuj z Trello",
- "import-board-title": "Import board from Trello",
- "import-board-trello-instruction": "W twojej tablicy na Trello, idź do 'Menu', następnie 'More', 'Print and Export', 'Export JSON' i skopiuj wynik",
+ "import-board": "import board",
+ "import-board-title-trello": "Import board from Trello",
+ "import-board-title-wekan": "Import board from Wekan",
+ "from-trello": "From Trello",
+ "from-wekan": "From Wekan",
+ "import-board-instruction-trello": "W twojej tablicy na Trello, idź do 'Menu', następnie 'More', 'Print and Export', 'Export JSON' i skopiuj wynik",
+ "import-board-instruction-wekan": "In your Wekan board, go to 'Menu', then 'Export board', and copy the text in the downloaded file.",
"import-json-placeholder": "Wklej twój JSON tutaj",
"import-map-members": "Map members",
"import-members-map": "Twoje zaimportowane tablice mają kilku członków. Proszę wybierz członków których chcesz zaimportować do Wekan",
@@ -355,5 +360,7 @@
"email-invite-register-subject": "__inviter__ wysłał Ci zaproszenie",
"email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
"error-invitation-code-not-exist": "Invitation code doesn't exist",
- "error-notAuthorized": "You are not authorized to view this page."
+ "error-notAuthorized": "You are not authorized to view this page.",
+ "outgoing-webhooks": "Outgoing Webhooks",
+ "outgoingWebhooksPopup-title": "Outgoing Webhooks"
} \ No newline at end of file
diff --git a/i18n/pt-BR.i18n.json b/i18n/pt-BR.i18n.json
index 3ef482ff..16259cbc 100644
--- a/i18n/pt-BR.i18n.json
+++ b/i18n/pt-BR.i18n.json
@@ -38,14 +38,14 @@
"activity-unjoined": "saiu de %s",
"activity-checklist-added": "Adicionado lista de verificação a %s",
"add": "Novo",
- "add-attachment": "Add Attachment",
- "add-board": "Add Board",
- "add-card": "Add Card",
- "add-checklist": "Add Checklist",
+ "add-attachment": "Adicionar Anexos",
+ "add-board": "Adiconar Quadro",
+ "add-card": "Adicionar Cartão",
+ "add-checklist": "Adicionar Checklist",
"add-checklist-item": "Adicionar um item à lista de verificação",
"add-cover": "Adicionar Capa",
- "add-label": "Add Label",
- "add-list": "Add List",
+ "add-label": "Adicionar Etiqueta",
+ "add-list": "Adicionar Lista",
"add-members": "Adicionar Membros",
"added": "Criado",
"addMemberPopup-title": "Membros",
@@ -55,18 +55,18 @@
"and-n-other-card": "E __count__ outro cartão",
"and-n-other-card_plural": "E __count__ outros cartões",
"apply": "Aplicar",
- "app-is-offline": "A aplicação está atualmente desligada, atualizando a página causará perda de dados.",
+ "app-is-offline": "O Wekan está carregando, por favor espere. Recarregar a página irá causar perda de dado. Se o Wekan não carregar por favor verifique se o servidor Wekan não está parado.",
"archive": "Arquivar",
"archive-all": "Arquivar Tudo",
"archive-board": "Arquivar Quadro",
"archive-card": "Arquivar Cartão",
- "archive-list": "Archive List",
+ "archive-list": "Lista de Arquivos",
"archive-selection": "Arquivar seleção",
"archiveBoardPopup-title": "Arquivar Quadro?",
"archived-items": "Itens Arquivados",
- "archived-boards": "Archived Boards",
- "restore-board": "Restore Board",
- "no-archived-boards": "No Archived Boards.",
+ "archived-boards": "Quadros Arquivados",
+ "restore-board": "Restaurar Quadro",
+ "no-archived-boards": "Nenhum Quadro Arquivado",
"archives": "Arquivos",
"assign-member": "Atribuir Membro",
"attached": "anexado",
@@ -139,12 +139,13 @@
"color-sky": "céu",
"color-yellow": "amarelo",
"comment": "Comentário",
- "comment-placeholder": "Write Comment",
+ "comment-placeholder": "Escrever Comentário",
"comment-only": "Somente comentários",
"comment-only-desc": "Pode comentar apenas em cartões.",
"computer": "Computador",
"create": "Criar",
"createBoardPopup-title": "Criar Quadro",
+ "chooseBoardSourcePopup-title": "Importar quadro",
"createLabelPopup-title": "Criar Etiqueta",
"current": "atual",
"date": "Data",
@@ -204,9 +205,13 @@
"headerBarCreateBoardPopup-title": "Criar Quadro",
"home": "Início",
"import": "Importar",
- "import-board": "Importar do Trello",
- "import-board-title": "Importar board do Trello",
- "import-board-trello-instruction": "No seu quadro do Trello, vá em 'Menu', depois em 'Mais', 'Imprimir e Exportar', 'Exportar JSON', então copie o texto emitido",
+ "import-board": "importar quadro",
+ "import-board-title-trello": "Importar board do Trello",
+ "import-board-title-wekan": "Importar quadro do Wekan",
+ "from-trello": "Do Trello",
+ "from-wekan": "Do Wekan",
+ "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",
+ "import-board-instruction-wekan": "In your Wekan board, go to 'Menu', then 'Export board', and copy the text in the downloaded file.",
"import-json-placeholder": "Cole seus dados JSON válidos aqui",
"import-map-members": "Mapear membros",
"import-members-map": "O seu quadro importado tem alguns membros. Por favor determine os membros que você deseja importar para os usuários Wekan",
@@ -355,5 +360,7 @@
"email-invite-register-subject": "__inviter__ lhe enviou um convite",
"email-invite-register-text": "Caro __user__,\n\n__inviter__ convidou você para colaborar no Wekan.\n\nPor favor, vá no link abaixo:\n__url__\n\nE seu código de convite é: __icode__\n\nObrigado.",
"error-invitation-code-not-exist": "O código do convite não existe",
- "error-notAuthorized": "You are not authorized to view this page."
+ "error-notAuthorized": "You are not authorized to view this page.",
+ "outgoing-webhooks": "Outgoing Webhooks",
+ "outgoingWebhooksPopup-title": "Outgoing Webhooks"
} \ No newline at end of file
diff --git a/i18n/ro.i18n.json b/i18n/ro.i18n.json
index 51ddf2fb..effbed5e 100644
--- a/i18n/ro.i18n.json
+++ b/i18n/ro.i18n.json
@@ -55,7 +55,7 @@
"and-n-other-card": "And __count__ other card",
"and-n-other-card_plural": "And __count__ other cards",
"apply": "Apply",
- "app-is-offline": "The application is currently offline, refreshing the page will cause data loss.",
+ "app-is-offline": "Wekan is loading, please wait. Refreshing the page will cause data loss. If Wekan does not load, please check that Wekan server has not stopped.",
"archive": "Archive",
"archive-all": "Archive All",
"archive-board": "Archive Board",
@@ -145,6 +145,7 @@
"computer": "Computer",
"create": "Create",
"createBoardPopup-title": "Create Board",
+ "chooseBoardSourcePopup-title": "Import board",
"createLabelPopup-title": "Create Label",
"current": "current",
"date": "Date",
@@ -204,9 +205,13 @@
"headerBarCreateBoardPopup-title": "Create Board",
"home": "Home",
"import": "Import",
- "import-board": "import from Trello",
- "import-board-title": "Import board from Trello",
- "import-board-trello-instruction": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text",
+ "import-board": "import board",
+ "import-board-title-trello": "Import board from Trello",
+ "import-board-title-wekan": "Import board from Wekan",
+ "from-trello": "From Trello",
+ "from-wekan": "From Wekan",
+ "import-board-instruction-trello": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text",
+ "import-board-instruction-wekan": "In your Wekan board, go to 'Menu', then 'Export board', and copy the text in the downloaded file.",
"import-json-placeholder": "Paste your valid JSON data here",
"import-map-members": "Map members",
"import-members-map": "Your imported board has some members. Please map the members you want to import to Wekan users",
@@ -355,5 +360,7 @@
"email-invite-register-subject": "__inviter__ sent you an invitation",
"email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
"error-invitation-code-not-exist": "Invitation code doesn't exist",
- "error-notAuthorized": "You are not authorized to view this page."
+ "error-notAuthorized": "You are not authorized to view this page.",
+ "outgoing-webhooks": "Outgoing Webhooks",
+ "outgoingWebhooksPopup-title": "Outgoing Webhooks"
} \ No newline at end of file
diff --git a/i18n/ru.i18n.json b/i18n/ru.i18n.json
index ab71df62..260f0dcc 100644
--- a/i18n/ru.i18n.json
+++ b/i18n/ru.i18n.json
@@ -38,14 +38,14 @@
"activity-unjoined": "вышел из %s",
"activity-checklist-added": "добавлен список в %s",
"add": "Создать",
- "add-attachment": "Add Attachment",
- "add-board": "Add Board",
- "add-card": "Add Card",
- "add-checklist": "Add Checklist",
+ "add-attachment": "Добавить вложение",
+ "add-board": "Добавить доску",
+ "add-card": "Добавить карту",
+ "add-checklist": "Добавить список",
"add-checklist-item": "Добавить елемент в список",
"add-cover": "Прикрепить",
- "add-label": "Add Label",
- "add-list": "Add List",
+ "add-label": "Добавить метку",
+ "add-list": "Добавить простой список",
"add-members": "Добавить пользователя",
"added": "Добавлено",
"addMemberPopup-title": "Участники",
@@ -55,18 +55,18 @@
"and-n-other-card": "И __count__ другая карточка",
"and-n-other-card_plural": "И __count__ другие карточки",
"apply": "Применить",
- "app-is-offline": "Приложение офлайн, обновление страницы приведет к потере данных.",
+ "app-is-offline": "Wekan загружается, пожалуйста подождите. Обновление страницы может привести к потере данных. Если Wekan не загрузился, пожалуйста проверьте что связь с сервером доступна.",
"archive": "Архивировать",
"archive-all": "Архивировать все",
"archive-board": "Архивировать доску",
"archive-card": "Архивировать карточку",
- "archive-list": "Archive List",
+ "archive-list": "Архивировать список",
"archive-selection": "Архивировать выбранное",
"archiveBoardPopup-title": "Заархивировать доску?",
"archived-items": "Объекты в архиве",
- "archived-boards": "Archived Boards",
- "restore-board": "Restore Board",
- "no-archived-boards": "No Archived Boards.",
+ "archived-boards": "Архивировать доску",
+ "restore-board": "Востановить доску",
+ "no-archived-boards": "Нет архивных досок.",
"archives": "Архивы",
"assign-member": "Пригласить пользователя",
"attached": "прикреплено",
@@ -139,12 +139,13 @@
"color-sky": "голубой",
"color-yellow": "желтый",
"comment": "Отправить",
- "comment-placeholder": "Write Comment",
+ "comment-placeholder": "Написать комментарий",
"comment-only": "Только комментирование",
"comment-only-desc": "Может комментировать только карточки.",
"computer": "Загрузить с компьютера",
"create": "Создать",
"createBoardPopup-title": "Создать доску",
+ "chooseBoardSourcePopup-title": "Импортировать доску",
"createLabelPopup-title": "Создать метку",
"current": "Текущий",
"date": "Дата",
@@ -204,9 +205,13 @@
"headerBarCreateBoardPopup-title": "Создать доску",
"home": "Главная",
"import": "Импорт",
- "import-board": "Импорт с Trello",
- "import-board-title": "Импортировать доску из Trello",
- "import-board-trello-instruction": "На вашей Trello доске нажмите “Menu” - “More” - “Print and export - “Export JSON” и скопируйте полученный текст",
+ "import-board": "импортировать доску",
+ "import-board-title-trello": "Импортировать доску из Trello",
+ "import-board-title-wekan": "Импортировать доску с Wekan",
+ "from-trello": "Из Trello",
+ "from-wekan": "Их Wekan",
+ "import-board-instruction-trello": "На вашей Trello доске нажмите “Menu” - “More” - “Print and export - “Export JSON” и скопируйте полученный текст",
+ "import-board-instruction-wekan": "На вашей Wekan доске, перейдите в “Меню”, далее “Экспортировать доску” и скопируйте текст из файла",
"import-json-placeholder": "Вставьте JSON сюда",
"import-map-members": "Карта пользователей",
"import-members-map": "Вы ипортировали доску с пользователями. Пожалуйста, составьте карту пользователей, которых вы хотите импортировать в Wekan пользователей",
@@ -234,9 +239,9 @@
"listActionPopup-title": "Список действий",
"listImportCardPopup-title": "Импортировать Trello карточку",
"listMorePopup-title": "Поделиться",
- "link-list": "Link to this list",
- "list-delete-pop": "All actions will be removed from the activity feed and you won't be able to recover the list. There is no undo.",
- "list-delete-suggest-archive": "You can archive a list to remove it from the board and preserve the activity.",
+ "link-list": "Ссылка на список",
+ "list-delete-pop": "Все действия будут удалены из ленты активности и вы не сможете заново открыть список. Действие необратимо.",
+ "list-delete-suggest-archive": "Вы можете заархивировать список, чтобы удалить его с доски и сохранить активность.",
"lists": "Списки",
"log-out": "Выйти",
"log-in": "Войти",
@@ -281,8 +286,8 @@
"quick-access-description": "Нажмите на звезду, что добавить ярлык доски на панель.",
"remove-cover": "Открепить",
"remove-from-board": "Удалить с доски",
- "remove-label": "Remove Label",
- "listDeletePopup-title": "Delete List ?",
+ "remove-label": "Удалить метку",
+ "listDeletePopup-title": "Удалить список?",
"remove-member": "Удалить участника",
"remove-member-from-card": "Удалить из карточки",
"remove-member-pop": "Удалить участника __name__ (__username__) из доски __boardTitle__? Участник будет удален из всех карточек. Также он получит уведомление о совершаемом действии.",
@@ -292,7 +297,7 @@
"restore": "Восстановить",
"save": "Сохранить",
"search": "Поиск",
- "select-color": "Select Color",
+ "select-color": "Выбрать цвет",
"shortcut-assign-self": "Связать себя с текущей карточкой",
"shortcut-autocomplete-emoji": "Автозаполнение emoji",
"shortcut-autocomplete-members": "Автозаполнение пользователей",
@@ -355,5 +360,7 @@
"email-invite-register-subject": "__inviter__ прислал вам приглашение",
"email-invite-register-text": "Уважаемый __user__,\n\n__inviter__ приглашает вас в Wekan для сотрудничества.\n\nПожалуйста, проследуйте по ссылке:\n__url__\n\nВаш код приглашения: __icode__\n\nСпасибо.",
"error-invitation-code-not-exist": "Код приглашения не существует",
- "error-notAuthorized": "You are not authorized to view this page."
+ "error-notAuthorized": "У вас нет доступа на просмотр этой страницы.",
+ "outgoing-webhooks": "Исходящие Веб-хуки",
+ "outgoingWebhooksPopup-title": "Исходящие Веб-хуки"
} \ No newline at end of file
diff --git a/i18n/sr.i18n.json b/i18n/sr.i18n.json
index a5f0bb52..00174ab7 100644
--- a/i18n/sr.i18n.json
+++ b/i18n/sr.i18n.json
@@ -55,7 +55,7 @@
"and-n-other-card": "And __count__ other card",
"and-n-other-card_plural": "And __count__ other cards",
"apply": "Primeni",
- "app-is-offline": "Aplikacija je trenutno van mreže, ponovno učitavanje stranice će dovesti do gubitka podataka.",
+ "app-is-offline": "Wekan is loading, please wait. Refreshing the page will cause data loss. If Wekan does not load, please check that Wekan server has not stopped.",
"archive": "Arhiviraj",
"archive-all": "Arhiviraj sve",
"archive-board": "Arhiviraj tablu",
@@ -145,6 +145,7 @@
"computer": "Computer",
"create": "Create",
"createBoardPopup-title": "Create Board",
+ "chooseBoardSourcePopup-title": "Import board",
"createLabelPopup-title": "Create Label",
"current": "current",
"date": "Datum",
@@ -204,9 +205,13 @@
"headerBarCreateBoardPopup-title": "Create Board",
"home": "Home",
"import": "Import",
- "import-board": "import from Trello",
- "import-board-title": "Uvezi tablu iz Trella",
- "import-board-trello-instruction": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text",
+ "import-board": "import board",
+ "import-board-title-trello": "Uvezi tablu iz Trella",
+ "import-board-title-wekan": "Import board from Wekan",
+ "from-trello": "From Trello",
+ "from-wekan": "From Wekan",
+ "import-board-instruction-trello": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text",
+ "import-board-instruction-wekan": "In your Wekan board, go to 'Menu', then 'Export board', and copy the text in the downloaded file.",
"import-json-placeholder": "Paste your valid JSON data here",
"import-map-members": "Mapiraj članove",
"import-members-map": "Your imported board has some members. Please map the members you want to import to Wekan users",
@@ -355,5 +360,7 @@
"email-invite-register-subject": "__inviter__ sent you an invitation",
"email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
"error-invitation-code-not-exist": "Invitation code doesn't exist",
- "error-notAuthorized": "You are not authorized to view this page."
+ "error-notAuthorized": "You are not authorized to view this page.",
+ "outgoing-webhooks": "Outgoing Webhooks",
+ "outgoingWebhooksPopup-title": "Outgoing Webhooks"
} \ No newline at end of file
diff --git a/i18n/sv.i18n.json b/i18n/sv.i18n.json
index 9e6b78a2..6f1d8bd0 100644
--- a/i18n/sv.i18n.json
+++ b/i18n/sv.i18n.json
@@ -38,14 +38,14 @@
"activity-unjoined": "gick ur %s",
"activity-checklist-added": "lade kontrollista till %s",
"add": "Lägg till",
- "add-attachment": "Add Attachment",
+ "add-attachment": "Lägg till bilaga",
"add-board": "Add Board",
- "add-card": "Add Card",
+ "add-card": "Lägg till kort",
"add-checklist": "Add Checklist",
"add-checklist-item": "Lägg till ett objekt till kontrollista",
"add-cover": "Lägg till omslag",
- "add-label": "Add Label",
- "add-list": "Add List",
+ "add-label": "Lägg till etikett",
+ "add-list": "Lägg till lista",
"add-members": "Lägg till medlemmar",
"added": "Lade till",
"addMemberPopup-title": "Medlemmar",
@@ -55,12 +55,12 @@
"and-n-other-card": "Och __count__ annat kort",
"and-n-other-card_plural": "Och __count__ andra kort",
"apply": "Tillämpa",
- "app-is-offline": "Applikationen är för närvarande offline, uppdatera sidan kommer att orsaka dataförlust .",
+ "app-is-offline": "Wekan is loading, please wait. Refreshing the page will cause data loss. If Wekan does not load, please check that Wekan server has not stopped.",
"archive": "Arkivera",
"archive-all": "Arkivera",
"archive-board": "Arkiverad anslagstavla",
"archive-card": "Arkivera kort",
- "archive-list": "Archive List",
+ "archive-list": "Arkivera lista",
"archive-selection": "Arkivera val",
"archiveBoardPopup-title": "Arkivera anslagstavla?",
"archived-items": "Arkivera objekt",
@@ -139,12 +139,13 @@
"color-sky": "himmel",
"color-yellow": "gul",
"comment": "Kommentera",
- "comment-placeholder": "Write Comment",
+ "comment-placeholder": "Skriv kommentar",
"comment-only": "Kommentera endast",
"comment-only-desc": "Kan endast kommentera kort.",
"computer": "Dator",
"create": "Skapa",
"createBoardPopup-title": "Skapa anslagstavla",
+ "chooseBoardSourcePopup-title": "Import board",
"createLabelPopup-title": "Skapa etikett",
"current": "aktuell",
"date": "Datum",
@@ -204,9 +205,13 @@
"headerBarCreateBoardPopup-title": "Skapa anslagstavla",
"home": "Hem",
"import": "Importera",
- "import-board": "Importera från Trello",
- "import-board-title": "Importera anslagstavla från Trello",
- "import-board-trello-instruction": "I din Trello-anslagstavla, gå till 'Meny', sedan 'Mera', 'Skriv ut och exportera', 'Exportera JSON' och kopiera den resulterande text.",
+ "import-board": "import board",
+ "import-board-title-trello": "Importera anslagstavla från Trello",
+ "import-board-title-wekan": "Import board from Wekan",
+ "from-trello": "From Trello",
+ "from-wekan": "From Wekan",
+ "import-board-instruction-trello": "I din Trello-anslagstavla, gå till 'Meny', sedan 'Mera', 'Skriv ut och exportera', 'Exportera JSON' och kopiera den resulterande text.",
+ "import-board-instruction-wekan": "In your Wekan board, go to 'Menu', then 'Export board', and copy the text in the downloaded file.",
"import-json-placeholder": "Klistra in giltigt JSON data här",
"import-map-members": "Kartlägg medlemmar",
"import-members-map": "Din importerade anslagstavla har några medlemmar. Kartlägg medlemmarna som du vill importera till Wekan-användare",
@@ -281,8 +286,8 @@
"quick-access-description": "Stjärnmärk en anslagstavla för att lägga till en genväg i detta fält.",
"remove-cover": "Ta bort omslag",
"remove-from-board": "Ta bort från anslagstavla",
- "remove-label": "Remove Label",
- "listDeletePopup-title": "Delete List ?",
+ "remove-label": "Ta bort etikett",
+ "listDeletePopup-title": "Ta bort lista",
"remove-member": "Ta bort medlem",
"remove-member-from-card": "Ta bort från kort",
"remove-member-pop": "Ta bort __name__ (__username__) från __boardTitle__? Medlemmen kommer att bli borttagen från alla kort i denna anslagstavla. De kommer att få en avisering.",
@@ -292,7 +297,7 @@
"restore": "Återställ",
"save": "Spara",
"search": "Sök",
- "select-color": "Select Color",
+ "select-color": "Välj färg",
"shortcut-assign-self": "Tilldela dig nuvarande kort",
"shortcut-autocomplete-emoji": "Komplettera automatiskt emoji",
"shortcut-autocomplete-members": "Komplettera automatiskt medlemmar",
@@ -355,5 +360,7 @@
"email-invite-register-subject": "__inviter__ skickade dig en inbjudan",
"email-invite-register-text": "Bästa __user__,\n\n__inviter__ inbjuder dig till Wekan för samarbeten.\n\nVänligen följ länken nedan:\n__url__\n\nOch din inbjudningskod är: __icode__\n\nTack.",
"error-invitation-code-not-exist": "Inbjudningskod finns inte",
- "error-notAuthorized": "You are not authorized to view this page."
+ "error-notAuthorized": "You are not authorized to view this page.",
+ "outgoing-webhooks": "Outgoing Webhooks",
+ "outgoingWebhooksPopup-title": "Outgoing Webhooks"
} \ No newline at end of file
diff --git a/i18n/ta.i18n.json b/i18n/ta.i18n.json
index f242b8d4..851b554b 100644
--- a/i18n/ta.i18n.json
+++ b/i18n/ta.i18n.json
@@ -55,7 +55,7 @@
"and-n-other-card": "And __count__ other card",
"and-n-other-card_plural": "And __count__ other cards",
"apply": "Apply",
- "app-is-offline": "The application is currently offline, refreshing the page will cause data loss.",
+ "app-is-offline": "Wekan is loading, please wait. Refreshing the page will cause data loss. If Wekan does not load, please check that Wekan server has not stopped.",
"archive": "Archive",
"archive-all": "Archive All",
"archive-board": "Archive Board",
@@ -145,6 +145,7 @@
"computer": "Computer",
"create": "Create",
"createBoardPopup-title": "Create Board",
+ "chooseBoardSourcePopup-title": "Import board",
"createLabelPopup-title": "Create Label",
"current": "current",
"date": "Date",
@@ -204,9 +205,13 @@
"headerBarCreateBoardPopup-title": "Create Board",
"home": "Home",
"import": "Import",
- "import-board": "import from Trello",
- "import-board-title": "Import board from Trello",
- "import-board-trello-instruction": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text.",
+ "import-board": "import board",
+ "import-board-title-trello": "Import board from Trello",
+ "import-board-title-wekan": "Import board from Wekan",
+ "from-trello": "From Trello",
+ "from-wekan": "From Wekan",
+ "import-board-instruction-trello": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text.",
+ "import-board-instruction-wekan": "In your Wekan board, go to 'Menu', then 'Export board', and copy the text in the downloaded file.",
"import-json-placeholder": "Paste your valid JSON data here",
"import-map-members": "Map members",
"import-members-map": "Your imported board has some members. Please map the members you want to import to Wekan users",
@@ -355,5 +360,7 @@
"email-invite-register-subject": "__inviter__ sent you an invitation",
"email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
"error-invitation-code-not-exist": "Invitation code doesn't exist",
- "error-notAuthorized": "You are not authorized to view this page."
+ "error-notAuthorized": "You are not authorized to view this page.",
+ "outgoing-webhooks": "Outgoing Webhooks",
+ "outgoingWebhooksPopup-title": "Outgoing Webhooks"
} \ No newline at end of file
diff --git a/i18n/th.i18n.json b/i18n/th.i18n.json
index c20ff2a0..5722c522 100644
--- a/i18n/th.i18n.json
+++ b/i18n/th.i18n.json
@@ -55,7 +55,7 @@
"and-n-other-card": "และการ์ดอื่น __count__",
"and-n-other-card_plural": "และการ์ดอื่น ๆ __count__",
"apply": "นำมาใช้",
- "app-is-offline": "ตอนนี้เว็บกำลังออฟไลน์, การรีเฟรชหน้าจะทำให้เกิดการสูญเสียข้อมูล",
+ "app-is-offline": "Wekan is loading, please wait. Refreshing the page will cause data loss. If Wekan does not load, please check that Wekan server has not stopped.",
"archive": "เอกสารที่เก็บไว้",
"archive-all": "เอกสารที่เก็บไว้ทั้งหมด",
"archive-board": "เอกสารบอร์ดที่เก็บไว้",
@@ -145,6 +145,7 @@
"computer": "คอมพิวเตอร์",
"create": "สร้าง",
"createBoardPopup-title": "สร้างบอร์ด",
+ "chooseBoardSourcePopup-title": "Import board",
"createLabelPopup-title": "สร้างป้ายกำกับ",
"current": "ปัจจุบัน",
"date": "วันที่",
@@ -204,9 +205,13 @@
"headerBarCreateBoardPopup-title": "สร้างบอร์ด",
"home": "หน้าหลัก",
"import": "นำเข้า",
- "import-board": "นำเข้าจาก Trello",
- "import-board-title": "นำเข้าบอร์ดจาก Trello",
- "import-board-trello-instruction": "ใน Trello ของคุณให้ไปที่ 'Menu' และไปที่ More -> Print and Export -> Export JSON และคัดลอกข้อความจากนั้น",
+ "import-board": "import board",
+ "import-board-title-trello": "นำเข้าบอร์ดจาก Trello",
+ "import-board-title-wekan": "Import board from Wekan",
+ "from-trello": "From Trello",
+ "from-wekan": "From Wekan",
+ "import-board-instruction-trello": "ใน Trello ของคุณให้ไปที่ 'Menu' และไปที่ More -> Print and Export -> Export JSON และคัดลอกข้อความจากนั้น",
+ "import-board-instruction-wekan": "In your Wekan board, go to 'Menu', then 'Export board', and copy the text in the downloaded file.",
"import-json-placeholder": "วางข้อมูล JSON ที่ถูกต้องของคุณที่นี่",
"import-map-members": "แผนที่สมาชิก",
"import-members-map": "Your imported board has some members. Please map the members you want to import to Wekan users",
@@ -355,5 +360,7 @@
"email-invite-register-subject": "__inviter__ ส่งคำเชิญให้คุณ",
"email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
"error-invitation-code-not-exist": "Invitation code doesn't exist",
- "error-notAuthorized": "You are not authorized to view this page."
+ "error-notAuthorized": "You are not authorized to view this page.",
+ "outgoing-webhooks": "Outgoing Webhooks",
+ "outgoingWebhooksPopup-title": "Outgoing Webhooks"
} \ No newline at end of file
diff --git a/i18n/tr.i18n.json b/i18n/tr.i18n.json
index 09ab7fb7..f678a4d0 100644
--- a/i18n/tr.i18n.json
+++ b/i18n/tr.i18n.json
@@ -1,8 +1,8 @@
{
"accept": "Kabul Et",
- "act-activity-notify": "[Wekan] Aktivite Bildirimi",
+ "act-activity-notify": "[Wekan] Etkinlik Bildirimi",
"act-addAttachment": "__card__ kartına __attachment__ dosyasını ekledi",
- "act-addComment": "__card__ kartına bir yorum bırakıt: __comment__",
+ "act-addComment": "__card__ kartına bir yorum bıraktı: __comment__",
"act-createBoard": "__board__ panosunu oluşturdu",
"act-createCard": "__card__ kartını ___list__ listesine ekledi.",
"act-createList": "__list__ listesini __board__ panosuna ekledi",
@@ -21,7 +21,7 @@
"act-withBoardTitle": "[Wekan] __board__",
"act-withCardTitle": "[__board__] __card__",
"actions": "İşlemler",
- "activities": "Aktiviteler",
+ "activities": "Etkinlikler",
"activity": "Etkinlik",
"activity-added": "%s içine %s ekledi",
"activity-archived": "%s arşivledi",
@@ -38,14 +38,14 @@
"activity-unjoined": "%s içinden ayrıldı",
"activity-checklist-added": "%s içine liste ekledi",
"add": "Ekle",
- "add-attachment": "Add Attachment",
- "add-board": "Add Board",
- "add-card": "Add Card",
- "add-checklist": "Add Checklist",
- "add-checklist-item": "Listeye yeni bir eleman ekle",
+ "add-attachment": "Ek Ekle",
+ "add-board": "Pano Ekle",
+ "add-card": "Kart Ekle",
+ "add-checklist": "Yapılacak Listesi Ekle",
+ "add-checklist-item": "Yapılacak listes yeni bir eleman ekle",
"add-cover": "Kapak resmi ekle",
- "add-label": "Add Label",
- "add-list": "Add List",
+ "add-label": "Etiket Ekle",
+ "add-list": "Liste Ekle",
"add-members": "Üye ekle",
"added": "Eklendi",
"addMemberPopup-title": "Üyeler",
@@ -55,27 +55,27 @@
"and-n-other-card": "Ve __count__ diğer kart",
"and-n-other-card_plural": "Ve __count__ diğer kart",
"apply": "Uygula",
- "app-is-offline": "Uygulama şu an çevrim dışı, saya yenilemek kaydedilmemiş veri kaybına yol açacaktır.",
+ "app-is-offline": "Wekan yükleniyor, lütfen bekleyin. Sayfayı yenilemek veri kaybına sebep olabilir. Eğer Wekan yüklenmezse, lütfen Wekan sunucusunun çalıştığından emin olun.",
"archive": "Arşivle",
"archive-all": "Tümünü Arşivle",
"archive-board": "Panoyu Arşivle",
- "archive-card": "Kartı arşivle",
- "archive-list": "Archive List",
+ "archive-card": "Kartı Arşivle",
+ "archive-list": "Listeyi Arşivle",
"archive-selection": "Seçimi arşivle",
"archiveBoardPopup-title": "Pano arşivlensin mi?",
"archived-items": "Arşivlenmiş Öğeler",
- "archived-boards": "Archived Boards",
- "restore-board": "Restore Board",
- "no-archived-boards": "No Archived Boards.",
+ "archived-boards": "Arşivlenmiş Panolar",
+ "restore-board": "Panoyu Tekrar Yükle",
+ "no-archived-boards": "Arşivlenmiş Pano Yok.",
"archives": "Arşiv",
"assign-member": "Üye ata",
- "attached": "dosya eklendi",
+ "attached": "dosya(sı) eklendi",
"attachment": "Ek Dosya",
- "attachment-delete-pop": "Ek dosya silme işlemi kalıcıdır. Geri dönüşü yok",
- "attachmentDeletePopup-title": "Ek Dosya Silinsin Mi?",
- "attachments": "Ek Dosyalar",
- "auto-watch": "Automatically watch boards when they are created",
- "avatar-too-big": "The avatar is too large (70KB max)",
+ "attachment-delete-pop": "Ek silme işlemi kalıcıdır ve geri alınamaz.",
+ "attachmentDeletePopup-title": "Ek Silinsin Mi?",
+ "attachments": "Ekler",
+ "auto-watch": "Oluşan yeni panoları kendiliğinden izlemeye al",
+ "avatar-too-big": "Avatar boyutu çok büyük (En fazla 70KB olabilir)",
"back": "Geri",
"board-change-color": "Renk değiştir",
"board-nb-stars": "%s yıldız",
@@ -124,27 +124,28 @@
"checklists": "Doğrulama Listeleri",
"click-to-star": "Bu panoyu yıldızlamak için tıkla.",
"click-to-unstar": "Bu panunun yıldızını kaldırmak için tıkla.",
- "clipboard": "Panodan (cliboard) veya sürükle ve bırak ile",
+ "clipboard": "Panodan (clipboard) veya sürükle ve bırak ile",
"close": "Kapat",
"close-board": "Panoyu kapat",
"close-board-pop": "You will be able to restore the board by clicking the “Archives” button from the home header.",
"color-black": "siyah",
"color-blue": "mavi",
"color-green": "yeşil",
- "color-lime": "çim",
+ "color-lime": "misket limonu",
"color-orange": "turuncu",
"color-pink": "pembe",
"color-purple": "mor",
"color-red": "kırmızı",
"color-sky": "açık mavi",
"color-yellow": "sarı",
- "comment": "Yorum Gönder",
- "comment-placeholder": "Write Comment",
- "comment-only": "Comment only",
+ "comment": "Yorum",
+ "comment-placeholder": "Yorum Yaz",
+ "comment-only": "Sadece yorum",
"comment-only-desc": "Sadece kartlara yorum yazabilir.",
"computer": "Bilgisayar",
"create": "Oluştur",
"createBoardPopup-title": "Pano Oluşturma",
+ "chooseBoardSourcePopup-title": "Panoyu içe aktar",
"createLabelPopup-title": "Etiket Oluşturma",
"current": "mevcut",
"date": "Tarih",
@@ -186,7 +187,7 @@
"error-json-schema": "Girdiğin JSON metni tüm bilgileri doğru biçimde barındırmıyor",
"error-list-doesNotExist": "Liste bulunamadı",
"error-user-doesNotExist": "Kullanıcı bulunamadı",
- "error-user-notAllowSelf": "You can not invite yourself",
+ "error-user-notAllowSelf": "Kendi kendini davet edemezsin",
"error-user-notCreated": "Bu üye oluşturulmadı",
"error-username-taken": "Kullanıcı adı zaten alınmış",
"export-board": "Panoyu dışarı aktar",
@@ -204,9 +205,13 @@
"headerBarCreateBoardPopup-title": "Pano Oluşturma",
"home": "Ana Sayfa",
"import": "İçeri aktar",
- "import-board": "Trello'dan içeri aktar",
- "import-board-title": "Trello'dan panoları içeri aktarır",
- "import-board-trello-instruction": "Trello panonuzda, 'Menü'ye gidip, ardıdan, 'daha fazlası' na tıklayın, 'Yazdır ve Çıktı al' sayfasından 'JSON biçiminde çıktı al' diyip, çıkan metni buraya kopyalayın.",
+ "import-board": "import board",
+ "import-board-title-trello": "Trello'dan panoyu içeri aktar",
+ "import-board-title-wekan": "Wekan'dan panoyu içe aktar",
+ "from-trello": "Trello'dan",
+ "from-wekan": "Wekan'dan",
+ "import-board-instruction-trello": "Trello panonuzda, 'Menü'ye gidip, ardıdan, 'daha fazlası' na tıklayın, 'Yazdır ve Çıktı al' sayfasından 'JSON biçiminde çıktı al' diyip, çıkan metni buraya kopyalayın.",
+ "import-board-instruction-wekan": "In your Wekan board, go to 'Menu', then 'Export board', and copy the text in the downloaded file.",
"import-json-placeholder": "Geçerli JSON verisini buraya yapıştırın",
"import-map-members": "Üyeleri eşleştirme",
"import-members-map": "İçe aktardığın panoda bazı kullanıcılar var. Lütfen bu kullanıcıları Wekan panosundaki kullanıcılarla eşleştirin.",
@@ -216,7 +221,7 @@
"info": "Bilgiler",
"initials": "İlk Harfleri",
"invalid-date": "Geçersiz tarih",
- "joined": "katılma",
+ "joined": "katıldı",
"just-invited": "Bu panoya şimdi davet edildin.",
"keyboard-shortcuts": "Klavye kısayolları",
"label-create": "Etiket Oluşturma",
@@ -234,7 +239,7 @@
"listActionPopup-title": "Liste İşlemleri",
"listImportCardPopup-title": "Bir Trello kartını içeri aktar",
"listMorePopup-title": "Daha",
- "link-list": "Link to this list",
+ "link-list": "Listeye doğrudan bağlantı",
"list-delete-pop": "All actions will be removed from the activity feed and you won't be able to recover the list. There is no undo.",
"list-delete-suggest-archive": "You can archive a list to remove it from the board and preserve the activity.",
"lists": "Listeler",
@@ -281,8 +286,8 @@
"quick-access-description": "Bu bara kısayol olarak bir pano eklemek için panoyu yıldızlamalısınız",
"remove-cover": "Kapak resmini kaldır",
"remove-from-board": "Panodan kaldır",
- "remove-label": "Remove Label",
- "listDeletePopup-title": "Delete List ?",
+ "remove-label": "Etiketi Kaldır",
+ "listDeletePopup-title": "Liste silinsin mi?",
"remove-member": "Üyeyi Çıkar",
"remove-member-from-card": "Karttan Çıkar",
"remove-member-pop": "__boardTitle__ panosundan __name__ (__username__) çıkarılsın mı? Üye, bu panodaki tüm kartlardan çıkarılacak ve bir bildirim alacak.",
@@ -292,7 +297,7 @@
"restore": "Geri yükleme",
"save": "Kaydet",
"search": "Arama",
- "select-color": "Select Color",
+ "select-color": "Renk Seç",
"shortcut-assign-self": "Kendini karta ekle",
"shortcut-autocomplete-emoji": "Otomatik tamamlayan emoji yüz ifadeleri",
"shortcut-autocomplete-members": "Otomatik tamamlayan üye isimleri",
@@ -349,11 +354,13 @@
"smtp-port": "SMTP portu",
"smtp-username": "Kullanıcı adı",
"smtp-password": "Parola",
- "smtp-tls": "TLS kriptolama desteği",
+ "smtp-tls": "TLS desteği",
"send-from": "Gönderen",
"invitation-code": "Davetiye kodu",
"email-invite-register-subject": "__inviter__ size bir davetiye gönderdi",
- "email-invite-register-text": "Sevgili __user__,\n\n__inviter__ Sizi koordine çalışabilmek için Wekan'a davet etti.\n\nLütfen aşağıdaki linke tıklayın:\n__url__\n\nDavetiye kodunuz: __icode__\n\nTeşekkürler.",
+ "email-invite-register-text": "Sevgili __user__,\n\n__inviter__ sizi beraber çalışabilmek için Wekan'a davet etti.\n\nLütfen aşağıdaki linke tıklayın:\n__url__\n\nDavetiye kodunuz: __icode__\n\nTeşekkürler.",
"error-invitation-code-not-exist": "Davetiye kodu bulunamadı",
- "error-notAuthorized": "You are not authorized to view this page."
+ "error-notAuthorized": "Bu sayfayı görmek için yetkiniz yok.",
+ "outgoing-webhooks": "Outgoing Webhooks",
+ "outgoingWebhooksPopup-title": "Outgoing Webhooks"
} \ No newline at end of file
diff --git a/i18n/uk.i18n.json b/i18n/uk.i18n.json
index aa6b1da3..52e7d606 100644
--- a/i18n/uk.i18n.json
+++ b/i18n/uk.i18n.json
@@ -55,7 +55,7 @@
"and-n-other-card": "та __count__ інших карток",
"and-n-other-card_plural": "та __count__ інших карток",
"apply": "Прийняти",
- "app-is-offline": "Додаток зараз оффлайн, оновлення сторінки призведе до втрати зміненних данних.",
+ "app-is-offline": "Wekan is loading, please wait. Refreshing the page will cause data loss. If Wekan does not load, please check that Wekan server has not stopped.",
"archive": "Архів",
"archive-all": "Архівувати всі",
"archive-board": "Архівувати Дошку",
@@ -145,6 +145,7 @@
"computer": "Computer",
"create": "Create",
"createBoardPopup-title": "Create Board",
+ "chooseBoardSourcePopup-title": "Import board",
"createLabelPopup-title": "Create Label",
"current": "current",
"date": "Date",
@@ -204,9 +205,13 @@
"headerBarCreateBoardPopup-title": "Create Board",
"home": "Home",
"import": "Import",
- "import-board": "import from Trello",
- "import-board-title": "Import board from Trello",
- "import-board-trello-instruction": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text.",
+ "import-board": "import board",
+ "import-board-title-trello": "Import board from Trello",
+ "import-board-title-wekan": "Import board from Wekan",
+ "from-trello": "From Trello",
+ "from-wekan": "From Wekan",
+ "import-board-instruction-trello": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text.",
+ "import-board-instruction-wekan": "In your Wekan board, go to 'Menu', then 'Export board', and copy the text in the downloaded file.",
"import-json-placeholder": "Paste your valid JSON data here",
"import-map-members": "Map members",
"import-members-map": "Your imported board has some members. Please map the members you want to import to Wekan users",
@@ -355,5 +360,7 @@
"email-invite-register-subject": "__inviter__ sent you an invitation",
"email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
"error-invitation-code-not-exist": "Invitation code doesn't exist",
- "error-notAuthorized": "You are not authorized to view this page."
+ "error-notAuthorized": "You are not authorized to view this page.",
+ "outgoing-webhooks": "Outgoing Webhooks",
+ "outgoingWebhooksPopup-title": "Outgoing Webhooks"
} \ No newline at end of file
diff --git a/i18n/vi.i18n.json b/i18n/vi.i18n.json
index 839a712b..9a65f9c9 100644
--- a/i18n/vi.i18n.json
+++ b/i18n/vi.i18n.json
@@ -55,7 +55,7 @@
"and-n-other-card": "And __count__ other card",
"and-n-other-card_plural": "And __count__ other cards",
"apply": "Ứng Dụng",
- "app-is-offline": "The application is currently offline, refreshing the page will cause data loss.",
+ "app-is-offline": "Wekan is loading, please wait. Refreshing the page will cause data loss. If Wekan does not load, please check that Wekan server has not stopped.",
"archive": "Lưu Trữ",
"archive-all": "Lưu Trữ Tất Cả",
"archive-board": "Lưu Trữ Bảng",
@@ -145,6 +145,7 @@
"computer": "Computer",
"create": "Create",
"createBoardPopup-title": "Create Board",
+ "chooseBoardSourcePopup-title": "Import board",
"createLabelPopup-title": "Create Label",
"current": "current",
"date": "Date",
@@ -204,9 +205,13 @@
"headerBarCreateBoardPopup-title": "Create Board",
"home": "Home",
"import": "Import",
- "import-board": "import from Trello",
- "import-board-title": "Import board from Trello",
- "import-board-trello-instruction": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text.",
+ "import-board": "import board",
+ "import-board-title-trello": "Import board from Trello",
+ "import-board-title-wekan": "Import board from Wekan",
+ "from-trello": "From Trello",
+ "from-wekan": "From Wekan",
+ "import-board-instruction-trello": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text.",
+ "import-board-instruction-wekan": "In your Wekan board, go to 'Menu', then 'Export board', and copy the text in the downloaded file.",
"import-json-placeholder": "Paste your valid JSON data here",
"import-map-members": "Map members",
"import-members-map": "Your imported board has some members. Please map the members you want to import to Wekan users",
@@ -355,5 +360,7 @@
"email-invite-register-subject": "__inviter__ sent you an invitation",
"email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
"error-invitation-code-not-exist": "Invitation code doesn't exist",
- "error-notAuthorized": "You are not authorized to view this page."
+ "error-notAuthorized": "You are not authorized to view this page.",
+ "outgoing-webhooks": "Outgoing Webhooks",
+ "outgoingWebhooksPopup-title": "Outgoing Webhooks"
} \ No newline at end of file
diff --git a/i18n/zh-CN.i18n.json b/i18n/zh-CN.i18n.json
index ee583916..e2a66b4c 100644
--- a/i18n/zh-CN.i18n.json
+++ b/i18n/zh-CN.i18n.json
@@ -38,14 +38,14 @@
"activity-unjoined": "已解除 %s 关联",
"activity-checklist-added": "已经将清单添加到 %s",
"add": "添加",
- "add-attachment": "Add Attachment",
- "add-board": "Add Board",
- "add-card": "Add Card",
- "add-checklist": "Add Checklist",
+ "add-attachment": "添加附件",
+ "add-board": "添加看板",
+ "add-card": "添加卡片",
+ "add-checklist": "添加待办清单",
"add-checklist-item": "扩充清单",
"add-cover": "添加封面",
- "add-label": "Add Label",
- "add-list": "Add List",
+ "add-label": "添加标签",
+ "add-list": "添加列表",
"add-members": "添加成员",
"added": "添加",
"addMemberPopup-title": "成员",
@@ -55,18 +55,18 @@
"and-n-other-card": "和其他 __count__ 个卡片",
"and-n-other-card_plural": "和其他 __count__ 个卡片",
"apply": "应用",
- "app-is-offline": "处于离线状态,刷新页面将导致数据丢失。",
+ "app-is-offline": "Wekan is loading, please wait. Refreshing the page will cause data loss. If Wekan does not load, please check that Wekan server has not stopped.",
"archive": "归档",
"archive-all": "全部归档",
"archive-board": "归档看板",
"archive-card": "归档卡片",
- "archive-list": "Archive List",
+ "archive-list": "归档列表",
"archive-selection": "归档所选内容",
"archiveBoardPopup-title": "确定要归档看板吗?",
"archived-items": "已归档项目",
- "archived-boards": "Archived Boards",
- "restore-board": "Restore Board",
- "no-archived-boards": "No Archived Boards.",
+ "archived-boards": "已归档看板",
+ "restore-board": "还原看板",
+ "no-archived-boards": "没有已归档的看板",
"archives": "回收箱",
"assign-member": "分配成员",
"attached": "附加",
@@ -139,12 +139,13 @@
"color-sky": "天蓝",
"color-yellow": "黄色",
"comment": "评论",
- "comment-placeholder": "Write Comment",
+ "comment-placeholder": "添加评论",
"comment-only": "Comment only",
"comment-only-desc": "只能在卡片上评论。",
"computer": "从本机上传",
"create": "创建",
"createBoardPopup-title": "创建看板",
+ "chooseBoardSourcePopup-title": "Import board",
"createLabelPopup-title": "创建标签",
"current": "当前",
"date": "日期",
@@ -204,9 +205,13 @@
"headerBarCreateBoardPopup-title": "创建看板",
"home": "首页",
"import": "导入",
- "import-board": "从 Trello 导入",
- "import-board-title": "从Trello导入看板",
- "import-board-trello-instruction": "在你的Trello看板中,点击“菜单”,然后选择“更多”,“打印与导出”,“导出为 JSON” 并拷贝结果文本",
+ "import-board": "import board",
+ "import-board-title-trello": "从Trello导入看板",
+ "import-board-title-wekan": "Import board from Wekan",
+ "from-trello": "From Trello",
+ "from-wekan": "From Wekan",
+ "import-board-instruction-trello": "在你的Trello看板中,点击“菜单”,然后选择“更多”,“打印与导出”,“导出为 JSON” 并拷贝结果文本",
+ "import-board-instruction-wekan": "In your Wekan board, go to 'Menu', then 'Export board', and copy the text in the downloaded file.",
"import-json-placeholder": "粘贴您有效的 JSON 数据至此",
"import-map-members": "映射成员",
"import-members-map": "您导入的看板有一些成员。请将您想导入的成员映射到 Wekan 用户。",
@@ -234,7 +239,7 @@
"listActionPopup-title": "列表操作",
"listImportCardPopup-title": "导入 Trello 卡片",
"listMorePopup-title": "更多",
- "link-list": "Link to this list",
+ "link-list": "关联到这个列表",
"list-delete-pop": "All actions will be removed from the activity feed and you won't be able to recover the list. There is no undo.",
"list-delete-suggest-archive": "You can archive a list to remove it from the board and preserve the activity.",
"lists": "列表",
@@ -281,8 +286,8 @@
"quick-access-description": "星标看板在导航条中添加快捷方式",
"remove-cover": "移除封面",
"remove-from-board": "从看板中删除",
- "remove-label": "Remove Label",
- "listDeletePopup-title": "Delete List ?",
+ "remove-label": "移除标签",
+ "listDeletePopup-title": "删除列表",
"remove-member": "移除成员",
"remove-member-from-card": "从该卡片中移除",
"remove-member-pop": "确定从 __boardTitle__ 中移除 __name__ (__username__) 吗? 该成员将被从该看板的所有卡片中移除,同时他会收到一条提醒。",
@@ -292,7 +297,7 @@
"restore": "还原",
"save": "保存",
"search": "搜索",
- "select-color": "Select Color",
+ "select-color": "选择颜色",
"shortcut-assign-self": "分配当前卡片给自己",
"shortcut-autocomplete-emoji": "表情符号自动补全",
"shortcut-autocomplete-members": "自动补全成员",
@@ -355,5 +360,7 @@
"email-invite-register-subject": "__inviter__ 向您发出邀请",
"email-invite-register-text": "亲爱的 __user__,\n\n__inviter__ 邀请您加入 Wekan 进行协作。\n\n请访问下面的链接︰\n__url__\n\n您的的邀请码是︰\n__icode__\n\n非常感谢。",
"error-invitation-code-not-exist": "邀请码不存在",
- "error-notAuthorized": "You are not authorized to view this page."
+ "error-notAuthorized": "You are not authorized to view this page.",
+ "outgoing-webhooks": "Outgoing Webhooks",
+ "outgoingWebhooksPopup-title": "Outgoing Webhooks"
} \ No newline at end of file
diff --git a/i18n/zh-TW.i18n.json b/i18n/zh-TW.i18n.json
index df1cdb55..35e839dc 100644
--- a/i18n/zh-TW.i18n.json
+++ b/i18n/zh-TW.i18n.json
@@ -55,7 +55,7 @@
"and-n-other-card": "和其他 __count__ 個卡片",
"and-n-other-card_plural": "和其他 __count__ 個卡片",
"apply": "送出",
- "app-is-offline": "目前狀態為離線,若整重將會造成資料遺失",
+ "app-is-offline": "Wekan is loading, please wait. Refreshing the page will cause data loss. If Wekan does not load, please check that Wekan server has not stopped.",
"archive": "刪除",
"archive-all": "全部刪除",
"archive-board": "刪除看板",
@@ -145,6 +145,7 @@
"computer": "從本機上傳",
"create": "建立",
"createBoardPopup-title": "建立看板",
+ "chooseBoardSourcePopup-title": "Import board",
"createLabelPopup-title": "建立標籤",
"current": "目前",
"date": "日期",
@@ -204,9 +205,13 @@
"headerBarCreateBoardPopup-title": "建立看板",
"home": "首頁",
"import": "匯入",
- "import-board": "匯入 Trello 資料",
- "import-board-title": "匯入在 Trello 的看板",
- "import-board-trello-instruction": "在你的Trello看板中,點選“功能表”,然後選擇“更多”,“列印與匯出”,“匯出為 JSON” 並拷貝結果文本",
+ "import-board": "import board",
+ "import-board-title-trello": "匯入在 Trello 的看板",
+ "import-board-title-wekan": "Import board from Wekan",
+ "from-trello": "From Trello",
+ "from-wekan": "From Wekan",
+ "import-board-instruction-trello": "在你的Trello看板中,點選“功能表”,然後選擇“更多”,“列印與匯出”,“匯出為 JSON” 並拷貝結果文本",
+ "import-board-instruction-wekan": "In your Wekan board, go to 'Menu', then 'Export board', and copy the text in the downloaded file.",
"import-json-placeholder": "貼上您有效的 JSON 資料至此",
"import-map-members": "複製成員",
"import-members-map": "您匯入的看板有一些成員。請將您想匯入的成員映射到 Wekan 使用者。",
@@ -355,5 +360,7 @@
"email-invite-register-subject": "__inviter__ 向您發出邀請",
"email-invite-register-text": "親愛的 __user__,\n\n__inviter__ 邀請您加入 Wekan 一同協作\n\n請點擊下列連結:\n__url__\n\n您的邀請碼為:__icode__\n\n謝謝。",
"error-invitation-code-not-exist": "邀請碼不存在",
- "error-notAuthorized": "沒有適合的權限觀看"
+ "error-notAuthorized": "沒有適合的權限觀看",
+ "outgoing-webhooks": "Outgoing Webhooks",
+ "outgoingWebhooksPopup-title": "Outgoing Webhooks"
} \ No newline at end of file
diff --git a/models/activities.js b/models/activities.js
index 9a41d4aa..f1e52493 100644
--- a/models/activities.js
+++ b/models/activities.js
@@ -131,5 +131,10 @@ if (Meteor.isServer) {
Notifications.getUsers(participants, watchers).forEach((user) => {
Notifications.notify(user, title, description, params);
});
+
+ const integration = Integrations.findOne({ boardId: board._id, type: 'outgoing-webhooks', enabled: true });
+ if (integration) {
+ Meteor.call('outgoingWebhooks', integration, description, params);
+ }
});
}
diff --git a/models/export.js b/models/export.js
index 7b22f45d..7243cf24 100644
--- a/models/export.js
+++ b/models/export.js
@@ -55,12 +55,32 @@ class Exporter {
result.cards = Cards.find(byBoard, noBoardId).fetch();
result.comments = CardComments.find(byBoard, noBoardId).fetch();
result.activities = Activities.find(byBoard, noBoardId).fetch();
- // for attachments we only export IDs and absolute url to original doc
+ // [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 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
+ callback(null, buffer.toString('base64'));
+ });
+ };
+ 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()),
+ // url: FlowRouter.url(attachment.url()),
+ file: getBase64DataSync(attachment),
+ name: attachment.original.name,
+ type: attachment.original.type,
};
});
diff --git a/models/import.js b/models/import.js
index 39a8835e..2e58c90f 100644
--- a/models/import.js
+++ b/models/import.js
@@ -1,507 +1,28 @@
-const DateString = Match.Where(function (dateAsString) {
- check(dateAsString, String);
- return moment(dateAsString, moment.ISO_8601).isValid();
-});
-
-class TrelloCreator {
- 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 Trello id
- // (so we only parse actions once!)
- this.createdAt = {
- board: null,
- cards: {},
- lists: {},
- };
- // The object creator Trello Id, indexed by the object Trello id
- // (so we only parse actions once!)
- this.createdBy = {
- cards: {}, // only cards have a field for that
- };
-
- // Map of labels Trello ID => Wekan ID
- this.labels = {};
- // Map of lists Trello ID => Wekan ID
- this.lists = {};
- // Map of cards Trello ID => Wekan ID
- this.cards = {};
- // The comments, indexed by Trello card id (to map when importing cards)
- this.comments = {};
- // the members, indexed by Trello member id => Wekan user ID
- this.members = data.membersMapping ? data.membersMapping : {};
-
- // maps a trelloCardId to an array of trelloAttachments
- 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 trelloUserId is provided and we have a mapping,
- * return it.
- * Otherwise return current logged user.
- * @param trelloUserId
- * @private
- */
- _user(trelloUserId) {
- if(trelloUserId && this.members[trelloUserId]) {
- return this.members[trelloUserId];
- }
- return Meteor.userId();
- }
-
- checkActions(trelloActions) {
- check(trelloActions, [Match.ObjectIncluding({
- data: Object,
- date: DateString,
- type: String,
- })]);
- // XXX we could perform more thorough checks based on action type
- }
-
- checkBoard(trelloBoard) {
- check(trelloBoard, Match.ObjectIncluding({
- closed: Boolean,
- name: String,
- prefs: Match.ObjectIncluding({
- // XXX refine control by validating 'background' against a list of
- // allowed values (is it worth the maintenance?)
- background: String,
- permissionLevel: Match.Where((value) => {
- return ['org', 'private', 'public'].indexOf(value)>= 0;
- }),
- }),
- }));
- }
-
- checkCards(trelloCards) {
- check(trelloCards, [Match.ObjectIncluding({
- closed: Boolean,
- dateLastActivity: DateString,
- desc: String,
- idLabels: [String],
- idMembers: [String],
- name: String,
- pos: Number,
- })]);
- }
-
- checkLabels(trelloLabels) {
- check(trelloLabels, [Match.ObjectIncluding({
- // XXX refine control by validating 'color' against a list of allowed
- // values (is it worth the maintenance?)
- color: String,
- name: String,
- })]);
- }
-
- checkLists(trelloLists) {
- check(trelloLists, [Match.ObjectIncluding({
- closed: Boolean,
- name: String,
- })]);
- }
-
- checkChecklists(trelloChecklists) {
- check(trelloChecklists, [Match.ObjectIncluding({
- idBoard: String,
- idCard: String,
- name: String,
- checkItems: [Match.ObjectIncluding({
- state: String,
- name: String,
- })],
- })]);
- }
-
- // You must call parseActions before calling this one.
- createBoardAndLabels(trelloBoard) {
- const boardToCreate = {
- archived: trelloBoard.closed,
- color: this.getColor(trelloBoard.prefs.background),
- // very old boards won't have a creation activity so no creation date
- createdAt: this._now(this.createdAt.board),
- labels: [],
- members: [{
- userId: Meteor.userId(),
- isAdmin: true,
- isActive: true,
- isCommentOnly: false,
- }],
- permission: this.getPermission(trelloBoard.prefs.permissionLevel),
- slug: getSlug(trelloBoard.name) || 'board',
- stars: 0,
- title: trelloBoard.name,
- };
- // now add other members
- if(trelloBoard.memberships) {
- trelloBoard.memberships.forEach((trelloMembership) => {
- const trelloId = trelloMembership.idMember;
- // do we have a mapping?
- if(this.members[trelloId]) {
- const wekanId = this.members[trelloId];
- // do we already have it in our list?
- const wekanMember = boardToCreate.members.find((wekanMember) => wekanMember.userId === wekanId);
- if(wekanMember) {
- // we're already mapped, but maybe with lower rights
- if(!wekanMember.isAdmin) {
- wekanMember.isAdmin = this.getAdmin(trelloMembership.memberType);
- }
- } else {
- boardToCreate.members.push({
- userId: wekanId,
- isAdmin: this.getAdmin(trelloMembership.memberType),
- isActive: true,
- isCommentOnly: false,
- });
- }
- }
- });
- }
- trelloBoard.labels.forEach((label) => {
- const labelToCreate = {
- _id: Random.id(6),
- color: label.color,
- name: label.name,
- };
- // We need to remember them by Trello 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: trelloBoard.id,
- system: 'Trello',
- url: trelloBoard.url,
- },
- // 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 Trello cards,
- * as well as all linked data: activities, comments, and attachments
- * @param trelloCards
- * @param boardId
- * @returns {Array}
- */
- createCards(trelloCards, boardId) {
- const result = [];
- trelloCards.forEach((card) => {
- const cardToCreate = {
- archived: card.closed,
- 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.desc,
- listId: this.lists[card.idList],
- sort: card.pos,
- title: card.name,
- // we attribute the card to its creator if available
- userId: this._user(this.createdBy.cards[card.id]),
- dueAt: card.due ? this._now(card.due) : null,
- };
- // add labels
- if (card.idLabels) {
- cardToCreate.labelIds = card.idLabels.map((trelloId) => {
- return this.labels[trelloId];
- });
- }
- // add members {
- if(card.idMembers) {
- const wekanMembers = [];
- // we can't just map, as some members may not have been mapped
- card.idMembers.forEach((trelloId) => {
- if(this.members[trelloId]) {
- const wekanId = this.members[trelloId];
- // we may map multiple Trello 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;
- }
- }
- // insert card
- const cardId = Cards.direct.insert(cardToCreate);
- // keep track of Trello 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: 'Trello',
- url: card.url,
- },
- // 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.date),
- text: comment.data.text,
- // we attribute the comment to the original author, default to current user
- userId: this._user(comment.idMemberCreator),
- };
- // dateLastActivity will be set from activity insert, no need to
- // update it ourselves
- const commentId = CardComments.direct.insert(commentToCreate);
- 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 trelloCoverId = card.idAttachmentCover;
- 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.
- if(Meteor.isServer) {
- file.attachData(att.url, function (error) {
- file.boardId = boardId;
- file.cardId = cardId;
- if (error) {
- throw(error);
- } else {
- const wekanAtt = Attachments.insert(file, () => {
- // we do nothing
- });
- //
- if(trelloCoverId === 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(trelloLabels, board) {
- trelloLabels.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(trelloLists, boardId) {
- trelloLists.forEach((list) => {
- const listToCreate = {
- archived: list.closed,
- 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
- // Trello boards (eg from 2013) that didn't log the 'createList' action
- // we require.
- createdAt: this._now(this.createdAt.lists[list.id]),
- title: list.name,
- };
- 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: 'Trello',
- },
- // We attribute the import to current user,
- // not the creator of the original object
- userId: this._user(),
- });
- });
- }
-
- createChecklists(trelloChecklists) {
- trelloChecklists.forEach((checklist) => {
- // Create the checklist
- const checklistToCreate = {
- cardId: this.cards[checklist.idCard],
- title: checklist.name,
- createdAt: this._now(),
- };
- const checklistId = Checklists.direct.insert(checklistToCreate);
- // Now add the items to the checklist
- const itemsToCreate = [];
- checklist.checkItems.forEach((item) => {
- itemsToCreate.push({
- _id: checklistId + itemsToCreate.length,
- title: item.name,
- isFinished: item.state === 'complete',
- });
- });
- Checklists.direct.update(checklistId, {$set: {items: itemsToCreate}});
- });
- }
-
- getAdmin(trelloMemberType) {
- return trelloMemberType === 'admin';
- }
-
- getColor(trelloColorCode) {
- // trello color name => wekan color
- const mapColors = {
- 'blue': 'belize',
- 'orange': 'pumpkin',
- 'green': 'nephritis',
- 'red': 'pomegranate',
- 'purple': 'wisteria',
- 'pink': 'pomegranate',
- 'lime': 'nephritis',
- 'sky': 'belize',
- 'grey': 'midnight',
- };
- const wekanColor = mapColors[trelloColorCode];
- return wekanColor || Boards.simpleSchema()._schema.color.allowedValues[0];
- }
-
- getPermission(trelloPermissionCode) {
- if (trelloPermissionCode === 'public') {
- return 'public';
- }
- // Wekan does NOT have organization level, so we default both 'private' and
- // 'org' to private.
- return 'private';
- }
-
- parseActions(trelloActions) {
- trelloActions.forEach((action) => {
- if (action.type === 'addAttachmentToCard') {
- // We have to be cautious, because the attachment could have been removed later.
- // In that case Trello still reports its addition, but removes its 'url' field.
- // So we test for that
- const trelloAttachment = action.data.attachment;
- if(trelloAttachment.url) {
- // 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 trelloCardId = action.data.card.id;
- if(!this.attachments[trelloCardId]) {
- this.attachments[trelloCardId] = [];
- }
- this.attachments[trelloCardId].push(trelloAttachment);
- }
- } else if (action.type === 'commentCard') {
- const id = action.data.card.id;
- if (this.comments[id]) {
- this.comments[id].push(action);
- } else {
- this.comments[id] = [action];
- }
- } else if (action.type === 'createBoard') {
- this.createdAt.board = action.date;
- } else if (action.type === 'createCard') {
- const cardId = action.data.card.id;
- this.createdAt.cards[cardId] = action.date;
- this.createdBy.cards[cardId] = action.idMemberCreator;
- } else if (action.type === 'createList') {
- const listId = action.data.list.id;
- this.createdAt.lists[listId] = action.date;
- }
- });
- }
-}
+import { TrelloCreator } from './trelloCreator';
+import { WekanCreator } from './wekanCreator';
Meteor.methods({
- importTrelloBoard(trelloBoard, data) {
- const trelloCreator = new TrelloCreator(data);
+ importBoard(board, data, importSource) {
+ check(board, Object);
+ check(data, Object);
+ check(importSource, String);
+ let creator;
+ switch (importSource) {
+ case 'trello':
+ creator = new TrelloCreator(data);
+ break;
+ case 'wekan':
+ creator = new WekanCreator(data);
+ break;
+ }
// 1. check all parameters are ok from a syntax point of view
- try {
- check(data, {
- membersMapping: Match.Optional(Object),
- });
- trelloCreator.checkActions(trelloBoard.actions);
- trelloCreator.checkBoard(trelloBoard);
- trelloCreator.checkLabels(trelloBoard.labels);
- trelloCreator.checkLists(trelloBoard.lists);
- trelloCreator.checkCards(trelloBoard.cards);
- trelloCreator.checkChecklists(trelloBoard.checklists);
- } catch (e) {
- throw new Meteor.Error('error-json-schema');
- }
+ creator.check(board);
// 2. check parameters are ok from a business point of view (exist &
// authorized) nothing to check, everyone can import boards in their account
// 3. create all elements
- trelloCreator.parseActions(trelloBoard.actions);
- const boardId = trelloCreator.createBoardAndLabels(trelloBoard);
- trelloCreator.createLists(trelloBoard.lists, boardId);
- trelloCreator.createCards(trelloBoard.cards, boardId);
- trelloCreator.createChecklists(trelloBoard.checklists);
- // XXX add members
- return boardId;
+ return creator.create(board);
},
});
diff --git a/models/integrations.js b/models/integrations.js
new file mode 100644
index 00000000..b9bf248f
--- /dev/null
+++ b/models/integrations.js
@@ -0,0 +1,54 @@
+Integrations = new Mongo.Collection('integrations');
+
+Integrations.attachSchema(new SimpleSchema({
+ enabled: {
+ type: Boolean,
+ defaultValue: true,
+ },
+ title: {
+ type: String,
+ optional: true,
+ },
+ type: {
+ type: String,
+ },
+ url: { // URL validation regex (https://mathiasbynens.be/demo/url-regex)
+ type: String,
+ },
+ token: {
+ type: String,
+ optional: true,
+ },
+ boardId: {
+ type: String,
+ },
+ createdAt: {
+ type: Date,
+ denyUpdate: false,
+ autoValue() { // eslint-disable-line consistent-return
+ if (this.isInsert) {
+ return new Date();
+ } else {
+ this.unset();
+ }
+ },
+ },
+ userId: {
+ type: String,
+ autoValue() { // eslint-disable-line consistent-return
+ if (this.isInsert || this.isUpdate) {
+ return this.userId;
+ }
+ },
+ },
+}));
+
+Integrations.allow({
+ insert(userId, doc) {
+ return allowIsBoardAdmin(userId, Boards.findOne(doc.boardId));
+ },
+ update(userId, doc) {
+ return allowIsBoardAdmin(userId, Boards.findOne(doc.boardId));
+ },
+ fetch: ['boardId'],
+});
diff --git a/models/trelloCreator.js b/models/trelloCreator.js
new file mode 100644
index 00000000..fbc4a878
--- /dev/null
+++ b/models/trelloCreator.js
@@ -0,0 +1,500 @@
+const DateString = Match.Where(function (dateAsString) {
+ check(dateAsString, String);
+ return moment(dateAsString, moment.ISO_8601).isValid();
+});
+
+export class TrelloCreator {
+ 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 Trello id
+ // (so we only parse actions once!)
+ this.createdAt = {
+ board: null,
+ cards: {},
+ lists: {},
+ };
+ // The object creator Trello Id, indexed by the object Trello id
+ // (so we only parse actions once!)
+ this.createdBy = {
+ cards: {}, // only cards have a field for that
+ };
+
+ // Map of labels Trello ID => Wekan ID
+ this.labels = {};
+ // Map of lists Trello ID => Wekan ID
+ this.lists = {};
+ // Map of cards Trello ID => Wekan ID
+ this.cards = {};
+ // The comments, indexed by Trello card id (to map when importing cards)
+ this.comments = {};
+ // the members, indexed by Trello member id => Wekan user ID
+ this.members = data.membersMapping ? data.membersMapping : {};
+
+ // maps a trelloCardId to an array of trelloAttachments
+ 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 trelloUserId is provided and we have a mapping,
+ * return it.
+ * Otherwise return current logged user.
+ * @param trelloUserId
+ * @private
+ */
+ _user(trelloUserId) {
+ if(trelloUserId && this.members[trelloUserId]) {
+ return this.members[trelloUserId];
+ }
+ return Meteor.userId();
+ }
+
+ checkActions(trelloActions) {
+ check(trelloActions, [Match.ObjectIncluding({
+ data: Object,
+ date: DateString,
+ type: String,
+ })]);
+ // XXX we could perform more thorough checks based on action type
+ }
+
+ checkBoard(trelloBoard) {
+ check(trelloBoard, Match.ObjectIncluding({
+ closed: Boolean,
+ name: String,
+ prefs: Match.ObjectIncluding({
+ // XXX refine control by validating 'background' against a list of
+ // allowed values (is it worth the maintenance?)
+ background: String,
+ permissionLevel: Match.Where((value) => {
+ return ['org', 'private', 'public'].indexOf(value)>= 0;
+ }),
+ }),
+ }));
+ }
+
+ checkCards(trelloCards) {
+ check(trelloCards, [Match.ObjectIncluding({
+ closed: Boolean,
+ dateLastActivity: DateString,
+ desc: String,
+ idLabels: [String],
+ idMembers: [String],
+ name: String,
+ pos: Number,
+ })]);
+ }
+
+ checkLabels(trelloLabels) {
+ check(trelloLabels, [Match.ObjectIncluding({
+ // XXX refine control by validating 'color' against a list of allowed
+ // values (is it worth the maintenance?)
+ color: String,
+ name: String,
+ })]);
+ }
+
+ checkLists(trelloLists) {
+ check(trelloLists, [Match.ObjectIncluding({
+ closed: Boolean,
+ name: String,
+ })]);
+ }
+
+ checkChecklists(trelloChecklists) {
+ check(trelloChecklists, [Match.ObjectIncluding({
+ idBoard: String,
+ idCard: String,
+ name: String,
+ checkItems: [Match.ObjectIncluding({
+ state: String,
+ name: String,
+ })],
+ })]);
+ }
+
+ // You must call parseActions before calling this one.
+ createBoardAndLabels(trelloBoard) {
+ const boardToCreate = {
+ archived: trelloBoard.closed,
+ color: this.getColor(trelloBoard.prefs.background),
+ // very old boards won't have a creation activity so no creation date
+ createdAt: this._now(this.createdAt.board),
+ labels: [],
+ members: [{
+ userId: Meteor.userId(),
+ isAdmin: true,
+ isActive: true,
+ isCommentOnly: false,
+ }],
+ permission: this.getPermission(trelloBoard.prefs.permissionLevel),
+ slug: getSlug(trelloBoard.name) || 'board',
+ stars: 0,
+ title: trelloBoard.name,
+ };
+ // now add other members
+ if(trelloBoard.memberships) {
+ trelloBoard.memberships.forEach((trelloMembership) => {
+ const trelloId = trelloMembership.idMember;
+ // do we have a mapping?
+ if(this.members[trelloId]) {
+ const wekanId = this.members[trelloId];
+ // do we already have it in our list?
+ const wekanMember = boardToCreate.members.find((wekanMember) => wekanMember.userId === wekanId);
+ if(wekanMember) {
+ // we're already mapped, but maybe with lower rights
+ if(!wekanMember.isAdmin) {
+ wekanMember.isAdmin = this.getAdmin(trelloMembership.memberType);
+ }
+ } else {
+ boardToCreate.members.push({
+ userId: wekanId,
+ isAdmin: this.getAdmin(trelloMembership.memberType),
+ isActive: true,
+ isCommentOnly: false,
+ });
+ }
+ }
+ });
+ }
+ trelloBoard.labels.forEach((label) => {
+ const labelToCreate = {
+ _id: Random.id(6),
+ color: label.color,
+ name: label.name,
+ };
+ // We need to remember them by Trello 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: trelloBoard.id,
+ system: 'Trello',
+ url: trelloBoard.url,
+ },
+ // 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 Trello cards,
+ * as well as all linked data: activities, comments, and attachments
+ * @param trelloCards
+ * @param boardId
+ * @returns {Array}
+ */
+ createCards(trelloCards, boardId) {
+ const result = [];
+ trelloCards.forEach((card) => {
+ const cardToCreate = {
+ archived: card.closed,
+ 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.desc,
+ listId: this.lists[card.idList],
+ sort: card.pos,
+ title: card.name,
+ // we attribute the card to its creator if available
+ userId: this._user(this.createdBy.cards[card.id]),
+ dueAt: card.due ? this._now(card.due) : null,
+ };
+ // add labels
+ if (card.idLabels) {
+ cardToCreate.labelIds = card.idLabels.map((trelloId) => {
+ return this.labels[trelloId];
+ });
+ }
+ // add members {
+ if(card.idMembers) {
+ const wekanMembers = [];
+ // we can't just map, as some members may not have been mapped
+ card.idMembers.forEach((trelloId) => {
+ if(this.members[trelloId]) {
+ const wekanId = this.members[trelloId];
+ // we may map multiple Trello 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;
+ }
+ }
+ // insert card
+ const cardId = Cards.direct.insert(cardToCreate);
+ // keep track of Trello 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: 'Trello',
+ url: card.url,
+ },
+ // 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.date),
+ text: comment.data.text,
+ // we attribute the comment to the original author, default to current user
+ userId: this._user(comment.idMemberCreator),
+ };
+ // dateLastActivity will be set from activity insert, no need to
+ // update it ourselves
+ const commentId = CardComments.direct.insert(commentToCreate);
+ 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 trelloCoverId = card.idAttachmentCover;
+ 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.
+ if(Meteor.isServer) {
+ file.attachData(att.url, function (error) {
+ file.boardId = boardId;
+ file.cardId = cardId;
+ if (error) {
+ throw(error);
+ } else {
+ const wekanAtt = Attachments.insert(file, () => {
+ // we do nothing
+ });
+ //
+ if(trelloCoverId === 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(trelloLabels, board) {
+ trelloLabels.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(trelloLists, boardId) {
+ trelloLists.forEach((list) => {
+ const listToCreate = {
+ archived: list.closed,
+ 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
+ // Trello boards (eg from 2013) that didn't log the 'createList' action
+ // we require.
+ createdAt: this._now(this.createdAt.lists[list.id]),
+ title: list.name,
+ };
+ 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: 'Trello',
+ },
+ // We attribute the import to current user,
+ // not the creator of the original object
+ userId: this._user(),
+ });
+ });
+ }
+
+ createChecklists(trelloChecklists) {
+ trelloChecklists.forEach((checklist) => {
+ // Create the checklist
+ const checklistToCreate = {
+ cardId: this.cards[checklist.idCard],
+ title: checklist.name,
+ createdAt: this._now(),
+ };
+ const checklistId = Checklists.direct.insert(checklistToCreate);
+ // Now add the items to the checklist
+ const itemsToCreate = [];
+ checklist.checkItems.forEach((item) => {
+ itemsToCreate.push({
+ _id: checklistId + itemsToCreate.length,
+ title: item.name,
+ isFinished: item.state === 'complete',
+ });
+ });
+ Checklists.direct.update(checklistId, {$set: {items: itemsToCreate}});
+ });
+ }
+
+ getAdmin(trelloMemberType) {
+ return trelloMemberType === 'admin';
+ }
+
+ getColor(trelloColorCode) {
+ // trello color name => wekan color
+ const mapColors = {
+ 'blue': 'belize',
+ 'orange': 'pumpkin',
+ 'green': 'nephritis',
+ 'red': 'pomegranate',
+ 'purple': 'wisteria',
+ 'pink': 'pomegranate',
+ 'lime': 'nephritis',
+ 'sky': 'belize',
+ 'grey': 'midnight',
+ };
+ const wekanColor = mapColors[trelloColorCode];
+ return wekanColor || Boards.simpleSchema()._schema.color.allowedValues[0];
+ }
+
+ getPermission(trelloPermissionCode) {
+ if (trelloPermissionCode === 'public') {
+ return 'public';
+ }
+ // Wekan does NOT have organization level, so we default both 'private' and
+ // 'org' to private.
+ return 'private';
+ }
+
+ parseActions(trelloActions) {
+ trelloActions.forEach((action) => {
+ if (action.type === 'addAttachmentToCard') {
+ // We have to be cautious, because the attachment could have been removed later.
+ // In that case Trello still reports its addition, but removes its 'url' field.
+ // So we test for that
+ const trelloAttachment = action.data.attachment;
+ if(trelloAttachment.url) {
+ // 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 trelloCardId = action.data.card.id;
+ if(!this.attachments[trelloCardId]) {
+ this.attachments[trelloCardId] = [];
+ }
+ this.attachments[trelloCardId].push(trelloAttachment);
+ }
+ } else if (action.type === 'commentCard') {
+ const id = action.data.card.id;
+ if (this.comments[id]) {
+ this.comments[id].push(action);
+ } else {
+ this.comments[id] = [action];
+ }
+ } else if (action.type === 'createBoard') {
+ this.createdAt.board = action.date;
+ } else if (action.type === 'createCard') {
+ const cardId = action.data.card.id;
+ this.createdAt.cards[cardId] = action.date;
+ this.createdBy.cards[cardId] = action.idMemberCreator;
+ } else if (action.type === 'createList') {
+ const listId = action.data.list.id;
+ this.createdAt.lists[listId] = action.date;
+ }
+ });
+ }
+
+ check(board) {
+ try {
+ // check(data, {
+ // membersMapping: Match.Optional(Object),
+ // });
+ this.checkActions(board.actions);
+ this.checkBoard(board);
+ this.checkLabels(board.labels);
+ this.checkLists(board.lists);
+ this.checkCards(board.cards);
+ this.checkChecklists(board.checklists);
+ } catch (e) {
+ throw new Meteor.Error('error-json-schema');
+ }
+ }
+
+ create(board) {
+ this.parseActions(board.actions);
+ const boardId = this.createBoardAndLabels(board);
+ this.createLists(board.lists, boardId);
+ this.createCards(board.cards, boardId);
+ this.createChecklists(board.checklists);
+ // XXX add members
+ return boardId;
+ }
+}
diff --git a/models/wekanCreator.js b/models/wekanCreator.js
new file mode 100644
index 00000000..723c5c67
--- /dev/null
+++ b/models/wekanCreator.js
@@ -0,0 +1,493 @@
+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: {},
+ };
+ // 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 lists Wekan ID => Wekan ID
+ this.lists = {};
+ // Map of cards Wekan ID => Wekan ID
+ this.cards = {};
+ // The comments, indexed by Wekan card id (to map when importing cards)
+ this.comments = {};
+ // the members, indexed by Wekan member id => Wekan user ID
+ this.members = data.membersMapping ? data.membersMapping : {};
+
+ // 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({
+ userId: String,
+ 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,
+ })]);
+ }
+
+ // checkChecklists(wekanChecklists) {
+ // check(wekanChecklists, [Match.ObjectIncluding({
+ // idBoard: String,
+ // idCard: String,
+ // name: String,
+ // checkItems: [Match.ObjectIncluding({
+ // state: String,
+ // name: String,
+ // })],
+ // })]);
+ // }
+
+ // You must call parseActions before calling this one.
+ createBoardAndLabels(wekanBoard) {
+ const boardToCreate = {
+ archived: wekanBoard.archived,
+ color: wekanBoard.color,
+ // very old boards won't have a creation activity so no creation date
+ createdAt: this._now(wekanBoard.createdAt),
+ labels: [],
+ members: [{
+ userId: Meteor.userId(),
+ isAdmin: true,
+ isActive: true,
+ isCommentOnly: false,
+ }],
+ permission: wekanBoard.permission,
+ slug: getSlug(wekanBoard.title) || 'board',
+ stars: 0,
+ title: wekanBoard.title,
+ };
+ // now add other members
+ if(wekanBoard.members) {
+ wekanBoard.members.forEach((wekanMember) => {
+ const wekanId = wekanMember.userId;
+ // do we have a mapping?
+ if(this.members[wekanId]) {
+ const wekanId = this.members[wekanId];
+ // do we already have it in our list?
+ const wekanMember = boardToCreate.members.find((wekanMember) => wekanMember.userId === wekanId);
+ if(!wekanMember) {
+ boardToCreate.members.push({
+ userId: wekanId,
+ isAdmin: wekanMember.isAdmin,
+ isActive: true,
+ isCommentOnly: false,
+ });
+ }
+ }
+ });
+ }
+ wekanBoard.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: wekanBoard.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],
+ sort: card.sort,
+ title: card.title,
+ // we attribute the card to its creator if available
+ userId: this._user(this.createdBy.cards[card._id]),
+ dueAt: card.dueAt ? this._now(card.dueAt) : 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;
+ }
+ }
+ // 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.date),
+ 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);
+ 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.
+ if(Meteor.isServer) {
+ if (att.url) {
+ file.attachData(att.url, function (error) {
+ file.boardId = boardId;
+ file.cardId = cardId;
+ if (error) {
+ throw(error);
+ } else {
+ const wekanAtt = Attachments.insert(file, () => {
+ // we do nothing
+ });
+ //
+ 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;
+ if (error) {
+ throw(error);
+ } else {
+ const wekanAtt = Attachments.insert(file, () => {
+ // we do nothing
+ });
+ //
+ 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) => {
+ 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,
+ };
+ 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(),
+ });
+ });
+ }
+
+ // createChecklists(wekanChecklists) {
+ // wekanChecklists.forEach((checklist) => {
+ // // Create the checklist
+ // const checklistToCreate = {
+ // cardId: this.cards[checklist.cardId],
+ // title: checklist.title,
+ // createdAt: this._now(),
+ // };
+ // const checklistId = Checklists.direct.insert(checklistToCreate);
+ // // Now add the items to the checklist
+ // const itemsToCreate = [];
+ // checklist.checkItems.forEach((item) => {
+ // itemsToCreate.push({
+ // _id: checklistId + itemsToCreate.length,
+ // title: item.title,
+ // isFinished: item.isFinished,
+ // });
+ // });
+ // Checklists.direct.update(checklistId, {$set: {items: itemsToCreate}});
+ // });
+ // }
+
+ 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(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;
+ }}
+ });
+ }
+
+ check(board) {
+ try {
+ // check(data, {
+ // membersMapping: Match.Optional(Object),
+ // });
+ this.checkActivities(board.activities);
+ this.checkBoard(board);
+ this.checkLabels(board.labels);
+ this.checkLists(board.lists);
+ this.checkCards(board.cards);
+ // Checklists are not exported yet
+ // this.checkChecklists(board.checklists);
+ } catch (e) {
+ throw new Meteor.Error('error-json-schema');
+ }
+ }
+
+ create(board) {
+ this.parseActivities(board);
+ const boardId = this.createBoardAndLabels(board);
+ this.createLists(board.lists, boardId);
+ this.createCards(board.cards, boardId);
+ // Checklists are not exported yet
+ // this.createChecklists(board.checklists);
+ // XXX add members
+ return boardId;
+ }
+}
diff --git a/package.json b/package.json
index 1f30d916..b1a672d8 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "wekan",
- "version": "0.27.0",
+ "version": "0.28.0",
"description": "The open-source Trello-like kanban",
"private": true,
"scripts": {
diff --git a/server/notifications/outgoing.js b/server/notifications/outgoing.js
new file mode 100644
index 00000000..a5bbc737
--- /dev/null
+++ b/server/notifications/outgoing.js
@@ -0,0 +1,47 @@
+const postCatchError = Meteor.wrapAsync((url, options, resolve) => {
+ HTTP.post(url, options, (err, res) => {
+ if (err) {
+ resolve(null, err.response);
+ } else {
+ resolve(null, res);
+ }
+ });
+});
+
+Meteor.methods({
+ outgoingWebhooks(integration, description, params) {
+ check(integration, Object);
+ check(description, String);
+ check(params, Object);
+
+ const quoteParams = _.clone(params);
+ ['card', 'list', 'oldList', 'board', 'comment'].forEach((key) => {
+ if (quoteParams[key]) quoteParams[key] = `"${params[key]}"`;
+ });
+
+ const user = Users.findOne(integration.userId);
+ const text = `${params.user} ${TAPi18n.__(description, quoteParams, user.getLanguage())}\n${params.url}`;
+
+ if (text.length === 0) return;
+
+ const value = {
+ text: `${text}`,
+ };
+
+ const options = {
+ headers: {
+ // 'Content-Type': 'application/json',
+ // 'X-Wekan-Activities-Token': 'Random.Id()',
+ },
+ data: value,
+ };
+
+ const response = postCatchError(integration.url, options);
+
+ if (response && response.statusCode && response.statusCode === 200) {
+ return true; // eslint-disable-line consistent-return
+ } else {
+ throw new Meteor.Error('error-invalid-webhook-response');
+ }
+ },
+});
diff --git a/server/publications/boards.js b/server/publications/boards.js
index 133082dd..f482f619 100644
--- a/server/publications/boards.js
+++ b/server/publications/boards.js
@@ -73,6 +73,7 @@ Meteor.publishRelations('board', function(boardId) {
],
}, { limit: 1 }), function(boardId, board) {
this.cursor(Lists.find({ boardId }));
+ this.cursor(Integrations.find({ boardId }));
// Cards and cards comments
// XXX Originally we were publishing the card documents as a child of the
diff --git a/snapcraft.yaml b/snapcraft.yaml
index e00e8fa1..6ff0a1fe 100644
--- a/snapcraft.yaml
+++ b/snapcraft.yaml
@@ -1,5 +1,5 @@
name: wekan
-version: "0.27-SNAPSHOT"
+version: "0.28-SNAPSHOT"
summary: The open-source Trello-like kanban
description: |
Wekan is an open-source and collaborative kanban board application.
@@ -69,7 +69,7 @@ parts:
wekan:
source: .
plugin: nodejs
- node-engine: 4.8.1
+ node-engine: 4.8.4
node-packages:
- npm@4.6.1
- node-gyp
@@ -91,9 +91,10 @@ parts:
chmod +x install_meteor.sh
sh install_meteor.sh
rm install_meteor.sh
- mkdir -p packages
- git clone https://github.com/wekan/flow-router.git packages/kadira-flow-router
- git clone https://github.com/meteor-useraccounts/core.git packages/meteor-useraccounts-core
+ cd ~/.meteor/packages
+ git clone --depth 1 -b master https://github.com/wekan/flow-router.git kadira-flow-router
+ git clone --depth 1 -b master https://github.com/meteor-useraccounts/core.git meteor-useraccounts-core
+ sed -i 's/api\.versionsFrom/\/\/api.versionsFrom/' ~/.meteor/packages/meteor-useraccounts-core/package.js
build: |
rm -rf package-lock.json .build
meteor add standard-minifier-js --allow-superuser