mirror of
https://github.com/plankanban/planka.git
synced 2025-07-18 20:59:44 +02:00
feat: Add board activity log
This commit is contained in:
parent
777ff467f3
commit
86cfd155f2
72 changed files with 833 additions and 169 deletions
68
server/api/controllers/actions/index-in-board.js
Executable file
68
server/api/controllers/actions/index-in-board.js
Executable file
|
@ -0,0 +1,68 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
const { idInput } = require('../../../utils/inputs');
|
||||
|
||||
const Errors = {
|
||||
BOARD_NOT_FOUND: {
|
||||
boardNotFound: 'Board not found',
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
inputs: {
|
||||
boardId: {
|
||||
...idInput,
|
||||
required: true,
|
||||
},
|
||||
beforeId: idInput,
|
||||
},
|
||||
|
||||
exits: {
|
||||
boardNotFound: {
|
||||
responseType: 'notFound',
|
||||
},
|
||||
},
|
||||
|
||||
async fn(inputs) {
|
||||
const { currentUser } = this.req;
|
||||
|
||||
const { board, project } = await sails.helpers.boards
|
||||
.getPathToProjectById(inputs.boardId)
|
||||
.intercept('pathNotFound', () => Errors.BOARD_NOT_FOUND);
|
||||
|
||||
const boardMembership = await BoardMembership.qm.getOneByBoardIdAndUserId(
|
||||
board.id,
|
||||
currentUser.id,
|
||||
);
|
||||
|
||||
if (!boardMembership) {
|
||||
if (currentUser.role !== User.Roles.ADMIN || project.ownerProjectManagerId) {
|
||||
const isProjectManager = await sails.helpers.users.isProjectManager(
|
||||
currentUser.id,
|
||||
project.id,
|
||||
);
|
||||
|
||||
if (!isProjectManager) {
|
||||
throw Errors.BOARD_NOT_FOUND; // Forbidden
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const actions = await Action.qm.getByBoardId(board.id, {
|
||||
beforeId: inputs.beforeId,
|
||||
});
|
||||
|
||||
const userIds = sails.helpers.utils.mapRecords(actions, 'userId', true, true);
|
||||
const users = await User.qm.getByIds(userIds);
|
||||
|
||||
return {
|
||||
items: actions,
|
||||
included: {
|
||||
users: sails.helpers.users.presentMany(users, currentUser),
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
|
@ -115,6 +115,7 @@ module.exports = {
|
|||
|
||||
const action = await Action.qm.createOne({
|
||||
...values,
|
||||
boardId: values.card.boardId,
|
||||
cardId: values.card.id,
|
||||
userId: values.user.id,
|
||||
});
|
||||
|
@ -149,10 +150,7 @@ module.exports = {
|
|||
values: {
|
||||
action,
|
||||
type: action.type,
|
||||
data: {
|
||||
...action.data,
|
||||
card: _.pick(values.card, ['name']),
|
||||
},
|
||||
data: action.data,
|
||||
userId: action.data.user.id,
|
||||
creatorUser: values.user,
|
||||
card: values.card,
|
||||
|
@ -182,10 +180,7 @@ module.exports = {
|
|||
userId,
|
||||
action,
|
||||
type: action.type,
|
||||
data: {
|
||||
...action.data,
|
||||
card: _.pick(values.card, ['name']),
|
||||
},
|
||||
data: action.data,
|
||||
creatorUser: values.user,
|
||||
card: values.card,
|
||||
},
|
||||
|
|
|
@ -35,6 +35,15 @@ module.exports = {
|
|||
|
||||
await sails.helpers.lists.deleteRelated(lists);
|
||||
|
||||
await Action.qm.update(
|
||||
{
|
||||
boardId: boardIdOrIds,
|
||||
},
|
||||
{
|
||||
boardId: null,
|
||||
},
|
||||
);
|
||||
|
||||
await NotificationService.qm.delete({
|
||||
boardId: boardIdOrIds,
|
||||
});
|
||||
|
|
|
@ -110,6 +110,7 @@ module.exports = {
|
|||
type: Action.Types.ADD_MEMBER_TO_CARD,
|
||||
data: {
|
||||
user: _.pick(values.user, ['id', 'name']),
|
||||
card: _.pick(values.card, ['name']),
|
||||
},
|
||||
user: inputs.actorUser,
|
||||
card: values.card,
|
||||
|
|
|
@ -86,6 +86,7 @@ module.exports = {
|
|||
type: Action.Types.REMOVE_MEMBER_FROM_CARD,
|
||||
data: {
|
||||
user: _.pick(inputs.user, ['id', 'name']),
|
||||
card: _.pick(inputs.card, ['name']),
|
||||
},
|
||||
user: inputs.actorUser,
|
||||
card: inputs.card,
|
||||
|
|
|
@ -124,6 +124,7 @@ module.exports = {
|
|||
card,
|
||||
type: Action.Types.CREATE_CARD,
|
||||
data: {
|
||||
card: _.pick(card, ['name']),
|
||||
list: _.pick(values.list, ['id', 'type', 'name']),
|
||||
},
|
||||
user: values.creatorUser,
|
||||
|
|
|
@ -276,6 +276,7 @@ module.exports = {
|
|||
card,
|
||||
type: Action.Types.CREATE_CARD, // TODO: introduce separate type?
|
||||
data: {
|
||||
card: _.pick(card, ['name']),
|
||||
list: _.pick(inputs.list, ['id', 'type', 'name']),
|
||||
},
|
||||
user: values.creatorUser,
|
||||
|
|
|
@ -463,6 +463,7 @@ module.exports = {
|
|||
card,
|
||||
type: Action.Types.MOVE_CARD,
|
||||
data: {
|
||||
card: _.pick(card, ['name']),
|
||||
fromList: _.pick(inputs.list, ['id', 'type', 'name']),
|
||||
toList: _.pick(values.list, ['id', 'type', 'name']),
|
||||
},
|
||||
|
|
|
@ -138,6 +138,7 @@ module.exports = {
|
|||
values: {
|
||||
type: task.isCompleted ? Action.Types.COMPLETE_TASK : Action.Types.UNCOMPLETE_TASK,
|
||||
data: {
|
||||
card: _.pick(inputs.card, ['name']),
|
||||
task: _.pick(task, ['id', 'name']),
|
||||
},
|
||||
user: inputs.actorUser,
|
||||
|
|
|
@ -11,6 +11,20 @@ const create = (arrayOfValues) => Action.createEach(arrayOfValues).fetch();
|
|||
|
||||
const createOne = (values) => Action.create({ ...values }).fetch();
|
||||
|
||||
const getByBoardId = (boardId, { beforeId } = {}) => {
|
||||
const criteria = {
|
||||
boardId,
|
||||
};
|
||||
|
||||
if (beforeId) {
|
||||
criteria.id = {
|
||||
'<': beforeId,
|
||||
};
|
||||
}
|
||||
|
||||
return Action.find(criteria).sort('id DESC').limit(LIMIT);
|
||||
};
|
||||
|
||||
const getByCardId = (cardId, { beforeId } = {}) => {
|
||||
const criteria = {
|
||||
cardId,
|
||||
|
@ -33,6 +47,7 @@ const delete_ = (criteria) => Action.destroy(criteria).fetch();
|
|||
module.exports = {
|
||||
create,
|
||||
createOne,
|
||||
getByBoardId,
|
||||
getByCardId,
|
||||
update,
|
||||
delete: delete_,
|
||||
|
|
|
@ -52,6 +52,10 @@ module.exports = {
|
|||
// ╠═╣╚═╗╚═╗║ ║║ ║╠═╣ ║ ║║ ║║║║╚═╗
|
||||
// ╩ ╩╚═╝╚═╝╚═╝╚═╝╩╩ ╩ ╩ ╩╚═╝╝╚╝╚═╝
|
||||
|
||||
boardId: {
|
||||
model: 'Board',
|
||||
columnName: 'board_id',
|
||||
},
|
||||
cardId: {
|
||||
model: 'Card',
|
||||
required: true,
|
||||
|
|
|
@ -163,7 +163,8 @@ module.exports.routes = {
|
|||
'PATCH /api/comments/:id': 'comments/update',
|
||||
'DELETE /api/comments/:id': 'comments/delete',
|
||||
|
||||
'GET /api/cards/:cardId/actions': 'actions/index',
|
||||
'GET /api/boards/:boardId/actions': 'actions/index-in-board',
|
||||
'GET /api/cards/:cardId/actions': 'actions/index-in-card',
|
||||
|
||||
'GET /api/notifications': 'notifications/index',
|
||||
'GET /api/notifications/:id': 'notifications/show',
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
exports.up = async (knex) => {
|
||||
await knex.schema.alterTable('action', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.bigInteger('board_id');
|
||||
|
||||
/* Indexes */
|
||||
|
||||
table.index('board_id');
|
||||
});
|
||||
|
||||
return knex.raw(`
|
||||
UPDATE action
|
||||
SET
|
||||
board_id = card.board_id,
|
||||
data = data || jsonb_build_object('card', jsonb_build_object('name', card.name))
|
||||
FROM card
|
||||
WHERE action.card_id = card.id;
|
||||
`);
|
||||
};
|
||||
|
||||
exports.down = (knex) =>
|
||||
knex.schema.table('action', (table) => {
|
||||
table.dropColumn('board_id');
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue