Compare commits

...
7 Commits
106 changed files with 1450 additions and 177 deletions
+53
View File
@@ -0,0 +1,53 @@
name: "🐛 Bug Report"
description: Report a bug found while using Planka
title: "[Bug]: "
labels: ["Type: Bug", "Status: Triage"]
body:
- type: dropdown
id: issue-type
attributes:
label: Where is the problem occurring?
description: Select the part of the application where you encountered the issue.
options:
- "I encountered the problem while using the application (Frontend)"
- "I encountered the problem while interacting with the server (Backend)"
- "I'm not sure"
- type: dropdown
id: browsers
attributes:
label: What browsers are you seeing the problem on?
multiple: true
options:
- Brave
- Chrome
- Firefox
- Microsoft Edge
- Safari
- Other
- type: textarea
id: current-behavior
attributes:
label: Current behaviour
description: A description of what is currently happening, including screenshots and other useful information (**DO NOT INCLUDE PRIVATE INFORMATION**).
placeholder: Currently...
validations:
required: true
- type: textarea
id: desired-behavior
attributes:
label: Desired behaviour
description: A clear description of what you think should happen.
placeholder: In this situation, I expected ...
- type: textarea
id: reproduction
attributes:
label: Steps to reproduce
description: Clearly describe which steps or actions you have taken to arrive at the problem. If you have some experience with the code, please link to the specific pieces of code.
placeholder: I did X, then Y, before arriving at Z, when ERROR ...
validations:
required: true
- type: textarea
id: other
attributes:
label: Other information
description: Any other details?
@@ -0,0 +1,33 @@
name: "✨ Feature Request"
description: Suggest a feature or enhancement to improve Planka.
labels: ["Type: Idea"]
body:
- type: dropdown
id: idea-type
attributes:
label: Is this a feature for the backend or frontend?
multiple: true
options:
- Backend
- Frontend
validations:
required: true
- type: textarea
id: feature
attributes:
label: What would you like?
description: A clear description of the feature or enhancement wanted.
placeholder: I'd like to be able to...
validations:
required: true
- type: textarea
id: reason
attributes:
label: Why is this needed?
description: A clear description of why this would be useful to have.
placeholder: I want this because...
- type: textarea
id: other
attributes:
label: Other information
placeholder: Any other details?
+2 -2
View File
@@ -15,13 +15,13 @@ type: application
# This is the chart version. This version number should be incremented each time you make changes # This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version. # to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/) # Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.1.33 version: 0.1.35
# This is the version number of the application being deployed. This version number should be # This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to # incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using. # follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes. # It is recommended to use it with quotes.
appVersion: "1.17.5" appVersion: "1.18.1"
dependencies: dependencies:
- alias: postgresql - alias: postgresql
+1 -1
View File
@@ -1 +1 @@
REACT_APP_VERSION=1.17.5 REACT_APP_VERSION=1.18.1
+2 -1
View File
@@ -5,6 +5,7 @@ import TextareaAutosize from 'react-textarea-autosize';
import { Button, Form, TextArea } from 'semantic-ui-react'; import { Button, Form, TextArea } from 'semantic-ui-react';
import { useClosableForm, useField } from '../../hooks'; import { useClosableForm, useField } from '../../hooks';
import { focusEnd } from '../../utils/element-helpers';
import styles from './NameEdit.module.scss'; import styles from './NameEdit.module.scss';
@@ -79,7 +80,7 @@ const NameEdit = React.forwardRef(({ children, defaultValue, onUpdate }, ref) =>
useEffect(() => { useEffect(() => {
if (isOpened) { if (isOpened) {
field.current.ref.current.focus(); focusEnd(field.current.ref.current);
} }
}, [isOpened]); }, [isOpened]);
@@ -6,6 +6,7 @@ import TextareaAutosize from 'react-textarea-autosize';
import { Button, Form, TextArea } from 'semantic-ui-react'; import { Button, Form, TextArea } from 'semantic-ui-react';
import { useForm } from '../../../hooks'; import { useForm } from '../../../hooks';
import { focusEnd } from '../../../utils/element-helpers';
import styles from './CommentEdit.module.scss'; import styles from './CommentEdit.module.scss';
@@ -70,7 +71,7 @@ const CommentEdit = React.forwardRef(({ children, defaultData, onUpdate }, ref)
useEffect(() => { useEffect(() => {
if (isOpened) { if (isOpened) {
textField.current.ref.current.focus(); focusEnd(textField.current.ref.current);
} }
}, [isOpened]); }, [isOpened]);
@@ -5,6 +5,7 @@ import TextareaAutosize from 'react-textarea-autosize';
import { Button, Form, TextArea } from 'semantic-ui-react'; import { Button, Form, TextArea } from 'semantic-ui-react';
import { useField } from '../../../hooks'; import { useField } from '../../../hooks';
import { focusEnd } from '../../../utils/element-helpers';
import styles from './NameEdit.module.scss'; import styles from './NameEdit.module.scss';
@@ -65,7 +66,7 @@ const NameEdit = React.forwardRef(({ children, defaultValue, onUpdate }, ref) =>
useEffect(() => { useEffect(() => {
if (isOpened) { if (isOpened) {
field.current.ref.current.focus(); focusEnd(field.current.ref.current);
} }
}, [isOpened]); }, [isOpened]);
+2 -1
View File
@@ -4,6 +4,7 @@ import TextareaAutosize from 'react-textarea-autosize';
import { TextArea } from 'semantic-ui-react'; import { TextArea } from 'semantic-ui-react';
import { useField } from '../../hooks'; import { useField } from '../../hooks';
import { focusEnd } from '../../utils/element-helpers';
import styles from './NameEdit.module.scss'; import styles from './NameEdit.module.scss';
@@ -71,7 +72,7 @@ const NameEdit = React.forwardRef(({ children, defaultValue, onUpdate }, ref) =>
useEffect(() => { useEffect(() => {
if (isOpened) { if (isOpened) {
field.current.ref.current.select(); focusEnd(field.current.ref.current);
} }
}, [isOpened]); }, [isOpened]);
+5
View File
@@ -0,0 +1,5 @@
// eslint-disable-next-line import/prefer-default-export
export const focusEnd = (element) => {
element.focus();
element.setSelectionRange(element.value.length + 1, element.value.length + 1);
};
+9
View File
@@ -45,6 +45,15 @@ services:
# - SMTP_PASSWORD= # - SMTP_PASSWORD=
# - SMTP_FROM="Demo Demo" <demo@demo.demo> # - SMTP_FROM="Demo Demo" <demo@demo.demo>
# Optional fields: accessToken, events, excludedEvents
# - |
# WEBHOOKS=[{
# "url": "http://localhost:3001",
# "accessToken": "notaccesstoken",
# "events": ["cardCreate", "cardUpdate", "cardDelete"],
# "excludedEvents": ["notificationCreate", "notificationUpdate"]
# }]
# - SLACK_BOT_TOKEN= # - SLACK_BOT_TOKEN=
# - SLACK_CHANNEL_ID= # - SLACK_CHANNEL_ID=
+9
View File
@@ -53,6 +53,15 @@ services:
# - SMTP_PASSWORD= # - SMTP_PASSWORD=
# - SMTP_FROM="Demo Demo" <demo@demo.demo> # - SMTP_FROM="Demo Demo" <demo@demo.demo>
# Optional fields: accessToken, events, excludedEvents
# - |
# WEBHOOKS=[{
# "url": "http://localhost:3001",
# "accessToken": "notaccesstoken",
# "events": ["cardCreate", "cardUpdate", "cardDelete"],
# "excludedEvents": ["notificationCreate", "notificationUpdate"]
# }]
# - SLACK_BOT_TOKEN= # - SLACK_BOT_TOKEN=
# - SLACK_CHANNEL_ID= # - SLACK_CHANNEL_ID=
depends_on: depends_on:
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "planka", "name": "planka",
"version": "1.17.5", "version": "1.18.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "planka", "name": "planka",
"version": "1.17.5", "version": "1.18.1",
"hasInstallScript": true, "hasInstallScript": true,
"license": "AGPL-3.0", "license": "AGPL-3.0",
"dependencies": { "dependencies": {
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "planka", "name": "planka",
"version": "1.17.5", "version": "1.18.1",
"private": true, "private": true,
"homepage": "https://plankanban.github.io/planka", "homepage": "https://plankanban.github.io/planka",
"repository": { "repository": {
+8
View File
@@ -43,6 +43,14 @@ SECRET_KEY=notsecretkey
# SMTP_PASSWORD= # SMTP_PASSWORD=
# SMTP_FROM="Demo Demo" <demo@demo.demo> # SMTP_FROM="Demo Demo" <demo@demo.demo>
# Optional fields: accessToken, events, excludedEvents
# WEBHOOKS='[{
# "url": "http://localhost:3001",
# "accessToken": "notaccesstoken",
# "events": ["cardCreate", "cardUpdate", "cardDelete"],
# "excludedEvents": ["notificationCreate", "notificationUpdate"]
# }]'
# SLACK_BOT_TOKEN= # SLACK_BOT_TOKEN=
# SLACK_CHANNEL_ID= # SLACK_CHANNEL_ID=
+5 -2
View File
@@ -44,12 +44,12 @@ module.exports = {
async fn(inputs, exits) { async fn(inputs, exits) {
const { currentUser } = this.req; const { currentUser } = this.req;
const { card } = await sails.helpers.cards const { card, list, board, project } = await sails.helpers.cards
.getProjectPath(inputs.cardId) .getProjectPath(inputs.cardId)
.intercept('pathNotFound', () => Errors.CARD_NOT_FOUND); .intercept('pathNotFound', () => Errors.CARD_NOT_FOUND);
const boardMembership = await BoardMembership.findOne({ const boardMembership = await BoardMembership.findOne({
boardId: card.boardId, boardId: board.id,
userId: currentUser.id, userId: currentUser.id,
}); });
@@ -83,6 +83,9 @@ module.exports = {
const fileData = await sails.helpers.attachments.processUploadedFile(file); const fileData = await sails.helpers.attachments.processUploadedFile(file);
const attachment = await sails.helpers.attachments.createOne.with({ const attachment = await sails.helpers.attachments.createOne.with({
project,
board,
list,
values: { values: {
...fileData, ...fileData,
card, card,
+4 -1
View File
@@ -33,7 +33,7 @@ module.exports = {
.intercept('pathNotFound', () => Errors.ATTACHMENT_NOT_FOUND); .intercept('pathNotFound', () => Errors.ATTACHMENT_NOT_FOUND);
let { attachment } = path; let { attachment } = path;
const { card, board } = path; const { card, list, board, project } = path;
const boardMembership = await BoardMembership.findOne({ const boardMembership = await BoardMembership.findOne({
boardId: board.id, boardId: board.id,
@@ -49,9 +49,12 @@ module.exports = {
} }
attachment = await sails.helpers.attachments.deleteOne.with({ attachment = await sails.helpers.attachments.deleteOne.with({
project,
board, board,
list,
card, card,
record: attachment, record: attachment,
actorUser: currentUser,
request: this.req, request: this.req,
}); });
+5 -1
View File
@@ -37,7 +37,7 @@ module.exports = {
.intercept('pathNotFound', () => Errors.ATTACHMENT_NOT_FOUND); .intercept('pathNotFound', () => Errors.ATTACHMENT_NOT_FOUND);
let { attachment } = path; let { attachment } = path;
const { board } = path; const { card, list, board, project } = path;
const boardMembership = await BoardMembership.findOne({ const boardMembership = await BoardMembership.findOne({
boardId: board.id, boardId: board.id,
@@ -56,8 +56,12 @@ module.exports = {
attachment = await sails.helpers.attachments.updateOne.with({ attachment = await sails.helpers.attachments.updateOne.with({
values, values,
project,
board, board,
list,
card,
record: attachment, record: attachment,
actorUser: currentUser,
request: this.req, request: this.req,
}); });
@@ -48,14 +48,11 @@ module.exports = {
async fn(inputs) { async fn(inputs) {
const { currentUser } = this.req; const { currentUser } = this.req;
const { board } = await sails.helpers.boards const { board, project } = await sails.helpers.boards
.getProjectPath(inputs.boardId) .getProjectPath(inputs.boardId)
.intercept('pathNotFound', () => Errors.BOARD_NOT_FOUND); .intercept('pathNotFound', () => Errors.BOARD_NOT_FOUND);
const isProjectManager = await sails.helpers.users.isProjectManager( const isProjectManager = await sails.helpers.users.isProjectManager(currentUser.id, project.id);
currentUser.id,
board.projectId,
);
if (!isProjectManager) { if (!isProjectManager) {
throw Errors.BOARD_NOT_FOUND; // Forbidden throw Errors.BOARD_NOT_FOUND; // Forbidden
@@ -71,11 +68,13 @@ module.exports = {
const boardMembership = await sails.helpers.boardMemberships.createOne const boardMembership = await sails.helpers.boardMemberships.createOne
.with({ .with({
project,
values: { values: {
...values, ...values,
board, board,
user, user,
}, },
actorUser: currentUser,
request: this.req, request: this.req,
}) })
.intercept('userAlreadyBoardMember', () => Errors.USER_ALREADY_BOARD_MEMBER); .intercept('userAlreadyBoardMember', () => Errors.USER_ALREADY_BOARD_MEMBER);
@@ -27,7 +27,7 @@ module.exports = {
.intercept('pathNotFound', () => Errors.BOARD_MEMBERSHIP_NOT_FOUND); .intercept('pathNotFound', () => Errors.BOARD_MEMBERSHIP_NOT_FOUND);
let { boardMembership } = path; let { boardMembership } = path;
const { project } = path; const { board, project } = path;
if (boardMembership.userId !== currentUser.id) { if (boardMembership.userId !== currentUser.id) {
const isProjectManager = await sails.helpers.users.isProjectManager( const isProjectManager = await sails.helpers.users.isProjectManager(
@@ -42,7 +42,9 @@ module.exports = {
boardMembership = await sails.helpers.boardMemberships.deleteOne.with({ boardMembership = await sails.helpers.boardMemberships.deleteOne.with({
project, project,
board,
record: boardMembership, record: boardMembership,
actorUser: currentUser,
request: this.req, request: this.req,
}); });
@@ -35,7 +35,7 @@ module.exports = {
.intercept('pathNotFound', () => Errors.BOARD_MEMBERSHIP_NOT_FOUND); .intercept('pathNotFound', () => Errors.BOARD_MEMBERSHIP_NOT_FOUND);
let { boardMembership } = path; let { boardMembership } = path;
const { project } = path; const { board, project } = path;
const isProjectManager = await sails.helpers.users.isProjectManager(currentUser.id, project.id); const isProjectManager = await sails.helpers.users.isProjectManager(currentUser.id, project.id);
@@ -47,7 +47,10 @@ module.exports = {
boardMembership = await sails.helpers.boardMemberships.updateOne.with({ boardMembership = await sails.helpers.boardMemberships.updateOne.with({
values, values,
project,
board,
record: boardMembership, record: boardMembership,
actorUser: currentUser,
request: this.req, request: this.req,
}); });
+1 -1
View File
@@ -103,7 +103,7 @@ module.exports = {
project, project,
}, },
import: boardImport, import: boardImport,
user: currentUser, actorUser: currentUser,
requestId: inputs.requestId, requestId: inputs.requestId,
request: this.req, request: this.req,
}); });
+7 -5
View File
@@ -22,25 +22,27 @@ module.exports = {
async fn(inputs) { async fn(inputs) {
const { currentUser } = this.req; const { currentUser } = this.req;
let { board } = await sails.helpers.boards const path = await sails.helpers.boards
.getProjectPath(inputs.id) .getProjectPath(inputs.id)
.intercept('pathNotFound', () => Errors.BOARD_NOT_FOUND); .intercept('pathNotFound', () => Errors.BOARD_NOT_FOUND);
let { board } = path;
const { project } = path;
if (!board) { if (!board) {
throw Errors.BOARD_NOT_FOUND; throw Errors.BOARD_NOT_FOUND;
} }
const isProjectManager = await sails.helpers.users.isProjectManager( const isProjectManager = await sails.helpers.users.isProjectManager(currentUser.id, project.id);
currentUser.id,
board.projectId,
);
if (!isProjectManager) { if (!isProjectManager) {
throw Errors.BOARD_NOT_FOUND; // Forbidden throw Errors.BOARD_NOT_FOUND; // Forbidden
} }
board = await sails.helpers.boards.deleteOne.with({ board = await sails.helpers.boards.deleteOne.with({
project,
record: board, record: board,
actorUser: currentUser,
request: this.req, request: this.req,
}); });
+7 -5
View File
@@ -29,18 +29,18 @@ module.exports = {
async fn(inputs) { async fn(inputs) {
const { currentUser } = this.req; const { currentUser } = this.req;
let { board } = await sails.helpers.boards const path = await sails.helpers.boards
.getProjectPath(inputs.id) .getProjectPath(inputs.id)
.intercept('pathNotFound', () => Errors.BOARD_NOT_FOUND); .intercept('pathNotFound', () => Errors.BOARD_NOT_FOUND);
let { board } = path;
const { project } = path;
if (!board) { if (!board) {
throw Errors.BOARD_NOT_FOUND; throw Errors.BOARD_NOT_FOUND;
} }
const isProjectManager = await sails.helpers.users.isProjectManager( const isProjectManager = await sails.helpers.users.isProjectManager(currentUser.id, project.id);
currentUser.id,
board.projectId,
);
if (!isProjectManager) { if (!isProjectManager) {
throw Errors.BOARD_NOT_FOUND; // Forbidden throw Errors.BOARD_NOT_FOUND; // Forbidden
@@ -50,7 +50,9 @@ module.exports = {
board = await sails.helpers.boards.updateOne.with({ board = await sails.helpers.boards.updateOne.with({
values, values,
project,
record: board, record: board,
actorUser: currentUser,
request: this.req, request: this.req,
}); });
+7 -3
View File
@@ -45,12 +45,12 @@ module.exports = {
async fn(inputs) { async fn(inputs) {
const { currentUser } = this.req; const { currentUser } = this.req;
const { card } = await sails.helpers.cards const { card, list, board, project } = await sails.helpers.cards
.getProjectPath(inputs.cardId) .getProjectPath(inputs.cardId)
.intercept('pathNotFound', () => Errors.CARD_NOT_FOUND); .intercept('pathNotFound', () => Errors.CARD_NOT_FOUND);
const boardMembership = await BoardMembership.findOne({ const boardMembership = await BoardMembership.findOne({
boardId: card.boardId, boardId: board.id,
userId: currentUser.id, userId: currentUser.id,
}); });
@@ -64,7 +64,7 @@ module.exports = {
const label = await Label.findOne({ const label = await Label.findOne({
id: inputs.labelId, id: inputs.labelId,
boardId: card.boardId, boardId: board.id,
}); });
if (!label) { if (!label) {
@@ -73,10 +73,14 @@ module.exports = {
const cardLabel = await sails.helpers.cardLabels.createOne const cardLabel = await sails.helpers.cardLabels.createOne
.with({ .with({
project,
board,
list,
values: { values: {
card, card,
label, label,
}, },
actorUser: currentUser,
request: this.req, request: this.req,
}) })
.intercept('labelAlreadyInCard', () => Errors.LABEL_ALREADY_IN_CARD); .intercept('labelAlreadyInCard', () => Errors.LABEL_ALREADY_IN_CARD);
+5 -1
View File
@@ -39,7 +39,7 @@ module.exports = {
async fn(inputs) { async fn(inputs) {
const { currentUser } = this.req; const { currentUser } = this.req;
const { board } = await sails.helpers.cards const { card, list, board, project } = await sails.helpers.cards
.getProjectPath(inputs.cardId) .getProjectPath(inputs.cardId)
.intercept('pathNotFound', () => Errors.CARD_NOT_FOUND); .intercept('pathNotFound', () => Errors.CARD_NOT_FOUND);
@@ -66,8 +66,12 @@ module.exports = {
} }
cardLabel = await sails.helpers.cardLabels.deleteOne.with({ cardLabel = await sails.helpers.cardLabels.deleteOne.with({
project,
board, board,
list,
card,
record: cardLabel, record: cardLabel,
actorUser: currentUser,
request: this.req, request: this.req,
}); });
@@ -45,12 +45,12 @@ module.exports = {
async fn(inputs) { async fn(inputs) {
const { currentUser } = this.req; const { currentUser } = this.req;
const { card } = await sails.helpers.cards const { card, list, board, project } = await sails.helpers.cards
.getProjectPath(inputs.cardId) .getProjectPath(inputs.cardId)
.intercept('pathNotFound', () => Errors.CARD_NOT_FOUND); .intercept('pathNotFound', () => Errors.CARD_NOT_FOUND);
const boardMembership = await BoardMembership.findOne({ const boardMembership = await BoardMembership.findOne({
boardId: card.boardId, boardId: board.id,
userId: currentUser.id, userId: currentUser.id,
}); });
@@ -62,7 +62,7 @@ module.exports = {
throw Errors.NOT_ENOUGH_RIGHTS; throw Errors.NOT_ENOUGH_RIGHTS;
} }
const isBoardMember = await sails.helpers.users.isBoardMember(inputs.userId, card.boardId); const isBoardMember = await sails.helpers.users.isBoardMember(inputs.userId, board.id);
if (!isBoardMember) { if (!isBoardMember) {
throw Errors.USER_NOT_FOUND; throw Errors.USER_NOT_FOUND;
@@ -70,10 +70,14 @@ module.exports = {
const cardMembership = await sails.helpers.cardMemberships.createOne const cardMembership = await sails.helpers.cardMemberships.createOne
.with({ .with({
project,
board,
list,
values: { values: {
card, card,
userId: inputs.userId, userId: inputs.userId,
}, },
actorUser: currentUser,
request: this.req, request: this.req,
}) })
.intercept('userAlreadyCardMember', () => Errors.USER_ALREADY_CARD_MEMBER); .intercept('userAlreadyCardMember', () => Errors.USER_ALREADY_CARD_MEMBER);
@@ -39,7 +39,7 @@ module.exports = {
async fn(inputs) { async fn(inputs) {
const { currentUser } = this.req; const { currentUser } = this.req;
const { board } = await sails.helpers.cards const { card, list, board, project } = await sails.helpers.cards
.getProjectPath(inputs.cardId) .getProjectPath(inputs.cardId)
.intercept('pathNotFound', () => Errors.CARD_NOT_FOUND); .intercept('pathNotFound', () => Errors.CARD_NOT_FOUND);
@@ -66,8 +66,12 @@ module.exports = {
} }
cardMembership = await sails.helpers.cardMemberships.deleteOne.with({ cardMembership = await sails.helpers.cardMemberships.deleteOne.with({
project,
board, board,
list,
card,
record: cardMembership, record: cardMembership,
actorUser: currentUser,
request: this.req, request: this.req,
}); });
+2 -1
View File
@@ -78,7 +78,7 @@ module.exports = {
async fn(inputs) { async fn(inputs) {
const { currentUser } = this.req; const { currentUser } = this.req;
const { board, list } = await sails.helpers.lists const { list, board, project } = await sails.helpers.lists
.getProjectPath(inputs.listId) .getProjectPath(inputs.listId)
.intercept('pathNotFound', () => Errors.LIST_NOT_FOUND); .intercept('pathNotFound', () => Errors.LIST_NOT_FOUND);
@@ -99,6 +99,7 @@ module.exports = {
const card = await sails.helpers.cards.createOne const card = await sails.helpers.cards.createOne
.with({ .with({
project,
board, board,
values: { values: {
...values, ...values,
+9 -3
View File
@@ -28,12 +28,15 @@ module.exports = {
async fn(inputs) { async fn(inputs) {
const { currentUser } = this.req; const { currentUser } = this.req;
let { card } = await sails.helpers.cards const path = await sails.helpers.cards
.getProjectPath(inputs.id) .getProjectPath(inputs.id)
.intercept('pathNotFound', () => Errors.CARD_NOT_FOUND); .intercept('pathNotFound', () => Errors.CARD_NOT_FOUND);
let { card } = path;
const { list, board, project } = path;
const boardMembership = await BoardMembership.findOne({ const boardMembership = await BoardMembership.findOne({
boardId: card.boardId, boardId: board.id,
userId: currentUser.id, userId: currentUser.id,
}); });
@@ -46,8 +49,11 @@ module.exports = {
} }
card = await sails.helpers.cards.deleteOne.with({ card = await sails.helpers.cards.deleteOne.with({
project,
board,
list,
record: card, record: card,
user: currentUser, actorUser: currentUser,
request: this.req, request: this.req,
}); });
+3 -2
View File
@@ -35,12 +35,12 @@ module.exports = {
async fn(inputs) { async fn(inputs) {
const { currentUser } = this.req; const { currentUser } = this.req;
const { card, list, board } = await sails.helpers.cards const { card, list, board, project } = await sails.helpers.cards
.getProjectPath(inputs.id) .getProjectPath(inputs.id)
.intercept('pathNotFound', () => Errors.CARD_NOT_FOUND); .intercept('pathNotFound', () => Errors.CARD_NOT_FOUND);
const boardMembership = await BoardMembership.findOne({ const boardMembership = await BoardMembership.findOne({
boardId: card.boardId, boardId: board.id,
userId: currentUser.id, userId: currentUser.id,
}); });
@@ -60,6 +60,7 @@ module.exports = {
cardLabels, cardLabels,
tasks, tasks,
} = await sails.helpers.cards.duplicateOne.with({ } = await sails.helpers.cards.duplicateOne.with({
project,
board, board,
list, list,
record: card, record: card,
+7 -3
View File
@@ -118,7 +118,7 @@ module.exports = {
.intercept('pathNotFound', () => Errors.CARD_NOT_FOUND); .intercept('pathNotFound', () => Errors.CARD_NOT_FOUND);
let { card } = path; let { card } = path;
const { list, board } = path; const { list, board, project } = path;
let boardMembership = await BoardMembership.findOne({ let boardMembership = await BoardMembership.findOne({
boardId: board.id, boardId: board.id,
@@ -133,9 +133,11 @@ module.exports = {
throw Errors.NOT_ENOUGH_RIGHTS; throw Errors.NOT_ENOUGH_RIGHTS;
} }
let nextProject;
let nextBoard; let nextBoard;
if (!_.isUndefined(inputs.boardId)) { if (!_.isUndefined(inputs.boardId)) {
({ board: nextBoard } = await sails.helpers.boards ({ board: nextBoard, project: nextProject } = await sails.helpers.boards
.getProjectPath(inputs.boardId) .getProjectPath(inputs.boardId)
.intercept('pathNotFound', () => Errors.BOARD_NOT_FOUND)); .intercept('pathNotFound', () => Errors.BOARD_NOT_FOUND));
@@ -177,15 +179,17 @@ module.exports = {
card = await sails.helpers.cards.updateOne card = await sails.helpers.cards.updateOne
.with({ .with({
project,
board, board,
list, list,
record: card, record: card,
values: { values: {
...values, ...values,
project: nextProject,
board: nextBoard, board: nextBoard,
list: nextList, list: nextList,
}, },
user: currentUser, actorUser: currentUser,
request: this.req, request: this.req,
}) })
.intercept('positionMustBeInValues', () => Errors.POSITION_MUST_BE_PRESENT) .intercept('positionMustBeInValues', () => Errors.POSITION_MUST_BE_PRESENT)
@@ -32,7 +32,7 @@ module.exports = {
async fn(inputs) { async fn(inputs) {
const { currentUser } = this.req; const { currentUser } = this.req;
const { board, card } = await sails.helpers.cards const { card, list, board, project } = await sails.helpers.cards
.getProjectPath(inputs.cardId) .getProjectPath(inputs.cardId)
.intercept('pathNotFound', () => Errors.CARD_NOT_FOUND); .intercept('pathNotFound', () => Errors.CARD_NOT_FOUND);
@@ -55,7 +55,9 @@ module.exports = {
}; };
const action = await sails.helpers.actions.createOne.with({ const action = await sails.helpers.actions.createOne.with({
project,
board, board,
list,
values: { values: {
...values, ...values,
card, card,
@@ -36,7 +36,7 @@ module.exports = {
.intercept('pathNotFound', () => Errors.COMMENT_ACTION_NOT_FOUND); .intercept('pathNotFound', () => Errors.COMMENT_ACTION_NOT_FOUND);
let { action } = path; let { action } = path;
const { board, project } = path; const { card, list, board, project } = path;
const isProjectManager = await sails.helpers.users.isProjectManager(currentUser.id, project.id); const isProjectManager = await sails.helpers.users.isProjectManager(currentUser.id, project.id);
@@ -60,8 +60,12 @@ module.exports = {
} }
action = await sails.helpers.actions.deleteOne.with({ action = await sails.helpers.actions.deleteOne.with({
project,
board, board,
list,
card,
record: action, record: action,
actorUser: currentUser,
request: this.req, request: this.req,
}); });
@@ -40,7 +40,7 @@ module.exports = {
.intercept('pathNotFound', () => Errors.COMMENT_ACTION_NOT_FOUND); .intercept('pathNotFound', () => Errors.COMMENT_ACTION_NOT_FOUND);
let { action } = path; let { action } = path;
const { board, project } = path; const { card, list, board, project } = path;
const isProjectManager = await sails.helpers.users.isProjectManager(currentUser.id, project.id); const isProjectManager = await sails.helpers.users.isProjectManager(currentUser.id, project.id);
@@ -69,8 +69,12 @@ module.exports = {
action = await sails.helpers.actions.updateOne.with({ action = await sails.helpers.actions.updateOne.with({
values, values,
project,
board, board,
list,
card,
record: action, record: action,
actorUser: currentUser,
request: this.req, request: this.req,
}); });
+3 -1
View File
@@ -42,7 +42,7 @@ module.exports = {
async fn(inputs) { async fn(inputs) {
const { currentUser } = this.req; const { currentUser } = this.req;
const { board } = await sails.helpers.boards const { board, project } = await sails.helpers.boards
.getProjectPath(inputs.boardId) .getProjectPath(inputs.boardId)
.intercept('pathNotFound', () => Errors.BOARD_NOT_FOUND); .intercept('pathNotFound', () => Errors.BOARD_NOT_FOUND);
@@ -62,10 +62,12 @@ module.exports = {
const values = _.pick(inputs, ['position', 'name', 'color']); const values = _.pick(inputs, ['position', 'name', 'color']);
const label = await sails.helpers.labels.createOne.with({ const label = await sails.helpers.labels.createOne.with({
project,
values: { values: {
...values, ...values,
board, board,
}, },
actorUser: currentUser,
request: this.req, request: this.req,
}); });
+8 -2
View File
@@ -28,12 +28,15 @@ module.exports = {
async fn(inputs) { async fn(inputs) {
const { currentUser } = this.req; const { currentUser } = this.req;
let { label } = await sails.helpers.labels const path = await sails.helpers.labels
.getProjectPath(inputs.id) .getProjectPath(inputs.id)
.intercept('pathNotFound', () => Errors.LABEL_NOT_FOUND); .intercept('pathNotFound', () => Errors.LABEL_NOT_FOUND);
let { label } = path;
const { board, project } = path;
const boardMembership = await BoardMembership.findOne({ const boardMembership = await BoardMembership.findOne({
boardId: label.boardId, boardId: board.id,
userId: currentUser.id, userId: currentUser.id,
}); });
@@ -46,7 +49,10 @@ module.exports = {
} }
label = await sails.helpers.labels.deleteOne.with({ label = await sails.helpers.labels.deleteOne.with({
project,
board,
record: label, record: label,
actorUser: currentUser,
request: this.req, request: this.req,
}); });
+8 -2
View File
@@ -40,12 +40,15 @@ module.exports = {
async fn(inputs) { async fn(inputs) {
const { currentUser } = this.req; const { currentUser } = this.req;
let { label } = await sails.helpers.labels const path = await sails.helpers.labels
.getProjectPath(inputs.id) .getProjectPath(inputs.id)
.intercept('pathNotFound', () => Errors.LABEL_NOT_FOUND); .intercept('pathNotFound', () => Errors.LABEL_NOT_FOUND);
let { label } = path;
const { board, project } = path;
const boardMembership = await BoardMembership.findOne({ const boardMembership = await BoardMembership.findOne({
boardId: label.boardId, boardId: board.id,
userId: currentUser.id, userId: currentUser.id,
}); });
@@ -61,7 +64,10 @@ module.exports = {
label = await sails.helpers.labels.updateOne.with({ label = await sails.helpers.labels.updateOne.with({
values, values,
project,
board,
record: label, record: label,
actorUser: currentUser,
request: this.req, request: this.req,
}); });
+3 -1
View File
@@ -36,7 +36,7 @@ module.exports = {
async fn(inputs) { async fn(inputs) {
const { currentUser } = this.req; const { currentUser } = this.req;
const { board } = await sails.helpers.boards const { board, project } = await sails.helpers.boards
.getProjectPath(inputs.boardId) .getProjectPath(inputs.boardId)
.intercept('pathNotFound', () => Errors.BOARD_NOT_FOUND); .intercept('pathNotFound', () => Errors.BOARD_NOT_FOUND);
@@ -56,10 +56,12 @@ module.exports = {
const values = _.pick(inputs, ['position', 'name']); const values = _.pick(inputs, ['position', 'name']);
const list = await sails.helpers.lists.createOne.with({ const list = await sails.helpers.lists.createOne.with({
project,
values: { values: {
...values, ...values,
board, board,
}, },
actorUser: currentUser,
request: this.req, request: this.req,
}); });
+8 -2
View File
@@ -28,12 +28,15 @@ module.exports = {
async fn(inputs) { async fn(inputs) {
const { currentUser } = this.req; const { currentUser } = this.req;
let { list } = await sails.helpers.lists const path = await sails.helpers.lists
.getProjectPath(inputs.id) .getProjectPath(inputs.id)
.intercept('pathNotFound', () => Errors.LIST_NOT_FOUND); .intercept('pathNotFound', () => Errors.LIST_NOT_FOUND);
let { list } = path;
const { board, project } = path;
const boardMembership = await BoardMembership.findOne({ const boardMembership = await BoardMembership.findOne({
boardId: list.boardId, boardId: board.id,
userId: currentUser.id, userId: currentUser.id,
}); });
@@ -46,7 +49,10 @@ module.exports = {
} }
list = await sails.helpers.lists.deleteOne.with({ list = await sails.helpers.lists.deleteOne.with({
project,
board,
record: list, record: list,
actorUser: currentUser,
request: this.req, request: this.req,
}); });
+5 -2
View File
@@ -32,12 +32,12 @@ module.exports = {
async fn(inputs) { async fn(inputs) {
const { currentUser } = this.req; const { currentUser } = this.req;
const { list } = await sails.helpers.lists const { list, board, project } = await sails.helpers.lists
.getProjectPath(inputs.id) .getProjectPath(inputs.id)
.intercept('pathNotFound', () => Errors.LIST_NOT_FOUND); .intercept('pathNotFound', () => Errors.LIST_NOT_FOUND);
const boardMembership = await BoardMembership.findOne({ const boardMembership = await BoardMembership.findOne({
boardId: list.boardId, boardId: board.id,
userId: currentUser.id, userId: currentUser.id,
}); });
@@ -50,8 +50,11 @@ module.exports = {
} }
const cards = await sails.helpers.lists.sortOne.with({ const cards = await sails.helpers.lists.sortOne.with({
project,
board,
record: list, record: list,
type: inputs.type, type: inputs.type,
actorUser: currentUser,
request: this.req, request: this.req,
}); });
+8 -2
View File
@@ -35,12 +35,15 @@ module.exports = {
async fn(inputs) { async fn(inputs) {
const { currentUser } = this.req; const { currentUser } = this.req;
let { list } = await sails.helpers.lists const path = await sails.helpers.lists
.getProjectPath(inputs.id) .getProjectPath(inputs.id)
.intercept('pathNotFound', () => Errors.LIST_NOT_FOUND); .intercept('pathNotFound', () => Errors.LIST_NOT_FOUND);
let { list } = path;
const { board, project } = path;
const boardMembership = await BoardMembership.findOne({ const boardMembership = await BoardMembership.findOne({
boardId: list.boardId, boardId: board.id,
userId: currentUser.id, userId: currentUser.id,
}); });
@@ -56,7 +59,10 @@ module.exports = {
list = await sails.helpers.lists.updateOne.with({ list = await sails.helpers.lists.updateOne.with({
values, values,
project,
board,
record: list, record: list,
actorUser: currentUser,
request: this.req, request: this.req,
}); });
@@ -18,7 +18,7 @@ module.exports = {
const notifications = await sails.helpers.notifications.updateMany.with({ const notifications = await sails.helpers.notifications.updateMany.with({
values, values,
recordsOrIds: inputs.ids.split(','), recordsOrIds: inputs.ids.split(','),
user: currentUser, actorUser: currentUser,
request: this.req, request: this.req,
}); });
@@ -63,6 +63,7 @@ module.exports = {
project, project,
user, user,
}, },
actorUser: currentUser,
request: this.req, request: this.req,
}) })
.intercept('userAlreadyProjectManager', () => Errors.USER_ALREADY_PROJECT_MANAGER); .intercept('userAlreadyProjectManager', () => Errors.USER_ALREADY_PROJECT_MANAGER);
@@ -40,6 +40,7 @@ module.exports = {
// TODO: check if the last one // TODO: check if the last one
projectManager = await sails.helpers.projectManagers.deleteOne.with({ projectManager = await sails.helpers.projectManagers.deleteOne.with({
record: projectManager, record: projectManager,
actorUser: currentUser,
request: this.req, request: this.req,
}); });
+1 -1
View File
@@ -13,7 +13,7 @@ module.exports = {
const { project, projectManager } = await sails.helpers.projects.createOne.with({ const { project, projectManager } = await sails.helpers.projects.createOne.with({
values, values,
user: currentUser, actorUser: currentUser,
request: this.req, request: this.req,
}); });
@@ -36,6 +36,7 @@ module.exports = {
project = await sails.helpers.projects.deleteOne.with({ project = await sails.helpers.projects.deleteOne.with({
record: project, record: project,
actorUser: currentUser,
request: this.req, request: this.req,
}); });
@@ -90,6 +90,7 @@ module.exports = {
values: { values: {
backgroundImage: fileData, backgroundImage: fileData,
}, },
actorUser: currentUser,
request: this.req, request: this.req,
}); });
@@ -81,6 +81,7 @@ module.exports = {
project = await sails.helpers.projects.updateOne.with({ project = await sails.helpers.projects.updateOne.with({
values, values,
record: project, record: project,
actorUser: currentUser,
request: this.req, request: this.req,
}); });
+6 -2
View File
@@ -39,12 +39,12 @@ module.exports = {
async fn(inputs) { async fn(inputs) {
const { currentUser } = this.req; const { currentUser } = this.req;
const { card } = await sails.helpers.cards const { card, list, board, project } = await sails.helpers.cards
.getProjectPath(inputs.cardId) .getProjectPath(inputs.cardId)
.intercept('pathNotFound', () => Errors.CARD_NOT_FOUND); .intercept('pathNotFound', () => Errors.CARD_NOT_FOUND);
const boardMembership = await BoardMembership.findOne({ const boardMembership = await BoardMembership.findOne({
boardId: card.boardId, boardId: board.id,
userId: currentUser.id, userId: currentUser.id,
}); });
@@ -59,10 +59,14 @@ module.exports = {
const values = _.pick(inputs, ['position', 'name', 'isCompleted']); const values = _.pick(inputs, ['position', 'name', 'isCompleted']);
const task = await sails.helpers.tasks.createOne.with({ const task = await sails.helpers.tasks.createOne.with({
project,
board,
list,
values: { values: {
...values, ...values,
card, card,
}, },
actorUser: currentUser,
request: this.req, request: this.req,
}); });
+5 -1
View File
@@ -33,7 +33,7 @@ module.exports = {
.intercept('pathNotFound', () => Errors.TASK_NOT_FOUND); .intercept('pathNotFound', () => Errors.TASK_NOT_FOUND);
let { task } = path; let { task } = path;
const { board } = path; const { card, list, board, project } = path;
const boardMembership = await BoardMembership.findOne({ const boardMembership = await BoardMembership.findOne({
boardId: board.id, boardId: board.id,
@@ -49,8 +49,12 @@ module.exports = {
} }
task = await sails.helpers.tasks.deleteOne.with({ task = await sails.helpers.tasks.deleteOne.with({
project,
board, board,
list,
card,
record: task, record: task,
actorUser: currentUser,
request: this.req, request: this.req,
}); });
+5 -1
View File
@@ -43,7 +43,7 @@ module.exports = {
.intercept('pathNotFound', () => Errors.TASK_NOT_FOUND); .intercept('pathNotFound', () => Errors.TASK_NOT_FOUND);
let { task } = path; let { task } = path;
const { board } = path; const { card, list, board, project } = path;
const boardMembership = await BoardMembership.findOne({ const boardMembership = await BoardMembership.findOne({
boardId: board.id, boardId: board.id,
@@ -62,8 +62,12 @@ module.exports = {
task = await sails.helpers.tasks.updateOne.with({ task = await sails.helpers.tasks.updateOne.with({
values, values,
project,
board, board,
list,
card,
record: task, record: task,
actorUser: currentUser,
request: this.req, request: this.req,
}); });
+3
View File
@@ -71,6 +71,8 @@ module.exports = {
}, },
async fn(inputs) { async fn(inputs) {
const { currentUser } = this.req;
if (sails.config.custom.oidcEnforced) { if (sails.config.custom.oidcEnforced) {
throw Errors.NOT_ENOUGH_RIGHTS; throw Errors.NOT_ENOUGH_RIGHTS;
} }
@@ -89,6 +91,7 @@ module.exports = {
const user = await sails.helpers.users.createOne const user = await sails.helpers.users.createOne
.with({ .with({
values, values,
actorUser: currentUser,
request: this.req, request: this.req,
}) })
.intercept('emailAlreadyInUse', () => Errors.EMAIL_ALREADY_IN_USE) .intercept('emailAlreadyInUse', () => Errors.EMAIL_ALREADY_IN_USE)
+3
View File
@@ -26,6 +26,8 @@ module.exports = {
}, },
async fn(inputs) { async fn(inputs) {
const { currentUser } = this.req;
let user = await sails.helpers.users.getOne(inputs.id); let user = await sails.helpers.users.getOne(inputs.id);
if (!user) { if (!user) {
@@ -38,6 +40,7 @@ module.exports = {
user = await sails.helpers.users.deleteOne.with({ user = await sails.helpers.users.deleteOne.with({
record: user, record: user,
actorUser: currentUser,
request: this.req, request: this.req,
}); });
@@ -91,7 +91,7 @@ module.exports = {
values: { values: {
avatar: fileData, avatar: fileData,
}, },
user: currentUser, actorUser: currentUser,
request: this.req, request: this.req,
}); });
+1 -1
View File
@@ -82,7 +82,7 @@ module.exports = {
.with({ .with({
values, values,
record: user, record: user,
user: currentUser, actorUser: currentUser,
request: this.req, request: this.req,
}) })
.intercept('emailAlreadyInUse', () => Errors.EMAIL_ALREADY_IN_USE); .intercept('emailAlreadyInUse', () => Errors.EMAIL_ALREADY_IN_USE);
@@ -80,7 +80,7 @@ module.exports = {
user = await sails.helpers.users.updateOne.with({ user = await sails.helpers.users.updateOne.with({
values, values,
record: user, record: user,
user: currentUser, actorUser: currentUser,
request: this.req, request: this.req,
}); });
@@ -83,7 +83,7 @@ module.exports = {
.with({ .with({
values, values,
record: user, record: user,
user: currentUser, actorUser: currentUser,
request: this.req, request: this.req,
}) })
.intercept('usernameAlreadyInUse', () => Errors.USERNAME_ALREADY_IN_USE); .intercept('usernameAlreadyInUse', () => Errors.USERNAME_ALREADY_IN_USE);
+1 -1
View File
@@ -95,7 +95,7 @@ module.exports = {
user = await sails.helpers.users.updateOne.with({ user = await sails.helpers.users.updateOne.with({
values, values,
record: user, record: user,
user: currentUser, actorUser: currentUser,
request: this.req, request: this.req,
}); });
+33 -9
View File
@@ -14,21 +14,21 @@ const valuesValidator = (value) => {
return true; return true;
}; };
const buildAndSendSlackMessage = async (user, card, action) => { const buildAndSendSlackMessage = async (card, action, actorUser) => {
const cardLink = `<${sails.config.custom.baseUrl}/cards/${card.id}|${card.name}>`; const cardLink = `<${sails.config.custom.baseUrl}/cards/${card.id}|${card.name}>`;
let markdown; let markdown;
switch (action.type) { switch (action.type) {
case Action.Types.CREATE_CARD: case Action.Types.CREATE_CARD:
markdown = `${cardLink} was created by ${user.name} in *${action.data.list.name}*`; markdown = `${cardLink} was created by ${actorUser.name} in *${action.data.list.name}*`;
break; break;
case Action.Types.MOVE_CARD: case Action.Types.MOVE_CARD:
markdown = `${cardLink} was moved by ${user.name} to *${action.data.toList.name}*`; markdown = `${cardLink} was moved by ${actorUser.name} to *${action.data.toList.name}*`;
break; break;
case Action.Types.COMMENT_CARD: case Action.Types.COMMENT_CARD:
markdown = `*${user.name}* commented on ${cardLink}:\n>${action.data.text}`; markdown = `*${actorUser.name}* commented on ${cardLink}:\n>${action.data.text}`;
break; break;
default: default:
@@ -45,10 +45,18 @@ module.exports = {
custom: valuesValidator, custom: valuesValidator,
required: true, required: true,
}, },
project: {
type: 'ref',
required: true,
},
board: { board: {
type: 'ref', type: 'ref',
required: true, required: true,
}, },
list: {
type: 'ref',
required: true,
},
request: { request: {
type: 'ref', type: 'ref',
}, },
@@ -72,6 +80,24 @@ module.exports = {
inputs.request, inputs.request,
); );
sails.helpers.utils.sendWebhooks.with({
event: 'actionCreate',
data: {
item: action,
included: {
projects: [inputs.project],
boards: [inputs.board],
lists: [inputs.list],
cards: [values.card],
},
},
user: values.user,
});
if (sails.config.custom.slackBotToken) {
buildAndSendSlackMessage(values.card, action, values.user);
}
const subscriptionUserIds = await sails.helpers.cards.getSubscriptionUserIds( const subscriptionUserIds = await sails.helpers.cards.getSubscriptionUserIds(
action.cardId, action.cardId,
action.userId, action.userId,
@@ -84,17 +110,15 @@ module.exports = {
userId, userId,
action, action,
}, },
user: values.user, project: inputs.project,
board: inputs.board, board: inputs.board,
list: inputs.list,
card: values.card, card: values.card,
actorUser: values.user,
}), }),
), ),
); );
if (sails.config.custom.slackBotToken) {
buildAndSendSlackMessage(values.user, values.card, action);
}
return action; return action;
}, },
}; };
+30
View File
@@ -4,10 +4,26 @@ module.exports = {
type: 'ref', type: 'ref',
required: true, required: true,
}, },
project: {
type: 'ref',
required: true,
},
board: { board: {
type: 'ref', type: 'ref',
required: true, required: true,
}, },
list: {
type: 'ref',
required: true,
},
card: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: { request: {
type: 'ref', type: 'ref',
}, },
@@ -25,6 +41,20 @@ module.exports = {
}, },
inputs.request, inputs.request,
); );
sails.helpers.utils.sendWebhooks.with({
event: 'actionDelete',
data: {
item: action,
included: {
projects: [inputs.project],
boards: [inputs.board],
lists: [inputs.list],
cards: [inputs.card],
},
},
user: inputs.actorUser,
});
} }
return action; return action;
+30
View File
@@ -8,10 +8,26 @@ module.exports = {
type: 'json', type: 'json',
required: true, required: true,
}, },
project: {
type: 'ref',
required: true,
},
board: { board: {
type: 'ref', type: 'ref',
required: true, required: true,
}, },
list: {
type: 'ref',
required: true,
},
card: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: { request: {
type: 'ref', type: 'ref',
}, },
@@ -31,6 +47,20 @@ module.exports = {
}, },
inputs.request, inputs.request,
); );
sails.helpers.utils.sendWebhooks.with({
event: 'actionUpdate',
data: {
item: action,
included: {
projects: [inputs.project],
boards: [inputs.board],
lists: [inputs.list],
cards: [inputs.card],
},
},
user: inputs.actorUser,
});
} }
return action; return action;
@@ -21,6 +21,18 @@ module.exports = {
custom: valuesValidator, custom: valuesValidator,
required: true, required: true,
}, },
project: {
type: 'ref',
required: true,
},
board: {
type: 'ref',
required: true,
},
list: {
type: 'ref',
required: true,
},
requestId: { requestId: {
type: 'string', type: 'string',
isNotEmptyString: true, isNotEmptyString: true,
@@ -49,12 +61,30 @@ module.exports = {
inputs.request, inputs.request,
); );
sails.helpers.utils.sendWebhooks.with({
event: 'attachmentCreate',
data: {
item: attachment,
included: {
projects: [inputs.project],
boards: [inputs.board],
lists: [inputs.list],
cards: [values.card],
},
},
user: values.creatorUser,
});
if (!values.card.coverAttachmentId && attachment.image) { if (!values.card.coverAttachmentId && attachment.image) {
await sails.helpers.cards.updateOne.with({ await sails.helpers.cards.updateOne.with({
record: values.card, record: values.card,
values: { values: {
coverAttachmentId: attachment.id, coverAttachmentId: attachment.id,
}, },
project: inputs.project,
board: inputs.board,
list: inputs.list,
actorUser: values.creatorUser,
}); });
} }
@@ -7,14 +7,26 @@ module.exports = {
type: 'ref', type: 'ref',
required: true, required: true,
}, },
project: {
type: 'ref',
required: true,
},
board: { board: {
type: 'ref', type: 'ref',
required: true, required: true,
}, },
list: {
type: 'ref',
required: true,
},
card: { card: {
type: 'ref', type: 'ref',
required: true, required: true,
}, },
actorUser: {
type: 'ref',
required: true,
},
request: { request: {
type: 'ref', type: 'ref',
}, },
@@ -27,6 +39,10 @@ module.exports = {
values: { values: {
coverAttachmentId: null, coverAttachmentId: null,
}, },
project: inputs.project,
board: inputs.board,
list: inputs.list,
actorUser: inputs.actorUser,
request: inputs.request, request: inputs.request,
}); });
} }
@@ -48,6 +64,20 @@ module.exports = {
}, },
inputs.request, inputs.request,
); );
sails.helpers.utils.sendWebhooks.with({
event: 'attachmentDelete',
data: {
item: attachment,
included: {
projects: [inputs.project],
boards: [inputs.board],
lists: [inputs.list],
cards: [inputs.card],
},
},
user: inputs.actorUser,
});
} }
return attachment; return attachment;
@@ -8,10 +8,26 @@ module.exports = {
type: 'json', type: 'json',
required: true, required: true,
}, },
project: {
type: 'ref',
required: true,
},
board: { board: {
type: 'ref', type: 'ref',
required: true, required: true,
}, },
list: {
type: 'ref',
required: true,
},
card: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: { request: {
type: 'ref', type: 'ref',
}, },
@@ -31,6 +47,20 @@ module.exports = {
}, },
inputs.request, inputs.request,
); );
sails.helpers.utils.sendWebhooks.with({
event: 'attachmentUpdate',
data: {
item: attachment,
included: {
projects: [inputs.project],
boards: [inputs.board],
lists: [inputs.list],
cards: [inputs.card],
},
},
user: inputs.actorUser,
});
} }
return attachment; return attachment;
@@ -21,6 +21,14 @@ module.exports = {
custom: valuesValidator, custom: valuesValidator,
required: true, required: true,
}, },
project: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: { request: {
type: 'ref', type: 'ref',
}, },
@@ -67,6 +75,19 @@ module.exports = {
inputs.request, inputs.request,
); );
sails.helpers.utils.sendWebhooks.with({
event: 'boardMembershipCreate',
data: {
item: boardMembership,
included: {
users: [values.user],
projects: [inputs.project],
boards: [values.board],
},
},
user: inputs.actorUser,
});
return boardMembership; return boardMembership;
}, },
}; };
@@ -10,6 +10,14 @@ module.exports = {
type: 'ref', type: 'ref',
required: true, required: true,
}, },
board: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: { request: {
type: 'ref', type: 'ref',
}, },
@@ -73,6 +81,18 @@ module.exports = {
); );
}); });
} }
sails.helpers.utils.sendWebhooks.with({
event: 'boardMembershipDelete',
data: {
item: boardMembership,
included: {
projects: [inputs.project],
boards: [inputs.board],
},
},
user: inputs.actorUser,
});
} }
return boardMembership; return boardMembership;
@@ -8,6 +8,18 @@ module.exports = {
type: 'json', type: 'json',
required: true, required: true,
}, },
project: {
type: 'ref',
required: true,
},
board: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: { request: {
type: 'ref', type: 'ref',
}, },
@@ -40,6 +52,18 @@ module.exports = {
}, },
inputs.request, inputs.request,
); );
sails.helpers.utils.sendWebhooks.with({
event: 'boardMembershipUpdate',
data: {
item: boardMembership,
included: {
projects: [inputs.project],
boards: [inputs.board],
},
},
user: inputs.actorUser,
});
} }
return boardMembership; return boardMembership;
+16 -3
View File
@@ -37,7 +37,7 @@ module.exports = {
type: 'json', type: 'json',
custom: importValidator, custom: importValidator,
}, },
user: { actorUser: {
type: 'ref', type: 'ref',
required: true, required: true,
}, },
@@ -80,6 +80,8 @@ module.exports = {
position: nextPosition, position: nextPosition,
}, },
}); });
// TODO: send webhooks
}); });
}); });
@@ -90,12 +92,12 @@ module.exports = {
}).fetch(); }).fetch();
if (inputs.import && inputs.import.type === Board.ImportTypes.TRELLO) { if (inputs.import && inputs.import.type === Board.ImportTypes.TRELLO) {
await sails.helpers.boards.importFromTrello(inputs.user, board, inputs.import.board); await sails.helpers.boards.importFromTrello(board, inputs.import.board, inputs.actorUser);
} }
const boardMembership = await BoardMembership.create({ const boardMembership = await BoardMembership.create({
boardId: board.id, boardId: board.id,
userId: inputs.user.id, userId: inputs.actorUser.id,
role: BoardMembership.Roles.EDITOR, role: BoardMembership.Roles.EDITOR,
}).fetch(); }).fetch();
@@ -111,6 +113,17 @@ module.exports = {
); );
}); });
sails.helpers.utils.sendWebhooks.with({
event: 'boardCreate',
data: {
item: board,
included: {
projects: [values.project],
},
},
user: inputs.actorUser,
});
return { return {
board, board,
boardMembership, boardMembership,
+19
View File
@@ -4,6 +4,14 @@ module.exports = {
type: 'ref', type: 'ref',
required: true, required: true,
}, },
project: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: { request: {
type: 'ref', type: 'ref',
}, },
@@ -33,6 +41,17 @@ module.exports = {
inputs.request, inputs.request,
); );
}); });
sails.helpers.utils.sendWebhooks.with({
event: 'boardDelete',
data: {
item: board,
included: {
projects: [inputs.project],
},
},
user: inputs.actorUser,
});
} }
return board; return board;
@@ -2,10 +2,6 @@ const POSITION_GAP = 65535; // TODO: move to config
module.exports = { module.exports = {
inputs: { inputs: {
user: {
type: 'ref',
required: true,
},
board: { board: {
type: 'ref', type: 'ref',
required: true, required: true,
@@ -14,6 +10,10 @@ module.exports = {
type: 'json', type: 'json',
required: true, required: true,
}, },
actorUser: {
type: 'ref',
required: true,
},
}, },
async fn(inputs) { async fn(inputs) {
@@ -87,7 +87,7 @@ module.exports = {
trelloComments.map(async (trelloComment) => { trelloComments.map(async (trelloComment) => {
return Action.create({ return Action.create({
cardId: plankaCard.id, cardId: plankaCard.id,
userId: inputs.user.id, userId: inputs.actorUser.id,
type: 'commentCard', type: 'commentCard',
data: { data: {
text: text:
@@ -105,7 +105,7 @@ module.exports = {
const plankaCard = await Card.create({ const plankaCard = await Card.create({
boardId: inputs.board.id, boardId: inputs.board.id,
listId: plankaList.id, listId: plankaList.id,
creatorUserId: inputs.user.id, creatorUserId: inputs.actorUser.id,
position: trelloCard.pos, position: trelloCard.pos,
name: trelloCard.name, name: trelloCard.name,
description: trelloCard.desc || null, description: trelloCard.desc || null,
+21
View File
@@ -21,6 +21,14 @@ module.exports = {
custom: valuesValidator, custom: valuesValidator,
required: true, required: true,
}, },
project: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: { request: {
type: 'ref', type: 'ref',
}, },
@@ -64,6 +72,8 @@ module.exports = {
position: nextPosition, position: nextPosition,
}, },
}); });
// TODO: send webhooks
}); });
}); });
} }
@@ -81,6 +91,17 @@ module.exports = {
inputs.request, inputs.request,
); );
}); });
sails.helpers.utils.sendWebhooks.with({
event: 'boardUpdate',
data: {
item: board,
included: {
projects: [inputs.project],
},
},
user: inputs.actorUser,
});
} }
return board; return board;
@@ -21,6 +21,22 @@ module.exports = {
custom: valuesValidator, custom: valuesValidator,
required: true, required: true,
}, },
project: {
type: 'ref',
required: true,
},
board: {
type: 'ref',
required: true,
},
list: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: { request: {
type: 'ref', type: 'ref',
}, },
@@ -50,6 +66,21 @@ module.exports = {
inputs.request, inputs.request,
); );
sails.helpers.utils.sendWebhooks.with({
event: 'cardLabelCreate',
data: {
item: cardLabel,
included: {
projects: [inputs.project],
boards: [inputs.board],
labels: [values.label],
lists: [inputs.list],
cards: [values.card],
},
},
user: inputs.actorUser,
});
return cardLabel; return cardLabel;
}, },
}; };
@@ -4,10 +4,26 @@ module.exports = {
type: 'ref', type: 'ref',
required: true, required: true,
}, },
project: {
type: 'ref',
required: true,
},
board: { board: {
type: 'ref', type: 'ref',
required: true, required: true,
}, },
list: {
type: 'ref',
required: true,
},
card: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: { request: {
type: 'ref', type: 'ref',
}, },
@@ -25,6 +41,20 @@ module.exports = {
}, },
inputs.request, inputs.request,
); );
sails.helpers.utils.sendWebhooks.with({
event: 'cardLabelDelete',
data: {
item: cardLabel,
included: {
projects: [inputs.project],
boards: [inputs.board],
lists: [inputs.list],
cards: [inputs.card],
},
},
user: inputs.actorUser,
});
} }
return cardLabel; return cardLabel;
@@ -21,6 +21,22 @@ module.exports = {
custom: valuesValidator, custom: valuesValidator,
required: true, required: true,
}, },
project: {
type: 'ref',
required: true,
},
board: {
type: 'ref',
required: true,
},
list: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: { request: {
type: 'ref', type: 'ref',
}, },
@@ -53,6 +69,20 @@ module.exports = {
inputs.request, inputs.request,
); );
sails.helpers.utils.sendWebhooks.with({
event: 'cardMembershipCreate',
data: {
item: cardMembership,
included: {
projects: [inputs.project],
boards: [inputs.board],
lists: [inputs.list],
cards: [values.card],
},
},
user: inputs.actorUser,
});
const cardSubscription = await CardSubscription.create({ const cardSubscription = await CardSubscription.create({
cardId: cardMembership.cardId, cardId: cardMembership.cardId,
userId: cardMembership.userId, userId: cardMembership.userId,
@@ -73,6 +103,8 @@ module.exports = {
}, },
inputs.request, inputs.request,
); );
// TODO: send webhooks
} }
return cardMembership; return cardMembership;
@@ -4,10 +4,26 @@ module.exports = {
type: 'ref', type: 'ref',
required: true, required: true,
}, },
project: {
type: 'ref',
required: true,
},
board: { board: {
type: 'ref', type: 'ref',
required: true, required: true,
}, },
list: {
type: 'ref',
required: true,
},
card: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: { request: {
type: 'ref', type: 'ref',
}, },
@@ -26,6 +42,20 @@ module.exports = {
inputs.request, inputs.request,
); );
sails.helpers.utils.sendWebhooks.with({
event: 'cardMembershipDelete',
data: {
item: cardMembership,
included: {
projects: [inputs.project],
boards: [inputs.board],
lists: [inputs.list],
cards: [inputs.card],
},
},
user: inputs.actorUser,
});
const cardSubscription = await CardSubscription.destroyOne({ const cardSubscription = await CardSubscription.destroyOne({
cardId: cardMembership.cardId, cardId: cardMembership.cardId,
userId: cardMembership.userId, userId: cardMembership.userId,
@@ -39,6 +69,8 @@ module.exports = {
isSubscribed: false, isSubscribed: false,
}, },
}); });
// TODO: send webhook
} }
} }
+24
View File
@@ -25,6 +25,10 @@ module.exports = {
custom: valuesValidator, custom: valuesValidator,
required: true, required: true,
}, },
project: {
type: 'ref',
required: true,
},
board: { board: {
type: 'ref', type: 'ref',
required: true, required: true,
@@ -66,6 +70,8 @@ module.exports = {
position: nextPosition, position: nextPosition,
}, },
}); });
// TODO: send webhooks
}); });
const card = await Card.create({ const card = await Card.create({
@@ -85,6 +91,19 @@ module.exports = {
inputs.request, inputs.request,
); );
sails.helpers.utils.sendWebhooks.with({
event: 'cardCreate',
data: {
item: card,
included: {
projects: [inputs.project],
boards: [inputs.board],
lists: [values.list],
},
},
user: values.creatorUser,
});
if (values.creatorUser.subscribeToOwnCards) { if (values.creatorUser.subscribeToOwnCards) {
await CardSubscription.create({ await CardSubscription.create({
cardId: card.id, cardId: card.id,
@@ -97,6 +116,8 @@ module.exports = {
isSubscribed: true, isSubscribed: true,
}, },
}); });
// TODO: send webhooks
} }
await sails.helpers.actions.createOne.with({ await sails.helpers.actions.createOne.with({
@@ -108,7 +129,10 @@ module.exports = {
}, },
user: values.creatorUser, user: values.creatorUser,
}, },
project: inputs.project,
board: inputs.board, board: inputs.board,
list: values.list,
request: inputs.request,
}); });
return card; return card;
+29 -4
View File
@@ -1,5 +1,5 @@
const buildAndSendSlackMessage = async (user, card) => { const buildAndSendSlackMessage = async (card, actorUser) => {
await sails.helpers.utils.sendSlackMessage(`*${card.name}* was deleted by ${user.name}`); await sails.helpers.utils.sendSlackMessage(`*${card.name}* was deleted by ${actorUser.name}`);
}; };
module.exports = { module.exports = {
@@ -8,7 +8,19 @@ module.exports = {
type: 'ref', type: 'ref',
required: true, required: true,
}, },
user: { project: {
type: 'ref',
required: true,
},
board: {
type: 'ref',
required: true,
},
list: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref', type: 'ref',
required: true, required: true,
}, },
@@ -30,8 +42,21 @@ module.exports = {
inputs.request, inputs.request,
); );
sails.helpers.utils.sendWebhooks.with({
event: 'cardDelete',
data: {
item: card,
included: {
projects: [inputs.project],
boards: [inputs.board],
lists: [inputs.list],
},
},
user: inputs.actorUser,
});
if (sails.config.custom.slackBotToken) { if (sails.config.custom.slackBotToken) {
buildAndSendSlackMessage(inputs.user, card); buildAndSendSlackMessage(card, inputs.actorUser);
} }
} }
+24
View File
@@ -25,6 +25,10 @@ module.exports = {
custom: valuesValidator, custom: valuesValidator,
required: true, required: true,
}, },
project: {
type: 'ref',
required: true,
},
board: { board: {
type: 'ref', type: 'ref',
required: true, required: true,
@@ -62,6 +66,8 @@ module.exports = {
position: nextPosition, position: nextPosition,
}, },
}); });
// TODO: send webhooks
}); });
const card = await Card.create({ const card = await Card.create({
@@ -108,6 +114,19 @@ module.exports = {
inputs.request, inputs.request,
); );
sails.helpers.utils.sendWebhooks.with({
event: 'cardCreate',
data: {
item: card,
included: {
projects: [inputs.project],
boards: [inputs.board],
lists: [inputs.list],
},
},
user: values.creatorUser,
});
if (values.creatorUser.subscribeToOwnCards) { if (values.creatorUser.subscribeToOwnCards) {
await CardSubscription.create({ await CardSubscription.create({
cardId: card.id, cardId: card.id,
@@ -120,6 +139,8 @@ module.exports = {
isSubscribed: true, isSubscribed: true,
}, },
}); });
// TODO: send webhooks
} }
await sails.helpers.actions.createOne.with({ await sails.helpers.actions.createOne.with({
@@ -131,7 +152,10 @@ module.exports = {
}, },
user: values.creatorUser, user: values.creatorUser,
}, },
project: inputs.project,
board: inputs.board, board: inputs.board,
list: inputs.list,
request: inputs.request,
}); });
return { return {
+86 -55
View File
@@ -7,8 +7,14 @@ const valuesValidator = (value) => {
return false; return false;
} }
if (!_.isUndefined(value.board) && !_.isPlainObject(value.board)) { if (!_.isUndefined(value.board)) {
return false; if (!_.isPlainObject(value.project)) {
return false;
}
if (!_.isPlainObject(value.board)) {
return false;
}
} }
if (!_.isUndefined(value.list) && !_.isPlainObject(value.list)) { if (!_.isUndefined(value.list) && !_.isPlainObject(value.list)) {
@@ -29,14 +35,21 @@ module.exports = {
custom: valuesValidator, custom: valuesValidator,
required: true, required: true,
}, },
user: { project: {
type: 'ref', type: 'ref',
required: true,
}, },
board: { board: {
type: 'ref', type: 'ref',
required: true,
}, },
list: { list: {
type: 'ref', type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
}, },
request: { request: {
type: 'ref', type: 'ref',
@@ -45,65 +58,56 @@ module.exports = {
exits: { exits: {
positionMustBeInValues: {}, positionMustBeInValues: {},
boardInValuesMustBelongToProject: {},
listMustBeInValues: {}, listMustBeInValues: {},
listInValuesMustBelongToBoard: {}, listInValuesMustBelongToBoard: {},
userMustBePresent: {},
boardMustBePresent: {},
listMustBePresent: {},
}, },
async fn(inputs) { async fn(inputs) {
const { isSubscribed, ...values } = inputs.values; const { isSubscribed, ...values } = inputs.values;
if (values.board || values.list || !_.isUndefined(values.position)) { if (values.project && values.project.id === inputs.project.id) {
if (!inputs.board) { delete values.project;
throw 'boardMustBePresent'; }
const project = values.project || inputs.project;
if (values.board) {
if (values.board.projectId !== project.id) {
throw 'boardInValuesMustBelongToProject';
} }
if (values.board) { if (values.board.id === inputs.board.id) {
if (values.board.id === inputs.board.id) { delete values.board;
delete values.board; } else {
} else { values.boardId = values.board.id;
values.boardId = values.board.id;
}
}
const board = values.board || inputs.board;
if (values.list) {
if (!inputs.list) {
throw 'listMustBePresent';
}
if (values.list.boardId !== board.id) {
throw 'listInValuesMustBelongToBoard';
}
if (values.list.id === inputs.list.id) {
delete values.list;
} else {
values.listId = values.list.id;
}
}
if (values.list) {
if (_.isUndefined(values.position)) {
throw 'positionMustBeInValues';
}
} else if (values.board) {
throw 'listMustBeInValues';
} }
} }
if ((!_.isUndefined(isSubscribed) || values.board || values.list) && !inputs.user) { const board = values.board || inputs.board;
throw 'userMustBePresent';
if (values.list) {
if (values.list.boardId !== board.id) {
throw 'listInValuesMustBelongToBoard';
}
if (values.list.id === inputs.list.id) {
delete values.list;
} else {
values.listId = values.list.id;
}
} else if (values.board) {
throw 'listMustBeInValues';
}
const list = values.list || inputs.list;
if (values.list && _.isUndefined(values.position)) {
throw 'positionMustBeInValues';
} }
if (!_.isUndefined(values.position)) { if (!_.isUndefined(values.position)) {
const boardId = values.boardId || inputs.record.boardId; const cards = await sails.helpers.lists.getCards(list.id, inputs.record.id);
const listId = values.listId || inputs.record.listId;
const cards = await sails.helpers.lists.getCards(listId, inputs.record.id);
const { position, repositions } = sails.helpers.utils.insertToPositionables( const { position, repositions } = sails.helpers.utils.insertToPositionables(
values.position, values.position,
@@ -115,17 +119,19 @@ module.exports = {
repositions.forEach(async ({ id, position: nextPosition }) => { repositions.forEach(async ({ id, position: nextPosition }) => {
await Card.update({ await Card.update({
id, id,
listId, listId: list.id,
}).set({ }).set({
position: nextPosition, position: nextPosition,
}); });
sails.sockets.broadcast(`board:${boardId}`, 'cardUpdate', { sails.sockets.broadcast(`board:${board.id}`, 'cardUpdate', {
item: { item: {
id, id,
position: nextPosition, position: nextPosition,
}, },
}); });
// TODO: send webhooks
}); });
} }
@@ -175,10 +181,12 @@ module.exports = {
} }
const { id } = await sails.helpers.labels.createOne.with({ const { id } = await sails.helpers.labels.createOne.with({
project,
values: { values: {
..._.omit(label, ['id', 'boardId']), ..._.omit(label, ['id', 'boardId']),
board: values.board, board: values.board,
}, },
actorUser: inputs.actorUser,
}); });
return id; return id;
@@ -209,6 +217,8 @@ module.exports = {
isSubscribed: true, isSubscribed: true,
}, },
}); });
// TODO: send webhooks
}); });
} else { } else {
sails.sockets.broadcast( sails.sockets.broadcast(
@@ -221,18 +231,34 @@ module.exports = {
); );
} }
sails.helpers.utils.sendWebhooks.with({
event: 'cardUpdate',
data: {
item: card,
included: {
projects: [project],
boards: [board],
lists: [list],
},
},
user: inputs.actorUser,
});
if (!values.board && values.list) { if (!values.board && values.list) {
await sails.helpers.actions.createOne.with({ await sails.helpers.actions.createOne.with({
project,
board,
list,
values: { values: {
card, card,
user: inputs.user, user: inputs.actorUser,
type: Action.Types.MOVE_CARD, type: Action.Types.MOVE_CARD,
data: { data: {
fromList: _.pick(inputs.list, ['id', 'name']), fromList: _.pick(inputs.list, ['id', 'name']),
toList: _.pick(values.list, ['id', 'name']), toList: _.pick(values.list, ['id', 'name']),
}, },
}, },
board: inputs.board, request: inputs.request,
}); });
} }
@@ -240,23 +266,26 @@ module.exports = {
} }
if (!_.isUndefined(isSubscribed)) { if (!_.isUndefined(isSubscribed)) {
const prevIsSubscribed = await sails.helpers.users.isCardSubscriber(inputs.user.id, card.id); const prevIsSubscribed = await sails.helpers.users.isCardSubscriber(
inputs.actorUser.id,
card.id,
);
if (isSubscribed !== prevIsSubscribed) { if (isSubscribed !== prevIsSubscribed) {
if (isSubscribed) { if (isSubscribed) {
await CardSubscription.create({ await CardSubscription.create({
cardId: card.id, cardId: card.id,
userId: inputs.user.id, userId: inputs.actorUser.id,
}).tolerate('E_UNIQUE'); }).tolerate('E_UNIQUE');
} else { } else {
await CardSubscription.destroyOne({ await CardSubscription.destroyOne({
cardId: card.id, cardId: card.id,
userId: inputs.user.id, userId: inputs.actorUser.id,
}); });
} }
sails.sockets.broadcast( sails.sockets.broadcast(
`user:${inputs.user.id}`, `user:${inputs.actorUser.id}`,
'cardUpdate', 'cardUpdate',
{ {
item: { item: {
@@ -266,6 +295,8 @@ module.exports = {
}, },
inputs.request, inputs.request,
); );
// TODO: send webhooks
} }
} }
+22
View File
@@ -21,6 +21,14 @@ module.exports = {
custom: valuesValidator, custom: valuesValidator,
required: true, required: true,
}, },
project: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: { request: {
type: 'ref', type: 'ref',
}, },
@@ -50,6 +58,8 @@ module.exports = {
position: nextPosition, position: nextPosition,
}, },
}); });
// TODO: send webhooks
}); });
const label = await Label.create({ const label = await Label.create({
@@ -67,6 +77,18 @@ module.exports = {
inputs.request, inputs.request,
); );
sails.helpers.utils.sendWebhooks.with({
event: 'labelCreate',
data: {
item: label,
included: {
projects: [inputs.project],
boards: [values.board],
},
},
user: inputs.actorUser,
});
return label; return label;
}, },
}; };
+24
View File
@@ -4,6 +4,18 @@ module.exports = {
type: 'ref', type: 'ref',
required: true, required: true,
}, },
project: {
type: 'ref',
required: true,
},
board: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: { request: {
type: 'ref', type: 'ref',
}, },
@@ -25,6 +37,18 @@ module.exports = {
}, },
inputs.request, inputs.request,
); );
sails.helpers.utils.sendWebhooks.with({
event: 'labelDelete',
data: {
item: label,
included: {
projects: [inputs.project],
boards: [inputs.board],
},
},
user: inputs.actorUser,
});
} }
return label; return label;
+26
View File
@@ -21,6 +21,18 @@ module.exports = {
custom: valuesValidator, custom: valuesValidator,
required: true, required: true,
}, },
project: {
type: 'ref',
required: true,
},
board: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: { request: {
type: 'ref', type: 'ref',
}, },
@@ -53,6 +65,8 @@ module.exports = {
position: nextPosition, position: nextPosition,
}, },
}); });
// TODO: send webhooks
}); });
} }
@@ -67,6 +81,18 @@ module.exports = {
}, },
inputs.request, inputs.request,
); );
sails.helpers.utils.sendWebhooks.with({
event: 'labelUpdate',
data: {
item: label,
included: {
projects: [inputs.project],
boards: [inputs.board],
},
},
user: inputs.actorUser,
});
} }
return label; return label;
+22
View File
@@ -21,6 +21,14 @@ module.exports = {
custom: valuesValidator, custom: valuesValidator,
required: true, required: true,
}, },
project: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: { request: {
type: 'ref', type: 'ref',
}, },
@@ -50,6 +58,8 @@ module.exports = {
position: nextPosition, position: nextPosition,
}, },
}); });
// TODO: send webhooks
}); });
const list = await List.create({ const list = await List.create({
@@ -67,6 +77,18 @@ module.exports = {
inputs.request, inputs.request,
); );
sails.helpers.utils.sendWebhooks.with({
event: 'listCreate',
data: {
item: list,
included: {
projects: [inputs.project],
boards: [values.board],
},
},
user: inputs.actorUser,
});
return list; return list;
}, },
}; };
+24
View File
@@ -4,6 +4,18 @@ module.exports = {
type: 'ref', type: 'ref',
required: true, required: true,
}, },
project: {
type: 'ref',
required: true,
},
board: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: { request: {
type: 'ref', type: 'ref',
}, },
@@ -21,6 +33,18 @@ module.exports = {
}, },
inputs.request, inputs.request,
); );
sails.helpers.utils.sendWebhooks.with({
event: 'listDelete',
data: {
item: list,
included: {
projects: [inputs.project],
boards: [inputs.board],
},
},
user: inputs.actorUser,
});
} }
return list; return list;
+25
View File
@@ -11,6 +11,18 @@ module.exports = {
isIn: Object.values(List.SortTypes), isIn: Object.values(List.SortTypes),
defaultsTo: List.SortTypes.NAME_ASC, defaultsTo: List.SortTypes.NAME_ASC,
}, },
project: {
type: 'ref',
required: true,
},
board: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: { request: {
type: 'ref', type: 'ref',
}, },
@@ -65,6 +77,19 @@ module.exports = {
inputs.request, inputs.request,
); );
sails.helpers.utils.sendWebhooks.with({
event: 'listSort',
data: {
item: inputs.record,
included: {
cards,
projects: [inputs.project],
boards: [inputs.board],
},
},
user: inputs.actorUser,
});
return cards; return cards;
}, },
}; };
+26
View File
@@ -21,6 +21,18 @@ module.exports = {
custom: valuesValidator, custom: valuesValidator,
required: true, required: true,
}, },
project: {
type: 'ref',
required: true,
},
board: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: { request: {
type: 'ref', type: 'ref',
}, },
@@ -53,6 +65,8 @@ module.exports = {
position: nextPosition, position: nextPosition,
}, },
}); });
// TODO: send webhooks
}); });
} }
@@ -67,6 +81,18 @@ module.exports = {
}, },
inputs.request, inputs.request,
); );
sails.helpers.utils.sendWebhooks.with({
event: 'listUpdate',
data: {
item: list,
included: {
projects: [inputs.project],
boards: [inputs.board],
},
},
user: inputs.actorUser,
});
} }
return list; return list;
+30 -7
View File
@@ -15,14 +15,14 @@ const valuesValidator = (value) => {
}; };
// TODO: use templates (views) to build html // TODO: use templates (views) to build html
const buildAndSendEmail = async (user, board, card, action, notifiableUser) => { const buildAndSendEmail = async (board, card, action, actorUser, notifiableUser) => {
let emailData; let emailData;
switch (action.type) { switch (action.type) {
case Action.Types.MOVE_CARD: case Action.Types.MOVE_CARD:
emailData = { emailData = {
subject: `${user.name} moved ${card.name} from ${action.data.fromList.name} to ${action.data.toList.name} on ${board.name}`, subject: `${actorUser.name} moved ${card.name} from ${action.data.fromList.name} to ${action.data.toList.name} on ${board.name}`,
html: html:
`<p>${user.name} moved ` + `<p>${actorUser.name} moved ` +
`<a href="${process.env.BASE_URL}/cards/${card.id}">${card.name}</a> ` + `<a href="${process.env.BASE_URL}/cards/${card.id}">${card.name}</a> ` +
`from ${action.data.fromList.name} to ${action.data.toList.name} ` + `from ${action.data.fromList.name} to ${action.data.toList.name} ` +
`on <a href="${process.env.BASE_URL}/boards/${board.id}">${board.name}</a></p>`, `on <a href="${process.env.BASE_URL}/boards/${board.id}">${board.name}</a></p>`,
@@ -31,9 +31,9 @@ const buildAndSendEmail = async (user, board, card, action, notifiableUser) => {
break; break;
case Action.Types.COMMENT_CARD: case Action.Types.COMMENT_CARD:
emailData = { emailData = {
subject: `${user.name} left a new comment to ${card.name} on ${board.name}`, subject: `${actorUser.name} left a new comment to ${card.name} on ${board.name}`,
html: html:
`<p>${user.name} left a new comment to ` + `<p>${actorUser.name} left a new comment to ` +
`<a href="${process.env.BASE_URL}/cards/${card.id}">${card.name}</a> ` + `<a href="${process.env.BASE_URL}/cards/${card.id}">${card.name}</a> ` +
`on <a href="${process.env.BASE_URL}/boards/${board.id}">${board.name}</a></p>` + `on <a href="${process.env.BASE_URL}/boards/${board.id}">${board.name}</a></p>` +
`<p>${action.data.text}</p>`, `<p>${action.data.text}</p>`,
@@ -57,7 +57,7 @@ module.exports = {
custom: valuesValidator, custom: valuesValidator,
required: true, required: true,
}, },
user: { project: {
type: 'ref', type: 'ref',
required: true, required: true,
}, },
@@ -65,10 +65,18 @@ module.exports = {
type: 'ref', type: 'ref',
required: true, required: true,
}, },
list: {
type: 'ref',
required: true,
},
card: { card: {
type: 'ref', type: 'ref',
required: true, required: true,
}, },
actorUser: {
type: 'ref',
required: true,
},
}, },
async fn(inputs) { async fn(inputs) {
@@ -96,9 +104,24 @@ module.exports = {
notifiableUser = await sails.helpers.users.getOne(notification.userId); notifiableUser = await sails.helpers.users.getOne(notification.userId);
} }
buildAndSendEmail(inputs.user, inputs.board, inputs.card, values.action, notifiableUser); buildAndSendEmail(inputs.board, inputs.card, values.action, inputs.actorUser, notifiableUser);
} }
sails.helpers.utils.sendWebhooks.with({
event: 'notificationCreate',
data: {
item: notification,
included: {
projects: [inputs.project],
boards: [inputs.board],
lists: [inputs.list],
cards: [inputs.card],
actions: [values.action],
},
},
user: inputs.actorUser,
});
return notification; return notification;
}, },
}; };
@@ -12,8 +12,9 @@ module.exports = {
type: 'json', type: 'json',
required: true, required: true,
}, },
user: { actorUser: {
type: 'ref', type: 'ref',
required: true,
}, },
request: { request: {
type: 'ref', type: 'ref',
@@ -23,7 +24,9 @@ module.exports = {
async fn(inputs) { async fn(inputs) {
const { values } = inputs; const { values } = inputs;
const criteria = {}; const criteria = {
userId: inputs.actorUser.id,
};
if (_.every(inputs.recordsOrIds, _.isPlainObject)) { if (_.every(inputs.recordsOrIds, _.isPlainObject)) {
criteria.id = sails.helpers.utils.mapRecords(inputs.recordsOrIds); criteria.id = sails.helpers.utils.mapRecords(inputs.recordsOrIds);
@@ -31,10 +34,6 @@ module.exports = {
criteria.id = inputs.recordsOrIds; criteria.id = inputs.recordsOrIds;
} }
if (inputs.user) {
criteria.userId = inputs.user.id;
}
const notifications = await Notification.update(criteria) const notifications = await Notification.update(criteria)
.set({ ...values }) .set({ ...values })
.fetch(); .fetch();
@@ -48,6 +47,14 @@ module.exports = {
}, },
inputs.request, inputs.request,
); );
sails.helpers.utils.sendWebhooks.with({
event: 'notificationUpdate',
data: {
item: notification,
},
user: inputs.actorUser,
});
}); });
return notifications; return notifications;
@@ -21,6 +21,10 @@ module.exports = {
custom: valuesValidator, custom: valuesValidator,
required: true, required: true,
}, },
actorUser: {
type: 'ref',
required: true,
},
request: { request: {
type: 'ref', type: 'ref',
}, },
@@ -55,6 +59,18 @@ module.exports = {
); );
}); });
sails.helpers.utils.sendWebhooks.with({
event: 'projectManagerCreate',
data: {
item: projectManager,
included: {
users: [values.user],
projects: [values.project],
},
},
user: inputs.actorUser,
});
return projectManager; return projectManager;
}, },
}; };
@@ -4,6 +4,10 @@ module.exports = {
type: 'ref', type: 'ref',
required: true, required: true,
}, },
actorUser: {
type: 'ref',
required: true,
},
request: { request: {
type: 'ref', type: 'ref',
}, },
@@ -27,6 +31,14 @@ module.exports = {
inputs.request, inputs.request,
); );
}); });
sails.helpers.utils.sendWebhooks.with({
event: 'projectManagerDelete',
data: {
item: projectManager,
},
user: inputs.actorUser,
});
} }
return projectManager; return projectManager;
+10 -2
View File
@@ -4,7 +4,7 @@ module.exports = {
type: 'json', type: 'json',
required: true, required: true,
}, },
user: { actorUser: {
type: 'ref', type: 'ref',
required: true, required: true,
}, },
@@ -20,7 +20,7 @@ module.exports = {
const projectManager = await ProjectManager.create({ const projectManager = await ProjectManager.create({
projectId: project.id, projectId: project.id,
userId: inputs.user.id, userId: inputs.actorUser.id,
}).fetch(); }).fetch();
sails.sockets.broadcast( sails.sockets.broadcast(
@@ -32,6 +32,14 @@ module.exports = {
inputs.request, inputs.request,
); );
sails.helpers.utils.sendWebhooks.with({
event: 'projectCreate',
data: {
item: project,
},
user: inputs.actorUser,
});
return { return {
project, project,
projectManager, projectManager,
+12
View File
@@ -4,6 +4,10 @@ module.exports = {
type: 'ref', type: 'ref',
required: true, required: true,
}, },
actorUser: {
type: 'ref',
required: true,
},
request: { request: {
type: 'ref', type: 'ref',
}, },
@@ -37,6 +41,14 @@ module.exports = {
inputs.request, inputs.request,
); );
}); });
sails.helpers.utils.sendWebhooks.with({
event: 'projectDelete',
data: {
item: project,
},
user: inputs.actorUser,
});
} }
return project; return project;
+12
View File
@@ -28,6 +28,10 @@ module.exports = {
custom: valuesValidator, custom: valuesValidator,
required: true, required: true,
}, },
actorUser: {
type: 'ref',
required: true,
},
request: { request: {
type: 'ref', type: 'ref',
}, },
@@ -108,6 +112,14 @@ module.exports = {
inputs.request, inputs.request,
); );
}); });
sails.helpers.utils.sendWebhooks.with({
event: 'projectUpdate',
data: {
item: project,
},
user: inputs.actorUser,
});
} }
return project; return project;
+32
View File
@@ -21,6 +21,22 @@ module.exports = {
custom: valuesValidator, custom: valuesValidator,
required: true, required: true,
}, },
project: {
type: 'ref',
required: true,
},
board: {
type: 'ref',
required: true,
},
list: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: { request: {
type: 'ref', type: 'ref',
}, },
@@ -50,6 +66,8 @@ module.exports = {
position: nextPosition, position: nextPosition,
}, },
}); });
// TODO: send webhooks
}); });
const task = await Task.create({ const task = await Task.create({
@@ -67,6 +85,20 @@ module.exports = {
inputs.request, inputs.request,
); );
sails.helpers.utils.sendWebhooks.with({
event: 'taskCreate',
data: {
item: task,
included: {
projects: [inputs.project],
boards: [inputs.board],
lists: [inputs.list],
cards: [values.card],
},
},
user: inputs.actorUser,
});
return task; return task;
}, },
}; };
+30
View File
@@ -4,10 +4,26 @@ module.exports = {
type: 'ref', type: 'ref',
required: true, required: true,
}, },
project: {
type: 'ref',
required: true,
},
board: { board: {
type: 'ref', type: 'ref',
required: true, required: true,
}, },
list: {
type: 'ref',
required: true,
},
card: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: { request: {
type: 'ref', type: 'ref',
}, },
@@ -25,6 +41,20 @@ module.exports = {
}, },
inputs.request, inputs.request,
); );
sails.helpers.utils.sendWebhooks.with({
event: 'taskDelete',
data: {
item: task,
included: {
projects: [inputs.project],
boards: [inputs.board],
lists: [inputs.list],
cards: [inputs.card],
},
},
user: inputs.actorUser,
});
} }
return task; return task;
+32
View File
@@ -21,10 +21,26 @@ module.exports = {
custom: valuesValidator, custom: valuesValidator,
required: true, required: true,
}, },
project: {
type: 'ref',
required: true,
},
board: { board: {
type: 'ref', type: 'ref',
required: true, required: true,
}, },
list: {
type: 'ref',
required: true,
},
card: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: { request: {
type: 'ref', type: 'ref',
}, },
@@ -57,6 +73,8 @@ module.exports = {
position: nextPosition, position: nextPosition,
}, },
}); });
// TODO: send webhooks
}); });
} }
@@ -71,6 +89,20 @@ module.exports = {
}, },
inputs.request, inputs.request,
); );
sails.helpers.utils.sendWebhooks.with({
event: 'taskUpdate',
data: {
item: task,
included: {
projects: [inputs.project],
boards: [inputs.board],
lists: [inputs.list],
cards: [inputs.card],
},
},
user: inputs.actorUser,
});
} }
return task; return task;
+12
View File
@@ -27,6 +27,10 @@ module.exports = {
custom: valuesValidator, custom: valuesValidator,
required: true, required: true,
}, },
actorUser: {
type: 'ref',
required: true,
},
request: { request: {
type: 'ref', type: 'ref',
}, },
@@ -84,6 +88,14 @@ module.exports = {
); );
}); });
sails.helpers.utils.sendWebhooks.with({
event: 'userCreate',
data: {
item: user,
},
user: inputs.actorUser,
});
return user; return user;
}, },
}; };
+12
View File
@@ -4,6 +4,10 @@ module.exports = {
type: 'ref', type: 'ref',
required: true, required: true,
}, },
actorUser: {
type: 'ref',
required: true,
},
request: { request: {
type: 'ref', type: 'ref',
}, },
@@ -59,6 +63,14 @@ module.exports = {
inputs.request, inputs.request,
); );
}); });
sails.helpers.utils.sendWebhooks.with({
event: 'userDelete',
data: {
item: user,
},
user: inputs.actorUser,
});
} }
return user; return user;
@@ -88,8 +88,11 @@ module.exports = {
// Otherwise, create a new user. // Otherwise, create a new user.
if (!user) { if (!user) {
user = await sails.helpers.users user = await sails.helpers.users.createOne
.createOne(values) .with({
values,
actorUser: User.OIDC,
})
.intercept('usernameAlreadyInUse', 'usernameAlreadyInUse'); .intercept('usernameAlreadyInUse', 'usernameAlreadyInUse');
} }
@@ -115,8 +118,12 @@ module.exports = {
} }
if (Object.keys(updateValues).length > 0) { if (Object.keys(updateValues).length > 0) {
user = await sails.helpers.users user = await sails.helpers.users.updateOne
.updateOne(user, updateValues, {}) // FIXME: hack for last parameter .with({
record: user,
values: updateValues,
actorUser: User.OIDC,
})
.intercept('emailAlreadyInUse', 'emailAlreadyInUse') .intercept('emailAlreadyInUse', 'emailAlreadyInUse')
.intercept('usernameAlreadyInUse', 'usernameAlreadyInUse'); .intercept('usernameAlreadyInUse', 'usernameAlreadyInUse');
} }
+14 -6
View File
@@ -38,7 +38,7 @@ module.exports = {
custom: valuesValidator, custom: valuesValidator,
required: true, required: true,
}, },
user: { actorUser: {
type: 'ref', type: 'ref',
required: true, required: true,
}, },
@@ -62,14 +62,14 @@ module.exports = {
let isOnlyPasswordChange = false; let isOnlyPasswordChange = false;
if (!_.isUndefined(values.password)) { if (!_.isUndefined(values.password)) {
if (Object.keys(values).length === 1) {
isOnlyPasswordChange = true;
}
Object.assign(values, { Object.assign(values, {
password: bcrypt.hashSync(values.password, 10), password: bcrypt.hashSync(values.password, 10),
passwordChangedAt: new Date().toISOString(), passwordChangedAt: new Date().toISOString(),
}); });
if (Object.keys(values).length === 1) {
isOnlyPasswordChange = true;
}
} }
if (values.username) { if (values.username) {
@@ -118,7 +118,7 @@ module.exports = {
inputs.request, inputs.request,
); );
if (user.id === inputs.user.id && inputs.request && inputs.request.isSocket) { if (user.id === inputs.actorUser.id && inputs.request && inputs.request.isSocket) {
const tempRoom = uuid(); const tempRoom = uuid();
sails.sockets.addRoomMembersToRooms(`@user:${user.id}`, tempRoom, () => { sails.sockets.addRoomMembersToRooms(`@user:${user.id}`, tempRoom, () => {
@@ -153,6 +153,14 @@ module.exports = {
inputs.request, inputs.request,
); );
}); });
sails.helpers.utils.sendWebhooks.with({
event: 'userUpdate',
data: {
item: user,
},
user: inputs.actorUser,
});
} }
} }

Some files were not shown because too many files have changed in this diff Show More