mirror of
https://github.com/plankanban/planka.git
synced 2025-12-06 09:13:16 +03:00
Compare commits
5 Commits
7be2343076
...
c058df8fc4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c058df8fc4 | ||
|
|
0023c63be8 | ||
|
|
26b3cffdab | ||
|
|
bf2ab4649e | ||
|
|
54e230d4c1 |
@@ -11,8 +11,8 @@ import { Link } from 'react-router-dom';
|
||||
import { Comment } from 'semantic-ui-react';
|
||||
|
||||
import selectors from '../../../selectors';
|
||||
import { isUserStatic } from '../../../utils/record-helpers';
|
||||
import Paths from '../../../constants/Paths';
|
||||
import { StaticUserIds } from '../../../constants/StaticUsers';
|
||||
import { ActivityTypes } from '../../../constants/Enums';
|
||||
import TimeAgo from '../../common/TimeAgo';
|
||||
import UserAvatar from '../../users/UserAvatar';
|
||||
@@ -30,12 +30,11 @@ const Item = React.memo(({ id }) => {
|
||||
|
||||
const [t] = useTranslation();
|
||||
|
||||
const userName =
|
||||
user.id === StaticUserIds.DELETED
|
||||
? t(`common.${user.name}`, {
|
||||
context: 'title',
|
||||
})
|
||||
: user.name;
|
||||
const userName = isUserStatic(user)
|
||||
? t(`common.${user.name}`, {
|
||||
context: 'title',
|
||||
})
|
||||
: user.name;
|
||||
|
||||
const cardName = card ? card.name : activity.data.card.name;
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import { useTranslation, Trans } from 'react-i18next';
|
||||
import { Comment } from 'semantic-ui-react';
|
||||
|
||||
import selectors from '../../../selectors';
|
||||
import { StaticUserIds } from '../../../constants/StaticUsers';
|
||||
import { isUserStatic } from '../../../utils/record-helpers';
|
||||
import { ActivityTypes } from '../../../constants/Enums';
|
||||
import TimeAgo from '../../common/TimeAgo';
|
||||
import UserAvatar from '../../users/UserAvatar';
|
||||
@@ -26,12 +26,11 @@ const Item = React.memo(({ id }) => {
|
||||
|
||||
const [t] = useTranslation();
|
||||
|
||||
const userName =
|
||||
user.id === StaticUserIds.DELETED
|
||||
? t(`common.${user.name}`, {
|
||||
context: 'title',
|
||||
})
|
||||
: user.name;
|
||||
const userName = isUserStatic(user)
|
||||
? t(`common.${user.name}`, {
|
||||
context: 'title',
|
||||
})
|
||||
: user.name;
|
||||
|
||||
let contentNode;
|
||||
switch (activity.type) {
|
||||
|
||||
@@ -15,13 +15,13 @@ import ListView from './ListView';
|
||||
const FiniteContent = React.memo(() => {
|
||||
const board = useSelector(selectors.selectCurrentBoard);
|
||||
const cardIds = useSelector(selectors.selectFilteredCardIdsForCurrentBoard);
|
||||
const hasAnyFiniteList = useSelector((state) => !!selectors.selectFirstFiniteListId(state));
|
||||
const canAddCard = useSelector((state) => !!selectors.selectFirstKanbanListId(state));
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const handleCardCreate = useCallback(
|
||||
(data, autoOpen) => {
|
||||
dispatch(entryActions.createCardInFirstFiniteList(data, undefined, autoOpen));
|
||||
dispatch(entryActions.createCardInCurrentContext(data, undefined, autoOpen));
|
||||
},
|
||||
[dispatch],
|
||||
);
|
||||
@@ -39,7 +39,7 @@ const FiniteContent = React.memo(() => {
|
||||
default:
|
||||
}
|
||||
|
||||
return <View cardIds={cardIds} onCardCreate={hasAnyFiniteList ? handleCardCreate : undefined} />;
|
||||
return <View cardIds={cardIds} onCardCreate={canAddCard ? handleCardCreate : undefined} />;
|
||||
});
|
||||
|
||||
export default FiniteContent;
|
||||
|
||||
@@ -23,7 +23,7 @@ import styles from './KanbanContent.module.scss';
|
||||
import globalStyles from '../../../../styles.module.scss';
|
||||
|
||||
const KanbanContent = React.memo(() => {
|
||||
const listIds = useSelector(selectors.selectFiniteListIdsForCurrentBoard);
|
||||
const listIds = useSelector(selectors.selectKanbanListIdsForCurrentBoard);
|
||||
|
||||
const canAddList = useSelector((state) => {
|
||||
const isEditModeEnabled = selectors.selectIsEditModeEnabled(state); // TODO: move out?
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
top: 2px;
|
||||
transition: background 85ms ease;
|
||||
width: 20px;
|
||||
z-index: 1000;
|
||||
|
||||
&:hover {
|
||||
background: #ebeef0;
|
||||
@@ -52,6 +53,7 @@
|
||||
background: #fff;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 1px 0 #ccc;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
|
||||
@@ -10,7 +10,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import { Icon } from 'semantic-ui-react';
|
||||
|
||||
import selectors from '../../../selectors';
|
||||
import { StaticUserIds } from '../../../constants/StaticUsers';
|
||||
import { isUserStatic } from '../../../utils/record-helpers';
|
||||
import TimeAgo from '../../common/TimeAgo';
|
||||
import UserAvatar from '../../users/UserAvatar';
|
||||
|
||||
@@ -32,7 +32,7 @@ const CreationDetailsStep = React.memo(({ userId }) => {
|
||||
</span>
|
||||
<span className={styles.content}>
|
||||
<div className={styles.name}>
|
||||
{user.id === StaticUserIds.DELETED
|
||||
{isUserStatic(user)
|
||||
? t(`common.${user.name}`, {
|
||||
context: 'title',
|
||||
})
|
||||
|
||||
@@ -9,32 +9,32 @@ import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Gallery, Item as GalleryItem } from 'react-photoswipe-gallery';
|
||||
import { Button, Grid, Icon } from 'semantic-ui-react';
|
||||
import { useDidUpdate } from '../../../../lib/hooks';
|
||||
import { useDidUpdate } from '../../../lib/hooks';
|
||||
|
||||
import selectors from '../../../../selectors';
|
||||
import entryActions from '../../../../entry-actions';
|
||||
import { usePopupInClosableContext } from '../../../../hooks';
|
||||
import { isUsableMarkdownElement } from '../../../../utils/element-helpers';
|
||||
import { BoardMembershipRoles, CardTypes, ListTypes } from '../../../../constants/Enums';
|
||||
import { CardTypeIcons } from '../../../../constants/Icons';
|
||||
import { ClosableContext } from '../../../../contexts';
|
||||
import selectors from '../../../selectors';
|
||||
import entryActions from '../../../entry-actions';
|
||||
import { usePopupInClosableContext } from '../../../hooks';
|
||||
import { isUsableMarkdownElement } from '../../../utils/element-helpers';
|
||||
import { BoardMembershipRoles, CardTypes, ListTypes } from '../../../constants/Enums';
|
||||
import { CardTypeIcons } from '../../../constants/Icons';
|
||||
import { ClosableContext } from '../../../contexts';
|
||||
import NameField from './NameField';
|
||||
import Thumbnail from './Thumbnail';
|
||||
import NameField from '../NameField';
|
||||
import CustomFieldGroups from '../CustomFieldGroups';
|
||||
import Communication from '../Communication';
|
||||
import CreationDetailsStep from '../CreationDetailsStep';
|
||||
import MoreActionsStep from '../MoreActionsStep';
|
||||
import Markdown from '../../../common/Markdown';
|
||||
import EditMarkdown from '../../../common/EditMarkdown';
|
||||
import ConfirmationStep from '../../../common/ConfirmationStep';
|
||||
import UserAvatar from '../../../users/UserAvatar';
|
||||
import BoardMembershipsStep from '../../../board-memberships/BoardMembershipsStep';
|
||||
import LabelChip from '../../../labels/LabelChip';
|
||||
import LabelsStep from '../../../labels/LabelsStep';
|
||||
import ListsStep from '../../../lists/ListsStep';
|
||||
import Attachments from '../../../attachments/Attachments';
|
||||
import AddAttachmentStep from '../../../attachments/AddAttachmentStep';
|
||||
import AddCustomFieldGroupStep from '../../../custom-field-groups/AddCustomFieldGroupStep';
|
||||
import CustomFieldGroups from './CustomFieldGroups';
|
||||
import Communication from './Communication';
|
||||
import CreationDetailsStep from './CreationDetailsStep';
|
||||
import MoreActionsStep from './MoreActionsStep';
|
||||
import Markdown from '../../common/Markdown';
|
||||
import EditMarkdown from '../../common/EditMarkdown';
|
||||
import ConfirmationStep from '../../common/ConfirmationStep';
|
||||
import UserAvatar from '../../users/UserAvatar';
|
||||
import BoardMembershipsStep from '../../board-memberships/BoardMembershipsStep';
|
||||
import LabelChip from '../../labels/LabelChip';
|
||||
import LabelsStep from '../../labels/LabelsStep';
|
||||
import ListsStep from '../../lists/ListsStep';
|
||||
import Attachments from '../../attachments/Attachments';
|
||||
import AddAttachmentStep from '../../attachments/AddAttachmentStep';
|
||||
import AddCustomFieldGroupStep from '../../custom-field-groups/AddCustomFieldGroupStep';
|
||||
|
||||
import styles from './StoryContent.module.scss';
|
||||
|
||||
@@ -99,7 +99,7 @@
|
||||
}
|
||||
|
||||
.coverWrapper {
|
||||
padding-bottom: 20px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.cursorPointer {
|
||||
@@ -283,7 +283,7 @@
|
||||
}
|
||||
|
||||
.moduleWrapperAttachments {
|
||||
margin-bottom: 20px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.moreActionsButton {
|
||||
@@ -1,8 +0,0 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import StoryContent from './StoryContent';
|
||||
|
||||
export default StoryContent;
|
||||
@@ -8,7 +8,7 @@ import PropTypes from 'prop-types';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { Item as GalleryItem } from 'react-photoswipe-gallery';
|
||||
|
||||
import selectors from '../../../../selectors';
|
||||
import selectors from '../../../selectors';
|
||||
|
||||
import styles from './Thumbnail.module.scss';
|
||||
|
||||
@@ -14,8 +14,7 @@ import { useDidUpdate } from '../../../lib/hooks';
|
||||
import selectors from '../../../selectors';
|
||||
import entryActions from '../../../entry-actions';
|
||||
import { usePopupInClosableContext } from '../../../hooks';
|
||||
import { isListArchiveOrTrash } from '../../../utils/record-helpers';
|
||||
import { StaticUserIds } from '../../../constants/StaticUsers';
|
||||
import { isListArchiveOrTrash, isUserStatic } from '../../../utils/record-helpers';
|
||||
import { BoardMembershipRoles } from '../../../constants/Enums';
|
||||
import { ClosableContext } from '../../../contexts';
|
||||
import Edit from './Edit';
|
||||
@@ -107,7 +106,7 @@ const Item = React.memo(({ id }) => {
|
||||
) : (
|
||||
<div className={classNames(styles.bubble, isCurrentUser && styles.bubbleRight)}>
|
||||
<div className={styles.header}>
|
||||
{user.id === StaticUserIds.DELETED
|
||||
{isUserStatic(user)
|
||||
? t(`common.${user.name}`, {
|
||||
context: 'title',
|
||||
})
|
||||
|
||||
@@ -17,9 +17,9 @@ import AddStep from './AddStep';
|
||||
import styles from './UsersPane.module.scss';
|
||||
|
||||
const UsersPane = React.memo(() => {
|
||||
const activeUsersTotal = useSelector(selectors.selectActiveUsersTotal);
|
||||
const activeUsersLimit = useSelector(selectors.selectActiveUsersLimit);
|
||||
const users = useSelector(selectors.selectUsers);
|
||||
const activeUsersTotal = useSelector(selectors.selectActiveUsersTotal);
|
||||
|
||||
const canAdd = useSelector((state) => {
|
||||
const oidcBootstrap = selectors.selectOidcBootstrap(state);
|
||||
|
||||
@@ -12,7 +12,7 @@ import selectors from '../../../selectors';
|
||||
import matchPaths from '../../../utils/match-paths';
|
||||
import Paths from '../../../constants/Paths';
|
||||
|
||||
const Linkify = React.memo(({ href, content, stopPropagation, ...props }) => {
|
||||
const Link = React.memo(({ href, content, stopPropagation, ...props }) => {
|
||||
const selectCardById = useMemo(() => selectors.makeSelectCardById(), []);
|
||||
|
||||
const url = useMemo(() => {
|
||||
@@ -68,14 +68,14 @@ const Linkify = React.memo(({ href, content, stopPropagation, ...props }) => {
|
||||
);
|
||||
});
|
||||
|
||||
Linkify.propTypes = {
|
||||
Link.propTypes = {
|
||||
href: PropTypes.string.isRequired,
|
||||
content: PropTypes.string.isRequired,
|
||||
stopPropagation: PropTypes.bool,
|
||||
};
|
||||
|
||||
Linkify.defaultProps = {
|
||||
Link.defaultProps = {
|
||||
stopPropagation: false,
|
||||
};
|
||||
|
||||
export default Linkify;
|
||||
export default Link;
|
||||
|
||||
@@ -14,8 +14,8 @@ import { Button } from 'semantic-ui-react';
|
||||
import selectors from '../../../selectors';
|
||||
import entryActions from '../../../entry-actions';
|
||||
import { mentionMarkupToText } from '../../../utils/mentions';
|
||||
import { isUserStatic } from '../../../utils/record-helpers';
|
||||
import Paths from '../../../constants/Paths';
|
||||
import { StaticUserIds } from '../../../constants/StaticUsers';
|
||||
import { NotificationTypes } from '../../../constants/Enums';
|
||||
import TimeAgo from '../../common/TimeAgo';
|
||||
import UserAvatar from '../../users/UserAvatar';
|
||||
@@ -42,12 +42,11 @@ const Item = React.memo(({ id, onClose }) => {
|
||||
dispatch(entryActions.deleteNotification(id));
|
||||
}, [id, dispatch]);
|
||||
|
||||
const creatorUserName =
|
||||
creatorUser.id === StaticUserIds.DELETED
|
||||
? t(`common.${creatorUser.name}`, {
|
||||
context: 'title',
|
||||
})
|
||||
: creatorUser.name;
|
||||
const creatorUserName = isUserStatic(creatorUser)
|
||||
? t(`common.${creatorUser.name}`, {
|
||||
context: 'title',
|
||||
})
|
||||
: creatorUser.name;
|
||||
|
||||
const cardName = card ? card.name : notification.data.card.name;
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import { useSelector } from 'react-redux';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import selectors from '../../../selectors';
|
||||
import { StaticUserIds } from '../../../constants/StaticUsers';
|
||||
import { isUserStatic } from '../../../utils/record-helpers';
|
||||
|
||||
import styles from './UserAvatar.module.scss';
|
||||
|
||||
@@ -62,7 +62,7 @@ const UserAvatar = React.memo(
|
||||
const contentNode = (
|
||||
<span
|
||||
title={
|
||||
user.id === StaticUserIds.DELETED
|
||||
isUserStatic(user)
|
||||
? t(`common.${user.name}`, {
|
||||
context: 'title',
|
||||
})
|
||||
|
||||
@@ -186,7 +186,7 @@ export default {
|
||||
CARDS_IN_CURRENT_LIST_FETCH: `${PREFIX}/CARDS_IN_CURRENT_LIST_FETCH`,
|
||||
CARDS_UPDATE_HANDLE: `${PREFIX}/CARDS_UPDATE_HANDLE`,
|
||||
CARD_CREATE: `${PREFIX}/CARD_CREATE`,
|
||||
CARD_IN_FIRST_FINITE_LIST_CREATE: `${PREFIX}/CARD_IN_FIRST_FINITE_LIST_CREATE`,
|
||||
CARD_IN_CURRENT_CONTEXT_CREATE: `${PREFIX}/CARD_IN_CURRENT_CONTEXT_CREATE`,
|
||||
CARD_IN_CURRENT_LIST_CREATE: `${PREFIX}/CARD_IN_CURRENT_LIST_CREATE`,
|
||||
CARD_CREATE_HANDLE: `${PREFIX}/CARD_CREATE_HANDLE`,
|
||||
CARD_UPDATE: `${PREFIX}/CARD_UPDATE`,
|
||||
|
||||
@@ -28,8 +28,8 @@ const createCard = (listId, data, index, autoOpen = false) => ({
|
||||
},
|
||||
});
|
||||
|
||||
const createCardInFirstFiniteList = (data, index = 0, autoOpen = false) => ({
|
||||
type: EntryActionTypes.CARD_IN_FIRST_FINITE_LIST_CREATE,
|
||||
const createCardInCurrentContext = (data, index = 0, autoOpen = false) => ({
|
||||
type: EntryActionTypes.CARD_IN_CURRENT_CONTEXT_CREATE,
|
||||
payload: {
|
||||
data,
|
||||
index,
|
||||
@@ -180,7 +180,7 @@ export default {
|
||||
fetchCardsInCurrentList,
|
||||
handleCardsUpdate,
|
||||
createCard,
|
||||
createCardInFirstFiniteList,
|
||||
createCardInCurrentContext,
|
||||
createCardInCurrentList,
|
||||
handleCardCreate,
|
||||
updateCard,
|
||||
|
||||
@@ -7,7 +7,7 @@ import { attr, fk, many } from 'redux-orm';
|
||||
|
||||
import BaseModel from './BaseModel';
|
||||
import buildSearchParts from '../utils/build-search-parts';
|
||||
import { isListFinite } from '../utils/record-helpers';
|
||||
import { isListKanban } from '../utils/record-helpers';
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
import Config from '../constants/Config';
|
||||
import { BoardContexts, BoardViews } from '../constants/Enums';
|
||||
@@ -285,8 +285,8 @@ export default class extends BaseModel {
|
||||
return this.lists.orderBy(['position', 'id.length', 'id']);
|
||||
}
|
||||
|
||||
getFiniteListsQuerySet() {
|
||||
return this.getListsQuerySet().filter((list) => isListFinite(list));
|
||||
getKanbanListsQuerySet() {
|
||||
return this.getListsQuerySet().filter((list) => isListKanban(list));
|
||||
}
|
||||
|
||||
getCustomFieldGroupsQuerySet() {
|
||||
@@ -316,7 +316,7 @@ export default class extends BaseModel {
|
||||
}
|
||||
|
||||
getCardsModelArray() {
|
||||
return this.getFiniteListsQuerySet()
|
||||
return this.getKanbanListsQuerySet()
|
||||
.toModelArray()
|
||||
.flatMap((listModel) => listModel.getCardsModelArray());
|
||||
}
|
||||
|
||||
@@ -336,13 +336,11 @@ export default class extends BaseModel {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (searchRegex) {
|
||||
cardModels = cardModels.filter(
|
||||
(cardModel) =>
|
||||
searchRegex.test(cardModel.name) ||
|
||||
(cardModel.description && searchRegex.test(cardModel.description)),
|
||||
);
|
||||
}
|
||||
cardModels = cardModels.filter(
|
||||
(cardModel) =>
|
||||
searchRegex.test(cardModel.name) ||
|
||||
(cardModel.description && searchRegex.test(cardModel.description)),
|
||||
);
|
||||
} else {
|
||||
const searchParts = buildSearchParts(this.board.search);
|
||||
|
||||
|
||||
@@ -40,8 +40,8 @@ export function* fetchCards(listId) {
|
||||
try {
|
||||
response.body = yield call(request, api.getCards, listId, {
|
||||
search: (search && search.trim()) || undefined,
|
||||
filterUserIds: filterUserIds.length > 0 ? filterUserIds.join(',') : undefined,
|
||||
filterLabelIds: filterLabelIds.length > 0 ? filterLabelIds.join(',') : undefined,
|
||||
userIds: filterUserIds.length > 0 ? filterUserIds.join(',') : undefined,
|
||||
labelIds: filterLabelIds.length > 0 ? filterLabelIds.join(',') : undefined,
|
||||
before: lastCard || undefined,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -167,10 +167,10 @@ export function* createCard(listId, data, index, autoOpen) {
|
||||
}
|
||||
}
|
||||
|
||||
export function* createCardInFirstFiniteList(data, index, autoOpen) {
|
||||
const firstFiniteListId = yield select(selectors.selectFirstFiniteListId);
|
||||
export function* createCardInCurrentContext(data, index, autoOpen) {
|
||||
const firstKanbanListId = yield select(selectors.selectFirstKanbanListId);
|
||||
|
||||
yield call(createCard, firstFiniteListId, data, index, autoOpen);
|
||||
yield call(createCard, firstKanbanListId, data, index, autoOpen);
|
||||
}
|
||||
|
||||
export function* createCardInCurrentList(data, autoOpen) {
|
||||
@@ -601,7 +601,7 @@ export default {
|
||||
fetchCardsInCurrentList,
|
||||
handleCardsUpdate,
|
||||
createCard,
|
||||
createCardInFirstFiniteList,
|
||||
createCardInCurrentContext,
|
||||
createCardInCurrentList,
|
||||
handleCardCreate,
|
||||
updateCard,
|
||||
|
||||
@@ -20,9 +20,9 @@ export default function* cardsWatchers() {
|
||||
services.createCard(listId, data, index, autoOpen),
|
||||
),
|
||||
takeEvery(
|
||||
EntryActionTypes.CARD_IN_FIRST_FINITE_LIST_CREATE,
|
||||
EntryActionTypes.CARD_IN_CURRENT_CONTEXT_CREATE,
|
||||
({ payload: { data, index, autoOpen } }) =>
|
||||
services.createCardInFirstFiniteList(data, index, autoOpen),
|
||||
services.createCardInCurrentContext(data, index, autoOpen),
|
||||
),
|
||||
takeEvery(EntryActionTypes.CARD_IN_CURRENT_LIST_CREATE, ({ payload: { data, autoOpen } }) =>
|
||||
services.createCardInCurrentList(data, autoOpen),
|
||||
|
||||
@@ -271,7 +271,7 @@ export const selectTrashListIdForCurrentBoard = createSelector(
|
||||
},
|
||||
);
|
||||
|
||||
export const selectFiniteListIdsForCurrentBoard = createSelector(
|
||||
export const selectKanbanListIdsForCurrentBoard = createSelector(
|
||||
orm,
|
||||
(state) => selectPath(state).boardId,
|
||||
({ Board }, id) => {
|
||||
@@ -286,7 +286,7 @@ export const selectFiniteListIdsForCurrentBoard = createSelector(
|
||||
}
|
||||
|
||||
return boardModel
|
||||
.getFiniteListsQuerySet()
|
||||
.getKanbanListsQuerySet()
|
||||
.toRefArray()
|
||||
.map((list) => list.id);
|
||||
},
|
||||
@@ -482,7 +482,7 @@ export default {
|
||||
selectLabelsForCurrentBoard,
|
||||
selectArchiveListIdForCurrentBoard,
|
||||
selectTrashListIdForCurrentBoard,
|
||||
selectFiniteListIdsForCurrentBoard,
|
||||
selectKanbanListIdsForCurrentBoard,
|
||||
selectAvailableListsForCurrentBoard,
|
||||
selectCardsExceptCurrentForCurrentBoard,
|
||||
selectFilteredCardIdsForCurrentBoard,
|
||||
|
||||
@@ -127,7 +127,7 @@ export const selectCurrentList = createSelector(
|
||||
},
|
||||
);
|
||||
|
||||
export const selectFirstFiniteListId = createSelector(
|
||||
export const selectFirstKanbanListId = createSelector(
|
||||
orm,
|
||||
(state) => selectPath(state).boardId,
|
||||
({ Board }, id) => {
|
||||
@@ -141,7 +141,7 @@ export const selectFirstFiniteListId = createSelector(
|
||||
return boardModel;
|
||||
}
|
||||
|
||||
const listModel = boardModel.getFiniteListsQuerySet().first();
|
||||
const listModel = boardModel.getKanbanListsQuerySet().first();
|
||||
return listModel && listModel.id;
|
||||
},
|
||||
);
|
||||
@@ -174,6 +174,6 @@ export default {
|
||||
selectIsListWithIdAvailableForCurrentUser,
|
||||
selectCurrentListId,
|
||||
selectCurrentList,
|
||||
selectFirstFiniteListId,
|
||||
selectFirstKanbanListId,
|
||||
selectFilteredCardIdsForCurrentList,
|
||||
};
|
||||
|
||||
@@ -75,7 +75,7 @@ export const selectNextListPosition = createSelector(
|
||||
return boardModel;
|
||||
}
|
||||
|
||||
return nextPosition(boardModel.getFiniteListsQuerySet().toRefArray(), index, excludedId);
|
||||
return nextPosition(boardModel.getKanbanListsQuerySet().toRefArray(), index, excludedId);
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@@ -3,8 +3,11 @@
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import { StaticUserIds } from '../constants/StaticUsers';
|
||||
import { ListTypes, UserRoles } from '../constants/Enums';
|
||||
|
||||
export const isUserStatic = (user) => [StaticUserIds.DELETED].includes(user.id);
|
||||
|
||||
export const isUserAdminOrProjectOwner = (user) =>
|
||||
[UserRoles.ADMIN, UserRoles.PROJECT_OWNER].includes(user.role);
|
||||
|
||||
@@ -12,3 +15,5 @@ export const isListArchiveOrTrash = (list) =>
|
||||
[ListTypes.ARCHIVE, ListTypes.TRASH].includes(list.type);
|
||||
|
||||
export const isListFinite = (list) => [ListTypes.ACTIVE, ListTypes.CLOSED].includes(list.type);
|
||||
|
||||
export const isListKanban = (list) => [ListTypes.ACTIVE, ListTypes.CLOSED].includes(list.type);
|
||||
|
||||
@@ -11,6 +11,7 @@ const USERNAME_REGEX = /^[a-zA-Z0-9]+((_|\.)?[a-zA-Z0-9])*$/;
|
||||
export const isUrl = (string) =>
|
||||
isURL(string, {
|
||||
protocols: ['http', 'https'],
|
||||
require_tld: false,
|
||||
require_protocol: true,
|
||||
max_allowed_length: 2048,
|
||||
});
|
||||
|
||||
@@ -23,10 +23,10 @@
|
||||
* - name: before
|
||||
* in: query
|
||||
* required: false
|
||||
* description: Pagination cursor (JSON object with id and listChangedAt)
|
||||
* description: Pagination cursor (JSON object with listChangedAt and id)
|
||||
* schema:
|
||||
* type: string
|
||||
* example: '{"id": "1357158568008091269", "listChangedAt": "2024-01-01T00:00:00.000Z"}'
|
||||
* example: '{"listChangedAt": "2024-01-01T00:00:00.000Z", "id": "1357158568008091269"}'
|
||||
* - name: search
|
||||
* in: query
|
||||
* required: false
|
||||
@@ -35,14 +35,14 @@
|
||||
* type: string
|
||||
* maxLength: 128
|
||||
* example: bug fix
|
||||
* - name: filterUserIds
|
||||
* - name: userIds
|
||||
* in: query
|
||||
* required: false
|
||||
* description: Comma-separated user IDs to filter by members
|
||||
* description: Comma-separated user IDs to filter by members or task assignees
|
||||
* schema:
|
||||
* type: string
|
||||
* example: 1357158568008091265,1357158568008091266
|
||||
* - name: filterLabelIds
|
||||
* - name: labelIds
|
||||
* in: query
|
||||
* required: false
|
||||
* description: Comma-separated label IDs to filter by labels
|
||||
@@ -179,8 +179,8 @@ module.exports = {
|
||||
isNotEmptyString: true,
|
||||
maxLength: 128,
|
||||
},
|
||||
filterUserIds: idsInput,
|
||||
filterLabelIds: idsInput,
|
||||
userIds: idsInput,
|
||||
labelIds: idsInput,
|
||||
},
|
||||
|
||||
exits: {
|
||||
@@ -215,31 +215,31 @@ module.exports = {
|
||||
}
|
||||
|
||||
let filterUserIds;
|
||||
if (inputs.filterUserIds) {
|
||||
if (inputs.userIds) {
|
||||
const boardMemberships = await BoardMembership.qm.getByBoardId(list.boardId);
|
||||
|
||||
const availableUserIdsSet = new Set(
|
||||
sails.helpers.utils.mapRecords(boardMemberships, 'userId'),
|
||||
);
|
||||
|
||||
filterUserIds = _.uniq(inputs.filterUserIds.split(','));
|
||||
filterUserIds = _.uniq(inputs.userIds.split(','));
|
||||
filterUserIds = filterUserIds.filter((userId) => availableUserIdsSet.has(userId));
|
||||
}
|
||||
|
||||
let filterLabelIds;
|
||||
if (inputs.filterLabelIds) {
|
||||
if (inputs.labelIds) {
|
||||
const labels = await Label.qm.getByBoardId(list.boardId);
|
||||
const availableLabelIdsSet = new Set(sails.helpers.utils.mapRecords(labels));
|
||||
|
||||
filterLabelIds = _.uniq(inputs.filterLabelIds.split(','));
|
||||
filterLabelIds = _.uniq(inputs.labelIds.split(','));
|
||||
filterLabelIds = filterLabelIds.filter((labelId) => availableLabelIdsSet.has(labelId));
|
||||
}
|
||||
|
||||
const cards = await Card.qm.getByEndlessListId(list.id, {
|
||||
filterUserIds,
|
||||
filterLabelIds,
|
||||
before: inputs.before,
|
||||
search: inputs.search,
|
||||
userIds: filterUserIds,
|
||||
labelIds: filterLabelIds,
|
||||
});
|
||||
|
||||
const cardIds = sails.helpers.utils.mapRecords(cards);
|
||||
|
||||
@@ -87,7 +87,7 @@ module.exports = {
|
||||
},
|
||||
type: {
|
||||
type: 'string',
|
||||
isIn: List.FINITE_TYPES,
|
||||
isIn: List.KANBAN_TYPES,
|
||||
required: true,
|
||||
},
|
||||
position: {
|
||||
|
||||
@@ -100,7 +100,7 @@ module.exports = {
|
||||
throw Errors.LIST_NOT_FOUND; // Forbidden
|
||||
}
|
||||
|
||||
if (!sails.helpers.lists.isFinite(list)) {
|
||||
if (!sails.helpers.lists.isKanban(list)) {
|
||||
throw Errors.NOT_ENOUGH_RIGHTS;
|
||||
}
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ module.exports = {
|
||||
boardId: idInput,
|
||||
type: {
|
||||
type: 'string',
|
||||
isIn: List.FINITE_TYPES,
|
||||
isIn: List.KANBAN_TYPES,
|
||||
},
|
||||
position: {
|
||||
type: 'number',
|
||||
@@ -146,7 +146,7 @@ module.exports = {
|
||||
throw Errors.LIST_NOT_FOUND; // Forbidden
|
||||
}
|
||||
|
||||
if (!sails.helpers.lists.isFinite(list)) {
|
||||
if (!sails.helpers.lists.isKanban(list)) {
|
||||
throw Errors.NOT_ENOUGH_RIGHTS;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* operationId: getUsers
|
||||
* responses:
|
||||
* 200:
|
||||
* description: List of users retrieved successfully
|
||||
* description: Users retrieved successfully
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* operationId: getWebhooks
|
||||
* responses:
|
||||
* 200:
|
||||
* description: List of webhooks retrieved successfully
|
||||
* description: Webhooks retrieved successfully
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
|
||||
@@ -23,7 +23,7 @@ const buildBodyByFormat = (board, card, action, actorUser, t) => {
|
||||
|
||||
switch (action.type) {
|
||||
case Action.Types.CREATE_CARD: {
|
||||
const listName = sails.helpers.lists.makeName(action.data.list);
|
||||
const listName = sails.helpers.lists.resolveName(action.data.list, t);
|
||||
|
||||
return {
|
||||
text: t('%s created %s in %s on %s', actorUser.name, card.name, listName, board.name),
|
||||
@@ -44,8 +44,8 @@ const buildBodyByFormat = (board, card, action, actorUser, t) => {
|
||||
};
|
||||
}
|
||||
case Action.Types.MOVE_CARD: {
|
||||
const fromListName = sails.helpers.lists.makeName(action.data.fromList);
|
||||
const toListName = sails.helpers.lists.makeName(action.data.toList);
|
||||
const fromListName = sails.helpers.lists.resolveName(action.data.fromList, t);
|
||||
const toListName = sails.helpers.lists.resolveName(action.data.toList, t);
|
||||
|
||||
return {
|
||||
text: t(
|
||||
|
||||
@@ -17,7 +17,7 @@ module.exports = {
|
||||
async fn(inputs) {
|
||||
return List.qm.getByBoardId(inputs.id, {
|
||||
exceptIdOrIds: inputs.exceptListIdOrIds,
|
||||
typeOrTypes: List.FINITE_TYPES,
|
||||
typeOrTypes: List.KANBAN_TYPES,
|
||||
});
|
||||
},
|
||||
};
|
||||
@@ -41,12 +41,26 @@ module.exports = {
|
||||
async fn(inputs) {
|
||||
const { values } = inputs;
|
||||
|
||||
if (sails.helpers.lists.isFinite(inputs.list)) {
|
||||
if (values.list) {
|
||||
const typeState = List.TYPE_STATE_BY_TYPE[values.list.type];
|
||||
|
||||
if (inputs.record.isClosed) {
|
||||
if (typeState === List.TypeStates.OPENED) {
|
||||
values.isClosed = false;
|
||||
}
|
||||
} else if (typeState === List.TypeStates.CLOSED) {
|
||||
values.isClosed = true;
|
||||
}
|
||||
}
|
||||
|
||||
const list = values.list || inputs.list;
|
||||
|
||||
if (sails.helpers.lists.isFinite(list)) {
|
||||
if (_.isUndefined(values.position)) {
|
||||
throw 'positionMustBeInValues';
|
||||
}
|
||||
|
||||
const cards = await Card.qm.getByListId(inputs.list.id);
|
||||
const cards = await Card.qm.getByListId(list.id);
|
||||
|
||||
const { position, repositions } = sails.helpers.utils.insertToPositionables(
|
||||
values.position,
|
||||
@@ -84,7 +98,6 @@ module.exports = {
|
||||
let card = await Card.qm.createOne({
|
||||
..._.pick(inputs.record, [
|
||||
'boardId',
|
||||
'listId',
|
||||
'prevListId',
|
||||
'type',
|
||||
'name',
|
||||
@@ -95,6 +108,7 @@ module.exports = {
|
||||
'isClosed',
|
||||
]),
|
||||
...values,
|
||||
listId: list.id,
|
||||
creatorUserId: values.creatorUser.id,
|
||||
listChangedAt: new Date().toISOString(),
|
||||
});
|
||||
@@ -240,7 +254,7 @@ module.exports = {
|
||||
included: {
|
||||
projects: [inputs.project],
|
||||
boards: [inputs.board],
|
||||
lists: [inputs.list],
|
||||
lists: [list],
|
||||
cardMemberships: nextCardMemberships,
|
||||
cardLabels: nextCardLabels,
|
||||
taskLists: nextTaskLists,
|
||||
@@ -277,19 +291,19 @@ module.exports = {
|
||||
}
|
||||
|
||||
await sails.helpers.actions.createOne.with({
|
||||
list,
|
||||
webhooks,
|
||||
values: {
|
||||
card,
|
||||
type: Action.Types.CREATE_CARD, // TODO: introduce separate type?
|
||||
data: {
|
||||
card: _.pick(card, ['name']),
|
||||
list: _.pick(inputs.list, ['id', 'type', 'name']),
|
||||
list: _.pick(list, ['id', 'type', 'name']),
|
||||
},
|
||||
user: values.creatorUser,
|
||||
},
|
||||
project: inputs.project,
|
||||
board: inputs.board,
|
||||
list: inputs.list,
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
@@ -25,7 +25,7 @@ module.exports = {
|
||||
async fn(inputs) {
|
||||
const { values } = inputs;
|
||||
|
||||
const lists = await sails.helpers.boards.getFiniteListsById(values.board.id);
|
||||
const lists = await sails.helpers.boards.getKanbanListsById(values.board.id);
|
||||
|
||||
const { position, repositions } = sails.helpers.utils.insertToPositionables(
|
||||
values.position,
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
// TODO: rename?
|
||||
module.exports = {
|
||||
sync: true,
|
||||
|
||||
@@ -15,6 +14,6 @@ module.exports = {
|
||||
},
|
||||
|
||||
fn(inputs) {
|
||||
return inputs.record.name || _.upperFirst(inputs.record.type);
|
||||
return List.KANBAN_TYPES.includes(inputs.record.type);
|
||||
},
|
||||
};
|
||||
27
server/api/helpers/lists/resolve-name.js
Normal file
27
server/api/helpers/lists/resolve-name.js
Normal file
@@ -0,0 +1,27 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
sync: true,
|
||||
|
||||
inputs: {
|
||||
record: {
|
||||
type: 'ref',
|
||||
required: true,
|
||||
},
|
||||
t: {
|
||||
type: 'ref',
|
||||
},
|
||||
},
|
||||
|
||||
fn(inputs) {
|
||||
if (inputs.record.name) {
|
||||
return inputs.record.name;
|
||||
}
|
||||
|
||||
const name = _.upperFirst(inputs.record.type);
|
||||
return inputs.t ? inputs.t(name) : name;
|
||||
},
|
||||
};
|
||||
@@ -58,7 +58,7 @@ module.exports = {
|
||||
const board = values.board || inputs.board;
|
||||
|
||||
if (!_.isUndefined(values.position)) {
|
||||
const lists = await sails.helpers.boards.getFiniteListsById(board.id, inputs.record.id);
|
||||
const lists = await sails.helpers.boards.getKanbanListsById(board.id, inputs.record.id);
|
||||
|
||||
const { position, repositions } = sails.helpers.utils.insertToPositionables(
|
||||
values.position,
|
||||
|
||||
@@ -29,8 +29,8 @@ const buildBodyByFormat = (board, card, notification, actorUser, t) => {
|
||||
|
||||
switch (notification.type) {
|
||||
case Notification.Types.MOVE_CARD: {
|
||||
const fromListName = sails.helpers.lists.makeName(notification.data.fromList);
|
||||
const toListName = sails.helpers.lists.makeName(notification.data.toList);
|
||||
const fromListName = sails.helpers.lists.resolveName(notification.data.fromList, t);
|
||||
const toListName = sails.helpers.lists.resolveName(notification.data.toList, t);
|
||||
|
||||
return {
|
||||
text: t(
|
||||
@@ -144,8 +144,8 @@ const buildEmail = (board, card, notification, actorUser, notifiableUser, t) =>
|
||||
let html;
|
||||
switch (notification.type) {
|
||||
case Notification.Types.MOVE_CARD: {
|
||||
const fromListName = sails.helpers.lists.makeName(notification.data.fromList);
|
||||
const toListName = sails.helpers.lists.makeName(notification.data.toList);
|
||||
const fromListName = sails.helpers.lists.resolveName(notification.data.fromList, t);
|
||||
const toListName = sails.helpers.lists.resolveName(notification.data.toList, t);
|
||||
|
||||
html = `<p>${t(
|
||||
'%s moved %s from %s to %s on %s',
|
||||
@@ -243,10 +243,6 @@ module.exports = {
|
||||
arrayOfValues.map((values) => {
|
||||
const id = ids.shift();
|
||||
|
||||
const isCommentRelated =
|
||||
values.type === Notification.Types.COMMENT_CARD ||
|
||||
values.type === Notification.Types.MENTION_IN_COMMENT;
|
||||
|
||||
const nextValues = {
|
||||
...values,
|
||||
id,
|
||||
@@ -254,10 +250,10 @@ module.exports = {
|
||||
boardId: values.card.boardId,
|
||||
cardId: values.card.id,
|
||||
};
|
||||
|
||||
if (isCommentRelated) {
|
||||
if (values.comment) {
|
||||
nextValues.commentId = values.comment.id;
|
||||
} else {
|
||||
}
|
||||
if (values.action) {
|
||||
nextValues.actionId = values.action.id;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,8 +29,8 @@ const buildBodyByFormat = (board, card, notification, actorUser, t) => {
|
||||
|
||||
switch (notification.type) {
|
||||
case Notification.Types.MOVE_CARD: {
|
||||
const fromListName = sails.helpers.lists.makeName(notification.data.fromList);
|
||||
const toListName = sails.helpers.lists.makeName(notification.data.toList);
|
||||
const fromListName = sails.helpers.lists.resolveName(notification.data.fromList, t);
|
||||
const toListName = sails.helpers.lists.resolveName(notification.data.toList, t);
|
||||
|
||||
return {
|
||||
text: t(
|
||||
@@ -152,8 +152,8 @@ const buildAndSendEmail = async (
|
||||
let html;
|
||||
switch (notification.type) {
|
||||
case Notification.Types.MOVE_CARD: {
|
||||
const fromListName = sails.helpers.lists.makeName(notification.data.fromList);
|
||||
const toListName = sails.helpers.lists.makeName(notification.data.toList);
|
||||
const fromListName = sails.helpers.lists.resolveName(notification.data.fromList, t);
|
||||
const toListName = sails.helpers.lists.resolveName(notification.data.toList, t);
|
||||
|
||||
html = `<p>${t(
|
||||
'%s moved %s from %s to %s on %s',
|
||||
@@ -234,13 +234,11 @@ module.exports = {
|
||||
async fn(inputs) {
|
||||
const { values } = inputs;
|
||||
|
||||
const isCommentRelated =
|
||||
values.type === Notification.Types.COMMENT_CARD ||
|
||||
values.type === Notification.Types.MENTION_IN_COMMENT;
|
||||
|
||||
if (isCommentRelated) {
|
||||
if (values.comment) {
|
||||
values.commentId = values.comment.id;
|
||||
} else {
|
||||
}
|
||||
|
||||
if (values.action) {
|
||||
values.actionId = values.action.id;
|
||||
}
|
||||
|
||||
@@ -268,13 +266,12 @@ module.exports = {
|
||||
boards: [inputs.board],
|
||||
lists: [inputs.list],
|
||||
cards: [values.card],
|
||||
...(isCommentRelated
|
||||
? {
|
||||
comments: [values.comment],
|
||||
}
|
||||
: {
|
||||
actions: [values.action],
|
||||
}),
|
||||
...(values.comment && {
|
||||
comments: [values.comment],
|
||||
}),
|
||||
...(values.action && {
|
||||
actions: [values.action],
|
||||
}),
|
||||
},
|
||||
}),
|
||||
user: values.creatorUser,
|
||||
|
||||
@@ -7,6 +7,7 @@ module.exports = {
|
||||
inputs: {
|
||||
roleOrRoles: {
|
||||
type: 'json',
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
const path = require('path');
|
||||
const I18n = require('i18n-2');
|
||||
|
||||
module.exports = {
|
||||
sync: true,
|
||||
|
||||
@@ -14,9 +17,21 @@ module.exports = {
|
||||
},
|
||||
|
||||
fn(inputs) {
|
||||
const i18n = _.cloneDeep(sails.hooks.i18n);
|
||||
const i18n = new I18n({
|
||||
locales: sails.config.i18n.locales,
|
||||
defaultLocale: sails.config.i18n.defaultLocale,
|
||||
directory: path.join(sails.config.appPath, sails.config.i18n.localesDirectory),
|
||||
extension: '.json',
|
||||
devMode: false,
|
||||
});
|
||||
|
||||
i18n.setLocale(inputs.language || sails.config.i18n.defaultLocale);
|
||||
|
||||
return i18n.__.bind(i18n); // eslint-disable-line no-underscore-dangle
|
||||
/* eslint-disable no-underscore-dangle */
|
||||
const translator = i18n.__.bind(i18n);
|
||||
translator.n = i18n.__n.bind(i18n);
|
||||
/* eslint-enable no-underscore-dangle */
|
||||
|
||||
return translator;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -31,6 +31,14 @@ const makeWhereQueryBuilder = (Model) => (criteria) => {
|
||||
return ['id = $1', [criteria]];
|
||||
};
|
||||
|
||||
const makeRowToModelTransformer = (Model) => {
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const transformations = _.invert(Model._transformer._transformations);
|
||||
|
||||
return (row) => _.mapKeys(row, (_, key) => transformations[key]);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
makeWhereQueryBuilder,
|
||||
makeRowToModelTransformer,
|
||||
};
|
||||
|
||||
@@ -172,16 +172,7 @@ const delete_ = (criteria) =>
|
||||
query += `END END, updated_at = $${queryValues.length} WHERE id IN (${inValues.join(', ')}) AND references_total IS NOT NULL RETURNING *`;
|
||||
|
||||
const queryResult = await sails.sendNativeQuery(query, queryValues).usingConnection(db);
|
||||
|
||||
uploadedFiles = queryResult.rows.map((row) => ({
|
||||
id: row.id,
|
||||
type: row.type,
|
||||
mimeType: row.mime_type,
|
||||
size: row.size,
|
||||
referencesTotal: row.references_total,
|
||||
createdAt: row.created_at,
|
||||
updatedAt: row.updated_at,
|
||||
}));
|
||||
uploadedFiles = queryResult.rows.map((row) => UploadedFile.qm.transformRowToModel(row));
|
||||
}
|
||||
|
||||
return { attachments, uploadedFiles };
|
||||
@@ -200,17 +191,7 @@ const deleteOne = (criteria) =>
|
||||
)
|
||||
.usingConnection(db);
|
||||
|
||||
const [row] = queryResult.rows;
|
||||
|
||||
uploadedFile = {
|
||||
id: row.id,
|
||||
type: row.type,
|
||||
mimeType: row.mime_type,
|
||||
size: row.size,
|
||||
referencesTotal: row.references_total,
|
||||
createdAt: row.created_at,
|
||||
updatedAt: row.updated_at,
|
||||
};
|
||||
uploadedFile = UploadedFile.qm.transformRowToModel(queryResult.rows[0]);
|
||||
}
|
||||
|
||||
return { attachment, uploadedFile };
|
||||
|
||||
@@ -105,16 +105,7 @@ const delete_ = (criteria) =>
|
||||
query += `END END, updated_at = $${queryValues.length} WHERE id IN (${inValues.join(', ')}) AND references_total IS NOT NULL RETURNING *`;
|
||||
|
||||
const queryResult = await sails.sendNativeQuery(query, queryValues).usingConnection(db);
|
||||
|
||||
uploadedFiles = queryResult.rows.map((row) => ({
|
||||
id: row.id,
|
||||
type: row.type,
|
||||
mimeType: row.mime_type,
|
||||
size: row.size,
|
||||
referencesTotal: row.references_total,
|
||||
createdAt: row.created_at,
|
||||
updatedAt: row.updated_at,
|
||||
}));
|
||||
uploadedFiles = queryResult.rows.map((row) => UploadedFile.qm.transformRowToModel(row));
|
||||
}
|
||||
|
||||
return { backgroundImages, uploadedFiles };
|
||||
@@ -131,17 +122,7 @@ const deleteOne = (criteria) =>
|
||||
)
|
||||
.usingConnection(db);
|
||||
|
||||
const [row] = queryResult.rows;
|
||||
|
||||
uploadedFile = {
|
||||
id: row.id,
|
||||
type: row.type,
|
||||
mimeType: row.mime_type,
|
||||
size: row.size,
|
||||
referencesTotal: row.references_total,
|
||||
createdAt: row.created_at,
|
||||
updatedAt: row.updated_at,
|
||||
};
|
||||
const uploadedFile = UploadedFile.qm.transformRowToModel(queryResult.rows[0]);
|
||||
|
||||
return { backgroundImage, uploadedFile };
|
||||
});
|
||||
|
||||
@@ -4,112 +4,17 @@
|
||||
*/
|
||||
|
||||
const buildSearchParts = require('../../../../utils/build-query-parts');
|
||||
const { makeRowToModelTransformer } = require('../helpers');
|
||||
|
||||
const LIMIT = 50;
|
||||
|
||||
const transformRowToModel = makeRowToModelTransformer(Card);
|
||||
|
||||
const defaultFind = (criteria, { sort = 'id', limit } = {}) =>
|
||||
Card.find(criteria).sort(sort).limit(limit);
|
||||
|
||||
/* Query methods */
|
||||
|
||||
const getIdsByEndlessListId = async (
|
||||
listId,
|
||||
{ before, search, filterUserIds, filterLabelIds } = {},
|
||||
) => {
|
||||
if (filterUserIds && filterUserIds.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (filterLabelIds && filterLabelIds.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const queryValues = [];
|
||||
let query = 'SELECT DISTINCT card.id FROM card';
|
||||
|
||||
if (filterUserIds) {
|
||||
query += ' LEFT JOIN card_membership ON card.id = card_membership.card_id';
|
||||
query += ' LEFT JOIN task_list ON card.id = task_list.card_id';
|
||||
query += ' LEFT JOIN task ON task_list.id = task.task_list_id';
|
||||
}
|
||||
|
||||
if (filterLabelIds) {
|
||||
query += ' LEFT JOIN card_label ON card.id = card_label.card_id';
|
||||
}
|
||||
|
||||
queryValues.push(listId);
|
||||
query += ` WHERE card.list_id = $${queryValues.length}`;
|
||||
|
||||
if (before) {
|
||||
queryValues.push(before.listChangedAt);
|
||||
query += ` AND (card.list_changed_at < $${queryValues.length} OR (card.list_changed_at = $${queryValues.length}`;
|
||||
|
||||
queryValues.push(before.id);
|
||||
query += ` AND card.id < $${queryValues.length}))`;
|
||||
}
|
||||
|
||||
if (search) {
|
||||
if (search.startsWith('/')) {
|
||||
queryValues.push(search.substring(1));
|
||||
query += ` AND (card.name ~* $${queryValues.length} OR card.description ~* $${queryValues.length})`;
|
||||
} else {
|
||||
const searchParts = buildSearchParts(search);
|
||||
|
||||
if (searchParts.length > 0) {
|
||||
let ilikeValues = searchParts.map((searchPart) => {
|
||||
queryValues.push(searchPart);
|
||||
return `'%' || $${queryValues.length} || '%'`;
|
||||
});
|
||||
|
||||
query += ` AND ((card.name ILIKE ALL(ARRAY[${ilikeValues.join(', ')}]))`;
|
||||
|
||||
ilikeValues = searchParts.map((searchPart) => {
|
||||
queryValues.push(searchPart);
|
||||
return `'%' || $${queryValues.length} || '%'`;
|
||||
});
|
||||
|
||||
query += ` OR (card.description ILIKE ALL(ARRAY[${ilikeValues.join(', ')}])))`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (filterUserIds) {
|
||||
const inValues = filterUserIds.map((filterUserId) => {
|
||||
queryValues.push(filterUserId);
|
||||
return `$${queryValues.length}`;
|
||||
});
|
||||
|
||||
query += ` AND (card_membership.user_id IN (${inValues.join(', ')}) OR task.assignee_user_id IN (${inValues.join(', ')}))`;
|
||||
}
|
||||
|
||||
if (filterLabelIds) {
|
||||
const inValues = filterLabelIds.map((filterLabelId) => {
|
||||
queryValues.push(filterLabelId);
|
||||
return `$${queryValues.length}`;
|
||||
});
|
||||
|
||||
query += ` AND card_label.label_id IN (${inValues.join(', ')})`;
|
||||
}
|
||||
|
||||
query += ` LIMIT ${LIMIT}`;
|
||||
|
||||
let queryResult;
|
||||
try {
|
||||
queryResult = await sails.sendNativeQuery(query, queryValues);
|
||||
} catch (error) {
|
||||
if (
|
||||
error.code === 'E_QUERY_FAILED' &&
|
||||
error.message.includes('Query failed: invalid regular expression')
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
return sails.helpers.utils.mapRecords(queryResult.rows);
|
||||
};
|
||||
|
||||
const createOne = (values) => Card.create({ ...values }).fetch();
|
||||
|
||||
const getByIds = (ids) => defaultFind(ids);
|
||||
@@ -133,45 +38,121 @@ const getByListId = async (listId, { exceptIdOrIds, sort = ['position', 'id'] }
|
||||
return defaultFind(criteria, { sort });
|
||||
};
|
||||
|
||||
const getByEndlessListId = async (listId, { before, search, filterUserIds, filterLabelIds }) => {
|
||||
const criteria = {};
|
||||
|
||||
const options = {
|
||||
sort: ['listChangedAt DESC', 'id DESC'],
|
||||
};
|
||||
|
||||
if (search || filterUserIds || filterLabelIds) {
|
||||
criteria.id = await getIdsByEndlessListId(listId, {
|
||||
before,
|
||||
search,
|
||||
filterUserIds,
|
||||
filterLabelIds,
|
||||
});
|
||||
} else {
|
||||
criteria.and = [{ listId }];
|
||||
|
||||
if (before) {
|
||||
criteria.and.push({
|
||||
or: [
|
||||
{
|
||||
listChangedAt: {
|
||||
'<': before.listChangedAt,
|
||||
},
|
||||
},
|
||||
{
|
||||
listChangedAt: before.listChangedAt,
|
||||
id: {
|
||||
'<': before.id,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
const getByEndlessListId = async (listId, { before, search, userIds, labelIds }) => {
|
||||
if (search || userIds || labelIds) {
|
||||
if (userIds && userIds.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
options.limit = LIMIT;
|
||||
if (labelIds && labelIds.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const queryValues = [];
|
||||
let query = 'SELECT DISTINCT card.* FROM card';
|
||||
|
||||
if (userIds) {
|
||||
query += ' LEFT JOIN card_membership ON card.id = card_membership.card_id';
|
||||
query += ' LEFT JOIN task_list ON card.id = task_list.card_id';
|
||||
query += ' LEFT JOIN task ON task_list.id = task.task_list_id';
|
||||
}
|
||||
|
||||
if (labelIds) {
|
||||
query += ' LEFT JOIN card_label ON card.id = card_label.card_id';
|
||||
}
|
||||
|
||||
queryValues.push(listId);
|
||||
query += ` WHERE card.list_id = $${queryValues.length}`;
|
||||
|
||||
if (before) {
|
||||
queryValues.push(before.listChangedAt);
|
||||
query += ` AND (card.list_changed_at < $${queryValues.length} OR (card.list_changed_at = $${queryValues.length}`;
|
||||
|
||||
queryValues.push(before.id);
|
||||
query += ` AND card.id < $${queryValues.length}))`;
|
||||
}
|
||||
|
||||
if (search) {
|
||||
if (search.startsWith('/')) {
|
||||
queryValues.push(search.substring(1));
|
||||
query += ` AND (card.name ~* $${queryValues.length} OR card.description ~* $${queryValues.length})`;
|
||||
} else {
|
||||
const searchParts = buildSearchParts(search);
|
||||
|
||||
if (searchParts.length > 0) {
|
||||
const ilikeValues = searchParts.map((searchPart) => {
|
||||
queryValues.push(searchPart);
|
||||
return `'%' || $${queryValues.length} || '%'`;
|
||||
});
|
||||
|
||||
query += ` AND ((card.name ILIKE ALL(ARRAY[${ilikeValues.join(', ')}])) OR (card.description ILIKE ALL(ARRAY[${ilikeValues.join(', ')}])))`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (userIds) {
|
||||
const inValues = userIds.map((userId) => {
|
||||
queryValues.push(userId);
|
||||
return `$${queryValues.length}`;
|
||||
});
|
||||
|
||||
query += ` AND (card_membership.user_id IN (${inValues.join(', ')}) OR task.assignee_user_id IN (${inValues.join(', ')}))`;
|
||||
}
|
||||
|
||||
if (labelIds) {
|
||||
const inValues = labelIds.map((labelId) => {
|
||||
queryValues.push(labelId);
|
||||
return `$${queryValues.length}`;
|
||||
});
|
||||
|
||||
query += ` AND card_label.label_id IN (${inValues.join(', ')})`;
|
||||
}
|
||||
|
||||
query += ` LIMIT ${LIMIT}`;
|
||||
|
||||
let queryResult;
|
||||
try {
|
||||
queryResult = await sails.sendNativeQuery(query, queryValues);
|
||||
} catch (error) {
|
||||
if (
|
||||
error.code === 'E_QUERY_FAILED' &&
|
||||
error.message.includes('Query failed: invalid regular expression')
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
return queryResult.rows.map((row) => transformRowToModel(row));
|
||||
}
|
||||
|
||||
return defaultFind(criteria, options);
|
||||
const criteria = {
|
||||
and: [{ listId }],
|
||||
};
|
||||
|
||||
if (before) {
|
||||
criteria.and.push({
|
||||
or: [
|
||||
{
|
||||
listChangedAt: {
|
||||
'<': before.listChangedAt,
|
||||
},
|
||||
},
|
||||
{
|
||||
listChangedAt: before.listChangedAt,
|
||||
id: {
|
||||
'<': before.id,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
return defaultFind(criteria, {
|
||||
sort: ['listChangedAt DESC', 'id DESC'],
|
||||
limit: LIMIT,
|
||||
});
|
||||
};
|
||||
|
||||
const getByListIds = async (listIds, { sort = ['position', 'id'] } = {}) =>
|
||||
@@ -252,8 +233,6 @@ const delete_ = (criteria) => Card.destroy(criteria).fetch();
|
||||
const deleteOne = (criteria) => Card.destroyOne(criteria);
|
||||
|
||||
module.exports = {
|
||||
getIdsByEndlessListId,
|
||||
|
||||
createOne,
|
||||
getByIds,
|
||||
getByBoardId,
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
const { makeRowToModelTransformer } = require('../helpers');
|
||||
|
||||
const transformRowToModel = makeRowToModelTransformer(CustomFieldValue);
|
||||
|
||||
const defaultFind = (criteria, { customFieldGroupIdOrIds }) => {
|
||||
if (customFieldGroupIdOrIds) {
|
||||
criteria.customFieldGroupId = customFieldGroupIdOrIds; // eslint-disable-line no-param-reassign
|
||||
@@ -32,17 +36,7 @@ const createOrUpdateOne = async (values) => {
|
||||
new Date().toISOString(),
|
||||
]);
|
||||
|
||||
const [row] = queryResult.rows;
|
||||
|
||||
return {
|
||||
id: row.id,
|
||||
cardId: row.card_id,
|
||||
customFieldGroupId: row.custom_field_group_id,
|
||||
customFieldId: row.custom_field_id,
|
||||
content: row.content,
|
||||
createdAt: row.created_at,
|
||||
updatedAt: row.updated_at,
|
||||
};
|
||||
return transformRowToModel(queryResult.rows[0]);
|
||||
};
|
||||
|
||||
const getByIds = (ids) => defaultFind(ids);
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
const { makeWhereQueryBuilder } = require('../helpers');
|
||||
const { makeRowToModelTransformer, makeWhereQueryBuilder } = require('../helpers');
|
||||
|
||||
const buildWhereQuery = makeWhereQueryBuilder(List);
|
||||
const transformRowToModel = makeRowToModelTransformer(List);
|
||||
|
||||
const defaultFind = (criteria, { sort = 'id' } = {}) => List.find(criteria).sort(sort);
|
||||
|
||||
@@ -67,10 +68,7 @@ const updateOne = async (criteria, values) => {
|
||||
return { list: null };
|
||||
}
|
||||
|
||||
const prev = {
|
||||
boardId: queryResult.rows[0].board_id,
|
||||
type: queryResult.rows[0].type,
|
||||
};
|
||||
const prev = transformRowToModel(queryResult.rows[0]);
|
||||
|
||||
const list = await List.updateOne(criteria)
|
||||
.set({ ...values })
|
||||
|
||||
@@ -3,12 +3,16 @@
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
const { makeRowToModelTransformer } = require('../helpers');
|
||||
|
||||
const COLUMN_NAME_BY_TYPE = {
|
||||
[UploadedFile.Types.USER_AVATAR]: 'user_avatars',
|
||||
[UploadedFile.Types.BACKGROUND_IMAGE]: 'background_images',
|
||||
[UploadedFile.Types.ATTACHMENT]: 'attachments',
|
||||
};
|
||||
|
||||
const transformRowToModel = makeRowToModelTransformer(UploadedFile);
|
||||
|
||||
/* Query methods */
|
||||
|
||||
const createOne = (values) =>
|
||||
@@ -47,4 +51,6 @@ const deleteOne = (criteria) =>
|
||||
module.exports = {
|
||||
createOne,
|
||||
deleteOne,
|
||||
|
||||
transformRowToModel,
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
const { makeWhereQueryBuilder } = require('../helpers');
|
||||
const { makeRowToModelTransformer, makeWhereQueryBuilder } = require('../helpers');
|
||||
|
||||
const hasAvatarChanged = (avatar, prevAvatar) => {
|
||||
if (!avatar && !prevAvatar) {
|
||||
@@ -18,6 +18,7 @@ const hasAvatarChanged = (avatar, prevAvatar) => {
|
||||
};
|
||||
|
||||
const buildWhereQuery = makeWhereQueryBuilder(User);
|
||||
const transformRowToModel = makeRowToModelTransformer(User);
|
||||
|
||||
const defaultFind = (criteria) => User.find(criteria).sort('id');
|
||||
|
||||
@@ -117,9 +118,7 @@ const updateOne = async (criteria, values) => {
|
||||
return { user: null };
|
||||
}
|
||||
|
||||
prev = {
|
||||
avatar: queryResult.rows[0].avatar,
|
||||
};
|
||||
prev = transformRowToModel(queryResult.rows[0]);
|
||||
}
|
||||
|
||||
const user = await User.updateOne(criteria)
|
||||
@@ -136,17 +135,7 @@ const updateOne = async (criteria, values) => {
|
||||
)
|
||||
.usingConnection(db);
|
||||
|
||||
const [row] = queryResult.rows;
|
||||
|
||||
uploadedFile = {
|
||||
id: row.id,
|
||||
type: row.type,
|
||||
mimeType: row.mime_type,
|
||||
size: row.size,
|
||||
referencesTotal: row.references_total,
|
||||
createdAt: row.created_at,
|
||||
updatedAt: row.updated_at,
|
||||
};
|
||||
uploadedFile = UploadedFile.qm.transformRowToModel(queryResult.rows[0]);
|
||||
}
|
||||
|
||||
if (user.avatar) {
|
||||
@@ -184,17 +173,7 @@ const deleteOne = (criteria) =>
|
||||
)
|
||||
.usingConnection(db);
|
||||
|
||||
const [row] = queryResult.rows;
|
||||
|
||||
uploadedFile = {
|
||||
id: row.id,
|
||||
type: row.type,
|
||||
mimeType: row.mime_type,
|
||||
size: row.size,
|
||||
referencesTotal: row.references_total,
|
||||
createdAt: row.created_at,
|
||||
updatedAt: row.updated_at,
|
||||
};
|
||||
uploadedFile = UploadedFile.qm.transformRowToModel(queryResult.rows[0]);
|
||||
}
|
||||
|
||||
return { user, uploadedFile };
|
||||
|
||||
@@ -95,6 +95,8 @@ const SortOrders = {
|
||||
|
||||
const FINITE_TYPES = [Types.ACTIVE, Types.CLOSED];
|
||||
|
||||
const KANBAN_TYPES = [Types.ACTIVE, Types.CLOSED];
|
||||
|
||||
const TYPE_STATE_BY_TYPE = {
|
||||
[Types.ACTIVE]: TypeStates.OPENED,
|
||||
[Types.CLOSED]: Types.CLOSED,
|
||||
@@ -119,6 +121,7 @@ module.exports = {
|
||||
SortFieldNames,
|
||||
SortOrders,
|
||||
FINITE_TYPES,
|
||||
KANBAN_TYPES,
|
||||
TYPE_STATE_BY_TYPE,
|
||||
COLORS,
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"Archive": "أرشيف",
|
||||
"Card Created": "تم إنشاء البطاقة",
|
||||
"Card Moved": "تم نقل البطاقة",
|
||||
"New Comment": "تعليق جديد",
|
||||
@@ -6,6 +7,7 @@
|
||||
"This is a test text message!": "هذه رسالة نصية تجريبية!",
|
||||
"This is a *test* **markdown** `message`!": "هذه *رسالة* **markdown** `تجريبية`!",
|
||||
"This is a <i>test</i> <b>html</b> <code>message</code>!": "هذه <i>رسالة</i> <b>html</b> <code>تجريبية</code>!",
|
||||
"Trash": "سلة المهملات",
|
||||
"You Were Added to Card": "تمت إضافتك إلى البطاقة",
|
||||
"You Were Mentioned in Comment": "تم ذكرك في تعليق",
|
||||
"%s added you to %s on %s": "%s أضافك إلى %s في %s",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"Archive": "Архив",
|
||||
"Card Created": "Картата е създадена",
|
||||
"Card Moved": "Картата е преместена",
|
||||
"New Comment": "Нов коментар",
|
||||
@@ -6,6 +7,7 @@
|
||||
"This is a test text message!": "Това е тестово текстово съобщение!",
|
||||
"This is a *test* **markdown** `message`!": "Това е *тестово* **markdown** `съобщение`!",
|
||||
"This is a <i>test</i> <b>html</b> <code>message</code>!": "Това е <i>тестово</i> <b>html</b> <code>съобщение</code>!",
|
||||
"Trash": "Кошче",
|
||||
"You Were Added to Card": "Бяхте добавени към картата",
|
||||
"You Were Mentioned in Comment": "Бяхте споменати в коментар",
|
||||
"%s added you to %s on %s": "%s ви добави към %s на %s",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"Archive": "Arxivar",
|
||||
"Card Created": "Targeta creada",
|
||||
"Card Moved": "Targeta moguda",
|
||||
"New Comment": "Comentari nou",
|
||||
@@ -6,6 +7,7 @@
|
||||
"This is a test text message!": "Aquest és un missatge de text de prova!",
|
||||
"This is a *test* **markdown** `message`!": "Aquest és un *missatge* **markdown** `de prova`!",
|
||||
"This is a <i>test</i> <b>html</b> <code>message</code>!": "Aquest és un <i>missatge</i> <b>html</b> <code>de prova</code>!",
|
||||
"Trash": "Paperera",
|
||||
"You Were Added to Card": "Has estat afegit a la targeta",
|
||||
"You Were Mentioned in Comment": "Has estat mencionat en un comentari",
|
||||
"%s added you to %s on %s": "%s t'ha afegit a %s el %s",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"Archive": "Archiv",
|
||||
"Card Created": "Karta vytvořena",
|
||||
"Card Moved": "Karta přesunuta",
|
||||
"New Comment": "Nový komentář",
|
||||
@@ -6,6 +7,7 @@
|
||||
"This is a test text message!": "Toto je testovací textová zpráva!",
|
||||
"This is a *test* **markdown** `message`!": "Toto je *testovací* **markdown** `zpráva`!",
|
||||
"This is a <i>test</i> <b>html</b> <code>message</code>!": "Toto je <i>testovací</i> <b>html</b> <code>zpráva</code>!",
|
||||
"Trash": "Koš",
|
||||
"You Were Added to Card": "Byli jste přidáni ke kartě",
|
||||
"You Were Mentioned in Comment": "Byli jste zmíněni v komentáři",
|
||||
"%s added you to %s on %s": "%s vás přidal k %s dne %s",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"Archive": "Arkiv",
|
||||
"Card Created": "Kort oprettet",
|
||||
"Card Moved": "Kort flyttet",
|
||||
"New Comment": "Ny kommentar",
|
||||
@@ -6,6 +7,7 @@
|
||||
"This is a test text message!": "Dette er en test tekstbesked!",
|
||||
"This is a *test* **markdown** `message`!": "Dette er en *test* **markdown** `besked`!",
|
||||
"This is a <i>test</i> <b>html</b> <code>message</code>!": "Dette er en <i>test</i> <b>html</b> <code>besked</code>!",
|
||||
"Trash": "Papirkurv",
|
||||
"You Were Added to Card": "Du blev tilføjet til kortet",
|
||||
"You Were Mentioned in Comment": "Du blev nævnt i en kommentar",
|
||||
"%s added you to %s on %s": "%s tilføjede dig til %s den %s",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"Archive": "Archiv",
|
||||
"Card Created": "Karte erstellt",
|
||||
"Card Moved": "Karte verschoben",
|
||||
"New Comment": "Neuer Kommentar",
|
||||
@@ -6,6 +7,7 @@
|
||||
"This is a test text message!": "Dies ist eine Test-Textnachricht!",
|
||||
"This is a *test* **markdown** `message`!": "Dies ist eine *Test*-**Markdown**-`Nachricht`!",
|
||||
"This is a <i>test</i> <b>html</b> <code>message</code>!": "Dies ist eine <i>Test</i>-<b>HTML</b>-<code>Nachricht</code>!",
|
||||
"Trash": "Papierkorb",
|
||||
"You Were Added to Card": "Sie wurden zur Karte hinzugefügt",
|
||||
"You Were Mentioned in Comment": "Sie wurden in einem Kommentar erwähnt",
|
||||
"%s added you to %s on %s": "%s hat Sie zu %s am %s hinzugefügt",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"Archive": "Αρχείο",
|
||||
"Card Created": "Η κάρτα δημιουργήθηκε",
|
||||
"Card Moved": "Η κάρτα μετακινήθηκε",
|
||||
"New Comment": "Νέο σχόλιο",
|
||||
@@ -6,6 +7,7 @@
|
||||
"This is a test text message!": "Αυτό είναι ένα δοκιμαστικό μήνυμα!",
|
||||
"This is a *test* **markdown** `message`!": "Αυτό είναι ένα *δοκιμαστικό* **markdown** `μήνυμα`!",
|
||||
"This is a <i>test</i> <b>html</b> <code>message</code>!": "Αυτό είναι ένα <i>δοκιμαστικό</i> <b>html</b> <code>μήνυμα</code>!",
|
||||
"Trash": "Κάδος απορριμμάτων",
|
||||
"You Were Added to Card": "Προστέθηκες στην κάρτα",
|
||||
"You Were Mentioned in Comment": "Αναφέρθηκες σε σχόλιο",
|
||||
"%s added you to %s on %s": "%s σε πρόσθεσε στο %s στο %s",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"Archive": "Archive",
|
||||
"Card Created": "Card Created",
|
||||
"Card Moved": "Card Moved",
|
||||
"New Comment": "New Comment",
|
||||
@@ -6,6 +7,7 @@
|
||||
"This is a test text message!": "This is a test text message!",
|
||||
"This is a *test* **markdown** `message`!": "This is a *test* **markdown** `message`!",
|
||||
"This is a <i>test</i> <b>html</b> <code>message</code>!": "This is a <i>test</i> <b>html</b> <code>message</code>!",
|
||||
"Trash": "Trash",
|
||||
"You Were Added to Card": "You Were Added to Card",
|
||||
"You Were Mentioned in Comment": "You Were Mentioned in Comment",
|
||||
"%s added you to %s on %s": "%s added you to %s on %s",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"Archive": "Archive",
|
||||
"Card Created": "Card Created",
|
||||
"Card Moved": "Card Moved",
|
||||
"New Comment": "New Comment",
|
||||
@@ -6,6 +7,7 @@
|
||||
"This is a test text message!": "This is a test text message!",
|
||||
"This is a *test* **markdown** `message`!": "This is a *test* **markdown** `message`!",
|
||||
"This is a <i>test</i> <b>html</b> <code>message</code>!": "This is a <i>test</i> <b>html</b> <code>message</code>!",
|
||||
"Trash": "Trash",
|
||||
"You Were Added to Card": "You Were Added to Card",
|
||||
"You Were Mentioned in Comment": "You Were Mentioned in Comment",
|
||||
"%s added you to %s on %s": "%s added you to %s on %s",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"Archive": "Archivo",
|
||||
"Card Created": "Tarjeta creada",
|
||||
"Card Moved": "Tarjeta movida",
|
||||
"New Comment": "Nuevo comentario",
|
||||
@@ -6,6 +7,7 @@
|
||||
"This is a test text message!": "¡Este es un mensaje de texto de prueba!",
|
||||
"This is a *test* **markdown** `message`!": "¡Este es un *mensaje* **markdown** `de prueba`!",
|
||||
"This is a <i>test</i> <b>html</b> <code>message</code>!": "¡Este es un <i>mensaje</i> <b>html</b> <code>de prueba</code>!",
|
||||
"Trash": "Papelera",
|
||||
"You Were Added to Card": "Fuiste añadido a la tarjeta",
|
||||
"You Were Mentioned in Comment": "Fuiste mencionado en un comentario",
|
||||
"%s added you to %s on %s": "%s te añadió a %s en %s",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"Archive": "Arhiiv",
|
||||
"Card Created": "Kaart loodud",
|
||||
"Card Moved": "Kaart liigutatud",
|
||||
"New Comment": "Uus kommentaar",
|
||||
@@ -6,6 +7,7 @@
|
||||
"This is a test text message!": "See on testi tekstisõnum!",
|
||||
"This is a *test* **markdown** `message`!": "See on *testi* **markdown** `sõnum`!",
|
||||
"This is a <i>test</i> <b>html</b> <code>message</code>!": "See on <i>testi</i> <b>html</b> <code>sõnum</code>!",
|
||||
"Trash": "Prügikast",
|
||||
"You Were Added to Card": "Teid lisati kaardile",
|
||||
"You Were Mentioned in Comment": "Teid mainiti kommentaaris",
|
||||
"%s added you to %s on %s": "%s lisas teid %s-le %s",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"Archive": "بایگانی",
|
||||
"Card Created": "کارت ایجاد شد",
|
||||
"Card Moved": "کارت منتقل شد",
|
||||
"New Comment": "نظر جدید",
|
||||
@@ -6,6 +7,7 @@
|
||||
"This is a test text message!": "این یک پیام متنی آزمایشی است!",
|
||||
"This is a *test* **markdown** `message`!": "این یک *پیام* **markdown** `آزمایشی` است!",
|
||||
"This is a <i>test</i> <b>html</b> <code>message</code>!": "این یک <i>پیام</i> <b>html</b> <code>آزمایشی</code> است!",
|
||||
"Trash": "سطل زباله",
|
||||
"You Were Added to Card": "شما به کارت اضافه شدید",
|
||||
"You Were Mentioned in Comment": "در نظری از شما نام برده شد",
|
||||
"%s added you to %s on %s": "%s شما را به %s در %s اضافه کرد",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"Archive": "Arkisto",
|
||||
"Card Created": "Kortti luotu",
|
||||
"Card Moved": "Kortti siirretty",
|
||||
"New Comment": "Uusi kommentti",
|
||||
@@ -6,6 +7,7 @@
|
||||
"This is a test text message!": "Tämä on testiviesti!",
|
||||
"This is a *test* **markdown** `message`!": "Tämä on *testi* **markdown** `viesti`!",
|
||||
"This is a <i>test</i> <b>html</b> <code>message</code>!": "Tämä on <i>testi</i> <b>html</b> <code>viesti</code>!",
|
||||
"Trash": "Roskakori",
|
||||
"You Were Added to Card": "Sinut lisättiin korttiin",
|
||||
"You Were Mentioned in Comment": "Sinut mainittiin kommentissa",
|
||||
"%s added you to %s on %s": "%s lisäsi sinut kohteeseen %s kohteessa %s",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"Archive": "Archive",
|
||||
"Card Created": "Carte créée",
|
||||
"Card Moved": "Carte déplacée",
|
||||
"New Comment": "Nouveau commentaire",
|
||||
@@ -6,6 +7,7 @@
|
||||
"This is a test text message!": "Ceci est un message texte de test !",
|
||||
"This is a *test* **markdown** `message`!": "Ceci est un *message* **markdown** `de test` !",
|
||||
"This is a <i>test</i> <b>html</b> <code>message</code>!": "Ceci est un <i>test</i> <b>html</b> <code>message</code>!",
|
||||
"Trash": "Corbeille",
|
||||
"You Were Added to Card": "Vous avez été ajouté à la carte",
|
||||
"You Were Mentioned in Comment": "Vous avez été mentionné dans un commentaire",
|
||||
"%s added you to %s on %s": "%s vous a ajouté à %s le %s",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"Archive": "Archívum",
|
||||
"Card Created": "Kártya létrehozva",
|
||||
"Card Moved": "Kártya áthelyezve",
|
||||
"New Comment": "Új hozzászólás",
|
||||
@@ -6,6 +7,7 @@
|
||||
"This is a test text message!": "Ez itt egy szöveges teszt üzenet!",
|
||||
"This is a *test* **markdown** `message`!": "Ez itt egy **markdown** formátumú *teszt* `üzenet`!",
|
||||
"This is a <i>test</i> <b>html</b> <code>message</code>!": "Ez egy <b>html</b> formátumú <i>teszt</i> <code>üzenet</code>!",
|
||||
"Trash": "Kuka",
|
||||
"You Were Added to Card": "Hozzádrendeltek egy kártyát",
|
||||
"You Were Mentioned in Comment": "Megemlítettek egy kártyán",
|
||||
"%s added you to %s on %s": "%s hozzárendelt téged a(z) %s kártyához a(z) %s projektben",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"Archive": "Arsip",
|
||||
"Card Created": "Kartu dibuat",
|
||||
"Card Moved": "Kartu dipindahkan",
|
||||
"New Comment": "Komentar baru",
|
||||
@@ -6,6 +7,7 @@
|
||||
"This is a test text message!": "Ini adalah pesan teks tes!",
|
||||
"This is a *test* **markdown** `message`!": "Ini adalah *pesan* **markdown** `tes`!",
|
||||
"This is a <i>test</i> <b>html</b> <code>message</code>!": "Ini adalah <i>pesan</i> <b>html</b> <code>tes</code>!",
|
||||
"Trash": "Sampah",
|
||||
"You Were Added to Card": "Anda ditambahkan ke kartu",
|
||||
"You Were Mentioned in Comment": "Anda disebutkan dalam komentar",
|
||||
"%s added you to %s on %s": "%s menambahkan Anda ke %s pada %s",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"Archive": "Archivio",
|
||||
"Card Created": "Nuova task creata",
|
||||
"Card Moved": "Task spostata",
|
||||
"New Comment": "Nuovo commento",
|
||||
@@ -6,6 +7,7 @@
|
||||
"This is a test text message!": "Questo è un messaggio di testo di test!",
|
||||
"This is a *test* **markdown** `message`!": "Questo è un *test* **markdown** `messaggio`!",
|
||||
"This is a <i>test</i> <b>html</b> <code>message</code>!": "Questo è un <i>test</i> <b>html</b> <code>messaggio</code>!",
|
||||
"Trash": "Cestino",
|
||||
"You Were Added to Card": "Sei stato aggiunto alla task",
|
||||
"You Were Mentioned in Comment": "Sei stato menzionato nel commento",
|
||||
"%s created %s in %s on %s": "%s ha creato %s in %s in %s",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"Archive": "アーカイブ",
|
||||
"Card Created": "カードが作成されました",
|
||||
"Card Moved": "カードが移動されました",
|
||||
"New Comment": "新しいコメント",
|
||||
@@ -6,6 +7,7 @@
|
||||
"This is a test text message!": "これはテストテキストメッセージです!",
|
||||
"This is a *test* **markdown** `message`!": "これは*テスト***markdown**`メッセージ`です!",
|
||||
"This is a <i>test</i> <b>html</b> <code>message</code>!": "これは<i>テスト</i><b>html</b><code>メッセージ</code>です!",
|
||||
"Trash": "ゴミ箱",
|
||||
"You Were Added to Card": "カードに追加されました",
|
||||
"You Were Mentioned in Comment": "コメントでメンションされました",
|
||||
"%s added you to %s on %s": "%sが%sの%sにあなたを追加しました",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"Archive": "보관함",
|
||||
"Card Created": "카드가 생성됨",
|
||||
"Card Moved": "카드가 이동됨",
|
||||
"New Comment": "새 댓글",
|
||||
@@ -6,6 +7,7 @@
|
||||
"This is a test text message!": "이것은 테스트 텍스트 메시지입니다!",
|
||||
"This is a *test* **markdown** `message`!": "이것은 *테스트* **markdown** `메시지`입니다!",
|
||||
"This is a <i>test</i> <b>html</b> <code>message</code>!": "이것은 <i>테스트</i> <b>html</b> <code>메시지</code>입니다!",
|
||||
"Trash": "휴지통",
|
||||
"You Were Added to Card": "카드에 추가되었습니다",
|
||||
"You Were Mentioned in Comment": "댓글에서 언급되었습니다",
|
||||
"%s added you to %s on %s": "%s님이 %s의 %s에 당신을 추가했습니다",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"Archive": "Archief",
|
||||
"Card Created": "Kaart aangemaakt",
|
||||
"Card Moved": "Kaart verplaatst",
|
||||
"New Comment": "Nieuwe reactie",
|
||||
@@ -6,6 +7,7 @@
|
||||
"This is a test text message!": "Dit is een test tekstbericht!",
|
||||
"This is a *test* **markdown** `message`!": "Dit is een *test* **markdown** `bericht`!",
|
||||
"This is a <i>test</i> <b>html</b> <code>message</code>!": "Dit is een <i>test</i> <b>html</b> <code>bericht</code>!",
|
||||
"Trash": "Prullenbak",
|
||||
"You Were Added to Card": "Je bent toegevoegd aan kaart",
|
||||
"You Were Mentioned in Comment": "Je bent genoemd in reactie",
|
||||
"%s added you to %s on %s": "%s heeft je toegevoegd aan %s op %s",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"Archive": "Archiwum",
|
||||
"Card Created": "Karta utworzona",
|
||||
"Card Moved": "Karta przeniesiona",
|
||||
"New Comment": "Nowy komentarz",
|
||||
@@ -6,6 +7,7 @@
|
||||
"This is a test text message!": "To jest testowa wiadomość tekstowa!",
|
||||
"This is a *test* **markdown** `message`!": "To jest *testowa* **markdown** `wiadomość`!",
|
||||
"This is a <i>test</i> <b>html</b> <code>message</code>!": "To jest <i>testowa</i> <b>html</b> <code>wiadomość</code>!",
|
||||
"Trash": "Kosz",
|
||||
"You Were Added to Card": "Zostałeś dodany do karty",
|
||||
"You Were Mentioned in Comment": "Zostałeś wspomniany w komentarzu",
|
||||
"%s added you to %s on %s": "%s dodał cię do %s w dniu %s",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"Archive": "Arquivo",
|
||||
"Card Created": "Cartão criado",
|
||||
"Card Moved": "Cartão movido",
|
||||
"New Comment": "Novo comentário",
|
||||
@@ -6,6 +7,7 @@
|
||||
"This is a test text message!": "Esta é uma mensagem de texto de teste!",
|
||||
"This is a *test* **markdown** `message`!": "Esta é uma *mensagem* **markdown** `de teste`!",
|
||||
"This is a <i>test</i> <b>html</b> <code>message</code>!": "Esta é uma <i>mensagem</i> <b>html</b> <code>de teste</code>!",
|
||||
"Trash": "Lixeira",
|
||||
"You Were Added to Card": "Você foi adicionado ao cartão",
|
||||
"You Were Mentioned in Comment": "Você foi mencionado em comentário",
|
||||
"%s added you to %s on %s": "%s adicionou você a %s em %s",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"Archive": "Arquivo",
|
||||
"Card Created": "Cartão criado",
|
||||
"Card Moved": "Cartão movido",
|
||||
"New Comment": "Novo comentário",
|
||||
@@ -6,6 +7,7 @@
|
||||
"This is a test text message!": "Esta é uma mensagem de texto de teste!",
|
||||
"This is a *test* **markdown** `message`!": "Esta é uma *mensagem* **markdown** `de teste`!",
|
||||
"This is a <i>test</i> <b>html</b> <code>message</code>!": "Esta é uma <i>mensagem</i> <b>html</b> <code>de teste</code>!",
|
||||
"Trash": "Lixo",
|
||||
"You Were Added to Card": "Foi adicionado ao cartão",
|
||||
"You Were Mentioned in Comment": "Foi mencionado num comentário",
|
||||
"%s added you to %s on %s": "%s adicionou-o a %s em %s",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"Archive": "Arhivă",
|
||||
"Card Created": "Card creat",
|
||||
"Card Moved": "Card mutat",
|
||||
"New Comment": "Comentariu nou",
|
||||
@@ -6,6 +7,7 @@
|
||||
"This is a test text message!": "Acesta este un mesaj text de test!",
|
||||
"This is a *test* **markdown** `message`!": "Acesta este un *mesaj* **markdown** `de test`!",
|
||||
"This is a <i>test</i> <b>html</b> <code>message</code>!": "Acesta este un <i>mesaj</i> <b>html</b> <code>de test</code>!",
|
||||
"Trash": "Coș de gunoi",
|
||||
"You Were Added to Card": "Ați fost adăugat la card",
|
||||
"You Were Mentioned in Comment": "Ați fost menționat într-un comentariu",
|
||||
"%s added you to %s on %s": "%s v-a adăugat la %s pe %s",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"Archive": "Архив",
|
||||
"Card Created": "Карточка создана",
|
||||
"Card Moved": "Карточка перемещена",
|
||||
"New Comment": "Новый комментарий",
|
||||
@@ -6,6 +7,7 @@
|
||||
"This is a test text message!": "Это тестовое сообщение!",
|
||||
"This is a *test* **markdown** `message`!": "Это *тестовое* **markdown** `сообщение`!",
|
||||
"This is a <i>test</i> <b>html</b> <code>message</code>!": "Это <i>тестовое</i> <b>html</b> <code>сообщение</code>!",
|
||||
"Trash": "Корзина",
|
||||
"You Were Added to Card": "Вы были добавлены к карточке",
|
||||
"You Were Mentioned in Comment": "Вы были упомянуты в комментарии",
|
||||
"%s added you to %s on %s": "%s добавил(а) вас к %s на %s",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"Archive": "Archív",
|
||||
"Card Created": "Karta vytvorená",
|
||||
"Card Moved": "Karta presunutá",
|
||||
"New Comment": "Nový komentár",
|
||||
@@ -6,6 +7,7 @@
|
||||
"This is a test text message!": "Toto je testovacia textová správa!",
|
||||
"This is a *test* **markdown** `message`!": "Toto je *testovacia* **markdown** `správa`!",
|
||||
"This is a <i>test</i> <b>html</b> <code>message</code>!": "Toto je <i>testovacia</i> <b>html</b> <code>správa</code>!",
|
||||
"Trash": "Kôš",
|
||||
"You Were Added to Card": "Boli ste pridaní ku karte",
|
||||
"You Were Mentioned in Comment": "Boli ste spomenutí v komentári",
|
||||
"%s added you to %s on %s": "%s vás pridal k %s dňa %s",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"Archive": "Архива",
|
||||
"Card Created": "Картица креирана",
|
||||
"Card Moved": "Картица премештена",
|
||||
"New Comment": "Нови коментар",
|
||||
@@ -6,6 +7,7 @@
|
||||
"This is a test text message!": "Ово је тест текстуална порука!",
|
||||
"This is a *test* **markdown** `message`!": "Ово је *тест* **markdown** `порука`!",
|
||||
"This is a <i>test</i> <b>html</b> <code>message</code>!": "Ово је <i>тест</i> <b>html</b> <code>порука</code>!",
|
||||
"Trash": "Корпа за отпатке",
|
||||
"You Were Added to Card": "Додати сте на картицу",
|
||||
"You Were Mentioned in Comment": "Поменути сте у коментару",
|
||||
"%s added you to %s on %s": "%s вас је додао на %s дана %s",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"Archive": "Arhiva",
|
||||
"Card Created": "Kartica kreirana",
|
||||
"Card Moved": "Kartica premeštena",
|
||||
"New Comment": "Novi komentar",
|
||||
@@ -6,6 +7,7 @@
|
||||
"This is a test text message!": "Ovo je test tekstualna poruka!",
|
||||
"This is a *test* **markdown** `message`!": "Ovo je *test* **markdown** `poruka`!",
|
||||
"This is a <i>test</i> <b>html</b> <code>message</code>!": "Ovo je <i>test</i> <b>html</b> <code>poruka</code>!",
|
||||
"Trash": "Korpa za otpatke",
|
||||
"You Were Added to Card": "Dodati ste na karticu",
|
||||
"You Were Mentioned in Comment": "Pomenuti ste u komentaru",
|
||||
"%s added you to %s on %s": "%s vas je dodao na %s dana %s",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"Archive": "Arkiv",
|
||||
"Card Created": "Kort skapat",
|
||||
"Card Moved": "Kort flyttat",
|
||||
"New Comment": "Ny kommentar",
|
||||
@@ -6,6 +7,7 @@
|
||||
"This is a test text message!": "Detta är ett test textmeddelande!",
|
||||
"This is a *test* **markdown** `message`!": "Detta är ett *test* **markdown** `meddelande`!",
|
||||
"This is a <i>test</i> <b>html</b> <code>message</code>!": "Detta är ett <i>test</i> <b>html</b> <code>meddelande</code>!",
|
||||
"Trash": "Papperskorg",
|
||||
"You Were Added to Card": "Du lades till på kort",
|
||||
"You Were Mentioned in Comment": "Du nämndes i kommentar",
|
||||
"%s added you to %s on %s": "%s lade till dig på %s den %s",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"Archive": "Arşiv",
|
||||
"Card Created": "Kart oluşturuldu",
|
||||
"Card Moved": "Kart taşındı",
|
||||
"New Comment": "Yeni yorum",
|
||||
@@ -6,6 +7,7 @@
|
||||
"This is a test text message!": "Bu bir test metin mesajıdır!",
|
||||
"This is a *test* **markdown** `message`!": "Bu bir *test* **markdown** `mesajı`!",
|
||||
"This is a <i>test</i> <b>html</b> <code>message</code>!": "Bu bir <i>test</i> <b>html</b> <code>mesajı</code>!",
|
||||
"Trash": "Çöp kutusu",
|
||||
"You Were Added to Card": "Karta eklendiniz",
|
||||
"You Were Mentioned in Comment": "Bir yorumda bahsedildiniz",
|
||||
"%s added you to %s on %s": "%s sizi %s'ye %s tarihinde ekledi",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"Archive": "Архів",
|
||||
"Card Created": "Картку створено",
|
||||
"Card Moved": "Картку переміщено",
|
||||
"New Comment": "Новий коментар",
|
||||
@@ -6,6 +7,7 @@
|
||||
"This is a test text message!": "Це нове повідомлення!",
|
||||
"This is a *test* **markdown** `message`!": "Це *тестове* **markdown** `повідомлення`!",
|
||||
"This is a <i>test</i> <b>html</b> <code>message</code>!": "Це <i>тестове</i> <b>html</b> <code>повідомлення</code>!",
|
||||
"Trash": "Кошик",
|
||||
"You Were Added to Card": "Вас було додано до картки",
|
||||
"You Were Mentioned in Comment": "Вас було згадано у коментарі",
|
||||
"%s added you to %s on %s": "%s додав(ла) вас до %s на %s",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"Archive": "Arxiv",
|
||||
"Card Created": "Karta yaratildi",
|
||||
"Card Moved": "Karta ko'chirildi",
|
||||
"New Comment": "Yangi izoh",
|
||||
@@ -6,6 +7,7 @@
|
||||
"This is a test text message!": "Bu test matn xabari!",
|
||||
"This is a *test* **markdown** `message`!": "Bu *test* **markdown** `xabar`!",
|
||||
"This is a <i>test</i> <b>html</b> <code>message</code>!": "Bu <i>test</i> <b>html</b> <code>xabar</code>!",
|
||||
"Trash": "Chiqindi quti",
|
||||
"You Were Added to Card": "Siz kartaga qo'shildingiz",
|
||||
"You Were Mentioned in Comment": "Siz izohda eslatildingiz",
|
||||
"%s added you to %s on %s": "%s sizni %s ga %s da qo'shdi",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"Archive": "归档",
|
||||
"Card Created": "卡片已创建",
|
||||
"Card Moved": "卡片已移动",
|
||||
"New Comment": "新评论",
|
||||
@@ -6,6 +7,7 @@
|
||||
"This is a test text message!": "这是一条测试文本消息!",
|
||||
"This is a *test* **markdown** `message`!": "这是一条*测试***markdown**`消息`!",
|
||||
"This is a <i>test</i> <b>html</b> <code>message</code>!": "这是一条<i>测试</i><b>html</b><code>消息</code>!",
|
||||
"Trash": "回收站",
|
||||
"You Were Added to Card": "您已被添加到卡片",
|
||||
"You Were Mentioned in Comment": "您在评论中被提及",
|
||||
"%s added you to %s on %s": "%s 在 %s 将您添加到 %s",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"Archive": "封存",
|
||||
"Card Created": "卡片已建立",
|
||||
"Card Moved": "卡片已移動",
|
||||
"New Comment": "新留言",
|
||||
@@ -6,6 +7,7 @@
|
||||
"This is a test text message!": "這是一則測試文字訊息!",
|
||||
"This is a *test* **markdown** `message`!": "這是一則*測試***markdown**`訊息`!",
|
||||
"This is a <i>test</i> <b>html</b> <code>message</code>!": "這是一則<i>測試</i><b>html</b><code>訊息</code>!",
|
||||
"Trash": "垃圾桶",
|
||||
"You Were Added to Card": "您已被加入卡片",
|
||||
"You Were Mentioned in Comment": "您在留言中被提及",
|
||||
"%s added you to %s on %s": "%s 在 %s 將您加入 %s",
|
||||
|
||||
16
server/db/migrations/20251121231641_rename_gin_indexes.js
Normal file
16
server/db/migrations/20251121231641_rename_gin_indexes.js
Normal file
@@ -0,0 +1,16 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
module.exports.up = (knex) =>
|
||||
knex.schema.raw(`
|
||||
ALTER INDEX card_name_index RENAME TO card_name_gin_index;
|
||||
ALTER INDEX card_description_index RENAME TO card_description_gin_index;
|
||||
`);
|
||||
|
||||
module.exports.down = (knex) =>
|
||||
knex.schema.raw(`
|
||||
ALTER INDEX card_name_gin_index RENAME TO card_name_index;
|
||||
ALTER INDEX card_description_gin_index RENAME TO card_description_index;
|
||||
`);
|
||||
1
server/package-lock.json
generated
1
server/package-lock.json
generated
@@ -16,6 +16,7 @@
|
||||
"escape-html": "^1.0.3",
|
||||
"escape-markdown": "^1.0.4",
|
||||
"fs-extra": "^11.3.2",
|
||||
"i18n-2": "^0.7.3",
|
||||
"ico-to-png": "^0.2.2",
|
||||
"istextorbinary": "^9.5.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
"escape-html": "^1.0.3",
|
||||
"escape-markdown": "^1.0.4",
|
||||
"fs-extra": "^11.3.2",
|
||||
"i18n-2": "^0.7.3",
|
||||
"ico-to-png": "^0.2.2",
|
||||
"istextorbinary": "^9.5.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
|
||||
@@ -16,6 +16,7 @@ const USERNAME_REGEX = /^[a-zA-Z0-9]+((_|\.)?[a-zA-Z0-9])*$/;
|
||||
const isUrl = (value) =>
|
||||
validator.isURL(value, {
|
||||
protocols: ['http', 'https'],
|
||||
require_tld: false,
|
||||
require_protocol: true,
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user