Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
49882a05d1 | ||
|
|
8e36a2bc83 | ||
|
|
24e47195e5 | ||
|
|
4b37958140 | ||
|
|
3edff32bde | ||
|
|
78c5e0f7ef | ||
|
|
49229e1723 | ||
|
|
14e2bfd1bf | ||
|
|
3c49e2d0ed | ||
|
|
d5bc66c9a4 | ||
|
|
4f6977668b | ||
|
|
17826c28e5 | ||
|
|
a3d71dd694 | ||
|
|
a5c39342ba | ||
|
|
ad75c541cf | ||
|
|
cd46cbeb8f | ||
|
|
888e1ad5d3 | ||
|
|
abb7108321 | ||
|
|
da21a2a410 | ||
|
|
904b5bf0f5 | ||
|
|
d21aa97219 | ||
|
|
6ee75fbdba | ||
|
|
c3b2a0c955 | ||
|
|
0ac42c145e | ||
|
|
4a77bc930f | ||
|
|
8d51245161 | ||
|
|
a707129146 | ||
|
|
2f3138207c | ||
|
|
dc7286a0ef | ||
|
|
13c2157e36 | ||
|
|
7033315cd3 | ||
|
|
775476f97c | ||
|
|
eb62c9ce6a | ||
|
|
60be4df76e | ||
|
|
f888cfd565 | ||
|
|
044126188d | ||
|
|
0fec711545 | ||
|
|
7a6afb8aea | ||
|
|
1e72177991 | ||
|
|
cdf070189e | ||
|
|
64bf455b29 | ||
|
|
0a53ee87b9 |
No files matched your search
@@ -91,3 +91,4 @@ wekan:accounts-cas
|
||||
wekan-scrollbar
|
||||
mquandalle:perfect-scrollbar
|
||||
mdg:meteor-apm-agent
|
||||
meteorhacks:unblock
|
||||
@@ -94,6 +94,7 @@ meteorhacks:collection-utils@1.2.0
|
||||
meteorhacks:meteorx@1.4.1
|
||||
meteorhacks:picker@1.0.3
|
||||
meteorhacks:subs-manager@1.6.4
|
||||
meteorhacks:unblock@1.1.0
|
||||
meteorspark:util@0.2.0
|
||||
minifier-css@1.2.16
|
||||
minifier-js@2.2.2
|
||||
|
||||
+36
-1
@@ -1,3 +1,38 @@
|
||||
# v2.32 2019-02-28 Wekan release
|
||||
|
||||
This release adds the following [performance improvements](https://github.com/wekan/wekan/pull/2214), thanks to justinr1234:
|
||||
|
||||
- New indexes for queries that were missing an index;
|
||||
- Bulk querying documents to reduce the number of mongo queries when loading a board;
|
||||
- Ensure oplog is being used to query the database by providing a `sort` key when `limit` is used querying the `boards` collection.
|
||||
|
||||
and fixes the following bugs related to [Template features](https://github.com/wekan/wekan/issues/2209), thanks to andresmanelli:
|
||||
|
||||
- [Fix filtering in swimlane view](https://github.com/wekan/wekan/commit/49229e1723de14cdc66dc6480624bba426d35e36) that was [broken since v2.29](https://github.com/wekan/wekan/issues/2213).
|
||||
|
||||
Thanks to above GitHub users for their contributions.
|
||||
|
||||
# v2.31 2019-02-28 Wekan release
|
||||
|
||||
This release fixes the following bugs related to [Template features](https://github.com/wekan/wekan/issues/2209), thanks to GitHub user andresmanelli:
|
||||
|
||||
- [Fix copy card](https://github.com/wekan/wekan/issues/2210).
|
||||
|
||||
# v2.30 2019-02-28 Wekan release
|
||||
|
||||
This release adds the following new [Template features](https://github.com/wekan/wekan/issues/2209), thanks to GitHub user andresmanelli:
|
||||
|
||||
- [Fix popup title. Add element title modification](https://github.com/wekan/wekan/commit/888e1ad5d3e32be53283aa32198057f669f3d706);
|
||||
- [Copy template attachments](https://github.com/wekan/wekan/commit/abb71083215462d91b084c4de13af0b130638e4d);
|
||||
- [Standarize copy functions. Match labels by name](https://github.com/wekan/wekan/commit/da21a2a410c9b905de89d66236748e0c8f5357ea).
|
||||
|
||||
# v2.29 2019-02-27 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- Swimlane/List/Board/Card templates. In Progress, please test and [add comment if you find not listed bugs](https://github.com/wekan/wekan/issues/2165).
|
||||
Thanks to GitHub user andresmanelli.
|
||||
|
||||
# v2.28 2019-02-27 Wekan release
|
||||
|
||||
This release adds the following new Sandstorm features and fixes:
|
||||
@@ -5,7 +40,7 @@ This release adds the following new Sandstorm features and fixes:
|
||||
- All Boards page [so it's possible to go back from subtask board](https://github.com/wekan/wekan/issues/2082).
|
||||
- Board favorites.
|
||||
- New Sandstorm board first user is Admin and [has IFTTT Rules](https://github.com/wekan/wekan/issues/2125) and Standalone Wekan Admin Panel.
|
||||
Probably some Admin Panel features do not work yet. Please keep backup of your grains before testig Admin Panel.
|
||||
Probably some Admin Panel features do not work yet. Please keep backup of your grains before testing Admin Panel.
|
||||
- Linked Cards and Linked Boards.
|
||||
- Some not needed options like Logout etc have been hidden from top bar right menu.
|
||||
- [Import board now works. "Board not found" is not problem anymore](https://github.com/wekan/wekan/issues/1430), because you can go to All Boards page to change to imported board.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
appId: wekan-public/apps/77b94f60-dec9-0136-304e-16ff53095928
|
||||
appVersion: "v2.28.0"
|
||||
appVersion: "v2.32.0"
|
||||
files:
|
||||
userUploads:
|
||||
- README.md
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
Template.boardListHeaderBar.events({
|
||||
'click .js-open-archived-board'() {
|
||||
Modal.open('archivedBoards');
|
||||
},
|
||||
});
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.subscribe('archivedBoards');
|
||||
|
||||
@@ -20,12 +20,15 @@ template(name="boardBody")
|
||||
class="{{#if draggingActive.get}}is-dragging-active{{/if}}")
|
||||
if showOverlay.get
|
||||
.board-overlay
|
||||
if isViewSwimlanes
|
||||
if currentBoard.isTemplatesBoard
|
||||
each currentBoard.swimlanes
|
||||
+swimlane(this)
|
||||
if isViewLists
|
||||
+listsGroup
|
||||
if isViewCalendar
|
||||
else if isViewSwimlanes
|
||||
each currentBoard.swimlanes
|
||||
+swimlane(this)
|
||||
else if isViewLists
|
||||
+listsGroup(currentBoard)
|
||||
else if isViewCalendar
|
||||
+calendarView
|
||||
|
||||
template(name="calendarView")
|
||||
|
||||
@@ -94,10 +94,11 @@ template(name="boardHeaderBar")
|
||||
i.fa.fa-search
|
||||
span {{_ 'search'}}
|
||||
|
||||
a.board-header-btn.js-toggle-board-view(
|
||||
title="{{_ 'board-view'}}")
|
||||
i.fa.fa-th-large
|
||||
span {{_ currentUser.profile.boardView}}
|
||||
unless currentBoard.isTemplatesBoard
|
||||
a.board-header-btn.js-toggle-board-view(
|
||||
title="{{_ 'board-view'}}")
|
||||
i.fa.fa-th-large
|
||||
span {{_ currentUser.profile.boardView}}
|
||||
|
||||
if canModifyBoard
|
||||
a.board-header-btn.js-multiselection-activate(
|
||||
@@ -130,7 +131,8 @@ template(name="boardMenuPopup")
|
||||
hr
|
||||
ul.pop-over-list
|
||||
li: a(href="{{exportUrl}}", download="{{exportFilename}}") {{_ 'export-board'}}
|
||||
li: a.js-archive-board {{_ 'archive-board'}}
|
||||
unless currentBoard.isTemplatesBoard
|
||||
li: a.js-archive-board {{_ 'archive-board'}}
|
||||
li: a.js-outgoing-webhooks {{_ 'outgoing-webhooks'}}
|
||||
hr
|
||||
ul.pop-over-list
|
||||
@@ -275,7 +277,10 @@ template(name="createBoard")
|
||||
input.primary.wide(type="submit" value="{{_ 'create'}}")
|
||||
span.quiet
|
||||
| {{_ 'or'}}
|
||||
a.js-import-board {{_ 'import-board'}}
|
||||
a.js-import-board {{_ 'import'}}
|
||||
span.quiet
|
||||
| /
|
||||
a.js-board-template {{_ 'template'}}
|
||||
|
||||
template(name="chooseBoardSource")
|
||||
ul.pop-over-list
|
||||
|
||||
@@ -304,6 +304,7 @@ const CreateBoard = BlazeComponent.extendComponent({
|
||||
'click .js-import': Popup.open('boardImportBoard'),
|
||||
submit: this.onSubmit,
|
||||
'click .js-import-board': Popup.open('chooseBoardSource'),
|
||||
'click .js-board-template': Popup.open('searchElement'),
|
||||
}];
|
||||
},
|
||||
}).register('createBoardPopup');
|
||||
|
||||
@@ -36,3 +36,6 @@ template(name="boardListHeaderBar")
|
||||
a.board-header-btn.js-open-archived-board
|
||||
i.fa.fa-archive
|
||||
span {{_ 'archives'}}
|
||||
a.board-header-btn(href="{{pathFor 'board' id=templatesBoardId slug=templatesBoardSlug}}")
|
||||
i.fa.fa-clone
|
||||
span {{_ 'templates'}}
|
||||
@@ -1,5 +1,20 @@
|
||||
const subManager = new SubsManager();
|
||||
|
||||
Template.boardListHeaderBar.events({
|
||||
'click .js-open-archived-board'() {
|
||||
Modal.open('archivedBoards');
|
||||
},
|
||||
});
|
||||
|
||||
Template.boardListHeaderBar.helpers({
|
||||
templatesBoardId() {
|
||||
return Meteor.user().getTemplatesBoardId();
|
||||
},
|
||||
templatesBoardSlug() {
|
||||
return Meteor.user().getTemplatesBoardSlug();
|
||||
},
|
||||
});
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
Meteor.subscribe('setting');
|
||||
@@ -9,6 +24,7 @@ BlazeComponent.extendComponent({
|
||||
return Boards.find({
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
type: 'board',
|
||||
}, {
|
||||
sort: ['title'],
|
||||
});
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
template(name="miniboard")
|
||||
.minicard(
|
||||
class="minicard-{{colorClass}}")
|
||||
.minicard-title
|
||||
.handle
|
||||
.fa.fa-arrows
|
||||
+viewer
|
||||
= title
|
||||
@@ -456,32 +456,15 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
}).register('boardsAndLists');
|
||||
|
||||
|
||||
function cloneCheckList(_id, checklist) {
|
||||
'use strict';
|
||||
const checklistId = checklist._id;
|
||||
checklist.cardId = _id;
|
||||
checklist._id = null;
|
||||
const newChecklistId = Checklists.insert(checklist);
|
||||
ChecklistItems.find({checklistId}).forEach(function(item) {
|
||||
item._id = null;
|
||||
item.checklistId = newChecklistId;
|
||||
item.cardId = _id;
|
||||
ChecklistItems.insert(item);
|
||||
});
|
||||
}
|
||||
|
||||
Template.copyCardPopup.events({
|
||||
'click .js-done'() {
|
||||
const card = Cards.findOne(Session.get('currentCard'));
|
||||
const oldId = card._id;
|
||||
card._id = null;
|
||||
const lSelect = $('.js-select-lists')[0];
|
||||
card.listId = lSelect.options[lSelect.selectedIndex].value;
|
||||
listId = lSelect.options[lSelect.selectedIndex].value;
|
||||
const slSelect = $('.js-select-swimlanes')[0];
|
||||
card.swimlaneId = slSelect.options[slSelect.selectedIndex].value;
|
||||
const swimlaneId = slSelect.options[slSelect.selectedIndex].value;
|
||||
const bSelect = $('.js-select-boards')[0];
|
||||
card.boardId = bSelect.options[bSelect.selectedIndex].value;
|
||||
const boardId = bSelect.options[bSelect.selectedIndex].value;
|
||||
const textarea = $('#copy-card-title');
|
||||
const title = textarea.val().trim();
|
||||
// insert new card to the bottom of new list
|
||||
@@ -490,38 +473,13 @@ Template.copyCardPopup.events({
|
||||
if (title) {
|
||||
card.title = title;
|
||||
card.coverId = '';
|
||||
const _id = Cards.insert(card);
|
||||
const _id = card.copy(boardId, swimlaneId, listId);
|
||||
// In case the filter is active we need to add the newly inserted card in
|
||||
// the list of exceptions -- cards that are not filtered. Otherwise the
|
||||
// card will disappear instantly.
|
||||
// See https://github.com/wekan/wekan/issues/80
|
||||
Filter.addException(_id);
|
||||
|
||||
// copy checklists
|
||||
let cursor = Checklists.find({cardId: oldId});
|
||||
cursor.forEach(function() {
|
||||
cloneCheckList(_id, arguments[0]);
|
||||
});
|
||||
|
||||
// copy subtasks
|
||||
cursor = Cards.find({parentId: oldId});
|
||||
cursor.forEach(function() {
|
||||
'use strict';
|
||||
const subtask = arguments[0];
|
||||
subtask.parentId = _id;
|
||||
subtask._id = null;
|
||||
/* const newSubtaskId = */ Cards.insert(subtask);
|
||||
});
|
||||
|
||||
// copy card comments
|
||||
cursor = CardComments.find({cardId: oldId});
|
||||
cursor.forEach(function () {
|
||||
'use strict';
|
||||
const comment = arguments[0];
|
||||
comment.cardId = _id;
|
||||
comment._id = null;
|
||||
CardComments.insert(comment);
|
||||
});
|
||||
Popup.close();
|
||||
}
|
||||
},
|
||||
@@ -558,9 +516,8 @@ Template.copyChecklistToManyCardsPopup.events({
|
||||
Filter.addException(_id);
|
||||
|
||||
// copy checklists
|
||||
let cursor = Checklists.find({cardId: oldId});
|
||||
cursor.forEach(function() {
|
||||
cloneCheckList(_id, arguments[0]);
|
||||
Checklists.find({cardId: oldId}).forEach((ch) => {
|
||||
ch.copy(_id);
|
||||
});
|
||||
|
||||
// copy subtasks
|
||||
@@ -574,13 +531,8 @@ Template.copyChecklistToManyCardsPopup.events({
|
||||
});
|
||||
|
||||
// copy card comments
|
||||
cursor = CardComments.find({cardId: oldId});
|
||||
cursor.forEach(function () {
|
||||
'use strict';
|
||||
const comment = arguments[0];
|
||||
comment.cardId = _id;
|
||||
comment._id = null;
|
||||
CardComments.insert(comment);
|
||||
CardComments.find({cardId: oldId}).forEach((cmt) => {
|
||||
cmt.copy(_id);
|
||||
});
|
||||
}
|
||||
Popup.close();
|
||||
|
||||
@@ -44,13 +44,19 @@ template(name="addCardForm")
|
||||
|
||||
.add-controls.clearfix
|
||||
button.primary.confirm(type="submit") {{_ 'add'}}
|
||||
span.quiet
|
||||
| {{_ 'or'}}
|
||||
a.js-link {{_ 'link'}}
|
||||
span.quiet
|
||||
|
|
||||
| /
|
||||
a.js-search {{_ 'search'}}
|
||||
unless currentBoard.isTemplatesBoard
|
||||
unless currentBoard.isTemplateBoard
|
||||
span.quiet
|
||||
| {{_ 'or'}}
|
||||
a.js-link {{_ 'link'}}
|
||||
span.quiet
|
||||
|
|
||||
| /
|
||||
a.js-search {{_ 'search'}}
|
||||
span.quiet
|
||||
|
|
||||
| /
|
||||
a.js-card-template {{_ 'template'}}
|
||||
|
||||
template(name="autocompleteLabelLine")
|
||||
.minicard-label(class="card-label-{{colorName}}" title=labelName)
|
||||
@@ -60,11 +66,9 @@ template(name="linkCardPopup")
|
||||
label {{_ 'boards'}}:
|
||||
.link-board-wrapper
|
||||
select.js-select-boards
|
||||
option(value="")
|
||||
each boards
|
||||
if $eq _id currentBoard._id
|
||||
option(value="{{_id}}" selected) {{_ 'current'}}
|
||||
else
|
||||
option(value="{{_id}}") {{title}}
|
||||
option(value="{{_id}}") {{title}}
|
||||
input.primary.confirm.js-link-board(type="button" value="{{_ 'link'}}")
|
||||
|
||||
label {{_ 'swimlanes'}}:
|
||||
@@ -85,19 +89,39 @@ template(name="linkCardPopup")
|
||||
.edit-controls.clearfix
|
||||
input.primary.confirm.js-done(type="button" value="{{_ 'link'}}")
|
||||
|
||||
template(name="searchCardPopup")
|
||||
label {{_ 'boards'}}:
|
||||
.link-board-wrapper
|
||||
select.js-select-boards
|
||||
each boards
|
||||
if $eq _id currentBoard._id
|
||||
option(value="{{_id}}" selected) {{_ 'current'}}
|
||||
else
|
||||
template(name="searchElementPopup")
|
||||
form
|
||||
label
|
||||
| {{_ 'title'}}
|
||||
input.js-element-title(type="text" placeholder="{{_ 'title'}}" autofocus required)
|
||||
unless isTemplateSearch
|
||||
label {{_ 'boards'}}:
|
||||
.link-board-wrapper
|
||||
select.js-select-boards
|
||||
option(value="")
|
||||
each boards
|
||||
option(value="{{_id}}") {{title}}
|
||||
form.js-search-term-form
|
||||
input(type="text" name="searchTerm" placeholder="{{_ 'search-example'}}" autofocus)
|
||||
.list-body.js-perfect-scrollbar.search-card-results
|
||||
.minicards.clearfix.js-minicards
|
||||
each results
|
||||
a.minicard-wrapper.js-minicard
|
||||
+minicard(this)
|
||||
if isBoardTemplateSearch
|
||||
each results
|
||||
a.minicard-wrapper.js-minicard
|
||||
+miniboard(this)
|
||||
if isListTemplateSearch
|
||||
each results
|
||||
a.minicard-wrapper.js-minicard
|
||||
+minilist(this)
|
||||
if isSwimlaneTemplateSearch
|
||||
each results
|
||||
a.minicard-wrapper.js-minicard
|
||||
+miniswimlane(this)
|
||||
if isCardTemplateSearch
|
||||
each results
|
||||
a.minicard-wrapper.js-minicard
|
||||
+minicard(this)
|
||||
unless isTemplateSearch
|
||||
each results
|
||||
a.minicard-wrapper.js-minicard
|
||||
+minicard(this)
|
||||
@@ -67,25 +67,47 @@ BlazeComponent.extendComponent({
|
||||
const labelIds = formComponent.labels.get();
|
||||
const customFields = formComponent.customFields.get();
|
||||
|
||||
const boardId = this.data().board();
|
||||
const board = this.data().board();
|
||||
let linkedId = '';
|
||||
let swimlaneId = '';
|
||||
const boardView = Meteor.user().profile.boardView;
|
||||
if (boardView === 'board-view-swimlanes')
|
||||
swimlaneId = this.parentComponent().parentComponent().data()._id;
|
||||
else if ((boardView === 'board-view-lists') || (boardView === 'board-view-cal'))
|
||||
swimlaneId = boardId.getDefaultSwimline()._id;
|
||||
|
||||
let cardType = 'cardType-card';
|
||||
if (title) {
|
||||
if (board.isTemplatesBoard()) {
|
||||
swimlaneId = this.parentComponent().parentComponent().data()._id; // Always swimlanes view
|
||||
const swimlane = Swimlanes.findOne(swimlaneId);
|
||||
// If this is the card templates swimlane, insert a card template
|
||||
if (swimlane.isCardTemplatesSwimlane())
|
||||
cardType = 'template-card';
|
||||
// If this is the board templates swimlane, insert a board template and a linked card
|
||||
else if (swimlane.isBoardTemplatesSwimlane()) {
|
||||
linkedId = Boards.insert({
|
||||
title,
|
||||
permission: 'private',
|
||||
type: 'template-board',
|
||||
});
|
||||
Swimlanes.insert({
|
||||
title: TAPi18n.__('default'),
|
||||
boardId: linkedId,
|
||||
});
|
||||
cardType = 'cardType-linkedBoard';
|
||||
}
|
||||
} else if (boardView === 'board-view-swimlanes')
|
||||
swimlaneId = this.parentComponent().parentComponent().data()._id;
|
||||
else if ((boardView === 'board-view-lists') || (boardView === 'board-view-cal'))
|
||||
swimlaneId = board.getDefaultSwimline()._id;
|
||||
|
||||
const _id = Cards.insert({
|
||||
title,
|
||||
members,
|
||||
labelIds,
|
||||
customFields,
|
||||
listId: this.data()._id,
|
||||
boardId: boardId._id,
|
||||
boardId: board._id,
|
||||
sort: sortIndex,
|
||||
swimlaneId,
|
||||
type: 'cardType-card',
|
||||
type: cardType,
|
||||
linkedId,
|
||||
});
|
||||
|
||||
// if the displayed card count is less than the total cards in the list,
|
||||
@@ -127,9 +149,9 @@ BlazeComponent.extendComponent({
|
||||
const methodName = evt.shiftKey ? 'toggleRange' : 'toggle';
|
||||
MultiSelection[methodName](this.currentData()._id);
|
||||
|
||||
// If the card is already selected, we want to de-select it.
|
||||
// XXX We should probably modify the minicard href attribute instead of
|
||||
// overwriting the event in case the card is already selected.
|
||||
// If the card is already selected, we want to de-select it.
|
||||
// XXX We should probably modify the minicard href attribute instead of
|
||||
// overwriting the event in case the card is already selected.
|
||||
} else if (Session.equals('currentCard', this.currentData()._id)) {
|
||||
evt.stopImmediatePropagation();
|
||||
evt.preventDefault();
|
||||
@@ -149,7 +171,8 @@ BlazeComponent.extendComponent({
|
||||
|
||||
idOrNull(swimlaneId) {
|
||||
const currentUser = Meteor.user();
|
||||
if (currentUser.profile.boardView === 'board-view-swimlanes')
|
||||
if (currentUser.profile.boardView === 'board-view-swimlanes' ||
|
||||
this.data().board().isTemplatesBoard())
|
||||
return swimlaneId;
|
||||
return undefined;
|
||||
},
|
||||
@@ -269,8 +292,8 @@ BlazeComponent.extendComponent({
|
||||
// work.
|
||||
$form.find('button[type=submit]').click();
|
||||
|
||||
// Pressing Tab should open the form of the next column, and Maj+Tab go
|
||||
// in the reverse order
|
||||
// Pressing Tab should open the form of the next column, and Maj+Tab go
|
||||
// in the reverse order
|
||||
} else if (evt.keyCode === 9) {
|
||||
evt.preventDefault();
|
||||
const isReverse = evt.shiftKey;
|
||||
@@ -292,7 +315,8 @@ BlazeComponent.extendComponent({
|
||||
return [{
|
||||
keydown: this.pressKey,
|
||||
'click .js-link': Popup.open('linkCard'),
|
||||
'click .js-search': Popup.open('searchCard'),
|
||||
'click .js-search': Popup.open('searchElement'),
|
||||
'click .js-card-template': Popup.open('searchElement'),
|
||||
}];
|
||||
},
|
||||
|
||||
@@ -330,7 +354,7 @@ BlazeComponent.extendComponent({
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
callback($.map(currentBoard.labels, (label) => {
|
||||
if (label.name.indexOf(term) > -1 ||
|
||||
label.color.indexOf(term) > -1) {
|
||||
label.color.indexOf(term) > -1) {
|
||||
return label;
|
||||
}
|
||||
return null;
|
||||
@@ -367,17 +391,7 @@ BlazeComponent.extendComponent({
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
// Prefetch first non-current board id
|
||||
const boardId = Boards.findOne({
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
_id: {$ne: Session.get('currentBoard')},
|
||||
}, {
|
||||
sort: ['title'],
|
||||
})._id;
|
||||
// Subscribe to this board
|
||||
subManager.subscribe('board', boardId);
|
||||
this.selectedBoardId = new ReactiveVar(boardId);
|
||||
this.selectedBoardId = new ReactiveVar('');
|
||||
this.selectedSwimlaneId = new ReactiveVar('');
|
||||
this.selectedListId = new ReactiveVar('');
|
||||
|
||||
@@ -403,6 +417,7 @@ BlazeComponent.extendComponent({
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
_id: {$ne: Session.get('currentBoard')},
|
||||
type: 'board',
|
||||
}, {
|
||||
sort: ['title'],
|
||||
});
|
||||
@@ -410,7 +425,7 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
swimlanes() {
|
||||
if (!this.selectedBoardId) {
|
||||
if (!this.selectedBoardId.get()) {
|
||||
return [];
|
||||
}
|
||||
const swimlanes = Swimlanes.find({boardId: this.selectedBoardId.get()});
|
||||
@@ -420,7 +435,7 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
lists() {
|
||||
if (!this.selectedBoardId) {
|
||||
if (!this.selectedBoardId.get()) {
|
||||
return [];
|
||||
}
|
||||
const lists = Lists.find({boardId: this.selectedBoardId.get()});
|
||||
@@ -441,6 +456,7 @@ BlazeComponent.extendComponent({
|
||||
archived: false,
|
||||
linkedId: {$nin: ownCardsIds},
|
||||
_id: {$nin: ownCardsIds},
|
||||
type: {$nin: ['template-card']},
|
||||
});
|
||||
},
|
||||
|
||||
@@ -508,12 +524,25 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
onCreated() {
|
||||
// Prefetch first non-current board id
|
||||
let board = Boards.findOne({
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
_id: {$ne: Session.get('currentBoard')},
|
||||
});
|
||||
this.isCardTemplateSearch = $(Popup._getTopStack().openerElement).hasClass('js-card-template');
|
||||
this.isListTemplateSearch = $(Popup._getTopStack().openerElement).hasClass('js-list-template');
|
||||
this.isSwimlaneTemplateSearch = $(Popup._getTopStack().openerElement).hasClass('js-open-add-swimlane-menu');
|
||||
this.isBoardTemplateSearch = $(Popup._getTopStack().openerElement).hasClass('js-add-board');
|
||||
this.isTemplateSearch = this.isCardTemplateSearch ||
|
||||
this.isListTemplateSearch ||
|
||||
this.isSwimlaneTemplateSearch ||
|
||||
this.isBoardTemplateSearch;
|
||||
let board = {};
|
||||
if (this.isTemplateSearch) {
|
||||
board = Boards.findOne(Meteor.user().profile.templatesBoardId);
|
||||
} else {
|
||||
// Prefetch first non-current board id
|
||||
board = Boards.findOne({
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
_id: {$nin: [Session.get('currentBoard'), Meteor.user().profile.templatesBoardId]},
|
||||
});
|
||||
}
|
||||
if (!board) {
|
||||
Popup.close();
|
||||
return;
|
||||
@@ -523,20 +552,21 @@ BlazeComponent.extendComponent({
|
||||
subManager.subscribe('board', boardId);
|
||||
this.selectedBoardId = new ReactiveVar(boardId);
|
||||
|
||||
this.boardId = Session.get('currentBoard');
|
||||
// In order to get current board info
|
||||
subManager.subscribe('board', this.boardId);
|
||||
board = Boards.findOne(this.boardId);
|
||||
// List where to insert card
|
||||
const list = $(Popup._getTopStack().openerElement).closest('.js-list');
|
||||
this.listId = Blaze.getData(list[0])._id;
|
||||
// Swimlane where to insert card
|
||||
const swimlane = $(Popup._getTopStack().openerElement).closest('.js-swimlane');
|
||||
this.swimlaneId = '';
|
||||
if (board.view === 'board-view-swimlanes')
|
||||
this.swimlaneId = Blaze.getData(swimlane[0])._id;
|
||||
else
|
||||
this.swimlaneId = Swimlanes.findOne({boardId: this.boardId})._id;
|
||||
if (!this.isBoardTemplateSearch) {
|
||||
this.boardId = Session.get('currentBoard');
|
||||
// In order to get current board info
|
||||
subManager.subscribe('board', this.boardId);
|
||||
this.swimlaneId = '';
|
||||
// Swimlane where to insert card
|
||||
const swimlane = $(Popup._getTopStack().openerElement).parents('.js-swimlane');
|
||||
if (Meteor.user().profile.boardView === 'board-view-swimlanes')
|
||||
this.swimlaneId = Blaze.getData(swimlane[0])._id;
|
||||
else
|
||||
this.swimlaneId = Swimlanes.findOne({boardId: this.boardId})._id;
|
||||
// List where to insert card
|
||||
const list = $(Popup._getTopStack().openerElement).closest('.js-list');
|
||||
this.listId = Blaze.getData(list[0])._id;
|
||||
}
|
||||
this.term = new ReactiveVar('');
|
||||
},
|
||||
|
||||
@@ -545,6 +575,7 @@ BlazeComponent.extendComponent({
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
_id: {$ne: Session.get('currentBoard')},
|
||||
type: 'board',
|
||||
}, {
|
||||
sort: ['title'],
|
||||
});
|
||||
@@ -556,7 +587,21 @@ BlazeComponent.extendComponent({
|
||||
return [];
|
||||
}
|
||||
const board = Boards.findOne(this.selectedBoardId.get());
|
||||
return board.searchCards(this.term.get(), false);
|
||||
if (!this.isTemplateSearch || this.isCardTemplateSearch) {
|
||||
return board.searchCards(this.term.get(), false);
|
||||
} else if (this.isListTemplateSearch) {
|
||||
return board.searchLists(this.term.get());
|
||||
} else if (this.isSwimlaneTemplateSearch) {
|
||||
return board.searchSwimlanes(this.term.get());
|
||||
} else if (this.isBoardTemplateSearch) {
|
||||
const boards = board.searchBoards(this.term.get());
|
||||
boards.forEach((board) => {
|
||||
subManager.subscribe('board', board.linkedId);
|
||||
});
|
||||
return boards;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
|
||||
events() {
|
||||
@@ -570,20 +615,49 @@ BlazeComponent.extendComponent({
|
||||
this.term.set(evt.target.searchTerm.value);
|
||||
},
|
||||
'click .js-minicard'(evt) {
|
||||
// LINK CARD
|
||||
const card = Blaze.getData(evt.currentTarget);
|
||||
const _id = Cards.insert({
|
||||
title: card.title, //dummy
|
||||
listId: this.listId,
|
||||
swimlaneId: this.swimlaneId,
|
||||
boardId: this.boardId,
|
||||
sort: Lists.findOne(this.listId).cards().count(),
|
||||
type: 'cardType-linkedCard',
|
||||
linkedId: card.linkedId || card._id,
|
||||
});
|
||||
Filter.addException(_id);
|
||||
// 0. Common
|
||||
const title = $('.js-element-title').val().trim();
|
||||
if (!title)
|
||||
return;
|
||||
const element = Blaze.getData(evt.currentTarget);
|
||||
element.title = title;
|
||||
let _id = '';
|
||||
if (!this.isTemplateSearch || this.isCardTemplateSearch) {
|
||||
// Card insertion
|
||||
// 1. Common
|
||||
element.sort = Lists.findOne(this.listId).cards().count();
|
||||
// 1.A From template
|
||||
if (this.isTemplateSearch) {
|
||||
element.type = 'cardType-card';
|
||||
element.linkedId = '';
|
||||
_id = element.copy(this.boardId, this.swimlaneId, this.listId);
|
||||
// 1.B Linked card
|
||||
} else {
|
||||
delete element._id;
|
||||
element.type = 'cardType-linkedCard';
|
||||
element.linkedId = element.linkedId || element._id;
|
||||
_id = Cards.insert(element);
|
||||
}
|
||||
Filter.addException(_id);
|
||||
// List insertion
|
||||
} else if (this.isListTemplateSearch) {
|
||||
element.sort = Swimlanes.findOne(this.swimlaneId).lists().count();
|
||||
element.type = 'list';
|
||||
_id = element.copy(this.boardId, this.swimlaneId);
|
||||
} else if (this.isSwimlaneTemplateSearch) {
|
||||
element.sort = Boards.findOne(this.boardId).swimlanes().count();
|
||||
element.type = 'swimlalne';
|
||||
_id = element.copy(this.boardId);
|
||||
} else if (this.isBoardTemplateSearch) {
|
||||
board = Boards.findOne(element.linkedId);
|
||||
board.sort = Boards.find({archived: false}).count();
|
||||
board.type = 'board';
|
||||
board.title = element.title;
|
||||
delete board.slug;
|
||||
_id = board.copy();
|
||||
}
|
||||
Popup.close();
|
||||
},
|
||||
}];
|
||||
},
|
||||
}).register('searchCardPopup');
|
||||
}).register('searchElementPopup');
|
||||
@@ -0,0 +1,8 @@
|
||||
template(name="minilist")
|
||||
.minicard(
|
||||
class="minicard-{{colorClass}}")
|
||||
.minicard-title
|
||||
.handle
|
||||
.fa.fa-arrows
|
||||
+viewer
|
||||
= title
|
||||
@@ -36,7 +36,10 @@ import sanitizeXss from 'xss';
|
||||
const at = HTML.CharRef({html: '@', str: '@'});
|
||||
Blaze.Template.registerHelper('mentions', new Template('mentions', function() {
|
||||
const view = this;
|
||||
let content = Blaze.toHTML(view.templateContentBlock);
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
if (!currentBoard)
|
||||
return HTML.Raw(sanitizeXss(content));
|
||||
const knowedUsers = currentBoard.members.map((member) => {
|
||||
const u = Users.findOne(member.userId);
|
||||
if(u){
|
||||
@@ -45,7 +48,6 @@ Blaze.Template.registerHelper('mentions', new Template('mentions', function() {
|
||||
return member;
|
||||
});
|
||||
const mentionRegex = /\B@([\w.]*)/gi;
|
||||
let content = Blaze.toHTML(view.templateContentBlock);
|
||||
|
||||
let currentMention;
|
||||
while ((currentMention = mentionRegex.exec(content)) !== null) {
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
template(name="miniswimlane")
|
||||
.minicard(
|
||||
class="minicard-{{colorClass}}")
|
||||
.minicard-title
|
||||
.handle
|
||||
.fa.fa-arrows
|
||||
+viewer
|
||||
= title
|
||||
@@ -1,15 +1,21 @@
|
||||
template(name="swimlaneHeader")
|
||||
.swimlane-header-wrap.js-swimlane-header(class='{{#if colorClass}}swimlane-{{colorClass}}{{/if}}')
|
||||
+inlinedForm
|
||||
+editSwimlaneTitleForm
|
||||
if this.isTemplateContainer
|
||||
+swimlaneFixedHeader(this)
|
||||
else
|
||||
.swimlane-header(
|
||||
class="{{#if currentUser.isBoardMember}}js-open-inlined-form is-editable{{/if}}")
|
||||
= title
|
||||
.swimlane-header-menu
|
||||
unless currentUser.isCommentOnly
|
||||
a.fa.fa-plus.js-open-add-swimlane-menu.swimlane-header-plus-icon
|
||||
a.fa.fa-navicon.js-open-swimlane-menu
|
||||
+inlinedForm
|
||||
+editSwimlaneTitleForm
|
||||
else
|
||||
+swimlaneFixedHeader(this)
|
||||
|
||||
template(name="swimlaneFixedHeader")
|
||||
.swimlane-header(
|
||||
class="{{#if currentUser.isBoardMember}}js-open-inlined-form is-editable{{/if}}")
|
||||
= title
|
||||
.swimlane-header-menu
|
||||
unless currentUser.isCommentOnly
|
||||
a.fa.fa-plus.js-open-add-swimlane-menu.swimlane-header-plus-icon
|
||||
a.fa.fa-navicon.js-open-swimlane-menu
|
||||
|
||||
template(name="editSwimlaneTitleForm")
|
||||
.list-composer
|
||||
@@ -22,9 +28,10 @@ template(name="swimlaneActionPopup")
|
||||
unless currentUser.isCommentOnly
|
||||
ul.pop-over-list
|
||||
li: a.js-set-swimlane-color {{_ 'select-color'}}
|
||||
hr
|
||||
ul.pop-over-list
|
||||
li: a.js-close-swimlane {{_ 'archive-swimlane'}}
|
||||
unless this.isTemplateContainer
|
||||
hr
|
||||
ul.pop-over-list
|
||||
li: a.js-close-swimlane {{_ 'archive-swimlane'}}
|
||||
|
||||
template(name="swimlaneAddPopup")
|
||||
unless currentUser.isCommentOnly
|
||||
@@ -33,6 +40,11 @@ template(name="swimlaneAddPopup")
|
||||
autocomplete="off" autofocus)
|
||||
.edit-controls.clearfix
|
||||
button.primary.confirm(type="submit") {{_ 'add'}}
|
||||
unless currentBoard.isTemplatesBoard
|
||||
unless currentBoard.isTemplateBoard
|
||||
span.quiet
|
||||
| {{_ 'or'}}
|
||||
a.js-swimlane-template {{_ 'template'}}
|
||||
|
||||
template(name="setSwimlaneColorPopup")
|
||||
form.edit-label
|
||||
|
||||
@@ -47,12 +47,14 @@ BlazeComponent.extendComponent({
|
||||
const titleInput = this.find('.swimlane-name-input');
|
||||
const title = titleInput.value.trim();
|
||||
const sortValue = calculateIndexData(this.currentSwimlane, nextSwimlane, 1);
|
||||
const swimlaneType = (currentBoard.isTemplatesBoard())?'template-swimlane':'swimlane';
|
||||
|
||||
if (title) {
|
||||
Swimlanes.insert({
|
||||
title,
|
||||
boardId: Session.get('currentBoard'),
|
||||
sort: sortValue.base,
|
||||
type: swimlaneType,
|
||||
});
|
||||
|
||||
titleInput.value = '';
|
||||
@@ -63,6 +65,7 @@ BlazeComponent.extendComponent({
|
||||
// with a minimum of interactions
|
||||
Popup.close();
|
||||
},
|
||||
'click .js-swimlane-template': Popup.open('searchElement'),
|
||||
}];
|
||||
},
|
||||
}).register('swimlaneAddPopup');
|
||||
|
||||
@@ -3,15 +3,15 @@ template(name="swimlane")
|
||||
+swimlaneHeader
|
||||
.swimlane.js-lists.js-swimlane
|
||||
if isMiniScreen
|
||||
if currentList
|
||||
if currentListIsInThisSwimlane _id
|
||||
+list(currentList)
|
||||
else
|
||||
each currentBoard.lists
|
||||
unless currentList
|
||||
each lists
|
||||
+miniList(this)
|
||||
if currentUser.isBoardMember
|
||||
+addListForm
|
||||
else
|
||||
each currentBoard.lists
|
||||
each lists
|
||||
+list(this)
|
||||
if currentCardIsInThisList _id ../_id
|
||||
+cardDetails(currentCard)
|
||||
@@ -24,12 +24,12 @@ template(name="listsGroup")
|
||||
if currentList
|
||||
+list(currentList)
|
||||
else
|
||||
each currentBoard.lists
|
||||
each lists
|
||||
+miniList(this)
|
||||
if currentUser.isBoardMember
|
||||
+addListForm
|
||||
else
|
||||
each currentBoard.lists
|
||||
each lists
|
||||
+list(this)
|
||||
if currentCardIsInThisList _id null
|
||||
+cardDetails(currentCard)
|
||||
@@ -44,7 +44,11 @@ template(name="addListForm")
|
||||
autocomplete="off" autofocus)
|
||||
.edit-controls.clearfix
|
||||
button.primary.confirm(type="submit") {{_ 'save'}}
|
||||
a.fa.fa-times-thin.js-close-inlined-form
|
||||
unless currentBoard.isTemplatesBoard
|
||||
unless currentBoard.isTemplateBoard
|
||||
span.quiet
|
||||
| {{_ 'or'}}
|
||||
a.js-list-template {{_ 'template'}}
|
||||
else
|
||||
a.open-list-composer.js-open-inlined-form
|
||||
i.fa.fa-plus
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
const { calculateIndex, enableClickOnTouch } = Utils;
|
||||
|
||||
function currentListIsInThisSwimlane(swimlaneId) {
|
||||
const currentList = Lists.findOne(Session.get('currentList'));
|
||||
return currentList && (currentList.swimlaneId === swimlaneId || currentList.swimlaneId === '');
|
||||
}
|
||||
|
||||
function currentCardIsInThisList(listId, swimlaneId) {
|
||||
const currentCard = Cards.findOne(Session.get('currentCard'));
|
||||
const currentUser = Meteor.user();
|
||||
@@ -114,6 +119,10 @@ BlazeComponent.extendComponent({
|
||||
return currentCardIsInThisList(listId, swimlaneId);
|
||||
},
|
||||
|
||||
currentListIsInThisSwimlane(swimlaneId) {
|
||||
return currentListIsInThisSwimlane(swimlaneId);
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
// Click-and-drag action
|
||||
@@ -153,6 +162,12 @@ BlazeComponent.extendComponent({
|
||||
}).register('swimlane');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
this.isListTemplatesSwimlane = this.currentBoard.isTemplatesBoard() && this.currentData().isListTemplatesSwimlane();
|
||||
this.currentSwimlane = this.currentData();
|
||||
},
|
||||
|
||||
// Proxy
|
||||
open() {
|
||||
this.childComponents('inlinedForm')[0].open();
|
||||
@@ -169,12 +184,15 @@ BlazeComponent.extendComponent({
|
||||
title,
|
||||
boardId: Session.get('currentBoard'),
|
||||
sort: $('.list').length,
|
||||
type: (this.isListTemplatesSwimlane)?'template-list':'list',
|
||||
swimlaneId: (this.currentBoard.isTemplatesBoard())?this.currentSwimlane._id:'',
|
||||
});
|
||||
|
||||
titleInput.value = '';
|
||||
titleInput.focus();
|
||||
}
|
||||
},
|
||||
'click .js-list-template': Popup.open('searchElement'),
|
||||
}];
|
||||
},
|
||||
}).register('addListForm');
|
||||
|
||||
@@ -21,6 +21,9 @@ template(name="memberMenuPopup")
|
||||
li: a.js-change-language {{_ 'changeLanguagePopup-title'}}
|
||||
if currentUser.isAdmin
|
||||
li: a.js-go-setting(href="{{pathFor 'setting'}}") {{_ 'admin-panel'}}
|
||||
hr
|
||||
ul.pop-over-list
|
||||
li: a(href="{{pathFor 'board' id=templatesBoardId slug=templatesBoardSlug}}") {{_ 'templates'}}
|
||||
unless isSandstorm
|
||||
hr
|
||||
ul.pop-over-list
|
||||
|
||||
@@ -3,6 +3,15 @@ Template.headerUserBar.events({
|
||||
'click .js-change-avatar': Popup.open('changeAvatar'),
|
||||
});
|
||||
|
||||
Template.memberMenuPopup.helpers({
|
||||
templatesBoardId() {
|
||||
return Meteor.user().getTemplatesBoardId();
|
||||
},
|
||||
templatesBoardSlug() {
|
||||
return Meteor.user().getTemplatesBoardSlug();
|
||||
},
|
||||
});
|
||||
|
||||
Template.memberMenuPopup.events({
|
||||
'click .js-edit-profile': Popup.open('editProfile'),
|
||||
'click .js-change-settings': Popup.open('changeSettings'),
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
|
||||
"copy-card-link-to-clipboard": "نسخ رابط البطاقة إلى الحافظة",
|
||||
"linkCardPopup-title": "ربط البطاقة",
|
||||
"searchCardPopup-title": "Search Card",
|
||||
"searchElementPopup-title": "بحث",
|
||||
"copyCardPopup-title": "نسخ البطاقة",
|
||||
"copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Сигурни ли сте, че искате да изтриете този чеклист?",
|
||||
"copy-card-link-to-clipboard": "Копирай връзката на картата в клипборда",
|
||||
"linkCardPopup-title": "Свържи картата",
|
||||
"searchCardPopup-title": "Търсене на карта",
|
||||
"searchElementPopup-title": "Търсене",
|
||||
"copyCardPopup-title": "Копирай картата",
|
||||
"copyChecklistToManyCardsPopup-title": "Копирай чеклисти в други карти",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
|
||||
"copy-card-link-to-clipboard": "Copy card link to clipboard",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchCardPopup-title": "Search Card",
|
||||
"searchElementPopup-title": "Search",
|
||||
"copyCardPopup-title": "Copy Card",
|
||||
"copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
|
||||
"copy-card-link-to-clipboard": "Copia l'enllaç de la ftixa al porta-retalls",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchCardPopup-title": "Buscar Fitxa",
|
||||
"searchElementPopup-title": "Cerca",
|
||||
"copyCardPopup-title": "Copia la fitxa",
|
||||
"copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Títols de fitxa i Descripcions de destí en aquest format JSON",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Opravdu chcete smazat tento checklist?",
|
||||
"copy-card-link-to-clipboard": "Kopírovat adresu karty do mezipaměti",
|
||||
"linkCardPopup-title": "Propojit kartu",
|
||||
"searchCardPopup-title": "Hledat Kartu",
|
||||
"searchElementPopup-title": "Hledat",
|
||||
"copyCardPopup-title": "Kopírovat kartu",
|
||||
"copyChecklistToManyCardsPopup-title": "Kopírovat checklist do více karet",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Názvy a popisy cílové karty v tomto formátu JSON",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
|
||||
"copy-card-link-to-clipboard": "Copy card link to clipboard",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchCardPopup-title": "Search Card",
|
||||
"searchElementPopup-title": "Search",
|
||||
"copyCardPopup-title": "Copy Card",
|
||||
"copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Wollen Sie die Checkliste wirklich löschen?",
|
||||
"copy-card-link-to-clipboard": "Kopiere Link zur Karte in die Zwischenablage",
|
||||
"linkCardPopup-title": "Karte verknüpfen",
|
||||
"searchCardPopup-title": "Karte suchen",
|
||||
"searchElementPopup-title": "Suche",
|
||||
"copyCardPopup-title": "Karte kopieren",
|
||||
"copyChecklistToManyCardsPopup-title": "Checklistenvorlage in mehrere Karten kopieren",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Titel und Beschreibungen der Zielkarten im folgenden JSON-Format",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
|
||||
"copy-card-link-to-clipboard": "Copy card link to clipboard",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchCardPopup-title": "Search Card",
|
||||
"searchElementPopup-title": "Αναζήτηση",
|
||||
"copyCardPopup-title": "Copy Card",
|
||||
"copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
|
||||
|
||||
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
|
||||
"copy-card-link-to-clipboard": "Copy card link to clipboard",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchCardPopup-title": "Search Card",
|
||||
"searchElementPopup-title": "Search",
|
||||
"copyCardPopup-title": "Copy Card",
|
||||
"copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
|
||||
|
||||
+7
-1
@@ -92,6 +92,8 @@
|
||||
"restore-board": "Restore Board",
|
||||
"no-archived-boards": "No Boards in Archive.",
|
||||
"archives": "Archive",
|
||||
"template": "Template",
|
||||
"templates": "Templates",
|
||||
"assign-member": "Assign member",
|
||||
"attached": "attached",
|
||||
"attachment": "Attachment",
|
||||
@@ -143,6 +145,7 @@
|
||||
"cardLabelsPopup-title": "Labels",
|
||||
"cardMembersPopup-title": "Members",
|
||||
"cardMorePopup-title": "More",
|
||||
"cardTemplatePopup-title": "Create template",
|
||||
"cards": "Cards",
|
||||
"cards-count": "Cards",
|
||||
"casSignIn" : "Sign In with CAS",
|
||||
@@ -204,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
|
||||
"copy-card-link-to-clipboard": "Copy card link to clipboard",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchCardPopup-title": "Search Card",
|
||||
"searchElementPopup-title": "Search",
|
||||
"copyCardPopup-title": "Copy Card",
|
||||
"copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
|
||||
@@ -453,6 +456,9 @@
|
||||
"welcome-swimlane": "Milestone 1",
|
||||
"welcome-list1": "Basics",
|
||||
"welcome-list2": "Advanced",
|
||||
"card-templates-swimlane": "Card Templates",
|
||||
"list-templates-swimlane": "List Templates",
|
||||
"board-templates-swimlane": "Board Templates",
|
||||
"what-to-do": "What do you want to do?",
|
||||
"wipLimitErrorPopup-title": "Invalid WIP Limit",
|
||||
"wipLimitErrorPopup-dialog-pt1": "The number of tasks in this list is higher than the WIP limit you've defined.",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
|
||||
"copy-card-link-to-clipboard": "Copy card link to clipboard",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchCardPopup-title": "Search Card",
|
||||
"searchElementPopup-title": "Serĉi",
|
||||
"copyCardPopup-title": "Copy Card",
|
||||
"copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
|
||||
|
||||
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
|
||||
"copy-card-link-to-clipboard": "Copiar enlace a tarjeta en el portapapeles",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchCardPopup-title": "Search Card",
|
||||
"searchElementPopup-title": "Buscar",
|
||||
"copyCardPopup-title": "Copiar Tarjeta",
|
||||
"copyChecklistToManyCardsPopup-title": "Copiar Plantilla Checklist a Muchas Tarjetas",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Títulos y Descripciones de la Tarjeta Destino en este formato JSON",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "¿Seguro que quieres eliminar la lista de verificación?",
|
||||
"copy-card-link-to-clipboard": "Copiar el enlace de la tarjeta al portapapeles",
|
||||
"linkCardPopup-title": "Enlazar tarjeta",
|
||||
"searchCardPopup-title": "Buscar tarjeta",
|
||||
"searchElementPopup-title": "Buscar",
|
||||
"copyCardPopup-title": "Copiar la tarjeta",
|
||||
"copyChecklistToManyCardsPopup-title": "Copiar la plantilla de la lista de verificación en varias tarjetas",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Títulos y descripciones de las tarjetas de destino en formato JSON",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
|
||||
"copy-card-link-to-clipboard": "Kopiatu txartela arbelera",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchCardPopup-title": "Search Card",
|
||||
"searchElementPopup-title": "Bilatu",
|
||||
"copyCardPopup-title": "Kopiatu txartela",
|
||||
"copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "مطمئنا چک لیست پاک شود؟",
|
||||
"copy-card-link-to-clipboard": "درج پیوند کارت در حافظه",
|
||||
"linkCardPopup-title": "ارتباط دادن کارت",
|
||||
"searchCardPopup-title": "جستجوی کارت",
|
||||
"searchElementPopup-title": "جستجو",
|
||||
"copyCardPopup-title": "کپی کارت",
|
||||
"copyChecklistToManyCardsPopup-title": "کپی قالب کارت به کارتهای متعدد",
|
||||
"copyChecklistToManyCardsPopup-instructions": "عنوان و توضیحات کارت مقصد در این قالب JSON",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Oletko varma että haluat poistaa tarkistuslistan?",
|
||||
"copy-card-link-to-clipboard": "Kopioi kortin linkki leikepöydälle",
|
||||
"linkCardPopup-title": "Linkitä kortti",
|
||||
"searchCardPopup-title": "Etsi kortti",
|
||||
"searchElementPopup-title": "Etsi",
|
||||
"copyCardPopup-title": "Kopioi kortti",
|
||||
"copyChecklistToManyCardsPopup-title": "Kopioi tarkistuslistan malli monille korteille",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Kohde korttien otsikot ja kuvaukset JSON muodossa",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Êtes-vous sûr de vouloir supprimer la checklist ?",
|
||||
"copy-card-link-to-clipboard": "Copier le lien vers la carte dans le presse-papier",
|
||||
"linkCardPopup-title": "Lier une Carte",
|
||||
"searchCardPopup-title": "Chercher une Carte",
|
||||
"searchElementPopup-title": "Chercher",
|
||||
"copyCardPopup-title": "Copier la carte",
|
||||
"copyChecklistToManyCardsPopup-title": "Copier le modèle de checklist vers plusieurs cartes",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Titres et descriptions des cartes de destination dans ce format JSON",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
|
||||
"copy-card-link-to-clipboard": "Copy card link to clipboard",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchCardPopup-title": "Search Card",
|
||||
"searchElementPopup-title": "Search",
|
||||
"copyCardPopup-title": "Copy Card",
|
||||
"copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "למחוק את רשימת המשימות?",
|
||||
"copy-card-link-to-clipboard": "העתקת קישור הכרטיס ללוח הגזירים",
|
||||
"linkCardPopup-title": "קישור כרטיס",
|
||||
"searchCardPopup-title": "חיפוש כרטיס",
|
||||
"searchElementPopup-title": "חיפוש",
|
||||
"copyCardPopup-title": "העתק כרטיס",
|
||||
"copyChecklistToManyCardsPopup-title": "העתקת תבנית רשימת מטלות למגוון כרטיסים",
|
||||
"copyChecklistToManyCardsPopup-instructions": "כותרות ותיאורים של כרטיסי יעד בתצורת JSON זו",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "क्या आप वाकई जांचसूची हटाना चाहते हैं?",
|
||||
"copy-card-link-to-clipboard": "कॉपी कार्ड क्लिपबोर्ड करने के लिए लिंक",
|
||||
"linkCardPopup-title": "कार्ड कड़ी",
|
||||
"searchCardPopup-title": "कार्ड खोज",
|
||||
"searchElementPopup-title": "Search",
|
||||
"copyCardPopup-title": "कार्ड प्रतिलिपि",
|
||||
"copyChecklistToManyCardsPopup-title": "कई कार्ड के लिए जांचसूची खाके की प्रतिलिपि बनाएँ",
|
||||
"copyChecklistToManyCardsPopup-instructions": "इस JSON प्रारूप में गंतव्य कार्ड शीर्षक और विवरण",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
|
||||
"copy-card-link-to-clipboard": "Kártya hivatkozásának másolása a vágólapra",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchCardPopup-title": "Search Card",
|
||||
"searchElementPopup-title": "Keresés",
|
||||
"copyCardPopup-title": "Kártya másolása",
|
||||
"copyChecklistToManyCardsPopup-title": "Ellenőrzőlista sablon másolása több kártyára",
|
||||
"copyChecklistToManyCardsPopup-instructions": "A célkártyák címe és a leírások ebben a JSON formátumban",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
|
||||
"copy-card-link-to-clipboard": "Copy card link to clipboard",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchCardPopup-title": "Search Card",
|
||||
"searchElementPopup-title": "Search",
|
||||
"copyCardPopup-title": "Copy Card",
|
||||
"copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
|
||||
"copy-card-link-to-clipboard": "Copy card link to clipboard",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchCardPopup-title": "Search Card",
|
||||
"searchElementPopup-title": "Cari",
|
||||
"copyCardPopup-title": "Copy Card",
|
||||
"copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
|
||||
"copy-card-link-to-clipboard": "Copy card link to clipboard",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchCardPopup-title": "Search Card",
|
||||
"searchElementPopup-title": "Search",
|
||||
"copyCardPopup-title": "Copy Card",
|
||||
"copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Sei sicuro di voler eliminare la checklist?",
|
||||
"copy-card-link-to-clipboard": "Copia link della scheda sulla clipboard",
|
||||
"linkCardPopup-title": "Collega scheda",
|
||||
"searchCardPopup-title": "Cerca scheda",
|
||||
"searchElementPopup-title": "Cerca",
|
||||
"copyCardPopup-title": "Copia Scheda",
|
||||
"copyChecklistToManyCardsPopup-title": "Copia template checklist su più schede",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Titolo e la descrizione della scheda di destinazione in questo formato JSON",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
|
||||
"copy-card-link-to-clipboard": "カードへのリンクをクリップボードにコピー",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchCardPopup-title": "Search Card",
|
||||
"searchElementPopup-title": "検索",
|
||||
"copyCardPopup-title": "カードをコピー",
|
||||
"copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "დარწმუნებული ხართ, რომ გსურთ კატალოგის წაშლა ? ",
|
||||
"copy-card-link-to-clipboard": "დააკოპირეთ ბარათის ბმული clipboard-ზე",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchCardPopup-title": "Search Card",
|
||||
"searchElementPopup-title": "ძებნა",
|
||||
"copyCardPopup-title": "ბარათის ასლი",
|
||||
"copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
|
||||
"copy-card-link-to-clipboard": "Copy card link to clipboard",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchCardPopup-title": "Search Card",
|
||||
"searchElementPopup-title": "Search",
|
||||
"copyCardPopup-title": "Copy Card",
|
||||
"copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
|
||||
"copy-card-link-to-clipboard": "클립보드에 카드의 링크가 복사되었습니다.",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchCardPopup-title": "Search Card",
|
||||
"searchElementPopup-title": "검색",
|
||||
"copyCardPopup-title": "카드 복사",
|
||||
"copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
|
||||
"copy-card-link-to-clipboard": "Copy card link to clipboard",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchCardPopup-title": "Search Card",
|
||||
"searchElementPopup-title": "Search",
|
||||
"copyCardPopup-title": "Copy Card",
|
||||
"copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Сигурни ли сте, че искате да изтриете този чеклист?",
|
||||
"copy-card-link-to-clipboard": "Копирай връзката на картата в клипборда",
|
||||
"linkCardPopup-title": "Свържи картата",
|
||||
"searchCardPopup-title": "Търсене на карта",
|
||||
"searchElementPopup-title": "Търсене",
|
||||
"copyCardPopup-title": "Копирай картата",
|
||||
"copyChecklistToManyCardsPopup-title": "Копирай чеклисти в други карти",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
|
||||
"copy-card-link-to-clipboard": "Copy card link to clipboard",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchCardPopup-title": "Search Card",
|
||||
"searchElementPopup-title": "Search",
|
||||
"copyCardPopup-title": "Copy Card",
|
||||
"copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
|
||||
"copy-card-link-to-clipboard": "Copy card link to clipboard",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchCardPopup-title": "Search Card",
|
||||
"searchElementPopup-title": "Search",
|
||||
"copyCardPopup-title": "Copy Card",
|
||||
"copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
|
||||
"copy-card-link-to-clipboard": "Kopieer kaart link naar klembord",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchCardPopup-title": "Search Card",
|
||||
"searchElementPopup-title": "Zoek",
|
||||
"copyCardPopup-title": "Kopieer kaart",
|
||||
"copyChecklistToManyCardsPopup-title": "Checklist sjabloon kopiëren naar meerdere kaarten",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Doel kaart titels en omschrijvingen in dit JSON formaat",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Czy jesteś pewien, że chcesz usunąć listę zadań?",
|
||||
"copy-card-link-to-clipboard": "Skopiuj łącze karty do schowka",
|
||||
"linkCardPopup-title": "Podepnij kartę",
|
||||
"searchCardPopup-title": "Znajdź kartę",
|
||||
"searchElementPopup-title": "Wyszukaj",
|
||||
"copyCardPopup-title": "Skopiuj kartę",
|
||||
"copyChecklistToManyCardsPopup-title": "Kopiuj szablon listy zadań do wielu kart",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Docelowe tytuły i opisy kart są w formacie JSON",
|
||||
|
||||
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Tem certeza que quer excluir a lista de verificação?",
|
||||
"copy-card-link-to-clipboard": "Copiar link do cartão para a área de transferência",
|
||||
"linkCardPopup-title": "Ligar Cartão",
|
||||
"searchCardPopup-title": "Procurar Cartão",
|
||||
"searchElementPopup-title": "Buscar",
|
||||
"copyCardPopup-title": "Copiar o cartão",
|
||||
"copyChecklistToManyCardsPopup-title": "Copiar modelo de lista de verificação para vários cartões",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Títulos e descrições do cartão de destino neste formato JSON",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
|
||||
"copy-card-link-to-clipboard": "Copy card link to clipboard",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchCardPopup-title": "Search Card",
|
||||
"searchElementPopup-title": "Search",
|
||||
"copyCardPopup-title": "Copy Card",
|
||||
"copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
|
||||
"copy-card-link-to-clipboard": "Copy card link to clipboard",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchCardPopup-title": "Search Card",
|
||||
"searchElementPopup-title": "Caută",
|
||||
"copyCardPopup-title": "Copy Card",
|
||||
"copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
|
||||
|
||||
+7
-7
@@ -92,8 +92,8 @@
|
||||
"restore-board": "Востановить доску",
|
||||
"no-archived-boards": "Нет досок в архиве.",
|
||||
"archives": "Архив",
|
||||
"template": "Template",
|
||||
"templates": "Templates",
|
||||
"template": "Шаблон",
|
||||
"templates": "Шаблоны",
|
||||
"assign-member": "Назначить участника",
|
||||
"attached": "прикреплено",
|
||||
"attachment": "Вложение",
|
||||
@@ -145,7 +145,7 @@
|
||||
"cardLabelsPopup-title": "Метки",
|
||||
"cardMembersPopup-title": "Участники",
|
||||
"cardMorePopup-title": "Поделиться",
|
||||
"cardTemplatePopup-title": "Create template",
|
||||
"cardTemplatePopup-title": "Создать шаблон",
|
||||
"cards": "Карточки",
|
||||
"cards-count": "Карточки",
|
||||
"casSignIn": "Войти через CAS",
|
||||
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Вы уверены, что хотите удалить контрольный список?",
|
||||
"copy-card-link-to-clipboard": "Копировать ссылку на карточку в буфер обмена",
|
||||
"linkCardPopup-title": "Карточка-ссылка",
|
||||
"searchCardPopup-title": "Найти карточку",
|
||||
"searchElementPopup-title": "Поиск",
|
||||
"copyCardPopup-title": "Копировать карточку",
|
||||
"copyChecklistToManyCardsPopup-title": "Копировать шаблон контрольного списка в несколько карточек",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Названия и описания целевых карт в формате JSON",
|
||||
@@ -456,9 +456,9 @@
|
||||
"welcome-swimlane": "Этап 1",
|
||||
"welcome-list1": "Основы",
|
||||
"welcome-list2": "Расширенно",
|
||||
"card-templates-swimlane": "Card Templates",
|
||||
"list-templates-swimlane": "List Templates",
|
||||
"board-templates-swimlane": "Board Templates",
|
||||
"card-templates-swimlane": "Шаблоны карточек",
|
||||
"list-templates-swimlane": "Шаблоны списков",
|
||||
"board-templates-swimlane": "Шаблоны досок",
|
||||
"what-to-do": "Что вы хотите сделать?",
|
||||
"wipLimitErrorPopup-title": "Некорректный лимит на кол-во задач",
|
||||
"wipLimitErrorPopup-dialog-pt1": "Количество задач в этом списке превышает установленный вами лимит",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
|
||||
"copy-card-link-to-clipboard": "Copy card link to clipboard",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchCardPopup-title": "Search Card",
|
||||
"searchElementPopup-title": "Pretraga",
|
||||
"copyCardPopup-title": "Copy Card",
|
||||
"copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Är du säker på att du vill radera checklista?",
|
||||
"copy-card-link-to-clipboard": "Kopiera kortlänk till urklipp",
|
||||
"linkCardPopup-title": "Länka kort",
|
||||
"searchCardPopup-title": "Sök kort",
|
||||
"searchElementPopup-title": "Sök",
|
||||
"copyCardPopup-title": "Kopiera kort",
|
||||
"copyChecklistToManyCardsPopup-title": "Kopiera checklist-mallen till flera kort",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Destinationskorttitlar och beskrivningar i detta JSON-format",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
|
||||
"copy-card-link-to-clipboard": "Copy card link to clipboard",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchCardPopup-title": "Search Card",
|
||||
"searchElementPopup-title": "Search",
|
||||
"copyCardPopup-title": "Copy Card",
|
||||
"copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
|
||||
"copy-card-link-to-clipboard": "Copy card link to clipboard",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchCardPopup-title": "Search Card",
|
||||
"searchElementPopup-title": "தேடு ",
|
||||
"copyCardPopup-title": "Copy Card",
|
||||
"copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
|
||||
"copy-card-link-to-clipboard": "Copy card link to clipboard",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchCardPopup-title": "Search Card",
|
||||
"searchElementPopup-title": "ค้นหา",
|
||||
"copyCardPopup-title": "Copy Card",
|
||||
"copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Kontrol listesini silmek istediğinden emin misin?",
|
||||
"copy-card-link-to-clipboard": "Kartın linkini kopyala",
|
||||
"linkCardPopup-title": "Bağlantı kartı",
|
||||
"searchCardPopup-title": "Kart Ara",
|
||||
"searchElementPopup-title": "Arama",
|
||||
"copyCardPopup-title": "Kartı Kopyala",
|
||||
"copyChecklistToManyCardsPopup-title": "Yapılacaklar Listesi şemasını birden çok karta kopyala",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Hedef Kart Başlıkları ve Açıklamaları bu JSON formatında",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
|
||||
"copy-card-link-to-clipboard": "Скопіювати посилання на картку в буфер обміну",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchCardPopup-title": "Search Card",
|
||||
"searchElementPopup-title": "Search",
|
||||
"copyCardPopup-title": "Copy Card",
|
||||
"copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
|
||||
"copy-card-link-to-clipboard": "Copy card link to clipboard",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchCardPopup-title": "Search Card",
|
||||
"searchElementPopup-title": "Search",
|
||||
"copyCardPopup-title": "Copy Card",
|
||||
"copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
|
||||
"copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
|
||||
|
||||
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "确定要删除清单吗?",
|
||||
"copy-card-link-to-clipboard": "复制卡片链接到剪贴板",
|
||||
"linkCardPopup-title": "链接卡片",
|
||||
"searchCardPopup-title": "搜索卡片",
|
||||
"searchElementPopup-title": "搜索",
|
||||
"copyCardPopup-title": "复制卡片",
|
||||
"copyChecklistToManyCardsPopup-title": "复制清单模板至多个卡片",
|
||||
"copyChecklistToManyCardsPopup-instructions": "以JSON格式表示目标卡片的标题和描述",
|
||||
|
||||
@@ -207,7 +207,7 @@
|
||||
"confirm-checklist-delete-dialog": "你確定要刪除待辦清單嗎?",
|
||||
"copy-card-link-to-clipboard": "將卡片連結複製到剪貼簿",
|
||||
"linkCardPopup-title": "連結卡片",
|
||||
"searchCardPopup-title": "搜尋卡片",
|
||||
"searchElementPopup-title": "搜尋",
|
||||
"copyCardPopup-title": "複製卡片",
|
||||
"copyChecklistToManyCardsPopup-title": "複製待辦清單的樣板到多個卡片",
|
||||
"copyChecklistToManyCardsPopup-instructions": "使用此 JSON 格式來表示目標卡片的標題和描述",
|
||||
|
||||
@@ -27,6 +27,10 @@ Attachments = new FS.Collection('attachments', {
|
||||
|
||||
|
||||
if (Meteor.isServer) {
|
||||
Meteor.startup(() => {
|
||||
Attachments.files._ensureIndex({ cardId: 1 });
|
||||
});
|
||||
|
||||
Attachments.allow({
|
||||
insert(userId, doc) {
|
||||
return allowIsBoardMember(userId, Boards.findOne(doc.boardId));
|
||||
|
||||
+106
-3
@@ -304,10 +304,31 @@ Boards.attachSchema(new SimpleSchema({
|
||||
defaultValue: false,
|
||||
optional: true,
|
||||
},
|
||||
type: {
|
||||
/**
|
||||
* The type of board
|
||||
*/
|
||||
type: String,
|
||||
defaultValue: 'board',
|
||||
},
|
||||
}));
|
||||
|
||||
|
||||
Boards.helpers({
|
||||
copy() {
|
||||
const oldId = this._id;
|
||||
delete this._id;
|
||||
const _id = Boards.insert(this);
|
||||
|
||||
// Copy all swimlanes in board
|
||||
Swimlanes.find({
|
||||
boardId: oldId,
|
||||
archived: false,
|
||||
}).forEach((swimlane) => {
|
||||
swimlane.type = 'swimlane';
|
||||
swimlane.copy(_id);
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Is supplied user authorized to view this board?
|
||||
*/
|
||||
@@ -456,6 +477,75 @@ Boards.helpers({
|
||||
return _id;
|
||||
},
|
||||
|
||||
searchBoards(term) {
|
||||
check(term, Match.OneOf(String, null, undefined));
|
||||
|
||||
const query = { boardId: this._id };
|
||||
query.type = 'cardType-linkedBoard';
|
||||
query.archived = false;
|
||||
|
||||
const projection = { limit: 10, sort: { createdAt: -1 } };
|
||||
|
||||
if (term) {
|
||||
const regex = new RegExp(term, 'i');
|
||||
|
||||
query.$or = [
|
||||
{ title: regex },
|
||||
{ description: regex },
|
||||
];
|
||||
}
|
||||
|
||||
return Cards.find(query, projection);
|
||||
},
|
||||
|
||||
searchSwimlanes(term) {
|
||||
check(term, Match.OneOf(String, null, undefined));
|
||||
|
||||
const query = { boardId: this._id };
|
||||
if (this.isTemplatesBoard()) {
|
||||
query.type = 'template-swimlane';
|
||||
query.archived = false;
|
||||
} else {
|
||||
query.type = {$nin: ['template-swimlane']};
|
||||
}
|
||||
const projection = { limit: 10, sort: { createdAt: -1 } };
|
||||
|
||||
if (term) {
|
||||
const regex = new RegExp(term, 'i');
|
||||
|
||||
query.$or = [
|
||||
{ title: regex },
|
||||
{ description: regex },
|
||||
];
|
||||
}
|
||||
|
||||
return Swimlanes.find(query, projection);
|
||||
},
|
||||
|
||||
searchLists(term) {
|
||||
check(term, Match.OneOf(String, null, undefined));
|
||||
|
||||
const query = { boardId: this._id };
|
||||
if (this.isTemplatesBoard()) {
|
||||
query.type = 'template-list';
|
||||
query.archived = false;
|
||||
} else {
|
||||
query.type = {$nin: ['template-list']};
|
||||
}
|
||||
const projection = { limit: 10, sort: { createdAt: -1 } };
|
||||
|
||||
if (term) {
|
||||
const regex = new RegExp(term, 'i');
|
||||
|
||||
query.$or = [
|
||||
{ title: regex },
|
||||
{ description: regex },
|
||||
];
|
||||
}
|
||||
|
||||
return Lists.find(query, projection);
|
||||
},
|
||||
|
||||
searchCards(term, excludeLinked) {
|
||||
check(term, Match.OneOf(String, null, undefined));
|
||||
|
||||
@@ -463,6 +553,12 @@ Boards.helpers({
|
||||
if (excludeLinked) {
|
||||
query.linkedId = null;
|
||||
}
|
||||
if (this.isTemplatesBoard()) {
|
||||
query.type = 'template-card';
|
||||
query.archived = false;
|
||||
} else {
|
||||
query.type = {$nin: ['template-card']};
|
||||
}
|
||||
const projection = { limit: 10, sort: { createdAt: -1 } };
|
||||
|
||||
if (term) {
|
||||
@@ -559,6 +655,13 @@ Boards.helpers({
|
||||
});
|
||||
},
|
||||
|
||||
isTemplateBoard() {
|
||||
return this.type === 'template-board';
|
||||
},
|
||||
|
||||
isTemplatesBoard() {
|
||||
return this.type === 'template-container';
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -907,7 +1010,7 @@ if (Meteor.isServer) {
|
||||
* @param {string} userId the ID of the user to retrieve the data
|
||||
* @return_type [{_id: string,
|
||||
title: string}]
|
||||
*/
|
||||
*/
|
||||
JsonRoutes.add('GET', '/api/users/:userId/boards', function (req, res) {
|
||||
try {
|
||||
Authentication.checkLoggedIn(req.userId);
|
||||
@@ -944,7 +1047,7 @@ if (Meteor.isServer) {
|
||||
*
|
||||
* @return_type [{_id: string,
|
||||
title: string}]
|
||||
*/
|
||||
*/
|
||||
JsonRoutes.add('GET', '/api/boards', function (req, res) {
|
||||
try {
|
||||
Authentication.checkUserId(req.userId);
|
||||
@@ -1015,7 +1118,7 @@ if (Meteor.isServer) {
|
||||
*
|
||||
* @return_type {_id: string,
|
||||
defaultSwimlaneId: string}
|
||||
*/
|
||||
*/
|
||||
JsonRoutes.add('POST', '/api/boards', function (req, res) {
|
||||
try {
|
||||
Authentication.checkUserId(req.userId);
|
||||
|
||||
@@ -67,6 +67,12 @@ CardComments.allow({
|
||||
});
|
||||
|
||||
CardComments.helpers({
|
||||
copy(newCardId) {
|
||||
this.cardId = newCardId;
|
||||
delete this._id;
|
||||
CardComments.insert(this);
|
||||
},
|
||||
|
||||
user() {
|
||||
return Users.findOne(this.userId);
|
||||
},
|
||||
|
||||
+61
-2
@@ -246,7 +246,7 @@ Cards.attachSchema(new SimpleSchema({
|
||||
* type of the card
|
||||
*/
|
||||
type: String,
|
||||
defaultValue: '',
|
||||
defaultValue: 'cardType-card',
|
||||
},
|
||||
linkedId: {
|
||||
/**
|
||||
@@ -272,6 +272,61 @@ Cards.allow({
|
||||
});
|
||||
|
||||
Cards.helpers({
|
||||
copy(boardId, swimlaneId, listId) {
|
||||
const oldBoard = Boards.findOne(this.boardId);
|
||||
const oldBoardLabels = oldBoard.labels;
|
||||
// Get old label names
|
||||
const oldCardLabels = _.pluck(_.filter(oldBoardLabels, (label) => {
|
||||
return _.contains(this.labelIds, label._id);
|
||||
}), 'name');
|
||||
|
||||
const newBoard = Boards.findOne(boardId);
|
||||
const newBoardLabels = newBoard.labels;
|
||||
const newCardLabels = _.pluck(_.filter(newBoardLabels, (label) => {
|
||||
return _.contains(oldCardLabels, label.name);
|
||||
}), '_id');
|
||||
|
||||
const oldId = this._id;
|
||||
const oldCard = Cards.findOne(oldId);
|
||||
|
||||
delete this._id;
|
||||
delete this.labelIds;
|
||||
this.labelIds= newCardLabels;
|
||||
this.boardId = boardId;
|
||||
this.swimlaneId = swimlaneId;
|
||||
this.listId = listId;
|
||||
const _id = Cards.insert(this);
|
||||
|
||||
// Copy attachments
|
||||
oldCard.attachments().forEach((att) => {
|
||||
att.cardId = _id;
|
||||
delete att._id;
|
||||
return Attachments.insert(att);
|
||||
});
|
||||
|
||||
// copy checklists
|
||||
Checklists.find({cardId: oldId}).forEach((ch) => {
|
||||
// REMOVE verify copy with arguments
|
||||
ch.copy(_id);
|
||||
});
|
||||
|
||||
// copy subtasks
|
||||
Cards.find({parentId: oldId}).forEach((subtask) => {
|
||||
subtask.parentId = _id;
|
||||
subtask._id = null;
|
||||
// REMOVE verify copy with arguments instead of insert?
|
||||
Cards.insert(subtask);
|
||||
});
|
||||
|
||||
// copy card comments
|
||||
CardComments.find({cardId: oldId}).forEach((cmt) => {
|
||||
// REMOVE verify copy with arguments
|
||||
cmt.copy(_id);
|
||||
});
|
||||
|
||||
return _id;
|
||||
},
|
||||
|
||||
list() {
|
||||
return Lists.findOne(this.listId);
|
||||
},
|
||||
@@ -930,6 +985,10 @@ Cards.helpers({
|
||||
return this.assignedBy;
|
||||
}
|
||||
},
|
||||
|
||||
isTemplateCard() {
|
||||
return this.type === 'template-card';
|
||||
},
|
||||
});
|
||||
|
||||
Cards.mutations({
|
||||
@@ -1230,7 +1289,7 @@ Cards.mutations({
|
||||
|
||||
function cardMove(userId, doc, fieldNames, oldListId, oldSwimlaneId) {
|
||||
if ((_.contains(fieldNames, 'listId') && doc.listId !== oldListId) ||
|
||||
(_.contains(fieldNames, 'swimlaneId') && doc.swimlaneId !== oldSwimlaneId)){
|
||||
(_.contains(fieldNames, 'swimlaneId') && doc.swimlaneId !== oldSwimlaneId)){
|
||||
Activities.insert({
|
||||
userId,
|
||||
oldListId,
|
||||
|
||||
@@ -189,6 +189,7 @@ function publishChekListUncompleted(userId, doc){
|
||||
if (Meteor.isServer) {
|
||||
Meteor.startup(() => {
|
||||
ChecklistItems._collection._ensureIndex({ checklistId: 1 });
|
||||
ChecklistItems._collection._ensureIndex({ cardId: 1 });
|
||||
});
|
||||
|
||||
ChecklistItems.after.update((userId, doc, fieldNames) => {
|
||||
|
||||
@@ -48,6 +48,19 @@ Checklists.attachSchema(new SimpleSchema({
|
||||
}));
|
||||
|
||||
Checklists.helpers({
|
||||
copy(newCardId) {
|
||||
const oldChecklistId = this._id;
|
||||
this._id = null;
|
||||
this.cardId = newCardId;
|
||||
const newChecklistId = Checklists.insert(this);
|
||||
ChecklistItems.find({checklistId: oldChecklistId}).forEach((item) => {
|
||||
item._id = null;
|
||||
item.checklistId = newChecklistId;
|
||||
item.cardId = newCardId;
|
||||
ChecklistItems.insert(item);
|
||||
});
|
||||
},
|
||||
|
||||
itemCount() {
|
||||
return ChecklistItems.find({ checklistId: this._id }).count();
|
||||
},
|
||||
|
||||
@@ -98,9 +98,9 @@ function customFieldCreation(userId, doc){
|
||||
}
|
||||
|
||||
if (Meteor.isServer) {
|
||||
/*Meteor.startup(() => {
|
||||
CustomFields._collection._ensureIndex({ boardId: 1});
|
||||
});*/
|
||||
Meteor.startup(() => {
|
||||
CustomFields._collection._ensureIndex({ boardId: 1 });
|
||||
});
|
||||
|
||||
CustomFields.after.insert((userId, doc) => {
|
||||
customFieldCreation(userId, doc);
|
||||
|
||||
@@ -88,6 +88,10 @@ Integrations.allow({
|
||||
|
||||
//INTEGRATIONS REST API
|
||||
if (Meteor.isServer) {
|
||||
Meteor.startup(() => {
|
||||
Integrations._collection._ensureIndex({ boardId: 1 });
|
||||
});
|
||||
|
||||
/**
|
||||
* @operation get_all_integrations
|
||||
* @summary Get all integrations in board
|
||||
|
||||
@@ -27,6 +27,13 @@ Lists.attachSchema(new SimpleSchema({
|
||||
*/
|
||||
type: String,
|
||||
},
|
||||
swimlaneId: {
|
||||
/**
|
||||
* the swimlane associated to this list. Used for templates
|
||||
*/
|
||||
type: String,
|
||||
defaultValue: '',
|
||||
},
|
||||
createdAt: {
|
||||
/**
|
||||
* creation date
|
||||
@@ -107,6 +114,13 @@ Lists.attachSchema(new SimpleSchema({
|
||||
'saddlebrown', 'paleturquoise', 'mistyrose', 'indigo',
|
||||
],
|
||||
},
|
||||
type: {
|
||||
/**
|
||||
* The type of list
|
||||
*/
|
||||
type: String,
|
||||
defaultValue: 'list',
|
||||
},
|
||||
}));
|
||||
|
||||
Lists.allow({
|
||||
@@ -123,6 +137,36 @@ Lists.allow({
|
||||
});
|
||||
|
||||
Lists.helpers({
|
||||
copy(boardId, swimlaneId) {
|
||||
const oldId = this._id;
|
||||
const oldSwimlaneId = this.swimlaneId || null;
|
||||
this.boardId = boardId;
|
||||
this.swimlaneId = swimlaneId;
|
||||
|
||||
let _id = null;
|
||||
existingListWithSameName = Lists.findOne({
|
||||
boardId,
|
||||
title: this.title,
|
||||
archived: false,
|
||||
});
|
||||
if (existingListWithSameName) {
|
||||
_id = existingListWithSameName._id;
|
||||
} else {
|
||||
delete this._id;
|
||||
delete this.swimlaneId;
|
||||
_id = Lists.insert(this);
|
||||
}
|
||||
|
||||
// Copy all cards in list
|
||||
Cards.find({
|
||||
swimlaneId: oldSwimlaneId,
|
||||
listId: oldId,
|
||||
archived: false,
|
||||
}).forEach((card) => {
|
||||
card.copy(boardId, swimlaneId, _id);
|
||||
});
|
||||
},
|
||||
|
||||
cards(swimlaneId) {
|
||||
const selector = {
|
||||
listId: this._id,
|
||||
@@ -169,6 +213,10 @@ Lists.helpers({
|
||||
return this.color;
|
||||
return '';
|
||||
},
|
||||
|
||||
isTemplateList() {
|
||||
return this.type === 'template-list';
|
||||
},
|
||||
});
|
||||
|
||||
Lists.mutations({
|
||||
@@ -177,10 +225,20 @@ Lists.mutations({
|
||||
},
|
||||
|
||||
archive() {
|
||||
if (this.isTemplateList()) {
|
||||
this.cards().forEach((card) => {
|
||||
return card.archive();
|
||||
});
|
||||
}
|
||||
return { $set: { archived: true } };
|
||||
},
|
||||
|
||||
restore() {
|
||||
if (this.isTemplateList()) {
|
||||
this.allCards().forEach((card) => {
|
||||
return card.restore();
|
||||
});
|
||||
}
|
||||
return { $set: { archived: false } };
|
||||
},
|
||||
|
||||
|
||||
@@ -78,6 +78,13 @@ Swimlanes.attachSchema(new SimpleSchema({
|
||||
}
|
||||
},
|
||||
},
|
||||
type: {
|
||||
/**
|
||||
* The type of swimlane
|
||||
*/
|
||||
type: String,
|
||||
defaultValue: 'swimlane',
|
||||
},
|
||||
}));
|
||||
|
||||
Swimlanes.allow({
|
||||
@@ -94,6 +101,30 @@ Swimlanes.allow({
|
||||
});
|
||||
|
||||
Swimlanes.helpers({
|
||||
copy(boardId) {
|
||||
const oldId = this._id;
|
||||
const oldBoardId = this.boardId;
|
||||
this.boardId = boardId;
|
||||
delete this._id;
|
||||
const _id = Swimlanes.insert(this);
|
||||
|
||||
const query = {
|
||||
swimlaneId: {$in: [oldId, '']},
|
||||
archived: false,
|
||||
};
|
||||
if (oldBoardId) {
|
||||
query.boardId = oldBoardId;
|
||||
}
|
||||
|
||||
// Copy all lists in swimlane
|
||||
Lists.find(query).forEach((list) => {
|
||||
list.type = 'list';
|
||||
list.swimlaneId = oldId;
|
||||
list.boardId = boardId;
|
||||
list.copy(boardId, _id);
|
||||
});
|
||||
},
|
||||
|
||||
cards() {
|
||||
return Cards.find(Filter.mongoSelector({
|
||||
swimlaneId: this._id,
|
||||
@@ -101,6 +132,18 @@ Swimlanes.helpers({
|
||||
}), { sort: ['sort'] });
|
||||
},
|
||||
|
||||
lists() {
|
||||
return Lists.find({
|
||||
boardId: this.boardId,
|
||||
swimlaneId: {$in: [this._id, '']},
|
||||
archived: false,
|
||||
}, { sort: ['sort'] });
|
||||
},
|
||||
|
||||
myLists() {
|
||||
return Lists.find({ swimlaneId: this._id });
|
||||
},
|
||||
|
||||
allCards() {
|
||||
return Cards.find({ swimlaneId: this._id });
|
||||
},
|
||||
@@ -114,6 +157,29 @@ Swimlanes.helpers({
|
||||
return this.color;
|
||||
return '';
|
||||
},
|
||||
|
||||
isTemplateSwimlane() {
|
||||
return this.type === 'template-swimlane';
|
||||
},
|
||||
|
||||
isTemplateContainer() {
|
||||
return this.type === 'template-container';
|
||||
},
|
||||
|
||||
isListTemplatesSwimlane() {
|
||||
const user = Users.findOne(Meteor.userId());
|
||||
return user.profile.listTemplatesSwimlaneId === this._id;
|
||||
},
|
||||
|
||||
isCardTemplatesSwimlane() {
|
||||
const user = Users.findOne(Meteor.userId());
|
||||
return user.profile.cardTemplatesSwimlaneId === this._id;
|
||||
},
|
||||
|
||||
isBoardTemplatesSwimlane() {
|
||||
const user = Users.findOne(Meteor.userId());
|
||||
return user.profile.boardTemplatesSwimlaneId === this._id;
|
||||
},
|
||||
});
|
||||
|
||||
Swimlanes.mutations({
|
||||
@@ -122,10 +188,20 @@ Swimlanes.mutations({
|
||||
},
|
||||
|
||||
archive() {
|
||||
if (this.isTemplateSwimlane()) {
|
||||
this.myLists().forEach((list) => {
|
||||
return list.archive();
|
||||
});
|
||||
}
|
||||
return { $set: { archived: true } };
|
||||
},
|
||||
|
||||
restore() {
|
||||
if (this.isTemplateSwimlane()) {
|
||||
this.myLists().forEach((list) => {
|
||||
return list.restore();
|
||||
});
|
||||
}
|
||||
return { $set: { archived: false } };
|
||||
},
|
||||
|
||||
|
||||
+84
-2
@@ -159,6 +159,34 @@ Users.attachSchema(new SimpleSchema({
|
||||
'board-view-cal',
|
||||
],
|
||||
},
|
||||
'profile.templatesBoardId': {
|
||||
/**
|
||||
* Reference to the templates board
|
||||
*/
|
||||
type: String,
|
||||
defaultValue: '',
|
||||
},
|
||||
'profile.cardTemplatesSwimlaneId': {
|
||||
/**
|
||||
* Reference to the card templates swimlane Id
|
||||
*/
|
||||
type: String,
|
||||
defaultValue: '',
|
||||
},
|
||||
'profile.listTemplatesSwimlaneId': {
|
||||
/**
|
||||
* Reference to the list templates swimlane Id
|
||||
*/
|
||||
type: String,
|
||||
defaultValue: '',
|
||||
},
|
||||
'profile.boardTemplatesSwimlaneId': {
|
||||
/**
|
||||
* Reference to the board templates swimlane Id
|
||||
*/
|
||||
type: String,
|
||||
defaultValue: '',
|
||||
},
|
||||
services: {
|
||||
/**
|
||||
* services field of the user
|
||||
@@ -328,6 +356,14 @@ Users.helpers({
|
||||
const profile = this.profile || {};
|
||||
return profile.language || 'en';
|
||||
},
|
||||
|
||||
getTemplatesBoardId() {
|
||||
return this.profile.templatesBoardId;
|
||||
},
|
||||
|
||||
getTemplatesBoardSlug() {
|
||||
return Boards.findOne(this.profile.templatesBoardId).slug;
|
||||
},
|
||||
});
|
||||
|
||||
Users.mutations({
|
||||
@@ -675,7 +711,6 @@ if (Meteor.isServer) {
|
||||
CollectionHooks.getUserId = () => {
|
||||
return fakeUserId.get() || getUserId();
|
||||
};
|
||||
/*
|
||||
if (!isSandstorm) {
|
||||
Users.after.insert((userId, doc) => {
|
||||
const fakeUser = {
|
||||
@@ -685,6 +720,7 @@ if (Meteor.isServer) {
|
||||
};
|
||||
|
||||
fakeUserId.withValue(doc._id, () => {
|
||||
/*
|
||||
// Insert the Welcome Board
|
||||
Boards.insert({
|
||||
title: TAPi18n.__('welcome-board'),
|
||||
@@ -701,10 +737,56 @@ if (Meteor.isServer) {
|
||||
Lists.insert({title: TAPi18n.__(title), boardId, sort: titleIndex}, fakeUser);
|
||||
});
|
||||
});
|
||||
*/
|
||||
|
||||
Boards.insert({
|
||||
title: TAPi18n.__('templates'),
|
||||
permission: 'private',
|
||||
type: 'template-container',
|
||||
}, fakeUser, (err, boardId) => {
|
||||
|
||||
// Insert the reference to our templates board
|
||||
Users.update(fakeUserId.get(), {$set: {'profile.templatesBoardId': boardId}});
|
||||
|
||||
// Insert the card templates swimlane
|
||||
Swimlanes.insert({
|
||||
title: TAPi18n.__('card-templates-swimlane'),
|
||||
boardId,
|
||||
sort: 1,
|
||||
type: 'template-container',
|
||||
}, fakeUser, (err, swimlaneId) => {
|
||||
|
||||
// Insert the reference to out card templates swimlane
|
||||
Users.update(fakeUserId.get(), {$set: {'profile.cardTemplatesSwimlaneId': swimlaneId}});
|
||||
});
|
||||
|
||||
// Insert the list templates swimlane
|
||||
Swimlanes.insert({
|
||||
title: TAPi18n.__('list-templates-swimlane'),
|
||||
boardId,
|
||||
sort: 2,
|
||||
type: 'template-container',
|
||||
}, fakeUser, (err, swimlaneId) => {
|
||||
|
||||
// Insert the reference to out list templates swimlane
|
||||
Users.update(fakeUserId.get(), {$set: {'profile.listTemplatesSwimlaneId': swimlaneId}});
|
||||
});
|
||||
|
||||
// Insert the board templates swimlane
|
||||
Swimlanes.insert({
|
||||
title: TAPi18n.__('board-templates-swimlane'),
|
||||
boardId,
|
||||
sort: 3,
|
||||
type: 'template-container',
|
||||
}, fakeUser, (err, swimlaneId) => {
|
||||
|
||||
// Insert the reference to out board templates swimlane
|
||||
Users.update(fakeUserId.get(), {$set: {'profile.boardTemplatesSwimlaneId': swimlaneId}});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
*/
|
||||
|
||||
Users.after.insert((userId, doc) => {
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "wekan",
|
||||
"version": "v2.28.0",
|
||||
"version": "v2.32.0",
|
||||
"description": "Open-Source kanban",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -22,10 +22,10 @@ const pkgdef :Spk.PackageDefinition = (
|
||||
appTitle = (defaultText = "Wekan"),
|
||||
# The name of the app as it is displayed to the user.
|
||||
|
||||
appVersion = 230,
|
||||
appVersion = 234,
|
||||
# Increment this for every release.
|
||||
|
||||
appMarketingVersion = (defaultText = "2.28.0~2019-02-27"),
|
||||
appMarketingVersion = (defaultText = "2.32.0~2019-02-28"),
|
||||
# Human-readable presentation of the app version.
|
||||
|
||||
minUpgradableAppVersion = 0,
|
||||
|
||||
@@ -422,3 +422,98 @@ Migrations.add('add-defaultAuthenticationMethod', () => {
|
||||
},
|
||||
}, noValidateMulti);
|
||||
});
|
||||
|
||||
Migrations.add('add-templates', () => {
|
||||
Boards.update({
|
||||
type: {
|
||||
$exists: false,
|
||||
},
|
||||
}, {
|
||||
$set: {
|
||||
type: 'board',
|
||||
},
|
||||
}, noValidateMulti);
|
||||
Swimlanes.update({
|
||||
type: {
|
||||
$exists: false,
|
||||
},
|
||||
}, {
|
||||
$set: {
|
||||
type: 'swimlane',
|
||||
},
|
||||
}, noValidateMulti);
|
||||
Lists.update({
|
||||
type: {
|
||||
$exists: false,
|
||||
},
|
||||
swimlaneId: {
|
||||
$exists: false,
|
||||
},
|
||||
}, {
|
||||
$set: {
|
||||
type: 'list',
|
||||
swimlaneId: '',
|
||||
},
|
||||
}, noValidateMulti);
|
||||
Users.find({
|
||||
'profile.templatesBoardId': {
|
||||
$exists: false,
|
||||
},
|
||||
}).forEach((user) => {
|
||||
// Create board and swimlanes
|
||||
Boards.insert({
|
||||
title: TAPi18n.__('templates'),
|
||||
permission: 'private',
|
||||
type: 'template-container',
|
||||
members: [
|
||||
{
|
||||
userId: user._id,
|
||||
isAdmin: true,
|
||||
isActive: true,
|
||||
isNoComments: false,
|
||||
isCommentOnly: false,
|
||||
},
|
||||
],
|
||||
}, (err, boardId) => {
|
||||
|
||||
// Insert the reference to our templates board
|
||||
Users.update(user._id, {$set: {'profile.templatesBoardId': boardId}});
|
||||
|
||||
// Insert the card templates swimlane
|
||||
Swimlanes.insert({
|
||||
title: TAPi18n.__('card-templates-swimlane'),
|
||||
boardId,
|
||||
sort: 1,
|
||||
type: 'template-container',
|
||||
}, (err, swimlaneId) => {
|
||||
|
||||
// Insert the reference to out card templates swimlane
|
||||
Users.update(user._id, {$set: {'profile.cardTemplatesSwimlaneId': swimlaneId}});
|
||||
});
|
||||
|
||||
// Insert the list templates swimlane
|
||||
Swimlanes.insert({
|
||||
title: TAPi18n.__('list-templates-swimlane'),
|
||||
boardId,
|
||||
sort: 2,
|
||||
type: 'template-container',
|
||||
}, (err, swimlaneId) => {
|
||||
|
||||
// Insert the reference to out list templates swimlane
|
||||
Users.update(user._id, {$set: {'profile.listTemplatesSwimlaneId': swimlaneId}});
|
||||
});
|
||||
|
||||
// Insert the board templates swimlane
|
||||
Swimlanes.insert({
|
||||
title: TAPi18n.__('board-templates-swimlane'),
|
||||
boardId,
|
||||
sort: 3,
|
||||
type: 'template-container',
|
||||
}, (err, swimlaneId) => {
|
||||
|
||||
// Insert the reference to out board templates swimlane
|
||||
Users.update(user._id, {$set: {'profile.boardTemplatesSwimlaneId': swimlaneId}});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -32,6 +32,7 @@ Meteor.publish('boards', function() {
|
||||
color: 1,
|
||||
members: 1,
|
||||
permission: 1,
|
||||
type: 1,
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -59,6 +60,7 @@ Meteor.publish('archivedBoards', function() {
|
||||
});
|
||||
|
||||
Meteor.publishRelations('board', function(boardId) {
|
||||
this.unblock();
|
||||
check(boardId, String);
|
||||
const thisUserId = this.userId;
|
||||
|
||||
@@ -71,7 +73,8 @@ Meteor.publishRelations('board', function(boardId) {
|
||||
{ permission: 'public' },
|
||||
{ members: { $elemMatch: { userId: this.userId, isActive: true }}},
|
||||
],
|
||||
}, { limit: 1 }), function(boardId, board) {
|
||||
// Sort required to ensure oplog usage
|
||||
}, { limit: 1, sort: { _id: 1 } }), function(boardId, board) {
|
||||
this.cursor(Lists.find({ boardId }));
|
||||
this.cursor(Swimlanes.find({ boardId }));
|
||||
this.cursor(Integrations.find({ boardId }));
|
||||
@@ -98,24 +101,47 @@ Meteor.publishRelations('board', function(boardId) {
|
||||
//
|
||||
// And in the meantime our code below works pretty well -- it's not even a
|
||||
// hack!
|
||||
|
||||
// Gather queries and send in bulk
|
||||
const cardComments = this.join(CardComments);
|
||||
cardComments.selector = (_ids) => ({ cardId: _ids });
|
||||
const attachments = this.join(Attachments);
|
||||
attachments.selector = (_ids) => ({ cardId: _ids });
|
||||
const checklists = this.join(Checklists);
|
||||
checklists.selector = (_ids) => ({ cardId: _ids });
|
||||
const checklistItems = this.join(ChecklistItems);
|
||||
checklistItems.selector = (_ids) => ({ cardId: _ids });
|
||||
const parentCards = this.join(Cards);
|
||||
parentCards.selector = (_ids) => ({ parentId: _ids });
|
||||
const boards = this.join(Boards);
|
||||
const subCards = this.join(Cards);
|
||||
|
||||
this.cursor(Cards.find({ boardId }), function(cardId, card) {
|
||||
if (card.type === 'cardType-linkedCard') {
|
||||
const impCardId = card.linkedId;
|
||||
this.cursor(Cards.find({ _id: impCardId }));
|
||||
this.cursor(CardComments.find({ cardId: impCardId }));
|
||||
this.cursor(Attachments.find({ cardId: impCardId }));
|
||||
this.cursor(Checklists.find({ cardId: impCardId }));
|
||||
this.cursor(ChecklistItems.find({ cardId: impCardId }));
|
||||
subCards.push(impCardId);
|
||||
cardComments.push(impCardId);
|
||||
attachments.push(impCardId);
|
||||
checklists.push(impCardId);
|
||||
checklistItems.push(impCardId);
|
||||
} else if (card.type === 'cardType-linkedBoard') {
|
||||
this.cursor(Boards.find({ _id: card.linkedId}));
|
||||
boards.push(card.linkedId);
|
||||
}
|
||||
this.cursor(CardComments.find({ cardId }));
|
||||
this.cursor(Attachments.find({ cardId }));
|
||||
this.cursor(Checklists.find({ cardId }));
|
||||
this.cursor(ChecklistItems.find({ cardId }));
|
||||
this.cursor(Cards.find({ parentId: cardId }));
|
||||
cardComments.push(cardId);
|
||||
attachments.push(cardId);
|
||||
checklists.push(cardId);
|
||||
checklistItems.push(cardId);
|
||||
parentCards.push(cardId);
|
||||
});
|
||||
|
||||
// Send bulk queries for all found ids
|
||||
subCards.send();
|
||||
cardComments.send();
|
||||
attachments.send();
|
||||
checklists.send();
|
||||
checklistItems.send();
|
||||
boards.send();
|
||||
|
||||
if (board.members) {
|
||||
// Board members. This publication also includes former board members that
|
||||
// aren't members anymore but may have some activities attached to them in
|
||||
|
||||
Reference in new issue
Block a user