mirror of
https://github.com/wekan/wekan.git
synced 2026-02-15 20:48:08 +01:00
Prettier & eslint project style update
This commit is contained in:
parent
a0a482aa8e
commit
3eb4d2c341
116 changed files with 6216 additions and 5240 deletions
|
|
@ -2,15 +2,20 @@
|
|||
// server
|
||||
import { AccountsLockout } from 'meteor/lucasantoniassi:accounts-lockout';
|
||||
|
||||
(new AccountsLockout({
|
||||
new AccountsLockout({
|
||||
knownUsers: {
|
||||
failuresBeforeLockout: process.env.ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURES_BEFORE || 3,
|
||||
failuresBeforeLockout:
|
||||
process.env.ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURES_BEFORE || 3,
|
||||
lockoutPeriod: process.env.ACCOUNTS_LOCKOUT_KNOWN_USERS_PERIOD || 60,
|
||||
failureWindow: process.env.ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURE_WINDOW || 15,
|
||||
failureWindow:
|
||||
process.env.ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURE_WINDOW || 15,
|
||||
},
|
||||
unknownUsers: {
|
||||
failuresBeforeLockout: process.env.ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURES_BERORE || 3,
|
||||
lockoutPeriod: process.env.ACCOUNTS_LOCKOUT_UNKNOWN_USERS_LOCKOUT_PERIOD || 60,
|
||||
failureWindow: process.env.ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURE_WINDOW || 15,
|
||||
failuresBeforeLockout:
|
||||
process.env.ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURES_BERORE || 3,
|
||||
lockoutPeriod:
|
||||
process.env.ACCOUNTS_LOCKOUT_UNKNOWN_USERS_LOCKOUT_PERIOD || 60,
|
||||
failureWindow:
|
||||
process.env.ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURE_WINDOW || 15,
|
||||
},
|
||||
})).startup();
|
||||
}).startup();
|
||||
|
|
|
|||
|
|
@ -1,21 +1,20 @@
|
|||
import Fiber from 'fibers';
|
||||
|
||||
Meteor.startup(() => {
|
||||
|
||||
// Node Fibers 100% CPU usage issue
|
||||
// https://github.com/wekan/wekan-mongodb/issues/2#issuecomment-381453161
|
||||
// https://github.com/meteor/meteor/issues/9796#issuecomment-381676326
|
||||
// https://github.com/sandstorm-io/sandstorm/blob/0f1fec013fe7208ed0fd97eb88b31b77e3c61f42/shell/server/00-startup.js#L99-L129
|
||||
Fiber.poolSize = 1e9;
|
||||
|
||||
Accounts.validateLoginAttempt(function (options) {
|
||||
Accounts.validateLoginAttempt(function(options) {
|
||||
const user = options.user || {};
|
||||
return !user.loginDisabled;
|
||||
});
|
||||
|
||||
Authentication = {};
|
||||
|
||||
Authentication.checkUserId = function (userId) {
|
||||
Authentication.checkUserId = function(userId) {
|
||||
if (userId === undefined) {
|
||||
const error = new Meteor.Error('Unauthorized', 'Unauthorized');
|
||||
error.statusCode = 401;
|
||||
|
|
@ -28,13 +27,12 @@ Meteor.startup(() => {
|
|||
error.statusCode = 403;
|
||||
throw error;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// This will only check if the user is logged in.
|
||||
// The authorization checks for the user will have to be done inside each API endpoint
|
||||
Authentication.checkLoggedIn = function(userId) {
|
||||
if(userId === undefined) {
|
||||
if (userId === undefined) {
|
||||
const error = new Meteor.Error('Unauthorized', 'Unauthorized');
|
||||
error.statusCode = 401;
|
||||
throw error;
|
||||
|
|
@ -44,7 +42,7 @@ Meteor.startup(() => {
|
|||
// An admin should be authorized to access everything, so we use a separate check for admins
|
||||
// This throws an error if otherReq is false and the user is not an admin
|
||||
Authentication.checkAdminOrCondition = function(userId, otherReq) {
|
||||
if(otherReq) return;
|
||||
if (otherReq) return;
|
||||
const admin = Users.findOne({ _id: userId, isAdmin: true });
|
||||
if (admin === undefined) {
|
||||
const error = new Meteor.Error('Forbidden', 'Forbidden');
|
||||
|
|
@ -58,14 +56,16 @@ Meteor.startup(() => {
|
|||
Authentication.checkLoggedIn(userId);
|
||||
|
||||
const board = Boards.findOne({ _id: boardId });
|
||||
const normalAccess = board.permission === 'public' || board.members.some((e) => e.userId === userId);
|
||||
const normalAccess =
|
||||
board.permission === 'public' ||
|
||||
board.members.some(e => e.userId === userId);
|
||||
Authentication.checkAdminOrCondition(userId, normalAccess);
|
||||
};
|
||||
|
||||
if (Meteor.isServer) {
|
||||
if(process.env.OAUTH2_CLIENT_ID !== '') {
|
||||
|
||||
ServiceConfiguration.configurations.upsert( // eslint-disable-line no-undef
|
||||
if (process.env.OAUTH2_CLIENT_ID !== '') {
|
||||
ServiceConfiguration.configurations.upsert(
|
||||
// eslint-disable-line no-undef
|
||||
{ service: 'oidc' },
|
||||
{
|
||||
$set: {
|
||||
|
|
@ -76,15 +76,14 @@ Meteor.startup(() => {
|
|||
authorizationEndpoint: process.env.OAUTH2_AUTH_ENDPOINT,
|
||||
userinfoEndpoint: process.env.OAUTH2_USERINFO_ENDPOINT,
|
||||
tokenEndpoint: process.env.OAUTH2_TOKEN_ENDPOINT,
|
||||
idTokenWhitelistFields: process.env.OAUTH2_ID_TOKEN_WHITELIST_FIELDS || [],
|
||||
idTokenWhitelistFields:
|
||||
process.env.OAUTH2_ID_TOKEN_WHITELIST_FIELDS || [],
|
||||
requestPermissions: process.env.OAUTH2_REQUEST_PERMISSIONS,
|
||||
},
|
||||
// OAUTH2_ID_TOKEN_WHITELIST_FIELDS || [],
|
||||
// OAUTH2_REQUEST_PERMISSIONS || 'openid profile email',
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,23 +1,27 @@
|
|||
Meteor.startup(() => {
|
||||
|
||||
if ( process.env.CORS ) {
|
||||
if (process.env.CORS) {
|
||||
// Listen to incoming HTTP requests, can only be used on the server
|
||||
WebApp.rawConnectHandlers.use(function(req, res, next) {
|
||||
res.setHeader('Access-Control-Allow-Origin', process.env.CORS);
|
||||
return next();
|
||||
});
|
||||
}
|
||||
if ( process.env.CORS_ALLOW_HEADERS ) {
|
||||
if (process.env.CORS_ALLOW_HEADERS) {
|
||||
WebApp.rawConnectHandlers.use(function(req, res, next) {
|
||||
res.setHeader('Access-Control-Allow-Headers', process.env.CORS_ALLOW_HEADERS);
|
||||
res.setHeader(
|
||||
'Access-Control-Allow-Headers',
|
||||
process.env.CORS_ALLOW_HEADERS,
|
||||
);
|
||||
return next();
|
||||
});
|
||||
}
|
||||
if ( process.env.CORS_EXPOSE_HEADERS ) {
|
||||
if (process.env.CORS_EXPOSE_HEADERS) {
|
||||
WebApp.rawConnectHandlers.use(function(req, res, next) {
|
||||
res.setHeader('Access-Control-Expose-Headers', process.env.CORS_EXPOSE_HEADERS);
|
||||
res.setHeader(
|
||||
'Access-Control-Expose-Headers',
|
||||
process.env.CORS_EXPOSE_HEADERS,
|
||||
);
|
||||
return next();
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
Meteor.startup(() => {
|
||||
|
||||
if ( process.env.HEADER_LOGIN_ID ) {
|
||||
if (process.env.HEADER_LOGIN_ID) {
|
||||
Meteor.settings.public.headerLoginId = process.env.HEADER_LOGIN_ID;
|
||||
Meteor.settings.public.headerLoginEmail = process.env.HEADER_LOGIN_EMAIL;
|
||||
Meteor.settings.public.headerLoginFirstname = process.env.HEADER_LOGIN_FIRSTNAME;
|
||||
Meteor.settings.public.headerLoginLastname = process.env.HEADER_LOGIN_LASTNAME;
|
||||
Meteor.settings.public.headerLoginFirstname =
|
||||
process.env.HEADER_LOGIN_FIRSTNAME;
|
||||
Meteor.settings.public.headerLoginLastname =
|
||||
process.env.HEADER_LOGIN_LASTNAME;
|
||||
}
|
||||
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ allowIsBoardMember = function(userId, board) {
|
|||
};
|
||||
|
||||
allowIsAnyBoardMember = function(userId, boards) {
|
||||
return _.some(boards, (board) => {
|
||||
return _.some(boards, board => {
|
||||
return board && board.hasMember(userId);
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -62,16 +62,16 @@ Migrations.add('board-background-color', () => {
|
|||
},
|
||||
},
|
||||
},
|
||||
noValidateMulti
|
||||
noValidateMulti,
|
||||
);
|
||||
});
|
||||
|
||||
Migrations.add('lowercase-board-permission', () => {
|
||||
['Public', 'Private'].forEach((permission) => {
|
||||
['Public', 'Private'].forEach(permission => {
|
||||
Boards.update(
|
||||
{ permission },
|
||||
{ $set: { permission: permission.toLowerCase() } },
|
||||
noValidateMulti
|
||||
noValidateMulti,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
@ -79,7 +79,7 @@ Migrations.add('lowercase-board-permission', () => {
|
|||
// Security migration: see https://github.com/wekan/wekan/issues/99
|
||||
Migrations.add('change-attachments-type-for-non-images', () => {
|
||||
const newTypeForNonImage = 'application/octet-stream';
|
||||
Attachments.find().forEach((file) => {
|
||||
Attachments.find().forEach(file => {
|
||||
if (!file.isImage()) {
|
||||
Attachments.update(
|
||||
file._id,
|
||||
|
|
@ -89,14 +89,14 @@ Migrations.add('change-attachments-type-for-non-images', () => {
|
|||
'copies.attachments.type': newTypeForNonImage,
|
||||
},
|
||||
},
|
||||
noValidate
|
||||
noValidate,
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Migrations.add('card-covers', () => {
|
||||
Cards.find().forEach((card) => {
|
||||
Cards.find().forEach(card => {
|
||||
const cover = Attachments.findOne({ cardId: card._id, cover: true });
|
||||
if (cover) {
|
||||
Cards.update(card._id, { $set: { coverId: cover._id } }, noValidate);
|
||||
|
|
@ -114,7 +114,7 @@ Migrations.add('use-css-class-for-boards-colors', () => {
|
|||
'#2C3E50': 'midnight',
|
||||
'#E67E22': 'pumpkin',
|
||||
};
|
||||
Boards.find().forEach((board) => {
|
||||
Boards.find().forEach(board => {
|
||||
const oldBoardColor = board.background.color;
|
||||
const newBoardColor = associationTable[oldBoardColor];
|
||||
Boards.update(
|
||||
|
|
@ -123,13 +123,13 @@ Migrations.add('use-css-class-for-boards-colors', () => {
|
|||
$set: { color: newBoardColor },
|
||||
$unset: { background: '' },
|
||||
},
|
||||
noValidate
|
||||
noValidate,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Migrations.add('denormalize-star-number-per-board', () => {
|
||||
Boards.find().forEach((board) => {
|
||||
Boards.find().forEach(board => {
|
||||
const nStars = Users.find({ 'profile.starredBoards': board._id }).count();
|
||||
Boards.update(board._id, { $set: { stars: nStars } }, noValidate);
|
||||
});
|
||||
|
|
@ -138,9 +138,12 @@ Migrations.add('denormalize-star-number-per-board', () => {
|
|||
// We want to keep a trace of former members so we can efficiently publish their
|
||||
// infos in the general board publication.
|
||||
Migrations.add('add-member-isactive-field', () => {
|
||||
Boards.find({}, { fields: { members: 1 } }).forEach((board) => {
|
||||
Boards.find({}, { fields: { members: 1 } }).forEach(board => {
|
||||
const allUsersWithSomeActivity = _.chain(
|
||||
Activities.find({ boardId: board._id }, { fields: { userId: 1 } }).fetch()
|
||||
Activities.find(
|
||||
{ boardId: board._id },
|
||||
{ fields: { userId: 1 } },
|
||||
).fetch(),
|
||||
)
|
||||
.pluck('userId')
|
||||
.uniq()
|
||||
|
|
@ -149,11 +152,11 @@ Migrations.add('add-member-isactive-field', () => {
|
|||
const formerUsers = _.difference(allUsersWithSomeActivity, currentUsers);
|
||||
|
||||
const newMemberSet = [];
|
||||
board.members.forEach((member) => {
|
||||
board.members.forEach(member => {
|
||||
member.isActive = true;
|
||||
newMemberSet.push(member);
|
||||
});
|
||||
formerUsers.forEach((userId) => {
|
||||
formerUsers.forEach(userId => {
|
||||
newMemberSet.push({
|
||||
userId,
|
||||
isAdmin: false,
|
||||
|
|
@ -170,7 +173,7 @@ Migrations.add('add-sort-checklists', () => {
|
|||
Checklists.direct.update(
|
||||
checklist._id,
|
||||
{ $set: { sort: index } },
|
||||
noValidate
|
||||
noValidate,
|
||||
);
|
||||
}
|
||||
checklist.items.forEach((item, index) => {
|
||||
|
|
@ -178,7 +181,7 @@ Migrations.add('add-sort-checklists', () => {
|
|||
Checklists.direct.update(
|
||||
{ _id: checklist._id, 'items._id': item._id },
|
||||
{ $set: { 'items.$.sort': index } },
|
||||
noValidate
|
||||
noValidate,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
@ -186,14 +189,14 @@ Migrations.add('add-sort-checklists', () => {
|
|||
});
|
||||
|
||||
Migrations.add('add-swimlanes', () => {
|
||||
Boards.find().forEach((board) => {
|
||||
Boards.find().forEach(board => {
|
||||
const swimlaneId = board.getDefaultSwimline()._id;
|
||||
Cards.find({ boardId: board._id }).forEach((card) => {
|
||||
Cards.find({ boardId: board._id }).forEach(card => {
|
||||
if (!card.hasOwnProperty('swimlaneId')) {
|
||||
Cards.direct.update(
|
||||
{ _id: card._id },
|
||||
{ $set: { swimlaneId } },
|
||||
noValidate
|
||||
noValidate,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
@ -201,19 +204,19 @@ Migrations.add('add-swimlanes', () => {
|
|||
});
|
||||
|
||||
Migrations.add('add-views', () => {
|
||||
Boards.find().forEach((board) => {
|
||||
Boards.find().forEach(board => {
|
||||
if (!board.hasOwnProperty('view')) {
|
||||
Boards.direct.update(
|
||||
{ _id: board._id },
|
||||
{ $set: { view: 'board-view-swimlanes' } },
|
||||
noValidate
|
||||
noValidate,
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Migrations.add('add-checklist-items', () => {
|
||||
Checklists.find().forEach((checklist) => {
|
||||
Checklists.find().forEach(checklist => {
|
||||
// Create new items
|
||||
_.sortBy(checklist.items, 'sort').forEach((item, index) => {
|
||||
ChecklistItems.direct.insert({
|
||||
|
|
@ -229,26 +232,26 @@ Migrations.add('add-checklist-items', () => {
|
|||
Checklists.direct.update(
|
||||
{ _id: checklist._id },
|
||||
{ $unset: { items: 1 } },
|
||||
noValidate
|
||||
noValidate,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Migrations.add('add-profile-view', () => {
|
||||
Users.find().forEach((user) => {
|
||||
Users.find().forEach(user => {
|
||||
if (!user.hasOwnProperty('profile.boardView')) {
|
||||
// Set default view
|
||||
Users.direct.update(
|
||||
{ _id: user._id },
|
||||
{ $set: { 'profile.boardView': 'board-view-lists' } },
|
||||
noValidate
|
||||
noValidate,
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Migrations.add('add-card-types', () => {
|
||||
Cards.find().forEach((card) => {
|
||||
Cards.find().forEach(card => {
|
||||
Cards.direct.update(
|
||||
{ _id: card._id },
|
||||
{
|
||||
|
|
@ -257,7 +260,7 @@ Migrations.add('add-card-types', () => {
|
|||
linkedId: null,
|
||||
},
|
||||
},
|
||||
noValidate
|
||||
noValidate,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
@ -274,7 +277,7 @@ Migrations.add('add-custom-fields-to-cards', () => {
|
|||
customFields: [],
|
||||
},
|
||||
},
|
||||
noValidateMulti
|
||||
noValidateMulti,
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -290,7 +293,7 @@ Migrations.add('add-requester-field', () => {
|
|||
requestedBy: '',
|
||||
},
|
||||
},
|
||||
noValidateMulti
|
||||
noValidateMulti,
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -306,7 +309,7 @@ Migrations.add('add-assigner-field', () => {
|
|||
assignedBy: '',
|
||||
},
|
||||
},
|
||||
noValidateMulti
|
||||
noValidateMulti,
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -322,7 +325,7 @@ Migrations.add('add-parent-field-to-cards', () => {
|
|||
parentId: '',
|
||||
},
|
||||
},
|
||||
noValidateMulti
|
||||
noValidateMulti,
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -339,7 +342,7 @@ Migrations.add('add-subtasks-boards', () => {
|
|||
subtasksDefaultListId: null,
|
||||
},
|
||||
},
|
||||
noValidateMulti
|
||||
noValidateMulti,
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -355,7 +358,7 @@ Migrations.add('add-subtasks-sort', () => {
|
|||
subtaskSort: -1,
|
||||
},
|
||||
},
|
||||
noValidateMulti
|
||||
noValidateMulti,
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -371,7 +374,7 @@ Migrations.add('add-subtasks-allowed', () => {
|
|||
allowsSubtasks: true,
|
||||
},
|
||||
},
|
||||
noValidateMulti
|
||||
noValidateMulti,
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -387,7 +390,7 @@ Migrations.add('add-subtasks-allowed', () => {
|
|||
presentParentTask: 'no-parent',
|
||||
},
|
||||
},
|
||||
noValidateMulti
|
||||
noValidateMulti,
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -403,7 +406,7 @@ Migrations.add('add-authenticationMethod', () => {
|
|||
authenticationMethod: 'password',
|
||||
},
|
||||
},
|
||||
noValidateMulti
|
||||
noValidateMulti,
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -415,7 +418,7 @@ Migrations.add('remove-tag', () => {
|
|||
'profile.tags': 1,
|
||||
},
|
||||
},
|
||||
noValidateMulti
|
||||
noValidateMulti,
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -427,7 +430,7 @@ Migrations.add('remove-customFields-references-broken', () => {
|
|||
customFields: { value: null },
|
||||
},
|
||||
},
|
||||
noValidateMulti
|
||||
noValidateMulti,
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -443,7 +446,7 @@ Migrations.add('add-product-name', () => {
|
|||
productName: '',
|
||||
},
|
||||
},
|
||||
noValidateMulti
|
||||
noValidateMulti,
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -459,7 +462,7 @@ Migrations.add('add-hide-logo', () => {
|
|||
hideLogo: false,
|
||||
},
|
||||
},
|
||||
noValidateMulti
|
||||
noValidateMulti,
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -475,7 +478,7 @@ Migrations.add('add-custom-html-after-body-start', () => {
|
|||
customHTMLafterBodyStart: '',
|
||||
},
|
||||
},
|
||||
noValidateMulti
|
||||
noValidateMulti,
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -491,7 +494,7 @@ Migrations.add('add-custom-html-before-body-end', () => {
|
|||
customHTMLbeforeBodyEnd: '',
|
||||
},
|
||||
},
|
||||
noValidateMulti
|
||||
noValidateMulti,
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -507,7 +510,7 @@ Migrations.add('add-displayAuthenticationMethod', () => {
|
|||
displayAuthenticationMethod: true,
|
||||
},
|
||||
},
|
||||
noValidateMulti
|
||||
noValidateMulti,
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -523,7 +526,7 @@ Migrations.add('add-defaultAuthenticationMethod', () => {
|
|||
defaultAuthenticationMethod: 'password',
|
||||
},
|
||||
},
|
||||
noValidateMulti
|
||||
noValidateMulti,
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -539,7 +542,7 @@ Migrations.add('add-templates', () => {
|
|||
type: 'board',
|
||||
},
|
||||
},
|
||||
noValidateMulti
|
||||
noValidateMulti,
|
||||
);
|
||||
Swimlanes.update(
|
||||
{
|
||||
|
|
@ -552,7 +555,7 @@ Migrations.add('add-templates', () => {
|
|||
type: 'swimlane',
|
||||
},
|
||||
},
|
||||
noValidateMulti
|
||||
noValidateMulti,
|
||||
);
|
||||
Lists.update(
|
||||
{
|
||||
|
|
@ -569,13 +572,13 @@ Migrations.add('add-templates', () => {
|
|||
swimlaneId: '',
|
||||
},
|
||||
},
|
||||
noValidateMulti
|
||||
noValidateMulti,
|
||||
);
|
||||
Users.find({
|
||||
'profile.templatesBoardId': {
|
||||
$exists: false,
|
||||
},
|
||||
}).forEach((user) => {
|
||||
}).forEach(user => {
|
||||
// Create board and swimlanes
|
||||
Boards.insert(
|
||||
{
|
||||
|
|
@ -611,7 +614,7 @@ Migrations.add('add-templates', () => {
|
|||
Users.update(user._id, {
|
||||
$set: { 'profile.cardTemplatesSwimlaneId': swimlaneId },
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// Insert the list templates swimlane
|
||||
|
|
@ -627,7 +630,7 @@ Migrations.add('add-templates', () => {
|
|||
Users.update(user._id, {
|
||||
$set: { 'profile.listTemplatesSwimlaneId': swimlaneId },
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// Insert the board templates swimlane
|
||||
|
|
@ -643,15 +646,15 @@ Migrations.add('add-templates', () => {
|
|||
Users.update(user._id, {
|
||||
$set: { 'profile.boardTemplatesSwimlaneId': swimlaneId },
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Migrations.add('fix-circular-reference_', () => {
|
||||
Cards.find().forEach((card) => {
|
||||
Cards.find().forEach(card => {
|
||||
if (card.parentId === card._id) {
|
||||
Cards.update(card._id, { $set: { parentId: '' } }, noValidateMulti);
|
||||
}
|
||||
|
|
@ -659,7 +662,7 @@ Migrations.add('fix-circular-reference_', () => {
|
|||
});
|
||||
|
||||
Migrations.add('mutate-boardIds-in-customfields', () => {
|
||||
CustomFields.find().forEach((cf) => {
|
||||
CustomFields.find().forEach(cf => {
|
||||
CustomFields.update(
|
||||
cf,
|
||||
{
|
||||
|
|
@ -670,7 +673,7 @@ Migrations.add('mutate-boardIds-in-customfields', () => {
|
|||
boardId: '',
|
||||
},
|
||||
},
|
||||
noValidateMulti
|
||||
noValidateMulti,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
@ -696,13 +699,13 @@ const firstBatchOfDbsToAddCreatedAndUpdated = [
|
|||
UnsavedEdits,
|
||||
];
|
||||
|
||||
firstBatchOfDbsToAddCreatedAndUpdated.forEach((db) => {
|
||||
firstBatchOfDbsToAddCreatedAndUpdated.forEach(db => {
|
||||
db.before.insert((userId, doc) => {
|
||||
doc.createdAt = Date.now();
|
||||
doc.updatedAt = doc.createdAt;
|
||||
});
|
||||
|
||||
db.before.update((userId, doc, fieldNames, modifier, options) => {
|
||||
db.before.update((userId, doc, fieldNames, modifier) => {
|
||||
modifier.$set = modifier.$set || {};
|
||||
modifier.$set.updatedAt = new Date();
|
||||
});
|
||||
|
|
@ -732,13 +735,13 @@ const modifiedAtTables = [
|
|||
|
||||
Migrations.add('add-missing-created-and-modified', () => {
|
||||
Promise.all(
|
||||
modifiedAtTables.map((db) =>
|
||||
modifiedAtTables.map(db =>
|
||||
db
|
||||
.rawCollection()
|
||||
.update(
|
||||
{ modifiedAt: { $exists: false } },
|
||||
{ $set: { modifiedAt: new Date() } },
|
||||
{ multi: true }
|
||||
{ multi: true },
|
||||
)
|
||||
.then(() =>
|
||||
db
|
||||
|
|
@ -746,16 +749,16 @@ Migrations.add('add-missing-created-and-modified', () => {
|
|||
.update(
|
||||
{ createdAt: { $exists: false } },
|
||||
{ $set: { createdAt: new Date() } },
|
||||
{ multi: true }
|
||||
)
|
||||
)
|
||||
)
|
||||
{ multi: true },
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.then(() => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.info('Successfully added createdAt and updatedAt to all tables');
|
||||
})
|
||||
.catch((e) => {
|
||||
.catch(e => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(e);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,11 +3,15 @@ Meteor.startup(() => {
|
|||
Notifications.subscribe('email', (user, title, description, params) => {
|
||||
// add quote to make titles easier to read in email text
|
||||
const quoteParams = _.clone(params);
|
||||
['card', 'list', 'oldList', 'board', 'comment'].forEach((key) => {
|
||||
['card', 'list', 'oldList', 'board', 'comment'].forEach(key => {
|
||||
if (quoteParams[key]) quoteParams[key] = `"${params[key]}"`;
|
||||
});
|
||||
|
||||
const text = `${params.user} ${TAPi18n.__(description, quoteParams, user.getLanguage())}\n${params.url}`;
|
||||
const text = `${params.user} ${TAPi18n.__(
|
||||
description,
|
||||
quoteParams,
|
||||
user.getLanguage(),
|
||||
)}\n${params.url}`;
|
||||
user.addEmailBuffer(text);
|
||||
|
||||
// unlike setTimeout(func, delay, args),
|
||||
|
|
@ -39,5 +43,3 @@ Meteor.startup(() => {
|
|||
}, process.env.EMAIL_NOTIFICATION_TIMEOUT || 30000);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -14,14 +14,14 @@ Notifications = {
|
|||
notifyServices[serviceName] = callback;
|
||||
},
|
||||
|
||||
unsubscribe: (serviceName) => {
|
||||
unsubscribe: serviceName => {
|
||||
if (typeof notifyServices[serviceName] === 'function')
|
||||
delete notifyServices[serviceName];
|
||||
},
|
||||
|
||||
getUsers: (watchers) => {
|
||||
getUsers: watchers => {
|
||||
const users = [];
|
||||
watchers.forEach((userId) => {
|
||||
watchers.forEach(userId => {
|
||||
const user = Users.findOne(userId);
|
||||
if (user) users.push(user);
|
||||
});
|
||||
|
|
@ -29,9 +29,10 @@ Notifications = {
|
|||
},
|
||||
|
||||
notify: (user, title, description, params) => {
|
||||
for(const k in notifyServices) {
|
||||
for (const k in notifyServices) {
|
||||
const notifyImpl = notifyServices[k];
|
||||
if (notifyImpl && typeof notifyImpl === 'function') notifyImpl(user, title, description, params);
|
||||
if (notifyImpl && typeof notifyImpl === 'function')
|
||||
notifyImpl(user, title, description, params);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -8,7 +8,18 @@ const postCatchError = Meteor.wrapAsync((url, options, resolve) => {
|
|||
});
|
||||
});
|
||||
|
||||
const webhooksAtbts = ( (process.env.WEBHOOKS_ATTRIBUTES && process.env.WEBHOOKS_ATTRIBUTES.split(',') ) || ['cardId', 'listId', 'oldListId', 'boardId', 'comment', 'user', 'card', 'commentId', 'swimlaneId']);
|
||||
const webhooksAtbts = (process.env.WEBHOOKS_ATTRIBUTES &&
|
||||
process.env.WEBHOOKS_ATTRIBUTES.split(',')) || [
|
||||
'cardId',
|
||||
'listId',
|
||||
'oldListId',
|
||||
'boardId',
|
||||
'comment',
|
||||
'user',
|
||||
'card',
|
||||
'commentId',
|
||||
'swimlaneId',
|
||||
];
|
||||
|
||||
Meteor.methods({
|
||||
outgoingWebhooks(integrations, description, params) {
|
||||
|
|
@ -18,13 +29,29 @@ Meteor.methods({
|
|||
|
||||
// label activity did not work yet, see wekan/models/activities.js
|
||||
const quoteParams = _.clone(params);
|
||||
['card', 'list', 'oldList', 'board', 'oldBoard', 'comment', 'checklist', 'swimlane', 'oldSwimlane', 'label', 'attachment'].forEach((key) => {
|
||||
[
|
||||
'card',
|
||||
'list',
|
||||
'oldList',
|
||||
'board',
|
||||
'oldBoard',
|
||||
'comment',
|
||||
'checklist',
|
||||
'swimlane',
|
||||
'oldSwimlane',
|
||||
'label',
|
||||
'attachment',
|
||||
].forEach(key => {
|
||||
if (quoteParams[key]) quoteParams[key] = `"${params[key]}"`;
|
||||
});
|
||||
|
||||
const userId = (params.userId) ? params.userId : integrations[0].userId;
|
||||
const userId = params.userId ? params.userId : integrations[0].userId;
|
||||
const user = Users.findOne(userId);
|
||||
const text = `${params.user} ${TAPi18n.__(description, quoteParams, user.getLanguage())}\n${params.url}`;
|
||||
const text = `${params.user} ${TAPi18n.__(
|
||||
description,
|
||||
quoteParams,
|
||||
user.getLanguage(),
|
||||
)}\n${params.url}`;
|
||||
|
||||
if (text.length === 0) return;
|
||||
|
||||
|
|
@ -32,7 +59,7 @@ Meteor.methods({
|
|||
text: `${text}`,
|
||||
};
|
||||
|
||||
webhooksAtbts.forEach((key) => {
|
||||
webhooksAtbts.forEach(key => {
|
||||
if (params[key]) value[key] = params[key];
|
||||
});
|
||||
value.description = description;
|
||||
|
|
@ -45,7 +72,7 @@ Meteor.methods({
|
|||
data: value,
|
||||
};
|
||||
|
||||
integrations.forEach((integration) => {
|
||||
integrations.forEach(integration => {
|
||||
const response = postCatchError(integration.url, options);
|
||||
|
||||
if (response && response.statusCode && response.statusCode === 200) {
|
||||
|
|
|
|||
|
|
@ -12,22 +12,19 @@ Meteor.methods({
|
|||
watchableObj = Boards.findOne(id);
|
||||
if (!watchableObj) throw new Meteor.Error('error-board-doesNotExist');
|
||||
board = watchableObj;
|
||||
|
||||
} else if (watchableType === 'list') {
|
||||
watchableObj = Lists.findOne(id);
|
||||
if (!watchableObj) throw new Meteor.Error('error-list-doesNotExist');
|
||||
board = watchableObj.board();
|
||||
|
||||
} else if (watchableType === 'card') {
|
||||
watchableObj = Cards.findOne(id);
|
||||
if (!watchableObj) throw new Meteor.Error('error-card-doesNotExist');
|
||||
board = watchableObj.board();
|
||||
|
||||
} else {
|
||||
throw new Meteor.Error('error-json-schema');
|
||||
}
|
||||
|
||||
if ((board.permission === 'private') && !board.hasMember(userId))
|
||||
if (board.permission === 'private' && !board.hasMember(userId))
|
||||
throw new Meteor.Error('error-board-notAMember');
|
||||
|
||||
watchableObj.setWatcher(userId, level);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
import { BrowserPolicy } from 'meteor/browser-policy-common';
|
||||
|
||||
Meteor.startup(() => {
|
||||
|
||||
if ( process.env.BROWSER_POLICY_ENABLED === 'true' ) {
|
||||
if (process.env.BROWSER_POLICY_ENABLED === 'true') {
|
||||
// Trusted URL that can embed Wekan in iFrame.
|
||||
const trusted = process.env.TRUSTED_URL;
|
||||
BrowserPolicy.framing.disallow();
|
||||
|
|
@ -13,8 +12,7 @@ Meteor.startup(() => {
|
|||
//BrowserPolicy.content.allowFontDataUrl();
|
||||
BrowserPolicy.framing.restrictToOrigin(trusted);
|
||||
//BrowserPolicy.content.allowScriptOrigin(trusted);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// Disable browser policy and allow all framing and including.
|
||||
// Use only at internal LAN, not at Internet.
|
||||
BrowserPolicy.framing.allowAll();
|
||||
|
|
@ -26,9 +24,8 @@ Meteor.startup(() => {
|
|||
|
||||
// If Matomo URL is set, allow it.
|
||||
const matomoUrl = process.env.MATOMO_ADDRESS;
|
||||
if (matomoUrl){
|
||||
if (matomoUrl) {
|
||||
//BrowserPolicy.content.allowScriptOrigin(matomoUrl);
|
||||
//BrowserPolicy.content.allowImageOrigin(matomoUrl);
|
||||
}
|
||||
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,16 +4,21 @@
|
|||
// We use this publication to paginate for these two publications.
|
||||
|
||||
Meteor.publish('activities', (kind, id, limit, hideSystem) => {
|
||||
check(kind, Match.Where((x) => {
|
||||
return ['board', 'card'].indexOf(x) !== -1;
|
||||
}));
|
||||
check(
|
||||
kind,
|
||||
Match.Where(x => {
|
||||
return ['board', 'card'].indexOf(x) !== -1;
|
||||
}),
|
||||
);
|
||||
check(id, String);
|
||||
check(limit, Number);
|
||||
check(hideSystem, Boolean);
|
||||
|
||||
const selector = (hideSystem) ? {$and: [{activityType: 'addComment'}, {[`${kind}Id`]: id}]} : {[`${kind}Id`]: id};
|
||||
const selector = hideSystem
|
||||
? { $and: [{ activityType: 'addComment' }, { [`${kind}Id`]: id }] }
|
||||
: { [`${kind}Id`]: id };
|
||||
return Activities.find(selector, {
|
||||
limit,
|
||||
sort: {createdAt: -1},
|
||||
sort: { createdAt: -1 },
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,58 +5,62 @@
|
|||
Meteor.publish('boards', function() {
|
||||
// Ensure that the user is connected. If it is not, we need to return an empty
|
||||
// array to tell the client to remove the previously published docs.
|
||||
if (!Match.test(this.userId, String))
|
||||
return [];
|
||||
if (!Match.test(this.userId, String)) return [];
|
||||
|
||||
// Defensive programming to verify that starredBoards has the expected
|
||||
// format -- since the field is in the `profile` a user can modify it.
|
||||
const {starredBoards = []} = Users.findOne(this.userId).profile || {};
|
||||
const { starredBoards = [] } = Users.findOne(this.userId).profile || {};
|
||||
check(starredBoards, [String]);
|
||||
|
||||
return Boards.find({
|
||||
archived: false,
|
||||
$or: [
|
||||
{
|
||||
_id: { $in: starredBoards },
|
||||
permission: 'public',
|
||||
},
|
||||
{ members: { $elemMatch: { userId: this.userId, isActive: true }}},
|
||||
],
|
||||
}, {
|
||||
fields: {
|
||||
_id: 1,
|
||||
archived: 1,
|
||||
slug: 1,
|
||||
title: 1,
|
||||
description: 1,
|
||||
color: 1,
|
||||
members: 1,
|
||||
permission: 1,
|
||||
type: 1,
|
||||
return Boards.find(
|
||||
{
|
||||
archived: false,
|
||||
$or: [
|
||||
{
|
||||
_id: { $in: starredBoards },
|
||||
permission: 'public',
|
||||
},
|
||||
{ members: { $elemMatch: { userId: this.userId, isActive: true } } },
|
||||
],
|
||||
},
|
||||
});
|
||||
{
|
||||
fields: {
|
||||
_id: 1,
|
||||
archived: 1,
|
||||
slug: 1,
|
||||
title: 1,
|
||||
description: 1,
|
||||
color: 1,
|
||||
members: 1,
|
||||
permission: 1,
|
||||
type: 1,
|
||||
},
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
Meteor.publish('archivedBoards', function() {
|
||||
if (!Match.test(this.userId, String))
|
||||
return [];
|
||||
if (!Match.test(this.userId, String)) return [];
|
||||
|
||||
return Boards.find({
|
||||
archived: true,
|
||||
members: {
|
||||
$elemMatch: {
|
||||
userId: this.userId,
|
||||
isAdmin: true,
|
||||
return Boards.find(
|
||||
{
|
||||
archived: true,
|
||||
members: {
|
||||
$elemMatch: {
|
||||
userId: this.userId,
|
||||
isAdmin: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
fields: {
|
||||
_id: 1,
|
||||
archived: 1,
|
||||
slug: 1,
|
||||
title: 1,
|
||||
{
|
||||
fields: {
|
||||
_id: 1,
|
||||
archived: 1,
|
||||
slug: 1,
|
||||
title: 1,
|
||||
},
|
||||
},
|
||||
});
|
||||
);
|
||||
});
|
||||
|
||||
// If isArchived = false, this will only return board elements which are not archived.
|
||||
|
|
@ -67,106 +71,130 @@ Meteor.publishRelations('board', function(boardId, isArchived) {
|
|||
check(isArchived, Boolean);
|
||||
const thisUserId = this.userId;
|
||||
|
||||
this.cursor(Boards.find({
|
||||
_id: boardId,
|
||||
archived: false,
|
||||
// If the board is not public the user has to be a member of it to see
|
||||
// it.
|
||||
$or: [
|
||||
{ permission: 'public' },
|
||||
{ members: { $elemMatch: { userId: this.userId, isActive: true }}},
|
||||
],
|
||||
// Sort required to ensure oplog usage
|
||||
}, { limit: 1, sort: { _id: 1 } }), function(boardId, board) {
|
||||
this.cursor(Lists.find({ boardId, archived: isArchived }));
|
||||
this.cursor(Swimlanes.find({ boardId, archived: isArchived }));
|
||||
this.cursor(Integrations.find({ boardId }));
|
||||
this.cursor(CustomFields.find({ boardIds: {$in: [boardId]} }, { sort: { name: 1 } }));
|
||||
this.cursor(
|
||||
Boards.find(
|
||||
{
|
||||
_id: boardId,
|
||||
archived: false,
|
||||
// If the board is not public the user has to be a member of it to see
|
||||
// it.
|
||||
$or: [
|
||||
{ permission: 'public' },
|
||||
{ members: { $elemMatch: { userId: this.userId, isActive: true } } },
|
||||
],
|
||||
// Sort required to ensure oplog usage
|
||||
},
|
||||
{ limit: 1, sort: { _id: 1 } },
|
||||
),
|
||||
function(boardId, board) {
|
||||
this.cursor(Lists.find({ boardId, archived: isArchived }));
|
||||
this.cursor(Swimlanes.find({ boardId, archived: isArchived }));
|
||||
this.cursor(Integrations.find({ boardId }));
|
||||
this.cursor(
|
||||
CustomFields.find(
|
||||
{ boardIds: { $in: [boardId] } },
|
||||
{ sort: { name: 1 } },
|
||||
),
|
||||
);
|
||||
|
||||
// Cards and cards comments
|
||||
// XXX Originally we were publishing the card documents as a child of the
|
||||
// list publication defined above using the following selector `{ listId:
|
||||
// list._id }`. But it was causing a race condition in publish-composite,
|
||||
// that I documented here:
|
||||
//
|
||||
// https://github.com/englue/meteor-publish-composite/issues/29
|
||||
//
|
||||
// cottz:publish had a similar problem:
|
||||
//
|
||||
// https://github.com/Goluis/cottz-publish/issues/4
|
||||
//
|
||||
// The current state of relational publishing in meteor is a bit sad,
|
||||
// there are a lot of various packages, with various APIs, some of them
|
||||
// are unmaintained. Fortunately this is something that will be fixed by
|
||||
// meteor-core at some point:
|
||||
//
|
||||
// https://trello.com/c/BGvIwkEa/48-easy-joins-in-subscriptions
|
||||
//
|
||||
// And in the meantime our code below works pretty well -- it's not even a
|
||||
// hack!
|
||||
// Cards and cards comments
|
||||
// XXX Originally we were publishing the card documents as a child of the
|
||||
// list publication defined above using the following selector `{ listId:
|
||||
// list._id }`. But it was causing a race condition in publish-composite,
|
||||
// that I documented here:
|
||||
//
|
||||
// https://github.com/englue/meteor-publish-composite/issues/29
|
||||
//
|
||||
// cottz:publish had a similar problem:
|
||||
//
|
||||
// https://github.com/Goluis/cottz-publish/issues/4
|
||||
//
|
||||
// The current state of relational publishing in meteor is a bit sad,
|
||||
// there are a lot of various packages, with various APIs, some of them
|
||||
// are unmaintained. Fortunately this is something that will be fixed by
|
||||
// meteor-core at some point:
|
||||
//
|
||||
// https://trello.com/c/BGvIwkEa/48-easy-joins-in-subscriptions
|
||||
//
|
||||
// And in the meantime our code below works pretty well -- it's not even a
|
||||
// hack!
|
||||
|
||||
// Gather queries and send in bulk
|
||||
const cardComments = this.join(CardComments);
|
||||
cardComments.selector = (_ids) => ({ cardId: _ids });
|
||||
const attachments = this.join(Attachments);
|
||||
attachments.selector = (_ids) => ({ cardId: _ids });
|
||||
const checklists = this.join(Checklists);
|
||||
checklists.selector = (_ids) => ({ cardId: _ids });
|
||||
const checklistItems = this.join(ChecklistItems);
|
||||
checklistItems.selector = (_ids) => ({ cardId: _ids });
|
||||
const parentCards = this.join(Cards);
|
||||
parentCards.selector = (_ids) => ({ parentId: _ids });
|
||||
const boards = this.join(Boards);
|
||||
const subCards = this.join(Cards);
|
||||
subCards.selector = (_ids) => ({ archived: isArchived });
|
||||
// Gather queries and send in bulk
|
||||
const cardComments = this.join(CardComments);
|
||||
cardComments.selector = _ids => ({ cardId: _ids });
|
||||
const attachments = this.join(Attachments);
|
||||
attachments.selector = _ids => ({ cardId: _ids });
|
||||
const checklists = this.join(Checklists);
|
||||
checklists.selector = _ids => ({ cardId: _ids });
|
||||
const checklistItems = this.join(ChecklistItems);
|
||||
checklistItems.selector = _ids => ({ cardId: _ids });
|
||||
const parentCards = this.join(Cards);
|
||||
parentCards.selector = _ids => ({ parentId: _ids });
|
||||
const boards = this.join(Boards);
|
||||
const subCards = this.join(Cards);
|
||||
subCards.selector = () => ({ archived: isArchived });
|
||||
|
||||
this.cursor(Cards.find({ boardId: {$in: [boardId, board.subtasksDefaultBoardId]}, archived: isArchived }), function(cardId, card) {
|
||||
if (card.type === 'cardType-linkedCard') {
|
||||
const impCardId = card.linkedId;
|
||||
subCards.push(impCardId);
|
||||
cardComments.push(impCardId);
|
||||
attachments.push(impCardId);
|
||||
checklists.push(impCardId);
|
||||
checklistItems.push(impCardId);
|
||||
} else if (card.type === 'cardType-linkedBoard') {
|
||||
boards.push(card.linkedId);
|
||||
this.cursor(
|
||||
Cards.find({
|
||||
boardId: { $in: [boardId, board.subtasksDefaultBoardId] },
|
||||
archived: isArchived,
|
||||
}),
|
||||
function(cardId, card) {
|
||||
if (card.type === 'cardType-linkedCard') {
|
||||
const impCardId = card.linkedId;
|
||||
subCards.push(impCardId);
|
||||
cardComments.push(impCardId);
|
||||
attachments.push(impCardId);
|
||||
checklists.push(impCardId);
|
||||
checklistItems.push(impCardId);
|
||||
} else if (card.type === 'cardType-linkedBoard') {
|
||||
boards.push(card.linkedId);
|
||||
}
|
||||
cardComments.push(cardId);
|
||||
attachments.push(cardId);
|
||||
checklists.push(cardId);
|
||||
checklistItems.push(cardId);
|
||||
parentCards.push(cardId);
|
||||
},
|
||||
);
|
||||
|
||||
// Send bulk queries for all found ids
|
||||
subCards.send();
|
||||
cardComments.send();
|
||||
attachments.send();
|
||||
checklists.send();
|
||||
checklistItems.send();
|
||||
boards.send();
|
||||
parentCards.send();
|
||||
|
||||
if (board.members) {
|
||||
// Board members. This publication also includes former board members that
|
||||
// aren't members anymore but may have some activities attached to them in
|
||||
// the history.
|
||||
const memberIds = _.pluck(board.members, 'userId');
|
||||
|
||||
// We omit the current user because the client should already have that data,
|
||||
// and sending it triggers a subtle bug:
|
||||
// https://github.com/wefork/wekan/issues/15
|
||||
this.cursor(
|
||||
Users.find(
|
||||
{
|
||||
_id: { $in: _.without(memberIds, thisUserId) },
|
||||
},
|
||||
{
|
||||
fields: {
|
||||
username: 1,
|
||||
'profile.fullname': 1,
|
||||
'profile.avatarUrl': 1,
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
this.cursor(presences.find({ userId: { $in: memberIds } }));
|
||||
}
|
||||
cardComments.push(cardId);
|
||||
attachments.push(cardId);
|
||||
checklists.push(cardId);
|
||||
checklistItems.push(cardId);
|
||||
parentCards.push(cardId);
|
||||
});
|
||||
|
||||
// Send bulk queries for all found ids
|
||||
subCards.send();
|
||||
cardComments.send();
|
||||
attachments.send();
|
||||
checklists.send();
|
||||
checklistItems.send();
|
||||
boards.send();
|
||||
parentCards.send();
|
||||
|
||||
if (board.members) {
|
||||
// Board members. This publication also includes former board members that
|
||||
// aren't members anymore but may have some activities attached to them in
|
||||
// the history.
|
||||
const memberIds = _.pluck(board.members, 'userId');
|
||||
|
||||
// We omit the current user because the client should already have that data,
|
||||
// and sending it triggers a subtle bug:
|
||||
// https://github.com/wefork/wekan/issues/15
|
||||
this.cursor(Users.find({
|
||||
_id: { $in: _.without(memberIds, thisUserId)},
|
||||
}, { fields: {
|
||||
'username': 1,
|
||||
'profile.fullname': 1,
|
||||
'profile.avatarUrl': 1,
|
||||
}}));
|
||||
|
||||
this.cursor(presences.find({ userId: { $in: memberIds } }));
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
return this.ready();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
Meteor.publish('card', (cardId) => {
|
||||
Meteor.publish('card', cardId => {
|
||||
check(cardId, String);
|
||||
return Cards.find({ _id: cardId });
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,19 +7,22 @@ Meteor.publish('people', function(limit) {
|
|||
|
||||
const user = Users.findOne(this.userId);
|
||||
if (user && user.isAdmin) {
|
||||
return Users.find({}, {
|
||||
limit,
|
||||
sort: {createdAt: -1},
|
||||
fields: {
|
||||
'username': 1,
|
||||
'profile.fullname': 1,
|
||||
'isAdmin': 1,
|
||||
'emails': 1,
|
||||
'createdAt': 1,
|
||||
'loginDisabled': 1,
|
||||
'authenticationMethod': 1,
|
||||
return Users.find(
|
||||
{},
|
||||
{
|
||||
limit,
|
||||
sort: { createdAt: -1 },
|
||||
fields: {
|
||||
username: 1,
|
||||
'profile.fullname': 1,
|
||||
isAdmin: 1,
|
||||
emails: 1,
|
||||
createdAt: 1,
|
||||
loginDisabled: 1,
|
||||
authenticationMethod: 1,
|
||||
},
|
||||
},
|
||||
});
|
||||
);
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
Meteor.publish('rules', (ruleId) => {
|
||||
Meteor.publish('rules', ruleId => {
|
||||
check(ruleId, String);
|
||||
return Rules.find({
|
||||
_id: ruleId,
|
||||
|
|
|
|||
|
|
@ -1,23 +1,25 @@
|
|||
Meteor.publish('setting', () => {
|
||||
return Settings.find({}, {
|
||||
fields:{
|
||||
disableRegistration: 1,
|
||||
productName: 1,
|
||||
hideLogo: 1,
|
||||
customHTMLafterBodyStart: 1,
|
||||
customHTMLbeforeBodyEnd: 1,
|
||||
displayAuthenticationMethod: 1,
|
||||
defaultAuthenticationMethod: 1,
|
||||
return Settings.find(
|
||||
{},
|
||||
{
|
||||
fields: {
|
||||
disableRegistration: 1,
|
||||
productName: 1,
|
||||
hideLogo: 1,
|
||||
customHTMLafterBodyStart: 1,
|
||||
customHTMLbeforeBodyEnd: 1,
|
||||
displayAuthenticationMethod: 1,
|
||||
defaultAuthenticationMethod: 1,
|
||||
},
|
||||
},
|
||||
});
|
||||
);
|
||||
});
|
||||
|
||||
Meteor.publish('mailServer', function () {
|
||||
if (!Match.test(this.userId, String))
|
||||
return [];
|
||||
Meteor.publish('mailServer', function() {
|
||||
if (!Match.test(this.userId, String)) return [];
|
||||
const user = Users.findOne(this.userId);
|
||||
if(user && user.isAdmin){
|
||||
return Settings.find({}, {fields: {mailServer: 1}});
|
||||
if (user && user.isAdmin) {
|
||||
return Settings.find({}, { fields: { mailServer: 1 } });
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ Meteor.publish('user-miniprofile', function(userId) {
|
|||
|
||||
return Users.find(userId, {
|
||||
fields: {
|
||||
'username': 1,
|
||||
username: 1,
|
||||
'profile.fullname': 1,
|
||||
'profile.avatarUrl': 1,
|
||||
},
|
||||
|
|
@ -20,9 +20,12 @@ Meteor.publish('user-admin', function() {
|
|||
|
||||
Meteor.publish('user-authenticationMethod', function(match) {
|
||||
check(match, String);
|
||||
return Users.find({$or: [{_id: match}, {email: match}, {username: match}]}, {
|
||||
fields: {
|
||||
'authenticationMethod': 1,
|
||||
return Users.find(
|
||||
{ $or: [{ _id: match }, { email: match }, { username: match }] },
|
||||
{
|
||||
fields: {
|
||||
authenticationMethod: 1,
|
||||
},
|
||||
},
|
||||
});
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,72 +1,78 @@
|
|||
RulesHelper = {
|
||||
executeRules(activity){
|
||||
executeRules(activity) {
|
||||
const matchingRules = this.findMatchingRules(activity);
|
||||
for(let i = 0; i< matchingRules.length; i++){
|
||||
for (let i = 0; i < matchingRules.length; i++) {
|
||||
const action = matchingRules[i].getAction();
|
||||
if(action !== undefined){
|
||||
if (action !== undefined) {
|
||||
this.performAction(activity, action);
|
||||
}
|
||||
}
|
||||
},
|
||||
findMatchingRules(activity){
|
||||
findMatchingRules(activity) {
|
||||
const activityType = activity.activityType;
|
||||
if(TriggersDef[activityType] === undefined){
|
||||
if (TriggersDef[activityType] === undefined) {
|
||||
return [];
|
||||
}
|
||||
const matchingFields = TriggersDef[activityType].matchingFields;
|
||||
const matchingMap = this.buildMatchingFieldsMap(activity, matchingFields);
|
||||
const matchingTriggers = Triggers.find(matchingMap);
|
||||
const matchingRules = [];
|
||||
matchingTriggers.forEach(function(trigger){
|
||||
matchingTriggers.forEach(function(trigger) {
|
||||
const rule = trigger.getRule();
|
||||
// Check that for some unknown reason there are some leftover triggers
|
||||
// not connected to any rules
|
||||
if(rule !== undefined){
|
||||
if (rule !== undefined) {
|
||||
matchingRules.push(trigger.getRule());
|
||||
}
|
||||
});
|
||||
return matchingRules;
|
||||
},
|
||||
buildMatchingFieldsMap(activity, matchingFields){
|
||||
const matchingMap = {'activityType':activity.activityType};
|
||||
for(let i = 0; i< matchingFields.length; i++){
|
||||
buildMatchingFieldsMap(activity, matchingFields) {
|
||||
const matchingMap = { activityType: activity.activityType };
|
||||
for (let i = 0; i < matchingFields.length; i++) {
|
||||
// Creating a matching map with the actual field of the activity
|
||||
// and with the wildcard (for example: trigger when a card is added
|
||||
// in any [*] board
|
||||
matchingMap[matchingFields[i]] = { $in: [activity[matchingFields[i]], '*']};
|
||||
matchingMap[matchingFields[i]] = {
|
||||
$in: [activity[matchingFields[i]], '*'],
|
||||
};
|
||||
}
|
||||
return matchingMap;
|
||||
},
|
||||
performAction(activity, action){
|
||||
const card = Cards.findOne({_id:activity.cardId});
|
||||
performAction(activity, action) {
|
||||
const card = Cards.findOne({ _id: activity.cardId });
|
||||
const boardId = activity.boardId;
|
||||
if(action.actionType === 'moveCardToTop'){
|
||||
if (action.actionType === 'moveCardToTop') {
|
||||
let listId;
|
||||
let list;
|
||||
if(action.listTitle === '*'){
|
||||
if (action.listTitle === '*') {
|
||||
listId = card.listId;
|
||||
list = card.list();
|
||||
}else{
|
||||
list = Lists.findOne({title: action.listTitle, boardId });
|
||||
} else {
|
||||
list = Lists.findOne({ title: action.listTitle, boardId });
|
||||
listId = list._id;
|
||||
}
|
||||
const minOrder = _.min(list.cardsUnfiltered(card.swimlaneId).map((c) => c.sort));
|
||||
const minOrder = _.min(
|
||||
list.cardsUnfiltered(card.swimlaneId).map(c => c.sort),
|
||||
);
|
||||
card.move(boardId, card.swimlaneId, listId, minOrder - 1);
|
||||
}
|
||||
if(action.actionType === 'moveCardToBottom'){
|
||||
if (action.actionType === 'moveCardToBottom') {
|
||||
let listId;
|
||||
let list;
|
||||
if(action.listTitle === '*'){
|
||||
if (action.listTitle === '*') {
|
||||
listId = card.listId;
|
||||
list = card.list();
|
||||
}else{
|
||||
list = Lists.findOne({title: action.listTitle, boardId});
|
||||
} else {
|
||||
list = Lists.findOne({ title: action.listTitle, boardId });
|
||||
listId = list._id;
|
||||
}
|
||||
const maxOrder = _.max(list.cardsUnfiltered(card.swimlaneId).map((c) => c.sort));
|
||||
const maxOrder = _.max(
|
||||
list.cardsUnfiltered(card.swimlaneId).map(c => c.sort),
|
||||
);
|
||||
card.move(boardId, card.swimlaneId, listId, maxOrder + 1);
|
||||
}
|
||||
if(action.actionType === 'sendEmail'){
|
||||
if (action.actionType === 'sendEmail') {
|
||||
const to = action.emailTo;
|
||||
const text = action.emailMsg || '';
|
||||
const subject = action.emailSubject || '';
|
||||
|
|
@ -84,38 +90,38 @@ RulesHelper = {
|
|||
}
|
||||
}
|
||||
|
||||
if(action.actionType === 'setDate') {
|
||||
if (action.actionType === 'setDate') {
|
||||
try {
|
||||
const currentDateTime = new Date();
|
||||
switch (action.dateField){
|
||||
case 'startAt': {
|
||||
const resStart = card.getStart();
|
||||
if (typeof resStart === 'undefined') {
|
||||
card.setStart(currentDateTime);
|
||||
switch (action.dateField) {
|
||||
case 'startAt': {
|
||||
const resStart = card.getStart();
|
||||
if (typeof resStart === 'undefined') {
|
||||
card.setStart(currentDateTime);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'endAt': {
|
||||
const resEnd = card.getEnd();
|
||||
if (typeof resEnd === 'undefined') {
|
||||
card.setEnd(currentDateTime);
|
||||
case 'endAt': {
|
||||
const resEnd = card.getEnd();
|
||||
if (typeof resEnd === 'undefined') {
|
||||
card.setEnd(currentDateTime);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'dueAt': {
|
||||
const resDue = card.getDue();
|
||||
if (typeof resDue === 'undefined') {
|
||||
card.setDue(currentDateTime);
|
||||
case 'dueAt': {
|
||||
const resDue = card.getDue();
|
||||
if (typeof resDue === 'undefined') {
|
||||
card.setDue(currentDateTime);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'receivedAt': {
|
||||
const resReceived = card.getReceived();
|
||||
if (typeof resReceived === 'undefined') {
|
||||
card.setReceived(currentDateTime);
|
||||
case 'receivedAt': {
|
||||
const resReceived = card.getReceived();
|
||||
if (typeof resReceived === 'undefined') {
|
||||
card.setReceived(currentDateTime);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// eslint-disable-next-line no-console
|
||||
|
|
@ -124,135 +130,177 @@ RulesHelper = {
|
|||
}
|
||||
}
|
||||
|
||||
if(action.actionType === 'updateDate'){
|
||||
if (action.actionType === 'updateDate') {
|
||||
const currentDateTimeUpdate = new Date();
|
||||
switch (action.dateField){
|
||||
case 'startAt': {
|
||||
card.setStart(currentDateTimeUpdate);
|
||||
break;
|
||||
}
|
||||
case 'endAt': {
|
||||
card.setEnd(currentDateTimeUpdate);
|
||||
break;
|
||||
}
|
||||
case 'dueAt': {
|
||||
card.setDue(currentDateTimeUpdate);
|
||||
break;
|
||||
}
|
||||
case 'receivedAt': {
|
||||
card.setReceived(currentDateTimeUpdate);
|
||||
break;
|
||||
}
|
||||
switch (action.dateField) {
|
||||
case 'startAt': {
|
||||
card.setStart(currentDateTimeUpdate);
|
||||
break;
|
||||
}
|
||||
case 'endAt': {
|
||||
card.setEnd(currentDateTimeUpdate);
|
||||
break;
|
||||
}
|
||||
case 'dueAt': {
|
||||
card.setDue(currentDateTimeUpdate);
|
||||
break;
|
||||
}
|
||||
case 'receivedAt': {
|
||||
card.setReceived(currentDateTimeUpdate);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(action.actionType === 'removeDate'){
|
||||
switch (action.dateField){
|
||||
case 'startAt': {
|
||||
card.unsetStart();
|
||||
break;
|
||||
}
|
||||
case 'endAt': {
|
||||
card.unsetEnd();
|
||||
break;
|
||||
}
|
||||
case 'dueAt': {
|
||||
card.unsetDue();
|
||||
break;
|
||||
}
|
||||
case 'receivedAt': {
|
||||
card.unsetReceived();
|
||||
break;
|
||||
}
|
||||
if (action.actionType === 'removeDate') {
|
||||
switch (action.dateField) {
|
||||
case 'startAt': {
|
||||
card.unsetStart();
|
||||
break;
|
||||
}
|
||||
case 'endAt': {
|
||||
card.unsetEnd();
|
||||
break;
|
||||
}
|
||||
case 'dueAt': {
|
||||
card.unsetDue();
|
||||
break;
|
||||
}
|
||||
case 'receivedAt': {
|
||||
card.unsetReceived();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(action.actionType === 'archive'){
|
||||
if (action.actionType === 'archive') {
|
||||
card.archive();
|
||||
}
|
||||
if(action.actionType === 'unarchive'){
|
||||
if (action.actionType === 'unarchive') {
|
||||
card.restore();
|
||||
}
|
||||
if(action.actionType === 'setColor'){
|
||||
if (action.actionType === 'setColor') {
|
||||
card.setColor(action.selectedColor);
|
||||
}
|
||||
if(action.actionType === 'addLabel'){
|
||||
if (action.actionType === 'addLabel') {
|
||||
card.addLabel(action.labelId);
|
||||
}
|
||||
if(action.actionType === 'removeLabel'){
|
||||
if (action.actionType === 'removeLabel') {
|
||||
card.removeLabel(action.labelId);
|
||||
}
|
||||
if(action.actionType === 'addMember'){
|
||||
const memberId = Users.findOne({username:action.username})._id;
|
||||
if (action.actionType === 'addMember') {
|
||||
const memberId = Users.findOne({ username: action.username })._id;
|
||||
card.assignMember(memberId);
|
||||
}
|
||||
if(action.actionType === 'removeMember'){
|
||||
if(action.username === '*'){
|
||||
if (action.actionType === 'removeMember') {
|
||||
if (action.username === '*') {
|
||||
const members = card.members;
|
||||
for(let i = 0; i< members.length; i++){
|
||||
for (let i = 0; i < members.length; i++) {
|
||||
card.unassignMember(members[i]);
|
||||
}
|
||||
}else{
|
||||
const memberId = Users.findOne({username:action.username})._id;
|
||||
} else {
|
||||
const memberId = Users.findOne({ username: action.username })._id;
|
||||
card.unassignMember(memberId);
|
||||
}
|
||||
}
|
||||
if(action.actionType === 'checkAll'){
|
||||
const checkList = Checklists.findOne({'title':action.checklistName, 'cardId':card._id});
|
||||
if (action.actionType === 'checkAll') {
|
||||
const checkList = Checklists.findOne({
|
||||
title: action.checklistName,
|
||||
cardId: card._id,
|
||||
});
|
||||
checkList.checkAllItems();
|
||||
}
|
||||
if(action.actionType === 'uncheckAll'){
|
||||
const checkList = Checklists.findOne({'title':action.checklistName, 'cardId':card._id});
|
||||
if (action.actionType === 'uncheckAll') {
|
||||
const checkList = Checklists.findOne({
|
||||
title: action.checklistName,
|
||||
cardId: card._id,
|
||||
});
|
||||
checkList.uncheckAllItems();
|
||||
}
|
||||
if(action.actionType === 'checkItem'){
|
||||
const checkList = Checklists.findOne({'title':action.checklistName, 'cardId':card._id});
|
||||
const checkItem = ChecklistItems.findOne({'title':action.checkItemName, 'checkListId':checkList._id});
|
||||
if (action.actionType === 'checkItem') {
|
||||
const checkList = Checklists.findOne({
|
||||
title: action.checklistName,
|
||||
cardId: card._id,
|
||||
});
|
||||
const checkItem = ChecklistItems.findOne({
|
||||
title: action.checkItemName,
|
||||
checkListId: checkList._id,
|
||||
});
|
||||
checkItem.check();
|
||||
}
|
||||
if(action.actionType === 'uncheckItem'){
|
||||
const checkList = Checklists.findOne({'title':action.checklistName, 'cardId':card._id});
|
||||
const checkItem = ChecklistItems.findOne({'title':action.checkItemName, 'checkListId':checkList._id});
|
||||
if (action.actionType === 'uncheckItem') {
|
||||
const checkList = Checklists.findOne({
|
||||
title: action.checklistName,
|
||||
cardId: card._id,
|
||||
});
|
||||
const checkItem = ChecklistItems.findOne({
|
||||
title: action.checkItemName,
|
||||
checkListId: checkList._id,
|
||||
});
|
||||
checkItem.uncheck();
|
||||
}
|
||||
if(action.actionType === 'addChecklist'){
|
||||
Checklists.insert({'title':action.checklistName, 'cardId':card._id, 'sort':0});
|
||||
if (action.actionType === 'addChecklist') {
|
||||
Checklists.insert({
|
||||
title: action.checklistName,
|
||||
cardId: card._id,
|
||||
sort: 0,
|
||||
});
|
||||
}
|
||||
if(action.actionType === 'removeChecklist'){
|
||||
Checklists.remove({'title':action.checklistName, 'cardId':card._id, 'sort':0});
|
||||
if (action.actionType === 'removeChecklist') {
|
||||
Checklists.remove({
|
||||
title: action.checklistName,
|
||||
cardId: card._id,
|
||||
sort: 0,
|
||||
});
|
||||
}
|
||||
if(action.actionType === 'addSwimlane'){
|
||||
if (action.actionType === 'addSwimlane') {
|
||||
Swimlanes.insert({
|
||||
title: action.swimlaneName,
|
||||
boardId,
|
||||
sort: 0,
|
||||
});
|
||||
}
|
||||
if(action.actionType === 'addChecklistWithItems'){
|
||||
const checkListId = Checklists.insert({'title':action.checklistName, 'cardId':card._id, 'sort':0});
|
||||
if (action.actionType === 'addChecklistWithItems') {
|
||||
const checkListId = Checklists.insert({
|
||||
title: action.checklistName,
|
||||
cardId: card._id,
|
||||
sort: 0,
|
||||
});
|
||||
const itemsArray = action.checklistItems.split(',');
|
||||
const checkList = Checklists.findOne({_id:checkListId});
|
||||
for(let i = 0; i <itemsArray.length; i++){
|
||||
ChecklistItems.insert({title:itemsArray[i], checklistId:checkListId, cardId:card._id, 'sort':checkList.itemCount()});
|
||||
const checkList = Checklists.findOne({ _id: checkListId });
|
||||
for (let i = 0; i < itemsArray.length; i++) {
|
||||
ChecklistItems.insert({
|
||||
title: itemsArray[i],
|
||||
checklistId: checkListId,
|
||||
cardId: card._id,
|
||||
sort: checkList.itemCount(),
|
||||
});
|
||||
}
|
||||
}
|
||||
if(action.actionType === 'createCard'){
|
||||
const list = Lists.findOne({title:action.listName, boardId});
|
||||
if (action.actionType === 'createCard') {
|
||||
const list = Lists.findOne({ title: action.listName, boardId });
|
||||
let listId = '';
|
||||
let swimlaneId = '';
|
||||
const swimlane = Swimlanes.findOne({title:action.swimlaneName, boardId});
|
||||
if(list === undefined){
|
||||
const swimlane = Swimlanes.findOne({
|
||||
title: action.swimlaneName,
|
||||
boardId,
|
||||
});
|
||||
if (list === undefined) {
|
||||
listId = '';
|
||||
}else{
|
||||
} else {
|
||||
listId = list._id;
|
||||
}
|
||||
if(swimlane === undefined){
|
||||
swimlaneId = Swimlanes.findOne({title:'Default', boardId})._id;
|
||||
}else{
|
||||
if (swimlane === undefined) {
|
||||
swimlaneId = Swimlanes.findOne({ title: 'Default', boardId })._id;
|
||||
} else {
|
||||
swimlaneId = swimlane._id;
|
||||
}
|
||||
Cards.insert({title:action.cardName, listId, swimlaneId, sort:0, boardId});
|
||||
Cards.insert({
|
||||
title: action.cardName,
|
||||
listId,
|
||||
swimlaneId,
|
||||
sort: 0,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,58 +1,69 @@
|
|||
TriggersDef = {
|
||||
createCard:{
|
||||
matchingFields: ['boardId', 'listName', 'userId', 'swimlaneName', 'cardTitle'],
|
||||
createCard: {
|
||||
matchingFields: [
|
||||
'boardId',
|
||||
'listName',
|
||||
'userId',
|
||||
'swimlaneName',
|
||||
'cardTitle',
|
||||
],
|
||||
},
|
||||
moveCard:{
|
||||
matchingFields: ['boardId', 'listName', 'oldListName', 'userId', 'swimlaneName', 'cardTitle'],
|
||||
moveCard: {
|
||||
matchingFields: [
|
||||
'boardId',
|
||||
'listName',
|
||||
'oldListName',
|
||||
'userId',
|
||||
'swimlaneName',
|
||||
'cardTitle',
|
||||
],
|
||||
},
|
||||
archivedCard:{
|
||||
archivedCard: {
|
||||
matchingFields: ['boardId', 'userId', 'cardTitle'],
|
||||
},
|
||||
restoredCard:{
|
||||
restoredCard: {
|
||||
matchingFields: ['boardId', 'userId', 'cardTitle'],
|
||||
},
|
||||
joinMember:{
|
||||
joinMember: {
|
||||
matchingFields: ['boardId', 'username', 'userId'],
|
||||
},
|
||||
unjoinMember:{
|
||||
unjoinMember: {
|
||||
matchingFields: ['boardId', 'username', 'userId'],
|
||||
},
|
||||
addChecklist:{
|
||||
addChecklist: {
|
||||
matchingFields: ['boardId', 'checklistName', 'userId'],
|
||||
},
|
||||
removeChecklist:{
|
||||
removeChecklist: {
|
||||
matchingFields: ['boardId', 'checklistName', 'userId'],
|
||||
},
|
||||
completeChecklist:{
|
||||
completeChecklist: {
|
||||
matchingFields: ['boardId', 'checklistName', 'userId'],
|
||||
},
|
||||
uncompleteChecklist:{
|
||||
uncompleteChecklist: {
|
||||
matchingFields: ['boardId', 'checklistName', 'userId'],
|
||||
},
|
||||
addedChecklistItem:{
|
||||
addedChecklistItem: {
|
||||
matchingFields: ['boardId', 'checklistItemName', 'userId'],
|
||||
},
|
||||
removedChecklistItem:{
|
||||
removedChecklistItem: {
|
||||
matchingFields: ['boardId', 'checklistItemName', 'userId'],
|
||||
},
|
||||
checkedItem:{
|
||||
checkedItem: {
|
||||
matchingFields: ['boardId', 'checklistItemName', 'userId'],
|
||||
},
|
||||
uncheckedItem:{
|
||||
uncheckedItem: {
|
||||
matchingFields: ['boardId', 'checklistItemName', 'userId'],
|
||||
},
|
||||
addAttachment:{
|
||||
addAttachment: {
|
||||
matchingFields: ['boardId', 'userId'],
|
||||
},
|
||||
deleteAttachment:{
|
||||
deleteAttachment: {
|
||||
matchingFields: ['boardId', 'userId'],
|
||||
},
|
||||
addedLabel:{
|
||||
addedLabel: {
|
||||
matchingFields: ['boardId', 'labelId', 'userId'],
|
||||
},
|
||||
removedLabel:{
|
||||
removedLabel: {
|
||||
matchingFields: ['boardId', 'labelId', 'userId'],
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue