1
0
Fork 0
mirror of https://github.com/plankanban/planka.git synced 2025-07-24 15:49:46 +02:00

feat: Version 2

Closes #627, closes #1047
This commit is contained in:
Maksim Eltyshev 2025-05-10 02:09:06 +02:00
parent ad7fb51cfa
commit 2ee1166747
1557 changed files with 76832 additions and 47042 deletions

View file

@ -0,0 +1,171 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
const escapeMarkdown = require('escape-markdown');
const escapeHtml = require('escape-html');
const buildAndSendNotifications = async (services, board, card, comment, actorUser, t) => {
const markdownCardLink = `[${escapeMarkdown(card.name)}](${sails.config.custom.baseUrl}/cards/${card.id})`;
const htmlCardLink = `<a href="${sails.config.custom.baseUrl}/cards/${card.id}}">${escapeHtml(card.name)}</a>`;
const commentText = _.truncate(comment.text);
await sails.helpers.utils.sendNotifications(services, t('New Comment'), {
text: `${t(
'%s left a new comment to %s on %s',
actorUser.name,
card.name,
board.name,
)}:\n${commentText}`,
markdown: `${t(
'%s left a new comment to %s on %s',
escapeMarkdown(actorUser.name),
markdownCardLink,
escapeMarkdown(board.name),
)}:\n\n*${escapeMarkdown(commentText)}*`,
html: `${t(
'%s left a new comment to %s on %s',
escapeHtml(actorUser.name),
htmlCardLink,
escapeHtml(board.name),
)}:\n\n<i>${escapeHtml(commentText)}</i>`,
});
};
module.exports = {
inputs: {
values: {
type: 'ref',
required: true,
},
project: {
type: 'ref',
required: true,
},
board: {
type: 'ref',
required: true,
},
list: {
type: 'ref',
required: true,
},
request: {
type: 'ref',
},
},
async fn(inputs) {
const { values } = inputs;
const comment = await Comment.qm.createOne({
...values,
cardId: values.card.id,
userId: values.user.id,
});
sails.sockets.broadcast(
`board:${inputs.board.id}`,
'commentCreate',
{
item: comment,
included: {
users: [sails.helpers.users.presentOne(values.user, {})],
},
},
inputs.request,
);
sails.helpers.utils.sendWebhooks.with({
event: 'commentCreate',
buildData: () => ({
item: comment,
included: {
projects: [inputs.project],
boards: [inputs.board],
lists: [inputs.list],
cards: [values.card],
},
}),
user: values.user,
});
const cardSubscriptionUserIds = await sails.helpers.cards.getSubscriptionUserIds(
comment.cardId,
comment.userId,
);
const boardSubscriptionUserIds = await sails.helpers.boards.getSubscriptionUserIds(
inputs.board.id,
comment.userId,
);
const notifiableUserIds = _.union(cardSubscriptionUserIds, boardSubscriptionUserIds);
await Promise.all(
notifiableUserIds.map((userId) =>
sails.helpers.notifications.createOne.with({
values: {
userId,
comment,
type: Notification.Types.COMMENT_CARD,
data: {
card: _.pick(values.card, ['name']),
text: comment.text,
},
creatorUser: values.user,
card: values.card,
},
project: inputs.project,
board: inputs.board,
list: inputs.list,
}),
),
);
if (values.user.subscribeToCardWhenCommenting) {
let cardSubscription;
try {
cardSubscription = await CardSubscription.qm.createOne({
cardId: comment.cardId,
userId: comment.userId,
});
} catch (error) {
if (error.code !== 'E_UNIQUE') {
throw error;
}
}
if (cardSubscription) {
sails.sockets.broadcast(`user:${comment.userId}`, 'cardUpdate', {
item: {
id: comment.cardId,
isSubscribed: true,
},
});
// TODO: send webhooks
}
}
const notificationServices = await NotificationService.qm.getByBoardId(inputs.board.id);
if (notificationServices.length > 0) {
const services = notificationServices.map((notificationService) =>
_.pick(notificationService, ['url', 'format']),
);
buildAndSendNotifications(
services,
inputs.board,
values.card,
comment,
values.user,
sails.helpers.utils.makeTranslator(),
);
}
return comment;
},
};

View file

@ -0,0 +1,67 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
module.exports = {
inputs: {
record: {
type: 'ref',
required: true,
},
project: {
type: 'ref',
required: true,
},
board: {
type: 'ref',
required: true,
},
list: {
type: 'ref',
required: true,
},
card: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: {
type: 'ref',
},
},
async fn(inputs) {
const comment = await Comment.qm.deleteOne(inputs.record.id);
if (comment) {
sails.sockets.broadcast(
`board:${inputs.board.id}`,
'commentDelete',
{
item: comment,
},
inputs.request,
);
sails.helpers.utils.sendWebhooks.with({
event: 'commentDelete',
buildData: () => ({
item: comment,
included: {
projects: [inputs.project],
boards: [inputs.board],
lists: [inputs.list],
cards: [inputs.card],
},
}),
user: inputs.actorUser,
});
}
return comment;
},
};

View file

@ -0,0 +1,39 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
module.exports = {
inputs: {
id: {
type: 'string',
required: true,
},
},
exits: {
pathNotFound: {},
},
async fn(inputs) {
const comment = await Comment.qm.getOneById(inputs.id);
if (!comment) {
throw 'pathNotFound';
}
const pathToProject = await sails.helpers.cards
.getPathToProjectById(comment.cardId)
.intercept('pathNotFound', (nodes) => ({
pathNotFound: {
comment,
...nodes,
},
}));
return {
comment,
...pathToProject,
};
},
};

View file

@ -0,0 +1,76 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
module.exports = {
inputs: {
record: {
type: 'ref',
required: true,
},
values: {
type: 'json',
required: true,
},
project: {
type: 'ref',
required: true,
},
board: {
type: 'ref',
required: true,
},
list: {
type: 'ref',
required: true,
},
card: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: {
type: 'ref',
},
},
async fn(inputs) {
const { values } = inputs;
const comment = await Comment.qm.updateOne(inputs.record.id, values);
if (comment) {
sails.sockets.broadcast(
`board:${inputs.board.id}`,
'commentUpdate',
{
item: comment,
},
inputs.request,
);
sails.helpers.utils.sendWebhooks.with({
event: 'commentUpdate',
buildData: () => ({
item: comment,
included: {
projects: [inputs.project],
boards: [inputs.board],
lists: [inputs.list],
cards: [inputs.card],
},
}),
buildPrevData: () => ({
item: inputs.record,
}),
user: inputs.actorUser,
});
}
return comment;
},
};