Update ReactiveCache call sites to use async/await for Meteor 3.0

Part 3 of ReactiveCache async migration:
- Add await before all ReactiveCache.getX() calls
- Make functions containing ReactiveCache calls async
- Convert forEach/map/filter loops with async callbacks to for...of
- Update model helpers, Meteor methods, JsonRoutes handlers
- Update collection hooks (.before/.after insert/update/remove)
- Update .allow() callbacks to async

Files updated across models/ and server/ directories:
- Model files: cards, boards, lists, swimlanes, activities, users,
  checklists, checklistItems, customFields, attachments, integrations,
  cardComments, settings files, creators, exporters, and more
- Server files: publications, methods, notifications, routes, migrations
This commit is contained in:
Harry Adel 2026-02-01 00:54:38 +02:00
parent 2f6e34c5f5
commit 71eb01e233
81 changed files with 2218 additions and 2148 deletions

View file

@ -46,8 +46,8 @@ AccessibilitySettings.attachSchema(
);
AccessibilitySettings.allow({
update(userId) {
const user = ReactiveCache.getUser(userId);
async update(userId) {
const user = await ReactiveCache.getUser(userId);
return user && user.isAdmin;
},
});

View file

@ -45,8 +45,8 @@ AccountSettings.attachSchema(
);
AccountSettings.allow({
update(userId) {
const user = ReactiveCache.getUser(userId);
async update(userId) {
const user = await ReactiveCache.getUser(userId);
return user && user.isAdmin;
},
});

View file

@ -4,14 +4,14 @@ import { Meteor } from 'meteor/meteor';
Actions = new Mongo.Collection('actions');
Actions.allow({
insert(userId, doc) {
return allowIsBoardAdmin(userId, ReactiveCache.getBoard(doc.boardId));
async insert(userId, doc) {
return allowIsBoardAdmin(userId, await ReactiveCache.getBoard(doc.boardId));
},
update(userId, doc) {
return allowIsBoardAdmin(userId, ReactiveCache.getBoard(doc.boardId));
async update(userId, doc) {
return allowIsBoardAdmin(userId, await ReactiveCache.getBoard(doc.boardId));
},
remove(userId, doc) {
return allowIsBoardAdmin(userId, ReactiveCache.getBoard(doc.boardId));
async remove(userId, doc) {
return allowIsBoardAdmin(userId, await ReactiveCache.getBoard(doc.boardId));
},
});

View file

@ -13,54 +13,54 @@ import { ReactiveCache } from '/imports/reactiveCache';
Activities = new Mongo.Collection('activities');
Activities.helpers({
board() {
return ReactiveCache.getBoard(this.boardId);
async board() {
return await ReactiveCache.getBoard(this.boardId);
},
oldBoard() {
return ReactiveCache.getBoard(this.oldBoardId);
async oldBoard() {
return await ReactiveCache.getBoard(this.oldBoardId);
},
user() {
return ReactiveCache.getUser(this.userId);
async user() {
return await ReactiveCache.getUser(this.userId);
},
member() {
return ReactiveCache.getUser(this.memberId);
async member() {
return await ReactiveCache.getUser(this.memberId);
},
list() {
return ReactiveCache.getList(this.listId);
async list() {
return await ReactiveCache.getList(this.listId);
},
swimlane() {
return ReactiveCache.getSwimlane(this.swimlaneId);
async swimlane() {
return await ReactiveCache.getSwimlane(this.swimlaneId);
},
oldSwimlane() {
return ReactiveCache.getSwimlane(this.oldSwimlaneId);
async oldSwimlane() {
return await ReactiveCache.getSwimlane(this.oldSwimlaneId);
},
oldList() {
return ReactiveCache.getList(this.oldListId);
async oldList() {
return await ReactiveCache.getList(this.oldListId);
},
card() {
return ReactiveCache.getCard(this.cardId);
async card() {
return await ReactiveCache.getCard(this.cardId);
},
comment() {
return ReactiveCache.getCardComment(this.commentId);
async comment() {
return await ReactiveCache.getCardComment(this.commentId);
},
attachment() {
return ReactiveCache.getAttachment(this.attachmentId);
async attachment() {
return await ReactiveCache.getAttachment(this.attachmentId);
},
checklist() {
return ReactiveCache.getChecklist(this.checklistId);
async checklist() {
return await ReactiveCache.getChecklist(this.checklistId);
},
checklistItem() {
return ReactiveCache.getChecklistItem(this.checklistItemId);
async checklistItem() {
return await ReactiveCache.getChecklistItem(this.checklistItemId);
},
subtasks() {
return ReactiveCache.getCard(this.subtaskId);
async subtasks() {
return await ReactiveCache.getCard(this.subtaskId);
},
customField() {
return ReactiveCache.getCustomField(this.customFieldId);
async customField() {
return await ReactiveCache.getCustomField(this.customFieldId);
},
label() {
async label() {
// Label activity did not work yet, unable to edit labels when tried this.
return ReactiveCache.getCard(this.labelId);
return await ReactiveCache.getCard(this.labelId);
},
});
@ -105,12 +105,12 @@ if (Meteor.isServer) {
//Activities._collection.dropIndex({ labelId: 1 }, { partialFilterExpression: { labelId: { $exists: true } } });
});
Activities.after.insert((userId, doc) => {
Activities.after.insert(async (userId, doc) => {
const activity = Activities._transform(doc);
let participants = [];
let watchers = [];
let title = 'act-activity-notify';
const board = ReactiveCache.getBoard(activity.boardId);
const board = await ReactiveCache.getBoard(activity.boardId);
const description = `act-${activity.activityType}`;
const params = {
activityId: activity._id,
@ -203,14 +203,16 @@ if (Meteor.isServer) {
let hasMentions = false; // Track if comment has @mentions
if (board) {
const comment = params.comment;
const knownUsers = board.members.map((member) => {
const u = ReactiveCache.getUser(member.userId);
// Build knownUsers with async user lookups
const knownUsers = [];
for (const member of board.members) {
const u = await ReactiveCache.getUser(member.userId);
if (u) {
member.username = u.username;
member.emails = u.emails;
}
return member;
});
knownUsers.push(member);
}
// Match @mentions including usernames with @ symbols (like email addresses)
// Pattern matches: @username, @user@example.com, @"quoted username"
const mentionRegex = /\B@(?:(?:"([\w.\s-]*)")|([\w.@-]+))/gi;
@ -225,30 +227,31 @@ if (Meteor.isServer) {
if (activity.boardId && username === 'board_members') {
// mentions all board members
const validUserIds = knownUsers
.map((u) => u.userId)
.filter((userId) => {
const user = ReactiveCache.getUser(userId);
return user && user._id;
});
const validUserIds = [];
for (const u of knownUsers) {
const user = await ReactiveCache.getUser(u.userId);
if (user && user._id) {
validUserIds.push(u.userId);
}
}
watchers = _.union(watchers, validUserIds);
title = 'act-atUserComment';
hasMentions = true;
} else if (activity.boardId && username === 'board_assignees') {
// mentions all assignees of all cards on the board
const allCards = ReactiveCache.getCards({ boardId: activity.boardId });
const allCards = await ReactiveCache.getCards({ boardId: activity.boardId });
const assigneeIds = [];
allCards.forEach((card) => {
for (const card of allCards) {
if (card.assignees && card.assignees.length > 0) {
card.assignees.forEach((assigneeId) => {
for (const assigneeId of card.assignees) {
// Only add if the user exists and is a board member
const user = ReactiveCache.getUser(assigneeId);
const user = await ReactiveCache.getUser(assigneeId);
if (user && _.findWhere(knownUsers, { userId: assigneeId })) {
assigneeIds.push(assigneeId);
}
});
}
}
});
}
watchers = _.union(watchers, assigneeIds);
title = 'act-atUserComment';
hasMentions = true;
@ -257,10 +260,13 @@ if (Meteor.isServer) {
const card = activity.card();
if (card && card.members && card.members.length > 0) {
// Filter to only valid users who are board members
const validMembers = card.members.filter((memberId) => {
const user = ReactiveCache.getUser(memberId);
return user && user._id && _.findWhere(knownUsers, { userId: memberId });
});
const validMembers = [];
for (const memberId of card.members) {
const user = await ReactiveCache.getUser(memberId);
if (user && user._id && _.findWhere(knownUsers, { userId: memberId })) {
validMembers.push(memberId);
}
}
watchers = _.union(watchers, validMembers);
}
title = 'act-atUserComment';
@ -270,10 +276,13 @@ if (Meteor.isServer) {
const card = activity.card();
if (card && card.assignees && card.assignees.length > 0) {
// Filter to only valid users who are board members
const validAssignees = card.assignees.filter((assigneeId) => {
const user = ReactiveCache.getUser(assigneeId);
return user && user._id && _.findWhere(knownUsers, { userId: assigneeId });
});
const validAssignees = [];
for (const assigneeId of card.assignees) {
const user = await ReactiveCache.getUser(assigneeId);
if (user && user._id && _.findWhere(knownUsers, { userId: assigneeId })) {
validAssignees.push(assigneeId);
}
}
watchers = _.union(watchers, validAssignees);
}
title = 'act-atUserComment';
@ -390,7 +399,7 @@ if (Meteor.isServer) {
Notifications.getUsers(watchers).forEach((user) => {
// Skip if user is undefined or doesn't have an _id (e.g., deleted user or invalid ID)
if (!user || !user._id) return;
// Don't notify a user of their own behavior, EXCEPT for self-mentions
const isSelfMention = (user._id === userId && title === 'act-atUserComment');
if (user._id !== userId || isSelfMention) {
@ -398,7 +407,7 @@ if (Meteor.isServer) {
}
});
const integrations = ReactiveCache.getIntegrations({
const integrations = await ReactiveCache.getIntegrations({
boardId: { $in: [board._id, Integrations.Const.GLOBAL_WEBHOOK_ID] },
// type: 'outgoing-webhooks', // all types
enabled: true,

View file

@ -50,8 +50,8 @@ Announcements.attachSchema(
);
Announcements.allow({
update(userId) {
const user = ReactiveCache.getUser(userId);
async update(userId) {
const user = await ReactiveCache.getUser(userId);
return user && user.isAdmin;
},
});

View file

@ -257,18 +257,18 @@ AttachmentStorageSettings.helpers({
if (Meteor.isServer) {
// Get or create default settings
Meteor.methods({
'getAttachmentStorageSettings'() {
async 'getAttachmentStorageSettings'() {
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
const user = ReactiveCache.getUser(this.userId);
const user = await ReactiveCache.getUser(this.userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('not-authorized', 'Admin access required');
}
let settings = AttachmentStorageSettings.findOne({});
if (!settings) {
// Create default settings
settings = {
@ -299,20 +299,20 @@ if (Meteor.isServer) {
createdBy: this.userId,
updatedBy: this.userId
};
AttachmentStorageSettings.insert(settings);
settings = AttachmentStorageSettings.findOne({});
}
return settings;
},
'updateAttachmentStorageSettings'(settings) {
async 'updateAttachmentStorageSettings'(settings) {
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
const user = ReactiveCache.getUser(this.userId);
const user = await ReactiveCache.getUser(this.userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('not-authorized', 'Admin access required');
}
@ -320,7 +320,7 @@ if (Meteor.isServer) {
// Validate settings
const schema = AttachmentStorageSettings.simpleSchema();
schema.validate(settings);
// Update settings
const result = AttachmentStorageSettings.upsert(
{},
@ -332,7 +332,7 @@ if (Meteor.isServer) {
}
}
);
return result;
},
@ -345,12 +345,12 @@ if (Meteor.isServer) {
return settings ? settings.getDefaultStorage() : STORAGE_NAME_FILESYSTEM;
},
'setDefaultAttachmentStorage'(storageName) {
async 'setDefaultAttachmentStorage'(storageName) {
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
const user = ReactiveCache.getUser(this.userId);
const user = await ReactiveCache.getUser(this.userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('not-authorized', 'Admin access required');
}
@ -369,18 +369,18 @@ if (Meteor.isServer) {
}
}
);
return result;
}
});
// Publication for settings
Meteor.publish('attachmentStorageSettings', function() {
Meteor.publish('attachmentStorageSettings', async function() {
if (!this.userId) {
return this.ready();
}
const user = ReactiveCache.getUser(this.userId);
const user = await ReactiveCache.getUser(this.userId);
if (!user || !user.isAdmin) {
return this.ready();
}

View file

@ -179,13 +179,13 @@ Attachments = new FilesCollection({
// We authorize the attachment download either:
// - if the board is public, everyone (even unconnected) can download it
// - if the board is private, only board members can download it
protected(fileObj) {
async protected(fileObj) {
// file may have been deleted already again after upload validation failed
if (!fileObj) {
return false;
}
const board = ReactiveCache.getBoard(fileObj.meta.boardId);
const board = await ReactiveCache.getBoard(fileObj.meta.boardId);
if (board.isPublic()) {
return true;
}
@ -196,11 +196,11 @@ Attachments = new FilesCollection({
if (Meteor.isServer) {
Attachments.allow({
insert(userId, fileObj) {
async insert(userId, fileObj) {
// ReadOnly users cannot upload attachments
return allowIsBoardMemberWithWriteAccess(userId, ReactiveCache.getBoard(fileObj.boardId));
return allowIsBoardMemberWithWriteAccess(userId, await ReactiveCache.getBoard(fileObj.boardId));
},
update(userId, fileObj, fields) {
async update(userId, fileObj, fields) {
// SECURITY: The 'name' field is sanitized in onBeforeUpload and server-side methods,
// but we block direct client-side $set operations on 'versions.*.path' to prevent
// path traversal attacks via storage migration exploits.
@ -230,9 +230,9 @@ if (Meteor.isServer) {
}
// ReadOnly users cannot update attachments
return allowIsBoardMemberWithWriteAccess(userId, ReactiveCache.getBoard(fileObj.boardId));
return allowIsBoardMemberWithWriteAccess(userId, await ReactiveCache.getBoard(fileObj.boardId));
},
remove(userId, fileObj) {
async remove(userId, fileObj) {
// Additional security check: ensure the file belongs to the board the user has access to
if (!fileObj || !fileObj.boardId) {
if (process.env.DEBUG === 'true') {
@ -241,7 +241,7 @@ if (Meteor.isServer) {
return false;
}
const board = ReactiveCache.getBoard(fileObj.boardId);
const board = await ReactiveCache.getBoard(fileObj.boardId);
if (!board) {
if (process.env.DEBUG === 'true') {
console.warn('Blocked attachment removal: board not found');
@ -293,7 +293,7 @@ if (Meteor.isServer) {
return { valid: true };
},
moveAttachmentToStorage(fileObjId, storageDestination) {
async moveAttachmentToStorage(fileObjId, storageDestination) {
check(fileObjId, String);
check(storageDestination, String);
@ -301,12 +301,12 @@ if (Meteor.isServer) {
throw new Meteor.Error('not-authorized', 'You must be logged in.');
}
const fileObj = ReactiveCache.getAttachment(fileObjId);
const fileObj = await ReactiveCache.getAttachment(fileObjId);
if (!fileObj) {
throw new Meteor.Error('attachment-not-found', 'Attachment not found');
}
const board = ReactiveCache.getBoard(fileObj.boardId);
const board = await ReactiveCache.getBoard(fileObj.boardId);
if (!board || !board.isVisibleBy({ _id: this.userId })) {
throw new Meteor.Error('not-authorized', 'You do not have access to this board.');
}
@ -319,7 +319,7 @@ if (Meteor.isServer) {
moveToStorage(fileObj, storageDestination, fileStoreStrategyFactory);
},
renameAttachment(fileObjId, newName) {
async renameAttachment(fileObjId, newName) {
check(fileObjId, String);
check(newName, String);
@ -328,13 +328,13 @@ if (Meteor.isServer) {
throw new Meteor.Error('not-authorized', 'User must be logged in');
}
const fileObj = ReactiveCache.getAttachment(fileObjId);
const fileObj = await ReactiveCache.getAttachment(fileObjId);
if (!fileObj) {
throw new Meteor.Error('file-not-found', 'Attachment not found');
}
// Verify the user has permission to modify this attachment
const board = ReactiveCache.getBoard(fileObj.boardId);
const board = await ReactiveCache.getBoard(fileObj.boardId);
if (!board) {
throw new Meteor.Error('board-not-found', 'Board not found');
}
@ -348,30 +348,30 @@ if (Meteor.isServer) {
rename(fileObj, newName, fileStoreStrategyFactory);
},
validateAttachment(fileObjId) {
async validateAttachment(fileObjId) {
check(fileObjId, String);
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'You must be logged in.');
}
const fileObj = ReactiveCache.getAttachment(fileObjId);
const fileObj = await ReactiveCache.getAttachment(fileObjId);
if (!fileObj) {
throw new Meteor.Error('attachment-not-found', 'Attachment not found');
}
const board = ReactiveCache.getBoard(fileObj.boardId);
const board = await ReactiveCache.getBoard(fileObj.boardId);
if (!board || !board.isVisibleBy({ _id: this.userId })) {
throw new Meteor.Error('not-authorized', 'You do not have access to this board.');
}
const isValid = Promise.await(isFileValid(fileObj, attachmentUploadMimeTypes, attachmentUploadSize, attachmentUploadExternalProgram));
const isValid = await isFileValid(fileObj, attachmentUploadMimeTypes, attachmentUploadSize, attachmentUploadExternalProgram);
if (!isValid) {
Attachments.remove(fileObjId);
}
},
validateAttachmentAndMoveToStorage(fileObjId, storageDestination) {
async validateAttachmentAndMoveToStorage(fileObjId, storageDestination) {
check(fileObjId, String);
check(storageDestination, String);
@ -379,12 +379,12 @@ if (Meteor.isServer) {
throw new Meteor.Error('not-authorized', 'You must be logged in.');
}
const fileObj = ReactiveCache.getAttachment(fileObjId);
const fileObj = await ReactiveCache.getAttachment(fileObjId);
if (!fileObj) {
throw new Meteor.Error('attachment-not-found', 'Attachment not found');
}
const board = ReactiveCache.getBoard(fileObj.boardId);
const board = await ReactiveCache.getBoard(fileObj.boardId);
if (!board || !board.isVisibleBy({ _id: this.userId })) {
throw new Meteor.Error('not-authorized', 'You do not have access to this board.');
}
@ -395,9 +395,9 @@ if (Meteor.isServer) {
throw new Meteor.Error('invalid-storage-destination', 'Invalid storage destination');
}
Meteor.call('validateAttachment', fileObjId);
await Meteor.callAsync('validateAttachment', fileObjId);
const fileObjAfter = ReactiveCache.getAttachment(fileObjId);
const fileObjAfter = await ReactiveCache.getAttachment(fileObjId);
if (fileObjAfter) {
Meteor.defer(() => Meteor.call('moveAttachmentToStorage', fileObjId, storageDestination));

View file

@ -106,7 +106,7 @@ Avatars = new FilesCollection({
}
return TAPi18n.__('avatar-too-big', {size: filesize(avatarsUploadSize)});
},
onAfterUpload(fileObj) {
async onAfterUpload(fileObj) {
// current storage is the filesystem, update object and database
Object.keys(fileObj.versions).forEach(versionName => {
fileObj.versions[versionName].storage = STORAGE_NAME_FILESYSTEM;
@ -114,12 +114,13 @@ Avatars = new FilesCollection({
Avatars.update({ _id: fileObj._id }, { $set: { "versions": fileObj.versions } });
const isValid = Promise.await(isFileValid(fileObj, avatarsUploadMimeTypes, avatarsUploadSize, avatarsUploadExternalProgram));
const isValid = await isFileValid(fileObj, avatarsUploadMimeTypes, avatarsUploadSize, avatarsUploadExternalProgram);
if (isValid) {
// Set avatar URL using universal URL generator (URL-agnostic)
const universalUrl = generateUniversalAvatarUrl(fileObj._id);
ReactiveCache.getUser(fileObj.userId).setAvatarUrl(universalUrl);
const user = await ReactiveCache.getUser(fileObj.userId);
user.setAvatarUrl(universalUrl);
} else {
Avatars.remove(fileObj._id);
}
@ -128,12 +129,13 @@ Avatars = new FilesCollection({
const ret = fileStoreStrategyFactory.getFileStrategy(fileObj, versionName).interceptDownload(http, this.cacheControl);
return ret;
},
onBeforeRemove(files) {
files.forEach(fileObj => {
async onBeforeRemove(files) {
for (const fileObj of files) {
if (fileObj.userId) {
ReactiveCache.getUser(fileObj.userId).setAvatarUrl('');
const user = await ReactiveCache.getUser(fileObj.userId);
user.setAvatarUrl('');
}
});
}
return true;
},

View file

@ -703,12 +703,12 @@ Boards.attachSchema(
);
Boards.helpers({
copy() {
async copy() {
const oldId = this._id;
const oldWatchers = this.watchers ? this.watchers.slice() : [];
delete this._id;
delete this.slug;
this.title = this.copyTitle();
this.title = await this.copyTitle();
const _id = Boards.insert(this);
// Temporary remove watchers to disable notifications
@ -719,23 +719,26 @@ Boards.helpers({
});
// Copy all swimlanes in board
ReactiveCache.getSwimlanes({
const swimlanes = await ReactiveCache.getSwimlanes({
boardId: oldId,
archived: false,
}).forEach(swimlane => {
});
for (const swimlane of swimlanes) {
swimlane.type = 'swimlane';
swimlane.copy(_id);
});
}
// copy custom field definitions
const cfMap = {};
ReactiveCache.getCustomFields({ boardIds: oldId }).forEach(cf => {
const customFields = await ReactiveCache.getCustomFields({ boardIds: oldId });
for (const cf of customFields) {
const id = cf._id;
delete cf._id;
cf.boardIds = [_id];
cfMap[id] = CustomFields.insert(cf);
});
ReactiveCache.getCards({ boardId: _id }).forEach(card => {
}
const cards = await ReactiveCache.getCards({ boardId: _id });
for (const card of cards) {
Cards.update(card._id, {
$set: {
customFields: card.customFields.map(cf => {
@ -744,30 +747,33 @@ Boards.helpers({
}),
},
});
});
}
// copy rules, actions, and triggers
const actionsMap = {};
ReactiveCache.getActions({ boardId: oldId }).forEach(action => {
const actions = await ReactiveCache.getActions({ boardId: oldId });
for (const action of actions) {
const id = action._id;
delete action._id;
action.boardId = _id;
actionsMap[id] = Actions.insert(action);
});
}
const triggersMap = {};
ReactiveCache.getTriggers({ boardId: oldId }).forEach(trigger => {
const triggers = await ReactiveCache.getTriggers({ boardId: oldId });
for (const trigger of triggers) {
const id = trigger._id;
delete trigger._id;
trigger.boardId = _id;
triggersMap[id] = Triggers.insert(trigger);
});
ReactiveCache.getRules({ boardId: oldId }).forEach(rule => {
}
const rules = await ReactiveCache.getRules({ boardId: oldId });
for (const rule of rules) {
delete rule._id;
rule.boardId = _id;
rule.actionId = actionsMap[rule.actionId];
rule.triggerId = triggersMap[rule.triggerId];
Rules.insert(rule);
});
}
// Re-set Watchers to reenable notification
Boards.update(_id, {
@ -781,8 +787,8 @@ Boards.helpers({
*
* @returns {string|null}
*/
copyTitle() {
return Boards.uniqueTitle(this.title);
async copyTitle() {
return await Boards.uniqueTitle(this.title);
},
/**
@ -823,8 +829,8 @@ Boards.helpers({
},
cards() {
const ret = ReactiveCache.getCards(
async cards() {
const ret = await ReactiveCache.getCards(
{ boardId: this._id, archived: false },
{ sort: { title: 1 } },
);
@ -835,11 +841,12 @@ Boards.helpers({
return this.draggableLists();
},
newestLists() {
async newestLists() {
// sorted lists from newest to the oldest, by its creation date or its cards' last modification date
const value = ReactiveCache.getCurrentUser()._getListSortBy();
const user = await ReactiveCache.getCurrentUser();
const value = user._getListSortBy();
const sortKey = { starred: -1, [value[0]]: value[1] }; // [["starred",-1],value];
return ReactiveCache.getLists(
return await ReactiveCache.getLists(
{
boardId: this._id,
archived: false,
@ -848,8 +855,8 @@ Boards.helpers({
);
},
draggableLists() {
return ReactiveCache.getLists(
async draggableLists() {
return await ReactiveCache.getLists(
{
boardId: this._id,
},
@ -860,28 +867,28 @@ Boards.helpers({
/** returns the last list
* @returns Document the last list
*/
getLastList() {
const ret = ReactiveCache.getList({ boardId: this._id }, { sort: { sort: 'desc' } });
async getLastList() {
const ret = await ReactiveCache.getList({ boardId: this._id }, { sort: { sort: 'desc' } });
return ret;
},
nullSortLists() {
return ReactiveCache.getLists({
async nullSortLists() {
return await ReactiveCache.getLists({
boardId: this._id,
archived: false,
sort: { $eq: null },
});
},
swimlanes() {
return ReactiveCache.getSwimlanes(
async swimlanes() {
return await ReactiveCache.getSwimlanes(
{ boardId: this._id, archived: false },
{ sort: { sort: 1 } },
);
},
nextSwimlane(swimlane) {
return ReactiveCache.getSwimlane(
async nextSwimlane(swimlane) {
return await ReactiveCache.getSwimlane(
{
boardId: this._id,
archived: false,
@ -894,16 +901,16 @@ Boards.helpers({
);
},
nullSortSwimlanes() {
return ReactiveCache.getSwimlanes({
async nullSortSwimlanes() {
return await ReactiveCache.getSwimlanes({
boardId: this._id,
archived: false,
sort: { $eq: null },
});
},
hasOvertimeCards() {
const card = ReactiveCache.getCard({
async hasOvertimeCards() {
const card = await ReactiveCache.getCard({
isOvertime: true,
boardId: this._id,
archived: false,
@ -911,8 +918,8 @@ Boards.helpers({
return card !== undefined;
},
hasSpentTimeCards() {
const card = ReactiveCache.getCard({
async hasSpentTimeCards() {
const card = await ReactiveCache.getCard({
spentTime: { $gt: 0 },
boardId: this._id,
archived: false,
@ -920,19 +927,20 @@ Boards.helpers({
return card !== undefined;
},
activities() {
async activities() {
let linkedBoardId = [this._id];
ReactiveCache.getCards({
const cards = await ReactiveCache.getCards({
"type": "cardType-linkedBoard",
"boardId": this._id}
).forEach(card => {
linkedBoardId.push(card.linkedId);
"boardId": this._id
});
const ret = ReactiveCache.getActivities({ boardId: { $in: linkedBoardId } }, { sort: { createdAt: -1 } });
for (const card of cards) {
linkedBoardId.push(card.linkedId);
}
const ret = await ReactiveCache.getActivities({ boardId: { $in: linkedBoardId } }, { sort: { createdAt: -1 } });
return ret;
},
activeMembers(){
async activeMembers(){
// Depend on the users collection for reactivity when users are loaded
const memberUserIds = _.pluck(this.members, 'userId');
const dummy = Meteor.users.find({ _id: { $in: memberUserIds } }).count();
@ -945,16 +953,21 @@ Boards.helpers({
return selected;
});
// Filter out members where user is not loaded
const filteredMembers = uniqueMembers.filter(member => {
const user = ReactiveCache.getUser(member.userId);
return user !== undefined;
});
const filteredMembers = [];
for (const member of uniqueMembers) {
const user = await ReactiveCache.getUser(member.userId);
if (user !== undefined) {
filteredMembers.push(member);
}
}
// Sort by role priority first (admin, normal, normal-assigned, no-comments, comment-only, comment-assigned, worker, read-only, read-assigned), then by fullname
return _.sortBy(filteredMembers, member => {
const user = ReactiveCache.getUser(member.userId);
// Build sort keys with async user lookup
const membersWithSortKey = [];
for (const member of filteredMembers) {
const user = await ReactiveCache.getUser(member.userId);
let rolePriority = 8; // Default for normal
if (member.isAdmin) rolePriority = 0;
else if (member.isReadAssignedOnly) rolePriority = 8;
else if (member.isReadOnly) rolePriority = 7;
@ -964,10 +977,11 @@ Boards.helpers({
else if (member.isNoComments) rolePriority = 3;
else if (member.isNormalAssignedOnly) rolePriority = 2;
else rolePriority = 1; // Normal
const fullname = user ? user.profile.fullname : '';
return rolePriority + '-' + fullname;
});
membersWithSortKey.push({ member, sortKey: rolePriority + '-' + fullname });
}
return _.sortBy(membersWithSortKey, 'sortKey').map(item => item.member);
},
activeOrgs() {
@ -990,8 +1004,8 @@ Boards.helpers({
return _.where(this.members, { isActive: true, isAdmin: true });
},
memberUsers() {
return ReactiveCache.getUsers({ _id: { $in: _.pluck(this.members, 'userId') } });
async memberUsers() {
return await ReactiveCache.getUsers({ _id: { $in: _.pluck(this.members, 'userId') } });
},
getLabel(name, color) {
@ -1111,8 +1125,8 @@ Boards.helpers({
return `board-color-${this.color}`;
},
customFields() {
const ret = ReactiveCache.getCustomFields(
async customFields() {
const ret = await ReactiveCache.getCustomFields(
{ boardIds: { $in: [this._id] } },
{ sort: { name: 1 } },
);
@ -1141,7 +1155,7 @@ Boards.helpers({
}
},
searchBoards(term) {
async searchBoards(term) {
check(term, Match.OneOf(String, null, undefined));
const query = { boardId: this._id };
@ -1156,11 +1170,11 @@ Boards.helpers({
query.$or = [{ title: regex }, { description: regex }];
}
const ret = ReactiveCache.getCards(query, projection);
const ret = await ReactiveCache.getCards(query, projection);
return ret;
},
searchSwimlanes(term) {
async searchSwimlanes(term) {
check(term, Match.OneOf(String, null, undefined));
const query = { boardId: this._id };
@ -1178,10 +1192,10 @@ Boards.helpers({
query.$or = [{ title: regex }, { description: regex }];
}
return ReactiveCache.getSwimlanes(query, projection);
return await ReactiveCache.getSwimlanes(query, projection);
},
searchLists(term) {
async searchLists(term) {
let ret = null;
if (term) {
check(term, Match.OneOf(String));
@ -1203,12 +1217,12 @@ Boards.helpers({
query.$or = [{ title: regex }, { description: regex }];
}
ret = ReactiveCache.getLists(query, projection);
ret = await ReactiveCache.getLists(query, projection);
}
return ret;
},
searchCards(term, excludeLinked) {
async searchCards(term, excludeLinked) {
let ret = null;
if (term) {
check(term, Match.OneOf(String));
@ -1234,7 +1248,7 @@ Boards.helpers({
{ description: regex },
{ customFields: { $elemMatch: { value: regex } } },
];
ret = ReactiveCache.getCards(query, projection);
ret = await ReactiveCache.getCards(query, projection);
}
return ret;
},
@ -1268,8 +1282,8 @@ Boards.helpers({
return this.subtasksDefaultBoardId;
},
getDefaultSubtasksBoard() {
return ReactiveCache.getBoard(this.getDefaultSubtasksBoardId());
async getDefaultSubtasksBoard() {
return await ReactiveCache.getBoard(this.getDefaultSubtasksBoardId());
},
//Date Settings option such as received date, start date and so on.
@ -1301,8 +1315,8 @@ Boards.helpers({
return this.dateSettingsDefaultBoardId;
},
getDefaultDateSettingsBoard() {
return ReactiveCache.getBoard(this.getDefaultDateSettingsBoardId());
async getDefaultDateSettingsBoard() {
return await ReactiveCache.getBoard(this.getDefaultDateSettingsBoardId());
},
getDefaultSubtasksListId() {
@ -1320,8 +1334,8 @@ Boards.helpers({
return this.subtasksDefaultListId;
},
getDefaultSubtasksList() {
return ReactiveCache.getList(this.getDefaultSubtasksListId());
async getDefaultSubtasksList() {
return await ReactiveCache.getList(this.getDefaultSubtasksListId());
},
getDefaultDateSettingsListId() {
@ -1339,15 +1353,15 @@ Boards.helpers({
return this.dateSettingsDefaultListId;
},
getDefaultDateSettingsList() {
return ReactiveCache.getList(this.getDefaultDateSettingsListId());
async getDefaultDateSettingsList() {
return await ReactiveCache.getList(this.getDefaultDateSettingsListId());
},
getDefaultSwimline() {
let result = ReactiveCache.getSwimlane({ boardId: this._id });
async getDefaultSwimline() {
let result = await ReactiveCache.getSwimlane({ boardId: this._id });
if (result === undefined) {
// Check if any swimlane exists for this board to avoid duplicates
const existingSwimlanes = ReactiveCache.getSwimlanes({ boardId: this._id });
const existingSwimlanes = await ReactiveCache.getSwimlanes({ boardId: this._id });
if (existingSwimlanes.length > 0) {
// Use the first existing swimlane
result = existingSwimlanes[0];
@ -1358,14 +1372,14 @@ Boards.helpers({
title: title,
boardId: this._id,
});
result = ReactiveCache.getSwimlane({ boardId: this._id });
result = await ReactiveCache.getSwimlane({ boardId: this._id });
}
}
return result;
},
getNextCardNumber() {
const boardCards = ReactiveCache.getCard(
async getNextCardNumber() {
const boardCards = await ReactiveCache.getCard(
{
boardId: this._id
},
@ -1384,16 +1398,16 @@ Boards.helpers({
return maxCardNr + 1;
},
cardsDueInBetween(start, end) {
const ret = ReactiveCache.getCards({
async cardsDueInBetween(start, end) {
const ret = await ReactiveCache.getCards({
boardId: this._id,
dueAt: { $gte: start, $lte: end },
});
return ret;
},
cardsInInterval(start, end) {
const ret = ReactiveCache.getCards({
async cardsInInterval(start, end) {
const ret = await ReactiveCache.getCards({
boardId: this._id,
$or: [
{
@ -1454,7 +1468,7 @@ Boards.helpers({
},
async setBackgroundImageURL(backgroundImageURL) {
const currentUser = ReactiveCache.getCurrentUser();
const currentUser = await ReactiveCache.getCurrentUser();
if (currentUser.isBoardAdmin() || currentUser.isAdmin()) {
return await Boards.updateAsync(this._id, { $set: { backgroundImageURL } });
}
@ -1717,32 +1731,31 @@ function boardRemover(userId, doc) {
);
}
Boards.uniqueTitle = title => {
Boards.uniqueTitle = async title => {
const m = title.match(
new RegExp('^(?<title>.*?)\\s*(\\[(?<num>\\d+)]\\s*$|\\s*$)'),
);
const base = escapeForRegex(m.groups.title);
const baseTitle = m.groups.title;
boards = ReactiveCache.getBoards({ title: new RegExp(`^${base}\\s*(\\[(?<num>\\d+)]\\s*$|\\s*$)`) });
const boards = await ReactiveCache.getBoards({ title: new RegExp(`^${base}\\s*(\\[(?<num>\\d+)]\\s*$|\\s*$)`) });
if (boards.length > 0) {
let num = 0;
ReactiveCache.getBoards({ title: new RegExp(`^${base}\\s*\\[\\d+]\\s*$`) }).forEach(
board => {
const m = board.title.match(
new RegExp('^(?<title>.*?)\\s*\\[(?<num>\\d+)]\\s*$'),
);
if (m) {
const n = parseInt(m.groups.num, 10);
num = num < n ? n : num;
}
},
);
const numberedBoards = await ReactiveCache.getBoards({ title: new RegExp(`^${base}\\s*\\[\\d+]\\s*$`) });
for (const board of numberedBoards) {
const m = board.title.match(
new RegExp('^(?<title>.*?)\\s*\\[(?<num>\\d+)]\\s*$'),
);
if (m) {
const n = parseInt(m.groups.num, 10);
num = num < n ? n : num;
}
}
return `${baseTitle} [${num + 1}]`;
}
return title;
};
Boards.userSearch = (
Boards.userSearch = async (
userId,
selector = {},
projection = {},
@ -1756,17 +1769,17 @@ Boards.userSearch = (
if (userId) {
selector.$or.push({ members: { $elemMatch: { userId, isActive: true } } });
}
const ret = ReactiveCache.getBoards(selector, projection);
const ret = await ReactiveCache.getBoards(selector, projection);
return ret;
};
Boards.userBoards = (
Boards.userBoards = async (
userId,
archived = false,
selector = {},
projection = {},
) => {
const user = ReactiveCache.getUser(userId);
const user = await ReactiveCache.getUser(userId);
if (!user) {
return [];
}
@ -1785,13 +1798,14 @@ Boards.userBoards = (
{ teams: { $elemMatch: { teamId: { $in: user.teamIds() }, isActive: true } } },
];
return ReactiveCache.getBoards(selector, projection);
return await ReactiveCache.getBoards(selector, projection);
};
Boards.userBoardIds = (userId, archived = false, selector = {}) => {
return Boards.userBoards(userId, archived, selector, {
Boards.userBoardIds = async (userId, archived = false, selector = {}) => {
const boards = await Boards.userBoards(userId, archived, selector, {
fields: { _id: 1 },
}).map(board => {
});
return boards.map(board => {
return board._id;
});
};
@ -1810,7 +1824,7 @@ Boards.labelColors = () => {
if (Meteor.isServer) {
Boards.allow({
insert(userId, doc) {
async insert(userId, doc) {
// Check if user is logged in
if (!userId) return false;
@ -1829,7 +1843,7 @@ if (Meteor.isServer) {
// All logged in users are allowed to reorder boards by dragging at All Boards page and Public Boards page.
Boards.allow({
update(userId, board, fieldNames) {
async update(userId, board, fieldNames) {
return canUpdateBoardSort(userId, board, fieldNames);
},
// Need members to verify membership in policy
@ -1839,7 +1853,7 @@ if (Meteor.isServer) {
// The number of users that have starred this board is managed by trusted code
// and the user is not allowed to update it
Boards.deny({
update(userId, board, fieldNames) {
async update(userId, board, fieldNames) {
return _.contains(fieldNames, 'stars');
},
fetch: [],
@ -1847,7 +1861,7 @@ if (Meteor.isServer) {
// We can't remove a member if it is the last administrator
Boards.deny({
update(userId, doc, fieldNames, modifier) {
async update(userId, doc, fieldNames, modifier) {
if (!_.contains(fieldNames, 'members')) return false;
// We only care in case of a $pull operation, ie remove a member
@ -1873,7 +1887,7 @@ if (Meteor.isServer) {
// Deny changing permission to public if allowPrivateOnly is enabled
Boards.deny({
update(userId, doc, fieldNames, modifier) {
async update(userId, doc, fieldNames, modifier) {
if (!_.contains(fieldNames, 'permission')) return false;
const allowPrivateOnly = TableVisibilityModeSettings.findOne('tableVisibilityMode-allowPrivateOnly')?.booleanValue;
@ -1887,13 +1901,13 @@ if (Meteor.isServer) {
});
Meteor.methods({
getBackgroundImageURL(boardId) {
async getBackgroundImageURL(boardId) {
check(boardId, String);
return ReactiveCache.getBoard(boardId, {}, { backgroundImageUrl: 1 });
return await ReactiveCache.getBoard(boardId, {}, { backgroundImageUrl: 1 });
},
async quitBoard(boardId) {
check(boardId, String);
const board = ReactiveCache.getBoard(boardId);
const board = await ReactiveCache.getBoard(boardId);
if (board) {
const userId = Meteor.userId();
const index = board.memberIndex(userId);
@ -1903,9 +1917,9 @@ if (Meteor.isServer) {
} else throw new Meteor.Error('error-board-notAMember');
} else throw new Meteor.Error('error-board-doesNotExist');
},
acceptInvite(boardId) {
async acceptInvite(boardId) {
check(boardId, String);
const board = ReactiveCache.getBoard(boardId);
const board = await ReactiveCache.getBoard(boardId);
if (!board) {
throw new Meteor.Error('error-board-doesNotExist');
}
@ -1927,9 +1941,10 @@ if (Meteor.isServer) {
}
});
},
myLabelNames() {
async myLabelNames() {
let names = [];
Boards.userBoards(Meteor.userId()).forEach(board => {
const boards = await Boards.userBoards(Meteor.userId());
for (const board of boards) {
// Only return labels when they exist.
if (board.labels !== undefined) {
names = names.concat(
@ -1939,21 +1954,21 @@ if (Meteor.isServer) {
return label.name;
}),
);
} else {
return [];
}
});
}
return _.uniq(names).sort();
},
myBoardNames() {
async myBoardNames() {
const boards = await Boards.userBoards(Meteor.userId());
return _.uniq(
Boards.userBoards(Meteor.userId()).map(board => {
boards.map(board => {
return board.title;
}),
).sort();
},
setAllBoardsHideActivities() {
if ((ReactiveCache.getCurrentUser() || {}).isAdmin) {
async setAllBoardsHideActivities() {
const currentUser = await ReactiveCache.getCurrentUser();
if ((currentUser || {}).isAdmin) {
Boards.update(
{
showActivities: true
@ -1977,7 +1992,7 @@ if (Meteor.isServer) {
Meteor.methods({
async archiveBoard(boardId) {
check(boardId, String);
const board = ReactiveCache.getBoard(boardId);
const board = await ReactiveCache.getBoard(boardId);
if (board) {
const userId = Meteor.userId();
const index = board.memberIndex(userId);
@ -1987,14 +2002,14 @@ if (Meteor.isServer) {
} else throw new Meteor.Error('error-board-notAMember');
} else throw new Meteor.Error('error-board-doesNotExist');
},
setBoardOrgs(boardOrgsArray, currBoardId){
async setBoardOrgs(boardOrgsArray, currBoardId){
check(boardOrgsArray, Array);
check(currBoardId, String);
const userId = Meteor.userId();
if (!userId) {
throw new Meteor.Error('not-authorized', 'You must be logged in to perform this action.');
}
const board = ReactiveCache.getBoard(currBoardId);
const board = await ReactiveCache.getBoard(currBoardId);
if (!board) {
throw new Meteor.Error('board-not-found', 'Board not found.');
}
@ -2013,7 +2028,7 @@ if (Meteor.isServer) {
},
});
},
setBoardTeams(boardTeamsArray, membersArray, currBoardId){
async setBoardTeams(boardTeamsArray, membersArray, currBoardId){
check(boardTeamsArray, Array);
check(membersArray, Array);
check(currBoardId, String);
@ -2021,7 +2036,7 @@ if (Meteor.isServer) {
if (!userId) {
throw new Meteor.Error('not-authorized', 'You must be logged in to perform this action.');
}
const board = ReactiveCache.getBoard(currBoardId);
const board = await ReactiveCache.getBoard(currBoardId);
if (!board) {
throw new Meteor.Error('board-not-found', 'Board not found.');
}
@ -2058,8 +2073,8 @@ if (Meteor.isServer) {
}
// Insert new board at last position in sort order.
Boards.before.insert((userId, doc) => {
const lastBoard = ReactiveCache.getBoard(
Boards.before.insert(async (userId, doc) => {
const lastBoard = await ReactiveCache.getBoard(
{ sort: { $exists: true } },
{ sort: { sort: -1 } },
);
@ -2243,7 +2258,7 @@ if (Meteor.isServer) {
* @return_type [{_id: string,
* title: string}]
*/
JsonRoutes.add('GET', '/api/users/:userId/boards', function(req, res) {
JsonRoutes.add('GET', '/api/users/:userId/boards', async function(req, res) {
try {
Authentication.checkLoggedIn(req.userId);
const paramUserId = req.params.userId;
@ -2254,7 +2269,7 @@ if (Meteor.isServer) {
req.userId === paramUserId,
);
const data = ReactiveCache.getBoards(
const boards = await ReactiveCache.getBoards(
{
archived: false,
'members.userId': paramUserId,
@ -2262,7 +2277,8 @@ if (Meteor.isServer) {
{
sort: { sort: 1 /* boards default sorting */ },
},
).map(function(board) {
);
const data = boards.map(function(board) {
return {
_id: board._id,
title: board.title,
@ -2285,17 +2301,18 @@ if (Meteor.isServer) {
* @return_type [{_id: string,
title: string}]
*/
JsonRoutes.add('GET', '/api/boards', function(req, res) {
JsonRoutes.add('GET', '/api/boards', async function(req, res) {
try {
Authentication.checkUserId(req.userId);
const boards = await ReactiveCache.getBoards(
{ permission: 'public' },
{
sort: { sort: 1 /* boards default sorting */ },
},
);
JsonRoutes.sendResult(res, {
code: 200,
data: ReactiveCache.getBoards(
{ permission: 'public' },
{
sort: { sort: 1 /* boards default sorting */ },
},
).map(function(doc) {
data: boards.map(function(doc) {
return {
_id: doc._id,
title: doc.title,
@ -2316,14 +2333,16 @@ if (Meteor.isServer) {
*
* @return_type {private: integer, public: integer}
*/
JsonRoutes.add('GET', '/api/boards_count', function(req, res) {
JsonRoutes.add('GET', '/api/boards_count', async function(req, res) {
try {
Authentication.checkUserId(req.userId);
const privateBoards = await ReactiveCache.getBoards({ permission: 'private' });
const publicBoards = await ReactiveCache.getBoards({ permission: 'public' });
JsonRoutes.sendResult(res, {
code: 200,
data: {
private: ReactiveCache.getBoards({ permission: 'private' }).length,
public: ReactiveCache.getBoards({ permission: 'public' }).length,
private: privateBoards.length,
public: publicBoards.length,
},
});
} catch (error) {
@ -2341,14 +2360,15 @@ if (Meteor.isServer) {
* @param {string} boardId the ID of the board to retrieve the data
* @return_type Boards
*/
JsonRoutes.add('GET', '/api/boards/:boardId', function(req, res) {
JsonRoutes.add('GET', '/api/boards/:boardId', async function(req, res) {
try {
const paramBoardId = req.params.boardId;
Authentication.checkBoardAccess(req.userId, paramBoardId);
const board = await ReactiveCache.getBoard(paramBoardId);
JsonRoutes.sendResult(res, {
code: 200,
data: ReactiveCache.getBoard(paramBoardId),
data: board,
});
} catch (error) {
JsonRoutes.sendResult(res, {
@ -2493,12 +2513,12 @@ if (Meteor.isServer) {
*
* @return_type string
*/
JsonRoutes.add('PUT', '/api/boards/:boardId/labels', function(req, res) {
JsonRoutes.add('PUT', '/api/boards/:boardId/labels', async function(req, res) {
const id = req.params.boardId;
Authentication.checkBoardWriteAccess(req.userId, id);
try {
if (req.body.hasOwnProperty('label')) {
const board = ReactiveCache.getBoard(id);
const board = await ReactiveCache.getBoard(id);
const color = req.body.label.color;
const name = req.body.label.name;
const labelId = Random.id(6);
@ -2536,14 +2556,14 @@ if (Meteor.isServer) {
*
* @return_type string
*/
JsonRoutes.add('POST', '/api/boards/:boardId/copy', function(req, res) {
JsonRoutes.add('POST', '/api/boards/:boardId/copy', async function(req, res) {
const id = req.params.boardId;
const board = ReactiveCache.getBoard(id);
const board = await ReactiveCache.getBoard(id);
const adminAccess = board.members.some(e => e.userId === req.userId && e.isAdmin);
Authentication.checkAdminOrCondition(req.userId, adminAccess);
try {
board['title'] = req.body.title || Boards.uniqueTitle(board.title);
ret = board.copy();
board['title'] = req.body.title || await Boards.uniqueTitle(board.title);
ret = await board.copy();
JsonRoutes.sendResult(res, {
code: 200,
data: ret,
@ -2580,7 +2600,7 @@ JsonRoutes.add('POST', '/api/boards/:boardId/copy', function(req, res) {
const boardId = req.params.boardId;
const memberId = req.params.memberId;
const { isAdmin, isNoComments, isCommentOnly, isWorker, isNormalAssignedOnly, isCommentAssignedOnly, isReadOnly, isReadAssignedOnly } = req.body;
const board = ReactiveCache.getBoard(boardId);
const board = await ReactiveCache.getBoard(boardId);
function isTrue(data) {
try {
return data.toLowerCase() === 'true';
@ -2630,13 +2650,14 @@ JsonRoutes.add('POST', '/api/boards/:boardId/copy', function(req, res) {
* cardId: string
* }]
*/
JsonRoutes.add('GET', '/api/boards/:boardId/attachments', function(req, res) {
JsonRoutes.add('GET', '/api/boards/:boardId/attachments', async function(req, res) {
const paramBoardId = req.params.boardId;
Authentication.checkBoardAccess(req.userId, paramBoardId);
const attachments = await ReactiveCache
.getAttachments({'meta.boardId': paramBoardId }, {}, true);
JsonRoutes.sendResult(res, {
code: 200,
data: ReactiveCache
.getAttachments({'meta.boardId': paramBoardId }, {}, true)
data: attachments
.each()
.map(function(attachment) {
return {

View file

@ -50,14 +50,14 @@ CardCommentReactions.attachSchema(
);
CardCommentReactions.allow({
insert(userId, doc) {
return allowIsBoardMember(userId, ReactiveCache.getBoard(doc.boardId));
async insert(userId, doc) {
return allowIsBoardMember(userId, await ReactiveCache.getBoard(doc.boardId));
},
update(userId, doc) {
return allowIsBoardMember(userId, ReactiveCache.getBoard(doc.boardId));
async update(userId, doc) {
return allowIsBoardMember(userId, await ReactiveCache.getBoard(doc.boardId));
},
remove(userId, doc) {
return allowIsBoardMember(userId, ReactiveCache.getBoard(doc.boardId));
async remove(userId, doc) {
return allowIsBoardMember(userId, await ReactiveCache.getBoard(doc.boardId));
},
fetch: ['boardId'],
});

View file

@ -81,15 +81,15 @@ CardComments.attachSchema(
);
CardComments.allow({
insert(userId, doc) {
async insert(userId, doc) {
// ReadOnly users cannot add comments. Only members who can comment are allowed.
return allowIsBoardMemberCommentOnly(userId, ReactiveCache.getBoard(doc.boardId));
return allowIsBoardMemberCommentOnly(userId, await ReactiveCache.getBoard(doc.boardId));
},
update(userId, doc) {
return userId === doc.userId || allowIsBoardAdmin(userId, ReactiveCache.getBoard(doc.boardId));
async update(userId, doc) {
return userId === doc.userId || allowIsBoardAdmin(userId, await ReactiveCache.getBoard(doc.boardId));
},
remove(userId, doc) {
return userId === doc.userId || allowIsBoardAdmin(userId, ReactiveCache.getBoard(doc.boardId));
async remove(userId, doc) {
return userId === doc.userId || allowIsBoardAdmin(userId, await ReactiveCache.getBoard(doc.boardId));
},
fetch: ['userId', 'boardId'],
});
@ -101,21 +101,21 @@ CardComments.helpers({
CardComments.insert(this);
},
user() {
return ReactiveCache.getUser(this.userId);
async user() {
return await ReactiveCache.getUser(this.userId);
},
reactions() {
const cardCommentReactions = ReactiveCache.getCardCommentReaction({cardCommentId: this._id});
async reactions() {
const cardCommentReactions = await ReactiveCache.getCardCommentReaction({cardCommentId: this._id});
return !!cardCommentReactions ? cardCommentReactions.reactions : [];
},
toggleReaction(reactionCodepoint) {
async toggleReaction(reactionCodepoint) {
if (reactionCodepoint !== sanitizeText(reactionCodepoint)) {
return false;
} else {
const cardCommentReactions = ReactiveCache.getCardCommentReaction({cardCommentId: this._id});
const cardCommentReactions = await ReactiveCache.getCardCommentReaction({cardCommentId: this._id});
const reactions = !!cardCommentReactions ? cardCommentReactions.reactions : [];
const userId = Meteor.userId();
const reaction = reactions.find(r => r.reactionCodepoint === reactionCodepoint);
@ -154,8 +154,8 @@ CardComments.helpers({
CardComments.hookOptions.after.update = { fetchPrevious: false };
function commentCreation(userId, doc) {
const card = ReactiveCache.getCard(doc.cardId);
async function commentCreation(userId, doc) {
const card = await ReactiveCache.getCard(doc.cardId);
Activities.insert({
userId,
activityType: 'addComment',
@ -167,9 +167,9 @@ function commentCreation(userId, doc) {
});
}
CardComments.textSearch = (userId, textArray) => {
CardComments.textSearch = async (userId, textArray) => {
const selector = {
boardId: { $in: Boards.userBoardIds(userId) },
boardId: { $in: await Boards.userBoardIds(userId) },
$and: [],
};
@ -180,7 +180,7 @@ CardComments.textSearch = (userId, textArray) => {
// eslint-disable-next-line no-console
// console.log('cardComments selector:', selector);
const comments = ReactiveCache.getCardComments(selector);
const comments = await ReactiveCache.getCardComments(selector);
// eslint-disable-next-line no-console
// console.log('count:', comments.count());
// eslint-disable-next-line no-console
@ -197,12 +197,12 @@ if (Meteor.isServer) {
await CardComments._collection.createIndexAsync({ cardId: 1, createdAt: -1 });
});
CardComments.after.insert((userId, doc) => {
commentCreation(userId, doc);
CardComments.after.insert(async (userId, doc) => {
await commentCreation(userId, doc);
});
CardComments.after.update((userId, doc) => {
const card = ReactiveCache.getCard(doc.cardId);
CardComments.after.update(async (userId, doc) => {
const card = await ReactiveCache.getCard(doc.cardId);
Activities.insert({
userId,
activityType: 'editComment',
@ -214,8 +214,8 @@ if (Meteor.isServer) {
});
});
CardComments.before.remove((userId, doc) => {
const card = ReactiveCache.getCard(doc.cardId);
CardComments.before.remove(async (userId, doc) => {
const card = await ReactiveCache.getCard(doc.cardId);
Activities.insert({
userId,
activityType: 'deleteComment',
@ -225,7 +225,7 @@ if (Meteor.isServer) {
listId: card.listId,
swimlaneId: card.swimlaneId,
});
const activity = ReactiveCache.getActivity({ commentId: doc._id });
const activity = await ReactiveCache.getActivity({ commentId: doc._id });
if (activity) {
Activities.remove(activity._id);
}
@ -244,7 +244,7 @@ if (Meteor.isServer) {
* comment: string,
* authorId: string}]
*/
JsonRoutes.add('GET', '/api/boards/:boardId/cards/:cardId/comments', function (
JsonRoutes.add('GET', '/api/boards/:boardId/cards/:cardId/comments', async function (
req,
res,
) {
@ -254,10 +254,10 @@ if (Meteor.isServer) {
Authentication.checkBoardAccess(req.userId, paramBoardId);
JsonRoutes.sendResult(res, {
code: 200,
data: ReactiveCache.getCardComments({
data: (await ReactiveCache.getCardComments({
boardId: paramBoardId,
cardId: paramCardId,
}).map(function (doc) {
})).map(function (doc) {
return {
_id: doc._id,
comment: doc.text,
@ -285,7 +285,7 @@ if (Meteor.isServer) {
JsonRoutes.add(
'GET',
'/api/boards/:boardId/cards/:cardId/comments/:commentId',
function (req, res) {
async function (req, res) {
try {
const paramBoardId = req.params.boardId;
const paramCommentId = req.params.commentId;
@ -293,7 +293,7 @@ if (Meteor.isServer) {
Authentication.checkBoardAccess(req.userId, paramBoardId);
JsonRoutes.sendResult(res, {
code: 200,
data: ReactiveCache.getCardComment({
data: await ReactiveCache.getCardComment({
_id: paramCommentId,
cardId: paramCardId,
boardId: paramBoardId,
@ -320,7 +320,7 @@ if (Meteor.isServer) {
JsonRoutes.add(
'POST',
'/api/boards/:boardId/cards/:cardId/comments',
function (req, res) {
async function (req, res) {
try {
const paramBoardId = req.params.boardId;
const paramCardId = req.params.cardId;
@ -339,12 +339,12 @@ if (Meteor.isServer) {
},
});
const cardComment = ReactiveCache.getCardComment({
const cardComment = await ReactiveCache.getCardComment({
_id: id,
cardId: paramCardId,
boardId: paramBoardId,
});
commentCreation(req.userId, cardComment);
await commentCreation(req.userId, cardComment);
} catch (error) {
JsonRoutes.sendResult(res, {
code: 200,

File diff suppressed because it is too large Load diff

View file

@ -69,17 +69,17 @@ ChecklistItems.attachSchema(
);
ChecklistItems.allow({
insert(userId, doc) {
async insert(userId, doc) {
// ReadOnly users cannot create checklist items
return allowIsBoardMemberWithWriteAccessByCard(userId, ReactiveCache.getCard(doc.cardId));
return allowIsBoardMemberWithWriteAccessByCard(userId, await ReactiveCache.getCard(doc.cardId));
},
update(userId, doc) {
async update(userId, doc) {
// ReadOnly users cannot edit checklist items
return allowIsBoardMemberWithWriteAccessByCard(userId, ReactiveCache.getCard(doc.cardId));
return allowIsBoardMemberWithWriteAccessByCard(userId, await ReactiveCache.getCard(doc.cardId));
},
remove(userId, doc) {
async remove(userId, doc) {
// ReadOnly users cannot delete checklist items
return allowIsBoardMemberWithWriteAccessByCard(userId, ReactiveCache.getCard(doc.cardId));
return allowIsBoardMemberWithWriteAccessByCard(userId, await ReactiveCache.getCard(doc.cardId));
},
fetch: ['userId', 'cardId'],
});
@ -104,7 +104,8 @@ ChecklistItems.helpers({
return await ChecklistItems.updateAsync(this._id, { $set: { isFinished: !this.isFinished } });
},
async move(checklistId, sortIndex) {
const cardId = ReactiveCache.getChecklist(checklistId).cardId;
const checklist = await ReactiveCache.getChecklist(checklistId);
const cardId = checklist.cardId;
return await ChecklistItems.updateAsync(this._id, {
$set: { cardId, checklistId, sort: sortIndex },
});
@ -112,8 +113,8 @@ ChecklistItems.helpers({
});
// Activities helper
function itemCreation(userId, doc) {
const card = ReactiveCache.getCard(doc.cardId);
async function itemCreation(userId, doc) {
const card = await ReactiveCache.getCard(doc.cardId);
const boardId = card.boardId;
Activities.insert({
userId,
@ -134,8 +135,8 @@ function itemRemover(userId, doc) {
});
}
function publishCheckActivity(userId, doc) {
const card = ReactiveCache.getCard(doc.cardId);
async function publishCheckActivity(userId, doc) {
const card = await ReactiveCache.getCard(doc.cardId);
const boardId = card.boardId;
let activityType;
if (doc.isFinished) {
@ -157,11 +158,11 @@ function publishCheckActivity(userId, doc) {
Activities.insert(act);
}
function publishChekListCompleted(userId, doc) {
const card = ReactiveCache.getCard(doc.cardId);
async function publishChekListCompleted(userId, doc) {
const card = await ReactiveCache.getCard(doc.cardId);
const boardId = card.boardId;
const checklistId = doc.checklistId;
const checkList = ReactiveCache.getChecklist(checklistId);
const checkList = await ReactiveCache.getChecklist(checklistId);
if (checkList.isFinished()) {
const act = {
userId,
@ -177,11 +178,11 @@ function publishChekListCompleted(userId, doc) {
}
}
function publishChekListUncompleted(userId, doc) {
const card = ReactiveCache.getCard(doc.cardId);
async function publishChekListUncompleted(userId, doc) {
const card = await ReactiveCache.getCard(doc.cardId);
const boardId = card.boardId;
const checklistId = doc.checklistId;
const checkList = ReactiveCache.getChecklist(checklistId);
const checkList = await ReactiveCache.getChecklist(checklistId);
// BUGS in IFTTT Rules: https://github.com/wekan/wekan/issues/1972
// Currently in checklist all are set as uncompleted/not checked,
// IFTTT Rule does not move card to other list.
@ -218,22 +219,22 @@ if (Meteor.isServer) {
await ChecklistItems._collection.createIndexAsync({ cardId: 1 });
});
ChecklistItems.after.update((userId, doc, fieldNames) => {
publishCheckActivity(userId, doc);
publishChekListCompleted(userId, doc, fieldNames);
ChecklistItems.after.update(async (userId, doc, fieldNames) => {
await publishCheckActivity(userId, doc);
await publishChekListCompleted(userId, doc, fieldNames);
});
ChecklistItems.before.update((userId, doc, fieldNames) => {
publishChekListUncompleted(userId, doc, fieldNames);
ChecklistItems.before.update(async (userId, doc, fieldNames) => {
await publishChekListUncompleted(userId, doc, fieldNames);
});
ChecklistItems.after.insert((userId, doc) => {
itemCreation(userId, doc);
ChecklistItems.after.insert(async (userId, doc) => {
await itemCreation(userId, doc);
});
ChecklistItems.before.remove((userId, doc) => {
ChecklistItems.before.remove(async (userId, doc) => {
itemRemover(userId, doc);
const card = ReactiveCache.getCard(doc.cardId);
const card = await ReactiveCache.getCard(doc.cardId);
const boardId = card.boardId;
Activities.insert({
userId,
@ -264,15 +265,15 @@ if (Meteor.isServer) {
JsonRoutes.add(
'GET',
'/api/boards/:boardId/cards/:cardId/checklists/:checklistId/items/:itemId',
function(req, res) {
async function(req, res) {
const paramBoardId = req.params.boardId;
const paramCardId = req.params.cardId;
const paramChecklistId = req.params.checklistId;
const paramItemId = req.params.itemId;
Authentication.checkBoardAccess(req.userId, paramBoardId);
const checklistItem = ReactiveCache.getChecklistItem(paramItemId);
const checklistItem = await ReactiveCache.getChecklistItem(paramItemId);
if (checklistItem && checklistItem.cardId === paramCardId && checklistItem.checklistId === paramChecklistId) {
const card = ReactiveCache.getCard(checklistItem.cardId);
const card = await ReactiveCache.getCard(checklistItem.cardId);
if (card && card.boardId === paramBoardId) {
JsonRoutes.sendResult(res, {
code: 200,
@ -305,17 +306,17 @@ if (Meteor.isServer) {
JsonRoutes.add(
'POST',
'/api/boards/:boardId/cards/:cardId/checklists/:checklistId/items',
function(req, res) {
async function(req, res) {
const paramBoardId = req.params.boardId;
const paramChecklistId = req.params.checklistId;
const paramCardId = req.params.cardId;
Authentication.checkBoardAccess(req.userId, paramBoardId);
const checklist = ReactiveCache.getChecklist({
const checklist = await ReactiveCache.getChecklist({
_id: paramChecklistId,
cardId: paramCardId,
});
if (checklist) {
const card = ReactiveCache.getCard(paramCardId);
const card = await ReactiveCache.getCard(paramCardId);
if (card && card.boardId === paramBoardId) {
const id = ChecklistItems.insert({
cardId: paramCardId,
@ -359,21 +360,21 @@ if (Meteor.isServer) {
JsonRoutes.add(
'PUT',
'/api/boards/:boardId/cards/:cardId/checklists/:checklistId/items/:itemId',
function(req, res) {
async function(req, res) {
const paramBoardId = req.params.boardId;
const paramCardId = req.params.cardId;
const paramChecklistId = req.params.checklistId;
const paramItemId = req.params.itemId;
Authentication.checkBoardAccess(req.userId, paramBoardId);
const checklistItem = ReactiveCache.getChecklistItem(paramItemId);
const checklistItem = await ReactiveCache.getChecklistItem(paramItemId);
if (!checklistItem || checklistItem.cardId !== paramCardId || checklistItem.checklistId !== paramChecklistId) {
JsonRoutes.sendResult(res, {
code: 404,
});
return;
}
const card = ReactiveCache.getCard(checklistItem.cardId);
const card = await ReactiveCache.getCard(checklistItem.cardId);
if (!card || card.boardId !== paramBoardId) {
JsonRoutes.sendResult(res, {
code: 404,
@ -427,21 +428,21 @@ if (Meteor.isServer) {
JsonRoutes.add(
'DELETE',
'/api/boards/:boardId/cards/:cardId/checklists/:checklistId/items/:itemId',
function(req, res) {
async function(req, res) {
const paramBoardId = req.params.boardId;
const paramCardId = req.params.cardId;
const paramChecklistId = req.params.checklistId;
const paramItemId = req.params.itemId;
Authentication.checkBoardAccess(req.userId, paramBoardId);
const checklistItem = ReactiveCache.getChecklistItem(paramItemId);
const checklistItem = await ReactiveCache.getChecklistItem(paramItemId);
if (!checklistItem || checklistItem.cardId !== paramCardId || checklistItem.checklistId !== paramChecklistId) {
JsonRoutes.sendResult(res, {
code: 404,
});
return;
}
const card = ReactiveCache.getCard(checklistItem.cardId);
const card = await ReactiveCache.getCard(checklistItem.cardId);
if (!card || card.boardId !== paramBoardId) {
JsonRoutes.sendResult(res, {
code: 404,

View file

@ -88,19 +88,18 @@ Checklists.attachSchema(
);
Checklists.helpers({
copy(newCardId) {
async copy(newCardId) {
let copyObj = Object.assign({}, this);
delete copyObj._id;
copyObj.cardId = newCardId;
const newChecklistId = Checklists.insert(copyObj);
ReactiveCache.getChecklistItems({ checklistId: this._id }).forEach(function(
item,
) {
const items = await ReactiveCache.getChecklistItems({ checklistId: this._id });
for (const item of items) {
item._id = null;
item.checklistId = newChecklistId;
item.cardId = newCardId;
ChecklistItems.insert(item);
});
}
},
itemCount() {
@ -151,19 +150,20 @@ Checklists.helpers({
return ret;
},
async checkAllItems() {
const checkItems = ReactiveCache.getChecklistItems({ checklistId: this._id });
const checkItems = await ReactiveCache.getChecklistItems({ checklistId: this._id });
for (const item of checkItems) {
await item.check();
}
},
async uncheckAllItems() {
const checkItems = ReactiveCache.getChecklistItems({ checklistId: this._id });
const checkItems = await ReactiveCache.getChecklistItems({ checklistId: this._id });
for (const item of checkItems) {
await item.uncheck();
}
},
itemIndex(itemId) {
const items = ReactiveCache.getChecklist({ _id: this._id }).items;
async itemIndex(itemId) {
const checklist = await ReactiveCache.getChecklist({ _id: this._id });
const items = checklist.items;
return _.pluck(items, '_id').indexOf(itemId);
},
@ -196,17 +196,17 @@ Checklists.helpers({
});
Checklists.allow({
insert(userId, doc) {
async insert(userId, doc) {
// ReadOnly users cannot create checklists
return allowIsBoardMemberWithWriteAccessByCard(userId, ReactiveCache.getCard(doc.cardId));
return allowIsBoardMemberWithWriteAccessByCard(userId, await ReactiveCache.getCard(doc.cardId));
},
update(userId, doc) {
async update(userId, doc) {
// ReadOnly users cannot edit checklists
return allowIsBoardMemberWithWriteAccessByCard(userId, ReactiveCache.getCard(doc.cardId));
return allowIsBoardMemberWithWriteAccessByCard(userId, await ReactiveCache.getCard(doc.cardId));
},
remove(userId, doc) {
async remove(userId, doc) {
// ReadOnly users cannot delete checklists
return allowIsBoardMemberWithWriteAccessByCard(userId, ReactiveCache.getCard(doc.cardId));
return allowIsBoardMemberWithWriteAccessByCard(userId, await ReactiveCache.getCard(doc.cardId));
},
fetch: ['userId', 'cardId'],
});
@ -221,22 +221,22 @@ Checklists.before.insert((userId, doc) => {
if (Meteor.isServer) {
Meteor.methods({
moveChecklist(checklistId, newCardId) {
async moveChecklist(checklistId, newCardId) {
check(checklistId, String);
check(newCardId, String);
const checklist = ReactiveCache.getChecklist(checklistId);
const checklist = await ReactiveCache.getChecklist(checklistId);
if (!checklist) {
throw new Meteor.Error('checklist-not-found', 'Checklist not found');
}
const newCard = ReactiveCache.getCard(newCardId);
const newCard = await ReactiveCache.getCard(newCardId);
if (!newCard) {
throw new Meteor.Error('card-not-found', 'Target card not found');
}
// Check permissions on both source and target cards
const sourceCard = ReactiveCache.getCard(checklist.cardId);
const sourceCard = await ReactiveCache.getCard(checklist.cardId);
if (!allowIsBoardMemberByCard(this.userId, sourceCard)) {
throw new Meteor.Error('not-authorized', 'Not authorized to move checklist from source card');
}
@ -245,22 +245,24 @@ if (Meteor.isServer) {
}
// Update activities
ReactiveCache.getActivities({ checklistId }).forEach(activity => {
const activities = await ReactiveCache.getActivities({ checklistId });
for (const activity of activities) {
Activities.update(activity._id, {
$set: {
cardId: newCardId,
},
});
});
}
// Update checklist items
ReactiveCache.getChecklistItems({ checklistId }).forEach(checklistItem => {
const checklistItems = await ReactiveCache.getChecklistItems({ checklistId });
for (const checklistItem of checklistItems) {
ChecklistItems.update(checklistItem._id, {
$set: {
cardId: newCardId,
},
});
});
}
// Update the checklist itself
Checklists.update(checklistId, {
@ -278,8 +280,8 @@ if (Meteor.isServer) {
await Checklists._collection.createIndexAsync({ cardId: 1, createdAt: 1 });
});
Checklists.after.insert((userId, doc) => {
const card = ReactiveCache.getCard(doc.cardId);
Checklists.after.insert(async (userId, doc) => {
const card = await ReactiveCache.getCard(doc.cardId);
Activities.insert({
userId,
activityType: 'addChecklist',
@ -292,19 +294,19 @@ if (Meteor.isServer) {
});
});
Checklists.before.remove((userId, doc) => {
const activities = ReactiveCache.getActivities({ checklistId: doc._id });
const card = ReactiveCache.getCard(doc.cardId);
Checklists.before.remove(async (userId, doc) => {
const activities = await ReactiveCache.getActivities({ checklistId: doc._id });
const card = await ReactiveCache.getCard(doc.cardId);
if (activities) {
activities.forEach(activity => {
for (const activity of activities) {
Activities.remove(activity._id);
});
}
}
Activities.insert({
userId,
activityType: 'removeChecklist',
cardId: doc.cardId,
boardId: ReactiveCache.getCard(doc.cardId).boardId,
boardId: (await ReactiveCache.getCard(doc.cardId)).boardId,
checklistId: doc._id,
checklistName: doc.title,
listId: card.listId,
@ -326,13 +328,13 @@ if (Meteor.isServer) {
JsonRoutes.add(
'GET',
'/api/boards/:boardId/cards/:cardId/checklists',
function(req, res) {
async function(req, res) {
const paramBoardId = req.params.boardId;
const paramCardId = req.params.cardId;
Authentication.checkBoardAccess(req.userId, paramBoardId);
// Verify the card belongs to the board
const card = ReactiveCache.getCard({
const card = await ReactiveCache.getCard({
_id: paramCardId,
boardId: paramBoardId,
});
@ -344,7 +346,7 @@ if (Meteor.isServer) {
return;
}
const checklists = ReactiveCache.getChecklists({ cardId: paramCardId }).map(function(
const checklists = (await ReactiveCache.getChecklists({ cardId: paramCardId })).map(function(
doc,
) {
return {
@ -384,14 +386,14 @@ if (Meteor.isServer) {
JsonRoutes.add(
'GET',
'/api/boards/:boardId/cards/:cardId/checklists/:checklistId',
function(req, res) {
async function(req, res) {
const paramBoardId = req.params.boardId;
const paramChecklistId = req.params.checklistId;
const paramCardId = req.params.cardId;
Authentication.checkBoardAccess(req.userId, paramBoardId);
// Verify the card belongs to the board
const card = ReactiveCache.getCard({
const card = await ReactiveCache.getCard({
_id: paramCardId,
boardId: paramBoardId,
});
@ -403,14 +405,14 @@ if (Meteor.isServer) {
return;
}
const checklist = ReactiveCache.getChecklist({
const checklist = await ReactiveCache.getChecklist({
_id: paramChecklistId,
cardId: paramCardId,
});
if (checklist) {
checklist.items = ReactiveCache.getChecklistItems({
checklist.items = (await ReactiveCache.getChecklistItems({
checklistId: checklist._id,
}).map(function(doc) {
})).map(function(doc) {
return {
_id: doc._id,
title: doc.title,
@ -442,19 +444,19 @@ if (Meteor.isServer) {
JsonRoutes.add(
'POST',
'/api/boards/:boardId/cards/:cardId/checklists',
function(req, res) {
async function(req, res) {
// Check user is logged in
//Authentication.checkLoggedIn(req.userId);
const paramBoardId = req.params.boardId;
Authentication.checkBoardAccess(req.userId, paramBoardId);
// Check user has permission to add checklist to the card
const board = ReactiveCache.getBoard(paramBoardId);
const board = await ReactiveCache.getBoard(paramBoardId);
const addPermission = allowIsBoardMemberCommentOnly(req.userId, board);
Authentication.checkAdminOrCondition(req.userId, addPermission);
const paramCardId = req.params.cardId;
// Verify the card belongs to the board
const card = ReactiveCache.getCard({
const card = await ReactiveCache.getCard({
_id: paramCardId,
boardId: paramBoardId,
});
@ -516,14 +518,14 @@ if (Meteor.isServer) {
JsonRoutes.add(
'DELETE',
'/api/boards/:boardId/cards/:cardId/checklists/:checklistId',
function(req, res) {
async function(req, res) {
const paramBoardId = req.params.boardId;
const paramCardId = req.params.cardId;
const paramChecklistId = req.params.checklistId;
Authentication.checkBoardAccess(req.userId, paramBoardId);
// Verify the card belongs to the board
const card = ReactiveCache.getCard({
const card = await ReactiveCache.getCard({
_id: paramCardId,
boardId: paramBoardId,
});
@ -536,7 +538,7 @@ if (Meteor.isServer) {
}
// Verify the checklist exists and belongs to the card
const checklist = ReactiveCache.getChecklist({
const checklist = await ReactiveCache.getChecklist({
_id: paramChecklistId,
cardId: paramCardId,
});

View file

@ -247,7 +247,7 @@ export class CsvCreator {
this.swimlane = swimlaneId;
}
createLists(csvData, boardId) {
async createLists(csvData, boardId) {
let numOfCreatedLists = 0;
for (let i = 1; i < csvData.length; i++) {
const listToCreate = {
@ -256,7 +256,7 @@ export class CsvCreator {
createdAt: this._now(),
};
if (csvData[i][this.fieldIndex.stage]) {
const existingList = ReactiveCache.getLists({
const existingList = await ReactiveCache.getLists({
title: csvData[i][this.fieldIndex.stage],
boardId,
});
@ -279,7 +279,7 @@ export class CsvCreator {
}
}
createCards(csvData, boardId) {
async createCards(csvData, boardId) {
for (let i = 1; i < csvData.length; i++) {
const cardToCreate = {
archived: false,
@ -321,7 +321,7 @@ export class CsvCreator {
}
// add the labels
if (csvData[i][this.fieldIndex.labels]) {
const board = ReactiveCache.getBoard(boardId);
const board = await ReactiveCache.getBoard(boardId);
for (const importedLabel of csvData[i][this.fieldIndex.labels].split(
' ',
)) {
@ -388,15 +388,15 @@ export class CsvCreator {
Meteor.settings.public &&
Meteor.settings.public.sandstorm;
if (isSandstorm && currentBoardId) {
const currentBoard = ReactiveCache.getBoard(currentBoardId);
const currentBoard = await ReactiveCache.getBoard(currentBoardId);
await currentBoard.archive();
}
this.mapHeadertoCardFieldIndex(board[0]);
const boardId = this.createBoard(board);
this.createLists(board, boardId);
await this.createLists(board, boardId);
this.createSwimlanes(boardId);
this.createCustomFields(boardId);
this.createCards(board, boardId);
await this.createCards(board, boardId);
return boardId;
}
}

View file

@ -164,26 +164,26 @@ CustomFields.helpers({
});
CustomFields.allow({
insert(userId, doc) {
async insert(userId, doc) {
return allowIsAnyBoardMember(
userId,
ReactiveCache.getBoards({
await ReactiveCache.getBoards({
_id: { $in: doc.boardIds },
}),
);
},
update(userId, doc) {
async update(userId, doc) {
return allowIsAnyBoardMember(
userId,
ReactiveCache.getBoards({
await ReactiveCache.getBoards({
_id: { $in: doc.boardIds },
}),
);
},
remove(userId, doc) {
async remove(userId, doc) {
return allowIsAnyBoardMember(
userId,
ReactiveCache.getBoards({
await ReactiveCache.getBoards({
_id: { $in: doc.boardIds },
}),
);
@ -214,9 +214,9 @@ function customFieldDeletion(userId, doc) {
// This has some bug, it does not show edited customField value at Outgoing Webhook,
// instead it shows undefined, and no listId and swimlaneId.
function customFieldEdit(userId, doc) {
const card = ReactiveCache.getCard(doc.cardId);
const customFieldValue = ReactiveCache.getActivity({ customFieldId: doc._id }).value;
async function customFieldEdit(userId, doc) {
const card = await ReactiveCache.getCard(doc.cardId);
const customFieldValue = (await ReactiveCache.getActivity({ customFieldId: doc._id })).value;
Activities.insert({
userId,
activityType: 'setCustomField',
@ -242,14 +242,14 @@ if (Meteor.isServer) {
}
});
CustomFields.before.update((userId, doc, fieldNames, modifier) => {
CustomFields.before.update(async (userId, doc, fieldNames, modifier) => {
if (_.contains(fieldNames, 'boardIds') && modifier.$pull) {
Cards.update(
{ boardId: modifier.$pull.boardIds, 'customFields._id': doc._id },
{ $pull: { customFields: { _id: doc._id } } },
{ multi: true },
);
customFieldEdit(userId, doc);
await customFieldEdit(userId, doc);
Activities.remove({
customFieldId: doc._id,
boardId: modifier.$pull.boardIds,
@ -296,7 +296,7 @@ if (Meteor.isServer) {
* name: string,
* type: string}]
*/
JsonRoutes.add('GET', '/api/boards/:boardId/custom-fields', function(
JsonRoutes.add('GET', '/api/boards/:boardId/custom-fields', async function(
req,
res,
) {
@ -304,7 +304,7 @@ if (Meteor.isServer) {
Authentication.checkBoardAccess(req.userId, paramBoardId);
JsonRoutes.sendResult(res, {
code: 200,
data: ReactiveCache.getCustomFields({ boardIds: { $in: [paramBoardId] } }).map(
data: (await ReactiveCache.getCustomFields({ boardIds: { $in: [paramBoardId] } })).map(
function(cf) {
return {
_id: cf._id,
@ -328,13 +328,13 @@ if (Meteor.isServer) {
JsonRoutes.add(
'GET',
'/api/boards/:boardId/custom-fields/:customFieldId',
function(req, res) {
async function(req, res) {
const paramBoardId = req.params.boardId;
const paramCustomFieldId = req.params.customFieldId;
Authentication.checkBoardAccess(req.userId, paramBoardId);
JsonRoutes.sendResult(res, {
code: 200,
data: ReactiveCache.getCustomField({
data: await ReactiveCache.getCustomField({
_id: paramCustomFieldId,
boardIds: { $in: [paramBoardId] },
}),
@ -356,13 +356,13 @@ if (Meteor.isServer) {
* @param {boolean} showSumAtTopOfList should the sum of the custom fields be shown at top of list?
* @return_type {_id: string}
*/
JsonRoutes.add('POST', '/api/boards/:boardId/custom-fields', function(
JsonRoutes.add('POST', '/api/boards/:boardId/custom-fields', async function(
req,
res,
) {
const paramBoardId = req.params.boardId;
Authentication.checkBoardAccess(req.userId, paramBoardId);
const board = ReactiveCache.getBoard(paramBoardId);
const board = await ReactiveCache.getBoard(paramBoardId);
const id = CustomFields.direct.insert({
name: req.body.name,
type: req.body.type,
@ -374,7 +374,7 @@ if (Meteor.isServer) {
boardIds: [board._id],
});
const customField = ReactiveCache.getCustomField({
const customField = await ReactiveCache.getCustomField({
_id: id,
boardIds: { $in: [paramBoardId] },
});

View file

@ -27,14 +27,14 @@ if (Meteor.isServer) {
* @param {string} boardId the ID of the board we are exporting
* @param {string} authToken the loginToken
*/
JsonRoutes.add('get', '/api/boards/:boardId/export', function (req, res) {
JsonRoutes.add('get', '/api/boards/:boardId/export', async function (req, res) {
const boardId = req.params.boardId;
let user = null;
let impersonateDone = false;
let adminId = null;
// First check if board exists and is public to avoid unnecessary authentication
const board = ReactiveCache.getBoard(boardId);
const board = await ReactiveCache.getBoard(boardId);
if (!board) {
JsonRoutes.sendResult(res, 404);
return;
@ -46,7 +46,7 @@ if (Meteor.isServer) {
const exporter = new Exporter(boardId);
JsonRoutes.sendResult(res, {
code: 200,
data: exporter.build(),
data: await exporter.build(),
});
return;
}
@ -64,18 +64,18 @@ if (Meteor.isServer) {
}
const hashToken = Accounts._hashLoginToken(loginToken);
user = ReactiveCache.getUser({
user = await ReactiveCache.getUser({
'services.resume.loginTokens.hashedToken': hashToken,
});
adminId = user._id.toString();
impersonateDone = ReactiveCache.getImpersonatedUser({ adminId: adminId });
impersonateDone = await ReactiveCache.getImpersonatedUser({ adminId: adminId });
} else if (!Meteor.settings.public.sandstorm) {
Authentication.checkUserId(req.userId);
user = ReactiveCache.getUser({ _id: req.userId, isAdmin: true });
user = await ReactiveCache.getUser({ _id: req.userId, isAdmin: true });
}
const exporter = new Exporter(boardId);
if (exporter.canExport(user) || impersonateDone) {
if (await exporter.canExport(user) || impersonateDone) {
if (impersonateDone) {
ImpersonatedUsers.insert({
adminId: adminId,
@ -86,7 +86,7 @@ if (Meteor.isServer) {
JsonRoutes.sendResult(res, {
code: 200,
data: exporter.build(),
data: await exporter.build(),
});
} else {
// we could send an explicit error message, but on the other hand the only
@ -118,7 +118,7 @@ if (Meteor.isServer) {
JsonRoutes.add(
'get',
'/api/boards/:boardId/attachments/:attachmentId/export',
function (req, res) {
async function (req, res) {
const boardId = req.params.boardId;
const attachmentId = req.params.attachmentId;
let user = null;
@ -126,7 +126,7 @@ if (Meteor.isServer) {
let adminId = null;
// First check if board exists and is public to avoid unnecessary authentication
const board = ReactiveCache.getBoard(boardId);
const board = await ReactiveCache.getBoard(boardId);
if (!board) {
JsonRoutes.sendResult(res, 404);
return;
@ -138,7 +138,7 @@ if (Meteor.isServer) {
const exporter = new Exporter(boardId, attachmentId);
JsonRoutes.sendResult(res, {
code: 200,
data: exporter.build(),
data: await exporter.build(),
});
return;
}
@ -156,18 +156,18 @@ if (Meteor.isServer) {
}
const hashToken = Accounts._hashLoginToken(loginToken);
user = ReactiveCache.getUser({
user = await ReactiveCache.getUser({
'services.resume.loginTokens.hashedToken': hashToken,
});
adminId = user._id.toString();
impersonateDone = ReactiveCache.getImpersonatedUser({ adminId: adminId });
impersonateDone = await ReactiveCache.getImpersonatedUser({ adminId: adminId });
} else if (!Meteor.settings.public.sandstorm) {
Authentication.checkUserId(req.userId);
user = ReactiveCache.getUser({ _id: req.userId, isAdmin: true });
user = await ReactiveCache.getUser({ _id: req.userId, isAdmin: true });
}
const exporter = new Exporter(boardId, attachmentId);
if (exporter.canExport(user) || impersonateDone) {
if (await exporter.canExport(user) || impersonateDone) {
if (impersonateDone) {
ImpersonatedUsers.insert({
adminId: adminId,
@ -178,7 +178,7 @@ if (Meteor.isServer) {
}
JsonRoutes.sendResult(res, {
code: 200,
data: exporter.build(),
data: await exporter.build(),
});
} else {
// we could send an explicit error message, but on the other hand the only
@ -203,14 +203,14 @@ if (Meteor.isServer) {
* @param {string} authToken the loginToken
* @param {string} delimiter delimiter to use while building export. Default is comma ','
*/
Picker.route('/api/boards/:boardId/export/csv', function (params, req, res) {
Picker.route('/api/boards/:boardId/export/csv', async function (params, req, res) {
const boardId = params.boardId;
let user = null;
let impersonateDone = false;
let adminId = null;
// First check if board exists and is public to avoid unnecessary authentication
const board = ReactiveCache.getBoard(boardId);
const board = await ReactiveCache.getBoard(boardId);
if (!board) {
res.writeHead(404);
res.end('Board not found');
@ -237,7 +237,7 @@ if (Meteor.isServer) {
// use Uint8Array to prevent from converting bytes to string
res.write(new Uint8Array([0xEF, 0xBB, 0xBF]));
}
res.write(exporter.buildCsv(params.query.delimiter, 'en'));
res.write(await exporter.buildCsv(params.query.delimiter, 'en'));
res.end();
return;
}
@ -256,21 +256,21 @@ if (Meteor.isServer) {
}
const hashToken = Accounts._hashLoginToken(loginToken);
user = ReactiveCache.getUser({
user = await ReactiveCache.getUser({
'services.resume.loginTokens.hashedToken': hashToken,
});
adminId = user._id.toString();
impersonateDone = ReactiveCache.getImpersonatedUser({ adminId: adminId });
impersonateDone = await ReactiveCache.getImpersonatedUser({ adminId: adminId });
} else if (!Meteor.settings.public.sandstorm) {
Authentication.checkUserId(req.userId);
user = ReactiveCache.getUser({
user = await ReactiveCache.getUser({
_id: req.userId,
isAdmin: true,
});
}
const exporter = new Exporter(boardId);
if (exporter.canExport(user) || impersonateDone) {
if (await exporter.canExport(user) || impersonateDone) {
if (impersonateDone) {
let exportType = 'exportCSV';
if( params.query.delimiter == "\t" ) {
@ -303,7 +303,7 @@ if (Meteor.isServer) {
// use Uint8Array to prevent from converting bytes to string
res.write(new Uint8Array([0xEF, 0xBB, 0xBF]));
}
res.write(exporter.buildCsv(params.query.delimiter, userLanguage));
res.write(await exporter.buildCsv(params.query.delimiter, userLanguage));
res.end();
} else {
res.writeHead(403);

View file

@ -30,14 +30,14 @@ runOnServer(function() {
* @param {string} boardId the ID of the board we are exporting
* @param {string} authToken the loginToken
*/
Picker.route('/api/boards/:boardId/exportExcel', function (params, req, res) {
Picker.route('/api/boards/:boardId/exportExcel', async function (params, req, res) {
const boardId = params.boardId;
let user = null;
let impersonateDone = false;
let adminId = null;
// First check if board exists and is public to avoid unnecessary authentication
const board = ReactiveCache.getBoard(boardId);
const board = await ReactiveCache.getBoard(boardId);
if (!board) {
res.end('Board not found');
return;
@ -64,14 +64,14 @@ runOnServer(function() {
}
const hashToken = Accounts._hashLoginToken(loginToken);
user = ReactiveCache.getUser({
user = await ReactiveCache.getUser({
'services.resume.loginTokens.hashedToken': hashToken,
});
adminId = user._id.toString();
impersonateDone = ReactiveCache.getImpersonatedUser({ adminId: adminId });
impersonateDone = await ReactiveCache.getImpersonatedUser({ adminId: adminId });
} else if (!Meteor.settings.public.sandstorm) {
Authentication.checkUserId(req.userId);
user = ReactiveCache.getUser({
user = await ReactiveCache.getUser({
_id: req.userId,
isAdmin: true,
});

View file

@ -30,7 +30,7 @@ runOnServer(function() {
* @param {string} boardId the ID of the board we are exporting
* @param {string} authToken the loginToken
*/
Picker.route('/api/boards/:boardId/lists/:listId/cards/:cardId/exportPDF', function (params, req, res) {
Picker.route('/api/boards/:boardId/lists/:listId/cards/:cardId/exportPDF', async function (params, req, res) {
const boardId = params.boardId;
const paramListId = req.params.listId;
const paramCardId = req.params.cardId;
@ -39,7 +39,7 @@ runOnServer(function() {
let adminId = null;
// First check if board exists and is public to avoid unnecessary authentication
const board = ReactiveCache.getBoard(boardId);
const board = await ReactiveCache.getBoard(boardId);
if (!board) {
res.end('Board not found');
return;
@ -66,14 +66,14 @@ runOnServer(function() {
}
const hashToken = Accounts._hashLoginToken(loginToken);
user = ReactiveCache.getUser({
user = await ReactiveCache.getUser({
'services.resume.loginTokens.hashedToken': hashToken,
});
adminId = user._id.toString();
impersonateDone = ReactiveCache.getImpersonatedUser({ adminId: adminId });
impersonateDone = await ReactiveCache.getImpersonatedUser({ adminId: adminId });
} else if (!Meteor.settings.public.sandstorm) {
Authentication.checkUserId(req.userId);
user = ReactiveCache.getUser({
user = await ReactiveCache.getUser({
_id: req.userId,
isAdmin: true,
});

View file

@ -34,7 +34,7 @@ export class Exporter {
this._attachmentId = attachmentId;
}
build() {
async build() {
const fs = Npm.require('fs');
const os = Npm.require('os');
const path = Npm.require('path');
@ -55,7 +55,7 @@ export class Exporter {
};
_.extend(
result,
ReactiveCache.getBoard(this._boardId, {
await ReactiveCache.getBoard(this._boardId, {
fields: {
stars: 0,
},
@ -97,7 +97,7 @@ export class Exporter {
const byBoardAndAttachment = this._attachmentId
? { boardId: this._boardId, _id: this._attachmentId }
: byBoard;
result.attachments = ReactiveCache.getAttachments(byBoardAndAttachment)
result.attachments = (await ReactiveCache.getAttachments(byBoardAndAttachment))
.map((attachment) => {
let filebase64 = null;
filebase64 = getBase64DataSync(attachment);
@ -116,41 +116,41 @@ export class Exporter {
return result.attachments.length > 0 ? result.attachments[0] : {};
}
result.lists = ReactiveCache.getLists(byBoard, noBoardId);
result.cards = ReactiveCache.getCards(byBoardNoLinked, noBoardId);
result.swimlanes = ReactiveCache.getSwimlanes(byBoard, noBoardId);
result.customFields = ReactiveCache.getCustomFields(
result.lists = await ReactiveCache.getLists(byBoard, noBoardId);
result.cards = await ReactiveCache.getCards(byBoardNoLinked, noBoardId);
result.swimlanes = await ReactiveCache.getSwimlanes(byBoard, noBoardId);
result.customFields = await ReactiveCache.getCustomFields(
{ boardIds: this._boardId },
{ fields: { boardIds: 0 } },
);
result.comments = ReactiveCache.getCardComments(byBoard, noBoardId);
result.activities = ReactiveCache.getActivities(byBoard, noBoardId);
result.rules = ReactiveCache.getRules(byBoard, noBoardId);
result.comments = await ReactiveCache.getCardComments(byBoard, noBoardId);
result.activities = await ReactiveCache.getActivities(byBoard, noBoardId);
result.rules = await ReactiveCache.getRules(byBoard, noBoardId);
result.checklists = [];
result.checklistItems = [];
result.subtaskItems = [];
result.triggers = [];
result.actions = [];
result.cards.forEach((card) => {
for (const card of result.cards) {
result.checklists.push(
...ReactiveCache.getChecklists({
...await ReactiveCache.getChecklists({
cardId: card._id,
}),
);
result.checklistItems.push(
...ReactiveCache.getChecklistItems({
...await ReactiveCache.getChecklistItems({
cardId: card._id,
}),
);
result.subtaskItems.push(
...ReactiveCache.getCards({
...await ReactiveCache.getCards({
parentId: card._id,
}),
);
});
result.rules.forEach((rule) => {
}
for (const rule of result.rules) {
result.triggers.push(
...ReactiveCache.getTriggers(
...await ReactiveCache.getTriggers(
{
_id: rule.triggerId,
},
@ -158,14 +158,14 @@ export class Exporter {
),
);
result.actions.push(
...ReactiveCache.getActions(
...await ReactiveCache.getActions(
{
_id: rule.actionId,
},
noBoardId,
),
);
});
}
// we also have to export some user data - as the other elements only
// include id but we have to be careful:
@ -211,7 +211,7 @@ export class Exporter {
'profile.avatarUrl': 1,
},
};
result.users = ReactiveCache.getUsers(byUserIds, userFields)
result.users = (await ReactiveCache.getUsers(byUserIds, userFields))
.map((user) => {
// user avatar is stored as a relative url, we export absolute
if ((user.profile || {}).avatarUrl) {
@ -222,8 +222,8 @@ export class Exporter {
return result;
}
buildCsv(userDelimiter = ',', userLanguage='en') {
const result = this.build();
async buildCsv(userDelimiter = ',', userLanguage='en') {
const result = await this.build();
const columnHeaders = [];
const cardRows = [];
@ -398,8 +398,8 @@ export class Exporter {
return Papa.unparse(cardRows, papaconfig);
}
canExport(user) {
const board = ReactiveCache.getBoard(this._boardId);
async canExport(user) {
const board = await ReactiveCache.getBoard(this._boardId);
return board && board.isVisibleBy(user);
}
}

View file

@ -101,21 +101,21 @@ Integrations.Const = {
},
};
const permissionHelper = {
allow(userId, doc) {
const user = ReactiveCache.getUser(userId);
const isAdmin = user && ReactiveCache.getCurrentUser().isAdmin;
return isAdmin || allowIsBoardAdmin(userId, ReactiveCache.getBoard(doc.boardId));
async allow(userId, doc) {
const user = await ReactiveCache.getUser(userId);
const isAdmin = user && (await ReactiveCache.getCurrentUser()).isAdmin;
return isAdmin || allowIsBoardAdmin(userId, await ReactiveCache.getBoard(doc.boardId));
},
};
Integrations.allow({
insert(userId, doc) {
return permissionHelper.allow(userId, doc);
async insert(userId, doc) {
return await permissionHelper.allow(userId, doc);
},
update(userId, doc) {
return permissionHelper.allow(userId, doc);
async update(userId, doc) {
return await permissionHelper.allow(userId, doc);
},
remove(userId, doc) {
return permissionHelper.allow(userId, doc);
async remove(userId, doc) {
return await permissionHelper.allow(userId, doc);
},
fetch: ['boardId'],
});
@ -134,7 +134,7 @@ if (Meteor.isServer) {
* @param {string} boardId the board ID
* @return_type [Integrations]
*/
JsonRoutes.add('GET', '/api/boards/:boardId/integrations', function(
JsonRoutes.add('GET', '/api/boards/:boardId/integrations', async function(
req,
res,
) {
@ -142,10 +142,10 @@ if (Meteor.isServer) {
const paramBoardId = req.params.boardId;
Authentication.checkBoardAccess(req.userId, paramBoardId);
const data = ReactiveCache.getIntegrations(
const data = (await ReactiveCache.getIntegrations(
{ boardId: paramBoardId },
{ fields: { token: 0 } },
).map(function(doc) {
)).map(function(doc) {
return doc;
});
@ -166,7 +166,7 @@ if (Meteor.isServer) {
* @param {string} intId the integration ID
* @return_type Integrations
*/
JsonRoutes.add('GET', '/api/boards/:boardId/integrations/:intId', function(
JsonRoutes.add('GET', '/api/boards/:boardId/integrations/:intId', async function(
req,
res,
) {
@ -177,7 +177,7 @@ if (Meteor.isServer) {
JsonRoutes.sendResult(res, {
code: 200,
data: ReactiveCache.getIntegration(
data: await ReactiveCache.getIntegration(
{ _id: paramIntId, boardId: paramBoardId },
{ fields: { token: 0 } },
),
@ -310,7 +310,7 @@ if (Meteor.isServer) {
JsonRoutes.add(
'DELETE',
'/api/boards/:boardId/integrations/:intId/activities',
function(req, res) {
async function(req, res) {
try {
const paramBoardId = req.params.boardId;
const paramIntId = req.params.intId;
@ -324,7 +324,7 @@ if (Meteor.isServer) {
JsonRoutes.sendResult(res, {
code: 200,
data: ReactiveCache.getIntegration(
data: await ReactiveCache.getIntegration(
{ _id: paramIntId, boardId: paramBoardId },
{ fields: { _id: 1, activities: 1 } },
),
@ -350,7 +350,7 @@ if (Meteor.isServer) {
JsonRoutes.add(
'POST',
'/api/boards/:boardId/integrations/:intId/activities',
function(req, res) {
async function(req, res) {
try {
const paramBoardId = req.params.boardId;
const paramIntId = req.params.intId;
@ -364,7 +364,7 @@ if (Meteor.isServer) {
JsonRoutes.sendResult(res, {
code: 200,
data: ReactiveCache.getIntegration(
data: await ReactiveCache.getIntegration(
{ _id: paramIntId, boardId: paramBoardId },
{ fields: { _id: 1, activities: 1 } },
),

View file

@ -55,8 +55,8 @@ InvitationCodes.attachSchema(
);
InvitationCodes.helpers({
author() {
return ReactiveCache.getUser(this.authorId);
async author() {
return await ReactiveCache.getUser(this.authorId);
},
});

View file

@ -580,8 +580,8 @@ export const moveToStorage = function(fileObj, storageDestination, fileStoreStra
});
};
export const copyFile = function(fileObj, newCardId, fileStoreStrategyFactory) {
const newCard = ReactiveCache.getCard(newCardId);
export const copyFile = async function(fileObj, newCardId, fileStoreStrategyFactory) {
const newCard = await ReactiveCache.getCard(newCardId);
Object.keys(fileObj.versions).forEach(versionName => {
const strategyRead = fileStoreStrategyFactory.getFileStrategy(fileObj, versionName);
const readStream = strategyRead.getReadStream();

View file

@ -180,30 +180,30 @@ Lists.attachSchema(
);
Lists.allow({
insert(userId, doc) {
async insert(userId, doc) {
// ReadOnly and CommentOnly users cannot create lists
return allowIsBoardMemberWithWriteAccess(userId, ReactiveCache.getBoard(doc.boardId));
return allowIsBoardMemberWithWriteAccess(userId, await ReactiveCache.getBoard(doc.boardId));
},
update(userId, doc) {
async update(userId, doc) {
// ReadOnly and CommentOnly users cannot edit lists
return allowIsBoardMemberWithWriteAccess(userId, ReactiveCache.getBoard(doc.boardId));
return allowIsBoardMemberWithWriteAccess(userId, await ReactiveCache.getBoard(doc.boardId));
},
remove(userId, doc) {
async remove(userId, doc) {
// ReadOnly and CommentOnly users cannot delete lists
return allowIsBoardMemberWithWriteAccess(userId, ReactiveCache.getBoard(doc.boardId));
return allowIsBoardMemberWithWriteAccess(userId, await ReactiveCache.getBoard(doc.boardId));
},
fetch: ['boardId'],
});
Lists.helpers({
copy(boardId, swimlaneId) {
async copy(boardId, swimlaneId) {
const oldId = this._id;
const oldSwimlaneId = this.swimlaneId || null;
this.boardId = boardId;
this.swimlaneId = swimlaneId;
let _id = null;
const existingListWithSameName = ReactiveCache.getList({
const existingListWithSameName = await ReactiveCache.getList({
boardId,
title: this.title,
archived: false,
@ -213,21 +213,22 @@ Lists.helpers({
} else {
delete this._id;
this.swimlaneId = swimlaneId; // Set the target swimlane for the copied list
_id = Lists.insert(this);
_id = await Lists.insertAsync(this);
}
// Copy all cards in list
ReactiveCache.getCards({
const cards = await ReactiveCache.getCards({
swimlaneId: oldSwimlaneId,
listId: oldId,
archived: false,
}).forEach(card => {
card.copy(boardId, swimlaneId, _id);
});
for (const card of cards) {
await card.copy(boardId, swimlaneId, _id);
}
},
async move(boardId, swimlaneId) {
const boardList = ReactiveCache.getList({
const boardList = await ReactiveCache.getList({
boardId,
title: this.title,
archived: false,
@ -235,13 +236,13 @@ Lists.helpers({
let listId;
if (boardList) {
listId = boardList._id;
for (const card of this.cards()) {
for (const card of await this.cards()) {
await card.move(boardId, this._id, boardList._id);
}
} else {
console.log('list.title:', this.title);
console.log('boardList:', boardList);
listId = Lists.insert({
listId = await Lists.insertAsync({
title: this.title,
boardId,
type: this.type,
@ -251,42 +252,42 @@ Lists.helpers({
});
}
for (const card of this.cards(swimlaneId)) {
for (const card of await this.cards(swimlaneId)) {
await card.move(boardId, swimlaneId, listId);
}
},
cards(swimlaneId) {
async cards(swimlaneId) {
const selector = {
listId: this._id,
archived: false,
};
if (swimlaneId) selector.swimlaneId = swimlaneId;
const ret = ReactiveCache.getCards(Filter.mongoSelector(selector), { sort: ['sort'] });
const ret = await ReactiveCache.getCards(Filter.mongoSelector(selector), { sort: ['sort'] });
return ret;
},
cardsUnfiltered(swimlaneId) {
async cardsUnfiltered(swimlaneId) {
const selector = {
listId: this._id,
archived: false,
};
if (swimlaneId) selector.swimlaneId = swimlaneId;
const ret = ReactiveCache.getCards(selector, { sort: ['sort'] });
const ret = await ReactiveCache.getCards(selector, { sort: ['sort'] });
return ret;
},
allCards() {
const ret = ReactiveCache.getCards({ listId: this._id });
async allCards() {
const ret = await ReactiveCache.getCards({ listId: this._id });
return ret;
},
board() {
return ReactiveCache.getBoard(this.boardId);
async board() {
return await ReactiveCache.getBoard(this.boardId);
},
getWipLimit(option) {
const list = ReactiveCache.getList(this._id);
async getWipLimit(option) {
const list = await ReactiveCache.getList(this._id);
if (!list.wipLimit) {
// Necessary check to avoid exceptions for the case where the doc doesn't have the wipLimit field yet set
return 0;
@ -310,9 +311,9 @@ Lists.helpers({
return this.starred === true;
},
isCollapsed() {
async isCollapsed() {
if (Meteor.isClient) {
const user = ReactiveCache.getCurrentUser();
const user = await ReactiveCache.getCurrentUser();
// Logged-in users: prefer profile/cookie-backed state
if (user && user.getCollapsedListFromStorage) {
const stored = user.getCollapsedListFromStorage(this.boardId, this._id);
@ -331,13 +332,13 @@ Lists.helpers({
return this.collapsed === true;
},
absoluteUrl() {
const card = ReactiveCache.getCard({ listId: this._id });
return card && card.absoluteUrl();
async absoluteUrl() {
const card = await ReactiveCache.getCard({ listId: this._id });
return card && (await card.absoluteUrl());
},
originRelativeUrl() {
const card = ReactiveCache.getCard({ listId: this._id });
return card && card.originRelativeUrl();
async originRelativeUrl() {
const card = await ReactiveCache.getCard({ listId: this._id });
return card && (await card.originRelativeUrl());
},
async remove() {
return await Lists.removeAsync({ _id: this._id });
@ -361,7 +362,7 @@ Lists.helpers({
async archive() {
if (this.isTemplateList()) {
for (const card of this.cards()) {
for (const card of await this.cards()) {
await card.archive();
}
}
@ -370,7 +371,7 @@ Lists.helpers({
async restore() {
if (this.isTemplateList()) {
for (const card of this.allCards()) {
for (const card of await this.allCards()) {
await card.restore();
}
}
@ -394,23 +395,25 @@ Lists.helpers({
},
});
Lists.userArchivedLists = userId => {
return ReactiveCache.getLists({
boardId: { $in: Boards.userBoardIds(userId, null) },
Lists.userArchivedLists = async userId => {
return await ReactiveCache.getLists({
boardId: { $in: await Boards.userBoardIds(userId, null) },
archived: true,
})
};
Lists.userArchivedListIds = () => {
return Lists.userArchivedLists().map(list => { return list._id; });
Lists.userArchivedListIds = async () => {
const lists = await Lists.userArchivedLists();
return lists.map(list => { return list._id; });
};
Lists.archivedLists = () => {
return ReactiveCache.getLists({ archived: true });
Lists.archivedLists = async () => {
return await ReactiveCache.getLists({ archived: true });
};
Lists.archivedListIds = () => {
return Lists.archivedLists().map(list => {
Lists.archivedListIds = async () => {
const lists = await Lists.archivedLists();
return lists.map(list => {
return list._id;
});
};
@ -424,12 +427,12 @@ Meteor.methods({
throw new Meteor.Error('not-authorized', 'You must be logged in.');
}
const list = ReactiveCache.getList(listId);
const list = await ReactiveCache.getList(listId);
if (!list) {
throw new Meteor.Error('list-not-found', 'List not found');
}
const board = ReactiveCache.getBoard(list.boardId);
const board = await 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.');
}
@ -447,63 +450,62 @@ Meteor.methods({
throw new Meteor.Error('not-authorized', 'You must be logged in.');
}
const list = ReactiveCache.getList(listId);
const list = await ReactiveCache.getList(listId);
if (!list) {
throw new Meteor.Error('list-not-found', 'List not found');
}
const board = ReactiveCache.getBoard(list.boardId);
const board = await 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) {
if ((await list.getWipLimit('value')) === 0) {
await list.setWipLimit(1);
}
list.toggleWipLimit(!list.getWipLimit('enabled'));
await list.toggleWipLimit(!(await list.getWipLimit('enabled')));
},
enableSoftLimit(listId) {
async enableSoftLimit(listId) {
check(listId, String);
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'You must be logged in.');
}
const list = ReactiveCache.getList(listId);
const list = await ReactiveCache.getList(listId);
if (!list) {
throw new Meteor.Error('list-not-found', 'List not found');
}
const board = ReactiveCache.getBoard(list.boardId);
const board = await 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.');
}
list.toggleSoftLimit(!list.getWipLimit('soft'));
await list.toggleSoftLimit(!(await list.getWipLimit('soft')));
},
myLists() {
async myLists() {
// my lists
return _.uniq(
ReactiveCache.getLists(
{
boardId: { $in: Boards.userBoardIds(this.userId) },
archived: false,
},
{
fields: { title: 1 },
},
).map(list => list.title),
).sort();
const lists = await ReactiveCache.getLists(
{
boardId: { $in: Boards.userBoardIds(this.userId) },
archived: false,
},
{
fields: { title: 1 },
},
);
return _.uniq(lists.map(list => list.title)).sort();
},
updateListSort(listId, boardId, updateData) {
async updateListSort(listId, boardId, updateData) {
check(listId, String);
check(boardId, String);
check(updateData, Object);
const board = ReactiveCache.getBoard(boardId);
const board = await ReactiveCache.getBoard(boardId);
if (!board) {
throw new Meteor.Error('board-not-found', 'Board not found');
}
@ -516,7 +518,7 @@ Meteor.methods({
}
}
const list = ReactiveCache.getList(listId);
const list = await ReactiveCache.getList(listId);
if (!list) {
throw new Meteor.Error('list-not-found', 'List not found');
}
@ -529,13 +531,13 @@ Meteor.methods({
});
if (updateData.swimlaneId) {
const swimlane = ReactiveCache.getSwimlane(updateData.swimlaneId);
const swimlane = await ReactiveCache.getSwimlane(updateData.swimlaneId);
if (!swimlane || swimlane.boardId !== boardId) {
throw new Meteor.Error('invalid-swimlane', 'Invalid swimlane for this board');
}
}
Lists.update(
await Lists.updateAsync(
{ _id: listId, boardId },
{
$set: {
@ -583,14 +585,14 @@ Lists.after.insert((userId, doc) => {
}, 100);
});
Lists.before.remove((userId, doc) => {
const cards = ReactiveCache.getCards({ listId: doc._id });
Lists.before.remove(async (userId, doc) => {
const cards = await ReactiveCache.getCards({ listId: doc._id });
if (cards) {
cards.forEach(card => {
Cards.remove(card._id);
});
for (const card of cards) {
await Cards.removeAsync(card._id);
}
}
Activities.insert({
await Activities.insertAsync({
userId,
type: 'list',
activityType: 'removeList',
@ -722,12 +724,12 @@ if (Meteor.isServer) {
* @param {string} title the title of the List
* @return_type {_id: string}
*/
JsonRoutes.add('POST', '/api/boards/:boardId/lists', function(req, res) {
JsonRoutes.add('POST', '/api/boards/:boardId/lists', async function(req, res) {
try {
const paramBoardId = req.params.boardId;
Authentication.checkBoardWriteAccess(req.userId, paramBoardId);
const board = ReactiveCache.getBoard(paramBoardId);
const id = Lists.insert({
const board = await ReactiveCache.getBoard(paramBoardId);
const id = await Lists.insertAsync({
title: req.body.title,
boardId: paramBoardId,
sort: board.lists().length,
@ -763,7 +765,7 @@ if (Meteor.isServer) {
* @param {boolean} [collapsed] whether the list is collapsed
* @return_type {_id: string}
*/
JsonRoutes.add('PUT', '/api/boards/:boardId/lists/:listId', function(
JsonRoutes.add('PUT', '/api/boards/:boardId/lists/:listId', async function(
req,
res,
) {
@ -773,7 +775,7 @@ if (Meteor.isServer) {
let updated = false;
Authentication.checkBoardWriteAccess(req.userId, paramBoardId);
const list = ReactiveCache.getList({
const list = await ReactiveCache.getList({
_id: paramListId,
boardId: paramBoardId,
archived: false,

View file

@ -48,8 +48,8 @@ LockoutSettings.attachSchema(
);
LockoutSettings.allow({
update(userId) {
const user = ReactiveCache.getUser(userId);
async update(userId) {
const user = await ReactiveCache.getUser(userId);
return user && user.isAdmin;
},
});

View file

@ -87,8 +87,8 @@ Org.attachSchema(
if (Meteor.isServer) {
Org.allow({
insert(userId, doc) {
const user = ReactiveCache.getUser(userId) || ReactiveCache.getCurrentUser();
async insert(userId, doc) {
const user = await ReactiveCache.getUser(userId) || await ReactiveCache.getCurrentUser();
if (user?.isAdmin)
return true;
if (!user) {
@ -96,8 +96,8 @@ if (Meteor.isServer) {
}
return doc._id === userId;
},
update(userId, doc) {
const user = ReactiveCache.getUser(userId) || ReactiveCache.getCurrentUser();
async update(userId, doc) {
const user = await ReactiveCache.getUser(userId) || await ReactiveCache.getCurrentUser();
if (user?.isAdmin)
return true;
if (!user) {
@ -105,8 +105,8 @@ if (Meteor.isServer) {
}
return doc._id === userId;
},
remove(userId, doc) {
const user = ReactiveCache.getUser(userId) || ReactiveCache.getCurrentUser();
async remove(userId, doc) {
const user = await ReactiveCache.getUser(userId) || await ReactiveCache.getCurrentUser();
if (user?.isAdmin)
return true;
if (!user) {
@ -119,7 +119,7 @@ if (Meteor.isServer) {
Meteor.methods({
setCreateOrg(
async setCreateOrg(
orgDisplayName,
orgDesc,
orgShortName,
@ -127,7 +127,7 @@ if (Meteor.isServer) {
orgWebsite,
orgIsActive,
) {
if (ReactiveCache.getCurrentUser()?.isAdmin) {
if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
check(orgDisplayName, String);
check(orgDesc, String);
check(orgShortName, String);
@ -135,7 +135,7 @@ if (Meteor.isServer) {
check(orgWebsite, String);
check(orgIsActive, Boolean);
const nOrgNames = ReactiveCache.getOrgs({ orgShortName }).length;
const nOrgNames = (await ReactiveCache.getOrgs({ orgShortName })).length;
if (nOrgNames > 0) {
throw new Meteor.Error('orgname-already-taken');
} else {
@ -150,7 +150,7 @@ if (Meteor.isServer) {
}
}
},
setCreateOrgFromOidc(
async setCreateOrgFromOidc(
orgDisplayName,
orgDesc,
orgShortName,
@ -165,7 +165,7 @@ if (Meteor.isServer) {
check(orgWebsite, String);
check(orgIsActive, Boolean);
const nOrgNames = ReactiveCache.getOrgs({ orgShortName }).length;
const nOrgNames = (await ReactiveCache.getOrgs({ orgShortName })).length;
if (nOrgNames > 0) {
throw new Meteor.Error('orgname-already-taken');
} else {
@ -179,19 +179,19 @@ if (Meteor.isServer) {
});
}
},
setOrgDisplayName(org, orgDisplayName) {
if (ReactiveCache.getCurrentUser()?.isAdmin) {
async setOrgDisplayName(org, orgDisplayName) {
if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
check(org, Object);
check(orgDisplayName, String);
Org.update(org, {
$set: { orgDisplayName: orgDisplayName },
});
Meteor.call('setUsersOrgsOrgDisplayName', org._id, orgDisplayName);
await Meteor.callAsync('setUsersOrgsOrgDisplayName', org._id, orgDisplayName);
}
},
setOrgDesc(org, orgDesc) {
if (ReactiveCache.getCurrentUser()?.isAdmin) {
async setOrgDesc(org, orgDesc) {
if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
check(org, Object);
check(orgDesc, String);
Org.update(org, {
@ -200,8 +200,8 @@ if (Meteor.isServer) {
}
},
setOrgShortName(org, orgShortName) {
if (ReactiveCache.getCurrentUser()?.isAdmin) {
async setOrgShortName(org, orgShortName) {
if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
check(org, Object);
check(orgShortName, String);
Org.update(org, {
@ -210,8 +210,8 @@ if (Meteor.isServer) {
}
},
setAutoAddUsersWithDomainName(org, orgAutoAddUsersWithDomainName) {
if (ReactiveCache.getCurrentUser()?.isAdmin) {
async setAutoAddUsersWithDomainName(org, orgAutoAddUsersWithDomainName) {
if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
check(org, Object);
check(orgAutoAddUsersWithDomainName, String);
Org.update(org, {
@ -220,8 +220,8 @@ if (Meteor.isServer) {
}
},
setOrgIsActive(org, orgIsActive) {
if (ReactiveCache.getCurrentUser()?.isAdmin) {
async setOrgIsActive(org, orgIsActive) {
if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
check(org, Object);
check(orgIsActive, Boolean);
Org.update(org, {
@ -229,7 +229,7 @@ if (Meteor.isServer) {
});
}
},
setOrgAllFieldsFromOidc(
async setOrgAllFieldsFromOidc(
org,
orgDisplayName,
orgDesc,
@ -255,9 +255,9 @@ if (Meteor.isServer) {
orgIsActive: orgIsActive,
},
});
Meteor.call('setUsersOrgsOrgDisplayName', org._id, orgDisplayName);
await Meteor.callAsync('setUsersOrgsOrgDisplayName', org._id, orgDisplayName);
},
setOrgAllFields(
async setOrgAllFields(
org,
orgDisplayName,
orgDesc,
@ -266,7 +266,7 @@ if (Meteor.isServer) {
orgWebsite,
orgIsActive,
) {
if (ReactiveCache.getCurrentUser()?.isAdmin) {
if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
check(org, Object);
check(orgDisplayName, String);
check(orgDesc, String);
@ -284,7 +284,7 @@ if (Meteor.isServer) {
orgIsActive: orgIsActive,
},
});
Meteor.call('setUsersOrgsOrgDisplayName', org._id, orgDisplayName);
await Meteor.callAsync('setUsersOrgsOrgDisplayName', org._id, orgDisplayName);
}
},
});

View file

@ -54,32 +54,32 @@ Rules.helpers({
async rename(description) {
return await Rules.updateAsync(this._id, { $set: { description } });
},
getAction() {
return ReactiveCache.getAction(this.actionId);
async getAction() {
return await ReactiveCache.getAction(this.actionId);
},
getTrigger() {
return ReactiveCache.getTrigger(this.triggerId);
async getTrigger() {
return await ReactiveCache.getTrigger(this.triggerId);
},
board() {
return ReactiveCache.getBoard(this.boardId);
async board() {
return await ReactiveCache.getBoard(this.boardId);
},
trigger() {
return ReactiveCache.getTrigger(this.triggerId);
async trigger() {
return await ReactiveCache.getTrigger(this.triggerId);
},
action() {
return ReactiveCache.getAction(this.actionId);
async action() {
return await ReactiveCache.getAction(this.actionId);
},
});
Rules.allow({
insert(userId, doc) {
return allowIsBoardAdmin(userId, ReactiveCache.getBoard(doc.boardId));
async insert(userId, doc) {
return allowIsBoardAdmin(userId, await ReactiveCache.getBoard(doc.boardId));
},
update(userId, doc) {
return allowIsBoardAdmin(userId, ReactiveCache.getBoard(doc.boardId));
async update(userId, doc) {
return allowIsBoardAdmin(userId, await ReactiveCache.getBoard(doc.boardId));
},
remove(userId, doc) {
return allowIsBoardAdmin(userId, ReactiveCache.getBoard(doc.boardId));
async remove(userId, doc) {
return allowIsBoardAdmin(userId, await ReactiveCache.getBoard(doc.boardId));
},
});

View file

@ -640,8 +640,8 @@ class ExporterCardPDF {
}
canExport(user) {
const board = ReactiveCache.getBoard(this._boardId);
async canExport(user) {
const board = await ReactiveCache.getBoard(this._boardId);
return board && board.isVisibleBy(user);
}
}

View file

@ -31,7 +31,7 @@ class ExporterExcel {
this.userLanguage = userLanguage;
}
build(res) {
async build(res) {
const fs = Npm.require('fs');
const os = Npm.require('os');
const path = Npm.require('path');
@ -56,16 +56,16 @@ class ExporterExcel {
};
_.extend(
result,
ReactiveCache.getBoard(this._boardId, {
await ReactiveCache.getBoard(this._boardId, {
fields: {
stars: 0,
},
}),
);
result.lists = ReactiveCache.getLists(byBoard, noBoardId);
result.cards = ReactiveCache.getCards(byBoardNoLinked, noBoardId);
result.swimlanes = ReactiveCache.getSwimlanes(byBoard, noBoardId);
result.customFields = ReactiveCache.getCustomFields(
result.lists = await ReactiveCache.getLists(byBoard, noBoardId);
result.cards = await ReactiveCache.getCards(byBoardNoLinked, noBoardId);
result.swimlanes = await ReactiveCache.getSwimlanes(byBoard, noBoardId);
result.customFields = await ReactiveCache.getCustomFields(
{
boardIds: {
$in: [this.boardId],
@ -77,34 +77,34 @@ class ExporterExcel {
},
},
);
result.comments = ReactiveCache.getCardComments(byBoard, noBoardId);
result.activities = ReactiveCache.getActivities(byBoard, noBoardId);
result.rules = ReactiveCache.getRules(byBoard, noBoardId);
result.comments = await ReactiveCache.getCardComments(byBoard, noBoardId);
result.activities = await ReactiveCache.getActivities(byBoard, noBoardId);
result.rules = await ReactiveCache.getRules(byBoard, noBoardId);
result.checklists = [];
result.checklistItems = [];
result.subtaskItems = [];
result.triggers = [];
result.actions = [];
result.cards.forEach((card) => {
for (const card of result.cards) {
result.checklists.push(
...ReactiveCache.getChecklists({
...await ReactiveCache.getChecklists({
cardId: card._id,
}),
);
result.checklistItems.push(
...ReactiveCache.getChecklistItems({
...await ReactiveCache.getChecklistItems({
cardId: card._id,
}),
);
result.subtaskItems.push(
...ReactiveCache.getCards({
...await ReactiveCache.getCards({
parentId: card._id,
}),
);
});
result.rules.forEach((rule) => {
}
for (const rule of result.rules) {
result.triggers.push(
...ReactiveCache.getTriggers(
...await ReactiveCache.getTriggers(
{
_id: rule.triggerId,
},
@ -112,14 +112,14 @@ class ExporterExcel {
),
);
result.actions.push(
...ReactiveCache.getActions(
...await ReactiveCache.getActions(
{
_id: rule.actionId,
},
noBoardId,
),
);
});
}
// we also have to export some user data - as the other elements only
// include id but we have to be careful:
@ -169,7 +169,7 @@ class ExporterExcel {
'profile.avatarUrl': 1,
},
};
result.users = ReactiveCache.getUsers(byUserIds, userFields)
result.users = (await ReactiveCache.getUsers(byUserIds, userFields))
.map((user) => {
// user avatar is stored as a relative url, we export absolute
if ((user.profile || {}).avatarUrl) {
@ -905,8 +905,8 @@ class ExporterExcel {
workbook.xlsx.write(res).then(function () {});
}
canExport(user) {
const board = ReactiveCache.getBoard(this._boardId);
async canExport(user) {
const board = await ReactiveCache.getBoard(this._boardId);
return board && board.isVisibleBy(user);
}
}

View file

@ -57,12 +57,12 @@ const getBoardTitleWithMostActivities = (dateWithXdaysAgo, nbLimit) => {
);
};
const getBoards = (boardIds) => {
const ret = ReactiveCache.getBoards({ _id: { $in: boardIds } });
const getBoards = async (boardIds) => {
const ret = await ReactiveCache.getBoards({ _id: { $in: boardIds } });
return ret;
};
Meteor.startup(() => {
WebApp.connectHandlers.use('/metrics', (req, res, next) => {
WebApp.connectHandlers.use('/metrics', async (req, res, next) => {
try {
const ipAddress =
req.headers['x-forwarded-for'] || req.socket.remoteAddress;
@ -95,7 +95,7 @@ Meteor.startup(() => {
metricsRes += '# Number of registered users\n';
// Get number of registered user
resCount = ReactiveCache.getUsers({}).length; // KPI 2
resCount = (await ReactiveCache.getUsers({})).length; // KPI 2
metricsRes += 'wekan_registeredUsers ' + resCount + '\n';
resCount = 0;
@ -103,7 +103,7 @@ Meteor.startup(() => {
metricsRes += '# Number of registered boards\n';
// Get number of registered boards
resCount = ReactiveCache.getBoards({ archived: false, type: 'board' }).length; // KPI 3
resCount = (await ReactiveCache.getBoards({ archived: false, type: 'board' })).length; // KPI 3
metricsRes += 'wekan_registeredboards ' + resCount + '\n';
resCount = 0;
@ -112,8 +112,8 @@ Meteor.startup(() => {
// Get number of registered boards by registered users
resCount =
ReactiveCache.getBoards({ archived: false, type: 'board' }).length /
ReactiveCache.getUsers({}).length; // KPI 4
(await ReactiveCache.getBoards({ archived: false, type: 'board' })).length /
(await ReactiveCache.getUsers({})).length; // KPI 4
metricsRes +=
'wekan_registeredboardsBysRegisteredUsers ' + resCount + '\n';
resCount = 0;
@ -122,11 +122,11 @@ Meteor.startup(() => {
metricsRes += '# Number of registered boards\n';
// Get board numbers with only one member
resCount = ReactiveCache.getBoards({
resCount = (await ReactiveCache.getBoards({
archived: false,
type: 'board',
members: { $size: 1 },
}).length; // KPI 5
})).length; // KPI 5
metricsRes +=
'wekan_registeredboardsWithOnlyOneMember ' + resCount + '\n';
resCount = 0;
@ -144,9 +144,9 @@ Meteor.startup(() => {
let dateWithXdaysAgo = new Date(
new Date() - xdays * 24 * 60 * 60 * 1000,
);
resCount = ReactiveCache.getUsers({
resCount = (await ReactiveCache.getUsers({
lastConnectionDate: { $gte: dateWithXdaysAgo },
}).length; // KPI 5
})).length; // KPI 5
metricsRes +=
'wekan_usersWithLastConnectionDated5DaysAgo ' + resCount + '\n';
resCount = 0;
@ -157,9 +157,9 @@ Meteor.startup(() => {
// Get number of users with last connection dated 10 days ago
xdays = 10;
dateWithXdaysAgo = new Date(new Date() - xdays * 24 * 60 * 60 * 1000);
resCount = ReactiveCache.getUsers({
resCount = (await ReactiveCache.getUsers({
lastConnectionDate: { $gte: dateWithXdaysAgo },
}).length; // KPI 5
})).length; // KPI 5
metricsRes +=
'wekan_usersWithLastConnectionDated10DaysAgo ' + resCount + '\n';
resCount = 0;
@ -170,9 +170,9 @@ Meteor.startup(() => {
// Get number of users with last connection dated 20 days ago
xdays = 20;
dateWithXdaysAgo = new Date(new Date() - xdays * 24 * 60 * 60 * 1000);
resCount = ReactiveCache.getUsers({
resCount = (await ReactiveCache.getUsers({
lastConnectionDate: { $gte: dateWithXdaysAgo },
}).length; // KPI 5
})).length; // KPI 5
metricsRes +=
'wekan_usersWithLastConnectionDated20DaysAgo ' + resCount + '\n';
resCount = 0;
@ -183,9 +183,9 @@ Meteor.startup(() => {
// Get number of users with last connection dated 20 days ago
xdays = 30;
dateWithXdaysAgo = new Date(new Date() - xdays * 24 * 60 * 60 * 1000);
resCount = ReactiveCache.getUsers({
resCount = (await ReactiveCache.getUsers({
lastConnectionDate: { $gte: dateWithXdaysAgo },
}).length; // KPI 5
})).length; // KPI 5
metricsRes +=
'wekan_usersWithLastConnectionDated30DaysAgo ' + resCount + '\n';
resCount = 0;

View file

@ -195,8 +195,8 @@ Settings.helpers({
},
});
Settings.allow({
update(userId) {
const user = ReactiveCache.getUser(userId);
async update(userId) {
const user = await ReactiveCache.getUser(userId);
return user && user.isAdmin;
},
});
@ -204,7 +204,7 @@ Settings.allow({
if (Meteor.isServer) {
Meteor.startup(async () => {
await Settings._collection.createIndexAsync({ modifiedAt: -1 });
const setting = ReactiveCache.getCurrentSetting();
const setting = await ReactiveCache.getCurrentSetting();
if (!setting) {
const now = new Date();
const domain = process.env.ROOT_URL.match(
@ -230,7 +230,7 @@ if (Meteor.isServer) {
}
if (isSandstorm) {
// At Sandstorm, Admin Panel has SMTP settings
const newSetting = ReactiveCache.getCurrentSetting();
const newSetting = await ReactiveCache.getCurrentSetting();
if (!process.env.MAIL_URL && newSetting.mailUrl())
process.env.MAIL_URL = newSetting.mailUrl();
Accounts.emailTemplates.from = process.env.MAIL_FROM
@ -284,15 +284,16 @@ if (Meteor.isServer) {
return config;
}
function sendInvitationEmail(_id) {
const icode = ReactiveCache.getInvitationCode(_id);
const author = ReactiveCache.getCurrentUser();
async function sendInvitationEmail(_id) {
const icode = await ReactiveCache.getInvitationCode(_id);
const author = await ReactiveCache.getCurrentUser();
try {
const fullName = ReactiveCache.getUser(icode.authorId)?.profile?.fullname || "";
const authorUser = await ReactiveCache.getUser(icode.authorId);
const fullName = authorUser?.profile?.fullname || "";
const params = {
email: icode.email,
inviter: fullName != "" ? fullName + " (" + ReactiveCache.getUser(icode.authorId).username + " )" : ReactiveCache.getUser(icode.authorId).username,
inviter: fullName != "" ? fullName + " (" + authorUser.username + " )" : authorUser.username,
user: icode.email.split('@')[0],
icode: icode.code,
url: FlowRouter.url('sign-up'),
@ -323,8 +324,8 @@ if (Meteor.isServer) {
}
}
function isNonAdminAllowedToSendMail(currentUser){
const currSett = ReactiveCache.getCurrentSetting();
async function isNonAdminAllowedToSendMail(currentUser){
const currSett = await ReactiveCache.getCurrentSetting();
let isAllowed = false;
if(currSett && currSett != undefined && currSett.disableRegistration && currSett.mailDomainName !== undefined && currSett.mailDomainName != ""){
for(let i = 0; i < currentUser.emails.length; i++) {
@ -361,20 +362,20 @@ if (Meteor.isServer) {
}
Meteor.methods({
sendInvitation(emails, boards) {
async sendInvitation(emails, boards) {
let rc = 0;
check(emails, [String]);
check(boards, [String]);
const user = ReactiveCache.getCurrentUser();
if (!user.isAdmin && !isNonAdminAllowedToSendMail(user)) {
const user = await ReactiveCache.getCurrentUser();
if (!user.isAdmin && !(await isNonAdminAllowedToSendMail(user))) {
rc = -1;
throw new Meteor.Error('not-allowed');
}
emails.forEach(email => {
for (const email of emails) {
if (email && SimpleSchema.RegEx.Email.test(email)) {
// Checks if the email is already link to an account.
const userExist = ReactiveCache.getUser({ email });
const userExist = await ReactiveCache.getUser({ email });
if (userExist) {
rc = -1;
throw new Meteor.Error(
@ -383,12 +384,12 @@ if (Meteor.isServer) {
);
}
// Checks if the email is already link to an invitation.
const invitation = ReactiveCache.getInvitationCode({ email });
const invitation = await ReactiveCache.getInvitationCode({ email });
if (invitation) {
InvitationCodes.update(invitation, {
$set: { boardsToBeInvited: boards },
});
sendInvitationEmail(invitation._id);
await sendInvitationEmail(invitation._id);
} else {
const code = getRandomNum(100000, 999999);
InvitationCodes.insert(
@ -399,9 +400,9 @@ if (Meteor.isServer) {
createdAt: new Date(),
authorId: Meteor.userId(),
},
function(err, _id) {
async function(err, _id) {
if (!err && _id) {
sendInvitationEmail(_id);
await sendInvitationEmail(_id);
} else {
rc = -1;
throw new Meteor.Error(
@ -413,15 +414,15 @@ if (Meteor.isServer) {
);
}
}
});
}
return rc;
},
sendSMTPTestEmail() {
async sendSMTPTestEmail() {
if (!Meteor.userId()) {
throw new Meteor.Error('invalid-user');
}
const user = ReactiveCache.getCurrentUser();
const user = await ReactiveCache.getCurrentUser();
if (!user.emails || !user.emails[0] || !user.emails[0].address) {
throw new Meteor.Error('email-invalid');
}
@ -471,8 +472,8 @@ if (Meteor.isServer) {
};
},
getCustomUI() {
const setting = ReactiveCache.getCurrentSetting();
async getCustomUI() {
const setting = await ReactiveCache.getCurrentSetting();
if (!setting.productName) {
return {
productName: '',
@ -484,8 +485,8 @@ if (Meteor.isServer) {
}
},
isDisableRegistration() {
const setting = ReactiveCache.getCurrentSetting();
async isDisableRegistration() {
const setting = await ReactiveCache.getCurrentSetting();
if (setting.disableRegistration === true) {
return true;
} else {
@ -493,8 +494,8 @@ if (Meteor.isServer) {
}
},
isDisableForgotPassword() {
const setting = ReactiveCache.getCurrentSetting();
async isDisableForgotPassword() {
const setting = await ReactiveCache.getCurrentSetting();
if (setting.disableForgotPassword === true) {
return true;
} else {

View file

@ -131,28 +131,28 @@ Swimlanes.attachSchema(
);
Swimlanes.allow({
insert(userId, doc) {
async insert(userId, doc) {
// ReadOnly and CommentOnly users cannot create swimlanes
return allowIsBoardMemberWithWriteAccess(userId, ReactiveCache.getBoard(doc.boardId));
return allowIsBoardMemberWithWriteAccess(userId, await ReactiveCache.getBoard(doc.boardId));
},
update(userId, doc) {
async update(userId, doc) {
// ReadOnly and CommentOnly users cannot edit swimlanes
return allowIsBoardMemberWithWriteAccess(userId, ReactiveCache.getBoard(doc.boardId));
return allowIsBoardMemberWithWriteAccess(userId, await ReactiveCache.getBoard(doc.boardId));
},
remove(userId, doc) {
async remove(userId, doc) {
// ReadOnly and CommentOnly users cannot delete swimlanes
return allowIsBoardMemberWithWriteAccess(userId, ReactiveCache.getBoard(doc.boardId));
return allowIsBoardMemberWithWriteAccess(userId, await ReactiveCache.getBoard(doc.boardId));
},
fetch: ['boardId'],
});
Swimlanes.helpers({
copy(boardId) {
async copy(boardId) {
const oldId = this._id;
const oldBoardId = this.boardId;
this.boardId = boardId;
delete this._id;
const _id = Swimlanes.insert(this);
const _id = await Swimlanes.insertAsync(this);
const query = {
swimlaneId: { $in: [oldId, ''] },
@ -163,17 +163,18 @@ Swimlanes.helpers({
}
// Copy all lists in swimlane
ReactiveCache.getLists(query).forEach(list => {
const lists = await ReactiveCache.getLists(query);
for (const list of lists) {
list.type = 'list';
list.swimlaneId = oldId;
list.boardId = boardId;
list.copy(boardId, _id);
});
await list.copy(boardId, _id);
}
},
async move(toBoardId) {
for (const list of this.lists()) {
const toList = ReactiveCache.getList({
for (const list of await this.lists()) {
const toList = await ReactiveCache.getList({
boardId: toBoardId,
title: list.title,
archived: false,
@ -193,7 +194,7 @@ Swimlanes.helpers({
});
}
const cards = ReactiveCache.getCards({
const cards = await ReactiveCache.getCards({
listId: list._id,
swimlaneId: this._id,
});
@ -209,11 +210,11 @@ Swimlanes.helpers({
});
// make sure there is a default swimlane
this.board().getDefaultSwimline();
(await this.board()).getDefaultSwimline();
},
cards() {
const ret = ReactiveCache.getCards(
async cards() {
const ret = await ReactiveCache.getCards(
Filter.mongoSelector({
swimlaneId: this._id,
archived: false,
@ -223,12 +224,12 @@ Swimlanes.helpers({
return ret;
},
lists() {
return this.draggableLists();
async lists() {
return await this.draggableLists();
},
newestLists() {
async newestLists() {
// Revert to shared lists across swimlanes: filter by board only
return ReactiveCache.getLists(
return await ReactiveCache.getLists(
{
boardId: this.boardId,
archived: false,
@ -236,9 +237,9 @@ Swimlanes.helpers({
{ sort: { modifiedAt: -1 } },
);
},
draggableLists() {
async draggableLists() {
// Revert to shared lists across swimlanes: filter by board only
return ReactiveCache.getLists(
return await ReactiveCache.getLists(
{
boardId: this.boardId,
//archived: false,
@ -247,10 +248,10 @@ Swimlanes.helpers({
);
},
myLists() {
async myLists() {
// Return per-swimlane lists: provide lists specific to this swimlane
return ReactiveCache.getLists(
{
return await ReactiveCache.getLists(
{
boardId: this.boardId,
swimlaneId: this._id,
archived: false
@ -259,14 +260,14 @@ Swimlanes.helpers({
);
},
allCards() {
const ret = ReactiveCache.getCards({ swimlaneId: this._id });
async allCards() {
const ret = await ReactiveCache.getCards({ swimlaneId: this._id });
return ret;
},
isCollapsed() {
async isCollapsed() {
if (Meteor.isClient) {
const user = ReactiveCache.getCurrentUser();
const user = await ReactiveCache.getCurrentUser();
if (user && user.getCollapsedSwimlaneFromStorage) {
const stored = user.getCollapsedSwimlaneFromStorage(this.boardId, this._id);
if (typeof stored === 'boolean') {
@ -283,8 +284,8 @@ Swimlanes.helpers({
return this.collapsed === true;
},
board() {
return ReactiveCache.getBoard(this.boardId);
async board() {
return await ReactiveCache.getBoard(this.boardId);
},
colorClass() {
@ -300,18 +301,18 @@ Swimlanes.helpers({
return this.type === 'template-container';
},
isListTemplatesSwimlane() {
const user = ReactiveCache.getCurrentUser();
async isListTemplatesSwimlane() {
const user = await ReactiveCache.getCurrentUser();
return (user.profile || {}).listTemplatesSwimlaneId === this._id;
},
isCardTemplatesSwimlane() {
const user = ReactiveCache.getCurrentUser();
async isCardTemplatesSwimlane() {
const user = await ReactiveCache.getCurrentUser();
return (user.profile || {}).cardTemplatesSwimlaneId === this._id;
},
isBoardTemplatesSwimlane() {
const user = ReactiveCache.getCurrentUser();
async isBoardTemplatesSwimlane() {
const user = await ReactiveCache.getCurrentUser();
return (user.profile || {}).boardTemplatesSwimlaneId === this._id;
},
@ -328,7 +329,7 @@ Swimlanes.helpers({
async archive() {
if (this.isTemplateSwimlane()) {
for (const list of this.myLists()) {
for (const list of await this.myLists()) {
await list.archive();
}
}
@ -337,7 +338,7 @@ Swimlanes.helpers({
async restore() {
if (this.isTemplateSwimlane()) {
for (const list of this.myLists()) {
for (const list of await this.myLists()) {
await list.restore();
}
}
@ -349,23 +350,25 @@ Swimlanes.helpers({
},
});
Swimlanes.userArchivedSwimlanes = userId => {
return ReactiveCache.getSwimlanes({
boardId: { $in: Boards.userBoardIds(userId, null) },
Swimlanes.userArchivedSwimlanes = async userId => {
return await ReactiveCache.getSwimlanes({
boardId: { $in: await Boards.userBoardIds(userId, null) },
archived: true,
})
};
Swimlanes.userArchivedSwimlaneIds = () => {
return Swimlanes.userArchivedSwimlanes().map(swim => { return swim._id; });
Swimlanes.userArchivedSwimlaneIds = async () => {
const swimlanes = await Swimlanes.userArchivedSwimlanes();
return swimlanes.map(swim => { return swim._id; });
};
Swimlanes.archivedSwimlanes = () => {
return ReactiveCache.getSwimlanes({ archived: true });
Swimlanes.archivedSwimlanes = async () => {
return await ReactiveCache.getSwimlanes({ archived: true });
};
Swimlanes.archivedSwimlaneIds = () => {
return Swimlanes.archivedSwimlanes().map(swim => {
Swimlanes.archivedSwimlaneIds = async () => {
const swimlanes = await Swimlanes.archivedSwimlanes();
return swimlanes.map(swim => {
return swim._id;
});
};
@ -396,8 +399,8 @@ if (Meteor.isServer) {
}, 100);
});
Swimlanes.before.remove(function(userId, doc) {
const lists = ReactiveCache.getLists(
Swimlanes.before.remove(async function(userId, doc) {
const lists = await ReactiveCache.getLists(
{
boardId: doc.boardId,
swimlaneId: { $in: [doc._id, ''] },
@ -407,14 +410,14 @@ if (Meteor.isServer) {
);
if (lists.length < 2) {
lists.forEach(list => {
list.remove();
});
for (const list of lists) {
await list.remove();
}
} else {
Cards.remove({ swimlaneId: doc._id });
await Cards.removeAsync({ swimlaneId: doc._id });
}
Activities.insert({
await Activities.insertAsync({
userId,
type: 'swimlane',
activityType: 'removeSwimlane',
@ -473,14 +476,15 @@ if (Meteor.isServer) {
* @return_type [{_id: string,
* title: string}]
*/
JsonRoutes.add('GET', '/api/boards/:boardId/swimlanes', function(req, res) {
JsonRoutes.add('GET', '/api/boards/:boardId/swimlanes', async function(req, res) {
try {
const paramBoardId = req.params.boardId;
Authentication.checkBoardAccess(req.userId, paramBoardId);
const swimlanes = await ReactiveCache.getSwimlanes({ boardId: paramBoardId, archived: false });
JsonRoutes.sendResult(res, {
code: 200,
data: ReactiveCache.getSwimlanes({ boardId: paramBoardId, archived: false }).map(
data: swimlanes.map(
function(doc) {
return {
_id: doc._id,
@ -506,7 +510,7 @@ if (Meteor.isServer) {
* @param {string} swimlaneId the ID of the swimlane
* @return_type Swimlanes
*/
JsonRoutes.add('GET', '/api/boards/:boardId/swimlanes/:swimlaneId', function(
JsonRoutes.add('GET', '/api/boards/:boardId/swimlanes/:swimlaneId', async function(
req,
res,
) {
@ -517,7 +521,7 @@ if (Meteor.isServer) {
JsonRoutes.sendResult(res, {
code: 200,
data: ReactiveCache.getSwimlane({
data: await ReactiveCache.getSwimlane({
_id: paramSwimlaneId,
boardId: paramBoardId,
archived: false,
@ -540,13 +544,13 @@ if (Meteor.isServer) {
* @param {string} title the new title of the swimlane
* @return_type {_id: string}
*/
JsonRoutes.add('POST', '/api/boards/:boardId/swimlanes', function(req, res) {
JsonRoutes.add('POST', '/api/boards/:boardId/swimlanes', async function(req, res) {
try {
const paramBoardId = req.params.boardId;
Authentication.checkBoardWriteAccess(req.userId, paramBoardId);
const board = ReactiveCache.getBoard(paramBoardId);
const id = Swimlanes.insert({
const board = await ReactiveCache.getBoard(paramBoardId);
const id = await Swimlanes.insertAsync({
title: req.body.title,
boardId: paramBoardId,
sort: board.swimlanes().length,
@ -575,13 +579,13 @@ if (Meteor.isServer) {
* @param {string} title the new title of the swimlane
* @return_type {_id: string}
*/
JsonRoutes.add('PUT', '/api/boards/:boardId/swimlanes/:swimlaneId', function(req, res) {
JsonRoutes.add('PUT', '/api/boards/:boardId/swimlanes/:swimlaneId', async function(req, res) {
try {
const paramBoardId = req.params.boardId;
const paramSwimlaneId = req.params.swimlaneId;
Authentication.checkBoardWriteAccess(req.userId, paramBoardId);
const board = ReactiveCache.getBoard(paramBoardId);
const swimlane = ReactiveCache.getSwimlane({
const board = await ReactiveCache.getBoard(paramBoardId);
const swimlane = await ReactiveCache.getSwimlane({
_id: paramSwimlaneId,
boardId: paramBoardId,
});

View file

@ -45,8 +45,8 @@ TableVisibilityModeSettings.attachSchema(
);
TableVisibilityModeSettings.allow({
update(userId) {
const user = ReactiveCache.getUser(userId);
async update(userId) {
const user = await ReactiveCache.getUser(userId);
return user && user.isAdmin;
},
});

View file

@ -78,8 +78,8 @@ Team.attachSchema(
if (Meteor.isServer) {
Team.allow({
insert(userId, doc) {
const user = ReactiveCache.getUser(userId) || ReactiveCache.getCurrentUser();
async insert(userId, doc) {
const user = await ReactiveCache.getUser(userId) || await ReactiveCache.getCurrentUser();
if (user?.isAdmin)
return true;
if (!user) {
@ -87,8 +87,8 @@ if (Meteor.isServer) {
}
return doc._id === userId;
},
update(userId, doc) {
const user = ReactiveCache.getUser(userId) || ReactiveCache.getCurrentUser();
async update(userId, doc) {
const user = await ReactiveCache.getUser(userId) || await ReactiveCache.getCurrentUser();
if (user?.isAdmin)
return true;
if (!user) {
@ -96,8 +96,8 @@ if (Meteor.isServer) {
}
return doc._id === userId;
},
remove(userId, doc) {
const user = ReactiveCache.getUser(userId) || ReactiveCache.getCurrentUser();
async remove(userId, doc) {
const user = await ReactiveCache.getUser(userId) || await ReactiveCache.getCurrentUser();
if (user?.isAdmin)
return true;
if (!user) {
@ -109,21 +109,21 @@ if (Meteor.isServer) {
});
Meteor.methods({
setCreateTeam(
async setCreateTeam(
teamDisplayName,
teamDesc,
teamShortName,
teamWebsite,
teamIsActive,
) {
if (ReactiveCache.getCurrentUser()?.isAdmin) {
if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
check(teamDisplayName, String);
check(teamDesc, String);
check(teamShortName, String);
check(teamWebsite, String);
check(teamIsActive, Boolean);
const nTeamNames = ReactiveCache.getTeams({ teamShortName }).length;
const nTeamNames = (await ReactiveCache.getTeams({ teamShortName })).length;
if (nTeamNames > 0) {
throw new Meteor.Error('teamname-already-taken');
} else {
@ -137,7 +137,7 @@ if (Meteor.isServer) {
}
}
},
setCreateTeamFromOidc(
async setCreateTeamFromOidc(
teamDisplayName,
teamDesc,
teamShortName,
@ -149,7 +149,7 @@ if (Meteor.isServer) {
check(teamShortName, String);
check(teamWebsite, String);
check(teamIsActive, Boolean);
const nTeamNames = ReactiveCache.getTeams({ teamShortName }).length;
const nTeamNames = (await ReactiveCache.getTeams({ teamShortName })).length;
if (nTeamNames > 0) {
throw new Meteor.Error('teamname-already-taken');
} else {
@ -162,19 +162,19 @@ if (Meteor.isServer) {
});
}
},
setTeamDisplayName(team, teamDisplayName) {
if (ReactiveCache.getCurrentUser()?.isAdmin) {
async setTeamDisplayName(team, teamDisplayName) {
if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
check(team, Object);
check(teamDisplayName, String);
Team.update(team, {
$set: { teamDisplayName: teamDisplayName },
});
Meteor.call('setUsersTeamsTeamDisplayName', team._id, teamDisplayName);
await Meteor.callAsync('setUsersTeamsTeamDisplayName', team._id, teamDisplayName);
}
},
setTeamDesc(team, teamDesc) {
if (ReactiveCache.getCurrentUser()?.isAdmin) {
async setTeamDesc(team, teamDesc) {
if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
check(team, Object);
check(teamDesc, String);
Team.update(team, {
@ -183,8 +183,8 @@ if (Meteor.isServer) {
}
},
setTeamShortName(team, teamShortName) {
if (ReactiveCache.getCurrentUser()?.isAdmin) {
async setTeamShortName(team, teamShortName) {
if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
check(team, Object);
check(teamShortName, String);
Team.update(team, {
@ -193,8 +193,8 @@ if (Meteor.isServer) {
}
},
setTeamIsActive(team, teamIsActive) {
if (ReactiveCache.getCurrentUser()?.isAdmin) {
async setTeamIsActive(team, teamIsActive) {
if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
check(team, Object);
check(teamIsActive, Boolean);
Team.update(team, {
@ -202,7 +202,7 @@ if (Meteor.isServer) {
});
}
},
setTeamAllFieldsFromOidc(
async setTeamAllFieldsFromOidc(
team,
teamDisplayName,
teamDesc,
@ -225,9 +225,9 @@ if (Meteor.isServer) {
teamIsActive: teamIsActive,
},
});
Meteor.call('setUsersTeamsTeamDisplayName', team._id, teamDisplayName);
await Meteor.callAsync('setUsersTeamsTeamDisplayName', team._id, teamDisplayName);
},
setTeamAllFields(
async setTeamAllFields(
team,
teamDisplayName,
teamDesc,
@ -235,7 +235,7 @@ if (Meteor.isServer) {
teamWebsite,
teamIsActive,
) {
if (ReactiveCache.getCurrentUser()?.isAdmin) {
if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
check(team, Object);
check(teamDisplayName, String);
check(teamDesc, String);
@ -251,7 +251,7 @@ if (Meteor.isServer) {
teamIsActive: teamIsActive,
},
});
Meteor.call('setUsersTeamsTeamDisplayName', team._id, teamDisplayName);
await Meteor.callAsync('setUsersTeamsTeamDisplayName', team._id, teamDisplayName);
}
},
});

View file

@ -58,8 +58,8 @@ Translation.attachSchema(
if (Meteor.isServer) {
Translation.allow({
insert(userId, doc) {
const user = ReactiveCache.getUser(userId) || ReactiveCache.getCurrentUser();
async insert(userId, doc) {
const user = await ReactiveCache.getUser(userId) || await ReactiveCache.getCurrentUser();
if (user?.isAdmin)
return true;
if (!user) {
@ -67,8 +67,8 @@ if (Meteor.isServer) {
}
return doc._id === userId;
},
update(userId, doc) {
const user = ReactiveCache.getUser(userId) || ReactiveCache.getCurrentUser();
async update(userId, doc) {
const user = await ReactiveCache.getUser(userId) || await ReactiveCache.getCurrentUser();
if (user?.isAdmin)
return true;
if (!user) {
@ -76,8 +76,8 @@ if (Meteor.isServer) {
}
return doc._id === userId;
},
remove(userId, doc) {
const user = ReactiveCache.getUser(userId) || ReactiveCache.getCurrentUser();
async remove(userId, doc) {
const user = await ReactiveCache.getUser(userId) || await ReactiveCache.getCurrentUser();
if (user?.isAdmin)
return true;
if (!user) {
@ -89,7 +89,7 @@ if (Meteor.isServer) {
});
Meteor.methods({
setCreateTranslation(
async setCreateTranslation(
language,
text,
translationText,
@ -98,11 +98,11 @@ if (Meteor.isServer) {
check(text, String);
check(translationText, String);
if (!ReactiveCache.getCurrentUser()?.isAdmin) {
if (!(await ReactiveCache.getCurrentUser())?.isAdmin) {
throw new Meteor.Error('not-authorized');
}
const nTexts = ReactiveCache.getTranslations({ language, text }).length;
const nTexts = (await ReactiveCache.getTranslations({ language, text })).length;
if (nTexts > 0) {
throw new Meteor.Error('text-already-taken');
} else {
@ -113,11 +113,11 @@ if (Meteor.isServer) {
});
}
},
setTranslationText(translation, translationText) {
async setTranslationText(translation, translationText) {
check(translation, Object);
check(translationText, String);
if (!ReactiveCache.getCurrentUser()?.isAdmin) {
if (!(await ReactiveCache.getCurrentUser())?.isAdmin) {
throw new Meteor.Error('not-authorized');
}
@ -125,10 +125,10 @@ if (Meteor.isServer) {
$set: { translationText: translationText },
});
},
deleteTranslation(translationId) {
async deleteTranslation(translationId) {
check(translationId, String);
if (!ReactiveCache.getCurrentUser()?.isAdmin) {
if (!(await ReactiveCache.getCurrentUser())?.isAdmin) {
throw new Meteor.Error('not-authorized');
}

View file

@ -775,7 +775,7 @@ export class TrelloCreator {
Meteor.settings.public &&
Meteor.settings.public.sandstorm;
if (isSandstorm && currentBoardId) {
const currentBoard = ReactiveCache.getBoard(currentBoardId);
const currentBoard = await ReactiveCache.getBoard(currentBoardId);
await currentBoard.archive();
}
this.parseActions(board.actions);

View file

@ -14,14 +14,14 @@ Triggers.before.update((userId, doc, fieldNames, modifier) => {
});
Triggers.allow({
insert(userId, doc) {
return allowIsBoardAdmin(userId, ReactiveCache.getBoard(doc.boardId));
async insert(userId, doc) {
return allowIsBoardAdmin(userId, await ReactiveCache.getBoard(doc.boardId));
},
update(userId, doc) {
return allowIsBoardAdmin(userId, ReactiveCache.getBoard(doc.boardId));
async update(userId, doc) {
return allowIsBoardAdmin(userId, await ReactiveCache.getBoard(doc.boardId));
},
remove(userId, doc) {
return allowIsBoardAdmin(userId, ReactiveCache.getBoard(doc.boardId));
async remove(userId, doc) {
return allowIsBoardAdmin(userId, await ReactiveCache.getBoard(doc.boardId));
},
});
@ -36,20 +36,20 @@ Triggers.helpers({
return this.desc;
},
getRule() {
return ReactiveCache.getRule({ triggerId: this._id });
async getRule() {
return await ReactiveCache.getRule({ triggerId: this._id });
},
fromList() {
return ReactiveCache.getList(this.fromId);
async fromList() {
return await ReactiveCache.getList(this.fromId);
},
toList() {
return ReactiveCache.getList(this.toId);
async toList() {
return await ReactiveCache.getList(this.toId);
},
findList(title) {
return ReactiveCache.getList({
async findList(title) {
return await ReactiveCache.getList({
title,
});
},

View file

@ -174,17 +174,17 @@ UserPositionHistory.helpers({
/**
* Can this change be undone?
*/
canUndo() {
async canUndo() {
// Can undo if the entity still exists
switch (this.entityType) {
case 'card':
return !!ReactiveCache.getCard(this.entityId);
return !!(await ReactiveCache.getCard(this.entityId));
case 'list':
return !!ReactiveCache.getList(this.entityId);
return !!(await ReactiveCache.getList(this.entityId));
case 'swimlane':
return !!ReactiveCache.getSwimlane(this.entityId);
return !!(await ReactiveCache.getSwimlane(this.entityId));
case 'checklist':
return !!ReactiveCache.getChecklist(this.entityId);
return !!(await ReactiveCache.getChecklist(this.entityId));
case 'checklistItem':
return !!ChecklistItems.findOne(this.entityId);
default:
@ -195,23 +195,23 @@ UserPositionHistory.helpers({
/**
* Undo this change
*/
undo() {
if (!this.canUndo()) {
async undo() {
if (!(await this.canUndo())) {
throw new Meteor.Error('cannot-undo', 'Entity no longer exists');
}
const userId = this.userId;
switch (this.entityType) {
case 'card': {
const card = ReactiveCache.getCard(this.entityId);
const card = await ReactiveCache.getCard(this.entityId);
if (card) {
// Restore previous position
const boardId = this.previousBoardId || card.boardId;
const swimlaneId = this.previousSwimlaneId || card.swimlaneId;
const listId = this.previousListId || card.listId;
const sort = this.previousSort !== undefined ? this.previousSort : card.sort;
Cards.update(card._id, {
$set: {
boardId,
@ -224,11 +224,11 @@ UserPositionHistory.helpers({
break;
}
case 'list': {
const list = ReactiveCache.getList(this.entityId);
const list = await ReactiveCache.getList(this.entityId);
if (list) {
const sort = this.previousSort !== undefined ? this.previousSort : list.sort;
const swimlaneId = this.previousSwimlaneId || list.swimlaneId;
Lists.update(list._id, {
$set: {
sort,
@ -239,10 +239,10 @@ UserPositionHistory.helpers({
break;
}
case 'swimlane': {
const swimlane = ReactiveCache.getSwimlane(this.entityId);
const swimlane = await ReactiveCache.getSwimlane(this.entityId);
if (swimlane) {
const sort = this.previousSort !== undefined ? this.previousSort : swimlane.sort;
Swimlanes.update(swimlane._id, {
$set: {
sort,
@ -252,10 +252,10 @@ UserPositionHistory.helpers({
break;
}
case 'checklist': {
const checklist = ReactiveCache.getChecklist(this.entityId);
const checklist = await ReactiveCache.getChecklist(this.entityId);
if (checklist) {
const sort = this.previousSort !== undefined ? this.previousSort : checklist.sort;
Checklists.update(checklist._id, {
$set: {
sort,
@ -270,7 +270,7 @@ UserPositionHistory.helpers({
if (item) {
const sort = this.previousSort !== undefined ? this.previousSort : item.sort;
const checklistId = this.previousState?.checklistId || item.checklistId;
ChecklistItems.update(item._id, {
$set: {
sort,
@ -411,19 +411,19 @@ Meteor.methods({
});
},
'userPositionHistory.undo'(historyId) {
async 'userPositionHistory.undo'(historyId) {
check(historyId, String);
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
const history = UserPositionHistory.findOne({ _id: historyId, userId: this.userId });
if (!history) {
throw new Meteor.Error('not-found', 'History entry not found');
}
return history.undo();
return await history.undo();
},
'userPositionHistory.getRecent'(boardId, limit = 50) {
@ -453,23 +453,23 @@ Meteor.methods({
).fetch();
},
'userPositionHistory.restoreToCheckpoint'(checkpointId) {
async 'userPositionHistory.restoreToCheckpoint'(checkpointId) {
check(checkpointId, String);
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
const checkpoint = UserPositionHistory.findOne({
_id: checkpointId,
const checkpoint = UserPositionHistory.findOne({
_id: checkpointId,
userId: this.userId,
isCheckpoint: true,
});
if (!checkpoint) {
throw new Meteor.Error('not-found', 'Checkpoint not found');
}
// Find all changes after this checkpoint and undo them in reverse order
const changesToUndo = UserPositionHistory.find(
{
@ -480,19 +480,19 @@ Meteor.methods({
},
{ sort: { createdAt: -1 } }
).fetch();
let undoneCount = 0;
changesToUndo.forEach(change => {
for (const change of changesToUndo) {
try {
if (change.canUndo()) {
change.undo();
if (await change.canUndo()) {
await change.undo();
undoneCount++;
}
} catch (e) {
console.warn('Failed to undo change:', change._id, e);
}
});
}
return { undoneCount, totalChanges: changesToUndo.length };
},
});

View file

@ -902,10 +902,10 @@ if (Meteor.isClient) {
return board && board.hasWorker(this._id);
},
isBoardAdmin(boardId) {
async isBoardAdmin(boardId) {
let board;
if (boardId) {
board = ReactiveCache.getBoard(boardId);
board = await ReactiveCache.getBoard(boardId);
} else {
board = Utils.getCurrentBoard();
}
@ -1798,7 +1798,7 @@ Users.helpers({
Meteor.methods({
// Secure user removal method with proper authorization checks
removeUser(targetUserId) {
async removeUser(targetUserId) {
check(targetUserId, String);
const currentUserId = Meteor.userId();
@ -1806,12 +1806,12 @@ Meteor.methods({
throw new Meteor.Error('not-authorized', 'User must be logged in');
}
const currentUser = ReactiveCache.getUser(currentUserId);
const currentUser = await ReactiveCache.getUser(currentUserId);
if (!currentUser) {
throw new Meteor.Error('not-authorized', 'Current user not found');
}
const targetUser = ReactiveCache.getUser(targetUserId);
const targetUser = await ReactiveCache.getUser(targetUserId);
if (!targetUser) {
throw new Meteor.Error('user-not-found', 'Target user not found');
}
@ -1829,9 +1829,9 @@ Meteor.methods({
}
// Check if target user is the last admin
const adminsNumber = ReactiveCache.getUsers({
const adminsNumber = (await ReactiveCache.getUsers({
isAdmin: true,
}).length;
})).length;
if (adminsNumber === 1 && targetUser.isAdmin) {
throw new Meteor.Error('not-authorized', 'Cannot delete the last administrator');
@ -1841,7 +1841,7 @@ Meteor.methods({
Users.remove(targetUserId);
return { success: true, message: 'User deleted successfully' };
},
editUser(targetUserId, updateData) {
async editUser(targetUserId, updateData) {
check(targetUserId, String);
check(updateData, Object);
@ -1850,7 +1850,7 @@ Meteor.methods({
throw new Meteor.Error('not-authorized', 'User must be logged in');
}
const currentUser = ReactiveCache.getUser(currentUserId);
const currentUser = await ReactiveCache.getUser(currentUserId);
if (!currentUser) {
throw new Meteor.Error('not-authorized', 'Current user not found');
}
@ -1860,7 +1860,7 @@ Meteor.methods({
throw new Meteor.Error('not-authorized', 'Only administrators can edit other users');
}
const targetUser = ReactiveCache.getUser(targetUserId);
const targetUser = await ReactiveCache.getUser(targetUserId);
if (!targetUser) {
throw new Meteor.Error('user-not-found', 'Target user not found');
}
@ -1894,9 +1894,9 @@ Meteor.methods({
Users.update(targetUserId, { $set: updateObject });
},
setListSortBy(value) {
async setListSortBy(value) {
check(value, String);
ReactiveCache.getCurrentUser().setListSortBy(value);
(await ReactiveCache.getCurrentUser()).setListSortBy(value);
},
setAvatarUrl(avatarUrl) {
check(avatarUrl, String);
@ -1943,8 +1943,8 @@ Meteor.methods({
Users.update(this.userId, { $set: { 'profile.GreyIcons': newValue } });
return newValue;
},
toggleDesktopDragHandles() {
const user = ReactiveCache.getCurrentUser();
async toggleDesktopDragHandles() {
const user = await ReactiveCache.getCurrentUser();
user.toggleDesktopHandles(user.hasShowDesktopDragHandles());
},
// Spaces: create a new space under parentId (or root when null)
@ -2015,16 +2015,16 @@ Meteor.methods({
});
return true;
},
toggleHideCheckedItems() {
const user = ReactiveCache.getCurrentUser();
async toggleHideCheckedItems() {
const user = await ReactiveCache.getCurrentUser();
user.toggleHideCheckedItems();
},
toggleCustomFieldsGrid() {
const user = ReactiveCache.getCurrentUser();
async toggleCustomFieldsGrid() {
const user = await ReactiveCache.getCurrentUser();
user.toggleFieldsGrid(user.hasCustomFieldsGrid());
},
toggleCardMaximized() {
const user = ReactiveCache.getCurrentUser();
async toggleCardMaximized() {
const user = await ReactiveCache.getCurrentUser();
user.toggleCardMaximized(user.hasCardMaximized());
},
setCardCollapsed(value) {
@ -2032,32 +2032,32 @@ Meteor.methods({
if (!this.userId) throw new Meteor.Error('not-logged-in');
Users.update(this.userId, { $set: { 'profile.cardCollapsed': value } });
},
toggleMinicardLabelText() {
const user = ReactiveCache.getCurrentUser();
async toggleMinicardLabelText() {
const user = await ReactiveCache.getCurrentUser();
user.toggleLabelText(user.hasHiddenMinicardLabelText());
},
toggleRescueCardDescription() {
const user = ReactiveCache.getCurrentUser();
async toggleRescueCardDescription() {
const user = await ReactiveCache.getCurrentUser();
user.toggleRescueCardDescription(user.hasRescuedCardDescription());
},
changeLimitToShowCardsCount(limit) {
async changeLimitToShowCardsCount(limit) {
check(limit, Number);
ReactiveCache.getCurrentUser().setShowCardsCountAt(limit);
(await ReactiveCache.getCurrentUser()).setShowCardsCountAt(limit);
},
changeStartDayOfWeek(startDay) {
async changeStartDayOfWeek(startDay) {
check(startDay, Number);
ReactiveCache.getCurrentUser().setStartDayOfWeek(startDay);
(await ReactiveCache.getCurrentUser()).setStartDayOfWeek(startDay);
},
changeDateFormat(dateFormat) {
async changeDateFormat(dateFormat) {
check(dateFormat, String);
ReactiveCache.getCurrentUser().setDateFormat(dateFormat);
(await ReactiveCache.getCurrentUser()).setDateFormat(dateFormat);
},
applyListWidth(boardId, listId, width, constraint) {
async applyListWidth(boardId, listId, width, constraint) {
check(boardId, String);
check(listId, String);
check(width, Number);
check(constraint, Number);
const user = ReactiveCache.getCurrentUser();
const user = await ReactiveCache.getCurrentUser();
user.setListWidth(boardId, listId, width);
user.setListConstraint(boardId, listId, constraint);
},
@ -2081,11 +2081,11 @@ Meteor.methods({
},
});
},
applySwimlaneHeight(boardId, swimlaneId, height) {
async applySwimlaneHeight(boardId, swimlaneId, height) {
check(boardId, String);
check(swimlaneId, String);
check(height, Number);
const user = ReactiveCache.getCurrentUser();
const user = await ReactiveCache.getCurrentUser();
user.setSwimlaneHeight(boardId, swimlaneId, height);
},
@ -2110,42 +2110,42 @@ Meteor.methods({
});
},
applySwimlaneHeightToStorage(boardId, swimlaneId, height) {
async applySwimlaneHeightToStorage(boardId, swimlaneId, height) {
check(boardId, String);
check(swimlaneId, String);
check(height, Number);
const user = ReactiveCache.getCurrentUser();
const user = await ReactiveCache.getCurrentUser();
if (user) {
user.setSwimlaneHeightToStorage(boardId, swimlaneId, height);
}
// For non-logged-in users, the client-side code will handle localStorage
},
applyListWidthToStorage(boardId, listId, width, constraint) {
async applyListWidthToStorage(boardId, listId, width, constraint) {
check(boardId, String);
check(listId, String);
check(width, Number);
check(constraint, Number);
const user = ReactiveCache.getCurrentUser();
const user = await ReactiveCache.getCurrentUser();
if (user) {
user.setListWidthToStorage(boardId, listId, width);
user.setListConstraintToStorage(boardId, listId, constraint);
}
// For non-logged-in users, the client-side code will handle localStorage
},
setZoomLevel(level) {
async setZoomLevel(level) {
check(level, Number);
const user = ReactiveCache.getCurrentUser();
const user = await ReactiveCache.getCurrentUser();
user.setZoomLevel(level);
},
setMobileMode(enabled) {
async setMobileMode(enabled) {
check(enabled, Boolean);
const user = ReactiveCache.getCurrentUser();
const user = await ReactiveCache.getCurrentUser();
user.setMobileMode(enabled);
},
setBoardView(view) {
async setBoardView(view) {
check(view, String);
const user = ReactiveCache.getCurrentUser();
const user = await ReactiveCache.getCurrentUser();
if (!user) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
@ -2155,7 +2155,7 @@ Meteor.methods({
if (Meteor.isServer) {
Meteor.methods({
setCreateUser(
async setCreateUser(
fullname,
username,
initials,
@ -2185,13 +2185,13 @@ if (Meteor.isServer) {
initials.includes('/')) {
return false;
}
if (ReactiveCache.getCurrentUser()?.isAdmin) {
const nUsersWithUsername = ReactiveCache.getUsers({
if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
const nUsersWithUsername = (await ReactiveCache.getUsers({
username,
}).length;
const nUsersWithEmail = ReactiveCache.getUsers({
})).length;
const nUsersWithEmail = (await ReactiveCache.getUsers({
email,
}).length;
})).length;
if (nUsersWithUsername > 0) {
throw new Meteor.Error('username-already-taken');
} else if (nUsersWithEmail > 0) {
@ -2206,8 +2206,8 @@ if (Meteor.isServer) {
from: 'admin',
});
const user =
ReactiveCache.getUser(username) ||
ReactiveCache.getUser({ username });
await ReactiveCache.getUser(username) ||
await ReactiveCache.getUser({ username });
if (user) {
Users.update(user._id, {
$set: {
@ -2222,7 +2222,7 @@ if (Meteor.isServer) {
}
}
},
setUsername(username, userId) {
async setUsername(username, userId) {
check(username, String);
check(userId, String);
// Prevent Hyperlink Injection https://github.com/wekan/wekan/issues/5176
@ -2231,10 +2231,10 @@ if (Meteor.isServer) {
userId.includes('/')) {
return false;
}
if (ReactiveCache.getCurrentUser()?.isAdmin) {
const nUsersWithUsername = ReactiveCache.getUsers({
if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
const nUsersWithUsername = (await ReactiveCache.getUsers({
username,
}).length;
})).length;
if (nUsersWithUsername > 0) {
throw new Meteor.Error('username-already-taken');
} else {
@ -2246,7 +2246,7 @@ if (Meteor.isServer) {
}
}
},
setEmail(email, userId) {
async setEmail(email, userId) {
check(email, String);
check(username, String);
// Prevent Hyperlink Injection https://github.com/wekan/wekan/issues/5176
@ -2255,11 +2255,11 @@ if (Meteor.isServer) {
email.includes('/')) {
return false;
}
if (ReactiveCache.getCurrentUser()?.isAdmin) {
if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
if (Array.isArray(email)) {
email = email.shift();
}
const existingUser = ReactiveCache.getUser(
const existingUser = await ReactiveCache.getUser(
{
'emails.address': email,
},
@ -2285,7 +2285,7 @@ if (Meteor.isServer) {
}
}
},
setUsernameAndEmail(username, email, userId) {
async setUsernameAndEmail(username, email, userId) {
check(username, String);
check(email, String);
check(userId, String);
@ -2296,22 +2296,22 @@ if (Meteor.isServer) {
userId.includes('/')) {
return false;
}
if (ReactiveCache.getCurrentUser()?.isAdmin) {
if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
if (Array.isArray(email)) {
email = email.shift();
}
Meteor.call('setUsername', username, userId);
Meteor.call('setEmail', email, userId);
await Meteor.callAsync('setUsername', username, userId);
await Meteor.callAsync('setEmail', email, userId);
}
},
setPassword(newPassword, userId) {
async setPassword(newPassword, userId) {
check(userId, String);
check(newPassword, String);
if (ReactiveCache.getCurrentUser()?.isAdmin) {
if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
Accounts.setPassword(userId, newPassword);
}
},
setEmailVerified(email, verified, userId) {
async setEmailVerified(email, verified, userId) {
check(email, String);
check(verified, Boolean);
check(userId, String);
@ -2321,7 +2321,7 @@ if (Meteor.isServer) {
userId.includes('/')) {
return false;
}
if (ReactiveCache.getCurrentUser()?.isAdmin) {
if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
Users.update(userId, {
$set: {
emails: [
@ -2334,7 +2334,7 @@ if (Meteor.isServer) {
});
}
},
setInitials(initials, userId) {
async setInitials(initials, userId) {
check(initials, String);
check(userId, String);
// Prevent Hyperlink Injection https://github.com/wekan/wekan/issues/5176
@ -2343,7 +2343,7 @@ if (Meteor.isServer) {
userId.includes('/')) {
return false;
}
if (ReactiveCache.getCurrentUser()?.isAdmin) {
if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
Users.update(userId, {
$set: {
'profile.initials': initials,
@ -2352,7 +2352,7 @@ if (Meteor.isServer) {
}
},
// we accept userId, username, email
inviteUserToBoard(username, boardId) {
async inviteUserToBoard(username, boardId) {
check(username, String);
check(boardId, String);
// Prevent Hyperlink Injection https://github.com/wekan/wekan/issues/5176
@ -2361,8 +2361,8 @@ if (Meteor.isServer) {
boardId.includes('/')) {
return false;
}
const inviter = ReactiveCache.getCurrentUser();
const board = ReactiveCache.getBoard(boardId);
const inviter = await ReactiveCache.getCurrentUser();
const board = await ReactiveCache.getBoard(boardId);
const member = _.find(board.members, function(member) { return member.userId === inviter._id; });
if (!member) throw new Meteor.Error('error-board-notAMember');
const allowInvite = member.isActive;
@ -2375,7 +2375,7 @@ if (Meteor.isServer) {
const posAt = username.indexOf('@');
let user = null;
if (posAt >= 0) {
user = ReactiveCache.getUser({
user = await ReactiveCache.getUser({
emails: {
$elemMatch: {
address: username,
@ -2384,15 +2384,15 @@ if (Meteor.isServer) {
});
} else {
user =
ReactiveCache.getUser(username) ||
ReactiveCache.getUser({ username });
await ReactiveCache.getUser(username) ||
await ReactiveCache.getUser({ username });
}
if (user) {
if (user._id === inviter._id)
throw new Meteor.Error('error-user-notAllowSelf');
} else {
if (posAt <= 0) throw new Meteor.Error('error-user-doesNotExist');
if (ReactiveCache.getCurrentSetting().disableRegistration) {
if ((await ReactiveCache.getCurrentSetting()).disableRegistration) {
throw new Meteor.Error('error-user-notCreated');
}
// Set in lowercase email before creating account
@ -2418,7 +2418,7 @@ if (Meteor.isServer) {
});
}
Accounts.sendEnrollmentEmail(newUserId);
user = ReactiveCache.getUser(newUserId);
user = await ReactiveCache.getUser(newUserId);
}
const memberIndex = board.members.findIndex(m => m.userId === user._id);
@ -2431,7 +2431,7 @@ if (Meteor.isServer) {
//Check if there is a subtasks board
if (board.subtasksDefaultBoardId) {
const subBoard = ReactiveCache.getBoard(board.subtasksDefaultBoardId);
const subBoard = await ReactiveCache.getBoard(board.subtasksDefaultBoardId);
//If there is, also add user to that board
if (subBoard) {
const subMemberIndex = subBoard.members.findIndex(m => m.userId === user._id);
@ -2495,35 +2495,35 @@ if (Meteor.isServer) {
email: user.emails[0].address,
};
},
impersonate(userId) {
async impersonate(userId) {
check(userId, String);
if (!ReactiveCache.getUser(userId))
if (!(await ReactiveCache.getUser(userId)))
throw new Meteor.Error(404, 'User not found');
if (!ReactiveCache.getCurrentUser().isAdmin)
if (!(await ReactiveCache.getCurrentUser()).isAdmin)
throw new Meteor.Error(403, 'Permission denied');
ImpersonatedUsers.insert({
adminId: ReactiveCache.getCurrentUser()._id,
adminId: (await ReactiveCache.getCurrentUser())._id,
userId: userId,
reason: 'clickedImpersonate',
});
this.setUserId(userId);
},
isImpersonated(userId) {
async isImpersonated(userId) {
check(userId, String);
const isImpersonated = ReactiveCache.getImpersonatedUser({ userId: userId });
const isImpersonated = await ReactiveCache.getImpersonatedUser({ userId: userId });
return isImpersonated;
},
setUsersTeamsTeamDisplayName(teamId, teamDisplayName) {
async setUsersTeamsTeamDisplayName(teamId, teamDisplayName) {
check(teamId, String);
check(teamDisplayName, String);
if (ReactiveCache.getCurrentUser()?.isAdmin) {
ReactiveCache.getUsers({
if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
for (const user of await ReactiveCache.getUsers({
teams: {
$elemMatch: { teamId: teamId },
},
}).forEach((user) => {
})) {
Users.update(
{
_id: user._id,
@ -2537,18 +2537,18 @@ if (Meteor.isServer) {
},
},
);
});
}
}
},
setUsersOrgsOrgDisplayName(orgId, orgDisplayName) {
async setUsersOrgsOrgDisplayName(orgId, orgDisplayName) {
check(orgId, String);
check(orgDisplayName, String);
if (ReactiveCache.getCurrentUser()?.isAdmin) {
ReactiveCache.getUsers({
if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
for (const user of await ReactiveCache.getUsers({
orgs: {
$elemMatch: { orgId: orgId },
},
}).forEach((user) => {
})) {
Users.update(
{
_id: user._id,
@ -2562,12 +2562,12 @@ if (Meteor.isServer) {
},
},
);
});
}
}
},
});
Accounts.onCreateUser((options, user) => {
const userCount = ReactiveCache.getUsers({}, {}, true).count();
Accounts.onCreateUser(async (options, user) => {
const userCount = (await ReactiveCache.getUsers({}, {}, true)).count();
user.isAdmin = userCount === 0;
if (user.services.oidc) {
@ -2607,7 +2607,7 @@ if (Meteor.isServer) {
user.authenticationMethod = 'oauth2';
// see if any existing user has this email address or username, otherwise create new
const existingUser = ReactiveCache.getUser({
const existingUser = await ReactiveCache.getUser({
$or: [
{
'emails.address': email,
@ -2641,7 +2641,7 @@ if (Meteor.isServer) {
return user;
}
const disableRegistration = ReactiveCache.getCurrentSetting().disableRegistration;
const disableRegistration = (await ReactiveCache.getCurrentSetting()).disableRegistration;
// If this is the first Authentication by the ldap and self registration disabled
if (disableRegistration && options && options.ldap) {
user.authenticationMethod = 'ldap';
@ -2659,7 +2659,7 @@ if (Meteor.isServer) {
'The invitation code is required',
);
}
const invitationCode = ReactiveCache.getInvitationCode({
const invitationCode = await ReactiveCache.getInvitationCode({
code: options.profile.invitationcode,
email: options.email,
valid: true,
@ -2702,8 +2702,8 @@ const addCronJob = _.debounce(
SyncedCron.add({
name: 'notification_cleanup',
schedule: (parser) => parser.text('every 1 days'),
job: () => {
for (const user of ReactiveCache.getUsers()) {
job: async () => {
for (const user of await ReactiveCache.getUsers()) {
if (!user.profile || !user.profile.notifications) continue;
for (const notification of user.profile.notifications) {
if (notification.read) {
@ -2938,9 +2938,9 @@ if (Meteor.isServer) {
});
}
Users.after.insert((userId, doc) => {
Users.after.insert(async (userId, doc) => {
// HACK
doc = ReactiveCache.getUser(doc._id);
doc = await ReactiveCache.getUser(doc._id);
if (doc.createdThroughApi) {
// The admin user should be able to create a user despite disabling registration because
// it is two different things (registration and creation).
@ -2957,19 +2957,19 @@ if (Meteor.isServer) {
}
//invite user to corresponding boards
const disableRegistration = ReactiveCache.getCurrentSetting().disableRegistration;
const disableRegistration = (await ReactiveCache.getCurrentSetting()).disableRegistration;
// If ldap, bypass the inviation code if the self registration isn't allowed.
// TODO : pay attention if ldap field in the user model change to another content ex : ldap field to connection_type
if (doc.authenticationMethod !== 'ldap' && disableRegistration) {
let invitationCode = null;
if (doc.authenticationMethod.toLowerCase() == 'oauth2') {
// OIDC authentication mode
invitationCode = ReactiveCache.getInvitationCode({
invitationCode = await ReactiveCache.getInvitationCode({
email: doc.emails[0].address.toLowerCase(),
valid: true,
});
} else {
invitationCode = ReactiveCache.getInvitationCode({
invitationCode = await ReactiveCache.getInvitationCode({
code: doc.profile.icode,
valid: true,
});
@ -2977,15 +2977,15 @@ if (Meteor.isServer) {
if (!invitationCode) {
throw new Meteor.Error('error-invitation-code-not-exist');
} else {
invitationCode.boardsToBeInvited.forEach((boardId) => {
const board = ReactiveCache.getBoard(boardId);
for (const boardId of invitationCode.boardsToBeInvited) {
const board = await ReactiveCache.getBoard(boardId);
const memberIndex = board.members.findIndex(m => m.userId === doc._id);
if (memberIndex >= 0) {
Boards.update(boardId, { $set: { [`members.${memberIndex}.isActive`]: true } });
} else {
Boards.update(boardId, { $push: { members: { userId: doc._id, isAdmin: false, isActive: true, isNoComments: false, isCommentOnly: false, isWorker: false, isNormalAssignedOnly: false, isCommentAssignedOnly: false, isReadOnly: false, isReadAssignedOnly: false } } });
}
});
}
if (!doc.profile) {
doc.profile = {};
}
@ -3026,16 +3026,16 @@ if (Meteor.isServer) {
* @summary returns the current user
* @return_type Users
*/
JsonRoutes.add('GET', '/api/user', function (req, res) {
JsonRoutes.add('GET', '/api/user', async function (req, res) {
try {
Authentication.checkLoggedIn(req.userId);
const data = ReactiveCache.getUser({
const data = await ReactiveCache.getUser({
_id: req.userId,
});
delete data.services;
// get all boards where the user is member of
let boards = ReactiveCache.getBoards(
let boards = await ReactiveCache.getBoards(
{
type: 'board',
'members.userId': req.userId,
@ -3106,22 +3106,22 @@ if (Meteor.isServer) {
* @param {string} userId the user ID or username
* @return_type Users
*/
JsonRoutes.add('GET', '/api/users/:userId', function (req, res) {
JsonRoutes.add('GET', '/api/users/:userId', async function (req, res) {
try {
Authentication.checkUserId(req.userId);
let id = req.params.userId;
let user = ReactiveCache.getUser({
let user = await ReactiveCache.getUser({
_id: id,
});
if (!user) {
user = ReactiveCache.getUser({
user = await ReactiveCache.getUser({
username: id,
});
id = user._id;
}
// get all boards where the user is member of
let boards = ReactiveCache.getBoards(
let boards = await ReactiveCache.getBoards(
{
type: 'board',
'members.userId': id,
@ -3175,12 +3175,12 @@ if (Meteor.isServer) {
Authentication.checkUserId(req.userId);
const id = req.params.userId;
const action = req.body.action;
let data = ReactiveCache.getUser({
let data = await ReactiveCache.getUser({
_id: id,
});
if (data !== undefined) {
if (action === 'takeOwnership') {
const boards = ReactiveCache.getBoards(
const boards = await ReactiveCache.getBoards(
{
'members.userId': id,
'members.isAdmin': true,
@ -3227,7 +3227,7 @@ if (Meteor.isServer) {
},
);
}
data = ReactiveCache.getUser(id);
data = await ReactiveCache.getUser(id);
}
}
JsonRoutes.sendResult(res, {
@ -3270,19 +3270,19 @@ if (Meteor.isServer) {
JsonRoutes.add(
'POST',
'/api/boards/:boardId/members/:userId/add',
function (req, res) {
async function (req, res) {
try {
Authentication.checkUserId(req.userId);
const userId = req.params.userId;
const boardId = req.params.boardId;
const action = req.body.action;
const { isAdmin, isNoComments, isCommentOnly, isWorker, isNormalAssignedOnly, isCommentAssignedOnly, isReadOnly, isReadAssignedOnly } = req.body;
let data = ReactiveCache.getUser(userId);
let data = await ReactiveCache.getUser(userId);
if (data !== undefined) {
if (action === 'add') {
data = ReactiveCache.getBoards({
data = (await ReactiveCache.getBoards({
_id: boardId,
}).map(function (board) {
})).map(function (board) {
const hasMember = board.members.some(m => m.userId === userId && m.isActive);
if (!hasMember) {
const memberIndex = board.members.findIndex(m => m.userId === userId);
@ -3345,18 +3345,18 @@ if (Meteor.isServer) {
JsonRoutes.add(
'POST',
'/api/boards/:boardId/members/:userId/remove',
function (req, res) {
async function (req, res) {
try {
Authentication.checkUserId(req.userId);
const userId = req.params.userId;
const boardId = req.params.boardId;
const action = req.body.action;
let data = ReactiveCache.getUser(userId);
let data = await ReactiveCache.getUser(userId);
if (data !== undefined) {
if (action === 'remove') {
data = ReactiveCache.getBoards({
data = (await ReactiveCache.getBoards({
_id: boardId,
}).map(function (board) {
})).map(function (board) {
const hasMember = board.members.some(m => m.userId === userId && m.isActive);
if (hasMember) {
const memberIndex = board.members.findIndex(m => m.userId === userId);
@ -3591,7 +3591,7 @@ if (Meteor.isServer) {
check(userData, Object);
return sanitizeUserForSearch(userData);
},
searchUsers(query, boardId) {
async searchUsers(query, boardId) {
check(query, String);
check(boardId, String);
@ -3599,8 +3599,8 @@ if (Meteor.isServer) {
throw new Meteor.Error('not-logged-in', 'User must be logged in');
}
const currentUser = ReactiveCache.getCurrentUser();
const board = ReactiveCache.getBoard(boardId);
const currentUser = await ReactiveCache.getCurrentUser();
const board = await ReactiveCache.getBoard(boardId);
// Check if current user is a member of the board
const member = _.find(board.members, function(member) { return member.userId === currentUser._id; });
@ -3613,7 +3613,7 @@ if (Meteor.isServer) {
}
const searchRegex = new RegExp(query, 'i');
const users = ReactiveCache.getUsers({
const users = await ReactiveCache.getUsers({
$or: [
{ username: searchRegex },
{ 'profile.fullname': searchRegex },

View file

@ -244,14 +244,14 @@ export class WekanCreator {
]);
}
getMembersToMap(data) {
async getMembersToMap(data) {
// we will work on the list itself (an ordered array of objects) when a
// mapping is done, we add a 'wekan' field to the object representing the
// imported member
const membersToMap = data.members;
const users = data.users;
// auto-map based on username
membersToMap.forEach(importedMember => {
for (const importedMember of membersToMap) {
importedMember.id = importedMember.userId;
delete importedMember.userId;
const user = users.filter(user => {
@ -261,11 +261,11 @@ export class WekanCreator {
importedMember.fullName = user.profile.fullname;
}
importedMember.username = user.username;
const wekanUser = ReactiveCache.getUser({ username: importedMember.username });
const wekanUser = await ReactiveCache.getUser({ username: importedMember.username });
if (wekanUser) {
importedMember.wekanId = wekanUser._id;
}
});
}
return membersToMap;
}
@ -665,8 +665,8 @@ export class WekanCreator {
});
}
createSubtasks(wekanCards) {
wekanCards.forEach(card => {
async createSubtasks(wekanCards) {
for (const card of wekanCards) {
// get new id of card (in created / new board)
const cardIdInNewBoard = this.cards[card._id];
@ -683,7 +683,7 @@ export class WekanCreator {
: card.parentId;
//if the parent card exists, proceed
if (ReactiveCache.getCard(parentIdInNewBoard)) {
if (await ReactiveCache.getCard(parentIdInNewBoard)) {
//set parent id of the card in the new board to the new id of the parent
Cards.direct.update(cardIdInNewBoard, {
$set: {
@ -691,7 +691,7 @@ export class WekanCreator {
},
});
}
});
}
}
createChecklists(wekanChecklists) {
@ -978,7 +978,7 @@ export class WekanCreator {
Meteor.settings.public &&
Meteor.settings.public.sandstorm;
if (isSandstorm && currentBoardId) {
const currentBoard = ReactiveCache.getBoard(currentBoardId);
const currentBoard = await ReactiveCache.getBoard(currentBoardId);
await currentBoard.archive();
}
this.parseActivities(board);
@ -987,7 +987,7 @@ export class WekanCreator {
this.createSwimlanes(board.swimlanes, boardId);
this.createCustomFields(board.customFields, boardId);
this.createCards(board.cards, boardId);
this.createSubtasks(board.cards);
await this.createSubtasks(board.cards);
this.createChecklists(board.checklists);
this.createChecklistItems(board.checklistItems);
this.importActivities(board.activities, boardId);

View file

@ -1,13 +1,13 @@
import { ReactiveCache } from '/imports/reactiveCache';
export function getMembersToMap(data) {
export async function getMembersToMap(data) {
// we will work on the list itself (an ordered array of objects) when a
// mapping is done, we add a 'wekan' field to the object representing the
// imported member
const membersToMap = data.members;
const users = data.users;
// auto-map based on username
membersToMap.forEach(importedMember => {
for (const importedMember of membersToMap) {
importedMember.id = importedMember.userId;
delete importedMember.userId;
const user = users.filter(user => {
@ -17,10 +17,10 @@ export function getMembersToMap(data) {
importedMember.fullName = user.profile.fullname;
}
importedMember.username = user.username;
const wekanUser = ReactiveCache.getUser({ username: importedMember.username });
const wekanUser = await ReactiveCache.getUser({ username: importedMember.username });
if (wekanUser) {
importedMember.wekanId = wekanUser._id;
}
});
}
return membersToMap;
}