Merge branch 'harryadel-collection-mutations-migrations'

This commit is contained in:
Lauri Ojansivu 2026-01-24 01:49:15 +02:00
commit 904211dba5
38 changed files with 752 additions and 1355 deletions

View file

@ -20,7 +20,6 @@ reywood:publish-composite
dburles:collection-helpers
idmontie:migrations
mongo@1.16.8
mquandalle:collection-mutations
# Account system
accounts-password@2.4.0

View file

@ -79,7 +79,6 @@ mongo-dev-server@1.1.0
mongo-id@1.0.8
mongo-livedata@1.0.12
mquandalle:autofocus@1.0.0
mquandalle:collection-mutations@0.1.0
mquandalle:jade@0.4.9
mquandalle:jade-compiler@0.4.5
msavin:usercache@1.8.0

View file

@ -18,7 +18,7 @@ ENV BUILD_DEPS="apt-utils gnupg gosu wget bzip2 g++ curl libarchive-tools build-
ENV \
DEBUG=false \
NODE_VERSION=v14.21.4 \
METEOR_RELEASE=METEOR@2.14 \
METEOR_RELEASE=METEOR@2.16 \
USE_EDGE=false \
METEOR_EDGE=1.5-beta.17 \
NPM_VERSION=6.14.17 \
@ -222,8 +222,8 @@ cd /home/wekan
chown --recursive wekan:wekan /home/wekan
echo "Starting meteor ${METEOR_RELEASE} installation... \n"
#gosu wekan:wekan curl https://install.meteor.com/ | /bin/sh
# Specify Meteor version 2.14 to be compatible: https://github.com/wekan/wekan/pull/5816/files
#gosu wekan:wekan npm -g install meteor@2.14 --unsafe-perm
# Specify Meteor version 2.16 to be compatible: https://github.com/wekan/wekan/pull/5816/files
#gosu wekan:wekan npm -g install meteor@2.16 --unsafe-perm
#mv /root/.meteor /home/wekan/
#chown --recursive wekan:wekan /home/wekan/.meteor

View file

@ -23,7 +23,7 @@ BlazeComponent.extendComponent({
events() {
return [
{
'click .js-restore-board'() {
async 'click .js-restore-board'() {
// TODO : Make isSandstorm variable global
const isSandstorm =
Meteor.settings &&
@ -31,13 +31,13 @@ BlazeComponent.extendComponent({
Meteor.settings.public.sandstorm;
if (isSandstorm && Utils.getCurrentBoardId()) {
const currentBoard = Utils.getCurrentBoard();
currentBoard.archive();
await currentBoard.archive();
}
const board = this.currentData();
board.restore();
await board.restore();
Utils.goBoardId(board._id);
},
'click .js-delete-board': Popup.afterConfirm('boardDelete', function() {
'click .js-delete-board': Popup.afterConfirm('boardDelete', async function() {
Popup.back();
const isSandstorm =
Meteor.settings &&
@ -45,9 +45,9 @@ BlazeComponent.extendComponent({
Meteor.settings.public.sandstorm;
if (isSandstorm && Utils.getCurrentBoardId()) {
const currentBoard = Utils.getCurrentBoard();
Boards.remove(currentBoard._id);
await Boards.removeAsync(currentBoard._id);
}
Boards.remove(this._id);
await Boards.removeAsync(this._id);
FlowRouter.go('home');
}),
},

View file

@ -10,7 +10,7 @@ const UPCLS = 'fa-sort-up';
const sortCardsBy = new ReactiveVar('');
Template.boardChangeTitlePopup.events({
submit(event, templateInstance) {
async submit(event, templateInstance) {
const newTitle = templateInstance
.$('.js-board-name')
.val()
@ -20,8 +20,8 @@ Template.boardChangeTitlePopup.events({
.val()
.trim();
if (newTitle) {
this.rename(newTitle);
this.setDescription(newDesc);
await this.rename(newTitle);
await this.setDescription(newDesc);
Popup.back();
}
event.preventDefault();
@ -364,10 +364,10 @@ const CreateBoard = BlazeComponent.extendComponent({
}).register('createTemplateContainerPopup');
(class HeaderBarCreateBoard extends CreateBoard {
onSubmit(event) {
async onSubmit(event) {
super.onSubmit(event);
// Immediately star boards crated with the headerbar popup.
ReactiveCache.getCurrentUser().toggleBoardStar(this.boardId.get());
await ReactiveCache.getCurrentUser().toggleBoardStar(this.boardId.get());
}
}.register('headerBarCreateBoardPopup'));

View file

@ -143,7 +143,7 @@ BlazeComponent.extendComponent({
ui.placeholder.height(ui.helper.height());
EscapeActions.executeUpTo('popup-close');
},
stop(evt, ui) {
async stop(evt, ui) {
const prevBoardDom = ui.item.prev('.js-board').get(0);
const nextBoardDom = ui.item.next('.js-board').get(0);
const sortIndex = Utils.calculateIndex(prevBoardDom, nextBoardDom, 1);
@ -153,7 +153,7 @@ BlazeComponent.extendComponent({
$boards.sortable('cancel');
const currentUser = ReactiveCache.getCurrentUser();
if (currentUser && typeof currentUser.setBoardSortIndex === 'function') {
currentUser.setBoardSortIndex(board._id, sortIndex.base);
await currentUser.setBoardSortIndex(board._id, sortIndex.base);
}
},
});

View file

@ -17,10 +17,10 @@ BlazeComponent.extendComponent({
events() {
return [
{
'submit .js-card-description'(event) {
async 'submit .js-card-description'(event) {
event.preventDefault();
const description = this.currentComponent().getValue();
this.data().setDescription(description);
await this.data().setDescription(description);
},
// Pressing Ctrl+Enter should submit the form
'keydown form textarea'(evt) {

View file

@ -462,18 +462,18 @@ BlazeComponent.extendComponent({
const currentMode = Utils.getMobileMode();
Utils.setMobileMode(!currentMode);
},
'submit .js-card-description'(event) {
async 'submit .js-card-description'(event) {
event.preventDefault();
const description = this.currentComponent().getValue();
this.data().setDescription(description);
await this.data().setDescription(description);
},
'submit .js-card-details-title'(event) {
async 'submit .js-card-details-title'(event) {
event.preventDefault();
const title = this.currentComponent().getValue().trim();
if (title) {
this.data().setTitle(title);
await this.data().setTitle(title);
} else {
this.data().setTitle('');
await this.data().setTitle('');
}
},
'submit .js-card-details-assigner'(event) {
@ -500,23 +500,23 @@ BlazeComponent.extendComponent({
this.find('button[type=submit]').click();
}
},
'submit .js-card-details-sort'(event) {
async 'submit .js-card-details-sort'(event) {
event.preventDefault();
const sort = parseFloat(this.currentComponent()
.getValue()
.trim());
if (!Number.isNaN(sort)) {
let card = this.data();
card.move(card.boardId, card.swimlaneId, card.listId, sort);
await card.move(card.boardId, card.swimlaneId, card.listId, sort);
}
},
'change .js-select-card-details-lists'(event) {
async 'change .js-select-card-details-lists'(event) {
let card = this.data();
const listSelect = this.$('.js-select-card-details-lists')[0];
const listId = listSelect.options[listSelect.selectedIndex].value;
const minOrder = card.getMinSort(listId, card.swimlaneId);
card.move(card.boardId, card.swimlaneId, listId, minOrder - 1);
await card.move(card.boardId, card.swimlaneId, listId, minOrder - 1);
},
'click .js-go-to-linked-card'() {
Utils.goCardId(this.data().linkedId);
@ -554,8 +554,8 @@ BlazeComponent.extendComponent({
Session.set('cardDetailsIsDragging', false);
Session.set('cardDetailsIsMouseDown', false);
},
'click #toggleHideCheckedChecklistItems'() {
this.data().toggleHideCheckedChecklistItems();
async 'click #toggleHideCheckedChecklistItems'() {
await this.data().toggleHideCheckedChecklistItems();
},
'click #toggleCustomFieldsGridButton'() {
Meteor.call('toggleCustomFieldsGrid');
@ -862,21 +862,21 @@ Template.cardDetailsActionsPopup.events({
'click .js-convert-checklist-item-to-card': Popup.open('convertChecklistItemToCard'),
'click .js-copy-checklist-cards': Popup.open('copyManyCards'),
'click .js-set-card-color': Popup.open('setCardColor'),
'click .js-move-card-to-top'(event) {
async 'click .js-move-card-to-top'(event) {
event.preventDefault();
const minOrder = this.getMinSort();
this.move(this.boardId, this.swimlaneId, this.listId, minOrder - 1);
await this.move(this.boardId, this.swimlaneId, this.listId, minOrder - 1);
Popup.back();
},
'click .js-move-card-to-bottom'(event) {
async 'click .js-move-card-to-bottom'(event) {
event.preventDefault();
const maxOrder = this.getMaxSort();
this.move(this.boardId, this.swimlaneId, this.listId, maxOrder + 1);
await this.move(this.boardId, this.swimlaneId, this.listId, maxOrder + 1);
Popup.back();
},
'click .js-archive': Popup.afterConfirm('cardArchive', function () {
'click .js-archive': Popup.afterConfirm('cardArchive', async function () {
Popup.close();
this.archive();
await this.archive();
Utils.goBoardId(this.boardId);
}),
'click .js-more': Popup.open('cardMore'),
@ -1011,11 +1011,11 @@ Template.editCardAssignerForm.events({
const ret = ReactiveCache.getCurrentUser().getMoveAndCopyDialogOptions();
return ret;
}
setDone(cardId, options) {
async setDone(cardId, options) {
ReactiveCache.getCurrentUser().setMoveAndCopyDialogOption(this.currentBoardId, options);
const card = this.data();
let sortIndex = 0;
if (cardId) {
const targetCard = ReactiveCache.getCard(cardId);
if (targetCard) {
@ -1030,8 +1030,8 @@ Template.editCardAssignerForm.events({
// If no card selected, move to end
sortIndex = card.getMaxSort(options.listId, options.swimlaneId) + 1;
}
card.move(options.boardId, options.swimlaneId, options.listId, sortIndex);
await card.move(options.boardId, options.swimlaneId, options.listId, sortIndex);
}
}).register('moveCardPopup');
@ -1041,7 +1041,7 @@ Template.editCardAssignerForm.events({
const ret = ReactiveCache.getCurrentUser().getMoveAndCopyDialogOptions();
return ret;
}
setDone(cardId, options) {
async setDone(cardId, options) {
ReactiveCache.getCurrentUser().setMoveAndCopyDialogOption(this.currentBoardId, options);
const card = this.data();
@ -1056,7 +1056,7 @@ Template.editCardAssignerForm.events({
if (newCardId) {
const newCard = ReactiveCache.getCard(newCardId);
let sortIndex = 0;
if (cardId) {
const targetCard = ReactiveCache.getCard(cardId);
if (targetCard) {
@ -1071,8 +1071,8 @@ Template.editCardAssignerForm.events({
// If no card selected, copy to end
sortIndex = newCard.getMaxSort(options.listId, options.swimlaneId) + 1;
}
newCard.move(options.boardId, options.swimlaneId, options.listId, sortIndex);
await newCard.move(options.boardId, options.swimlaneId, options.listId, sortIndex);
}
// In case the filter is active we need to add the newly inserted card in
@ -1090,7 +1090,7 @@ Template.editCardAssignerForm.events({
const ret = ReactiveCache.getCurrentUser().getMoveAndCopyDialogOptions();
return ret;
}
setDone(cardId, options) {
async setDone(cardId, options) {
ReactiveCache.getCurrentUser().setMoveAndCopyDialogOption(this.currentBoardId, options);
const card = this.data();
@ -1106,7 +1106,7 @@ Template.editCardAssignerForm.events({
sort: 0,
});
const newCard = ReactiveCache.getCard(_id);
let sortIndex = 0;
if (cardId) {
const targetCard = ReactiveCache.getCard(cardId);
@ -1121,8 +1121,8 @@ Template.editCardAssignerForm.events({
} else {
sortIndex = newCard.getMaxSort(options.listId, options.swimlaneId) + 1;
}
newCard.move(options.boardId, options.swimlaneId, options.listId, sortIndex);
await newCard.move(options.boardId, options.swimlaneId, options.listId, sortIndex);
Filter.addException(_id);
}
@ -1135,7 +1135,7 @@ Template.editCardAssignerForm.events({
const ret = ReactiveCache.getCurrentUser().getMoveAndCopyDialogOptions();
return ret;
}
setDone(cardId, options) {
async setDone(cardId, options) {
ReactiveCache.getCurrentUser().setMoveAndCopyDialogOption(this.currentBoardId, options);
const card = this.data();
@ -1151,7 +1151,7 @@ Template.editCardAssignerForm.events({
if (newCardId) {
const newCard = ReactiveCache.getCard(newCardId);
let sortIndex = 0;
if (cardId) {
const targetCard = ReactiveCache.getCard(cardId);
if (targetCard) {
@ -1165,8 +1165,8 @@ Template.editCardAssignerForm.events({
} else {
sortIndex = newCard.getMaxSort(options.listId, options.swimlaneId) + 1;
}
newCard.move(options.boardId, options.swimlaneId, options.listId, sortIndex);
await newCard.move(options.boardId, options.swimlaneId, options.listId, sortIndex);
}
// In case the filter is active we need to add the newly inserted card in
@ -1202,14 +1202,14 @@ BlazeComponent.extendComponent({
'click .js-palette-color'() {
this.currentColor.set(this.currentData().color);
},
'click .js-submit'(event) {
async 'click .js-submit'(event) {
event.preventDefault();
this.currentCard.setColor(this.currentColor.get());
await this.currentCard.setColor(this.currentColor.get());
Popup.back();
},
'click .js-remove-color'(event) {
async 'click .js-remove-color'(event) {
event.preventDefault();
this.currentCard.setColor(null);
await this.currentCard.setColor(null);
Popup.back();
},
},
@ -1240,21 +1240,21 @@ BlazeComponent.extendComponent({
const color = colorClass ? colorClass.replace('card-details-', '') : null;
this.currentColor.set(color);
},
'click .js-submit'(event) {
async 'click .js-submit'(event) {
event.preventDefault();
const color = this.currentColor.get();
// Use MultiSelection to get selected cards and set color on each
ReactiveCache.getCards(MultiSelection.getMongoSelector()).forEach(card => {
card.setColor(color);
});
for (const card of ReactiveCache.getCards(MultiSelection.getMongoSelector())) {
await card.setColor(color);
}
Popup.back();
},
'click .js-remove-color'(event) {
async 'click .js-remove-color'(event) {
event.preventDefault();
// Use MultiSelection to get selected cards and remove color from each
ReactiveCache.getCards(MultiSelection.getMongoSelector()).forEach(card => {
card.setColor(null);
});
for (const card of ReactiveCache.getCards(MultiSelection.getMongoSelector())) {
await card.setColor(null);
}
Popup.back();
},
},
@ -1866,7 +1866,7 @@ BlazeComponent.extendComponent({
// Close the card details pane by pressing escape
EscapeActions.register(
'detailsPane',
() => {
async () => {
// if card description diverges from database due to editing
// ask user whether changes should be applied
if (ReactiveCache.getCurrentUser()) {
@ -1874,7 +1874,7 @@ EscapeActions.register(
currentDescription = document.getElementsByClassName("editor js-new-description-input").item(0)
if (currentDescription?.value && !(currentDescription.value === Utils.getCurrentCard().getDescription())) {
if (confirm(TAPi18n.__('rescue-card-description-dialogue'))) {
Utils.getCurrentCard().setDescription(document.getElementsByClassName("editor js-new-description-input").item(0).value);
await Utils.getCurrentCard().setDescription(document.getElementsByClassName("editor js-new-description-input").item(0).value);
// Save it!
console.log(document.getElementsByClassName("editor js-new-description-input").item(0).value);
console.log("current description", Utils.getCurrentCard().getDescription());

View file

@ -157,21 +157,21 @@ BlazeComponent.extendComponent({
textarea.focus();
},
editChecklist(event) {
async editChecklist(event) {
event.preventDefault();
const textarea = this.find('textarea.js-edit-checklist-item');
const title = textarea.value.trim();
const checklist = this.currentData().checklist;
checklist.setTitle(title);
await checklist.setTitle(title);
},
editChecklistItem(event) {
async editChecklistItem(event) {
event.preventDefault();
const textarea = this.find('textarea.js-edit-checklist-item');
const title = textarea.value.trim();
const item = this.currentData().item;
item.setTitle(title);
await item.setTitle(title);
},
pressKey(event) {
@ -321,20 +321,20 @@ BlazeComponent.extendComponent({
},
'click .js-move-checklist': Popup.open('moveChecklist'),
'click .js-copy-checklist': Popup.open('copyChecklist'),
'click .js-hide-checked-checklist-items'(event) {
async 'click .js-hide-checked-checklist-items'(event) {
event.preventDefault();
this.data().checklist.toggleHideCheckedChecklistItems();
await this.data().checklist.toggleHideCheckedChecklistItems();
Popup.back();
},
'click .js-hide-all-checklist-items'(event) {
async 'click .js-hide-all-checklist-items'(event) {
event.preventDefault();
this.data().checklist.toggleHideAllChecklistItems();
await this.data().checklist.toggleHideAllChecklistItems();
Popup.back();
},
'click .js-toggle-show-checklist-at-minicard'(event) {
async 'click .js-toggle-show-checklist-at-minicard'(event) {
event.preventDefault();
const checklist = this.data().checklist;
checklist.toggleShowChecklistAtMinicard();
await checklist.toggleShowChecklistAtMinicard();
Popup.back();
},
}
@ -365,11 +365,11 @@ Template.checklistItemDetail.helpers({
});
BlazeComponent.extendComponent({
toggleItem() {
async toggleItem() {
const checklist = this.currentData().checklist;
const item = this.currentData().item;
if (checklist && item && item._id) {
item.toggleItem();
await item.toggleItem();
}
},
events() {

View file

@ -91,10 +91,10 @@ BlazeComponent.extendComponent({
}
},
toggleChecklistItem() {
async toggleChecklistItem() {
const item = this.currentData();
if (item && item._id) {
item.toggleItem();
await item.toggleItem();
}
},
@ -319,15 +319,15 @@ Template.cardDetailsActionsPopup.events({
this.move(this.boardId, this.swimlaneId, this.listId, minOrder - 1);
Popup.back();
},
'click .js-move-card-to-bottom'(event) {
async 'click .js-move-card-to-bottom'(event) {
event.preventDefault();
const maxOrder = this.getMaxSort();
this.move(this.boardId, this.swimlaneId, this.listId, maxOrder + 1);
await this.move(this.boardId, this.swimlaneId, this.listId, maxOrder + 1);
Popup.back();
},
'click .js-archive': Popup.afterConfirm('cardArchive', function () {
'click .js-archive': Popup.afterConfirm('cardArchive', async function () {
Popup.close();
this.archive();
await this.archive();
Utils.goBoardId(this.boardId);
}),
'click .js-toggle-watch-card'() {

View file

@ -62,10 +62,10 @@ BlazeComponent.extendComponent({
textarea.focus();
},
deleteSubtask() {
async deleteSubtask() {
const subtask = this.currentData().subtask;
if (subtask && subtask._id) {
subtask.archive();
await subtask.archive();
}
},
@ -73,12 +73,12 @@ BlazeComponent.extendComponent({
return ReactiveCache.getCurrentUser().isBoardAdmin();
},
editSubtask(event) {
async editSubtask(event) {
event.preventDefault();
const textarea = this.find('textarea.js-edit-subtask-item');
const title = textarea.value.trim();
const subtask = this.currentData().subtask;
subtask.setTitle(title);
await subtask.setTitle(title);
},
pressKey(event) {
@ -105,10 +105,10 @@ BlazeComponent.extendComponent({
}).register('subtasks');
BlazeComponent.extendComponent({
toggleItem() {
async toggleItem() {
const item = this.currentData().item;
if (item && item._id) {
item.toggleItem();
await item.toggleItem();
}
},
events() {
@ -138,11 +138,11 @@ BlazeComponent.extendComponent({
});
}
},
'click .js-delete-subtask' : Popup.afterConfirm('subtaskDelete', function () {
'click .js-delete-subtask' : Popup.afterConfirm('subtaskDelete', async function () {
Popup.back(2);
const subtask = this.subtask;
if (subtask && subtask._id) {
subtask.archive();
await subtask.archive();
}
}),
}

View file

@ -22,14 +22,14 @@ BlazeComponent.extendComponent({
isBoardAdmin() {
return ReactiveCache.getCurrentUser().isBoardAdmin();
},
starred(check = undefined) {
async starred(check = undefined) {
const list = Template.currentData();
const status = list.isStarred();
if (check === undefined) {
// just check
return status;
} else {
list.star(!status);
await list.star(!status);
return !status;
}
},
@ -45,14 +45,14 @@ BlazeComponent.extendComponent({
return next;
}
},
editTitle(event) {
async editTitle(event) {
event.preventDefault();
const newTitle = this.childComponents('inlinedForm')[0]
.getValue()
.trim();
const list = this.currentData();
if (newTitle) {
list.rename(newTitle.trim());
await list.rename(newTitle.trim());
}
},
@ -226,9 +226,9 @@ Template.listActionPopup.events({
if (!err && ret) Popup.back();
});
},
'click .js-close-list'(event) {
async 'click .js-close-list'(event) {
event.preventDefault();
this.archive();
await this.archive();
Popup.back();
},
'click .js-set-wip-limit': Popup.open('setWipLimit'),
@ -255,26 +255,26 @@ BlazeComponent.extendComponent({
}
},
enableSoftLimit() {
async enableSoftLimit() {
const list = Template.currentData();
if (
list.getWipLimit('soft') &&
list.getWipLimit('value') < list.cards().length
) {
list.setWipLimit(list.cards().length);
await list.setWipLimit(list.cards().length);
}
Meteor.call('enableSoftLimit', Template.currentData()._id);
},
enableWipLimit() {
async enableWipLimit() {
const list = Template.currentData();
// Prevent user from using previously stored wipLimit.value if it is less than the current number of cards in the list
if (
!list.getWipLimit('enabled') &&
list.getWipLimit('value') < list.cards().length
) {
list.setWipLimit(list.cards().length);
await list.setWipLimit(list.cards().length);
}
Meteor.call('enableWipLimit', list._id);
},
@ -368,12 +368,12 @@ BlazeComponent.extendComponent({
'click .js-palette-color'() {
this.currentColor.set(this.currentData().color);
},
'click .js-submit'() {
this.currentList.setColor(this.currentColor.get());
async 'click .js-submit'() {
await this.currentList.setColor(this.currentColor.get());
Popup.close();
},
'click .js-remove-color'() {
this.currentList.setColor(null);
async 'click .js-remove-color'() {
await this.currentList.setColor(null);
Popup.close();
},
},

View file

@ -15,12 +15,12 @@ Template.bookmarks.helpers({
});
Template.bookmarks.events({
'click .js-toggle-star'(e) {
async 'click .js-toggle-star'(e) {
e.preventDefault();
const boardId = this._id;
const user = ReactiveCache.getCurrentUser();
if (user && boardId) {
user.toggleBoardStar(boardId);
await user.toggleBoardStar(boardId);
}
},
});
@ -42,12 +42,12 @@ Template.bookmarksPopup.helpers({
});
Template.bookmarksPopup.events({
'click .js-toggle-star'(e) {
async 'click .js-toggle-star'(e) {
e.preventDefault();
const boardId = this._id;
const user = ReactiveCache.getCurrentUser();
if (user && boardId) {
user.toggleBoardStar(boardId);
await user.toggleBoardStar(boardId);
}
},
});

View file

@ -327,9 +327,9 @@ Template.boardMenuPopup.events({
// You could add a toast notification here if available
}
}),
'click .js-archive-board ': Popup.afterConfirm('archiveBoard', function() {
'click .js-archive-board ': Popup.afterConfirm('archiveBoard', async function() {
const currentBoard = Utils.getCurrentBoard();
currentBoard.archive();
await currentBoard.archive();
// XXX We should have some kind of notification on top of the page to
// confirm that the board was successfully archived.
FlowRouter.go('home');
@ -382,7 +382,7 @@ Template.memberPopup.events({
Popup.back();
},
'click .js-change-role': Popup.open('changePermissions'),
'click .js-remove-member': Popup.afterConfirm('removeMember', function() {
'click .js-remove-member': Popup.afterConfirm('removeMember', async function() {
// This works from removing member from board, card members and assignees.
const boardId = Session.get('currentBoard');
const memberId = this.userId;
@ -392,7 +392,7 @@ Template.memberPopup.events({
ReactiveCache.getCards({ boardId, assignees: memberId }).forEach(card => {
card.unassignAssignee(memberId);
});
ReactiveCache.getBoard(boardId).removeMember(memberId);
await ReactiveCache.getBoard(boardId).removeMember(memberId);
Popup.back();
}),
'click .js-leave-member': Popup.afterConfirm('leaveBoard', () => {
@ -774,10 +774,10 @@ BlazeComponent.extendComponent({
events() {
return [
{
'click .js-select-background'(evt) {
async 'click .js-select-background'(evt) {
const currentBoard = Utils.getCurrentBoard();
const newColor = this.currentData().toString();
currentBoard.setColor(newColor);
await currentBoard.setColor(newColor);
evt.preventDefault();
},
},
@ -789,10 +789,10 @@ BlazeComponent.extendComponent({
events() {
return [
{
submit(event) {
async submit(event) {
const currentBoard = Utils.getCurrentBoard();
const backgroundImageURL = this.find('.js-board-background-image-url').value.trim();
currentBoard.setBackgroundImageURL(backgroundImageURL);
await currentBoard.setBackgroundImageURL(backgroundImageURL);
Utils.setBackgroundImage();
Popup.back();
event.preventDefault();
@ -1945,7 +1945,7 @@ Template.removeBoardTeamPopup.helpers({
});
Template.changePermissionsPopup.events({
'click .js-set-admin, click .js-set-normal, click .js-set-normal-assigned-only, click .js-set-no-comments, click .js-set-comment-only, click .js-set-comment-assigned-only, click .js-set-read-only, click .js-set-read-assigned-only, click .js-set-worker'(
async 'click .js-set-admin, click .js-set-normal, click .js-set-normal-assigned-only, click .js-set-no-comments, click .js-set-comment-only, click .js-set-comment-assigned-only, click .js-set-read-only, click .js-set-read-assigned-only, click .js-set-worker'(
event,
) {
const currentBoard = Utils.getCurrentBoard();
@ -1964,7 +1964,7 @@ Template.changePermissionsPopup.events({
const isReadAssignedOnly = $(event.currentTarget).hasClass('js-set-read-assigned-only');
const isNoComments = $(event.currentTarget).hasClass('js-set-no-comments');
const isWorker = $(event.currentTarget).hasClass('js-set-worker');
currentBoard.setMemberPermission(
await currentBoard.setMemberPermission(
memberId,
isAdmin,
isNoComments,

View file

@ -81,76 +81,76 @@ BlazeComponent.extendComponent({
events() {
return [
{
'click .js-restore-card'() {
async 'click .js-restore-card'() {
const card = this.currentData();
if (card.canBeRestored()) {
card.restore();
await card.restore();
}
},
'click .js-restore-all-cards'() {
this.archivedCards().forEach(card => {
async 'click .js-restore-all-cards'() {
for (const card of this.archivedCards()) {
if (card.canBeRestored()) {
card.restore();
await card.restore();
}
});
}
},
'click .js-delete-card': Popup.afterConfirm('cardDelete', function() {
'click .js-delete-card': Popup.afterConfirm('cardDelete', async function() {
const cardId = this._id;
Cards.remove(cardId);
await Cards.removeAsync(cardId);
Popup.back();
}),
'click .js-delete-all-cards': Popup.afterConfirm('cardDelete', () => {
this.archivedCards().forEach(card => {
Cards.remove(card._id);
});
'click .js-delete-all-cards': Popup.afterConfirm('cardDelete', async () => {
for (const card of this.archivedCards()) {
await Cards.removeAsync(card._id);
}
Popup.back();
}),
'click .js-restore-list'() {
async 'click .js-restore-list'() {
const list = this.currentData();
list.restore();
await list.restore();
},
'click .js-restore-all-lists'() {
this.archivedLists().forEach(list => {
list.restore();
});
async 'click .js-restore-all-lists'() {
for (const list of this.archivedLists()) {
await list.restore();
}
},
'click .js-delete-list': Popup.afterConfirm('listDelete', function() {
this.remove();
'click .js-delete-list': Popup.afterConfirm('listDelete', async function() {
await this.remove();
Popup.back();
}),
'click .js-delete-all-lists': Popup.afterConfirm('listDelete', () => {
this.archivedLists().forEach(list => {
list.remove();
});
'click .js-delete-all-lists': Popup.afterConfirm('listDelete', async () => {
for (const list of this.archivedLists()) {
await list.remove();
}
Popup.back();
}),
'click .js-restore-swimlane'() {
async 'click .js-restore-swimlane'() {
const swimlane = this.currentData();
swimlane.restore();
await swimlane.restore();
},
'click .js-restore-all-swimlanes'() {
this.archivedSwimlanes().forEach(swimlane => {
swimlane.restore();
});
async 'click .js-restore-all-swimlanes'() {
for (const swimlane of this.archivedSwimlanes()) {
await swimlane.restore();
}
},
'click .js-delete-swimlane': Popup.afterConfirm(
'swimlaneDelete',
function() {
this.remove();
async function() {
await this.remove();
Popup.back();
},
),
'click .js-delete-all-swimlanes': Popup.afterConfirm(
'swimlaneDelete',
() => {
this.archivedSwimlanes().forEach(swimlane => {
swimlane.remove();
});
async () => {
for (const swimlane of this.archivedSwimlanes()) {
await swimlane.remove();
}
Popup.back();
},
),

View file

@ -8,14 +8,14 @@ Meteor.startup(() => {
});
BlazeComponent.extendComponent({
editTitle(event) {
async editTitle(event) {
event.preventDefault();
const newTitle = this.childComponents('inlinedForm')[0]
.getValue()
.trim();
const swimlane = this.currentData();
if (newTitle) {
swimlane.rename(newTitle.trim());
await swimlane.rename(newTitle.trim());
}
},
collapsed(check = undefined) {
@ -106,9 +106,9 @@ Template.editSwimlaneTitleForm.helpers({
Template.swimlaneActionPopup.events({
'click .js-set-swimlane-color': Popup.open('setSwimlaneColor'),
'click .js-set-swimlane-height': Popup.open('setSwimlaneHeight'),
'click .js-close-swimlane'(event) {
async 'click .js-close-swimlane'(event) {
event.preventDefault();
this.archive();
await this.archive();
Popup.back();
},
'click .js-move-swimlane': Popup.open('moveSwimlane'),
@ -183,20 +183,20 @@ BlazeComponent.extendComponent({
events() {
return [
{
'submit form'(event) {
async 'submit form'(event) {
event.preventDefault();
this.currentSwimlane.setColor(this.currentColor.get());
await this.currentSwimlane.setColor(this.currentColor.get());
Popup.back();
},
'click .js-palette-color'() {
this.currentColor.set(this.currentData().color);
},
'click .js-submit'() {
this.currentSwimlane.setColor(this.currentColor.get());
async 'click .js-submit'() {
await this.currentSwimlane.setColor(this.currentColor.get());
Popup.back();
},
'click .js-remove-color'() {
this.currentSwimlane.setColor(null);
async 'click .js-remove-color'() {
await this.currentSwimlane.setColor(null);
Popup.back();
},
},

View file

@ -268,7 +268,7 @@ hotkeys('space', (event) => {
}
});
const archiveCard = (event) => {
const archiveCard = async (event) => {
event.preventDefault();
const cardId = getSelectedCardId();
if (!cardId) {
@ -282,7 +282,7 @@ const archiveCard = (event) => {
if (Utils.canModifyBoard()) {
const card = Cards.findOne(cardId);
card.archive();
await card.archive();
}
};

View file

@ -2,14 +2,14 @@ import { ReactiveCache } from '/imports/reactiveCache';
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
Utils = {
setBackgroundImage(url) {
async setBackgroundImage(url) {
const currentBoard = Utils.getCurrentBoard();
if (currentBoard.backgroundImageURL !== undefined) {
$(".board-wrapper").css({"background":"url(" + currentBoard.backgroundImageURL + ")","background-size":"cover"});
$(".swimlane,.swimlane .list,.swimlane .list .list-body,.swimlane .list:first-child .list-body").css({"background-color":"transparent"});
$(".minicard").css({"opacity": "0.9"});
} else if (currentBoard["background-color"]) {
currentBoard.setColor(currentBoard["background-color"]);
await currentBoard.setColor(currentBoard["background-color"]);
}
},
/** returns the current board id

View file

@ -16,7 +16,7 @@ sudo npm install -g n
export N_NODE_MIRROR=https://github.com/wekan/node-v14-esm/releases/download
sudo -E n 14.21.4
sudo npm -g install @mapbox/node-pre-gyp
sudo npm -g install meteor@2.14 --unsafe-perm
sudo npm -g install meteor@2.16 --unsafe-perm
export PATH=$PATH:$HOME/.meteor
meteor npm install production
meteor build .build --directory --platforms=web.browser

View file

@ -1431,93 +1431,80 @@ Boards.helpers({
isTemplatesBoard() {
return this.type === 'template-container';
},
});
Boards.mutations({
archive() {
return { $set: { archived: true, archivedAt: new Date() } };
async archive() {
return await Boards.updateAsync(this._id, { $set: { archived: true, archivedAt: new Date() } });
},
restore() {
return { $set: { archived: false } };
async restore() {
return await Boards.updateAsync(this._id, { $set: { archived: false } });
},
rename(title) {
return { $set: { title } };
async rename(title) {
return await Boards.updateAsync(this._id, { $set: { title } });
},
setDescription(description) {
return { $set: { description } };
async setDescription(description) {
return await Boards.updateAsync(this._id, { $set: { description } });
},
setColor(color) {
return { $set: { color } };
async setColor(color) {
return await Boards.updateAsync(this._id, { $set: { color } });
},
setBackgroundImageURL(backgroundImageURL) {
async setBackgroundImageURL(backgroundImageURL) {
const currentUser = ReactiveCache.getCurrentUser();
if(currentUser.isBoardAdmin()) {
return { $set: { backgroundImageURL } };
} else if (currentUser.isAdmin()) {
return { $set: { backgroundImageURL } };
} else {
return false;
if (currentUser.isBoardAdmin() || currentUser.isAdmin()) {
return await Boards.updateAsync(this._id, { $set: { backgroundImageURL } });
}
return false;
},
setVisibility(visibility) {
return { $set: { permission: visibility } };
async setVisibility(visibility) {
return await Boards.updateAsync(this._id, { $set: { permission: visibility } });
},
addLabel(name, color) {
// If label with the same name and color already exists we don't want to
// create another one because they would be indistinguishable in the UI
// (they would still have different `_id` but that is not exposed to the
// user).
async addLabel(name, color) {
if (!this.getLabel(name, color)) {
const _id = Random.id(6);
return { $push: { labels: { _id, name, color } } };
return await Boards.updateAsync(this._id, { $push: { labels: { _id, name, color } } });
}
return {};
return null;
},
editLabel(labelId, name, color) {
async editLabel(labelId, name, color) {
if (!this.getLabel(name, color)) {
const labelIndex = this.labelIndex(labelId);
return {
return await Boards.updateAsync(this._id, {
$set: {
[`labels.${labelIndex}.name`]: name,
[`labels.${labelIndex}.color`]: color,
},
};
});
}
return {};
return null;
},
removeLabel(labelId) {
return { $pull: { labels: { _id: labelId } } };
async removeLabel(labelId) {
return await Boards.updateAsync(this._id, { $pull: { labels: { _id: labelId } } });
},
changeOwnership(fromId, toId) {
async changeOwnership(fromId, toId) {
const memberIndex = this.memberIndex(fromId);
return {
$set: {
[`members.${memberIndex}.userId`]: toId,
},
};
return await Boards.updateAsync(this._id, {
$set: { [`members.${memberIndex}.userId`]: toId },
});
},
addMember(memberId) {
async addMember(memberId) {
const memberIndex = this.memberIndex(memberId);
if (memberIndex >= 0) {
return {
$set: {
[`members.${memberIndex}.isActive`]: true,
},
};
return await Boards.updateAsync(this._id, {
$set: { [`members.${memberIndex}.isActive`]: true },
});
}
return {
return await Boards.updateAsync(this._id, {
$push: {
members: {
userId: memberId,
@ -1532,32 +1519,28 @@ Boards.mutations({
isReadAssignedOnly: false,
},
},
};
});
},
removeMember(memberId) {
async removeMember(memberId) {
const memberIndex = this.memberIndex(memberId);
// we do not allow the only one admin to be removed
const allowRemove =
!this.members[memberIndex].isAdmin || this.activeAdmins().length > 1;
if (!allowRemove) {
return {
$set: {
[`members.${memberIndex}.isActive`]: true,
},
};
return await Boards.updateAsync(this._id, {
$set: { [`members.${memberIndex}.isActive`]: true },
});
}
return {
return await Boards.updateAsync(this._id, {
$set: {
[`members.${memberIndex}.isActive`]: false,
[`members.${memberIndex}.isAdmin`]: false,
},
};
});
},
setMemberPermission(
async setMemberPermission(
memberId,
isAdmin,
isNoComments,
@ -1570,12 +1553,11 @@ Boards.mutations({
currentUserId = Meteor.userId(),
) {
const memberIndex = this.memberIndex(memberId);
// do not allow change permission of self
if (memberId === currentUserId) {
isAdmin = this.members[memberIndex].isAdmin;
}
return {
return await Boards.updateAsync(this._id, {
$set: {
[`members.${memberIndex}.isAdmin`]: isAdmin,
[`members.${memberIndex}.isNoComments`]: isNoComments,
@ -1586,144 +1568,143 @@ Boards.mutations({
[`members.${memberIndex}.isReadOnly`]: isReadOnly,
[`members.${memberIndex}.isReadAssignedOnly`]: isReadAssignedOnly,
},
};
});
},
setAllowsSubtasks(allowsSubtasks) {
return { $set: { allowsSubtasks } };
async setAllowsSubtasks(allowsSubtasks) {
return await Boards.updateAsync(this._id, { $set: { allowsSubtasks } });
},
setAllowsCreator(allowsCreator) {
return { $set: { allowsCreator } };
async setAllowsCreator(allowsCreator) {
return await Boards.updateAsync(this._id, { $set: { allowsCreator } });
},
setAllowsCreatorOnMinicard(allowsCreatorOnMinicard) {
return { $set: { allowsCreatorOnMinicard } };
async setAllowsCreatorOnMinicard(allowsCreatorOnMinicard) {
return await Boards.updateAsync(this._id, { $set: { allowsCreatorOnMinicard } });
},
setAllowsMembers(allowsMembers) {
return { $set: { allowsMembers } };
async setAllowsMembers(allowsMembers) {
return await Boards.updateAsync(this._id, { $set: { allowsMembers } });
},
setAllowsChecklists(allowsChecklists) {
return { $set: { allowsChecklists } };
async setAllowsChecklists(allowsChecklists) {
return await Boards.updateAsync(this._id, { $set: { allowsChecklists } });
},
setAllowsAssignee(allowsAssignee) {
return { $set: { allowsAssignee } };
async setAllowsAssignee(allowsAssignee) {
return await Boards.updateAsync(this._id, { $set: { allowsAssignee } });
},
setAllowsAssignedBy(allowsAssignedBy) {
return { $set: { allowsAssignedBy } };
async setAllowsAssignedBy(allowsAssignedBy) {
return await Boards.updateAsync(this._id, { $set: { allowsAssignedBy } });
},
setAllowsShowListsOnMinicard(allowsShowListsOnMinicard) {
return { $set: { allowsShowListsOnMinicard } };
async setAllowsShowListsOnMinicard(allowsShowListsOnMinicard) {
return await Boards.updateAsync(this._id, { $set: { allowsShowListsOnMinicard } });
},
setAllowsChecklistAtMinicard(allowsChecklistAtMinicard) {
return { $set: { allowsChecklistAtMinicard } };
async setAllowsChecklistAtMinicard(allowsChecklistAtMinicard) {
return await Boards.updateAsync(this._id, { $set: { allowsChecklistAtMinicard } });
},
setAllowsRequestedBy(allowsRequestedBy) {
return { $set: { allowsRequestedBy } };
async setAllowsRequestedBy(allowsRequestedBy) {
return await Boards.updateAsync(this._id, { $set: { allowsRequestedBy } });
},
setAllowsCardSortingByNumber(allowsCardSortingByNumber) {
return { $set: { allowsCardSortingByNumber } };
async setAllowsCardSortingByNumber(allowsCardSortingByNumber) {
return await Boards.updateAsync(this._id, { $set: { allowsCardSortingByNumber } });
},
setAllowsShowLists(allowsShowLists) {
return { $set: { allowsShowLists } };
async setAllowsShowLists(allowsShowLists) {
return await Boards.updateAsync(this._id, { $set: { allowsShowLists } });
},
setAllowsAttachments(allowsAttachments) {
return { $set: { allowsAttachments } };
async setAllowsAttachments(allowsAttachments) {
return await Boards.updateAsync(this._id, { $set: { allowsAttachments } });
},
setAllowsLabels(allowsLabels) {
return { $set: { allowsLabels } };
async setAllowsLabels(allowsLabels) {
return await Boards.updateAsync(this._id, { $set: { allowsLabels } });
},
setAllowsComments(allowsComments) {
return { $set: { allowsComments } };
async setAllowsComments(allowsComments) {
return await Boards.updateAsync(this._id, { $set: { allowsComments } });
},
setAllowsDescriptionTitle(allowsDescriptionTitle) {
return { $set: { allowsDescriptionTitle } };
async setAllowsDescriptionTitle(allowsDescriptionTitle) {
return await Boards.updateAsync(this._id, { $set: { allowsDescriptionTitle } });
},
setAllowsCardNumber(allowsCardNumber) {
return { $set: { allowsCardNumber } };
async setAllowsCardNumber(allowsCardNumber) {
return await Boards.updateAsync(this._id, { $set: { allowsCardNumber } });
},
setAllowsDescriptionText(allowsDescriptionText) {
return { $set: { allowsDescriptionText } };
async setAllowsDescriptionText(allowsDescriptionText) {
return await Boards.updateAsync(this._id, { $set: { allowsDescriptionText } });
},
setallowsDescriptionTextOnMinicard(allowsDescriptionTextOnMinicard) {
return { $set: { allowsDescriptionTextOnMinicard } };
async setallowsDescriptionTextOnMinicard(allowsDescriptionTextOnMinicard) {
return await Boards.updateAsync(this._id, { $set: { allowsDescriptionTextOnMinicard } });
},
setallowsCoverAttachmentOnMinicard(allowsCoverAttachmentOnMinicard) {
return { $set: { allowsCoverAttachmentOnMinicard } };
async setallowsCoverAttachmentOnMinicard(allowsCoverAttachmentOnMinicard) {
return await Boards.updateAsync(this._id, { $set: { allowsCoverAttachmentOnMinicard } });
},
setallowsBadgeAttachmentOnMinicard(allowsBadgeAttachmentOnMinicard) {
return { $set: { allowsBadgeAttachmentOnMinicard } };
async setallowsBadgeAttachmentOnMinicard(allowsBadgeAttachmentOnMinicard) {
return await Boards.updateAsync(this._id, { $set: { allowsBadgeAttachmentOnMinicard } });
},
setallowsCardSortingByNumberOnMinicard(allowsCardSortingByNumberOnMinicard) {
return { $set: { allowsCardSortingByNumberOnMinicard } };
async setallowsCardSortingByNumberOnMinicard(allowsCardSortingByNumberOnMinicard) {
return await Boards.updateAsync(this._id, { $set: { allowsCardSortingByNumberOnMinicard } });
},
setAllowsActivities(allowsActivities) {
return { $set: { allowsActivities } };
async setAllowsActivities(allowsActivities) {
return await Boards.updateAsync(this._id, { $set: { allowsActivities } });
},
setAllowsReceivedDate(allowsReceivedDate) {
return { $set: { allowsReceivedDate } };
async setAllowsReceivedDate(allowsReceivedDate) {
return await Boards.updateAsync(this._id, { $set: { allowsReceivedDate } });
},
setAllowsCardCounterList(allowsCardCounterList) {
return { $set: { allowsCardCounterList } };
async setAllowsCardCounterList(allowsCardCounterList) {
return await Boards.updateAsync(this._id, { $set: { allowsCardCounterList } });
},
setAllowsBoardMemberList(allowsBoardMemberList) {
return { $set: { allowsBoardMemberList } };
async setAllowsBoardMemberList(allowsBoardMemberList) {
return await Boards.updateAsync(this._id, { $set: { allowsBoardMemberList } });
},
setAllowsStartDate(allowsStartDate) {
return { $set: { allowsStartDate } };
async setAllowsStartDate(allowsStartDate) {
return await Boards.updateAsync(this._id, { $set: { allowsStartDate } });
},
setAllowsEndDate(allowsEndDate) {
return { $set: { allowsEndDate } };
async setAllowsEndDate(allowsEndDate) {
return await Boards.updateAsync(this._id, { $set: { allowsEndDate } });
},
setAllowsDueDate(allowsDueDate) {
return { $set: { allowsDueDate } };
async setAllowsDueDate(allowsDueDate) {
return await Boards.updateAsync(this._id, { $set: { allowsDueDate } });
},
setSubtasksDefaultBoardId(subtasksDefaultBoardId) {
return { $set: { subtasksDefaultBoardId } };
async setSubtasksDefaultBoardId(subtasksDefaultBoardId) {
return await Boards.updateAsync(this._id, { $set: { subtasksDefaultBoardId } });
},
setSubtasksDefaultListId(subtasksDefaultListId) {
return { $set: { subtasksDefaultListId } };
async setSubtasksDefaultListId(subtasksDefaultListId) {
return await Boards.updateAsync(this._id, { $set: { subtasksDefaultListId } });
},
setPresentParentTask(presentParentTask) {
return { $set: { presentParentTask } };
async setPresentParentTask(presentParentTask) {
return await Boards.updateAsync(this._id, { $set: { presentParentTask } });
},
move(sortIndex) {
return { $set: { sort: sortIndex } };
async move(sortIndex) {
return await Boards.updateAsync(this._id, { $set: { sort: sortIndex } });
},
toggleShowActivities() {
return { $set: { showActivities: !this.showActivities } };
async toggleShowActivities() {
return await Boards.updateAsync(this._id, { $set: { showActivities: !this.showActivities } });
},
});
@ -1909,14 +1890,14 @@ if (Meteor.isServer) {
check(boardId, String);
return ReactiveCache.getBoard(boardId, {}, { backgroundImageUrl: 1 });
},
quitBoard(boardId) {
async quitBoard(boardId) {
check(boardId, String);
const board = ReactiveCache.getBoard(boardId);
if (board) {
const userId = Meteor.userId();
const index = board.memberIndex(userId);
if (index >= 0) {
board.removeMember(userId);
await board.removeMember(userId);
return true;
} else throw new Meteor.Error('error-board-notAMember');
} else throw new Meteor.Error('error-board-doesNotExist');
@ -1993,14 +1974,14 @@ if (Meteor.isServer) {
});
Meteor.methods({
archiveBoard(boardId) {
async archiveBoard(boardId) {
check(boardId, String);
const board = ReactiveCache.getBoard(boardId);
if (board) {
const userId = Meteor.userId();
const index = board.memberIndex(userId);
if (index >= 0) {
board.archive();
await board.archive();
return true;
} else throw new Meteor.Error('error-board-notAMember');
} else throw new Meteor.Error('error-board-doesNotExist');
@ -2159,7 +2140,7 @@ if (Meteor.isServer) {
}
if (modifier.$set) {
const boardId = doc._id;
foreachRemovedMember(doc, modifier.$set, memberId => {
foreachRemovedMember(doc, modifier.$set, async memberId => {
Cards.update(
{ boardId },
{
@ -2182,7 +2163,7 @@ if (Meteor.isServer) {
);
const board = Boards._transform(doc);
board.setWatcher(memberId, false);
await board.setWatcher(memberId, false);
// Remove board from users starred list
if (!board.isPublic()) {
@ -2589,7 +2570,7 @@ JsonRoutes.add('POST', '/api/boards/:boardId/copy', function(req, res) {
* @param {boolean} isReadOnly ReadOnly capability
* @param {boolean} isReadAssignedOnly ReadAssignedOnly capability
*/
JsonRoutes.add('POST', '/api/boards/:boardId/members/:memberId', function(
JsonRoutes.add('POST', '/api/boards/:boardId/members/:memberId', async function(
req,
res,
) {
@ -2606,7 +2587,7 @@ JsonRoutes.add('POST', '/api/boards/:boardId/copy', function(req, res) {
return data;
}
}
const query = board.setMemberPermission(
const query = await board.setMemberPermission(
memberId,
isTrue(isAdmin),
isTrue(isNoComments),

View file

@ -572,15 +572,17 @@ Cards.helpers({
});
},
mapCustomFieldsToBoard(boardId) {
async mapCustomFieldsToBoard(boardId) {
// Map custom fields to new board
return this.customFields.map(cf => {
const result = [];
for (const cf of this.customFields) {
const oldCf = ReactiveCache.getCustomField(cf._id);
// Check if oldCf is undefined or null
if (!oldCf) {
//console.error(`Custom field with ID ${cf._id} not found.`);
return cf; // Skip this field if oldCf is not found
result.push(cf); // Skip this field if oldCf is not found
continue;
}
const newCf = ReactiveCache.getCustomField({
@ -592,11 +594,12 @@ Cards.helpers({
if (newCf) {
cf._id = newCf._id;
} else if (!_.contains(oldCf.boardIds, boardId)) {
oldCf.addBoard(boardId);
await oldCf.addBoard(boardId);
}
return cf;
});
result.push(cf);
}
return result;
},
@ -1203,11 +1206,11 @@ Cards.helpers({
}
},
assignMember(memberId) {
async assignMember(memberId) {
let ret;
if (this.isLinkedBoard()) {
const board = ReactiveCache.getBoard(this.linkedId);
ret = board.addMember(memberId);
ret = await board.addMember(memberId);
} else {
ret = Cards.update(
{ _id: this.getRealId() },
@ -1234,7 +1237,7 @@ Cards.helpers({
}
},
unassignMember(memberId) {
async unassignMember(memberId) {
if (this.isLinkedCard()) {
return Cards.update(
{ _id: this.linkedId },
@ -1242,7 +1245,7 @@ Cards.helpers({
);
} else if (this.isLinkedBoard()) {
const board = ReactiveCache.getBoard(this.linkedId);
return board.removeMember(memberId);
return await board.removeMember(memberId);
} else {
return Cards.update({ _id: this._id }, { $pull: { members: memberId } });
}
@ -1991,52 +1994,41 @@ Cards.helpers({
}
return pokerWinnersListMap[0].pokerCard;
},
});
Cards.mutations({
applyToChildren(funct) {
ReactiveCache.getCards({
parentId: this._id,
}).forEach(card => {
funct(card);
async applyToChildren(funct) {
const cards = ReactiveCache.getCards({ parentId: this._id });
for (const card of cards) {
await funct(card);
}
},
async archive() {
await this.applyToChildren(async card => {
await card.archive();
});
return await Cards.updateAsync(this._id, {
$set: { archived: true, archivedAt: new Date() },
});
},
archive() {
this.applyToChildren(card => {
return card.archive();
async restore() {
await this.applyToChildren(async card => {
await card.restore();
});
return await Cards.updateAsync(this._id, {
$set: { archived: false },
});
return {
$set: {
archived: true,
archivedAt: new Date(),
},
};
},
restore() {
this.applyToChildren(card => {
return card.restore();
});
return {
$set: {
archived: false,
},
};
},
moveToEndOfList({ listId, swimlaneId } = {}) {
async moveToEndOfList({ listId, swimlaneId } = {}) {
swimlaneId = swimlaneId || this.swimlaneId;
const boardId = this.boardId;
let sortIndex = 0;
// This should never happen, but there was a bug that was fixed in commit
// ea0239538a68e225c867411a4f3e0d27c158383.
if (!swimlaneId) {
const board = ReactiveCache.getBoard(boardId);
swimlaneId = board.getDefaultSwimline()._id;
}
// Move the minicard to the end of the target list
let parentElementDom = $(`#swimlane-${swimlaneId}`).get(0);
if (!parentElementDom) parentElementDom = $(':root');
@ -2045,7 +2037,7 @@ Cards.mutations({
.get(0);
if (lastCardDom) sortIndex = Utils.calculateIndex(lastCardDom, null).base;
return this.moveOptionalArgs({
return await this.moveOptionalArgs({
boardId,
swimlaneId,
listId,
@ -2053,22 +2045,19 @@ Cards.mutations({
});
},
moveOptionalArgs({ boardId, swimlaneId, listId, sort } = {}) {
async moveOptionalArgs({ boardId, swimlaneId, listId, sort } = {}) {
boardId = boardId || this.boardId;
swimlaneId = swimlaneId || this.swimlaneId;
// This should never happen, but there was a bug that was fixed in commit
// ea0239538a68e225c867411a4f3e0d27c158383.
if (!swimlaneId) {
const board = ReactiveCache.getBoard(boardId);
swimlaneId = board.getDefaultSwimline()._id;
}
listId = listId || this.listId;
if (sort === undefined || sort === null) sort = this.sort;
return this.move(boardId, swimlaneId, listId, sort);
return await this.move(boardId, swimlaneId, listId, sort);
},
move(boardId, swimlaneId, listId, sort = null) {
// Capture previous state for history tracking
async move(boardId, swimlaneId, listId, sort = null) {
const previousState = {
boardId: this.boardId,
swimlaneId: this.swimlaneId,
@ -2076,20 +2065,13 @@ Cards.mutations({
sort: this.sort,
};
const mutatedFields = {
boardId,
swimlaneId,
listId,
};
const mutatedFields = { boardId, swimlaneId, listId };
if (sort !== null) {
mutatedFields.sort = sort;
}
// we must only copy the labels and custom fields if the target board
// differs from the source board
if (this.boardId !== boardId) {
// Get label names
const oldBoard = ReactiveCache.getBoard(this.boardId);
const oldBoardLabels = oldBoard.labels;
const oldCardLabels = _.pluck(
@ -2108,7 +2090,6 @@ Cards.mutations({
'_id',
);
// assign the new card number from the target board
const newCardNumber = newBoard.getNextCardNumber();
Object.assign(mutatedFields, {
@ -2119,11 +2100,8 @@ Cards.mutations({
mutatedFields.customFields = this.mapCustomFieldsToBoard(newBoard._id);
}
Cards.update(this._id, {
$set: mutatedFields,
});
await Cards.updateAsync(this._id, { $set: mutatedFields });
// Track position change in user history (server-side only)
if (Meteor.isServer && Meteor.userId() && typeof UserPositionHistory !== 'undefined') {
try {
UserPositionHistory.trackChange({
@ -2145,7 +2123,6 @@ Cards.mutations({
}
}
// Ensure attachments follow the card to its new board/list/swimlane
if (Meteor.isServer) {
const updateMeta = {};
if (mutatedFields.boardId !== undefined) updateMeta['meta.boardId'] = mutatedFields.boardId;
@ -2154,293 +2131,159 @@ Cards.mutations({
if (Object.keys(updateMeta).length > 0) {
try {
Attachments.collection.update(
await Attachments.collection.updateAsync(
{ 'meta.cardId': this._id },
{ $set: updateMeta },
{ multi: true },
);
} catch (err) {
// Do not block the move if attachment update fails
// eslint-disable-next-line no-console
console.error('Failed to update attachments metadata after moving card', this._id, err);
}
}
}
},
addLabel(labelId) {
async addLabel(labelId) {
this.labelIds.push(labelId);
return {
$addToSet: {
labelIds: labelId,
},
};
return await Cards.updateAsync(this._id, { $addToSet: { labelIds: labelId } });
},
removeLabel(labelId) {
async removeLabel(labelId) {
this.labelIds = _.without(this.labelIds, labelId);
return {
$pull: {
labelIds: labelId,
},
};
return await Cards.updateAsync(this._id, { $pull: { labelIds: labelId } });
},
toggleLabel(labelId) {
async toggleLabel(labelId) {
if (this.labelIds && this.labelIds.indexOf(labelId) > -1) {
return this.removeLabel(labelId);
return await this.removeLabel(labelId);
} else {
return this.addLabel(labelId);
return await this.addLabel(labelId);
}
},
setColor(newColor) {
async setColor(newColor) {
if (newColor === 'white') {
newColor = null;
}
return {
$set: {
color: newColor,
},
};
return await Cards.updateAsync(this._id, { $set: { color: newColor } });
},
assignMember(memberId) {
return {
$addToSet: {
members: memberId,
},
};
async assignMember(memberId) {
return await Cards.updateAsync(this._id, { $addToSet: { members: memberId } });
},
assignAssignee(assigneeId) {
// If there is not any assignee, allow one assignee, not more.
/*
if (this.getAssignees().length === 0) {
return {
$addToSet: {
assignees: assigneeId,
},
};
*/
// Allow more that one assignee:
// https://github.com/wekan/wekan/issues/3302
return {
$addToSet: {
assignees: assigneeId,
},
};
//} else {
// return false,
//}
async assignAssignee(assigneeId) {
return await Cards.updateAsync(this._id, { $addToSet: { assignees: assigneeId } });
},
unassignMember(memberId) {
return {
$pull: {
members: memberId,
},
};
async unassignMember(memberId) {
return await Cards.updateAsync(this._id, { $pull: { members: memberId } });
},
unassignAssignee(assigneeId) {
return {
$pull: {
assignees: assigneeId,
},
};
async unassignAssignee(assigneeId) {
return await Cards.updateAsync(this._id, { $pull: { assignees: assigneeId } });
},
toggleMember(memberId) {
async toggleMember(memberId) {
if (this.members && this.members.indexOf(memberId) > -1) {
return this.unassignMember(memberId);
return await this.unassignMember(memberId);
} else {
return this.assignMember(memberId);
return await this.assignMember(memberId);
}
},
toggleAssignee(assigneeId) {
async toggleAssignee(assigneeId) {
if (this.assignees && this.assignees.indexOf(assigneeId) > -1) {
return this.unassignAssignee(assigneeId);
return await this.unassignAssignee(assigneeId);
} else {
return this.assignAssignee(assigneeId);
return await this.assignAssignee(assigneeId);
}
},
assignCustomField(customFieldId) {
return {
$addToSet: {
customFields: {
_id: customFieldId,
value: null,
},
},
};
async assignCustomField(customFieldId) {
return await Cards.updateAsync(this._id, {
$addToSet: { customFields: { _id: customFieldId, value: null } },
});
},
unassignCustomField(customFieldId) {
return {
$pull: {
customFields: {
_id: customFieldId,
},
},
};
async unassignCustomField(customFieldId) {
return await Cards.updateAsync(this._id, {
$pull: { customFields: { _id: customFieldId } },
});
},
toggleCustomField(customFieldId) {
async toggleCustomField(customFieldId) {
if (this.customFields && this.customFieldIndex(customFieldId) > -1) {
return this.unassignCustomField(customFieldId);
return await this.unassignCustomField(customFieldId);
} else {
return this.assignCustomField(customFieldId);
return await this.assignCustomField(customFieldId);
}
},
toggleShowActivities() {
return {
$set: {
showActivities: !this.showActivities,
}
};
async toggleShowActivities() {
return await Cards.updateAsync(this._id, {
$set: { showActivities: !this.showActivities },
});
},
toggleShowChecklistAtMinicard() {
return {
$set: {
showChecklistAtMinicard: !this.showChecklistAtMinicard,
}
};
async toggleShowChecklistAtMinicard() {
return await Cards.updateAsync(this._id, {
$set: { showChecklistAtMinicard: !this.showChecklistAtMinicard },
});
},
setCustomField(customFieldId, value) {
// todo
async setCustomField(customFieldId, value) {
const index = this.customFieldIndex(customFieldId);
if (index > -1) {
const update = {
$set: {},
};
const update = { $set: {} };
update.$set[`customFields.${index}.value`] = value;
return update;
return await Cards.updateAsync(this._id, update);
}
// TODO
// Ignatz 18.05.2018: Return null to silence ESLint. No Idea if that is correct
return null;
},
setCover(coverId) {
return {
$set: {
coverId,
},
};
async setCover(coverId) {
return await Cards.updateAsync(this._id, { $set: { coverId } });
},
unsetCover() {
return {
$unset: {
coverId: '',
},
};
async unsetCover() {
return await Cards.updateAsync(this._id, { $unset: { coverId: '' } });
},
//setReceived(receivedAt) {
// return {
// $set: {
// receivedAt,
// },
// };
//},
unsetReceived() {
return {
$unset: {
receivedAt: '',
},
};
async unsetReceived() {
return await Cards.updateAsync(this._id, { $unset: { receivedAt: '' } });
},
//setStart(startAt) {
// return {
// $set: {
// startAt,
// },
// };
//},
unsetStart() {
return {
$unset: {
startAt: '',
},
};
async unsetStart() {
return await Cards.updateAsync(this._id, { $unset: { startAt: '' } });
},
//setDue(dueAt) {
// return {
// $set: {
// dueAt,
// },
// };
//},
unsetDue() {
return {
$unset: {
dueAt: '',
},
};
async unsetDue() {
return await Cards.updateAsync(this._id, { $unset: { dueAt: '' } });
},
//setEnd(endAt) {
// return {
// $set: {
// endAt,
// },
// };
//},
unsetEnd() {
return {
$unset: {
endAt: '',
},
};
async unsetEnd() {
return await Cards.updateAsync(this._id, { $unset: { endAt: '' } });
},
setOvertime(isOvertime) {
return {
$set: {
isOvertime,
},
};
async setOvertime(isOvertime) {
return await Cards.updateAsync(this._id, { $set: { isOvertime } });
},
setSpentTime(spentTime) {
return {
$set: {
spentTime,
},
};
async setSpentTime(spentTime) {
return await Cards.updateAsync(this._id, { $set: { spentTime } });
},
unsetSpentTime() {
return {
$unset: {
spentTime: '',
isOvertime: false,
},
};
async unsetSpentTime() {
return await Cards.updateAsync(this._id, { $unset: { spentTime: '', isOvertime: false } });
},
setParentId(parentId) {
return {
$set: {
parentId,
},
};
async setParentId(parentId) {
return await Cards.updateAsync(this._id, { $set: { parentId } });
},
setVoteQuestion(question, publicVote, allowNonBoardMembers) {
return {
async setVoteQuestion(question, publicVote, allowNonBoardMembers) {
return await Cards.updateAsync(this._id, {
$set: {
vote: {
question,
@ -2450,61 +2293,42 @@ Cards.mutations({
negative: [],
},
},
};
});
},
unsetVote() {
return {
$unset: {
vote: '',
},
};
async unsetVote() {
return await Cards.updateAsync(this._id, { $unset: { vote: '' } });
},
setVoteEnd(end) {
return {
$set: { 'vote.end': end },
};
async setVoteEnd(end) {
return await Cards.updateAsync(this._id, { $set: { 'vote.end': end } });
},
unsetVoteEnd() {
return {
$unset: { 'vote.end': '' },
};
async unsetVoteEnd() {
return await Cards.updateAsync(this._id, { $unset: { 'vote.end': '' } });
},
setVote(userId, forIt) {
async setVote(userId, forIt) {
switch (forIt) {
case true:
// vote for it
return {
$pull: {
'vote.negative': userId,
},
$addToSet: {
'vote.positive': userId,
},
};
return await Cards.updateAsync(this._id, {
$pull: { 'vote.negative': userId },
$addToSet: { 'vote.positive': userId },
});
case false:
// vote against
return {
$pull: {
'vote.positive': userId,
},
$addToSet: {
'vote.negative': userId,
},
};
return await Cards.updateAsync(this._id, {
$pull: { 'vote.positive': userId },
$addToSet: { 'vote.negative': userId },
});
default:
// Remove votes
return {
$pull: {
'vote.positive': userId,
'vote.negative': userId,
},
};
return await Cards.updateAsync(this._id, {
$pull: { 'vote.positive': userId, 'vote.negative': userId },
});
}
},
setPokerQuestion(question, allowNonBoardMembers) {
return {
async setPokerQuestion(question, allowNonBoardMembers) {
return await Cards.updateAsync(this._id, {
$set: {
poker: {
question,
@ -2521,246 +2345,47 @@ Cards.mutations({
unsure: [],
},
},
};
});
},
setPokerEstimation(estimation) {
return {
$set: { 'poker.estimation': estimation },
};
async setPokerEstimation(estimation) {
return await Cards.updateAsync(this._id, { $set: { 'poker.estimation': estimation } });
},
unsetPokerEstimation() {
return {
$unset: { 'poker.estimation': '' },
};
async unsetPokerEstimation() {
return await Cards.updateAsync(this._id, { $unset: { 'poker.estimation': '' } });
},
unsetPoker() {
return {
$unset: {
poker: '',
},
};
async unsetPoker() {
return await Cards.updateAsync(this._id, { $unset: { poker: '' } });
},
setPokerEnd(end) {
return {
$set: { 'poker.end': end },
};
async setPokerEnd(end) {
return await Cards.updateAsync(this._id, { $set: { 'poker.end': end } });
},
unsetPokerEnd() {
return {
$unset: { 'poker.end': '' },
};
async unsetPokerEnd() {
return await Cards.updateAsync(this._id, { $unset: { 'poker.end': '' } });
},
setPoker(userId, state) {
switch (state) {
case 'one':
// poker one
return {
$pull: {
'poker.two': userId,
'poker.three': userId,
'poker.five': userId,
'poker.eight': userId,
'poker.thirteen': userId,
'poker.twenty': userId,
'poker.forty': userId,
'poker.oneHundred': userId,
'poker.unsure': userId,
},
$addToSet: {
'poker.one': userId,
},
};
case 'two':
// poker two
return {
$pull: {
'poker.one': userId,
'poker.three': userId,
'poker.five': userId,
'poker.eight': userId,
'poker.thirteen': userId,
'poker.twenty': userId,
'poker.forty': userId,
'poker.oneHundred': userId,
'poker.unsure': userId,
},
$addToSet: {
'poker.two': userId,
},
};
case 'three':
// poker three
return {
$pull: {
'poker.one': userId,
'poker.two': userId,
'poker.five': userId,
'poker.eight': userId,
'poker.thirteen': userId,
'poker.twenty': userId,
'poker.forty': userId,
'poker.oneHundred': userId,
'poker.unsure': userId,
},
$addToSet: {
'poker.three': userId,
},
};
async setPoker(userId, state) {
const pokerFields = ['one', 'two', 'three', 'five', 'eight', 'thirteen', 'twenty', 'forty', 'oneHundred', 'unsure'];
const pullFields = {};
pokerFields.forEach(f => { pullFields[`poker.${f}`] = userId; });
case 'five':
// poker five
return {
$pull: {
'poker.one': userId,
'poker.two': userId,
'poker.three': userId,
'poker.eight': userId,
'poker.thirteen': userId,
'poker.twenty': userId,
'poker.forty': userId,
'poker.oneHundred': userId,
'poker.unsure': userId,
},
$addToSet: {
'poker.five': userId,
},
};
case 'eight':
// poker eight
return {
$pull: {
'poker.one': userId,
'poker.two': userId,
'poker.three': userId,
'poker.five': userId,
'poker.thirteen': userId,
'poker.twenty': userId,
'poker.forty': userId,
'poker.oneHundred': userId,
'poker.unsure': userId,
},
$addToSet: {
'poker.eight': userId,
},
};
case 'thirteen':
// poker thirteen
return {
$pull: {
'poker.one': userId,
'poker.two': userId,
'poker.three': userId,
'poker.five': userId,
'poker.eight': userId,
'poker.twenty': userId,
'poker.forty': userId,
'poker.oneHundred': userId,
'poker.unsure': userId,
},
$addToSet: {
'poker.thirteen': userId,
},
};
case 'twenty':
// poker twenty
return {
$pull: {
'poker.one': userId,
'poker.two': userId,
'poker.three': userId,
'poker.five': userId,
'poker.eight': userId,
'poker.thirteen': userId,
'poker.forty': userId,
'poker.oneHundred': userId,
'poker.unsure': userId,
},
$addToSet: {
'poker.twenty': userId,
},
};
case 'forty':
// poker forty
return {
$pull: {
'poker.one': userId,
'poker.two': userId,
'poker.three': userId,
'poker.five': userId,
'poker.eight': userId,
'poker.thirteen': userId,
'poker.twenty': userId,
'poker.oneHundred': userId,
'poker.unsure': userId,
},
$addToSet: {
'poker.forty': userId,
},
};
case 'oneHundred':
// poker one hundred
return {
$pull: {
'poker.one': userId,
'poker.two': userId,
'poker.three': userId,
'poker.five': userId,
'poker.eight': userId,
'poker.thirteen': userId,
'poker.twenty': userId,
'poker.forty': userId,
'poker.unsure': userId,
},
$addToSet: {
'poker.oneHundred': userId,
},
};
case 'unsure':
// poker unsure
return {
$pull: {
'poker.one': userId,
'poker.two': userId,
'poker.three': userId,
'poker.five': userId,
'poker.eight': userId,
'poker.thirteen': userId,
'poker.twenty': userId,
'poker.forty': userId,
'poker.oneHundred': userId,
},
$addToSet: {
'poker.unsure': userId,
},
};
default:
// Remove pokers
return {
$pull: {
'poker.one': userId,
'poker.two': userId,
'poker.three': userId,
'poker.five': userId,
'poker.eight': userId,
'poker.thirteen': userId,
'poker.twenty': userId,
'poker.forty': userId,
'poker.oneHundred': userId,
'poker.unsure': userId,
},
};
if (pokerFields.includes(state)) {
delete pullFields[`poker.${state}`];
return await Cards.updateAsync(this._id, {
$pull: pullFields,
$addToSet: { [`poker.${state}`]: userId },
});
} else {
return await Cards.updateAsync(this._id, { $pull: pullFields });
}
},
replayPoker() {
return {
async replayPoker() {
return await Cards.updateAsync(this._id, {
$set: {
'poker.one': [],
'poker.two': [],
@ -2773,7 +2398,7 @@ Cards.mutations({
'poker.oneHundred': [],
'poker.unsure': [],
},
};
});
},
});
@ -4544,7 +4169,7 @@ JsonRoutes.add('GET', '/api/boards/:boardId/cards_count', function(
JsonRoutes.add(
'POST',
'/api/boards/:boardId/lists/:listId/cards/:cardId/archive',
function(req, res) {
async function(req, res) {
const paramBoardId = req.params.boardId;
const paramCardId = req.params.cardId;
const paramListId = req.params.listId;
@ -4558,7 +4183,7 @@ JsonRoutes.add('GET', '/api/boards/:boardId/cards_count', function(
if (!card) {
throw new Meteor.Error(404, 'Card not found');
}
card.archive();
await card.archive();
JsonRoutes.sendResult(res, {
code: 200,
data: {
@ -4583,7 +4208,7 @@ JsonRoutes.add('GET', '/api/boards/:boardId/cards_count', function(
JsonRoutes.add(
'POST',
'/api/boards/:boardId/lists/:listId/cards/:cardId/unarchive',
function(req, res) {
async function(req, res) {
const paramBoardId = req.params.boardId;
const paramCardId = req.params.cardId;
const paramListId = req.params.listId;
@ -4597,7 +4222,7 @@ JsonRoutes.add('GET', '/api/boards/:boardId/cards_count', function(
if (!card) {
throw new Meteor.Error(404, 'Card not found');
}
card.restore();
await card.restore();
JsonRoutes.sendResult(res, {
code: 200,
data: {

View file

@ -90,29 +90,24 @@ ChecklistItems.before.insert((userId, doc) => {
}
});
// Mutations
ChecklistItems.mutations({
setTitle(title) {
return { $set: { title } };
ChecklistItems.helpers({
async setTitle(title) {
return await ChecklistItems.updateAsync(this._id, { $set: { title } });
},
check() {
return { $set: { isFinished: true } };
async check() {
return await ChecklistItems.updateAsync(this._id, { $set: { isFinished: true } });
},
uncheck() {
return { $set: { isFinished: false } };
async uncheck() {
return await ChecklistItems.updateAsync(this._id, { $set: { isFinished: false } });
},
toggleItem() {
return { $set: { isFinished: !this.isFinished } };
async toggleItem() {
return await ChecklistItems.updateAsync(this._id, { $set: { isFinished: !this.isFinished } });
},
move(checklistId, sortIndex) {
async move(checklistId, sortIndex) {
const cardId = ReactiveCache.getChecklist(checklistId).cardId;
const mutatedFields = {
cardId,
checklistId,
sort: sortIndex,
};
return { $set: mutatedFields };
return await ChecklistItems.updateAsync(this._id, {
$set: { cardId, checklistId, sort: sortIndex },
});
},
});

View file

@ -150,22 +150,49 @@ Checklists.helpers({
}
return ret;
},
checkAllItems() {
async checkAllItems() {
const checkItems = ReactiveCache.getChecklistItems({ checklistId: this._id });
checkItems.forEach(function(item) {
item.check();
});
for (const item of checkItems) {
await item.check();
}
},
uncheckAllItems() {
async uncheckAllItems() {
const checkItems = ReactiveCache.getChecklistItems({ checklistId: this._id });
checkItems.forEach(function(item) {
item.uncheck();
});
for (const item of checkItems) {
await item.uncheck();
}
},
itemIndex(itemId) {
const items = ReactiveCache.getChecklist({ _id: this._id }).items;
return _.pluck(items, '_id').indexOf(itemId);
},
async setTitle(title) {
return await Checklists.updateAsync(this._id, { $set: { title } });
},
/** move the checklist to another card
* @param newCardId move the checklist to this cardId
*/
async move(newCardId) {
// Note: Activities and ChecklistItems updates are now handled server-side
// in the moveChecklist Meteor method to avoid client-side permission issues
return await Checklists.updateAsync(this._id, { $set: { cardId: newCardId } });
},
async toggleHideCheckedChecklistItems() {
return await Checklists.updateAsync(this._id, {
$set: { hideCheckedChecklistItems: !this.hideCheckedChecklistItems },
});
},
async toggleHideAllChecklistItems() {
return await Checklists.updateAsync(this._id, {
$set: { hideAllChecklistItems: !this.hideAllChecklistItems },
});
},
async toggleShowChecklistAtMinicard() {
return await Checklists.updateAsync(this._id, {
$set: { showChecklistAtMinicard: !this.showChecklistAtMinicard },
});
},
});
Checklists.allow({
@ -191,46 +218,6 @@ Checklists.before.insert((userId, doc) => {
}
});
Checklists.mutations({
setTitle(title) {
return { $set: { title } };
},
/** move the checklist to another card
* @param newCardId move the checklist to this cardId
*/
move(newCardId) {
// Note: Activities and ChecklistItems updates are now handled server-side
// in the moveChecklist Meteor method to avoid client-side permission issues
// update the checklist itself
return {
$set: {
cardId: newCardId,
},
};
},
toggleHideCheckedChecklistItems() {
return {
$set: {
hideCheckedChecklistItems: !this.hideCheckedChecklistItems,
}
};
},
toggleHideAllChecklistItems() {
return {
$set: {
hideAllChecklistItems: !this.hideAllChecklistItems,
}
};
},
toggleShowChecklistAtMinicard() {
return {
$set: {
showChecklistAtMinicard: !this.showChecklistAtMinicard,
}
};
},
});
if (Meteor.isServer) {
Meteor.methods({

View file

@ -382,14 +382,14 @@ export class CsvCreator {
}
}
create(board, currentBoardId) {
async create(board, currentBoardId) {
const isSandstorm =
Meteor.settings &&
Meteor.settings.public &&
Meteor.settings.public.sandstorm;
if (isSandstorm && currentBoardId) {
const currentBoard = ReactiveCache.getBoard(currentBoardId);
currentBoard.archive();
await currentBoard.archive();
}
this.mapHeadertoCardFieldIndex(board[0]);
const boardId = this.createBoard(board);

View file

@ -152,17 +152,14 @@ CustomFields.addToAllCards = cf => {
);
};
CustomFields.mutations({
addBoard(boardId) {
CustomFields.helpers({
async addBoard(boardId) {
if (boardId) {
return {
$push: {
boardIds: boardId,
},
};
} else {
return null;
return await CustomFields.updateAsync(this._id, {
$push: { boardIds: boardId },
});
}
return null;
},
});

View file

@ -226,7 +226,7 @@ Lists.helpers({
});
},
move(boardId, swimlaneId) {
async move(boardId, swimlaneId) {
const boardList = ReactiveCache.getList({
boardId,
title: this.title,
@ -235,9 +235,9 @@ Lists.helpers({
let listId;
if (boardList) {
listId = boardList._id;
this.cards().forEach(card => {
card.move(boardId, this._id, boardList._id);
});
for (const card of this.cards()) {
await card.move(boardId, this._id, boardList._id);
}
} else {
console.log('list.title:', this.title);
console.log('boardList:', boardList);
@ -251,9 +251,9 @@ Lists.helpers({
});
}
this.cards(swimlaneId).forEach(card => {
card.move(boardId, swimlaneId, listId);
});
for (const card of this.cards(swimlaneId)) {
await card.move(boardId, swimlaneId, listId);
}
},
cards(swimlaneId) {
@ -339,64 +339,58 @@ Lists.helpers({
const card = ReactiveCache.getCard({ listId: this._id });
return card && card.originRelativeUrl();
},
remove() {
Lists.remove({ _id: this._id });
async remove() {
return await Lists.removeAsync({ _id: this._id });
},
});
Lists.mutations({
rename(title) {
async rename(title) {
// Basic client-side validation - server will handle full sanitization
if (typeof title === 'string') {
// Basic length check to prevent abuse
const sanitizedTitle = title.length > 1000 ? title.substring(0, 1000) : title;
return { $set: { title: sanitizedTitle } };
return await Lists.updateAsync(this._id, { $set: { title: sanitizedTitle } });
}
return { $set: { title } };
return await Lists.updateAsync(this._id, { $set: { title } });
},
star(enable = true) {
return { $set: { starred: !!enable } };
async star(enable = true) {
return await Lists.updateAsync(this._id, { $set: { starred: !!enable } });
},
collapse(enable = true) {
return { $set: { collapsed: !!enable } };
async collapse(enable = true) {
return await Lists.updateAsync(this._id, { $set: { collapsed: !!enable } });
},
archive() {
async archive() {
if (this.isTemplateList()) {
this.cards().forEach(card => {
return card.archive();
});
for (const card of this.cards()) {
await card.archive();
}
}
return { $set: { archived: true, archivedAt: new Date() } };
return await Lists.updateAsync(this._id, { $set: { archived: true, archivedAt: new Date() } });
},
restore() {
async restore() {
if (this.isTemplateList()) {
this.allCards().forEach(card => {
return card.restore();
});
for (const card of this.allCards()) {
await card.restore();
}
}
return { $set: { archived: false } };
return await Lists.updateAsync(this._id, { $set: { archived: false } });
},
toggleSoftLimit(toggle) {
return { $set: { 'wipLimit.soft': toggle } };
async toggleSoftLimit(toggle) {
return await Lists.updateAsync(this._id, { $set: { 'wipLimit.soft': toggle } });
},
toggleWipLimit(toggle) {
return { $set: { 'wipLimit.enabled': toggle } };
async toggleWipLimit(toggle) {
return await Lists.updateAsync(this._id, { $set: { 'wipLimit.enabled': toggle } });
},
setWipLimit(limit) {
return { $set: { 'wipLimit.value': limit } };
async setWipLimit(limit) {
return await Lists.updateAsync(this._id, { $set: { 'wipLimit.value': limit } });
},
setColor(newColor) {
return {
$set: {
color: newColor,
},
};
async setColor(newColor) {
return await Lists.updateAsync(this._id, { $set: { color: newColor } });
},
});
@ -422,49 +416,49 @@ Lists.archivedListIds = () => {
};
Meteor.methods({
applyWipLimit(listId, limit) {
async applyWipLimit(listId, limit) {
check(listId, String);
check(limit, Number);
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'You must be logged in.');
}
const list = ReactiveCache.getList(listId);
if (!list) {
throw new Meteor.Error('list-not-found', 'List not found');
}
const board = ReactiveCache.getBoard(list.boardId);
if (!board || !board.hasAdmin(this.userId)) {
throw new Meteor.Error('not-authorized', 'You must be a board admin to modify WIP limits.');
}
if (limit === 0) {
limit = 1;
}
list.setWipLimit(limit);
await list.setWipLimit(limit);
},
enableWipLimit(listId) {
async enableWipLimit(listId) {
check(listId, String);
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'You must be logged in.');
}
const list = ReactiveCache.getList(listId);
if (!list) {
throw new Meteor.Error('list-not-found', 'List not found');
}
const board = ReactiveCache.getBoard(list.boardId);
if (!board || !board.hasAdmin(this.userId)) {
throw new Meteor.Error('not-authorized', 'You must be a board admin to modify WIP limits.');
}
if (list.getWipLimit('value') === 0) {
list.setWipLimit(1);
await list.setWipLimit(1);
}
list.toggleWipLimit(!list.getWipLimit('enabled'));
},

View file

@ -50,13 +50,10 @@ Rules.attachSchema(
}),
);
Rules.mutations({
rename(description) {
return { $set: { description } };
},
});
Rules.helpers({
async rename(description) {
return await Rules.updateAsync(this._id, { $set: { description } });
},
getAction() {
return ReactiveCache.getAction(this.actionId);
},

View file

@ -171,8 +171,8 @@ Swimlanes.helpers({
});
},
move(toBoardId) {
this.lists().forEach(list => {
async move(toBoardId) {
for (const list of this.lists()) {
const toList = ReactiveCache.getList({
boardId: toBoardId,
title: list.title,
@ -183,25 +183,26 @@ Swimlanes.helpers({
if (toList) {
toListId = toList._id;
} else {
toListId = Lists.insert({
toListId = await Lists.insertAsync({
title: list.title,
boardId: toBoardId,
type: list.type,
archived: false,
wipLimit: list.wipLimit,
swimlaneId: toSwimlaneId, // Set the target swimlane for the copied list
swimlaneId: this._id,
});
}
ReactiveCache.getCards({
const cards = ReactiveCache.getCards({
listId: list._id,
swimlaneId: this._id,
}).forEach(card => {
card.move(toBoardId, this._id, toListId);
});
});
for (const card of cards) {
await card.move(toBoardId, this._id, toListId);
}
}
Swimlanes.update(this._id, {
await Swimlanes.updateAsync(this._id, {
$set: {
boardId: toBoardId,
},
@ -314,43 +315,37 @@ Swimlanes.helpers({
return (user.profile || {}).boardTemplatesSwimlaneId === this._id;
},
remove() {
Swimlanes.remove({ _id: this._id });
async remove() {
return await Swimlanes.removeAsync({ _id: this._id });
},
});
Swimlanes.mutations({
rename(title) {
return { $set: { title } };
async rename(title) {
return await Swimlanes.updateAsync(this._id, { $set: { title } });
},
// NOTE: collapse() removed - collapsed state is per-user only
// Use user.setCollapsedSwimlane(boardId, swimlaneId, collapsed) instead
archive() {
async archive() {
if (this.isTemplateSwimlane()) {
this.myLists().forEach(list => {
return list.archive();
});
for (const list of this.myLists()) {
await list.archive();
}
}
return { $set: { archived: true, archivedAt: new Date() } };
return await Swimlanes.updateAsync(this._id, { $set: { archived: true, archivedAt: new Date() } });
},
restore() {
async restore() {
if (this.isTemplateSwimlane()) {
this.myLists().forEach(list => {
return list.restore();
});
for (const list of this.myLists()) {
await list.restore();
}
}
return { $set: { archived: false } };
return await Swimlanes.updateAsync(this._id, { $set: { archived: false } });
},
setColor(newColor) {
return {
$set: {
color: newColor,
},
};
async setColor(newColor) {
return await Swimlanes.updateAsync(this._id, { $set: { color: newColor } });
},
});

View file

@ -767,7 +767,7 @@ export class TrelloCreator {
}
}
create(board, currentBoardId) {
async create(board, currentBoardId) {
// TODO : Make isSandstorm variable global
const isSandstorm =
Meteor.settings &&
@ -775,7 +775,7 @@ export class TrelloCreator {
Meteor.settings.public.sandstorm;
if (isSandstorm && currentBoardId) {
const currentBoard = ReactiveCache.getBoard(currentBoardId);
currentBoard.archive();
await currentBoard.archive();
}
this.parseActions(board.actions);
const boardId = this.createBoardAndLabels(board);

View file

@ -3,16 +3,6 @@ import { Meteor } from 'meteor/meteor';
Triggers = new Mongo.Collection('triggers');
Triggers.mutations({
rename(description) {
return {
$set: {
description,
},
};
},
});
Triggers.before.insert((userId, doc) => {
doc.createdAt = new Date();
doc.updatedAt = doc.createdAt;
@ -36,6 +26,12 @@ Triggers.allow({
});
Triggers.helpers({
async rename(description) {
return await Triggers.updateAsync(this._id, {
$set: { description },
});
},
description() {
return this.desc;
},

View file

@ -1593,376 +1593,206 @@ Users.helpers({
}
return null;
},
});
Users.mutations({
/** set the confirmed board id/swimlane id/list id of a board
* @param boardId the current board id
* @param options an object with the confirmed field values
*/
setMoveAndCopyDialogOption(boardId, options) {
async setMoveAndCopyDialogOption(boardId, options) {
let currentOptions = this.getMoveAndCopyDialogOptions();
currentOptions[boardId] = options;
return {
$set: {
'profile.moveAndCopyDialog': currentOptions,
},
};
return await Users.updateAsync(this._id, { $set: { 'profile.moveAndCopyDialog': currentOptions } });
},
/** set the confirmed board id/swimlane id/list id/card id of a board (move checklist)
* @param boardId the current board id
* @param options an object with the confirmed field values
*/
setMoveChecklistDialogOption(boardId, options) {
async setMoveChecklistDialogOption(boardId, options) {
let currentOptions = this.getMoveChecklistDialogOptions();
currentOptions[boardId] = options;
return {
$set: {
'profile.moveChecklistDialog': currentOptions,
},
};
return await Users.updateAsync(this._id, { $set: { 'profile.moveChecklistDialog': currentOptions } });
},
/** set the confirmed board id/swimlane id/list id/card id of a board (copy checklist)
* @param boardId the current board id
* @param options an object with the confirmed field values
*/
setCopyChecklistDialogOption(boardId, options) {
async setCopyChecklistDialogOption(boardId, options) {
let currentOptions = this.getCopyChecklistDialogOptions();
currentOptions[boardId] = options;
return {
$set: {
'profile.copyChecklistDialog': currentOptions,
},
};
return await Users.updateAsync(this._id, { $set: { 'profile.copyChecklistDialog': currentOptions } });
},
toggleBoardStar(boardId) {
async toggleBoardStar(boardId) {
const queryKind = this.hasStarred(boardId) ? '$pull' : '$addToSet';
return {
[queryKind]: {
'profile.starredBoards': boardId,
},
};
return await Users.updateAsync(this._id, { [queryKind]: { 'profile.starredBoards': boardId } });
},
/**
* Set per-user board sort index for a board
* Stored at profile.boardSortIndex[boardId] = sortIndex (Number)
*/
setBoardSortIndex(boardId, sortIndex) {
async setBoardSortIndex(boardId, sortIndex) {
const mapping = (this.profile && this.profile.boardSortIndex) || {};
mapping[boardId] = sortIndex;
return {
$set: {
'profile.boardSortIndex': mapping,
},
};
return await Users.updateAsync(this._id, { $set: { 'profile.boardSortIndex': mapping } });
},
toggleAutoWidth(boardId) {
async toggleAutoWidth(boardId) {
const { autoWidthBoards = {} } = this.profile || {};
autoWidthBoards[boardId] = !autoWidthBoards[boardId];
return {
$set: {
'profile.autoWidthBoards': autoWidthBoards,
},
};
return await Users.updateAsync(this._id, { $set: { 'profile.autoWidthBoards': autoWidthBoards } });
},
toggleKeyboardShortcuts() {
async toggleKeyboardShortcuts() {
const { keyboardShortcuts = true } = this.profile || {};
return {
$set: {
'profile.keyboardShortcuts': !keyboardShortcuts,
},
};
return await Users.updateAsync(this._id, { $set: { 'profile.keyboardShortcuts': !keyboardShortcuts } });
},
toggleVerticalScrollbars() {
async toggleVerticalScrollbars() {
const { verticalScrollbars = true } = this.profile || {};
return {
$set: {
'profile.verticalScrollbars': !verticalScrollbars,
},
};
return await Users.updateAsync(this._id, { $set: { 'profile.verticalScrollbars': !verticalScrollbars } });
},
toggleShowWeekOfYear() {
async toggleShowWeekOfYear() {
const { showWeekOfYear = true } = this.profile || {};
return {
$set: {
'profile.showWeekOfYear': !showWeekOfYear,
},
};
return await Users.updateAsync(this._id, { $set: { 'profile.showWeekOfYear': !showWeekOfYear } });
},
addInvite(boardId) {
return {
$addToSet: {
'profile.invitedBoards': boardId,
},
};
async addInvite(boardId) {
return await Users.updateAsync(this._id, { $addToSet: { 'profile.invitedBoards': boardId } });
},
removeInvite(boardId) {
return {
$pull: {
'profile.invitedBoards': boardId,
},
};
async removeInvite(boardId) {
return await Users.updateAsync(this._id, { $pull: { 'profile.invitedBoards': boardId } });
},
addTag(tag) {
return {
$addToSet: {
'profile.tags': tag,
},
};
async addTag(tag) {
return await Users.updateAsync(this._id, { $addToSet: { 'profile.tags': tag } });
},
removeTag(tag) {
return {
$pull: {
'profile.tags': tag,
},
};
async removeTag(tag) {
return await Users.updateAsync(this._id, { $pull: { 'profile.tags': tag } });
},
toggleTag(tag) {
if (this.hasTag(tag)) this.removeTag(tag);
else this.addTag(tag);
async toggleTag(tag) {
if (this.hasTag(tag)) {
return await this.removeTag(tag);
} else {
return await this.addTag(tag);
}
},
setListSortBy(value) {
return {
$set: {
'profile.listSortBy': value,
},
};
async setListSortBy(value) {
return await Users.updateAsync(this._id, { $set: { 'profile.listSortBy': value } });
},
setName(value) {
return {
$set: {
'profile.fullname': value,
},
};
async setName(value) {
return await Users.updateAsync(this._id, { $set: { 'profile.fullname': value } });
},
toggleDesktopHandles(value = false) {
return {
$set: {
'profile.showDesktopDragHandles': !value,
},
};
async toggleDesktopHandles(value = false) {
return await Users.updateAsync(this._id, { $set: { 'profile.showDesktopDragHandles': !value } });
},
toggleFieldsGrid(value = false) {
return {
$set: {
'profile.customFieldsGrid': !value,
},
};
async toggleFieldsGrid(value = false) {
return await Users.updateAsync(this._id, { $set: { 'profile.customFieldsGrid': !value } });
},
toggleCardMaximized(value = false) {
return {
$set: {
'profile.cardMaximized': !value,
},
};
async toggleCardMaximized(value = false) {
return await Users.updateAsync(this._id, { $set: { 'profile.cardMaximized': !value } });
},
toggleCardCollapsed(value = false) {
return {
$set: {
'profile.cardCollapsed': !value,
},
};
async toggleCardCollapsed(value = false) {
return await Users.updateAsync(this._id, { $set: { 'profile.cardCollapsed': !value } });
},
toggleShowActivities(value = false) {
return {
$set: {
'profile.showActivities': !value,
},
};
async toggleShowActivities(value = false) {
return await Users.updateAsync(this._id, { $set: { 'profile.showActivities': !value } });
},
toggleLabelText(value = false) {
return {
$set: {
'profile.hiddenMinicardLabelText': !value,
},
};
},
toggleRescueCardDescription(value = false) {
return {
$set: {
'profile.rescueCardDescription': !value,
},
};
},
toggleGreyIcons(value = false) {
return {
$set: {
'profile.GreyIcons': !value,
},
};
async toggleLabelText(value = false) {
return await Users.updateAsync(this._id, { $set: { 'profile.hiddenMinicardLabelText': !value } });
},
addNotification(activityId) {
return {
$addToSet: {
'profile.notifications': {
activity: activityId,
read: null,
},
},
};
async toggleRescueCardDescription(value = false) {
return await Users.updateAsync(this._id, { $set: { 'profile.rescueCardDescription': !value } });
},
removeNotification(activityId) {
return {
$pull: {
'profile.notifications': {
activity: activityId,
},
},
};
async toggleGreyIcons(value = false) {
return await Users.updateAsync(this._id, { $set: { 'profile.GreyIcons': !value } });
},
addEmailBuffer(text) {
return {
$addToSet: {
'profile.emailBuffer': text,
},
};
async addNotification(activityId) {
return await Users.updateAsync(this._id, {
$addToSet: { 'profile.notifications': { activity: activityId, read: null } },
});
},
clearEmailBuffer() {
return {
$set: {
'profile.emailBuffer': [],
},
};
async removeNotification(activityId) {
return await Users.updateAsync(this._id, {
$pull: { 'profile.notifications': { activity: activityId } },
});
},
setAvatarUrl(avatarUrl) {
return {
$set: {
'profile.avatarUrl': avatarUrl,
},
};
async addEmailBuffer(text) {
return await Users.updateAsync(this._id, { $addToSet: { 'profile.emailBuffer': text } });
},
setShowCardsCountAt(limit) {
return {
$set: {
'profile.showCardsCountAt': limit,
},
};
async clearEmailBuffer() {
return await Users.updateAsync(this._id, { $set: { 'profile.emailBuffer': [] } });
},
setStartDayOfWeek(startDay) {
return {
$set: {
'profile.startDayOfWeek': startDay,
},
};
async setAvatarUrl(avatarUrl) {
return await Users.updateAsync(this._id, { $set: { 'profile.avatarUrl': avatarUrl } });
},
setDateFormat(dateFormat) {
return {
$set: {
'profile.dateFormat': dateFormat,
},
};
async setShowCardsCountAt(limit) {
return await Users.updateAsync(this._id, { $set: { 'profile.showCardsCountAt': limit } });
},
setBoardView(view) {
return {
$set: {
'profile.boardView': view,
},
};
async setStartDayOfWeek(startDay) {
return await Users.updateAsync(this._id, { $set: { 'profile.startDayOfWeek': startDay } });
},
setListWidth(boardId, listId, width) {
async setDateFormat(dateFormat) {
return await Users.updateAsync(this._id, { $set: { 'profile.dateFormat': dateFormat } });
},
async setBoardView(view) {
return await Users.updateAsync(this._id, { $set: { 'profile.boardView': view } });
},
async setListWidth(boardId, listId, width) {
let currentWidths = this.getListWidths();
if (!currentWidths[boardId]) {
currentWidths[boardId] = {};
}
if (!currentWidths[boardId]) currentWidths[boardId] = {};
currentWidths[boardId][listId] = width;
return {
$set: {
'profile.listWidths': currentWidths,
},
};
return await Users.updateAsync(this._id, { $set: { 'profile.listWidths': currentWidths } });
},
setListConstraint(boardId, listId, constraint) {
async setListConstraint(boardId, listId, constraint) {
let currentConstraints = this.getListConstraints();
if (!currentConstraints[boardId]) {
currentConstraints[boardId] = {};
}
if (!currentConstraints[boardId]) currentConstraints[boardId] = {};
currentConstraints[boardId][listId] = constraint;
return {
$set: {
'profile.listConstraints': currentConstraints,
},
};
return await Users.updateAsync(this._id, { $set: { 'profile.listConstraints': currentConstraints } });
},
setSwimlaneHeight(boardId, swimlaneId, height) {
async setSwimlaneHeight(boardId, swimlaneId, height) {
let currentHeights = this.getSwimlaneHeights();
if (!currentHeights[boardId]) {
currentHeights[boardId] = {};
}
if (!currentHeights[boardId]) currentHeights[boardId] = {};
currentHeights[boardId][swimlaneId] = height;
return {
$set: {
'profile.swimlaneHeights': currentHeights,
},
};
return await Users.updateAsync(this._id, { $set: { 'profile.swimlaneHeights': currentHeights } });
},
setCollapsedList(boardId, listId, collapsed) {
async setCollapsedList(boardId, listId, collapsed) {
const current = (this.profile && this.profile.collapsedLists) || {};
if (!current[boardId]) current[boardId] = {};
current[boardId][listId] = !!collapsed;
return {
$set: {
'profile.collapsedLists': current,
},
};
return await Users.updateAsync(this._id, { $set: { 'profile.collapsedLists': current } });
},
setCollapsedSwimlane(boardId, swimlaneId, collapsed) {
async setCollapsedSwimlane(boardId, swimlaneId, collapsed) {
const current = (this.profile && this.profile.collapsedSwimlanes) || {};
if (!current[boardId]) current[boardId] = {};
current[boardId][swimlaneId] = !!collapsed;
return {
$set: {
'profile.collapsedSwimlanes': current,
},
};
return await Users.updateAsync(this._id, { $set: { 'profile.collapsedSwimlanes': current } });
},
setZoomLevel(level) {
return {
$set: {
'profile.zoomLevel': level,
},
};
async setZoomLevel(level) {
return await Users.updateAsync(this._id, { $set: { 'profile.zoomLevel': level } });
},
setMobileMode(enabled) {
return {
$set: {
'profile.mobileMode': enabled,
},
};
async setMobileMode(enabled) {
return await Users.updateAsync(this._id, { $set: { 'profile.mobileMode': enabled } });
},
setCardZoom(level) {
return {
$set: {
'profile.cardZoom': level,
},
};
async setCardZoom(level) {
return await Users.updateAsync(this._id, { $set: { 'profile.cardZoom': level } });
},
});
@ -3340,7 +3170,7 @@ if (Meteor.isServer) {
* @return_type {_id: string,
* title: string}
*/
JsonRoutes.add('PUT', '/api/users/:userId', function (req, res) {
JsonRoutes.add('PUT', '/api/users/:userId', async function (req, res) {
try {
Authentication.checkUserId(req.userId);
const id = req.params.userId;
@ -3350,7 +3180,7 @@ if (Meteor.isServer) {
});
if (data !== undefined) {
if (action === 'takeOwnership') {
data = ReactiveCache.getBoards(
const boards = ReactiveCache.getBoards(
{
'members.userId': id,
'members.isAdmin': true,
@ -3360,16 +3190,18 @@ if (Meteor.isServer) {
sort: 1 /* boards default sorting */,
},
},
).map(function (board) {
);
data = [];
for (const board of boards) {
if (board.hasMember(req.userId)) {
board.removeMember(req.userId);
await board.removeMember(req.userId);
}
board.changeOwnership(id, req.userId);
return {
data.push({
_id: board._id,
title: board.title,
};
});
});
}
} else {
if (action === 'disableLogin' && id !== req.userId) {
Users.update(

View file

@ -19,13 +19,13 @@ const simpleWatchable = collection => {
findWatcher(userId) {
return _.contains(this.watchers, userId);
},
});
collection.mutations({
setWatcher(userId, level) {
async setWatcher(userId, level) {
// if level undefined or null or false, then remove
if (!level) return { $pull: { watchers: userId } };
return { $addToSet: { watchers: userId } };
if (!level) {
return await collection.updateAsync(this._id, { $pull: { watchers: userId } });
}
return await collection.updateAsync(this._id, { $addToSet: { watchers: userId } });
},
});
};
@ -66,20 +66,20 @@ const complexWatchable = collection => {
const watcher = this.findWatcher(userId);
return watcher ? watcher.level : complexWatchDefault;
},
});
collection.mutations({
setWatcher(userId, level) {
async setWatcher(userId, level) {
// if level undefined or null or false, then remove
if (level === complexWatchDefault) level = null;
if (!level) return { $pull: { watchers: { userId } } };
if (!level) {
return await collection.updateAsync(this._id, { $pull: { watchers: { userId } } });
}
const index = this.watcherIndex(userId);
if (index < 0) return { $push: { watchers: { userId, level } } };
return {
$set: {
[`watchers.${index}.level`]: level,
},
};
if (index < 0) {
return await collection.updateAsync(this._id, { $push: { watchers: { userId, level } } });
}
return await collection.updateAsync(this._id, {
$set: { [`watchers.${index}.level`]: level },
});
},
});
};

View file

@ -970,7 +970,7 @@ export class WekanCreator {
// }
}
create(board, currentBoardId) {
async create(board, currentBoardId) {
// TODO : Make isSandstorm variable global
const isSandstorm =
Meteor.settings &&
@ -978,7 +978,7 @@ export class WekanCreator {
Meteor.settings.public.sandstorm;
if (isSandstorm && currentBoardId) {
const currentBoard = ReactiveCache.getBoard(currentBoardId);
currentBoard.archive();
await currentBoard.archive();
}
this.parseActivities(board);
const boardId = this.createBoardAndLabels(board);

View file

@ -319,7 +319,7 @@ Meteor.methods({
});
Meteor.methods({
'boardRoutineOnLogin': function(info, oidcUserId)
'boardRoutineOnLogin': async function(info, oidcUserId)
{
check(info, Object);
check(oidcUserId, String);
@ -333,8 +333,8 @@ Meteor.methods({
const memberIndex = _.pluck(board?.members, 'userId').indexOf(userId);
if(!board || !userId || memberIndex > -1) return
board.addMember(userId)
board.setMemberPermission(
await board.addMember(userId)
await board.setMemberPermission(
userId,
defaultBoardParams.contains("isAdmin"),
defaultBoardParams.contains("isNoComments"),

View file

@ -47,7 +47,7 @@ do
# Latest fibers for Meteor sudo mkdir -p /usr/local/lib/node_modules/fibers/.node-gyp sudo npm -g install fibers
sudo npm -g install @mapbox/node-pre-gyp
# Install Meteor, if it's not yet installed
sudo npm -g install meteor@2.14 --unsafe-perm
sudo npm -g install meteor@2.16 --unsafe-perm
#sudo chown -R $(id -u):$(id -g) $HOME/.npm $HOME/.meteor
elif [[ "$OSTYPE" == "darwin"* ]]; then
echo "macOS"
@ -89,7 +89,7 @@ do
npm -g uninstall node-pre-gyp
npm -g install @mapbox/node-pre-gyp
npm -g install node-gyp
npm -g install meteor@2.14
npm -g install meteor@2.16
export PATH=~/.meteor:$PATH
exit;
elif [[ "$OSTYPE" == "cygwin" ]]; then

View file

@ -1,7 +1,7 @@
import { ReactiveCache } from '/imports/reactiveCache';
Meteor.methods({
watch(watchableType, id, level) {
async watch(watchableType, id, level) {
check(watchableType, String);
check(id, String);
check(level, Match.OneOf(String, null));
@ -29,7 +29,7 @@ Meteor.methods({
if (board.permission === 'private' && !board.hasMember(userId))
throw new Meteor.Error('error-board-notAMember');
watchableObj.setWatcher(userId, level);
await watchableObj.setWatcher(userId, level);
return true;
},
});

View file

@ -17,7 +17,7 @@ Meteor.methods({
return ret;
},
moveSwimlane(swimlaneId, toBoardId) {
async moveSwimlane(swimlaneId, toBoardId) {
check(swimlaneId, String);
check(toBoardId, String);
@ -26,7 +26,7 @@ Meteor.methods({
let ret = false;
if (swimlane && toBoard) {
swimlane.move(toBoardId);
await swimlane.move(toBoardId);
ret = true;
}

View file

@ -1,12 +1,12 @@
import { ReactiveCache } from '/imports/reactiveCache';
RulesHelper = {
executeRules(activity) {
async executeRules(activity) {
const matchingRules = this.findMatchingRules(activity);
for (let i = 0; i < matchingRules.length; i++) {
const action = matchingRules[i].getAction();
if (action !== undefined) {
this.performAction(activity, action);
await this.performAction(activity, action);
}
}
},
@ -57,7 +57,7 @@ RulesHelper = {
});
return matchingMap;
},
performAction(activity, action) {
async performAction(activity, action) {
const card = ReactiveCache.getCard(activity.cardId);
const boardId = activity.boardId;
if (
@ -112,12 +112,12 @@ RulesHelper = {
const minOrder = _.min(
list.cardsUnfiltered(swimlaneId).map(c => c.sort),
);
card.move(action.boardId, swimlaneId, listId, minOrder - 1);
await card.move(action.boardId, swimlaneId, listId, minOrder - 1);
} else {
const maxOrder = _.max(
list.cardsUnfiltered(swimlaneId).map(c => c.sort),
);
card.move(action.boardId, swimlaneId, listId, maxOrder + 1);
await card.move(action.boardId, swimlaneId, listId, maxOrder + 1);
}
}
if (action.actionType === 'sendEmail') {
@ -247,13 +247,13 @@ RulesHelper = {
}
}
if (action.actionType === 'archive') {
card.archive();
await card.archive();
}
if (action.actionType === 'unarchive') {
card.restore();
await card.restore();
}
if (action.actionType === 'setColor') {
card.setColor(action.selectedColor);
await card.setColor(action.selectedColor);
}
if (action.actionType === 'addLabel') {
card.addLabel(action.labelId);
@ -281,14 +281,14 @@ RulesHelper = {
title: action.checklistName,
cardId: card._id,
});
checkList.checkAllItems();
await checkList.checkAllItems();
}
if (action.actionType === 'uncheckAll') {
const checkList = ReactiveCache.getChecklist({
title: action.checklistName,
cardId: card._id,
});
checkList.uncheckAllItems();
await checkList.uncheckAllItems();
}
if (action.actionType === 'checkItem') {
const checkList = ReactiveCache.getChecklist({
@ -299,7 +299,7 @@ RulesHelper = {
title: action.checkItemName,
checkListId: checkList._id,
});
checkItem.check();
await checkItem.check();
}
if (action.actionType === 'uncheckItem') {
const checkList = ReactiveCache.getChecklist({
@ -310,7 +310,7 @@ RulesHelper = {
title: action.checkItemName,
checkListId: checkList._id,
});
checkItem.uncheck();
await checkItem.uncheck();
}
if (action.actionType === 'addChecklist') {
Checklists.insert({