mirror of
https://github.com/plankanban/planka.git
synced 2025-12-18 01:11:13 +03:00
feat: Add ability to configure and test SMTP via UI
This commit is contained in:
@@ -224,11 +224,11 @@ extraEnv: []
|
||||
## value: "Your Name"
|
||||
## - name: SMTP_SECURE
|
||||
## value: "true"
|
||||
## - name: SMTP_TLS_REJECT_UNAUTHORIZED
|
||||
## value: "false"
|
||||
## - name: SMTP_USER
|
||||
## value: "your_email@example.com"
|
||||
## - name: SMTP_PASSWORD
|
||||
## value: "your_password"
|
||||
## - name: SMTP_FROM
|
||||
## value: "your_email@example.com"
|
||||
## - name: SMTP_TLS_REJECT_UNAUTHORIZED
|
||||
## value: "false"
|
||||
|
||||
59
client/src/actions/config.js
Normal file
59
client/src/actions/config.js
Normal file
@@ -0,0 +1,59 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const updateConfig = (data) => ({
|
||||
type: ActionTypes.CONFIG_UPDATE,
|
||||
payload: {
|
||||
data,
|
||||
},
|
||||
});
|
||||
|
||||
updateConfig.success = (config) => ({
|
||||
type: ActionTypes.CONFIG_UPDATE__SUCCESS,
|
||||
payload: {
|
||||
config,
|
||||
},
|
||||
});
|
||||
|
||||
updateConfig.failure = (error) => ({
|
||||
type: ActionTypes.CONFIG_UPDATE__FAILURE,
|
||||
payload: {
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
const handleConfigUpdate = (config) => ({
|
||||
type: ActionTypes.CONFIG_UPDATE_HANDLE,
|
||||
payload: {
|
||||
config,
|
||||
},
|
||||
});
|
||||
|
||||
const testSmtpConfig = () => ({
|
||||
type: ActionTypes.SMTP_CONFIG_TEST,
|
||||
payload: {},
|
||||
});
|
||||
|
||||
testSmtpConfig.success = (logs) => ({
|
||||
type: ActionTypes.SMTP_CONFIG_TEST__SUCCESS,
|
||||
payload: {
|
||||
logs,
|
||||
},
|
||||
});
|
||||
|
||||
testSmtpConfig.failure = (error) => ({
|
||||
type: ActionTypes.SMTP_CONFIG_TEST__FAILURE,
|
||||
payload: {
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
export default {
|
||||
updateConfig,
|
||||
handleConfigUpdate,
|
||||
testSmtpConfig,
|
||||
};
|
||||
@@ -6,6 +6,7 @@
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const initializeCore = (
|
||||
config,
|
||||
user,
|
||||
board,
|
||||
webhooks,
|
||||
@@ -32,6 +33,7 @@ const initializeCore = (
|
||||
) => ({
|
||||
type: ActionTypes.CORE_INITIALIZE,
|
||||
payload: {
|
||||
config,
|
||||
user,
|
||||
board,
|
||||
webhooks,
|
||||
@@ -58,10 +60,10 @@ const initializeCore = (
|
||||
},
|
||||
});
|
||||
|
||||
initializeCore.fetchConfig = (config) => ({
|
||||
type: ActionTypes.CORE_INITIALIZE__CONFIG_FETCH,
|
||||
initializeCore.fetchBootstrap = (bootstrap) => ({
|
||||
type: ActionTypes.CORE_INITIALIZE__BOOTSTRAP_FETCH,
|
||||
payload: {
|
||||
config,
|
||||
bootstrap,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import socket from './socket';
|
||||
import login from './login';
|
||||
import core from './core';
|
||||
import modals from './modals';
|
||||
import config from './config';
|
||||
import webhooks from './webhooks';
|
||||
import users from './users';
|
||||
import projects from './projects';
|
||||
@@ -36,6 +37,7 @@ export default {
|
||||
...login,
|
||||
...core,
|
||||
...modals,
|
||||
...config,
|
||||
...webhooks,
|
||||
...users,
|
||||
...projects,
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const initializeLogin = (config) => ({
|
||||
const initializeLogin = (bootstrap) => ({
|
||||
type: ActionTypes.LOGIN_INITIALIZE,
|
||||
payload: {
|
||||
config,
|
||||
bootstrap,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ const handleSocketDisconnect = () => ({
|
||||
});
|
||||
|
||||
const handleSocketReconnect = (
|
||||
bootstrap,
|
||||
config,
|
||||
user,
|
||||
board,
|
||||
@@ -38,6 +39,7 @@ const handleSocketReconnect = (
|
||||
) => ({
|
||||
type: ActionTypes.SOCKET_RECONNECT_HANDLE,
|
||||
payload: {
|
||||
bootstrap,
|
||||
config,
|
||||
user,
|
||||
board,
|
||||
|
||||
@@ -65,6 +65,7 @@ const handleUserUpdate = (
|
||||
user,
|
||||
projectIds,
|
||||
boardIds,
|
||||
bootstrap,
|
||||
config,
|
||||
board,
|
||||
webhooks,
|
||||
@@ -94,6 +95,7 @@ const handleUserUpdate = (
|
||||
user,
|
||||
projectIds,
|
||||
boardIds,
|
||||
bootstrap,
|
||||
config,
|
||||
board,
|
||||
webhooks,
|
||||
|
||||
14
client/src/api/bootstrap.js
vendored
Normal file
14
client/src/api/bootstrap.js
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import http from './http';
|
||||
|
||||
/* Actions */
|
||||
|
||||
const getBootstrap = (headers) => http.get('/bootstrap', undefined, headers);
|
||||
|
||||
export default {
|
||||
getBootstrap,
|
||||
};
|
||||
@@ -3,12 +3,18 @@
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import http from './http';
|
||||
import socket from './socket';
|
||||
|
||||
/* Actions */
|
||||
|
||||
const getConfig = (headers) => http.get('/config', undefined, headers);
|
||||
const getConfig = (headers) => socket.get('/config', undefined, headers);
|
||||
|
||||
const updateConfig = (data, headers) => socket.patch('/config', data, headers);
|
||||
|
||||
const testSmtpConfig = (headers) => socket.post('/config/test-smtp', undefined, headers);
|
||||
|
||||
export default {
|
||||
getConfig,
|
||||
updateConfig,
|
||||
testSmtpConfig,
|
||||
};
|
||||
|
||||
@@ -5,9 +5,10 @@
|
||||
|
||||
import http from './http';
|
||||
import socket from './socket';
|
||||
import config from './config';
|
||||
import bootstrap from './bootstrap';
|
||||
import terms from './terms';
|
||||
import accessTokens from './access-tokens';
|
||||
import config from './config';
|
||||
import webhooks from './webhooks';
|
||||
import users from './users';
|
||||
import projects from './projects';
|
||||
@@ -35,9 +36,10 @@ import notificationServices from './notification-services';
|
||||
export { http, socket };
|
||||
|
||||
export default {
|
||||
...config,
|
||||
...bootstrap,
|
||||
...terms,
|
||||
...accessTokens,
|
||||
...config,
|
||||
...webhooks,
|
||||
...users,
|
||||
...projects,
|
||||
|
||||
@@ -5,18 +5,22 @@
|
||||
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Modal, Tab } from 'semantic-ui-react';
|
||||
|
||||
import selectors from '../../../selectors';
|
||||
import entryActions from '../../../entry-actions';
|
||||
import { useClosableModal } from '../../../hooks';
|
||||
import UsersPane from './UsersPane';
|
||||
import SmtpPane from './SmtpPane';
|
||||
import WebhooksPane from './WebhooksPane';
|
||||
|
||||
import styles from './AdministrationModal.module.scss';
|
||||
|
||||
const AdministrationModal = React.memo(() => {
|
||||
const config = useSelector(selectors.selectConfig);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const [t] = useTranslation();
|
||||
const [activeTabIndex, setActiveTabIndex] = useState(0);
|
||||
@@ -38,13 +42,21 @@ const AdministrationModal = React.memo(() => {
|
||||
}),
|
||||
render: () => <UsersPane />,
|
||||
},
|
||||
{
|
||||
];
|
||||
if (config.smtpHost !== undefined) {
|
||||
panes.push({
|
||||
menuItem: t('common.smtp', {
|
||||
context: 'title',
|
||||
}),
|
||||
render: () => <SmtpPane />,
|
||||
});
|
||||
}
|
||||
panes.push({
|
||||
menuItem: t('common.webhooks', {
|
||||
context: 'title',
|
||||
}),
|
||||
render: () => <WebhooksPane />,
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
const isUsersPaneActive = activeTabIndex === 0;
|
||||
|
||||
|
||||
227
client/src/components/common/AdministrationModal/SmtpPane.jsx
Normal file
227
client/src/components/common/AdministrationModal/SmtpPane.jsx
Normal file
@@ -0,0 +1,227 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import { dequal } from 'dequal';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import TextareaAutosize from 'react-textarea-autosize';
|
||||
import { Button, Checkbox, Divider, Form, Header, Tab, TextArea } from 'semantic-ui-react';
|
||||
import { Input } from '../../../lib/custom-ui';
|
||||
|
||||
import selectors from '../../../selectors';
|
||||
import entryActions from '../../../entry-actions';
|
||||
import { useForm, useNestedRef } from '../../../hooks';
|
||||
|
||||
import styles from './SmtpPane.module.scss';
|
||||
|
||||
const SmtpPane = React.memo(() => {
|
||||
const config = useSelector(selectors.selectConfig);
|
||||
const smtpTest = useSelector(selectors.selectSmtpTest);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const [t] = useTranslation();
|
||||
|
||||
const [passwordFieldRef, handlePasswordFieldRef] = useNestedRef('inputRef');
|
||||
|
||||
const defaultData = useMemo(
|
||||
() => ({
|
||||
smtpHost: config.smtpHost,
|
||||
smtpPort: config.smtpPort,
|
||||
smtpName: config.smtpName,
|
||||
smtpSecure: config.smtpSecure,
|
||||
smtpTlsRejectUnauthorized: config.smtpTlsRejectUnauthorized,
|
||||
smtpUser: config.smtpUser,
|
||||
smtpPassword: config.smtpPassword,
|
||||
smtpFrom: config.smtpFrom,
|
||||
}),
|
||||
[config],
|
||||
);
|
||||
|
||||
const [data, handleFieldChange] = useForm(() => ({
|
||||
...defaultData,
|
||||
smtpHost: defaultData.smtpHost || '',
|
||||
smtpPort: defaultData.smtpPort || '',
|
||||
smtpName: defaultData.smtpName || '',
|
||||
smtpSecure: defaultData.smtpSecure,
|
||||
smtpTlsRejectUnauthorized: defaultData.smtpTlsRejectUnauthorized,
|
||||
smtpUser: defaultData.smtpUser || '',
|
||||
smtpPassword: defaultData.smtpPassword || '',
|
||||
smtpFrom: defaultData.smtpFrom || '',
|
||||
}));
|
||||
|
||||
const isPasswordSet = defaultData.smtpPassword === undefined;
|
||||
|
||||
const cleanData = useMemo(
|
||||
() => ({
|
||||
...data,
|
||||
smtpHost: data.smtpHost.trim() || null,
|
||||
smtpPort: parseInt(data.smtpPort, 10) || null,
|
||||
smtpName: data.smtpName.trim() || null,
|
||||
smtpUser: data.smtpUser.trim() || null,
|
||||
smtpPassword: data.smtpPassword || (isPasswordSet ? undefined : null),
|
||||
smtpFrom: data.smtpFrom.trim() || null,
|
||||
}),
|
||||
[data, isPasswordSet],
|
||||
);
|
||||
|
||||
const handleSubmit = useCallback(() => {
|
||||
dispatch(entryActions.updateConfig(cleanData));
|
||||
}, [dispatch, cleanData]);
|
||||
|
||||
const handlePasswordClear = useCallback(() => {
|
||||
dispatch(
|
||||
entryActions.updateConfig({
|
||||
smtpPassword: null,
|
||||
}),
|
||||
);
|
||||
|
||||
passwordFieldRef.current.focus();
|
||||
}, [dispatch, passwordFieldRef]);
|
||||
|
||||
const handleTestClick = useCallback(() => {
|
||||
dispatch(entryActions.testSmtpConfig());
|
||||
}, [dispatch]);
|
||||
|
||||
const isModified = !dequal(cleanData, defaultData);
|
||||
|
||||
return (
|
||||
<Tab.Pane attached={false} className={styles.wrapper}>
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<div className={styles.text}>{t('common.host')}</div>
|
||||
<Input
|
||||
fluid
|
||||
name="smtpHost"
|
||||
value={data.smtpHost}
|
||||
maxLength={256}
|
||||
className={styles.field}
|
||||
onChange={handleFieldChange}
|
||||
/>
|
||||
<div className={styles.text}>{t('common.port')}</div>
|
||||
<Input
|
||||
fluid
|
||||
type="number"
|
||||
name="smtpPort"
|
||||
value={data.smtpPort}
|
||||
placeholder={data.smtpSecure ? '465' : '587'}
|
||||
min={0}
|
||||
max={65535}
|
||||
step={1}
|
||||
className={styles.field}
|
||||
onChange={handleFieldChange}
|
||||
/>
|
||||
<div className={styles.text}>
|
||||
{t('common.clientHostnameInEhlo')} (
|
||||
{t('common.optional', {
|
||||
context: 'inline',
|
||||
})}
|
||||
)
|
||||
</div>
|
||||
<Input
|
||||
fluid
|
||||
name="smtpName"
|
||||
value={data.smtpName}
|
||||
maxLength={256}
|
||||
className={styles.field}
|
||||
onChange={handleFieldChange}
|
||||
/>
|
||||
<Checkbox
|
||||
name="smtpSecure"
|
||||
checked={data.smtpSecure}
|
||||
label={t('common.useSecureConnection')}
|
||||
className={styles.checkbox}
|
||||
onChange={handleFieldChange}
|
||||
/>
|
||||
<Checkbox
|
||||
name="smtpTlsRejectUnauthorized"
|
||||
checked={data.smtpTlsRejectUnauthorized}
|
||||
label={t('common.rejectUnauthorizedTlsCertificates')}
|
||||
className={classNames(styles.field, styles.checkbox)}
|
||||
onChange={handleFieldChange}
|
||||
/>
|
||||
<div className={styles.text}>
|
||||
{t('common.username')} (
|
||||
{t('common.optional', {
|
||||
context: 'inline',
|
||||
})}
|
||||
)
|
||||
</div>
|
||||
<Input
|
||||
fluid
|
||||
name="smtpUser"
|
||||
value={data.smtpUser}
|
||||
maxLength={256}
|
||||
className={styles.field}
|
||||
onChange={handleFieldChange}
|
||||
/>
|
||||
<div className={styles.text}>
|
||||
{t('common.password')} (
|
||||
{t('common.optional', {
|
||||
context: 'inline',
|
||||
})}
|
||||
)
|
||||
</div>
|
||||
<Input.Password
|
||||
fluid
|
||||
ref={handlePasswordFieldRef}
|
||||
name="smtpPassword"
|
||||
value={data.smtpPassword}
|
||||
placeholder={isPasswordSet ? t('common.passwordIsSet') : undefined}
|
||||
maxLength={256}
|
||||
className={styles.field}
|
||||
onClear={!data.smtpPassword && isPasswordSet ? handlePasswordClear : undefined}
|
||||
onChange={handleFieldChange}
|
||||
/>
|
||||
<div className={styles.text}>
|
||||
{t('common.defaultFrom')} (
|
||||
{t('common.optional', {
|
||||
context: 'inline',
|
||||
})}
|
||||
)
|
||||
</div>
|
||||
<Input
|
||||
fluid
|
||||
name="smtpFrom"
|
||||
value={data.smtpFrom}
|
||||
maxLength={256}
|
||||
className={styles.field}
|
||||
onChange={handleFieldChange}
|
||||
/>
|
||||
<div className={styles.controls}>
|
||||
<Button positive disabled={!isModified} content={t('action.save')} />
|
||||
{config.smtpHost && !isModified && (
|
||||
<Button
|
||||
type="button"
|
||||
content={t('action.sendTestEmail')}
|
||||
loading={smtpTest.isLoading}
|
||||
disabled={smtpTest.isLoading}
|
||||
onClick={handleTestClick}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Form>
|
||||
{smtpTest.logs && (
|
||||
<>
|
||||
<Divider horizontal>
|
||||
<Header as="h4">
|
||||
{t('common.testLog', {
|
||||
context: 'title',
|
||||
})}
|
||||
</Header>
|
||||
</Divider>
|
||||
<TextArea
|
||||
readOnly
|
||||
as={TextareaAutosize}
|
||||
value={smtpTest.logs.join('\n')}
|
||||
className={styles.testLog}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Tab.Pane>
|
||||
);
|
||||
});
|
||||
|
||||
export default SmtpPane;
|
||||
@@ -0,0 +1,41 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
:global(#app) {
|
||||
.checkbox {
|
||||
display: block;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.field {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.testLog {
|
||||
border: 1px solid rgba(9, 30, 66, 0.13);
|
||||
border-radius: 3px;
|
||||
color: #333;
|
||||
line-height: 1.4;
|
||||
padding: 8px 12px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.text {
|
||||
color: #444444;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
@@ -22,8 +22,8 @@ const UsersPane = React.memo(() => {
|
||||
const activeUsersTotal = useSelector(selectors.selectActiveUsersTotal);
|
||||
|
||||
const canAdd = useSelector((state) => {
|
||||
const oidcConfig = selectors.selectOidcConfig(state);
|
||||
return !oidcConfig || !oidcConfig.isEnforced;
|
||||
const oidcBootstrap = selectors.selectOidcBootstrap(state);
|
||||
return !oidcBootstrap || !oidcBootstrap.isEnforced;
|
||||
});
|
||||
|
||||
const [t] = useTranslation();
|
||||
|
||||
@@ -30,8 +30,8 @@ const Core = React.memo(() => {
|
||||
|
||||
// TODO: move to selector?
|
||||
const isNewVersionAvailable = useSelector((state) => {
|
||||
const config = selectors.selectConfig(state);
|
||||
return !!config && config.version !== version;
|
||||
const bootstrap = selectors.selectBootstrap(state);
|
||||
return !!bootstrap && bootstrap.version !== version;
|
||||
});
|
||||
|
||||
const [t] = useTranslation();
|
||||
|
||||
@@ -86,7 +86,7 @@ const createMessage = (error) => {
|
||||
};
|
||||
|
||||
const Content = React.memo(() => {
|
||||
const config = useSelector(selectors.selectConfig);
|
||||
const bootstrap = useSelector(selectors.selectBootstrap);
|
||||
|
||||
const {
|
||||
data: defaultData,
|
||||
@@ -139,8 +139,8 @@ const Content = React.memo(() => {
|
||||
dispatch(entryActions.clearAuthenticateError());
|
||||
}, [dispatch]);
|
||||
|
||||
const withOidc = !!config.oidc;
|
||||
const isOidcEnforced = withOidc && config.oidc.isEnforced;
|
||||
const withOidc = !!bootstrap.oidc;
|
||||
const isOidcEnforced = withOidc && bootstrap.oidc.isEnforced;
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOidcEnforced) {
|
||||
|
||||
@@ -39,7 +39,7 @@ export default {
|
||||
/* Core */
|
||||
|
||||
CORE_INITIALIZE: 'CORE_INITIALIZE',
|
||||
CORE_INITIALIZE__CONFIG_FETCH: 'CORE_INITIALIZE__CONFIG_FETCH',
|
||||
CORE_INITIALIZE__BOOTSTRAP_FETCH: 'CORE_INITIALIZE__BOOTSTRAP_FETCH',
|
||||
FAVORITES_TOGGLE: 'FAVORITES_TOGGLE',
|
||||
EDIT_MODE_TOGGLE: 'EDIT_MODE_TOGGLE',
|
||||
HOME_VIEW_UPDATE: 'HOME_VIEW_UPDATE',
|
||||
@@ -51,6 +51,16 @@ export default {
|
||||
MODAL_OPEN: 'MODAL_OPEN',
|
||||
MODAL_CLOSE: 'MODAL_CLOSE',
|
||||
|
||||
/* Config */
|
||||
|
||||
CONFIG_UPDATE: 'CONFIG_UPDATE',
|
||||
CONFIG_UPDATE__SUCCESS: 'CONFIG_UPDATE__SUCCESS',
|
||||
CONFIG_UPDATE__FAILURE: 'CONFIG_UPDATE__FAILURE',
|
||||
CONFIG_UPDATE_HANDLE: 'CONFIG_UPDATE_HANDLE',
|
||||
SMTP_CONFIG_TEST: 'SMTP_CONFIG_TEST',
|
||||
SMTP_CONFIG_TEST__SUCCESS: 'SMTP_CONFIG_TEST__SUCCESS',
|
||||
SMTP_CONFIG_TEST__FAILURE: 'SMTP_CONFIG_TEST__FAILURE',
|
||||
|
||||
/* Webhooks */
|
||||
|
||||
WEBHOOK_CREATE: 'WEBHOOK_CREATE',
|
||||
|
||||
@@ -34,6 +34,12 @@ export default {
|
||||
MODAL_OPEN: `${PREFIX}/MODAL_OPEN`,
|
||||
MODAL_CLOSE: `${PREFIX}/MODAL_CLOSE`,
|
||||
|
||||
/* Config */
|
||||
|
||||
CONFIG_UPDATE: `${PREFIX}/CONFIG_UPDATE`,
|
||||
CONFIG_UPDATE_HANDLE: `${PREFIX}/CONFIG_UPDATE_HANDLE`,
|
||||
SMTP_CONFIG_TEST: `${PREFIX}/SMTP_CONFIG_TEST`,
|
||||
|
||||
/* Webhooks */
|
||||
|
||||
WEBHOOK_CREATE: `${PREFIX}/WEBHOOK_CREATE`,
|
||||
|
||||
@@ -39,6 +39,8 @@ export default [
|
||||
'commentUpdate',
|
||||
'commentDelete',
|
||||
|
||||
'configUpdate',
|
||||
|
||||
'customFieldCreate',
|
||||
'customFieldUpdate',
|
||||
'customFieldDelete',
|
||||
|
||||
31
client/src/entry-actions/config.js
Normal file
31
client/src/entry-actions/config.js
Normal file
@@ -0,0 +1,31 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import EntryActionTypes from '../constants/EntryActionTypes';
|
||||
|
||||
const updateConfig = (data) => ({
|
||||
type: EntryActionTypes.CONFIG_UPDATE,
|
||||
payload: {
|
||||
data,
|
||||
},
|
||||
});
|
||||
|
||||
const handleConfigUpdate = (config) => ({
|
||||
type: EntryActionTypes.CONFIG_UPDATE_HANDLE,
|
||||
payload: {
|
||||
config,
|
||||
},
|
||||
});
|
||||
|
||||
const testSmtpConfig = () => ({
|
||||
type: EntryActionTypes.SMTP_CONFIG_TEST,
|
||||
payload: {},
|
||||
});
|
||||
|
||||
export default {
|
||||
updateConfig,
|
||||
handleConfigUpdate,
|
||||
testSmtpConfig,
|
||||
};
|
||||
@@ -7,6 +7,7 @@ import socket from './socket';
|
||||
import login from './login';
|
||||
import core from './core';
|
||||
import modals from './modals';
|
||||
import config from './config';
|
||||
import webhooks from './webhooks';
|
||||
import users from './users';
|
||||
import projects from './projects';
|
||||
@@ -34,6 +35,7 @@ export default {
|
||||
...login,
|
||||
...core,
|
||||
...modals,
|
||||
...config,
|
||||
...webhooks,
|
||||
...users,
|
||||
...projects,
|
||||
|
||||
@@ -5,13 +5,15 @@
|
||||
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
const CHECKED_TYPES_SET = new Set(['checkbox', 'radio']);
|
||||
|
||||
export default (initialData) => {
|
||||
const [data, setData] = useState(initialData);
|
||||
|
||||
const handleFieldChange = useCallback((_, { type, name: fieldName, value, checked }) => {
|
||||
setData((prevData) => ({
|
||||
...prevData,
|
||||
[fieldName]: type === 'radio' ? checked : value,
|
||||
[fieldName]: CHECKED_TYPES_SET.has(type) ? checked : value,
|
||||
}));
|
||||
}, []);
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import styles from './InputPassword.module.css';
|
||||
const STRENGTH_SCORE_COLORS = ['red', 'orange', 'yellow', 'olive', 'green'];
|
||||
|
||||
const InputPassword = React.forwardRef(
|
||||
({ value, withStrengthBar, minStrengthScore, className, ...props }, ref) => {
|
||||
({ value, withStrengthBar, minStrengthScore, className, onClear, ...props }, ref) => {
|
||||
const [isVisible, toggleVisible] = useToggle();
|
||||
|
||||
const strengthScore = useMemo(() => {
|
||||
@@ -32,8 +32,13 @@ const InputPassword = React.forwardRef(
|
||||
const inputProps = {
|
||||
...props,
|
||||
ref,
|
||||
value,
|
||||
type: isVisible ? 'text' : 'password',
|
||||
icon: <Icon link name={isVisible ? 'eye' : 'eye slash'} onClick={handleToggleClick} />,
|
||||
icon: onClear ? (
|
||||
<Icon link name="cancel" onClick={onClear} />
|
||||
) : (
|
||||
<Icon link name={isVisible ? 'eye' : 'eye slash'} onClick={handleToggleClick} />
|
||||
),
|
||||
};
|
||||
|
||||
if (!withStrengthBar) {
|
||||
@@ -68,12 +73,14 @@ InputPassword.propTypes = {
|
||||
withStrengthBar: PropTypes.bool,
|
||||
minStrengthScore: PropTypes.number,
|
||||
className: PropTypes.string,
|
||||
onClear: PropTypes.func,
|
||||
};
|
||||
|
||||
InputPassword.defaultProps = {
|
||||
withStrengthBar: false,
|
||||
minStrengthScore: 2,
|
||||
className: undefined,
|
||||
onClear: undefined,
|
||||
};
|
||||
|
||||
export default React.memo(InputPassword);
|
||||
|
||||
@@ -98,6 +98,7 @@ export default {
|
||||
cardsOnThisListAreCompleteAndReadyToBeArchived: null,
|
||||
cardsOnThisListAreReadyToBeWorkedOn: null,
|
||||
clickHereOrRefreshPageToUpdate: null,
|
||||
clientHostnameInEhlo: null,
|
||||
closed: null,
|
||||
color: 'اللون',
|
||||
comments: null,
|
||||
@@ -120,6 +121,7 @@ export default {
|
||||
date: 'تاريخ',
|
||||
deactivateUser_title: null,
|
||||
defaultCardType_title: null,
|
||||
defaultFrom: null,
|
||||
defaultView_title: null,
|
||||
deleteAllBoardsToBeAbleToDeleteThisProject: null,
|
||||
deleteAttachment_title: 'حذف المرفق',
|
||||
@@ -183,6 +185,7 @@ export default {
|
||||
grid: null,
|
||||
hideCompletedTasks: null,
|
||||
hideFromProjectListAndFavorites: null,
|
||||
host: null,
|
||||
hours: 'ساعات',
|
||||
importBoard_title: 'استيراد اللوحة',
|
||||
invalidCurrentPassword: 'كلمة المرور الحالية غير صالحة',
|
||||
@@ -226,8 +229,10 @@ export default {
|
||||
optional_inline: 'اختياري',
|
||||
organization: 'المنظمة',
|
||||
others: null,
|
||||
passwordIsSet: null,
|
||||
phone: 'الهاتف',
|
||||
plankaUsesAppriseToSendNotificationsToOver100PopularServices: null,
|
||||
port: null,
|
||||
preferences: 'التفضيلات',
|
||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||
'نصيحة: اضغط على Ctrl-V (Cmd-V على Mac) لإضافة مرفق من الحافظة.',
|
||||
@@ -236,6 +241,7 @@ export default {
|
||||
projectNotFound_title: 'المشروع غير موجود',
|
||||
projectOwner: null,
|
||||
referenceDataAndKnowledgeStorage: null,
|
||||
rejectUnauthorizedTlsCertificates: null,
|
||||
removeManager_title: 'إزالة المدير',
|
||||
removeMember_title: 'إزالة العضو',
|
||||
role: null,
|
||||
@@ -262,6 +268,7 @@ export default {
|
||||
shared: null,
|
||||
sharedWithMe_title: null,
|
||||
showOnFrontOfCard: null,
|
||||
smtp: null,
|
||||
sortList_title: 'فرز القائمة',
|
||||
stopwatch: 'المؤقت',
|
||||
story: null,
|
||||
@@ -273,6 +280,7 @@ export default {
|
||||
taskList_title: null,
|
||||
team: null,
|
||||
terms: null,
|
||||
testLog_title: null,
|
||||
thereIsNoPreviewAvailableForThisAttachment: 'لا يوجد معاينة متاحة لهذا المرفق.',
|
||||
time: 'الوقت',
|
||||
title: 'العنوان',
|
||||
@@ -286,6 +294,7 @@ export default {
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: null,
|
||||
url: null,
|
||||
useSecureConnection: null,
|
||||
userActions_title: 'إجراءات المستخدم',
|
||||
userAddedCardToList: null,
|
||||
userAddedThisCardToList: '<0>{{user}}</0> تمت إضافة هذه البطاقة إلى {{list}}',
|
||||
@@ -422,6 +431,7 @@ export default {
|
||||
restoreToList: null,
|
||||
returnToBoard: null,
|
||||
save: 'حفظ',
|
||||
sendTestEmail: null,
|
||||
showActive: null,
|
||||
showAllAttachments: 'إظهار جميع المرفقات ({{hidden}} hidden)',
|
||||
showCardsWithThisUser: null,
|
||||
|
||||
@@ -102,6 +102,7 @@ export default {
|
||||
cardsOnThisListAreCompleteAndReadyToBeArchived: null,
|
||||
cardsOnThisListAreReadyToBeWorkedOn: null,
|
||||
clickHereOrRefreshPageToUpdate: null,
|
||||
clientHostnameInEhlo: null,
|
||||
closed: null,
|
||||
color: 'Цвят',
|
||||
comments: null,
|
||||
@@ -124,6 +125,7 @@ export default {
|
||||
date: 'Дата',
|
||||
deactivateUser_title: null,
|
||||
defaultCardType_title: null,
|
||||
defaultFrom: null,
|
||||
defaultView_title: null,
|
||||
deleteAllBoardsToBeAbleToDeleteThisProject: null,
|
||||
deleteAttachment_title: 'Изтриване на прикачен файл',
|
||||
@@ -187,6 +189,7 @@ export default {
|
||||
grid: null,
|
||||
hideCompletedTasks: null,
|
||||
hideFromProjectListAndFavorites: null,
|
||||
host: null,
|
||||
hours: 'Часове',
|
||||
importBoard_title: 'Импортиране на табло',
|
||||
invalidCurrentPassword: 'Невалидна текуща парола',
|
||||
@@ -230,8 +233,10 @@ export default {
|
||||
optional_inline: 'по желание',
|
||||
organization: 'Организация',
|
||||
others: null,
|
||||
passwordIsSet: null,
|
||||
phone: 'Телефон',
|
||||
plankaUsesAppriseToSendNotificationsToOver100PopularServices: null,
|
||||
port: null,
|
||||
preferences: 'Предпочитания',
|
||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||
'Съвет: натиснете Ctrl-V (Cmd-V на Mac), за да добавите прикачен файл от клипборда',
|
||||
@@ -240,6 +245,7 @@ export default {
|
||||
projectNotFound_title: 'Проектът не е намерен',
|
||||
projectOwner: null,
|
||||
referenceDataAndKnowledgeStorage: null,
|
||||
rejectUnauthorizedTlsCertificates: null,
|
||||
removeManager_title: 'Премахване на мениджър',
|
||||
removeMember_title: 'Премахване на член',
|
||||
role: null,
|
||||
@@ -266,6 +272,7 @@ export default {
|
||||
shared: null,
|
||||
sharedWithMe_title: null,
|
||||
showOnFrontOfCard: null,
|
||||
smtp: null,
|
||||
sortList_title: 'Сортиране на списък',
|
||||
stopwatch: 'Хронометър',
|
||||
story: null,
|
||||
@@ -277,6 +284,7 @@ export default {
|
||||
taskList_title: null,
|
||||
team: null,
|
||||
terms: null,
|
||||
testLog_title: null,
|
||||
thereIsNoPreviewAvailableForThisAttachment: 'Няма наличен преглед за този прикачен файл.',
|
||||
time: 'Време',
|
||||
title: 'Заглавие',
|
||||
@@ -290,6 +298,7 @@ export default {
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: null,
|
||||
url: null,
|
||||
useSecureConnection: null,
|
||||
userActions_title: 'Потребителски действия',
|
||||
userAddedCardToList: null,
|
||||
userAddedThisCardToList: '<0>{{user}}</0> добави тази карта в {{list}}',
|
||||
@@ -427,6 +436,7 @@ export default {
|
||||
restoreToList: null,
|
||||
returnToBoard: null,
|
||||
save: 'Запазване',
|
||||
sendTestEmail: null,
|
||||
showActive: null,
|
||||
showAllAttachments: 'Показване на всички прикачени файлове ({{hidden}} скрити)',
|
||||
showCardsWithThisUser: null,
|
||||
|
||||
@@ -111,6 +111,7 @@ export default {
|
||||
'Karty na tomto seznamu jsou kompletní a připravené k archivaci.',
|
||||
cardsOnThisListAreReadyToBeWorkedOn: 'Karty na tomto seznamu jsou připraveny k práci.',
|
||||
clickHereOrRefreshPageToUpdate: '<0>Klikněte sem</0> nebo aktualizujte stránku.',
|
||||
clientHostnameInEhlo: null,
|
||||
closed: 'Uzavřeno',
|
||||
color: 'Barva',
|
||||
comments: 'Komentáře',
|
||||
@@ -133,6 +134,7 @@ export default {
|
||||
date: 'Datum',
|
||||
deactivateUser_title: 'Deaktivace uživatele',
|
||||
defaultCardType_title: 'Výchozí typ karty',
|
||||
defaultFrom: null,
|
||||
defaultView_title: 'Výchozí zobrazení',
|
||||
deleteAllBoardsToBeAbleToDeleteThisProject:
|
||||
'Pro smazání tohoto projektu je třeba nejprve smazat všechny nástěnky',
|
||||
@@ -197,6 +199,7 @@ export default {
|
||||
grid: 'Mřížka',
|
||||
hideCompletedTasks: 'Skrýt dokončené úkoly',
|
||||
hideFromProjectListAndFavorites: 'Skrýt ze seznamu projektů a oblíbených položek',
|
||||
host: null,
|
||||
hours: 'Hodiny',
|
||||
importBoard_title: 'Importovat nástěnku',
|
||||
invalidCurrentPassword: 'Neplatné aktuální heslo',
|
||||
@@ -241,9 +244,11 @@ export default {
|
||||
optional_inline: 'volitelné',
|
||||
organization: 'Společnost',
|
||||
others: 'Jiné',
|
||||
passwordIsSet: null,
|
||||
phone: 'Telefon',
|
||||
plankaUsesAppriseToSendNotificationsToOver100PopularServices:
|
||||
'PLANKA používá <1><0>Apprise</0></1> k zasílání oznámení do více než 100 oblíbených služeb.',
|
||||
port: null,
|
||||
preferences: 'Volby',
|
||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||
'Tip: stisknutím Ctrl-V (Cmd-V na Macu) přidáte přílohu ze schránky.',
|
||||
@@ -252,6 +257,7 @@ export default {
|
||||
projectNotFound_title: 'Projekt nenalezen',
|
||||
projectOwner: 'Vlastník projektu',
|
||||
referenceDataAndKnowledgeStorage: 'Uchovávání referenčních údajů a znalostí.',
|
||||
rejectUnauthorizedTlsCertificates: null,
|
||||
removeManager_title: 'Odstranit správce',
|
||||
removeMember_title: 'Odstranit člena',
|
||||
role: 'Role',
|
||||
@@ -278,6 +284,7 @@ export default {
|
||||
shared: 'Sdílené',
|
||||
sharedWithMe_title: 'Sdíleno se mnou',
|
||||
showOnFrontOfCard: 'Zobrazit na přední straně karty',
|
||||
smtp: null,
|
||||
sortList_title: 'Řadit podle',
|
||||
stopwatch: 'Časovač',
|
||||
story: 'Příběh',
|
||||
@@ -289,6 +296,7 @@ export default {
|
||||
taskList_title: 'Seznam úkolů',
|
||||
team: 'Tým',
|
||||
terms: 'Podmínky',
|
||||
testLog_title: null,
|
||||
thereIsNoPreviewAvailableForThisAttachment: 'Pro tuto přílohu není k dispozici žádný náhled.',
|
||||
time: 'Čas',
|
||||
title: 'Titulek',
|
||||
@@ -302,6 +310,7 @@ export default {
|
||||
uploadFailedNotEnoughStorageSpace: 'Nahrávání se nezdařilo: Nedostatek úložného prostoru.',
|
||||
uploadedImages: 'Nahrané obrázky',
|
||||
url: 'URL',
|
||||
useSecureConnection: null,
|
||||
userActions_title: 'Akce uživatele',
|
||||
userAddedCardToList: '<0>{{user}}</0> přidal <2>{{card}}</2> do {{list}}',
|
||||
userAddedThisCardToList: '<0>{{user}}</0> přidal kartu do {{list}}',
|
||||
@@ -442,6 +451,7 @@ export default {
|
||||
restoreToList: 'Obnovit do {{list}}',
|
||||
returnToBoard: 'Návrat na nástěnku',
|
||||
save: 'Uložit',
|
||||
sendTestEmail: null,
|
||||
showActive: 'Zobrazit aktivní',
|
||||
showAllAttachments: 'Zozbrazit všechny přílohy ({{hidden}} skryté)',
|
||||
showCardsWithThisUser: 'Zobrazit karty tohoto uživatele',
|
||||
|
||||
@@ -115,6 +115,7 @@ export default {
|
||||
'Kort på denne liste er afsluttede og klar til at blive arkiveret.',
|
||||
cardsOnThisListAreReadyToBeWorkedOn: 'Kort på denne liste er klar til at blive arbejdet på.',
|
||||
clickHereOrRefreshPageToUpdate: '<0>Klik her</0> eller opdater siden for at opdatere.',
|
||||
clientHostnameInEhlo: null,
|
||||
closed: 'Lukket',
|
||||
color: 'Farve',
|
||||
comments: 'Kommentarer',
|
||||
@@ -138,6 +139,7 @@ export default {
|
||||
date: 'Dato',
|
||||
deactivateUser_title: 'Deaktiver bruger',
|
||||
defaultCardType_title: 'Standard korttype',
|
||||
defaultFrom: null,
|
||||
defaultView_title: 'Standard visning',
|
||||
deleteAllBoardsToBeAbleToDeleteThisProject:
|
||||
'Slet alle tavler for at kunne slette dette projekt.',
|
||||
@@ -202,6 +204,7 @@ export default {
|
||||
grid: 'Gitter',
|
||||
hideCompletedTasks: null,
|
||||
hideFromProjectListAndFavorites: 'Skjul fra projektliste og favoritter',
|
||||
host: null,
|
||||
hours: 'Timer',
|
||||
importBoard_title: 'Importer tavle',
|
||||
invalidCurrentPassword: 'Nuværende adgangskode er ugyldig',
|
||||
@@ -245,9 +248,11 @@ export default {
|
||||
optional_inline: 'valgfri',
|
||||
organization: 'Organisation',
|
||||
others: 'Andre',
|
||||
passwordIsSet: null,
|
||||
phone: 'Telefon',
|
||||
plankaUsesAppriseToSendNotificationsToOver100PopularServices:
|
||||
'PLANKA bruger <1><0>Apprise</0></1> til at sende notifikationer til over 100 populære tjenester.',
|
||||
port: null,
|
||||
preferences: 'Præferencer',
|
||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||
'Tip: Tryk Ctrl-V (Cmd-V på Mac) for at vedhæfte direkte fra udklipsholder.',
|
||||
@@ -256,6 +261,7 @@ export default {
|
||||
projectNotFound_title: 'Projekt ikke fundet',
|
||||
projectOwner: 'Projektejer',
|
||||
referenceDataAndKnowledgeStorage: 'Reference data og vidensopbevaring',
|
||||
rejectUnauthorizedTlsCertificates: null,
|
||||
removeManager_title: 'Fjern projektleder',
|
||||
removeMember_title: 'Fjern medlem',
|
||||
role: 'Rolle',
|
||||
@@ -282,6 +288,7 @@ export default {
|
||||
shared: 'Delt',
|
||||
sharedWithMe_title: 'Delt med mig',
|
||||
showOnFrontOfCard: 'Vis på forsiden af kortet',
|
||||
smtp: null,
|
||||
sortList_title: 'Sortér liste',
|
||||
stopwatch: 'Stopur',
|
||||
story: 'Story',
|
||||
@@ -293,6 +300,7 @@ export default {
|
||||
taskList_title: 'Opgaveliste',
|
||||
team: 'Team',
|
||||
terms: null,
|
||||
testLog_title: null,
|
||||
thereIsNoPreviewAvailableForThisAttachment:
|
||||
'Der er ingen forhåndsvisning tilgængelig for denne vedhæftning.',
|
||||
time: 'Tid',
|
||||
@@ -307,6 +315,7 @@ export default {
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: 'Uploadede billeder',
|
||||
url: null,
|
||||
useSecureConnection: null,
|
||||
userActions_title: 'Brugerhandlinger',
|
||||
userAddedCardToList: '<0>{{user}}</0> tilføjede <2>{{card}}</2> til {{list}}',
|
||||
userAddedThisCardToList: '<0>{{user}}</0> tilføjede kortet til {{list}}',
|
||||
@@ -447,6 +456,7 @@ export default {
|
||||
restoreToList: 'Gendan til {{list}}',
|
||||
returnToBoard: 'Tilbage til tavle',
|
||||
save: 'Gem ændringer',
|
||||
sendTestEmail: null,
|
||||
showActive: 'Vis aktive',
|
||||
showAllAttachments: 'Vis alle vedhæftninger ({{hidden}} skjulte)',
|
||||
showCardsWithThisUser: 'Vis kort med denne bruger',
|
||||
|
||||
@@ -125,6 +125,7 @@ export default {
|
||||
cardsOnThisListAreReadyToBeWorkedOn: 'Karten in dieser Liste sind bereit zur Bearbeitung.',
|
||||
clickHereOrRefreshPageToUpdate:
|
||||
'<0>Hier klicken</0> oder Seite aktualisieren, um zu aktualisieren.',
|
||||
clientHostnameInEhlo: null,
|
||||
closed: 'Geschlossen',
|
||||
color: 'Farbe',
|
||||
comments: 'Kommentare',
|
||||
@@ -147,6 +148,7 @@ export default {
|
||||
date: 'Datum',
|
||||
deactivateUser_title: 'Benutzer deaktivieren',
|
||||
defaultCardType_title: 'Standard-Kartentyp',
|
||||
defaultFrom: null,
|
||||
defaultView_title: 'Standardansicht',
|
||||
deleteAllBoardsToBeAbleToDeleteThisProject:
|
||||
'Löschen Sie alle Arbeitsbereiche, um dieses Projekt löschen zu können',
|
||||
@@ -211,6 +213,7 @@ export default {
|
||||
grid: 'Raster',
|
||||
hideCompletedTasks: null,
|
||||
hideFromProjectListAndFavorites: 'Aus Projektliste und Favoriten ausblenden',
|
||||
host: null,
|
||||
hours: 'Stunden',
|
||||
importBoard_title: 'Board importieren',
|
||||
invalidCurrentPassword: 'Das aktuelle Passwort ist falsch',
|
||||
@@ -254,9 +257,11 @@ export default {
|
||||
optional_inline: 'Optional',
|
||||
organization: 'Organisation',
|
||||
others: 'Andere',
|
||||
passwordIsSet: null,
|
||||
phone: 'Telefon',
|
||||
plankaUsesAppriseToSendNotificationsToOver100PopularServices:
|
||||
'PLANKA verwendet <1><0>Apprise</0></1>, um Benachrichtigungen an über 100 beliebte Dienste zu senden.',
|
||||
port: null,
|
||||
preferences: 'Voreinstellungen',
|
||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||
'Tipp: Drücken Sie STRG-V (Cmd-V auf Mac), um einen Anhang aus der Zwischenablage hinzuzufügen.',
|
||||
@@ -265,6 +270,7 @@ export default {
|
||||
projectNotFound_title: 'Projekt nicht gefunden',
|
||||
projectOwner: 'Projektleitung',
|
||||
referenceDataAndKnowledgeStorage: 'Speichern von Wissen und Referenzen.',
|
||||
rejectUnauthorizedTlsCertificates: null,
|
||||
removeManager_title: 'Projektleiter entfernen',
|
||||
removeMember_title: 'Mitglied entfernen',
|
||||
role: 'Rolle',
|
||||
@@ -291,6 +297,7 @@ export default {
|
||||
shared: 'Geteilt',
|
||||
sharedWithMe_title: 'Mit mir geteilt',
|
||||
showOnFrontOfCard: 'Auf der Vorderseite der Karte anzeigen',
|
||||
smtp: null,
|
||||
sortList_title: 'Liste sortieren',
|
||||
stopwatch: 'Stoppuhr',
|
||||
story: 'Wissen',
|
||||
@@ -302,6 +309,7 @@ export default {
|
||||
taskList_title: 'Aufgaben',
|
||||
team: 'Team',
|
||||
terms: null,
|
||||
testLog_title: null,
|
||||
thereIsNoPreviewAvailableForThisAttachment: 'Für diesen Anhang ist keine Vorschau verfügbar.',
|
||||
time: 'Zeit',
|
||||
title: 'Titel',
|
||||
@@ -315,6 +323,7 @@ export default {
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: 'Hochgeladene Bilder',
|
||||
url: null,
|
||||
useSecureConnection: null,
|
||||
userActions_title: 'Benutzeraktionen',
|
||||
userAddedCardToList: null,
|
||||
userAddedThisCardToList: '<0>{{user}}</0> hat diese Karte hinzugefügt zu {{list}}',
|
||||
@@ -452,6 +461,7 @@ export default {
|
||||
restoreToList: 'Wiederherstellen in {{list}}',
|
||||
returnToBoard: 'Zurück zum Arbeitsbereich',
|
||||
save: 'Speichern',
|
||||
sendTestEmail: null,
|
||||
showActive: 'Aktive anzeigen',
|
||||
showAllAttachments: 'Alle Anhänge anzeigen ({{hidden}} versteckt)',
|
||||
showCardsWithThisUser: 'Karten mit diesem Benutzer zeigen',
|
||||
|
||||
@@ -125,6 +125,7 @@ export default {
|
||||
cardsOnThisListAreReadyToBeWorkedOn:
|
||||
'Οι κάρτες σε αυτήν τη λίστα είναι έτοιμες για επεξεργασία.',
|
||||
clickHereOrRefreshPageToUpdate: '<0>Κάντε κλικ εδώ</0> ή ανανεώστε τη σελίδα για ενημέρωση.',
|
||||
clientHostnameInEhlo: null,
|
||||
closed: 'Κλειστό',
|
||||
color: 'Χρώμα',
|
||||
comments: 'Σχόλια',
|
||||
@@ -148,6 +149,7 @@ export default {
|
||||
date: 'Ημερομηνία',
|
||||
deactivateUser_title: 'Απενεργοποίηση χρήστη',
|
||||
defaultCardType_title: 'Προεπιλεγμένος τύπος κάρτας',
|
||||
defaultFrom: null,
|
||||
defaultView_title: 'Προεπιλεγμένη προβολή',
|
||||
deleteAllBoardsToBeAbleToDeleteThisProject:
|
||||
'Διαγράψτε όλους τους πίνακες για να μπορέσετε να διαγράψετε αυτό το έργο',
|
||||
@@ -212,6 +214,7 @@ export default {
|
||||
grid: 'Πλέγμα',
|
||||
hideCompletedTasks: null,
|
||||
hideFromProjectListAndFavorites: 'Απόκρυψη από τη λίστα έργων και τα αγαπημένα',
|
||||
host: null,
|
||||
hours: 'Ώρες',
|
||||
importBoard_title: 'Εισαγωγή πίνακα',
|
||||
invalidCurrentPassword: 'Μη έγκυρος τρέχων κωδικός',
|
||||
@@ -255,9 +258,11 @@ export default {
|
||||
optional_inline: 'προαιρετικό',
|
||||
organization: 'Οργάνωση',
|
||||
others: 'Άλλοι',
|
||||
passwordIsSet: null,
|
||||
phone: 'Τηλέφωνο',
|
||||
plankaUsesAppriseToSendNotificationsToOver100PopularServices:
|
||||
'Το PLANKA χρησιμοποιεί το <1><0>Apprise</0></1> για να στέλνει ειδοποιήσεις σε πάνω από 100 δημοφιλείς υπηρεσίες.',
|
||||
port: null,
|
||||
preferences: 'Προτιμήσεις',
|
||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||
'Συμβουλή: πατήστε Ctrl-V (Cmd-V σε Mac) για να προσθέσετε συνημμένο από το πρόχειρο.',
|
||||
@@ -266,6 +271,7 @@ export default {
|
||||
projectNotFound_title: 'Το έργο δεν βρέθηκε',
|
||||
projectOwner: 'Ιδιοκτήτης έργου',
|
||||
referenceDataAndKnowledgeStorage: 'Αποθήκευση δεδομένων και γνώσης αναφοράς.',
|
||||
rejectUnauthorizedTlsCertificates: null,
|
||||
removeManager_title: 'Αφαίρεση διαχειριστή',
|
||||
removeMember_title: 'Αφαίρεση μέλους',
|
||||
role: 'Ρόλος',
|
||||
@@ -292,6 +298,7 @@ export default {
|
||||
shared: 'Κοινόχρηστο',
|
||||
sharedWithMe_title: 'Κοινόχρηστο με εμένα',
|
||||
showOnFrontOfCard: 'Εμφάνιση στο μπροστινό μέρος της κάρτας',
|
||||
smtp: null,
|
||||
sortList_title: 'Ταξινόμηση λίστας',
|
||||
stopwatch: 'Χρονόμετρο',
|
||||
story: 'Ιστορία',
|
||||
@@ -303,6 +310,7 @@ export default {
|
||||
taskList_title: 'Λίστα εργασιών',
|
||||
team: 'Ομάδα',
|
||||
terms: null,
|
||||
testLog_title: null,
|
||||
thereIsNoPreviewAvailableForThisAttachment:
|
||||
'Δεν υπάρχει διαθέσιμη προεπισκόπηση για αυτό το συνημμένο.',
|
||||
time: 'Ώρα',
|
||||
@@ -317,6 +325,7 @@ export default {
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: 'Μεταφορτωμένες εικόνες',
|
||||
url: null,
|
||||
useSecureConnection: null,
|
||||
userActions_title: 'Ενέργειες χρήστη',
|
||||
userAddedCardToList: '<0>{{user}}</0> πρόσθεσε <2>{{card}}</2> στη λίστα {{list}}',
|
||||
userAddedThisCardToList: '<0>{{user}}</0> πρόσθεσε αυτήν την κάρτα στη λίστα {{list}}',
|
||||
@@ -463,6 +472,7 @@ export default {
|
||||
restoreToList: 'Επαναφορά στη {{list}}',
|
||||
returnToBoard: 'Επιστροφή στον πίνακα',
|
||||
save: 'Αποθήκευση',
|
||||
sendTestEmail: null,
|
||||
showActive: 'Εμφάνιση ενεργών',
|
||||
showAllAttachments: 'Εμφάνιση όλων των συνημμένων ({{hidden}} κρυφά)',
|
||||
showCardsWithThisUser: 'Εμφάνιση καρτών με αυτόν τον χρήστη',
|
||||
|
||||
@@ -115,6 +115,7 @@ export default {
|
||||
'Cards on this list are complete and ready to be archived.',
|
||||
cardsOnThisListAreReadyToBeWorkedOn: 'Cards on this list are ready to be worked on.',
|
||||
clickHereOrRefreshPageToUpdate: '<0>Click here</0> or refresh the page to update.',
|
||||
clientHostnameInEhlo: 'Client hostname in EHLO',
|
||||
closed: 'Closed',
|
||||
color: 'Color',
|
||||
comments: 'Comments',
|
||||
@@ -137,6 +138,7 @@ export default {
|
||||
date: 'Date',
|
||||
deactivateUser_title: 'Deactivate User',
|
||||
defaultCardType_title: 'Default Card Type',
|
||||
defaultFrom: 'Default "from"',
|
||||
defaultView_title: 'Default View',
|
||||
deleteAllBoardsToBeAbleToDeleteThisProject:
|
||||
'Delete all boards to be able to delete this project',
|
||||
@@ -201,6 +203,7 @@ export default {
|
||||
grid: 'Grid',
|
||||
hideCompletedTasks: 'Hide completed tasks',
|
||||
hideFromProjectListAndFavorites: 'Hide from project list and favorites',
|
||||
host: 'Host',
|
||||
hours: 'Hours',
|
||||
importBoard_title: 'Import Board',
|
||||
invalidCurrentPassword: 'Invalid current password',
|
||||
@@ -245,9 +248,11 @@ export default {
|
||||
optional_inline: 'optional',
|
||||
organization: 'Organization',
|
||||
others: 'Others',
|
||||
passwordIsSet: 'Password is set',
|
||||
phone: 'Phone',
|
||||
plankaUsesAppriseToSendNotificationsToOver100PopularServices:
|
||||
'PLANKA uses <1><0>Apprise</0></1> to send notifications to over 100 popular services.',
|
||||
port: 'Port',
|
||||
preferences: 'Preferences',
|
||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||
'Tip: press Ctrl-V (Cmd-V on Mac) to add an attachment from the clipboard.',
|
||||
@@ -256,6 +261,7 @@ export default {
|
||||
projectNotFound_title: 'Project Not Found',
|
||||
projectOwner: 'Project owner',
|
||||
referenceDataAndKnowledgeStorage: 'Reference data and knowledge storage.',
|
||||
rejectUnauthorizedTlsCertificates: 'Reject unauthorized TLS certificates',
|
||||
removeManager_title: 'Remove Manager',
|
||||
removeMember_title: 'Remove Member',
|
||||
role: 'Role',
|
||||
@@ -282,6 +288,7 @@ export default {
|
||||
shared: 'Shared',
|
||||
sharedWithMe_title: 'Shared With Me',
|
||||
showOnFrontOfCard: 'Show on front of card',
|
||||
smtp: 'SMTP',
|
||||
sortList_title: 'Sort List',
|
||||
stopwatch: 'Stopwatch',
|
||||
story: 'Story',
|
||||
@@ -293,6 +300,7 @@ export default {
|
||||
taskList_title: 'Task List',
|
||||
team: 'Team',
|
||||
terms: 'Terms',
|
||||
testLog_title: 'Test Log',
|
||||
thereIsNoPreviewAvailableForThisAttachment:
|
||||
'There is no preview available for this attachment.',
|
||||
time: 'Time',
|
||||
@@ -307,6 +315,7 @@ export default {
|
||||
uploadFailedNotEnoughStorageSpace: 'Upload failed: Not enough storage space.',
|
||||
uploadedImages: 'Uploaded images',
|
||||
url: 'URL',
|
||||
useSecureConnection: 'Use secure connection',
|
||||
userActions_title: 'User Actions',
|
||||
userAddedCardToList: '<0>{{user}}</0> added <2>{{card}}</2> to {{list}}',
|
||||
userAddedThisCardToList: '<0>{{user}}</0> added this card to {{list}}',
|
||||
@@ -446,6 +455,7 @@ export default {
|
||||
restoreToList: 'Restore to {{list}}',
|
||||
returnToBoard: 'Return to board',
|
||||
save: 'Save',
|
||||
sendTestEmail: 'Send test email',
|
||||
showActive: 'Show active',
|
||||
showAllAttachments: 'Show all attachments ({{hidden}} hidden)',
|
||||
showCardsWithThisUser: 'Show cards with this user',
|
||||
|
||||
@@ -110,6 +110,7 @@ export default {
|
||||
'Cards on this list are complete and ready to be archived.',
|
||||
cardsOnThisListAreReadyToBeWorkedOn: 'Cards on this list are ready to be worked on.',
|
||||
clickHereOrRefreshPageToUpdate: '<0>Click here</0> or refresh the page to update.',
|
||||
clientHostnameInEhlo: 'Client hostname in EHLO',
|
||||
closed: 'Closed',
|
||||
color: 'Color',
|
||||
comments: 'Comments',
|
||||
@@ -132,6 +133,7 @@ export default {
|
||||
date: 'Date',
|
||||
deactivateUser_title: 'Deactivate User',
|
||||
defaultCardType_title: 'Default Card Type',
|
||||
defaultFrom: 'Default "from"',
|
||||
defaultView_title: 'Default View',
|
||||
deleteAllBoardsToBeAbleToDeleteThisProject:
|
||||
'Delete all boards to be able to delete this project',
|
||||
@@ -196,6 +198,7 @@ export default {
|
||||
grid: 'Grid',
|
||||
hideCompletedTasks: 'Hide completed tasks',
|
||||
hideFromProjectListAndFavorites: 'Hide from project list and favorites',
|
||||
host: 'Host',
|
||||
hours: 'Hours',
|
||||
importBoard_title: 'Import Board',
|
||||
invalidCurrentPassword: 'Invalid current password',
|
||||
@@ -240,9 +243,11 @@ export default {
|
||||
optional_inline: 'optional',
|
||||
organization: 'Organization',
|
||||
others: 'Others',
|
||||
passwordIsSet: 'Password is set',
|
||||
phone: 'Phone',
|
||||
plankaUsesAppriseToSendNotificationsToOver100PopularServices:
|
||||
'PLANKA uses <1><0>Apprise</0></1> to send notifications to over 100 popular services.',
|
||||
port: 'Port',
|
||||
preferences: 'Preferences',
|
||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||
'Tip: press Ctrl-V (Cmd-V on Mac) to add an attachment from the clipboard.',
|
||||
@@ -251,6 +256,7 @@ export default {
|
||||
projectNotFound_title: 'Project Not Found',
|
||||
projectOwner: 'Project owner',
|
||||
referenceDataAndKnowledgeStorage: 'Reference data and knowledge storage.',
|
||||
rejectUnauthorizedTlsCertificates: 'Reject unauthorized TLS certificates',
|
||||
removeManager_title: 'Remove Manager',
|
||||
removeMember_title: 'Remove Member',
|
||||
role: 'Role',
|
||||
@@ -277,6 +283,7 @@ export default {
|
||||
shared: 'Shared',
|
||||
sharedWithMe_title: 'Shared With Me',
|
||||
showOnFrontOfCard: 'Show on front of card',
|
||||
smtp: 'SMTP',
|
||||
sortList_title: 'Sort List',
|
||||
stopwatch: 'Stopwatch',
|
||||
story: 'Story',
|
||||
@@ -288,6 +295,7 @@ export default {
|
||||
taskList_title: 'Task List',
|
||||
team: 'Team',
|
||||
terms: 'Terms',
|
||||
testLog_title: 'Test Log',
|
||||
thereIsNoPreviewAvailableForThisAttachment:
|
||||
'There is no preview available for this attachment.',
|
||||
time: 'Time',
|
||||
@@ -302,6 +310,7 @@ export default {
|
||||
uploadFailedNotEnoughStorageSpace: 'Upload failed: Not enough storage space.',
|
||||
uploadedImages: 'Uploaded images',
|
||||
url: 'URL',
|
||||
useSecureConnection: 'Use secure connection',
|
||||
userActions_title: 'User Actions',
|
||||
userAddedCardToList: '<0>{{user}}</0> added <2>{{card}}</2> to {{list}}',
|
||||
userAddedThisCardToList: '<0>{{user}}</0> added this card to {{list}}',
|
||||
@@ -441,6 +450,7 @@ export default {
|
||||
restoreToList: 'Restore to {{list}}',
|
||||
returnToBoard: 'Return to board',
|
||||
save: 'Save',
|
||||
sendTestEmail: 'Send test email',
|
||||
showActive: 'Show active',
|
||||
showAllAttachments: 'Show all attachments ({{hidden}} hidden)',
|
||||
showCardsWithThisUser: 'Show cards with this user',
|
||||
|
||||
@@ -116,6 +116,7 @@ export default {
|
||||
cardsOnThisListAreReadyToBeWorkedOn:
|
||||
'Las tarjetas en esta lista están listas para ser trabajadas.',
|
||||
clickHereOrRefreshPageToUpdate: '<0>Haz clic aquí</0> o actualiza la página para actualizar.',
|
||||
clientHostnameInEhlo: null,
|
||||
closed: 'Cerrado',
|
||||
color: 'Color',
|
||||
comments: 'Comentarios',
|
||||
@@ -138,6 +139,7 @@ export default {
|
||||
date: 'Fecha',
|
||||
deactivateUser_title: 'Desactivar usuario',
|
||||
defaultCardType_title: 'Tipo de tarjeta por defecto',
|
||||
defaultFrom: null,
|
||||
defaultView_title: 'Vista por defecto',
|
||||
deleteAllBoardsToBeAbleToDeleteThisProject:
|
||||
'Elimina todos los tableros para poder eliminar este proyecto.',
|
||||
@@ -202,6 +204,7 @@ export default {
|
||||
grid: 'Cuadrícula',
|
||||
hideCompletedTasks: null,
|
||||
hideFromProjectListAndFavorites: 'Ocultar de la lista de proyectos y favoritos',
|
||||
host: null,
|
||||
hours: 'Horas',
|
||||
importBoard_title: 'Importar tablero',
|
||||
invalidCurrentPassword: 'Contraseña actual inválida',
|
||||
@@ -245,9 +248,11 @@ export default {
|
||||
optional_inline: 'opcional',
|
||||
organization: 'Organización',
|
||||
others: 'Otros',
|
||||
passwordIsSet: null,
|
||||
phone: 'Teléfono',
|
||||
plankaUsesAppriseToSendNotificationsToOver100PopularServices:
|
||||
'PLANKA utiliza <1><0>Apprise</0></1> para enviar notificaciones a más de 100 servicios populares.',
|
||||
port: null,
|
||||
preferences: 'Preferencias',
|
||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||
'Tip: presiona Ctrl-V (Cmd-V en Mac) para añadir adjuntos desde el portapapeles.',
|
||||
@@ -256,6 +261,7 @@ export default {
|
||||
projectNotFound_title: 'Proyecto no encontrado',
|
||||
projectOwner: 'Propietario del proyecto',
|
||||
referenceDataAndKnowledgeStorage: 'Datos de referencia y almacenamiento de conocimiento',
|
||||
rejectUnauthorizedTlsCertificates: null,
|
||||
removeManager_title: null,
|
||||
removeMember_title: 'Remover Miembro',
|
||||
role: 'Rol',
|
||||
@@ -282,6 +288,7 @@ export default {
|
||||
shared: 'Compartido',
|
||||
sharedWithMe_title: 'Compartido conmigo',
|
||||
showOnFrontOfCard: 'Mostrar en el frente de la tarjeta',
|
||||
smtp: null,
|
||||
sortList_title: 'Ordenar',
|
||||
stopwatch: 'Temporizador',
|
||||
story: 'Historia',
|
||||
@@ -293,6 +300,7 @@ export default {
|
||||
taskList_title: 'Lista de tareas',
|
||||
team: 'Equipo',
|
||||
terms: null,
|
||||
testLog_title: null,
|
||||
thereIsNoPreviewAvailableForThisAttachment:
|
||||
'No hay vista previa disponible para este adjunto.',
|
||||
time: 'Tiempo',
|
||||
@@ -307,6 +315,7 @@ export default {
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: 'Imágenes subidas',
|
||||
url: null,
|
||||
useSecureConnection: null,
|
||||
userActions_title: 'Acciones de Usuario',
|
||||
userAddedCardToList: '<0>{{user}}</0> añadió <2>{{card}}</2> a {{list}}',
|
||||
userAddedThisCardToList: '<0>{{user}}</0> añadido a esta tarjeta en {{list}}',
|
||||
@@ -446,6 +455,7 @@ export default {
|
||||
restoreToList: 'Restaurar a {{list}}',
|
||||
returnToBoard: 'Volver al tablero',
|
||||
save: 'Guardar',
|
||||
sendTestEmail: null,
|
||||
showActive: 'Mostrar activos',
|
||||
showAllAttachments: 'Mostrar todos los adjuntos ({{hidden}} ocultos)',
|
||||
showCardsWithThisUser: 'Mostrar tarjetas con este usuario',
|
||||
|
||||
@@ -115,6 +115,7 @@ export default {
|
||||
'Kaardid sellel nimekirjal on täidetud ja valmis arhiveerimiseks.',
|
||||
cardsOnThisListAreReadyToBeWorkedOn: 'Kaardid sellel nimekirjal on valmis tööle.',
|
||||
clickHereOrRefreshPageToUpdate: '<0>Klõpsa siia</0> või uuendage lehte.',
|
||||
clientHostnameInEhlo: null,
|
||||
closed: 'Suletud',
|
||||
color: 'Värv',
|
||||
comments: 'Kommentaarid',
|
||||
@@ -137,6 +138,7 @@ export default {
|
||||
date: 'Kuupäev',
|
||||
deactivateUser_title: 'Deaktiveeri kasutaja',
|
||||
defaultCardType_title: 'Vaikimisi kaardi tüüp',
|
||||
defaultFrom: null,
|
||||
defaultView_title: 'Vaikimisi vaade',
|
||||
deleteAllBoardsToBeAbleToDeleteThisProject:
|
||||
'Kustuta kõik tahvlid, et seda projekti kustutada',
|
||||
@@ -201,6 +203,7 @@ export default {
|
||||
grid: 'Grill',
|
||||
hideCompletedTasks: null,
|
||||
hideFromProjectListAndFavorites: 'Peida projektiloendist ja lemmikutest',
|
||||
host: null,
|
||||
hours: 'Tunnid',
|
||||
importBoard_title: 'Impordi tahvel',
|
||||
invalidCurrentPassword: 'Vale praegune parool',
|
||||
@@ -244,9 +247,11 @@ export default {
|
||||
optional_inline: 'valikuline',
|
||||
organization: 'Organisatsioon',
|
||||
others: 'Teised',
|
||||
passwordIsSet: null,
|
||||
phone: 'Telefon',
|
||||
plankaUsesAppriseToSendNotificationsToOver100PopularServices:
|
||||
'PLANKA kasutab <1><0>Apprise</0></1> teavitusteenuse, et teavitada üle 100 populaarset teenust.',
|
||||
port: null,
|
||||
preferences: 'Eelistused',
|
||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||
"Näpunäide: vajutage Ctrl-V (Cmd-V Mac'il) manuse lisamiseks kleebist.",
|
||||
@@ -255,6 +260,7 @@ export default {
|
||||
projectNotFound_title: 'Projekt ei leitud',
|
||||
projectOwner: 'Projekti omanik',
|
||||
referenceDataAndKnowledgeStorage: 'Viideandmete ja teadmise salvestamiseks.',
|
||||
rejectUnauthorizedTlsCertificates: null,
|
||||
removeManager_title: 'Eemalda haldur',
|
||||
removeMember_title: 'Eemalda liige',
|
||||
role: 'Roll',
|
||||
@@ -281,6 +287,7 @@ export default {
|
||||
shared: 'Jagatud',
|
||||
sharedWithMe_title: 'Jagatud minuga',
|
||||
showOnFrontOfCard: 'Kuva kaardi ees',
|
||||
smtp: null,
|
||||
sortList_title: 'Nimekiri sorteerimine',
|
||||
stopwatch: 'Stopper',
|
||||
story: 'Kirjeldus',
|
||||
@@ -292,6 +299,7 @@ export default {
|
||||
taskList_title: 'Ülesanne nimekiri',
|
||||
team: 'Töögrupp',
|
||||
terms: null,
|
||||
testLog_title: null,
|
||||
thereIsNoPreviewAvailableForThisAttachment: 'Selle manusi eelvaadet pole saadaval.',
|
||||
time: 'Aeg',
|
||||
title: 'Pealkiri',
|
||||
@@ -305,6 +313,7 @@ export default {
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: 'Laaditud pildid',
|
||||
url: null,
|
||||
useSecureConnection: null,
|
||||
userActions_title: 'Kasutaja tegevused',
|
||||
userAddedCardToList: '<0>{{user}}</0> lisas <2>{{card}}</2> nimekirjaan {{list}}',
|
||||
userAddedThisCardToList: '<0>{{user}}</0> lisas selle kaardi nimekirjaan {{list}}',
|
||||
@@ -445,6 +454,7 @@ export default {
|
||||
restoreToList: 'Taasta nimekirja {{list}}',
|
||||
returnToBoard: 'Tagasi tahvlile',
|
||||
save: 'Salvesta',
|
||||
sendTestEmail: null,
|
||||
showActive: 'Näita aktiivseid',
|
||||
showAllAttachments: 'Näita kõiki manuseid ({{hidden}} peidetud)',
|
||||
showCardsWithThisUser: 'Näita selle kasutajaga kaarte',
|
||||
|
||||
@@ -99,6 +99,7 @@ export default {
|
||||
cardsOnThisListAreCompleteAndReadyToBeArchived: null,
|
||||
cardsOnThisListAreReadyToBeWorkedOn: null,
|
||||
clickHereOrRefreshPageToUpdate: null,
|
||||
clientHostnameInEhlo: null,
|
||||
closed: null,
|
||||
color: 'رنگ',
|
||||
comments: null,
|
||||
@@ -121,6 +122,7 @@ export default {
|
||||
date: 'تاریخ',
|
||||
deactivateUser_title: null,
|
||||
defaultCardType_title: null,
|
||||
defaultFrom: null,
|
||||
defaultView_title: null,
|
||||
deleteAllBoardsToBeAbleToDeleteThisProject: null,
|
||||
deleteAttachment_title: 'حذف پیوست',
|
||||
@@ -184,6 +186,7 @@ export default {
|
||||
grid: null,
|
||||
hideCompletedTasks: null,
|
||||
hideFromProjectListAndFavorites: null,
|
||||
host: null,
|
||||
hours: 'ساعتها',
|
||||
importBoard_title: 'وارد کردن برد',
|
||||
invalidCurrentPassword: 'رمز عبور فعلی نامعتبر است',
|
||||
@@ -227,8 +230,10 @@ export default {
|
||||
optional_inline: 'اختیاری',
|
||||
organization: 'سازمان',
|
||||
others: null,
|
||||
passwordIsSet: null,
|
||||
phone: 'تلفن',
|
||||
plankaUsesAppriseToSendNotificationsToOver100PopularServices: null,
|
||||
port: null,
|
||||
preferences: 'ترجیحات',
|
||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||
'نکته: با فشردن Ctrl-V (Cmd-V در مک) میتوانید پیوست را از کلیپ بورد اضافه کنید.',
|
||||
@@ -237,6 +242,7 @@ export default {
|
||||
projectNotFound_title: 'پروژه یافت نشد',
|
||||
projectOwner: null,
|
||||
referenceDataAndKnowledgeStorage: null,
|
||||
rejectUnauthorizedTlsCertificates: null,
|
||||
removeManager_title: 'حذف مدیر',
|
||||
removeMember_title: 'حذف عضو',
|
||||
role: null,
|
||||
@@ -263,6 +269,7 @@ export default {
|
||||
shared: null,
|
||||
sharedWithMe_title: null,
|
||||
showOnFrontOfCard: null,
|
||||
smtp: null,
|
||||
sortList_title: 'مرتبسازی لیست',
|
||||
stopwatch: 'کرنومتر',
|
||||
story: null,
|
||||
@@ -274,6 +281,7 @@ export default {
|
||||
taskList_title: null,
|
||||
team: null,
|
||||
terms: null,
|
||||
testLog_title: null,
|
||||
thereIsNoPreviewAvailableForThisAttachment: 'پیش نمایشی برای این پیوست موجود نیست.',
|
||||
time: 'زمان',
|
||||
title: 'عنوان',
|
||||
@@ -287,6 +295,7 @@ export default {
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: null,
|
||||
url: null,
|
||||
useSecureConnection: null,
|
||||
userActions_title: 'اقدامات کاربر',
|
||||
userAddedCardToList: null,
|
||||
userAddedThisCardToList: '<0>{{user}}</0> این کارت را به {{list}} اضافه کرد',
|
||||
@@ -424,6 +433,7 @@ export default {
|
||||
restoreToList: null,
|
||||
returnToBoard: null,
|
||||
save: 'ذخیره',
|
||||
sendTestEmail: null,
|
||||
showActive: null,
|
||||
showAllAttachments: 'نمایش همه پیوستها ({{hidden}} مخفی)',
|
||||
showCardsWithThisUser: null,
|
||||
|
||||
@@ -111,6 +111,7 @@ export default {
|
||||
'Tämän listan kortit ovat valmiita ja voidaan arkistoida.',
|
||||
cardsOnThisListAreReadyToBeWorkedOn: 'Tämän listan kortit ovat valmiita työstettäväksi.',
|
||||
clickHereOrRefreshPageToUpdate: '<0>Päivitä tästä</0> tai lataa sivu uudelleen.',
|
||||
clientHostnameInEhlo: null,
|
||||
closed: 'Suljettu',
|
||||
color: 'Väri',
|
||||
comments: 'Kommentit',
|
||||
@@ -133,6 +134,7 @@ export default {
|
||||
date: 'Päivämäärä',
|
||||
deactivateUser_title: 'Poista käyttäjä käytöstä',
|
||||
defaultCardType_title: 'Oletuskorttityyppi',
|
||||
defaultFrom: null,
|
||||
defaultView_title: 'Oletusnäkymä',
|
||||
deleteAllBoardsToBeAbleToDeleteThisProject:
|
||||
'Poista kaikki taulut, jotta voit poistaa tämän projektin',
|
||||
@@ -197,6 +199,7 @@ export default {
|
||||
grid: 'Ruudukko',
|
||||
hideCompletedTasks: null,
|
||||
hideFromProjectListAndFavorites: 'Piilota projektilistasta ja suosikeista',
|
||||
host: null,
|
||||
hours: 'Tunnit',
|
||||
importBoard_title: 'Tuo taulu',
|
||||
invalidCurrentPassword: 'Virheellinen nykyinen salasana',
|
||||
@@ -240,9 +243,11 @@ export default {
|
||||
optional_inline: 'valinnainen',
|
||||
organization: 'Organisaatio',
|
||||
others: 'Muut',
|
||||
passwordIsSet: null,
|
||||
phone: 'Puhelin',
|
||||
plankaUsesAppriseToSendNotificationsToOver100PopularServices:
|
||||
'PLANKA käyttää <1><0>Apprise</0></1> lähettääkseen ilmoituksia yli 100 suosittuun palveluun.',
|
||||
port: null,
|
||||
preferences: 'Asetukset',
|
||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||
'Vinkki: paina Ctrl-V (tai Cmd-V Macilla) lisätäksesi liitteen leikepöydältä.',
|
||||
@@ -251,6 +256,7 @@ export default {
|
||||
projectNotFound_title: 'Projektia ei löytynyt',
|
||||
projectOwner: 'Projektin omistaja',
|
||||
referenceDataAndKnowledgeStorage: 'Viitetiedot ja tietovarasto.',
|
||||
rejectUnauthorizedTlsCertificates: null,
|
||||
removeManager_title: 'Poista ylläpitäjä',
|
||||
removeMember_title: 'Poista jäsen',
|
||||
role: 'Rooli',
|
||||
@@ -277,6 +283,7 @@ export default {
|
||||
shared: 'Jaettu',
|
||||
sharedWithMe_title: 'Jaettu kanssani',
|
||||
showOnFrontOfCard: 'Näytä kortin etupuolella',
|
||||
smtp: null,
|
||||
sortList_title: 'Lajittele lista',
|
||||
stopwatch: 'Ajastin',
|
||||
story: 'Tarina',
|
||||
@@ -288,6 +295,7 @@ export default {
|
||||
taskList_title: 'Tehtävälista',
|
||||
team: 'Tiimi',
|
||||
terms: null,
|
||||
testLog_title: null,
|
||||
thereIsNoPreviewAvailableForThisAttachment: 'Tälle liitteelle ei ole esikatselua saatavilla.',
|
||||
time: 'Aika',
|
||||
title: 'Otsikko',
|
||||
@@ -301,6 +309,7 @@ export default {
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: 'Ladatut kuvat',
|
||||
url: null,
|
||||
useSecureConnection: null,
|
||||
userActions_title: 'Käyttäjän toiminnot',
|
||||
userAddedCardToList: '<0>{{user}}</0> lisäsi <2>{{card}}</2> listaan {{list}}',
|
||||
userAddedThisCardToList: '<0>{{user}}</0> lisäsi tämän kortin listaan {{list}}',
|
||||
@@ -445,6 +454,7 @@ export default {
|
||||
restoreToList: 'Palauta listaan {{list}}',
|
||||
returnToBoard: 'Palaa tauluun',
|
||||
save: 'Tallenna',
|
||||
sendTestEmail: null,
|
||||
showActive: 'Näytä aktiiviset',
|
||||
showAllAttachments: 'Näytä kaikki liitteet ({{hidden}} piilotettu)',
|
||||
showCardsWithThisUser: 'Näytä kortit, joissa tämä käyttäjä',
|
||||
|
||||
@@ -118,6 +118,7 @@ export default {
|
||||
cardsOnThisListAreReadyToBeWorkedOn: 'Les cartes de cette liste sont prêtes à être traitées.',
|
||||
clickHereOrRefreshPageToUpdate:
|
||||
'<0>Cliquez ici</0> ou rafraîchissez la page pour mettre à jour.',
|
||||
clientHostnameInEhlo: null,
|
||||
closed: 'Fermé',
|
||||
color: 'Couleur',
|
||||
comments: 'Commentaires',
|
||||
@@ -141,6 +142,7 @@ export default {
|
||||
date: 'Date',
|
||||
deactivateUser_title: 'Désactiver l’utilisateur',
|
||||
defaultCardType_title: 'Type de carte par défaut',
|
||||
defaultFrom: null,
|
||||
defaultView_title: 'Vue par défaut',
|
||||
deleteAllBoardsToBeAbleToDeleteThisProject:
|
||||
'Supprimer tous les tableaux pour pouvoir supprimer ce projet.',
|
||||
@@ -205,6 +207,7 @@ export default {
|
||||
grid: 'Grille',
|
||||
hideCompletedTasks: null,
|
||||
hideFromProjectListAndFavorites: 'Masquer de la liste des projets et des favoris',
|
||||
host: null,
|
||||
hours: 'Heures',
|
||||
importBoard_title: 'Importer un tableau',
|
||||
invalidCurrentPassword: 'Mot de passe actuel invalide',
|
||||
@@ -248,9 +251,11 @@ export default {
|
||||
optional_inline: 'optionnel',
|
||||
organization: 'Organisation',
|
||||
others: 'Autres',
|
||||
passwordIsSet: null,
|
||||
phone: 'Téléphone',
|
||||
plankaUsesAppriseToSendNotificationsToOver100PopularServices:
|
||||
'PLANKA utilise <1><0>Apprise</0></1> pour envoyer des notifications vers plus de 100 services populaires.',
|
||||
port: null,
|
||||
preferences: 'Préférences',
|
||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||
'Conseil: appuyer sur Ctrl-V (Cmd-V sur Mac) pour ajouter une pièce jointe depuis le presse-papiers',
|
||||
@@ -259,6 +264,7 @@ export default {
|
||||
projectNotFound_title: 'Projet introuvable',
|
||||
projectOwner: 'Propriétaire de projet',
|
||||
referenceDataAndKnowledgeStorage: 'Stockage de données de référence et de connaissances.',
|
||||
rejectUnauthorizedTlsCertificates: null,
|
||||
removeManager_title: 'Supprimer le responsable',
|
||||
removeMember_title: 'Supprimer le membre',
|
||||
role: 'Rôle',
|
||||
@@ -285,6 +291,7 @@ export default {
|
||||
shared: 'Partagé',
|
||||
sharedWithMe_title: 'Partagé avec moi',
|
||||
showOnFrontOfCard: 'Afficher sur le devant de la carte',
|
||||
smtp: null,
|
||||
sortList_title: 'Trier la liste',
|
||||
stopwatch: 'Minuteur',
|
||||
story: 'Story',
|
||||
@@ -296,6 +303,7 @@ export default {
|
||||
taskList_title: 'Liste de tâches',
|
||||
team: "Mes projets d'équipe",
|
||||
terms: null,
|
||||
testLog_title: null,
|
||||
thereIsNoPreviewAvailableForThisAttachment:
|
||||
"Il n'y a pas d'aperçu disponible pour cette pièce jointe.",
|
||||
time: 'Temps',
|
||||
@@ -310,6 +318,7 @@ export default {
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: 'Images téléchargées',
|
||||
url: 'URL',
|
||||
useSecureConnection: null,
|
||||
userActions_title: "Actions de l'utilisateur",
|
||||
userAddedCardToList: '<0>{{user}}</0> a ajouté <2>{{card}}</2> à {{list}}',
|
||||
userAddedThisCardToList: '<0>{{user}}</0> a ajouté cette carte à {{list}}',
|
||||
@@ -449,6 +458,7 @@ export default {
|
||||
restoreToList: 'Restauré dans {{list}}',
|
||||
returnToBoard: 'Retourner au tableau',
|
||||
save: 'Sauvegarder',
|
||||
sendTestEmail: null,
|
||||
showActive: 'Voir les actifs',
|
||||
showAllAttachments: 'Afficher toutes les pièces jointes ({{hidden}} masquées)',
|
||||
showCardsWithThisUser: 'Voir les cartes avec cet utilisateur',
|
||||
|
||||
@@ -108,6 +108,7 @@ export default {
|
||||
cardsOnThisListAreReadyToBeWorkedOn: 'A listán lévő kártyák készen állnak a munkára.',
|
||||
clickHereOrRefreshPageToUpdate:
|
||||
'<0>Kattintson ide</0> vagy frissítse az oldalt a frissítéshez.',
|
||||
clientHostnameInEhlo: null,
|
||||
closed: 'Lezárt',
|
||||
color: 'Szín',
|
||||
comments: 'Megjegyzések',
|
||||
@@ -130,6 +131,7 @@ export default {
|
||||
date: 'Dátum',
|
||||
deactivateUser_title: 'Felhasználó inaktiválása',
|
||||
defaultCardType_title: 'Alapértelmezett kártyatípus',
|
||||
defaultFrom: null,
|
||||
defaultView_title: 'Alapértelmezett nézet',
|
||||
deleteAllBoardsToBeAbleToDeleteThisProject:
|
||||
'A projekt törléséhez törölni kell az összes táblát.',
|
||||
@@ -194,6 +196,7 @@ export default {
|
||||
grid: 'Rács',
|
||||
hideCompletedTasks: 'Befejezett feladatok elrejtése',
|
||||
hideFromProjectListAndFavorites: 'Elrejtés a projektlistából és a kedvencekből',
|
||||
host: null,
|
||||
hours: 'Órák',
|
||||
importBoard_title: 'Tábla importálása',
|
||||
invalidCurrentPassword: 'Érvénytelen jelenlegi jelszó',
|
||||
@@ -238,9 +241,11 @@ export default {
|
||||
optional_inline: 'opcionális',
|
||||
organization: 'Szervezet',
|
||||
others: 'Egyebek',
|
||||
passwordIsSet: null,
|
||||
phone: 'Telefon',
|
||||
plankaUsesAppriseToSendNotificationsToOver100PopularServices:
|
||||
'A PLANKA az Apprise szolgáltatást használja több mint 100 népszerű szolgáltatás értesítéseinek küldésére.',
|
||||
port: null,
|
||||
preferences: 'Beállítások',
|
||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||
'Tipp: nyomja meg a Ctrl-V (Cmd-V a Mac-en) billentyűkombinációt a vágólapról történő melléklet hozzáadásához.',
|
||||
@@ -249,6 +254,7 @@ export default {
|
||||
projectNotFound_title: 'Projekt nem található',
|
||||
projectOwner: 'Projekt tulajdonos',
|
||||
referenceDataAndKnowledgeStorage: 'Referenciaadatok és tudástár.',
|
||||
rejectUnauthorizedTlsCertificates: null,
|
||||
removeManager_title: 'Menedzser eltávolítása',
|
||||
removeMember_title: 'Tag eltávolítása',
|
||||
role: 'Szerepkör',
|
||||
@@ -275,6 +281,7 @@ export default {
|
||||
shared: 'Megosztott',
|
||||
sharedWithMe_title: 'Velem megosztva',
|
||||
showOnFrontOfCard: 'Megjelenítés a kártya borítóján',
|
||||
smtp: null,
|
||||
sortList_title: 'Rendezés listában',
|
||||
stopwatch: 'Stopper',
|
||||
story: 'Story',
|
||||
@@ -286,6 +293,7 @@ export default {
|
||||
taskList_title: 'Feladatlista',
|
||||
team: 'Csapat',
|
||||
terms: 'Felhasználási feltételek',
|
||||
testLog_title: null,
|
||||
thereIsNoPreviewAvailableForThisAttachment: 'Nincs elérhető előnézet ehhez a melléklethez.',
|
||||
time: 'Idő',
|
||||
title: 'Cím',
|
||||
@@ -299,6 +307,7 @@ export default {
|
||||
uploadFailedNotEnoughStorageSpace: 'Feltöltési hiba: nincs elég szabad tárhely',
|
||||
uploadedImages: 'Feltöltött képek',
|
||||
url: 'URL',
|
||||
useSecureConnection: null,
|
||||
userActions_title: 'Felhasználói műveletek',
|
||||
userAddedCardToList:
|
||||
'<0>{{user}}</0> hozzáadta a(z) <2>{{card}}</2> kártyát ehhez a listához: {{list}}',
|
||||
@@ -446,6 +455,7 @@ export default {
|
||||
restoreToList: 'Visszaállítás ide: {{list}}',
|
||||
returnToBoard: 'Vissza a táblához',
|
||||
save: 'Mentés',
|
||||
sendTestEmail: null,
|
||||
showActive: 'Aktívak megjelenítése',
|
||||
showAllAttachments: 'Összes melléklet megjelenítése ({{hidden}} rejtve)',
|
||||
showCardsWithThisUser: 'Kártyák megjelenítése ezzel a felhasználóval',
|
||||
|
||||
@@ -101,6 +101,7 @@ export default {
|
||||
cardsOnThisListAreCompleteAndReadyToBeArchived: null,
|
||||
cardsOnThisListAreReadyToBeWorkedOn: null,
|
||||
clickHereOrRefreshPageToUpdate: null,
|
||||
clientHostnameInEhlo: null,
|
||||
closed: null,
|
||||
color: 'Warna',
|
||||
comments: null,
|
||||
@@ -123,6 +124,7 @@ export default {
|
||||
date: 'Tanggal',
|
||||
deactivateUser_title: null,
|
||||
defaultCardType_title: null,
|
||||
defaultFrom: null,
|
||||
defaultView_title: null,
|
||||
deleteAllBoardsToBeAbleToDeleteThisProject: null,
|
||||
deleteAttachment_title: 'Hapus Lampiran',
|
||||
@@ -186,6 +188,7 @@ export default {
|
||||
grid: null,
|
||||
hideCompletedTasks: null,
|
||||
hideFromProjectListAndFavorites: null,
|
||||
host: null,
|
||||
hours: 'Jam',
|
||||
importBoard_title: 'Impor Papan',
|
||||
invalidCurrentPassword: 'Kata sandi saat ini tidak valid',
|
||||
@@ -229,8 +232,10 @@ export default {
|
||||
optional_inline: 'opsional',
|
||||
organization: 'Organisasi',
|
||||
others: null,
|
||||
passwordIsSet: null,
|
||||
phone: 'Ponsel',
|
||||
plankaUsesAppriseToSendNotificationsToOver100PopularServices: null,
|
||||
port: null,
|
||||
preferences: 'Preferensi',
|
||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||
'Tip: tekan Ctrl-V (Cmd-V di Mac) untuk menambahkan lampiran dari papan klip.',
|
||||
@@ -239,6 +244,7 @@ export default {
|
||||
projectNotFound_title: 'Proyek Tidak Ditemukan',
|
||||
projectOwner: null,
|
||||
referenceDataAndKnowledgeStorage: null,
|
||||
rejectUnauthorizedTlsCertificates: null,
|
||||
removeManager_title: 'Hapus Manager',
|
||||
removeMember_title: 'Hapus Anggota',
|
||||
role: null,
|
||||
@@ -265,6 +271,7 @@ export default {
|
||||
shared: null,
|
||||
sharedWithMe_title: null,
|
||||
showOnFrontOfCard: null,
|
||||
smtp: null,
|
||||
sortList_title: null,
|
||||
stopwatch: 'Stopwatch',
|
||||
story: null,
|
||||
@@ -276,6 +283,7 @@ export default {
|
||||
taskList_title: null,
|
||||
team: null,
|
||||
terms: null,
|
||||
testLog_title: null,
|
||||
thereIsNoPreviewAvailableForThisAttachment:
|
||||
'Tidak ada pratinjau yang tersedia untuk lampiran ini.',
|
||||
time: 'Waktu',
|
||||
@@ -290,6 +298,7 @@ export default {
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: null,
|
||||
url: null,
|
||||
useSecureConnection: null,
|
||||
userActions_title: 'Aksi Pengguna',
|
||||
userAddedCardToList: null,
|
||||
userAddedThisCardToList: '<0>{{user}}</0> menambahkan kartu ini ke {{list}}',
|
||||
@@ -426,6 +435,7 @@ export default {
|
||||
restoreToList: null,
|
||||
returnToBoard: null,
|
||||
save: 'Simpan',
|
||||
sendTestEmail: null,
|
||||
showActive: null,
|
||||
showAllAttachments: 'Tampilkan semua lampiran ({{hidden}} tersembunyi)',
|
||||
showCardsWithThisUser: null,
|
||||
|
||||
@@ -116,6 +116,7 @@ export default {
|
||||
cardsOnThisListAreReadyToBeWorkedOn:
|
||||
'Le schede in questa lista sono pronte per essere lavorate.',
|
||||
clickHereOrRefreshPageToUpdate: '<0>Clicca qui</0> o ricarica la pagina per aggiornare.',
|
||||
clientHostnameInEhlo: null,
|
||||
closed: 'Chiuso',
|
||||
color: 'Colore',
|
||||
comments: 'Commenti',
|
||||
@@ -139,6 +140,7 @@ export default {
|
||||
date: 'Data',
|
||||
deactivateUser_title: 'Disattiva utente',
|
||||
defaultCardType_title: 'Tipo di scheda predefinito',
|
||||
defaultFrom: null,
|
||||
defaultView_title: 'Vista predefinita',
|
||||
deleteAllBoardsToBeAbleToDeleteThisProject:
|
||||
'Elimina tutte le bacheche per poter eliminare questo progetto.',
|
||||
@@ -203,6 +205,7 @@ export default {
|
||||
grid: 'Griglia',
|
||||
hideCompletedTasks: 'Nascondi task completate',
|
||||
hideFromProjectListAndFavorites: 'Nascondi dalla lista dei progetti e dai preferiti',
|
||||
host: null,
|
||||
hours: 'Ore',
|
||||
importBoard_title: 'Importa board',
|
||||
invalidCurrentPassword: 'Password corrente non valida',
|
||||
@@ -247,9 +250,11 @@ export default {
|
||||
optional_inline: 'opzionale',
|
||||
organization: 'Organizazzione',
|
||||
others: 'Altri',
|
||||
passwordIsSet: null,
|
||||
phone: 'Telefono',
|
||||
plankaUsesAppriseToSendNotificationsToOver100PopularServices:
|
||||
'PLANKA utilizza <1><0>Apprise</0></1> per inviare notifiche a oltre 100 servizi popolari.',
|
||||
port: null,
|
||||
preferences: 'Preferenze',
|
||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||
'Consiglio: premi Ctrl-V (Cmd-V on Mac) per aggiungere un allegato dalla clipboard.',
|
||||
@@ -258,6 +263,7 @@ export default {
|
||||
projectNotFound_title: 'Progetto non trovato',
|
||||
projectOwner: 'Proprietario del progetto',
|
||||
referenceDataAndKnowledgeStorage: 'Dati di riferimento e di archiviazione',
|
||||
rejectUnauthorizedTlsCertificates: null,
|
||||
removeManager_title: 'Rimuovi manager',
|
||||
removeMember_title: 'Rimuovi membro',
|
||||
role: 'Ruolo',
|
||||
@@ -284,6 +290,7 @@ export default {
|
||||
shared: 'Condiviso',
|
||||
sharedWithMe_title: 'Condiviso con me',
|
||||
showOnFrontOfCard: 'Mostra davanti alla scheda',
|
||||
smtp: null,
|
||||
sortList_title: 'Ordina',
|
||||
stopwatch: 'Timer',
|
||||
story: 'Storia',
|
||||
@@ -295,6 +302,7 @@ export default {
|
||||
taskList_title: 'Lista di task',
|
||||
team: 'Team',
|
||||
terms: 'Ho letto e accetto i termini e condizioni.',
|
||||
testLog_title: null,
|
||||
thereIsNoPreviewAvailableForThisAttachment:
|
||||
'Non è disponibile alcuna anteprima per questo allegato.',
|
||||
time: 'Tempo',
|
||||
@@ -310,6 +318,7 @@ export default {
|
||||
'Caricamento fallito: spazio di archiviazione insufficiente.',
|
||||
uploadedImages: 'Immagini caricate',
|
||||
url: 'URL',
|
||||
useSecureConnection: null,
|
||||
userActions_title: 'Azioni utente',
|
||||
userAddedCardToList: '<0>{{user}}</0> ha aggiunto <2>{{card}}</2> a {{list}}',
|
||||
userAddedThisCardToList: '<0>{{user}}</0> ha aggiunto questa task a {{list}}',
|
||||
@@ -450,6 +459,7 @@ export default {
|
||||
restoreToList: 'Ripristina a {{list}}',
|
||||
returnToBoard: 'Torna alla bacheca',
|
||||
save: 'Salva',
|
||||
sendTestEmail: null,
|
||||
showActive: 'Mostra attivi',
|
||||
showAllAttachments: 'Mostra tutti gli allegati ({{hidden}} nascosti)',
|
||||
showCardsWithThisUser: 'Mostra schede con questo utente',
|
||||
|
||||
@@ -101,6 +101,7 @@ export default {
|
||||
cardsOnThisListAreCompleteAndReadyToBeArchived: null,
|
||||
cardsOnThisListAreReadyToBeWorkedOn: null,
|
||||
clickHereOrRefreshPageToUpdate: null,
|
||||
clientHostnameInEhlo: null,
|
||||
closed: null,
|
||||
color: '色',
|
||||
comments: null,
|
||||
@@ -123,6 +124,7 @@ export default {
|
||||
date: '日付',
|
||||
deactivateUser_title: null,
|
||||
defaultCardType_title: null,
|
||||
defaultFrom: null,
|
||||
defaultView_title: null,
|
||||
deleteAllBoardsToBeAbleToDeleteThisProject: null,
|
||||
deleteAttachment_title: '添付ファイルを削除',
|
||||
@@ -186,6 +188,7 @@ export default {
|
||||
grid: null,
|
||||
hideCompletedTasks: null,
|
||||
hideFromProjectListAndFavorites: null,
|
||||
host: null,
|
||||
hours: '時間',
|
||||
importBoard_title: 'インポートボード',
|
||||
invalidCurrentPassword: '現在のパスワードが無効',
|
||||
@@ -229,8 +232,10 @@ export default {
|
||||
optional_inline: '任意',
|
||||
organization: '組織',
|
||||
others: null,
|
||||
passwordIsSet: null,
|
||||
phone: '電話番号',
|
||||
plankaUsesAppriseToSendNotificationsToOver100PopularServices: null,
|
||||
port: null,
|
||||
preferences: '環境設定',
|
||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||
'ヒント: Ctrl-V(MacではCmd-V)を押して、クリップボードから添付ファイルを追加します。',
|
||||
@@ -239,6 +244,7 @@ export default {
|
||||
projectNotFound_title: 'プロジェクトがありません',
|
||||
projectOwner: null,
|
||||
referenceDataAndKnowledgeStorage: null,
|
||||
rejectUnauthorizedTlsCertificates: null,
|
||||
removeManager_title: 'マネージャーを削除',
|
||||
removeMember_title: 'メンバーを削除',
|
||||
role: null,
|
||||
@@ -265,6 +271,7 @@ export default {
|
||||
shared: null,
|
||||
sharedWithMe_title: null,
|
||||
showOnFrontOfCard: null,
|
||||
smtp: null,
|
||||
sortList_title: null,
|
||||
stopwatch: 'タイマー',
|
||||
story: null,
|
||||
@@ -276,6 +283,7 @@ export default {
|
||||
taskList_title: null,
|
||||
team: null,
|
||||
terms: null,
|
||||
testLog_title: null,
|
||||
thereIsNoPreviewAvailableForThisAttachment: 'この添付ファイルにはプレビューがありません。',
|
||||
time: '時間',
|
||||
title: 'タイトル',
|
||||
@@ -289,6 +297,7 @@ export default {
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: null,
|
||||
url: null,
|
||||
useSecureConnection: null,
|
||||
userActions_title: 'ユーザーのアクション',
|
||||
userAddedCardToList: null,
|
||||
userAddedThisCardToList: '<0>{{user}}</0> 様が {{list}} をこのカードに追加しました',
|
||||
@@ -426,6 +435,7 @@ export default {
|
||||
restoreToList: null,
|
||||
returnToBoard: null,
|
||||
save: '保存',
|
||||
sendTestEmail: null,
|
||||
showActive: null,
|
||||
showAllAttachments: '全ての添付ファイルを表示する({{hidden}} 非表示)',
|
||||
showCardsWithThisUser: null,
|
||||
|
||||
@@ -99,6 +99,7 @@ export default {
|
||||
cardsOnThisListAreCompleteAndReadyToBeArchived: null,
|
||||
cardsOnThisListAreReadyToBeWorkedOn: null,
|
||||
clickHereOrRefreshPageToUpdate: null,
|
||||
clientHostnameInEhlo: null,
|
||||
closed: null,
|
||||
color: '색상',
|
||||
comments: null,
|
||||
@@ -121,6 +122,7 @@ export default {
|
||||
date: '날짜',
|
||||
deactivateUser_title: null,
|
||||
defaultCardType_title: null,
|
||||
defaultFrom: null,
|
||||
defaultView_title: null,
|
||||
deleteAllBoardsToBeAbleToDeleteThisProject: null,
|
||||
deleteAttachment_title: '첨부 파일 삭제',
|
||||
@@ -184,6 +186,7 @@ export default {
|
||||
grid: null,
|
||||
hideCompletedTasks: null,
|
||||
hideFromProjectListAndFavorites: null,
|
||||
host: null,
|
||||
hours: '시간',
|
||||
importBoard_title: '보드 가져오기',
|
||||
invalidCurrentPassword: '잘못된 현재 비밀번호',
|
||||
@@ -227,8 +230,10 @@ export default {
|
||||
optional_inline: '선택 사항',
|
||||
organization: '조직',
|
||||
others: null,
|
||||
passwordIsSet: null,
|
||||
phone: '전화',
|
||||
plankaUsesAppriseToSendNotificationsToOver100PopularServices: null,
|
||||
port: null,
|
||||
preferences: '환경 설정',
|
||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||
'팁: Ctrl-V (Mac에서는 Cmd-V)를 눌러 클립보드에서 첨부 파일을 추가하세요.',
|
||||
@@ -237,6 +242,7 @@ export default {
|
||||
projectNotFound_title: '프로젝트를 찾을 수 없음',
|
||||
projectOwner: null,
|
||||
referenceDataAndKnowledgeStorage: null,
|
||||
rejectUnauthorizedTlsCertificates: null,
|
||||
removeManager_title: '관리자 제거',
|
||||
removeMember_title: '멤버 제거',
|
||||
role: null,
|
||||
@@ -263,6 +269,7 @@ export default {
|
||||
shared: null,
|
||||
sharedWithMe_title: null,
|
||||
showOnFrontOfCard: null,
|
||||
smtp: null,
|
||||
sortList_title: '목록 정렬',
|
||||
stopwatch: '스톱워치',
|
||||
story: null,
|
||||
@@ -274,6 +281,7 @@ export default {
|
||||
taskList_title: null,
|
||||
team: null,
|
||||
terms: null,
|
||||
testLog_title: null,
|
||||
thereIsNoPreviewAvailableForThisAttachment:
|
||||
'이 첨부 파일에 대한 미리보기를 사용할 수 없습니다.',
|
||||
time: '시간',
|
||||
@@ -288,6 +296,7 @@ export default {
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: null,
|
||||
url: null,
|
||||
useSecureConnection: null,
|
||||
userActions_title: '사용자 작업',
|
||||
userAddedCardToList: null,
|
||||
userAddedThisCardToList: '<0>{{user}}</0>님이 이 카드를 {{list}}에 추가했습니다',
|
||||
@@ -425,6 +434,7 @@ export default {
|
||||
restoreToList: null,
|
||||
returnToBoard: null,
|
||||
save: '저장',
|
||||
sendTestEmail: null,
|
||||
showActive: null,
|
||||
showAllAttachments: '모든 첨부 파일 보기 ({{hidden}} 숨김)',
|
||||
showCardsWithThisUser: null,
|
||||
|
||||
@@ -101,6 +101,7 @@ export default {
|
||||
cardsOnThisListAreCompleteAndReadyToBeArchived: null,
|
||||
cardsOnThisListAreReadyToBeWorkedOn: null,
|
||||
clickHereOrRefreshPageToUpdate: null,
|
||||
clientHostnameInEhlo: null,
|
||||
closed: null,
|
||||
color: 'Kleur',
|
||||
comments: null,
|
||||
@@ -123,6 +124,7 @@ export default {
|
||||
date: 'Datum',
|
||||
deactivateUser_title: null,
|
||||
defaultCardType_title: null,
|
||||
defaultFrom: null,
|
||||
defaultView_title: null,
|
||||
deleteAllBoardsToBeAbleToDeleteThisProject: null,
|
||||
deleteAttachment_title: 'Bijlage verwijderen',
|
||||
@@ -186,6 +188,7 @@ export default {
|
||||
grid: null,
|
||||
hideCompletedTasks: null,
|
||||
hideFromProjectListAndFavorites: null,
|
||||
host: null,
|
||||
hours: 'Uren',
|
||||
importBoard_title: 'Bord importeren',
|
||||
invalidCurrentPassword: 'Ongeldig huidig wachtwoord',
|
||||
@@ -229,8 +232,10 @@ export default {
|
||||
optional_inline: 'optioneel',
|
||||
organization: 'Organisatie',
|
||||
others: null,
|
||||
passwordIsSet: null,
|
||||
phone: 'Telefoon',
|
||||
plankaUsesAppriseToSendNotificationsToOver100PopularServices: null,
|
||||
port: null,
|
||||
preferences: 'Voorkeuren',
|
||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||
'Tip: druk op Ctrl-V (Cmd-V op Mac) om een bijlage van het klembord toe te voegen.',
|
||||
@@ -239,6 +244,7 @@ export default {
|
||||
projectNotFound_title: 'Project niet gevonden',
|
||||
projectOwner: null,
|
||||
referenceDataAndKnowledgeStorage: null,
|
||||
rejectUnauthorizedTlsCertificates: null,
|
||||
removeManager_title: 'Manager verwijderen',
|
||||
removeMember_title: 'Lid verwijderen',
|
||||
role: null,
|
||||
@@ -265,6 +271,7 @@ export default {
|
||||
shared: null,
|
||||
sharedWithMe_title: null,
|
||||
showOnFrontOfCard: null,
|
||||
smtp: null,
|
||||
sortList_title: null,
|
||||
stopwatch: 'Stopwatch',
|
||||
story: null,
|
||||
@@ -276,6 +283,7 @@ export default {
|
||||
taskList_title: null,
|
||||
team: null,
|
||||
terms: null,
|
||||
testLog_title: null,
|
||||
thereIsNoPreviewAvailableForThisAttachment:
|
||||
'Er is geen voorbeeld beschikbaar voor deze bijlage.',
|
||||
time: 'Tijd',
|
||||
@@ -290,6 +298,7 @@ export default {
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: null,
|
||||
url: null,
|
||||
useSecureConnection: null,
|
||||
userActions_title: 'Gebruikersacties',
|
||||
userAddedCardToList: null,
|
||||
userAddedThisCardToList: '<0>{{user}}</0> heeft deze kaart toegevoegd aan {{list}}',
|
||||
@@ -427,6 +436,7 @@ export default {
|
||||
restoreToList: null,
|
||||
returnToBoard: null,
|
||||
save: 'Opslaan',
|
||||
sendTestEmail: null,
|
||||
showActive: null,
|
||||
showAllAttachments: 'Alle bijlagen weergeven ({{hidden}} verbergen)',
|
||||
showCardsWithThisUser: null,
|
||||
|
||||
@@ -108,6 +108,7 @@ export default {
|
||||
'Karty na tej liście są ukończone i gotowe do zarchiwizowania.',
|
||||
cardsOnThisListAreReadyToBeWorkedOn: 'Karty na tej liście są gotowe do pracy nad nimi.',
|
||||
clickHereOrRefreshPageToUpdate: '<0>Naciśnij tutaj</0> lub odśwież stronę, by zaktualizować.',
|
||||
clientHostnameInEhlo: null,
|
||||
closed: 'Zamknięte',
|
||||
color: 'Kolor',
|
||||
comments: 'Komentarze',
|
||||
@@ -130,6 +131,7 @@ export default {
|
||||
date: 'Data',
|
||||
deactivateUser_title: 'Dezaktywuj Użytkownika',
|
||||
defaultCardType_title: 'Domyślny Typ Karty',
|
||||
defaultFrom: null,
|
||||
defaultView_title: 'Domyślny Widok',
|
||||
deleteAllBoardsToBeAbleToDeleteThisProject: null,
|
||||
deleteAttachment_title: 'Usuń Załącznik',
|
||||
@@ -193,6 +195,7 @@ export default {
|
||||
grid: 'Siatka',
|
||||
hideCompletedTasks: null,
|
||||
hideFromProjectListAndFavorites: 'Ukryj z listy projektów i ulubionych',
|
||||
host: null,
|
||||
hours: 'Godzin',
|
||||
importBoard_title: 'Importuj Tablicę',
|
||||
invalidCurrentPassword: 'Błędne obecne hasło',
|
||||
@@ -236,9 +239,11 @@ export default {
|
||||
optional_inline: 'opcjonalny',
|
||||
organization: 'Organizacja',
|
||||
others: 'Inne',
|
||||
passwordIsSet: null,
|
||||
phone: 'Telefon',
|
||||
plankaUsesAppriseToSendNotificationsToOver100PopularServices:
|
||||
'PLANKA używa <1><0>Apprise</0></1> do wysyłania powiadomień do ponad 100 popularnych serwisów.',
|
||||
port: null,
|
||||
preferences: 'Preferencje',
|
||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||
'Podpowiedź: naciśnij Ctrl-V (Cmd-V na Macu) aby dodać załącznik ze schowka.',
|
||||
@@ -247,6 +252,7 @@ export default {
|
||||
projectNotFound_title: 'Projektu Nie Znaleziono',
|
||||
projectOwner: 'Właściciel projektu',
|
||||
referenceDataAndKnowledgeStorage: 'Odnoś się do danych i przechowuj wiedzę',
|
||||
rejectUnauthorizedTlsCertificates: null,
|
||||
removeManager_title: 'Usuń Zarządcę',
|
||||
removeMember_title: 'Usuń Członka',
|
||||
role: 'Rola',
|
||||
@@ -273,6 +279,7 @@ export default {
|
||||
shared: 'Udostępniane',
|
||||
sharedWithMe_title: 'Udostępniane Dla Mnie',
|
||||
showOnFrontOfCard: 'Pokazuj na przodzie karty',
|
||||
smtp: null,
|
||||
sortList_title: 'Sortowanie Listy',
|
||||
stopwatch: 'Stoper',
|
||||
story: 'Scenorys',
|
||||
@@ -284,6 +291,7 @@ export default {
|
||||
taskList_title: 'Lista Zadań',
|
||||
team: 'Zespół',
|
||||
terms: null,
|
||||
testLog_title: null,
|
||||
thereIsNoPreviewAvailableForThisAttachment: 'Brak podglądu dostępnego dla tego załącznika.',
|
||||
time: 'Czas',
|
||||
title: 'Tytuł',
|
||||
@@ -297,6 +305,7 @@ export default {
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: 'Wgrane obrazy',
|
||||
url: null,
|
||||
useSecureConnection: null,
|
||||
userActions_title: 'Akcje Użytkownika',
|
||||
userAddedCardToList: '<0>{{user}}</0> dodał <2>{{card}}</2> do {{list}}',
|
||||
userAddedThisCardToList: '<0>{{user}}</0> dodał tę kartę do {{list}}',
|
||||
@@ -437,6 +446,7 @@ export default {
|
||||
restoreToList: 'Przywróć na {{list}}',
|
||||
returnToBoard: 'Przywróć do tablicy',
|
||||
save: 'Zapisz',
|
||||
sendTestEmail: null,
|
||||
showActive: 'Pokaż aktywne',
|
||||
showAllAttachments: 'Pokaż wszystkie załączniki ({{hidden}} są ukryte)',
|
||||
showCardsWithThisUser: 'Pokaż karty z tym użytkownikiem',
|
||||
|
||||
@@ -116,6 +116,7 @@ export default {
|
||||
cardsOnThisListAreReadyToBeWorkedOn:
|
||||
'Os cartões nesta lista estão prontos para serem trabalhados.',
|
||||
clickHereOrRefreshPageToUpdate: '<0>Clique aqui</0> ou atualize a página para atualizar.',
|
||||
clientHostnameInEhlo: null,
|
||||
closed: 'Fechado',
|
||||
color: 'Cor',
|
||||
comments: 'Comentários',
|
||||
@@ -139,6 +140,7 @@ export default {
|
||||
date: 'Data',
|
||||
deactivateUser_title: 'Desativar Usuário',
|
||||
defaultCardType_title: 'Tipo de Cartão Padrão',
|
||||
defaultFrom: null,
|
||||
defaultView_title: 'Visualização Padrão',
|
||||
deleteAllBoardsToBeAbleToDeleteThisProject:
|
||||
'Excluir todos os quadros para poder excluir este projeto',
|
||||
@@ -203,6 +205,7 @@ export default {
|
||||
grid: 'Grade',
|
||||
hideCompletedTasks: 'Ocultar tarefas concluídas',
|
||||
hideFromProjectListAndFavorites: 'Ocultar da lista de projetos e favoritos',
|
||||
host: null,
|
||||
hours: 'Horas',
|
||||
importBoard_title: 'Importar Quadro',
|
||||
invalidCurrentPassword: 'Senha atual inválida',
|
||||
@@ -247,9 +250,11 @@ export default {
|
||||
optional_inline: 'opcional',
|
||||
organization: 'Organização',
|
||||
others: 'Outros',
|
||||
passwordIsSet: null,
|
||||
phone: 'Telefone',
|
||||
plankaUsesAppriseToSendNotificationsToOver100PopularServices:
|
||||
'PLANKA usa <1><0>Apprise</0></1> para enviar notificações para mais de 100 serviços populares.',
|
||||
port: null,
|
||||
preferences: 'Preferências',
|
||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||
'Dica: pressione Ctrl-V (Cmd-V no Mac) para adicionar um anexo da área de transferência.',
|
||||
@@ -258,6 +263,7 @@ export default {
|
||||
projectNotFound_title: 'Projeto não encontrado',
|
||||
projectOwner: 'Proprietário do projeto',
|
||||
referenceDataAndKnowledgeStorage: 'Armazenamento de dados de referência e conhecimento.',
|
||||
rejectUnauthorizedTlsCertificates: null,
|
||||
removeManager_title: 'Remover Gerente',
|
||||
removeMember_title: 'Remover Membro',
|
||||
role: 'Função',
|
||||
@@ -284,6 +290,7 @@ export default {
|
||||
shared: 'Compartilhado',
|
||||
sharedWithMe_title: 'Compartilhado Comigo',
|
||||
showOnFrontOfCard: 'Mostrar na frente do cartão',
|
||||
smtp: null,
|
||||
sortList_title: 'Ordenar Lista',
|
||||
stopwatch: 'Cronômetro',
|
||||
story: 'História',
|
||||
@@ -295,6 +302,7 @@ export default {
|
||||
taskList_title: 'Lista de Tarefas',
|
||||
team: 'Equipe',
|
||||
terms: null,
|
||||
testLog_title: null,
|
||||
thereIsNoPreviewAvailableForThisAttachment:
|
||||
'Não há pré-visualização disponível para este anexo.',
|
||||
time: 'Tempo',
|
||||
@@ -309,6 +317,7 @@ export default {
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: 'Imagens enviadas',
|
||||
url: 'URL',
|
||||
useSecureConnection: null,
|
||||
userActions_title: 'Ações do Usuário',
|
||||
userAddedCardToList: '<0>{{user}}</0> adicionou <2>{{card}}</2> à {{list}}',
|
||||
userAddedThisCardToList: '<0>{{user}}</0> adicionou este cartão a {{list}}',
|
||||
@@ -449,6 +458,7 @@ export default {
|
||||
restoreToList: 'Restaurar para {{list}}',
|
||||
returnToBoard: 'Voltar ao quadro',
|
||||
save: 'Salvar',
|
||||
sendTestEmail: null,
|
||||
showActive: 'Mostrar ativos',
|
||||
showAllAttachments: 'Mostrar todos os anexos ({{hidden}} ocultos)',
|
||||
showCardsWithThisUser: 'Mostrar cartões com este usuário',
|
||||
|
||||
@@ -102,6 +102,7 @@ export default {
|
||||
cardsOnThisListAreCompleteAndReadyToBeArchived: null,
|
||||
cardsOnThisListAreReadyToBeWorkedOn: null,
|
||||
clickHereOrRefreshPageToUpdate: null,
|
||||
clientHostnameInEhlo: null,
|
||||
closed: null,
|
||||
color: 'Cor',
|
||||
comments: null,
|
||||
@@ -124,6 +125,7 @@ export default {
|
||||
date: 'Data',
|
||||
deactivateUser_title: null,
|
||||
defaultCardType_title: null,
|
||||
defaultFrom: null,
|
||||
defaultView_title: null,
|
||||
deleteAllBoardsToBeAbleToDeleteThisProject: null,
|
||||
deleteAttachment_title: 'Eliminar Anexo',
|
||||
@@ -187,6 +189,7 @@ export default {
|
||||
grid: null,
|
||||
hideCompletedTasks: null,
|
||||
hideFromProjectListAndFavorites: null,
|
||||
host: null,
|
||||
hours: 'Horas',
|
||||
importBoard_title: 'Importar Quadro',
|
||||
invalidCurrentPassword: 'Palavra-passe atual inválida',
|
||||
@@ -230,8 +233,10 @@ export default {
|
||||
optional_inline: 'opcional',
|
||||
organization: 'Organização',
|
||||
others: null,
|
||||
passwordIsSet: null,
|
||||
phone: 'Telefone',
|
||||
plankaUsesAppriseToSendNotificationsToOver100PopularServices: null,
|
||||
port: null,
|
||||
preferences: 'Preferências',
|
||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||
'Dica: prima Ctrl-V (Cmd-V no Mac) para adicionar um anexo da área de transferência.',
|
||||
@@ -240,6 +245,7 @@ export default {
|
||||
projectNotFound_title: 'Projeto não encontrado',
|
||||
projectOwner: null,
|
||||
referenceDataAndKnowledgeStorage: null,
|
||||
rejectUnauthorizedTlsCertificates: null,
|
||||
removeManager_title: 'Remover Gestor',
|
||||
removeMember_title: 'Remover Membro',
|
||||
role: null,
|
||||
@@ -266,6 +272,7 @@ export default {
|
||||
shared: null,
|
||||
sharedWithMe_title: null,
|
||||
showOnFrontOfCard: null,
|
||||
smtp: null,
|
||||
sortList_title: null,
|
||||
stopwatch: 'Cronómetro',
|
||||
story: null,
|
||||
@@ -277,6 +284,7 @@ export default {
|
||||
taskList_title: null,
|
||||
team: null,
|
||||
terms: null,
|
||||
testLog_title: null,
|
||||
thereIsNoPreviewAvailableForThisAttachment:
|
||||
'Não há pré-visualização disponível para este anexo.',
|
||||
time: 'Tempo',
|
||||
@@ -291,6 +299,7 @@ export default {
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: null,
|
||||
url: null,
|
||||
useSecureConnection: null,
|
||||
userActions_title: 'Ações do Utilizador',
|
||||
userAddedCardToList: null,
|
||||
userAddedThisCardToList: '<0>{{user}}</0> adicionou este cartão à {{list}}',
|
||||
@@ -428,6 +437,7 @@ export default {
|
||||
restoreToList: null,
|
||||
returnToBoard: null,
|
||||
save: 'Guardar',
|
||||
sendTestEmail: null,
|
||||
showActive: null,
|
||||
showAllAttachments: 'Mostrar todos os anexos ({{hidden}} ocultos)',
|
||||
showCardsWithThisUser: null,
|
||||
|
||||
@@ -101,6 +101,7 @@ export default {
|
||||
cardsOnThisListAreCompleteAndReadyToBeArchived: null,
|
||||
cardsOnThisListAreReadyToBeWorkedOn: null,
|
||||
clickHereOrRefreshPageToUpdate: null,
|
||||
clientHostnameInEhlo: null,
|
||||
closed: null,
|
||||
color: 'Culoarea',
|
||||
comments: null,
|
||||
@@ -123,6 +124,7 @@ export default {
|
||||
date: 'Data',
|
||||
deactivateUser_title: null,
|
||||
defaultCardType_title: null,
|
||||
defaultFrom: null,
|
||||
defaultView_title: null,
|
||||
deleteAllBoardsToBeAbleToDeleteThisProject: null,
|
||||
deleteAttachment_title: 'Ștergeți atașamentul',
|
||||
@@ -186,6 +188,7 @@ export default {
|
||||
grid: null,
|
||||
hideCompletedTasks: null,
|
||||
hideFromProjectListAndFavorites: null,
|
||||
host: null,
|
||||
hours: 'Ore',
|
||||
importBoard_title: 'Import Tabla',
|
||||
invalidCurrentPassword: 'Parolă actuală nevalidă',
|
||||
@@ -229,8 +232,10 @@ export default {
|
||||
optional_inline: 'optional',
|
||||
organization: 'Organizatia',
|
||||
others: null,
|
||||
passwordIsSet: null,
|
||||
phone: 'Telefon',
|
||||
plankaUsesAppriseToSendNotificationsToOver100PopularServices: null,
|
||||
port: null,
|
||||
preferences: 'Preferințe',
|
||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||
'Sfat: apăsați Ctrl-V (Cmd-V pe Mac) pentru a adăuga un atașament din clipboard.',
|
||||
@@ -239,6 +244,7 @@ export default {
|
||||
projectNotFound_title: 'Proiectul nu a fost găsit',
|
||||
projectOwner: null,
|
||||
referenceDataAndKnowledgeStorage: null,
|
||||
rejectUnauthorizedTlsCertificates: null,
|
||||
removeManager_title: 'Eliminați Manager',
|
||||
removeMember_title: 'Eliminați membru',
|
||||
role: null,
|
||||
@@ -265,6 +271,7 @@ export default {
|
||||
shared: null,
|
||||
sharedWithMe_title: null,
|
||||
showOnFrontOfCard: null,
|
||||
smtp: null,
|
||||
sortList_title: null,
|
||||
stopwatch: 'Cronometru',
|
||||
story: null,
|
||||
@@ -276,6 +283,7 @@ export default {
|
||||
taskList_title: null,
|
||||
team: null,
|
||||
terms: null,
|
||||
testLog_title: null,
|
||||
thereIsNoPreviewAvailableForThisAttachment:
|
||||
'Nu există nicio previzualizare disponibilă pentru acest atașament.',
|
||||
time: 'Timp',
|
||||
@@ -290,6 +298,7 @@ export default {
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: null,
|
||||
url: null,
|
||||
useSecureConnection: null,
|
||||
userActions_title: 'Acțiunile utilizatorului',
|
||||
userAddedCardToList: null,
|
||||
userAddedThisCardToList: '<0>{{user}}</0> a adăugat acest card în {{list}}',
|
||||
@@ -427,6 +436,7 @@ export default {
|
||||
restoreToList: null,
|
||||
returnToBoard: null,
|
||||
save: 'Salveaza',
|
||||
sendTestEmail: null,
|
||||
showActive: null,
|
||||
showAllAttachments: 'Afișați toate atașamentele ({{hidden}} ascunse)',
|
||||
showCardsWithThisUser: null,
|
||||
|
||||
@@ -114,6 +114,7 @@ export default {
|
||||
'Карточки в этом списке завершены и готовы к архивированию.',
|
||||
cardsOnThisListAreReadyToBeWorkedOn: 'Карточки в этом списке готовы к работе.',
|
||||
clickHereOrRefreshPageToUpdate: '<0>Нажмите здесь</0> или обновите страницу для обновления.',
|
||||
clientHostnameInEhlo: null,
|
||||
closed: 'Закрыто',
|
||||
color: 'Цвет',
|
||||
comments: 'Комментарии',
|
||||
@@ -136,6 +137,7 @@ export default {
|
||||
date: 'Дата',
|
||||
deactivateUser_title: 'Деактивировать пользователя',
|
||||
defaultCardType_title: 'Тип карточки по умолчанию',
|
||||
defaultFrom: null,
|
||||
defaultView_title: 'Вид по умолчанию',
|
||||
deleteAllBoardsToBeAbleToDeleteThisProject:
|
||||
'Удалите все доски, чтобы иметь возможность удалить этот проект',
|
||||
@@ -200,6 +202,7 @@ export default {
|
||||
grid: 'Сетка',
|
||||
hideCompletedTasks: null,
|
||||
hideFromProjectListAndFavorites: 'Скрыть из списка проектов и избранного',
|
||||
host: null,
|
||||
hours: 'Часы',
|
||||
importBoard_title: 'Импорт доски',
|
||||
invalidCurrentPassword: 'Неверный текущий пароль',
|
||||
@@ -243,9 +246,11 @@ export default {
|
||||
optional_inline: 'необязательно',
|
||||
organization: 'Организация',
|
||||
others: 'Другие',
|
||||
passwordIsSet: null,
|
||||
phone: 'Телефон',
|
||||
plankaUsesAppriseToSendNotificationsToOver100PopularServices:
|
||||
'PLANKA использует <1><0>Apprise</0></1> для отправки уведомлений в более чем 100 популярных сервисов.',
|
||||
port: null,
|
||||
preferences: 'Предпочтения',
|
||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||
'Совет: нажмите Ctrl-V (Cmd-V на Mac), чтобы добавить вложение из буфера обмена.',
|
||||
@@ -254,6 +259,7 @@ export default {
|
||||
projectNotFound_title: 'Проект не найден',
|
||||
projectOwner: 'Владелец проекта',
|
||||
referenceDataAndKnowledgeStorage: 'Хранение справочных данных и знаний',
|
||||
rejectUnauthorizedTlsCertificates: null,
|
||||
removeManager_title: 'Удалить менеджера',
|
||||
removeMember_title: 'Удаление участника',
|
||||
role: 'Роль',
|
||||
@@ -280,6 +286,7 @@ export default {
|
||||
shared: 'Общий',
|
||||
sharedWithMe_title: 'Общий со мной',
|
||||
showOnFrontOfCard: 'Показать на лицевой стороне карточки',
|
||||
smtp: null,
|
||||
sortList_title: 'Сортировка списка',
|
||||
stopwatch: 'Секундомер',
|
||||
story: 'История',
|
||||
@@ -291,6 +298,7 @@ export default {
|
||||
taskList_title: 'Список задач',
|
||||
team: 'Команда',
|
||||
terms: null,
|
||||
testLog_title: null,
|
||||
thereIsNoPreviewAvailableForThisAttachment: 'Предпросмотр для этого вложения недоступен.',
|
||||
time: 'Время',
|
||||
title: 'Название',
|
||||
@@ -304,6 +312,7 @@ export default {
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: 'Загруженные изображения',
|
||||
url: null,
|
||||
useSecureConnection: null,
|
||||
userActions_title: 'Действия с пользователем',
|
||||
userAddedCardToList: null,
|
||||
userAddedThisCardToList: '<0>{{user}}</0> добавил(а) эту карточку в {{list}}',
|
||||
@@ -441,6 +450,7 @@ export default {
|
||||
restoreToList: 'Восстановить в {{list}}',
|
||||
returnToBoard: 'Вернуться на доску',
|
||||
save: 'Сохранить',
|
||||
sendTestEmail: null,
|
||||
showActive: 'Показать активные',
|
||||
showAllAttachments: 'Показать все вложения ({{hidden}} скрыто)',
|
||||
showCardsWithThisUser: 'Показать карточки с этим пользователем',
|
||||
|
||||
@@ -101,6 +101,7 @@ export default {
|
||||
cardsOnThisListAreCompleteAndReadyToBeArchived: null,
|
||||
cardsOnThisListAreReadyToBeWorkedOn: null,
|
||||
clickHereOrRefreshPageToUpdate: null,
|
||||
clientHostnameInEhlo: null,
|
||||
closed: null,
|
||||
color: 'Farba',
|
||||
comments: null,
|
||||
@@ -123,6 +124,7 @@ export default {
|
||||
date: 'Dátum',
|
||||
deactivateUser_title: null,
|
||||
defaultCardType_title: null,
|
||||
defaultFrom: null,
|
||||
defaultView_title: null,
|
||||
deleteAllBoardsToBeAbleToDeleteThisProject: null,
|
||||
deleteAttachment_title: 'Zmazať prílohu',
|
||||
@@ -186,6 +188,7 @@ export default {
|
||||
grid: null,
|
||||
hideCompletedTasks: null,
|
||||
hideFromProjectListAndFavorites: null,
|
||||
host: null,
|
||||
hours: 'Hodiny',
|
||||
importBoard_title: null,
|
||||
invalidCurrentPassword: 'Neplatné aktuálne heslo',
|
||||
@@ -229,8 +232,10 @@ export default {
|
||||
optional_inline: 'voliteľné',
|
||||
organization: 'Spoločnosť',
|
||||
others: null,
|
||||
passwordIsSet: null,
|
||||
phone: 'Telefón',
|
||||
plankaUsesAppriseToSendNotificationsToOver100PopularServices: null,
|
||||
port: null,
|
||||
preferences: 'Voľby',
|
||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||
'Tip: stlačte Ctrl-V (Cmd-V na Mac) pre vloženie prílohy zo schránky.',
|
||||
@@ -239,6 +244,7 @@ export default {
|
||||
projectNotFound_title: 'Projekt neexistuje',
|
||||
projectOwner: null,
|
||||
referenceDataAndKnowledgeStorage: null,
|
||||
rejectUnauthorizedTlsCertificates: null,
|
||||
removeManager_title: 'Odstrániť správcu',
|
||||
removeMember_title: 'Odstrániť člena',
|
||||
role: null,
|
||||
@@ -265,6 +271,7 @@ export default {
|
||||
shared: null,
|
||||
sharedWithMe_title: null,
|
||||
showOnFrontOfCard: null,
|
||||
smtp: null,
|
||||
sortList_title: null,
|
||||
stopwatch: 'Časovač',
|
||||
story: null,
|
||||
@@ -276,6 +283,7 @@ export default {
|
||||
taskList_title: null,
|
||||
team: null,
|
||||
terms: null,
|
||||
testLog_title: null,
|
||||
thereIsNoPreviewAvailableForThisAttachment: null,
|
||||
time: 'Čas',
|
||||
title: 'Názov',
|
||||
@@ -289,6 +297,7 @@ export default {
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: null,
|
||||
url: null,
|
||||
useSecureConnection: null,
|
||||
userActions_title: 'Akcie na používateľovi',
|
||||
userAddedCardToList: null,
|
||||
userAddedThisCardToList: '<0>{{user}}</0> pridal kartu do {{list}}',
|
||||
@@ -426,6 +435,7 @@ export default {
|
||||
restoreToList: null,
|
||||
returnToBoard: null,
|
||||
save: 'Uložiť',
|
||||
sendTestEmail: null,
|
||||
showActive: null,
|
||||
showAllAttachments: 'Zozbraziť všetky prílohy ({{hidden}} skryté)',
|
||||
showCardsWithThisUser: null,
|
||||
|
||||
@@ -101,6 +101,7 @@ export default {
|
||||
cardsOnThisListAreCompleteAndReadyToBeArchived: null,
|
||||
cardsOnThisListAreReadyToBeWorkedOn: null,
|
||||
clickHereOrRefreshPageToUpdate: null,
|
||||
clientHostnameInEhlo: null,
|
||||
closed: null,
|
||||
color: 'Боја',
|
||||
comments: null,
|
||||
@@ -123,6 +124,7 @@ export default {
|
||||
date: 'Датум',
|
||||
deactivateUser_title: null,
|
||||
defaultCardType_title: null,
|
||||
defaultFrom: null,
|
||||
defaultView_title: null,
|
||||
deleteAllBoardsToBeAbleToDeleteThisProject: null,
|
||||
deleteAttachment_title: 'Обриши прилог',
|
||||
@@ -186,6 +188,7 @@ export default {
|
||||
grid: null,
|
||||
hideCompletedTasks: null,
|
||||
hideFromProjectListAndFavorites: null,
|
||||
host: null,
|
||||
hours: 'Сати',
|
||||
importBoard_title: 'Увези таблу',
|
||||
invalidCurrentPassword: 'Неисправна тренутна лозинка',
|
||||
@@ -229,8 +232,10 @@ export default {
|
||||
optional_inline: 'опционо',
|
||||
organization: 'Организација',
|
||||
others: null,
|
||||
passwordIsSet: null,
|
||||
phone: 'Телефон',
|
||||
plankaUsesAppriseToSendNotificationsToOver100PopularServices: null,
|
||||
port: null,
|
||||
preferences: 'Својства',
|
||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||
'Савет: притисни Ctrl-V (Cmd-V на Меку) да би додао прилог са бележнице.',
|
||||
@@ -239,6 +244,7 @@ export default {
|
||||
projectNotFound_title: 'Пројекат није пронађен',
|
||||
projectOwner: null,
|
||||
referenceDataAndKnowledgeStorage: null,
|
||||
rejectUnauthorizedTlsCertificates: null,
|
||||
removeManager_title: 'Уклони руководиоца',
|
||||
removeMember_title: 'Уклони члана',
|
||||
role: null,
|
||||
@@ -265,6 +271,7 @@ export default {
|
||||
shared: null,
|
||||
sharedWithMe_title: null,
|
||||
showOnFrontOfCard: null,
|
||||
smtp: null,
|
||||
sortList_title: 'Сложи списак',
|
||||
stopwatch: 'Штоперица',
|
||||
story: null,
|
||||
@@ -276,6 +283,7 @@ export default {
|
||||
taskList_title: null,
|
||||
team: null,
|
||||
terms: null,
|
||||
testLog_title: null,
|
||||
thereIsNoPreviewAvailableForThisAttachment: 'Нема прегледа доступног за овај прилог.',
|
||||
time: 'Време',
|
||||
title: 'Наслов',
|
||||
@@ -289,6 +297,7 @@ export default {
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: null,
|
||||
url: null,
|
||||
useSecureConnection: null,
|
||||
userActions_title: 'Корисничке радње',
|
||||
userAddedCardToList: null,
|
||||
userAddedThisCardToList: '<0>{{user}}</0> је додао ову картицу на {{list}}',
|
||||
@@ -426,6 +435,7 @@ export default {
|
||||
restoreToList: null,
|
||||
returnToBoard: null,
|
||||
save: 'Сачувај',
|
||||
sendTestEmail: null,
|
||||
showActive: null,
|
||||
showAllAttachments: 'Прикажи све ({{hidden}} сакривене прилоге)',
|
||||
showCardsWithThisUser: null,
|
||||
|
||||
@@ -98,6 +98,7 @@ export default {
|
||||
cardsOnThisListAreCompleteAndReadyToBeArchived: null,
|
||||
cardsOnThisListAreReadyToBeWorkedOn: null,
|
||||
clickHereOrRefreshPageToUpdate: null,
|
||||
clientHostnameInEhlo: null,
|
||||
closed: null,
|
||||
color: 'Boja',
|
||||
comments: null,
|
||||
@@ -120,6 +121,7 @@ export default {
|
||||
date: 'Datum',
|
||||
deactivateUser_title: null,
|
||||
defaultCardType_title: null,
|
||||
defaultFrom: null,
|
||||
defaultView_title: null,
|
||||
deleteAllBoardsToBeAbleToDeleteThisProject: null,
|
||||
deleteAttachment_title: 'Obriši prilog',
|
||||
@@ -183,6 +185,7 @@ export default {
|
||||
grid: null,
|
||||
hideCompletedTasks: null,
|
||||
hideFromProjectListAndFavorites: null,
|
||||
host: null,
|
||||
hours: 'Sati',
|
||||
importBoard_title: 'Uvezi tablu',
|
||||
invalidCurrentPassword: 'Neispravna trenutna lozinka',
|
||||
@@ -226,8 +229,10 @@ export default {
|
||||
optional_inline: 'opciono',
|
||||
organization: 'Organizacija',
|
||||
others: null,
|
||||
passwordIsSet: null,
|
||||
phone: 'Telefon',
|
||||
plankaUsesAppriseToSendNotificationsToOver100PopularServices: null,
|
||||
port: null,
|
||||
preferences: 'Svojstva',
|
||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||
'Savet: pritisni Ctrl-V (Cmd-V na Meku) da bi dodao prilog sa beležnice.',
|
||||
@@ -236,6 +241,7 @@ export default {
|
||||
projectNotFound_title: 'Projekat nije pronađen',
|
||||
projectOwner: null,
|
||||
referenceDataAndKnowledgeStorage: null,
|
||||
rejectUnauthorizedTlsCertificates: null,
|
||||
removeManager_title: 'Ukloni rukovodioca',
|
||||
removeMember_title: 'Ukloni člana',
|
||||
role: null,
|
||||
@@ -262,6 +268,7 @@ export default {
|
||||
shared: null,
|
||||
sharedWithMe_title: null,
|
||||
showOnFrontOfCard: null,
|
||||
smtp: null,
|
||||
sortList_title: 'Složi spisak',
|
||||
stopwatch: 'Štoperica',
|
||||
story: null,
|
||||
@@ -273,6 +280,7 @@ export default {
|
||||
taskList_title: null,
|
||||
team: null,
|
||||
terms: null,
|
||||
testLog_title: null,
|
||||
thereIsNoPreviewAvailableForThisAttachment: 'Nema pregleda dostupnog za ovaj prilog.',
|
||||
time: 'Vreme',
|
||||
title: 'Naslov',
|
||||
@@ -286,6 +294,7 @@ export default {
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: null,
|
||||
url: null,
|
||||
useSecureConnection: null,
|
||||
userActions_title: 'Korisničke radnje',
|
||||
userAddedCardToList: null,
|
||||
userAddedThisCardToList: '<0>{{user}}</0> je dodao ovu karticu na {{list}}',
|
||||
@@ -423,6 +432,7 @@ export default {
|
||||
restoreToList: null,
|
||||
returnToBoard: null,
|
||||
save: 'Sačuvaj',
|
||||
sendTestEmail: null,
|
||||
showActive: null,
|
||||
showAllAttachments: 'Prikaži sve ({{hidden}} sakrivene priloge)',
|
||||
showCardsWithThisUser: null,
|
||||
|
||||
@@ -100,6 +100,7 @@ export default {
|
||||
cardsOnThisListAreCompleteAndReadyToBeArchived: null,
|
||||
cardsOnThisListAreReadyToBeWorkedOn: null,
|
||||
clickHereOrRefreshPageToUpdate: null,
|
||||
clientHostnameInEhlo: null,
|
||||
closed: null,
|
||||
color: 'Färg',
|
||||
comments: null,
|
||||
@@ -122,6 +123,7 @@ export default {
|
||||
date: 'Datum',
|
||||
deactivateUser_title: null,
|
||||
defaultCardType_title: null,
|
||||
defaultFrom: null,
|
||||
defaultView_title: null,
|
||||
deleteAllBoardsToBeAbleToDeleteThisProject: null,
|
||||
deleteAttachment_title: 'Ta bort bilaga',
|
||||
@@ -185,6 +187,7 @@ export default {
|
||||
grid: null,
|
||||
hideCompletedTasks: null,
|
||||
hideFromProjectListAndFavorites: null,
|
||||
host: null,
|
||||
hours: 'Timmar',
|
||||
importBoard_title: null,
|
||||
invalidCurrentPassword: 'Ogiltigt nuvarande lösenord',
|
||||
@@ -228,8 +231,10 @@ export default {
|
||||
optional_inline: 'valfri',
|
||||
organization: 'Organisation',
|
||||
others: null,
|
||||
passwordIsSet: null,
|
||||
phone: 'Telefon',
|
||||
plankaUsesAppriseToSendNotificationsToOver100PopularServices: null,
|
||||
port: null,
|
||||
preferences: 'Preferenser',
|
||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||
'Tips: tryck på Ctrl-V (Cmd-V på Mac) för att lägga till en bilaga från urklipp.',
|
||||
@@ -238,6 +243,7 @@ export default {
|
||||
projectNotFound_title: 'Projekt hittades inte',
|
||||
projectOwner: null,
|
||||
referenceDataAndKnowledgeStorage: null,
|
||||
rejectUnauthorizedTlsCertificates: null,
|
||||
removeManager_title: 'Ta bort projektledare',
|
||||
removeMember_title: 'Ta bort medlem',
|
||||
role: null,
|
||||
@@ -264,6 +270,7 @@ export default {
|
||||
shared: null,
|
||||
sharedWithMe_title: null,
|
||||
showOnFrontOfCard: null,
|
||||
smtp: null,
|
||||
sortList_title: null,
|
||||
stopwatch: 'Timer',
|
||||
story: null,
|
||||
@@ -275,6 +282,7 @@ export default {
|
||||
taskList_title: null,
|
||||
team: null,
|
||||
terms: null,
|
||||
testLog_title: null,
|
||||
thereIsNoPreviewAvailableForThisAttachment: null,
|
||||
time: 'Tid',
|
||||
title: 'Titel',
|
||||
@@ -288,6 +296,7 @@ export default {
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: null,
|
||||
url: null,
|
||||
useSecureConnection: null,
|
||||
userActions_title: 'Användaråtgärder',
|
||||
userAddedCardToList: null,
|
||||
userAddedThisCardToList: '<0>{{user}}</0> lade till detta kort i {{list}}',
|
||||
@@ -425,6 +434,7 @@ export default {
|
||||
restoreToList: null,
|
||||
returnToBoard: null,
|
||||
save: 'Spara',
|
||||
sendTestEmail: null,
|
||||
showActive: null,
|
||||
showAllAttachments: 'Visa alla bilagor ({{hidden}} dolda)',
|
||||
showCardsWithThisUser: null,
|
||||
|
||||
@@ -98,6 +98,7 @@ export default {
|
||||
cardsOnThisListAreCompleteAndReadyToBeArchived: null,
|
||||
cardsOnThisListAreReadyToBeWorkedOn: null,
|
||||
clickHereOrRefreshPageToUpdate: null,
|
||||
clientHostnameInEhlo: null,
|
||||
closed: null,
|
||||
color: 'renk',
|
||||
comments: null,
|
||||
@@ -120,6 +121,7 @@ export default {
|
||||
date: 'tarih',
|
||||
deactivateUser_title: null,
|
||||
defaultCardType_title: null,
|
||||
defaultFrom: null,
|
||||
defaultView_title: null,
|
||||
deleteAllBoardsToBeAbleToDeleteThisProject: null,
|
||||
deleteAttachment_title: 'Eki Sil',
|
||||
@@ -183,6 +185,7 @@ export default {
|
||||
grid: null,
|
||||
hideCompletedTasks: null,
|
||||
hideFromProjectListAndFavorites: null,
|
||||
host: null,
|
||||
hours: 'saat',
|
||||
importBoard_title: null,
|
||||
invalidCurrentPassword: 'Mevcut şifre yanlış',
|
||||
@@ -226,8 +229,10 @@ export default {
|
||||
optional_inline: 'İsteğe bağlı',
|
||||
organization: 'Organizasyon',
|
||||
others: null,
|
||||
passwordIsSet: null,
|
||||
phone: 'telefon',
|
||||
plankaUsesAppriseToSendNotificationsToOver100PopularServices: null,
|
||||
port: null,
|
||||
preferences: 'Tercihler',
|
||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||
'İpucu: Panodan bir ek eklemek için CTRL-V ye (Macte Cmd-V) basın.',
|
||||
@@ -236,6 +241,7 @@ export default {
|
||||
projectNotFound_title: 'Proje bulunamadı',
|
||||
projectOwner: null,
|
||||
referenceDataAndKnowledgeStorage: null,
|
||||
rejectUnauthorizedTlsCertificates: null,
|
||||
removeManager_title: 'Yöneticiyi Kaldır',
|
||||
removeMember_title: 'Üyeyi Kaldır',
|
||||
role: null,
|
||||
@@ -262,6 +268,7 @@ export default {
|
||||
shared: null,
|
||||
sharedWithMe_title: null,
|
||||
showOnFrontOfCard: null,
|
||||
smtp: null,
|
||||
sortList_title: null,
|
||||
stopwatch: 'kronometre',
|
||||
story: null,
|
||||
@@ -273,6 +280,7 @@ export default {
|
||||
taskList_title: null,
|
||||
team: null,
|
||||
terms: null,
|
||||
testLog_title: null,
|
||||
thereIsNoPreviewAvailableForThisAttachment: 'Bu ek için önizleme mevcut değil.',
|
||||
time: 'zaman',
|
||||
title: 'başlık',
|
||||
@@ -286,6 +294,7 @@ export default {
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: null,
|
||||
url: null,
|
||||
useSecureConnection: null,
|
||||
userActions_title: 'Kullanıcı İşlemleri',
|
||||
userAddedCardToList: null,
|
||||
userAddedThisCardToList: '<0>{{user}}</0> bu kartı {{list}} listesine ekledi',
|
||||
@@ -423,6 +432,7 @@ export default {
|
||||
restoreToList: null,
|
||||
returnToBoard: null,
|
||||
save: 'Kaydet',
|
||||
sendTestEmail: null,
|
||||
showActive: null,
|
||||
showAllAttachments: 'Tüm ekleri göster ({{hidden}} gizli)',
|
||||
showCardsWithThisUser: null,
|
||||
|
||||
@@ -111,6 +111,7 @@ export default {
|
||||
'Картки з цього списку завершені і готові до архівування.',
|
||||
cardsOnThisListAreReadyToBeWorkedOn: 'Картки з цього списку готові до роботи.',
|
||||
clickHereOrRefreshPageToUpdate: '<0>Натисніть тут</0> або оновіть сторінку для оновлення.',
|
||||
clientHostnameInEhlo: null,
|
||||
closed: 'Закрито',
|
||||
color: 'Колір',
|
||||
comments: 'Коментарі',
|
||||
@@ -134,6 +135,7 @@ export default {
|
||||
date: 'Дата',
|
||||
deactivateUser_title: 'Деактивувати користувача',
|
||||
defaultCardType_title: 'Тип картки за замовчуванням',
|
||||
defaultFrom: null,
|
||||
defaultView_title: 'Вигляд за замовчуванням',
|
||||
deleteAllBoardsToBeAbleToDeleteThisProject:
|
||||
'Видаліть усі дошки, щоб мати змогу видалити цей проект',
|
||||
@@ -198,6 +200,7 @@ export default {
|
||||
grid: 'Сітка',
|
||||
hideCompletedTasks: 'Приховати виконані завдання',
|
||||
hideFromProjectListAndFavorites: 'Приховати зі списку проектів та обраного',
|
||||
host: null,
|
||||
hours: 'Години',
|
||||
importBoard_title: 'Імпортувати Дошку',
|
||||
invalidCurrentPassword: 'Невірний поточний пароль',
|
||||
@@ -242,9 +245,11 @@ export default {
|
||||
optional_inline: 'опціонально',
|
||||
organization: 'Організація',
|
||||
others: 'Інші',
|
||||
passwordIsSet: null,
|
||||
phone: 'Телефон',
|
||||
plankaUsesAppriseToSendNotificationsToOver100PopularServices:
|
||||
'PLANKA використовує <1><0>Apprise</0></1> для надсилання сповіщень на понад 100 популярних сервісів.',
|
||||
port: null,
|
||||
preferences: 'Уподобання',
|
||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||
'Порада: натисніть Ctrl-V (⌘V на Mac), щоб додати вкладення з буфера обміну.',
|
||||
@@ -253,6 +258,7 @@ export default {
|
||||
projectNotFound_title: 'Проект не знайдено',
|
||||
projectOwner: 'Власник проекту',
|
||||
referenceDataAndKnowledgeStorage: 'Довідкові дані та сховище знань.',
|
||||
rejectUnauthorizedTlsCertificates: null,
|
||||
removeManager_title: 'Видалити Менеджера',
|
||||
removeMember_title: 'Видалити Учасника',
|
||||
role: 'Роль',
|
||||
@@ -279,6 +285,7 @@ export default {
|
||||
shared: 'Спільне',
|
||||
sharedWithMe_title: 'Поділіться зі мною',
|
||||
showOnFrontOfCard: 'Показати на лицьовій стороні картки',
|
||||
smtp: null,
|
||||
sortList_title: 'Сортування списку',
|
||||
stopwatch: 'Секундомір',
|
||||
story: 'Історія',
|
||||
@@ -290,6 +297,7 @@ export default {
|
||||
taskList_title: 'Список завдань',
|
||||
team: 'Команда',
|
||||
terms: null,
|
||||
testLog_title: null,
|
||||
thereIsNoPreviewAvailableForThisAttachment: 'Для цього вкладення немає доступного перегляду.',
|
||||
time: 'Час',
|
||||
title: 'Назва',
|
||||
@@ -303,6 +311,7 @@ export default {
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: 'Завантажені зображення',
|
||||
url: 'Посилання',
|
||||
useSecureConnection: null,
|
||||
userActions_title: 'Дії користувача',
|
||||
userAddedCardToList: '<0>{{user}}</0> додав(ла) <2>{{card}}</2> до {{list}}',
|
||||
userAddedThisCardToList: '<0>{{user}}</0> додав(ла) цю картку до {{list}}',
|
||||
@@ -443,6 +452,7 @@ export default {
|
||||
restoreToList: 'Відновити до {{list}}',
|
||||
returnToBoard: 'Повернутися до дошки',
|
||||
save: 'Зберегти',
|
||||
sendTestEmail: null,
|
||||
showActive: 'Показати активний',
|
||||
showAllAttachments: 'Показати всі вкладення ({{hidden}} приховані)',
|
||||
showCardsWithThisUser: 'Показати картки з цим користувачем',
|
||||
|
||||
@@ -97,6 +97,7 @@ export default {
|
||||
cardsOnThisListAreCompleteAndReadyToBeArchived: null,
|
||||
cardsOnThisListAreReadyToBeWorkedOn: null,
|
||||
clickHereOrRefreshPageToUpdate: null,
|
||||
clientHostnameInEhlo: null,
|
||||
closed: null,
|
||||
color: 'Rang',
|
||||
comments: null,
|
||||
@@ -119,6 +120,7 @@ export default {
|
||||
date: 'Sana',
|
||||
deactivateUser_title: null,
|
||||
defaultCardType_title: null,
|
||||
defaultFrom: null,
|
||||
defaultView_title: null,
|
||||
deleteAllBoardsToBeAbleToDeleteThisProject: null,
|
||||
deleteAttachment_title: "Ilovani O'chirish",
|
||||
@@ -182,6 +184,7 @@ export default {
|
||||
grid: null,
|
||||
hideCompletedTasks: null,
|
||||
hideFromProjectListAndFavorites: null,
|
||||
host: null,
|
||||
hours: 'Soat',
|
||||
importBoard_title: null,
|
||||
invalidCurrentPassword: 'Hozirgi parol xato',
|
||||
@@ -225,8 +228,10 @@ export default {
|
||||
optional_inline: 'ixtiyoriy',
|
||||
organization: 'Tashkilot',
|
||||
others: null,
|
||||
passwordIsSet: null,
|
||||
phone: 'Telefon',
|
||||
plankaUsesAppriseToSendNotificationsToOver100PopularServices: null,
|
||||
port: null,
|
||||
preferences: 'Afzalliklar',
|
||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||
"Tip: Buferdan ilova qo'shish uchun Ctrl-V (Mac da Cmd-V) ni bosing.",
|
||||
@@ -235,6 +240,7 @@ export default {
|
||||
projectNotFound_title: 'Loyiha Topilmadi',
|
||||
projectOwner: null,
|
||||
referenceDataAndKnowledgeStorage: null,
|
||||
rejectUnauthorizedTlsCertificates: null,
|
||||
removeManager_title: "Boshqaruvchini O'chirish",
|
||||
removeMember_title: "A'zoni O'chirish",
|
||||
role: null,
|
||||
@@ -261,6 +267,7 @@ export default {
|
||||
shared: null,
|
||||
sharedWithMe_title: null,
|
||||
showOnFrontOfCard: null,
|
||||
smtp: null,
|
||||
sortList_title: null,
|
||||
stopwatch: 'Taymer',
|
||||
story: null,
|
||||
@@ -272,6 +279,7 @@ export default {
|
||||
taskList_title: null,
|
||||
team: null,
|
||||
terms: null,
|
||||
testLog_title: null,
|
||||
thereIsNoPreviewAvailableForThisAttachment: null,
|
||||
time: 'Vaqt',
|
||||
title: 'Sarlavha',
|
||||
@@ -285,6 +293,7 @@ export default {
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: null,
|
||||
url: null,
|
||||
useSecureConnection: null,
|
||||
userActions_title: 'Foydalanuvchi Amallari',
|
||||
userAddedCardToList: null,
|
||||
userAddedThisCardToList: "Ushbu kartani {{list}} ga<0>{{user}}</0> qo'shdi",
|
||||
@@ -422,6 +431,7 @@ export default {
|
||||
restoreToList: null,
|
||||
returnToBoard: null,
|
||||
save: 'Saqlash',
|
||||
sendTestEmail: null,
|
||||
showActive: null,
|
||||
showAllAttachments: "Barcha ilovalarni ko'rsatish ({{hidden}} yashirilgan)",
|
||||
showCardsWithThisUser: null,
|
||||
|
||||
@@ -98,6 +98,7 @@ export default {
|
||||
cardsOnThisListAreCompleteAndReadyToBeArchived: '此列表中的卡片已完成并准备归档',
|
||||
cardsOnThisListAreReadyToBeWorkedOn: '此列表中的卡片已准备就绪可开始工作',
|
||||
clickHereOrRefreshPageToUpdate: '<0>点击此处</0>或刷新页面更新',
|
||||
clientHostnameInEhlo: null,
|
||||
closed: '已关闭',
|
||||
color: '颜色',
|
||||
comments: '评论',
|
||||
@@ -120,6 +121,7 @@ export default {
|
||||
date: '日期',
|
||||
deactivateUser_title: '停用用户',
|
||||
defaultCardType_title: '默认卡片类型',
|
||||
defaultFrom: null,
|
||||
defaultView_title: '默认视图',
|
||||
deleteAllBoardsToBeAbleToDeleteThisProject: '删除所有面板后方可删除此项目',
|
||||
deleteAttachment_title: '删除附件',
|
||||
@@ -183,6 +185,7 @@ export default {
|
||||
grid: '网格',
|
||||
hideCompletedTasks: null,
|
||||
hideFromProjectListAndFavorites: '从项目列表和收藏中隐藏',
|
||||
host: null,
|
||||
hours: '小时',
|
||||
importBoard_title: '导入面板',
|
||||
invalidCurrentPassword: '当前密码错误',
|
||||
@@ -226,9 +229,11 @@ export default {
|
||||
optional_inline: '可选的',
|
||||
organization: '组织机构',
|
||||
others: '其他',
|
||||
passwordIsSet: null,
|
||||
phone: '电话',
|
||||
plankaUsesAppriseToSendNotificationsToOver100PopularServices:
|
||||
'PLANKA使用<1><0>Apprise</0></1>向100多个流行服务发送通知',
|
||||
port: null,
|
||||
preferences: '偏好',
|
||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||
'提示: 按下 Ctrl-V (Mac: Cmd-V) 从剪切板添加附件',
|
||||
@@ -237,6 +242,7 @@ export default {
|
||||
projectNotFound_title: '项目未找到',
|
||||
projectOwner: '项目所有者',
|
||||
referenceDataAndKnowledgeStorage: '参考数据和知识存储',
|
||||
rejectUnauthorizedTlsCertificates: null,
|
||||
removeManager_title: '删除管理员',
|
||||
removeMember_title: '删除成员',
|
||||
role: '角色',
|
||||
@@ -263,6 +269,7 @@ export default {
|
||||
shared: '共享',
|
||||
sharedWithMe_title: '与我共享',
|
||||
showOnFrontOfCard: '在卡片正面显示',
|
||||
smtp: null,
|
||||
sortList_title: '排序列表',
|
||||
stopwatch: '计时器',
|
||||
story: '故事',
|
||||
@@ -274,6 +281,7 @@ export default {
|
||||
taskList_title: '任务列表',
|
||||
team: '团队',
|
||||
terms: null,
|
||||
testLog_title: null,
|
||||
thereIsNoPreviewAvailableForThisAttachment: '此附件无法预览',
|
||||
time: '时间',
|
||||
title: '标题',
|
||||
@@ -287,6 +295,7 @@ export default {
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: '已上传图片',
|
||||
url: '网址',
|
||||
useSecureConnection: null,
|
||||
userActions_title: '用户操作',
|
||||
userAddedCardToList: '<0>{{user}}</0> 将 <2>{{card}}</2> 添加到 {{list}}',
|
||||
userAddedThisCardToList: '<0>{{user}}</0> 向列表 {{list}} 添加了该卡片',
|
||||
@@ -424,6 +433,7 @@ export default {
|
||||
restoreToList: '恢复到 {{list}}',
|
||||
returnToBoard: '返回面板',
|
||||
save: '保存',
|
||||
sendTestEmail: null,
|
||||
showActive: '显示活跃',
|
||||
showAllAttachments: '显示所有的附件 ({{hidden}} 隐藏)',
|
||||
showCardsWithThisUser: '显示包含此用户的卡片',
|
||||
|
||||
@@ -95,6 +95,7 @@ export default {
|
||||
cardsOnThisListAreCompleteAndReadyToBeArchived: null,
|
||||
cardsOnThisListAreReadyToBeWorkedOn: null,
|
||||
clickHereOrRefreshPageToUpdate: null,
|
||||
clientHostnameInEhlo: null,
|
||||
closed: null,
|
||||
color: '顏色',
|
||||
comments: null,
|
||||
@@ -117,6 +118,7 @@ export default {
|
||||
date: '日期',
|
||||
deactivateUser_title: null,
|
||||
defaultCardType_title: null,
|
||||
defaultFrom: null,
|
||||
defaultView_title: null,
|
||||
deleteAllBoardsToBeAbleToDeleteThisProject: null,
|
||||
deleteAttachment_title: '刪除附件',
|
||||
@@ -180,6 +182,7 @@ export default {
|
||||
grid: null,
|
||||
hideCompletedTasks: null,
|
||||
hideFromProjectListAndFavorites: null,
|
||||
host: null,
|
||||
hours: '小時',
|
||||
importBoard_title: '導入看板',
|
||||
invalidCurrentPassword: '當前密碼錯誤',
|
||||
@@ -223,8 +226,10 @@ export default {
|
||||
optional_inline: '可選的',
|
||||
organization: '組織機構',
|
||||
others: null,
|
||||
passwordIsSet: null,
|
||||
phone: '電話',
|
||||
plankaUsesAppriseToSendNotificationsToOver100PopularServices: null,
|
||||
port: null,
|
||||
preferences: '偏好設定',
|
||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||
'提示: 按下 Ctrl-V (Mac: Cmd-V) 從剪貼簿添加附件',
|
||||
@@ -233,6 +238,7 @@ export default {
|
||||
projectNotFound_title: '專案未找到',
|
||||
projectOwner: null,
|
||||
referenceDataAndKnowledgeStorage: null,
|
||||
rejectUnauthorizedTlsCertificates: null,
|
||||
removeManager_title: '刪除管理員',
|
||||
removeMember_title: '刪除成員',
|
||||
role: null,
|
||||
@@ -259,6 +265,7 @@ export default {
|
||||
shared: null,
|
||||
sharedWithMe_title: null,
|
||||
showOnFrontOfCard: null,
|
||||
smtp: null,
|
||||
sortList_title: null,
|
||||
stopwatch: '碼表',
|
||||
story: null,
|
||||
@@ -270,6 +277,7 @@ export default {
|
||||
taskList_title: null,
|
||||
team: null,
|
||||
terms: null,
|
||||
testLog_title: null,
|
||||
thereIsNoPreviewAvailableForThisAttachment: '此附件無法預覽',
|
||||
time: '時間',
|
||||
title: '標題',
|
||||
@@ -283,6 +291,7 @@ export default {
|
||||
uploadFailedNotEnoughStorageSpace: null,
|
||||
uploadedImages: null,
|
||||
url: null,
|
||||
useSecureConnection: null,
|
||||
userActions_title: '使用者操作',
|
||||
userAddedCardToList: null,
|
||||
userAddedThisCardToList: '<0>{{user}}</0> 向列表 {{list}} 添加了該卡片',
|
||||
@@ -419,6 +428,7 @@ export default {
|
||||
restoreToList: null,
|
||||
returnToBoard: null,
|
||||
save: '保存',
|
||||
sendTestEmail: null,
|
||||
showActive: null,
|
||||
showAllAttachments: '顯示所有附件 ({{hidden}} 隱藏)',
|
||||
showCardsWithThisUser: null,
|
||||
|
||||
@@ -7,7 +7,7 @@ import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const initialState = {
|
||||
isInitializing: true,
|
||||
config: null,
|
||||
bootstrap: null,
|
||||
};
|
||||
|
||||
// eslint-disable-next-line default-param-last
|
||||
@@ -16,13 +16,13 @@ export default (state = initialState, { type, payload }) => {
|
||||
case ActionTypes.SOCKET_RECONNECT_HANDLE:
|
||||
return {
|
||||
...state,
|
||||
config: payload.config,
|
||||
bootstrap: payload.bootstrap,
|
||||
};
|
||||
case ActionTypes.LOGIN_INITIALIZE:
|
||||
return {
|
||||
...state,
|
||||
isInitializing: false,
|
||||
config: payload.config,
|
||||
bootstrap: payload.bootstrap,
|
||||
};
|
||||
case ActionTypes.AUTHENTICATE__SUCCESS:
|
||||
case ActionTypes.WITH_OIDC_AUTHENTICATE__SUCCESS:
|
||||
@@ -36,16 +36,16 @@ export default (state = initialState, { type, payload }) => {
|
||||
...state,
|
||||
isInitializing: false,
|
||||
};
|
||||
case ActionTypes.CORE_INITIALIZE__CONFIG_FETCH:
|
||||
case ActionTypes.CORE_INITIALIZE__BOOTSTRAP_FETCH:
|
||||
return {
|
||||
...state,
|
||||
config: payload.config,
|
||||
bootstrap: payload.bootstrap,
|
||||
};
|
||||
case ActionTypes.USER_UPDATE_HANDLE:
|
||||
if (payload.config) {
|
||||
if (payload.bootstrap) {
|
||||
return {
|
||||
...state,
|
||||
config: payload.config,
|
||||
bootstrap: payload.bootstrap,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ const initialState = {
|
||||
isFavoritesEnabled: false,
|
||||
isEditModeEnabled: false,
|
||||
modal: null,
|
||||
config: null,
|
||||
boardId: null,
|
||||
cardId: null,
|
||||
recentCardId: null,
|
||||
@@ -70,13 +71,30 @@ export default (state = initialState, { type, payload }) => {
|
||||
...state,
|
||||
isContentFetching: true,
|
||||
};
|
||||
case ActionTypes.CORE_INITIALIZE:
|
||||
case ActionTypes.SOCKET_RECONNECT_HANDLE:
|
||||
case ActionTypes.USER_UPDATE_HANDLE:
|
||||
if (payload.config) {
|
||||
return {
|
||||
...state,
|
||||
config: payload.config,
|
||||
};
|
||||
}
|
||||
|
||||
return state;
|
||||
case ActionTypes.CORE_INITIALIZE: {
|
||||
const nextState = {
|
||||
...state,
|
||||
isFavoritesEnabled: payload.user.enableFavoritesByDefault,
|
||||
homeView: payload.user.defaultHomeView,
|
||||
projectsOrder: payload.user.defaultProjectsOrder,
|
||||
};
|
||||
|
||||
if (payload.config) {
|
||||
nextState.config = payload.config;
|
||||
}
|
||||
|
||||
return nextState;
|
||||
}
|
||||
case ActionTypes.FAVORITES_TOGGLE:
|
||||
return {
|
||||
...state,
|
||||
@@ -102,6 +120,27 @@ export default (state = initialState, { type, payload }) => {
|
||||
...state,
|
||||
modal: payload,
|
||||
};
|
||||
case ActionTypes.CONFIG_UPDATE:
|
||||
return {
|
||||
...state,
|
||||
config: {
|
||||
...state.config,
|
||||
...payload.data,
|
||||
},
|
||||
};
|
||||
case ActionTypes.CONFIG_UPDATE__SUCCESS:
|
||||
return {
|
||||
...state,
|
||||
config: {
|
||||
...state.config,
|
||||
...payload.config,
|
||||
},
|
||||
};
|
||||
case ActionTypes.CONFIG_UPDATE_HANDLE:
|
||||
return {
|
||||
...state,
|
||||
config: payload.config,
|
||||
};
|
||||
case ActionTypes.PROJECTS_SEARCH:
|
||||
return {
|
||||
...state,
|
||||
|
||||
@@ -8,9 +8,11 @@ import { combineReducers } from 'redux';
|
||||
import authenticateForm from './authenticate-form';
|
||||
import userCreateForm from './user-create-form';
|
||||
import projectCreateForm from './project-create-form';
|
||||
import smtpTest from './smtp-test';
|
||||
|
||||
export default combineReducers({
|
||||
authenticateForm,
|
||||
userCreateForm,
|
||||
projectCreateForm,
|
||||
smtpTest,
|
||||
});
|
||||
|
||||
35
client/src/reducers/ui/smtp-test.js
Normal file
35
client/src/reducers/ui/smtp-test.js
Normal file
@@ -0,0 +1,35 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import ActionTypes from '../../constants/ActionTypes';
|
||||
|
||||
const initialState = {
|
||||
isLoading: false,
|
||||
logs: null,
|
||||
error: null,
|
||||
};
|
||||
|
||||
// eslint-disable-next-line default-param-last
|
||||
export default (state = initialState, { type, payload }) => {
|
||||
switch (type) {
|
||||
case ActionTypes.SMTP_CONFIG_TEST:
|
||||
return {
|
||||
...state,
|
||||
isLoading: true,
|
||||
};
|
||||
case ActionTypes.SMTP_CONFIG_TEST__SUCCESS:
|
||||
return {
|
||||
...initialState,
|
||||
logs: payload.logs,
|
||||
};
|
||||
case ActionTypes.SMTP_CONFIG_TEST__FAILURE:
|
||||
return {
|
||||
...initialState,
|
||||
error: payload.error,
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
@@ -21,14 +21,14 @@ export default function* coreSaga() {
|
||||
|
||||
yield take(ActionTypes.LOGOUT);
|
||||
|
||||
const oidcConfig = yield select(selectors.selectOidcConfig);
|
||||
const oidcBootstrap = yield select(selectors.selectOidcBootstrap);
|
||||
|
||||
if (oidcConfig && oidcConfig.endSessionUrl !== null) {
|
||||
if (oidcBootstrap && oidcBootstrap.endSessionUrl !== null) {
|
||||
const currentUser = yield select(selectors.selectCurrentUser);
|
||||
|
||||
if (!currentUser || currentUser.isSsoUser) {
|
||||
// Redirect the user to the IDP to log out.
|
||||
window.location.href = oidcConfig.endSessionUrl;
|
||||
window.location.href = oidcBootstrap.endSessionUrl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,11 @@ export function* fetchCore() {
|
||||
included: { notificationServices: notificationServices1 },
|
||||
} = yield call(request, api.getCurrentUser, true);
|
||||
|
||||
let config;
|
||||
let webhooks;
|
||||
|
||||
if (user.role === UserRoles.ADMIN) {
|
||||
({ item: config } = yield call(request, api.getConfig));
|
||||
({ items: webhooks } = yield call(request, api.getWebhooks));
|
||||
}
|
||||
|
||||
@@ -105,6 +108,7 @@ export function* fetchCore() {
|
||||
}
|
||||
|
||||
return {
|
||||
config,
|
||||
user,
|
||||
board,
|
||||
webhooks,
|
||||
|
||||
49
client/src/sagas/core/services/config.js
Normal file
49
client/src/sagas/core/services/config.js
Normal file
@@ -0,0 +1,49 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import { call, put } from 'redux-saga/effects';
|
||||
|
||||
import request from '../request';
|
||||
import actions from '../../../actions';
|
||||
import api from '../../../api';
|
||||
|
||||
export function* updateConfig(data) {
|
||||
yield put(actions.updateConfig(data));
|
||||
|
||||
let config;
|
||||
try {
|
||||
({ item: config } = yield call(request, api.updateConfig, data));
|
||||
} catch (error) {
|
||||
yield put(actions.updateConfig.failure(error));
|
||||
return;
|
||||
}
|
||||
|
||||
yield put(actions.updateConfig.success(config));
|
||||
}
|
||||
|
||||
export function* handleConfigUpdate(config) {
|
||||
yield put(actions.handleConfigUpdate(config));
|
||||
}
|
||||
|
||||
export function* testSmtpConfig() {
|
||||
yield put(actions.testSmtpConfig());
|
||||
|
||||
let logs;
|
||||
try {
|
||||
({
|
||||
included: { logs },
|
||||
} = yield call(request, api.testSmtpConfig));
|
||||
} catch (error) {
|
||||
yield put(actions.testSmtpConfig.failure(error));
|
||||
}
|
||||
|
||||
yield put(actions.testSmtpConfig.success(logs));
|
||||
}
|
||||
|
||||
export default {
|
||||
updateConfig,
|
||||
handleConfigUpdate,
|
||||
testSmtpConfig,
|
||||
};
|
||||
@@ -14,11 +14,12 @@ import i18n from '../../../i18n';
|
||||
import { removeAccessToken } from '../../../utils/access-token-storage';
|
||||
|
||||
export function* initializeCore() {
|
||||
const { item: config } = yield call(request, api.getConfig); // TODO: handle error
|
||||
const { item: bootstrap } = yield call(request, api.getBootstrap); // TODO: handle error
|
||||
|
||||
yield put(actions.initializeCore.fetchConfig(config));
|
||||
yield put(actions.initializeCore.fetchBootstrap(bootstrap));
|
||||
|
||||
const {
|
||||
config,
|
||||
user,
|
||||
board,
|
||||
webhooks,
|
||||
@@ -49,6 +50,7 @@ export function* initializeCore() {
|
||||
|
||||
yield put(
|
||||
actions.initializeCore(
|
||||
config,
|
||||
user,
|
||||
board,
|
||||
webhooks,
|
||||
|
||||
@@ -7,6 +7,7 @@ import router from './router';
|
||||
import socket from './socket';
|
||||
import core from './core';
|
||||
import modals from './modals';
|
||||
import config from './config';
|
||||
import webhooks from './webhooks';
|
||||
import users from './users';
|
||||
import projects from './projects';
|
||||
@@ -34,6 +35,7 @@ export default {
|
||||
...socket,
|
||||
...core,
|
||||
...modals,
|
||||
...config,
|
||||
...webhooks,
|
||||
...users,
|
||||
...projects,
|
||||
|
||||
@@ -21,6 +21,7 @@ export function* handleSocketReconnect() {
|
||||
|
||||
yield put(actions.handleSocketReconnect.fetchCore(currentUserId, boardId));
|
||||
|
||||
let bootstrap;
|
||||
let config;
|
||||
let user;
|
||||
let board;
|
||||
@@ -47,7 +48,7 @@ export function* handleSocketReconnect() {
|
||||
let notificationServices;
|
||||
|
||||
try {
|
||||
({ item: config } = yield call(request, api.getConfig));
|
||||
({ item: bootstrap } = yield call(request, api.getBootstrap));
|
||||
|
||||
({
|
||||
user,
|
||||
@@ -80,6 +81,7 @@ export function* handleSocketReconnect() {
|
||||
|
||||
yield put(
|
||||
actions.handleSocketReconnect(
|
||||
bootstrap,
|
||||
config,
|
||||
user,
|
||||
board,
|
||||
|
||||
@@ -68,6 +68,7 @@ export function* handleUserUpdate(user) {
|
||||
const currentUser = yield select(selectors.selectCurrentUser);
|
||||
const isCurrentUser = user.id === currentUser.id;
|
||||
|
||||
let bootstrap;
|
||||
let config;
|
||||
let board;
|
||||
let webhooks;
|
||||
@@ -102,6 +103,7 @@ export function* handleUserUpdate(user) {
|
||||
({ items: users1 } = yield call(request, api.getUsers));
|
||||
|
||||
if (user.role === UserRoles.ADMIN) {
|
||||
({ item: bootstrap } = yield call(request, api.getBootstrap));
|
||||
({ item: config } = yield call(request, api.getConfig));
|
||||
({ items: webhooks } = yield call(request, api.getWebhooks));
|
||||
|
||||
@@ -164,6 +166,7 @@ export function* handleUserUpdate(user) {
|
||||
user,
|
||||
projectIds,
|
||||
boardIds,
|
||||
bootstrap,
|
||||
config,
|
||||
board,
|
||||
webhooks,
|
||||
@@ -248,10 +251,10 @@ export function* updateUserPassword(id, data) {
|
||||
yield put(actions.updateUserPassword(id, data));
|
||||
|
||||
let user;
|
||||
let accessTokens;
|
||||
let accessToken;
|
||||
|
||||
try {
|
||||
({ item: user, included: { accessTokens } = {} } = yield call(
|
||||
({ item: user, included: { accessToken } = {} } = yield call(
|
||||
request,
|
||||
api.updateUserPassword,
|
||||
id,
|
||||
@@ -262,8 +265,6 @@ export function* updateUserPassword(id, data) {
|
||||
return;
|
||||
}
|
||||
|
||||
const accessToken = accessTokens && accessTokens[0];
|
||||
|
||||
if (accessToken) {
|
||||
yield call(setAccessToken, accessToken);
|
||||
}
|
||||
|
||||
21
client/src/sagas/core/watchers/config.js
Normal file
21
client/src/sagas/core/watchers/config.js
Normal file
@@ -0,0 +1,21 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import { all, takeEvery } from 'redux-saga/effects';
|
||||
|
||||
import services from '../services';
|
||||
import EntryActionTypes from '../../../constants/EntryActionTypes';
|
||||
|
||||
export default function* configWatchers() {
|
||||
yield all([
|
||||
takeEvery(EntryActionTypes.CONFIG_UPDATE, ({ payload: { data } }) =>
|
||||
services.updateConfig(data),
|
||||
),
|
||||
takeEvery(EntryActionTypes.CONFIG_UPDATE_HANDLE, ({ payload: { config } }) =>
|
||||
services.handleConfigUpdate(config),
|
||||
),
|
||||
takeEvery(EntryActionTypes.SMTP_CONFIG_TEST, () => services.testSmtpConfig()),
|
||||
]);
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import router from './router';
|
||||
import socket from './socket';
|
||||
import core from './core';
|
||||
import modals from './modals';
|
||||
import config from './config';
|
||||
import webhooks from './webhooks';
|
||||
import users from './users';
|
||||
import projects from './projects';
|
||||
@@ -34,6 +35,7 @@ export default [
|
||||
socket,
|
||||
core,
|
||||
modals,
|
||||
config,
|
||||
webhooks,
|
||||
users,
|
||||
projects,
|
||||
|
||||
@@ -29,6 +29,18 @@ const createSocketEventsChannel = () =>
|
||||
emit(entryActions.handleConfigUpdate(item));
|
||||
};
|
||||
|
||||
const handleWebhookCreate = ({ item }) => {
|
||||
emit(entryActions.handleWebhookCreate(item));
|
||||
};
|
||||
|
||||
const handleWebhookUpdate = ({ item }) => {
|
||||
emit(entryActions.handleWebhookUpdate(item));
|
||||
};
|
||||
|
||||
const handleWebhookDelete = ({ item }) => {
|
||||
emit(entryActions.handleWebhookDelete(item));
|
||||
};
|
||||
|
||||
const handleUserCreate = ({ item }) => {
|
||||
emit(entryActions.handleUserCreate(item));
|
||||
};
|
||||
@@ -280,6 +292,10 @@ const createSocketEventsChannel = () =>
|
||||
|
||||
socket.on('configUpdate', handleConfigUpdate);
|
||||
|
||||
socket.on('webhookCreate', handleWebhookCreate);
|
||||
socket.on('webhookUpdate', handleWebhookUpdate);
|
||||
socket.on('webhookDelete', handleWebhookDelete);
|
||||
|
||||
socket.on('userCreate', handleUserCreate);
|
||||
socket.on('userUpdate', handleUserUpdate);
|
||||
socket.on('userDelete', handleUserDelete);
|
||||
@@ -370,6 +386,10 @@ const createSocketEventsChannel = () =>
|
||||
|
||||
socket.off('configUpdate', handleConfigUpdate);
|
||||
|
||||
socket.off('webhookCreate', handleWebhookCreate);
|
||||
socket.off('webhookUpdate', handleWebhookUpdate);
|
||||
socket.off('webhookDelete', handleWebhookDelete);
|
||||
|
||||
socket.off('userCreate', handleUserCreate);
|
||||
socket.off('userUpdate', handleUserUpdate);
|
||||
socket.off('userDelete', handleUserDelete);
|
||||
|
||||
@@ -16,9 +16,9 @@ import Paths from '../../../constants/Paths';
|
||||
import AccessTokenSteps from '../../../constants/AccessTokenSteps';
|
||||
|
||||
export function* initializeLogin() {
|
||||
const { item: config } = yield call(api.getConfig); // TODO: handle error
|
||||
const { item: bootstrap } = yield call(api.getBootstrap); // TODO: handle error
|
||||
|
||||
yield put(actions.initializeLogin(config));
|
||||
yield put(actions.initializeLogin(bootstrap));
|
||||
}
|
||||
|
||||
export function* authenticate(data) {
|
||||
@@ -42,7 +42,7 @@ export function* authenticate(data) {
|
||||
}
|
||||
|
||||
export function* authenticateWithOidc() {
|
||||
const oidcConfig = yield select(selectors.selectOidcConfig);
|
||||
const oidcBootstrap = yield select(selectors.selectOidcBootstrap);
|
||||
|
||||
const state = nanoid();
|
||||
window.localStorage.setItem('oidc-state', state);
|
||||
@@ -50,7 +50,7 @@ export function* authenticateWithOidc() {
|
||||
const nonce = nanoid();
|
||||
window.localStorage.setItem('oidc-nonce', nonce);
|
||||
|
||||
let redirectUrl = `${oidcConfig.authorizationUrl}`;
|
||||
let redirectUrl = `${oidcBootstrap.authorizationUrl}`;
|
||||
redirectUrl += `&state=${encodeURIComponent(state)}`;
|
||||
redirectUrl += `&nonce=${encodeURIComponent(nonce)}`;
|
||||
|
||||
|
||||
@@ -49,9 +49,9 @@ export function* handleLocationChange() {
|
||||
|
||||
switch (pathsMatch.pattern.path) {
|
||||
case Paths.LOGIN: {
|
||||
const oidcConfig = yield select(selectors.selectOidcConfig);
|
||||
const oidcBootstrap = yield select(selectors.selectOidcBootstrap);
|
||||
|
||||
if (oidcConfig) {
|
||||
if (oidcBootstrap) {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
|
||||
if (params.has('authenticateWithOidc')) {
|
||||
|
||||
@@ -7,11 +7,11 @@ export const selectIsSocketDisconnected = ({ socket: { isDisconnected } }) => is
|
||||
|
||||
export const selectIsInitializing = ({ common: { isInitializing } }) => isInitializing;
|
||||
|
||||
export const selectConfig = ({ common: { config } }) => config;
|
||||
export const selectBootstrap = ({ common: { bootstrap } }) => bootstrap;
|
||||
|
||||
export const selectOidcConfig = (state) => selectConfig(state).oidc;
|
||||
export const selectOidcBootstrap = (state) => selectBootstrap(state).oidc;
|
||||
|
||||
export const selectActiveUsersLimit = (state) => selectConfig(state).activeUsersLimit;
|
||||
export const selectActiveUsersLimit = (state) => selectBootstrap(state).activeUsersLimit;
|
||||
|
||||
export const selectAccessToken = ({ auth: { accessToken } }) => accessToken;
|
||||
|
||||
@@ -21,14 +21,17 @@ export const selectUserCreateForm = ({ ui: { userCreateForm } }) => userCreateFo
|
||||
|
||||
export const selectProjectCreateForm = ({ ui: { projectCreateForm } }) => projectCreateForm;
|
||||
|
||||
export const selectSmtpTest = ({ ui: { smtpTest } }) => smtpTest;
|
||||
|
||||
export default {
|
||||
selectIsSocketDisconnected,
|
||||
selectIsInitializing,
|
||||
selectConfig,
|
||||
selectOidcConfig,
|
||||
selectBootstrap,
|
||||
selectOidcBootstrap,
|
||||
selectActiveUsersLimit,
|
||||
selectAccessToken,
|
||||
selectAuthenticateForm,
|
||||
selectUserCreateForm,
|
||||
selectProjectCreateForm,
|
||||
selectSmtpTest,
|
||||
};
|
||||
|
||||
@@ -11,6 +11,8 @@ export const selectIsFavoritesEnabled = ({ core: { isFavoritesEnabled } }) => is
|
||||
|
||||
export const selectIsEditModeEnabled = ({ core: { isEditModeEnabled } }) => isEditModeEnabled;
|
||||
|
||||
export const selectConfig = ({ core: { config } }) => config;
|
||||
|
||||
export const selectRecentCardId = ({ core: { recentCardId } }) => recentCardId;
|
||||
|
||||
export const selectPrevCardId = ({ core: { prevCardIds } }) => prevCardIds.at(-1);
|
||||
@@ -29,6 +31,7 @@ export default {
|
||||
selectIsLogouting,
|
||||
selectIsFavoritesEnabled,
|
||||
selectIsEditModeEnabled,
|
||||
selectConfig,
|
||||
selectRecentCardId,
|
||||
selectPrevCardId,
|
||||
selectHomeView,
|
||||
|
||||
@@ -76,14 +76,15 @@ services:
|
||||
# - OIDC_ENFORCED=true
|
||||
|
||||
# Email Notifications (https://nodemailer.com/smtp/)
|
||||
# These values override and disable configuration in the UI if set.
|
||||
# - SMTP_HOST=
|
||||
# - SMTP_PORT=587
|
||||
# - SMTP_NAME=
|
||||
# - SMTP_SECURE=true
|
||||
# - SMTP_TLS_REJECT_UNAUTHORIZED=false
|
||||
# - SMTP_USER=
|
||||
# - SMTP_PASSWORD=
|
||||
# - SMTP_FROM="Demo Demo" <demo@demo.demo>
|
||||
# - SMTP_TLS_REJECT_UNAUTHORIZED=false
|
||||
|
||||
# Using Gravatar directly exposes user IPs and hashed emails to a third party (GDPR risk).
|
||||
# Use a proxy you control for privacy, or leave commented out or empty to disable.
|
||||
|
||||
@@ -94,16 +94,17 @@ services:
|
||||
# - OIDC_ENFORCED=true
|
||||
|
||||
# Email Notifications (https://nodemailer.com/smtp/)
|
||||
# These values override and disable configuration in the UI if set.
|
||||
# - SMTP_HOST=
|
||||
# - SMTP_PORT=587
|
||||
# - SMTP_NAME=
|
||||
# - SMTP_SECURE=true
|
||||
# - SMTP_TLS_REJECT_UNAUTHORIZED=false
|
||||
# - SMTP_USER=
|
||||
# - SMTP_PASSWORD=
|
||||
# Optionally store in secrets - then SMTP_PASSWORD should not be set
|
||||
# - SMTP_PASSWORD__FILE=/run/secrets/smtp_password
|
||||
# - SMTP_FROM="Demo Demo" <demo@demo.demo>
|
||||
# - SMTP_TLS_REJECT_UNAUTHORIZED=false
|
||||
|
||||
# Using Gravatar directly exposes user IPs and hashed emails to a third party (GDPR risk).
|
||||
# Use a proxy you control for privacy, or leave commented out or empty to disable.
|
||||
|
||||
@@ -67,14 +67,15 @@ SECRET_KEY=notsecretkey
|
||||
# OIDC_ENFORCED=true
|
||||
|
||||
# Email Notifications (https://nodemailer.com/smtp/)
|
||||
# These values override and disable configuration in the UI if set.
|
||||
# SMTP_HOST=
|
||||
# SMTP_PORT=587
|
||||
# SMTP_NAME=
|
||||
# SMTP_SECURE=true
|
||||
# SMTP_TLS_REJECT_UNAUTHORIZED=false
|
||||
# SMTP_USER=
|
||||
# SMTP_PASSWORD=
|
||||
# SMTP_FROM="Demo Demo" <demo@demo.demo>
|
||||
# SMTP_TLS_REJECT_UNAUTHORIZED=false
|
||||
|
||||
# Using Gravatar directly exposes user IPs and hashed emails to a third party (GDPR risk).
|
||||
# Use a proxy you control for privacy, or leave commented out or empty to disable.
|
||||
|
||||
72
server/api/controllers/bootstrap/show.js
Normal file
72
server/api/controllers/bootstrap/show.js
Normal file
@@ -0,0 +1,72 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /bootstrap:
|
||||
* get:
|
||||
* summary: Get application bootstrap
|
||||
* description: Retrieves the application bootstrap.
|
||||
* tags:
|
||||
* - Bootstrap
|
||||
* operationId: getBootstrap
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Bootstrap retrieved successfully
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required:
|
||||
* - oidc
|
||||
* - version
|
||||
* properties:
|
||||
* oidc:
|
||||
* type: object
|
||||
* required:
|
||||
* - authorizationUrl
|
||||
* - endSessionUrl
|
||||
* - isEnforced
|
||||
* nullable: true
|
||||
* description: OpenID Connect configuration (null if not configured)
|
||||
* properties:
|
||||
* authorizationUrl:
|
||||
* type: string
|
||||
* format: uri
|
||||
* description: OIDC authorization URL for initiating authentication
|
||||
* example: https://oidc.example.com/auth
|
||||
* endSessionUrl:
|
||||
* type: string
|
||||
* format: uri
|
||||
* nullable: true
|
||||
* description: OIDC end session URL for logout (null if not supported by provider)
|
||||
* example: https://oidc.example.com/logout
|
||||
* isEnforced:
|
||||
* type: boolean
|
||||
* description: Whether OIDC authentication is enforced (users must use OIDC to login)
|
||||
* example: false
|
||||
* activeUsersLimit:
|
||||
* type: number
|
||||
* nullable: true
|
||||
* description: Maximum number of active users allowed (conditionally added for admins if configured)
|
||||
* example: 100
|
||||
* version:
|
||||
* type: string
|
||||
* description: Current version of the PLANKA application
|
||||
* example: 2.0.0
|
||||
* security: []
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
async fn() {
|
||||
const { currentUser } = this.req;
|
||||
|
||||
const oidc = await sails.hooks.oidc.getBootstrap();
|
||||
|
||||
return {
|
||||
item: sails.helpers.bootstrap.presentOne(oidc, currentUser),
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -8,7 +8,7 @@
|
||||
* /config:
|
||||
* get:
|
||||
* summary: Get application configuration
|
||||
* description: Retrieves the application configuration.
|
||||
* description: Retrieves the application configuration. Requires admin privileges.
|
||||
* tags:
|
||||
* - Config
|
||||
* operationId: getConfig
|
||||
@@ -24,39 +24,14 @@
|
||||
* properties:
|
||||
* item:
|
||||
* $ref: '#/components/schemas/Config'
|
||||
* security: []
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
async fn() {
|
||||
const { currentUser } = this.req;
|
||||
|
||||
const oidcClient = await sails.hooks.oidc.getClient();
|
||||
|
||||
let oidc = null;
|
||||
if (oidcClient) {
|
||||
const authorizationUrlParams = {
|
||||
scope: sails.config.custom.oidcScopes,
|
||||
};
|
||||
|
||||
if (!sails.config.custom.oidcUseDefaultResponseMode) {
|
||||
authorizationUrlParams.response_mode = sails.config.custom.oidcResponseMode;
|
||||
}
|
||||
|
||||
oidc = {
|
||||
authorizationUrl: oidcClient.authorizationUrl(authorizationUrlParams),
|
||||
endSessionUrl: oidcClient.issuer.end_session_endpoint ? oidcClient.endSessionUrl({}) : null,
|
||||
isEnforced: sails.config.custom.oidcEnforced,
|
||||
};
|
||||
}
|
||||
const config = await Config.qm.getOneMain();
|
||||
|
||||
return {
|
||||
item: sails.helpers.config.presentOne(
|
||||
{
|
||||
oidc,
|
||||
},
|
||||
currentUser,
|
||||
),
|
||||
item: sails.helpers.config.presentOne(config),
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
117
server/api/controllers/config/test-smtp.js
Normal file
117
server/api/controllers/config/test-smtp.js
Normal file
@@ -0,0 +1,117 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /config/test-smtp:
|
||||
* post:
|
||||
* summary: Test SMTP configuration
|
||||
* description: Sends a test email to verify the SMTP is configured correctly. Only available when SMTP is configured via the UI.
|
||||
* tags:
|
||||
* - Config
|
||||
* operationId: testSmtpConfig
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Test email sent successfully
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required:
|
||||
* - item
|
||||
* properties:
|
||||
* item:
|
||||
* $ref: '#/components/schemas/Config'
|
||||
* 401:
|
||||
* $ref: '#/components/responses/Unauthorized'
|
||||
* 403:
|
||||
* $ref: '#/components/responses/Forbidden'
|
||||
*/
|
||||
|
||||
const Errors = {
|
||||
NOT_AVAILABLE: {
|
||||
notAvailable: 'Not available',
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
exits: {
|
||||
notAvailable: {
|
||||
responseType: 'forbidden',
|
||||
},
|
||||
},
|
||||
|
||||
async fn() {
|
||||
const { currentUser } = this.req;
|
||||
|
||||
if (sails.config.custom.smtpHost) {
|
||||
return Errors.NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
const { transporter, config } = await sails.helpers.utils.makeSmtpTransporter({
|
||||
connectionTimeout: 5000,
|
||||
greetingTimeout: 5000,
|
||||
socketTimeout: 10000,
|
||||
dnsTimeout: 3000,
|
||||
});
|
||||
|
||||
if (!transporter) {
|
||||
return Errors.NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
const logs = [];
|
||||
try {
|
||||
logs.push('📧 Sending test email...');
|
||||
/* eslint-disable no-underscore-dangle */
|
||||
const info = await transporter.sendMail({
|
||||
to: currentUser.email,
|
||||
subject: this.req.i18n.__('Test Title'),
|
||||
text: this.req.i18n.__('This is a test text message!'),
|
||||
html: this.req.i18n.__('This is a <i>test</i> <b>html</b> <code>message</code>!'),
|
||||
});
|
||||
/* eslint-enable no-underscore-dangle */
|
||||
logs.push('✅ Email sent successfully!', '');
|
||||
|
||||
logs.push(`📬 Message ID: ${info.messageId}`);
|
||||
if (info.response) {
|
||||
logs.push(`📤 Server response: ${info.response.trim()}`);
|
||||
}
|
||||
|
||||
logs.push('', '🎉 Your configuration is working correctly!');
|
||||
} catch (error) {
|
||||
logs.push('❌ Failed to send email!', '');
|
||||
|
||||
if (error.code) {
|
||||
logs.push(`⚠️ Error code: ${error.code}`);
|
||||
}
|
||||
logs.push(`💬 Reason: ${error.message.trim()}`);
|
||||
|
||||
if (error.code === 'EDNS') {
|
||||
logs.push('', '💡 Hint: Check your host setting.');
|
||||
} else if (error.code === 'ETIMEDOUT') {
|
||||
logs.push('', '💡 Hint: Check your host and port settings.');
|
||||
} else if (error.code === 'EAUTH') {
|
||||
logs.push('', '💡 Hint: Check your username and password.');
|
||||
} else if (error.code === 'ESOCKET') {
|
||||
if (error.message.includes('ECONNREFUSED') || error.message.includes('ETIMEDOUT')) {
|
||||
logs.push('', '💡 Hint: Check your host and port settings.');
|
||||
} else if (error.message.includes('wrong version number')) {
|
||||
logs.push('', '💡 Hint: Try toggling "Use secure connection".');
|
||||
} else if (error.message.includes('certificate')) {
|
||||
logs.push('', '💡 Hint: Try toggling "Reject unauthorized TLS certificates".');
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
transporter.close();
|
||||
}
|
||||
|
||||
return {
|
||||
item: sails.helpers.config.presentOne(config),
|
||||
included: {
|
||||
logs,
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
151
server/api/controllers/config/update.js
Normal file
151
server/api/controllers/config/update.js
Normal file
@@ -0,0 +1,151 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /config:
|
||||
* patch:
|
||||
* summary: Update application configuration
|
||||
* description: Updates the application configuration. Requires admin privileges.
|
||||
* tags:
|
||||
* - Config
|
||||
* operationId: updateConfig
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* smtpHost:
|
||||
* type: string
|
||||
* maxLength: 256
|
||||
* nullable: true
|
||||
* description: Hostname or IP address of the SMTP server
|
||||
* example: smtp.example.com
|
||||
* smtpHost:
|
||||
* type: number
|
||||
* minimum: 0
|
||||
* maximum: 65535
|
||||
* nullable: true
|
||||
* description: Port number of the SMTP server
|
||||
* example: 587
|
||||
* smtpName:
|
||||
* type: string
|
||||
* maxLength: 256
|
||||
* nullable: true
|
||||
* description: Client hostname used in the EHLO command for SMTP
|
||||
* example: localhost
|
||||
* smtpSecure:
|
||||
* type: boolean
|
||||
* description: Whether to use a secure connection for SMTP
|
||||
* example: false
|
||||
* smtpTlsRejectUnauthorized:
|
||||
* type: boolean
|
||||
* description: Whether to reject unauthorized or self-signed TLS certificates for SMTP connections
|
||||
* example: true
|
||||
* smtpUser:
|
||||
* type: string
|
||||
* maxLength: 256
|
||||
* nullable: true
|
||||
* description: Username for authenticating with the SMTP server
|
||||
* example: no-reply@example.com
|
||||
* smtpPassword:
|
||||
* type: string
|
||||
* maxLength: 256
|
||||
* nullable: true
|
||||
* description: Password for authenticating with the SMTP server
|
||||
* example: SecurePassword123!
|
||||
* smtpFrom:
|
||||
* type: string
|
||||
* maxLength: 256
|
||||
* nullable: true
|
||||
* description: Default "from" used for outgoing SMTP emails
|
||||
* example: no-reply@example.com
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Configuration updated successfully
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required:
|
||||
* - item
|
||||
* properties:
|
||||
* item:
|
||||
* $ref: '#/components/schemas/Config'
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
inputs: {
|
||||
smtpHost: {
|
||||
type: 'string',
|
||||
isNotEmptyString: true,
|
||||
maxLength: 256,
|
||||
allowNull: true,
|
||||
},
|
||||
smtpPort: {
|
||||
type: 'number',
|
||||
min: 0,
|
||||
max: 65535,
|
||||
allowNull: true,
|
||||
},
|
||||
smtpName: {
|
||||
type: 'string',
|
||||
isNotEmptyString: true,
|
||||
maxLength: 256,
|
||||
allowNull: true,
|
||||
},
|
||||
smtpSecure: {
|
||||
type: 'boolean',
|
||||
},
|
||||
smtpTlsRejectUnauthorized: {
|
||||
type: 'boolean',
|
||||
},
|
||||
smtpUser: {
|
||||
type: 'string',
|
||||
isNotEmptyString: true,
|
||||
maxLength: 256,
|
||||
allowNull: true,
|
||||
},
|
||||
smtpPassword: {
|
||||
type: 'string',
|
||||
isNotEmptyString: true,
|
||||
maxLength: 256,
|
||||
allowNull: true,
|
||||
},
|
||||
smtpFrom: {
|
||||
type: 'string',
|
||||
isNotEmptyString: true,
|
||||
maxLength: 256,
|
||||
allowNull: true,
|
||||
},
|
||||
},
|
||||
|
||||
async fn(inputs) {
|
||||
const { currentUser } = this.req;
|
||||
|
||||
const values = _.pick(inputs, [
|
||||
'smtpHost',
|
||||
'smtpPort',
|
||||
'smtpName',
|
||||
'smtpSecure',
|
||||
'smtpTlsRejectUnauthorized',
|
||||
'smtpUser',
|
||||
'smtpPassword',
|
||||
'smtpFrom',
|
||||
]);
|
||||
|
||||
const config = await sails.helpers.config.updateMain.with({
|
||||
values,
|
||||
actorUser: currentUser,
|
||||
request: this.req,
|
||||
});
|
||||
|
||||
return {
|
||||
item: sails.helpers.config.presentOne(config),
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -84,10 +84,17 @@ module.exports = {
|
||||
}
|
||||
}
|
||||
|
||||
await sails.helpers.notificationServices.testOne.with({
|
||||
record: notificationService,
|
||||
i18n: this.req.i18n,
|
||||
/* eslint-disable no-underscore-dangle */
|
||||
await sails.helpers.utils.sendNotifications.with({
|
||||
services: [_.pick(notificationService, ['url', 'format'])],
|
||||
title: this.req.i18n.__('Test Title'),
|
||||
bodyByFormat: {
|
||||
text: this.req.i18n.__('This is a test text message!'),
|
||||
markdown: this.req.i18n.__('This is a *test* **markdown** `message`!'),
|
||||
html: this.req.i18n.__('This is a <i>test</i> <b>html</b> <code>message</code>!'),
|
||||
},
|
||||
});
|
||||
/* eslint-enable no-underscore-dangle */
|
||||
|
||||
return {
|
||||
item: notificationService,
|
||||
|
||||
@@ -54,13 +54,11 @@
|
||||
* included:
|
||||
* type: object
|
||||
* required:
|
||||
* - accessTokens
|
||||
* - accessToken
|
||||
* properties:
|
||||
* accessTokens:
|
||||
* type: array
|
||||
* description: New acces tokens (when updating own password)
|
||||
* items:
|
||||
* accessToken:
|
||||
* type: string
|
||||
* description: New acces tokens (when updating own password)
|
||||
* example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ4...
|
||||
* 400:
|
||||
* $ref: '#/components/responses/ValidationError'
|
||||
@@ -180,7 +178,7 @@ module.exports = {
|
||||
return {
|
||||
item: sails.helpers.users.presentOne(user, currentUser),
|
||||
included: {
|
||||
accessTokens: [accessToken],
|
||||
accessToken,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -154,9 +154,9 @@ module.exports = {
|
||||
await sails.helpers.notifications.createOne.with({
|
||||
values: {
|
||||
action,
|
||||
userId: action.data.user.id,
|
||||
type: action.type,
|
||||
data: action.data,
|
||||
userId: action.data.user.id,
|
||||
creatorUser: values.user,
|
||||
card: values.card,
|
||||
},
|
||||
@@ -179,24 +179,20 @@ module.exports = {
|
||||
|
||||
const notifiableUserIds = _.union(cardSubscriptionUserIds, boardSubscriptionUserIds);
|
||||
|
||||
await Promise.all(
|
||||
notifiableUserIds.map((userId) =>
|
||||
sails.helpers.notifications.createOne.with({
|
||||
values: {
|
||||
await sails.helpers.notifications.createMany.with({
|
||||
arrayOfValues: notifiableUserIds.map((userId) => ({
|
||||
userId,
|
||||
action,
|
||||
type: action.type,
|
||||
data: action.data,
|
||||
creatorUser: values.user,
|
||||
card: values.card,
|
||||
},
|
||||
})),
|
||||
project: inputs.project,
|
||||
board: inputs.board,
|
||||
list: inputs.list,
|
||||
webhooks: inputs.webhooks,
|
||||
}),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
29
server/api/helpers/bootstrap/present-one.js
Normal file
29
server/api/helpers/bootstrap/present-one.js
Normal file
@@ -0,0 +1,29 @@
|
||||
/*!
|
||||
* 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: {
|
||||
oidc: {
|
||||
type: 'ref',
|
||||
},
|
||||
user: {
|
||||
type: 'ref',
|
||||
},
|
||||
},
|
||||
|
||||
fn(inputs) {
|
||||
const data = {
|
||||
oidc: inputs.oidc,
|
||||
version: sails.config.custom.version,
|
||||
};
|
||||
if (inputs.user && inputs.user.role === User.Roles.ADMIN) {
|
||||
data.activeUsersLimit = sails.config.custom.activeUsersLimit;
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
};
|
||||
@@ -124,11 +124,9 @@ module.exports = {
|
||||
boardSubscriptionUserIds,
|
||||
);
|
||||
|
||||
await Promise.all(
|
||||
notifiableUserIds.map((userId) =>
|
||||
sails.helpers.notifications.createOne.with({
|
||||
await sails.helpers.notifications.createMany.with({
|
||||
webhooks,
|
||||
values: {
|
||||
arrayOfValues: notifiableUserIds.map((userId) => ({
|
||||
userId,
|
||||
comment,
|
||||
type: mentionUserIdsSet.has(userId)
|
||||
@@ -140,13 +138,11 @@ module.exports = {
|
||||
},
|
||||
creatorUser: values.user,
|
||||
card: values.card,
|
||||
},
|
||||
})),
|
||||
project: inputs.project,
|
||||
board: inputs.board,
|
||||
list: inputs.list,
|
||||
}),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
if (values.user.subscribeToCardWhenCommenting) {
|
||||
let cardSubscription;
|
||||
|
||||
@@ -11,20 +11,17 @@ module.exports = {
|
||||
type: 'ref',
|
||||
required: true,
|
||||
},
|
||||
user: {
|
||||
type: 'ref',
|
||||
},
|
||||
},
|
||||
|
||||
fn(inputs) {
|
||||
const data = {
|
||||
...inputs.record,
|
||||
version: sails.config.custom.version,
|
||||
};
|
||||
if (inputs.user && inputs.user.role === User.Roles.ADMIN) {
|
||||
data.activeUsersLimit = sails.config.custom.activeUsersLimit;
|
||||
if (sails.config.custom.smtpHost) {
|
||||
return _.omit(inputs.record, Config.SMTP_FIELD_NAMES);
|
||||
}
|
||||
|
||||
return data;
|
||||
if (inputs.record.smtpPassword) {
|
||||
return _.omit(inputs.record, 'smtpPassword');
|
||||
}
|
||||
|
||||
return inputs.record;
|
||||
},
|
||||
};
|
||||
|
||||
53
server/api/helpers/config/update-main.js
Normal file
53
server/api/helpers/config/update-main.js
Normal file
@@ -0,0 +1,53 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
inputs: {
|
||||
values: {
|
||||
type: 'json',
|
||||
required: true,
|
||||
},
|
||||
actorUser: {
|
||||
type: 'ref',
|
||||
required: true,
|
||||
},
|
||||
request: {
|
||||
type: 'ref',
|
||||
},
|
||||
},
|
||||
|
||||
async fn(inputs) {
|
||||
const { values } = inputs;
|
||||
|
||||
const config = await Config.qm.updateOneMain(values);
|
||||
|
||||
const configRelatedUserIds = await sails.helpers.users.getAllIds(User.Roles.ADMIN);
|
||||
|
||||
configRelatedUserIds.forEach((userId) => {
|
||||
sails.sockets.broadcast(
|
||||
`user:${userId}`,
|
||||
'configUpdate',
|
||||
{
|
||||
item: sails.helpers.config.presentOne(config),
|
||||
},
|
||||
inputs.request,
|
||||
);
|
||||
});
|
||||
|
||||
const webhooks = await Webhook.qm.getAll();
|
||||
|
||||
// TODO: with prevData?
|
||||
sails.helpers.utils.sendWebhooks.with({
|
||||
webhooks,
|
||||
event: Webhook.Events.CONFIG_UPDATE,
|
||||
buildData: () => ({
|
||||
item: sails.helpers.config.presentOne(config),
|
||||
}),
|
||||
user: inputs.actorUser,
|
||||
});
|
||||
|
||||
return config;
|
||||
},
|
||||
};
|
||||
@@ -1,33 +0,0 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
/* eslint-disable no-underscore-dangle */
|
||||
|
||||
module.exports = {
|
||||
inputs: {
|
||||
record: {
|
||||
type: 'ref',
|
||||
required: true,
|
||||
},
|
||||
i18n: {
|
||||
type: 'ref',
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
async fn(inputs) {
|
||||
const { i18n } = inputs;
|
||||
|
||||
await sails.helpers.utils.sendNotifications.with({
|
||||
services: [_.pick(inputs.record, ['url', 'format'])],
|
||||
title: i18n.__('Test Title'),
|
||||
bodyByFormat: {
|
||||
text: i18n.__('This is a test text message!'),
|
||||
markdown: i18n.__('This is a *test* **markdown** `message`!'),
|
||||
html: i18n.__('This is a <i>test</i> <b>html</b> <code>message</code>'),
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
358
server/api/helpers/notifications/create-many.js
Normal file
358
server/api/helpers/notifications/create-many.js
Normal file
@@ -0,0 +1,358 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
const escapeMarkdown = require('escape-markdown');
|
||||
const escapeHtml = require('escape-html');
|
||||
|
||||
const { mentionMarkupToText } = require('../../../utils/mentions');
|
||||
|
||||
const buildTitle = (notification, t) => {
|
||||
switch (notification.type) {
|
||||
case Notification.Types.MOVE_CARD:
|
||||
return t('Card Moved');
|
||||
case Notification.Types.COMMENT_CARD:
|
||||
return t('New Comment');
|
||||
case Notification.Types.ADD_MEMBER_TO_CARD:
|
||||
return t('You Were Added to Card');
|
||||
case Notification.Types.MENTION_IN_COMMENT:
|
||||
return t('You Were Mentioned in Comment');
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const buildBodyByFormat = (board, card, notification, actorUser, t) => {
|
||||
const markdownCardLink = `[${escapeMarkdown(card.name)}](${sails.config.custom.baseUrl}/cards/${card.id})`;
|
||||
const htmlCardLink = `<a href="${sails.config.custom.baseUrl}/cards/${card.id}">${escapeHtml(card.name)}</a>`;
|
||||
|
||||
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);
|
||||
|
||||
return {
|
||||
text: t(
|
||||
'%s moved %s from %s to %s on %s',
|
||||
actorUser.name,
|
||||
card.name,
|
||||
fromListName,
|
||||
toListName,
|
||||
board.name,
|
||||
),
|
||||
markdown: t(
|
||||
'%s moved %s from %s to %s on %s',
|
||||
escapeMarkdown(actorUser.name),
|
||||
markdownCardLink,
|
||||
`**${escapeMarkdown(fromListName)}**`,
|
||||
`**${escapeMarkdown(toListName)}**`,
|
||||
escapeMarkdown(board.name),
|
||||
),
|
||||
html: t(
|
||||
'%s moved %s from %s to %s on %s',
|
||||
escapeHtml(actorUser.name),
|
||||
htmlCardLink,
|
||||
`<b>${escapeHtml(fromListName)}</b>`,
|
||||
`<b>${escapeHtml(toListName)}</b>`,
|
||||
escapeHtml(board.name),
|
||||
),
|
||||
};
|
||||
}
|
||||
case Notification.Types.COMMENT_CARD: {
|
||||
const commentText = _.truncate(mentionMarkupToText(notification.data.text));
|
||||
|
||||
return {
|
||||
text: `${t(
|
||||
'%s left a new comment to %s on %s',
|
||||
actorUser.name,
|
||||
card.name,
|
||||
board.name,
|
||||
)}:\n${commentText}`,
|
||||
markdown: `${t(
|
||||
'%s left a new comment to %s on %s',
|
||||
escapeMarkdown(actorUser.name),
|
||||
markdownCardLink,
|
||||
escapeMarkdown(board.name),
|
||||
)}:\n\n*${escapeMarkdown(commentText)}*`,
|
||||
html: `${t(
|
||||
'%s left a new comment to %s on %s',
|
||||
escapeHtml(actorUser.name),
|
||||
htmlCardLink,
|
||||
escapeHtml(board.name),
|
||||
)}:\n\n<i>${escapeHtml(commentText)}</i>`,
|
||||
};
|
||||
}
|
||||
case Notification.Types.ADD_MEMBER_TO_CARD:
|
||||
return {
|
||||
text: t('%s added you to %s on %s', actorUser.name, card.name, board.name),
|
||||
markdown: t(
|
||||
'%s added you to %s on %s',
|
||||
escapeMarkdown(actorUser.name),
|
||||
markdownCardLink,
|
||||
escapeMarkdown(board.name),
|
||||
),
|
||||
html: t(
|
||||
'%s added you to %s on %s',
|
||||
escapeHtml(actorUser.name),
|
||||
htmlCardLink,
|
||||
escapeHtml(board.name),
|
||||
),
|
||||
};
|
||||
case Notification.Types.MENTION_IN_COMMENT: {
|
||||
const commentText = _.truncate(mentionMarkupToText(notification.data.text));
|
||||
|
||||
return {
|
||||
text: `${t(
|
||||
'%s mentioned you in %s on %s',
|
||||
actorUser.name,
|
||||
card.name,
|
||||
board.name,
|
||||
)}:\n${commentText}`,
|
||||
markdown: `${t(
|
||||
'%s mentioned you in %s on %s',
|
||||
escapeMarkdown(actorUser.name),
|
||||
markdownCardLink,
|
||||
escapeMarkdown(board.name),
|
||||
)}:\n\n*${escapeMarkdown(commentText)}*`,
|
||||
html: `${t(
|
||||
'%s mentioned you in %s on %s',
|
||||
escapeHtml(actorUser.name),
|
||||
htmlCardLink,
|
||||
escapeHtml(board.name),
|
||||
)}:\n\n<i>${escapeHtml(commentText)}</i>`,
|
||||
};
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const buildAndSendNotifications = async (services, board, card, notification, actorUser, t) => {
|
||||
await sails.helpers.utils.sendNotifications(
|
||||
services,
|
||||
buildTitle(notification, t),
|
||||
buildBodyByFormat(board, card, notification, actorUser, t),
|
||||
);
|
||||
};
|
||||
|
||||
// TODO: use templates (views) to build html
|
||||
const buildEmail = (board, card, notification, actorUser, notifiableUser, t) => {
|
||||
const cardLink = `<a href="${sails.config.custom.baseUrl}/cards/${card.id}">${escapeHtml(card.name)}</a>`;
|
||||
const boardLink = `<a href="${sails.config.custom.baseUrl}/boards/${board.id}">${escapeHtml(board.name)}</a>`;
|
||||
|
||||
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);
|
||||
|
||||
html = `<p>${t(
|
||||
'%s moved %s from %s to %s on %s',
|
||||
escapeHtml(actorUser.name),
|
||||
cardLink,
|
||||
escapeHtml(fromListName),
|
||||
escapeHtml(toListName),
|
||||
boardLink,
|
||||
)}</p>`;
|
||||
|
||||
break;
|
||||
}
|
||||
case Notification.Types.COMMENT_CARD:
|
||||
html = `<p>${t(
|
||||
'%s left a new comment to %s on %s',
|
||||
escapeHtml(actorUser.name),
|
||||
cardLink,
|
||||
boardLink,
|
||||
)}</p><p>${escapeHtml(mentionMarkupToText(notification.data.text))}</p>`;
|
||||
|
||||
break;
|
||||
case Notification.Types.ADD_MEMBER_TO_CARD:
|
||||
html = `<p>${t(
|
||||
'%s added you to %s on %s',
|
||||
escapeHtml(actorUser.name),
|
||||
cardLink,
|
||||
boardLink,
|
||||
)}</p>`;
|
||||
|
||||
break;
|
||||
case Notification.Types.MENTION_IN_COMMENT:
|
||||
html = `<p>${t(
|
||||
'%s mentioned you in %s on %s',
|
||||
escapeHtml(actorUser.name),
|
||||
cardLink,
|
||||
boardLink,
|
||||
)}</p><p>${escapeHtml(mentionMarkupToText(notification.data.text))}</p>`;
|
||||
|
||||
break;
|
||||
default:
|
||||
return null; // TODO: throw error?
|
||||
}
|
||||
|
||||
return {
|
||||
html,
|
||||
to: notifiableUser.email,
|
||||
subject: buildTitle(notification, t),
|
||||
};
|
||||
};
|
||||
|
||||
const sendEmails = async (transporter, emails) => {
|
||||
await Promise.all(
|
||||
emails.map((email) =>
|
||||
sails.helpers.utils.sendEmail.with({
|
||||
...email,
|
||||
transporter,
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
transporter.close();
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
inputs: {
|
||||
arrayOfValues: {
|
||||
type: 'ref',
|
||||
required: true,
|
||||
},
|
||||
project: {
|
||||
type: 'ref',
|
||||
required: true,
|
||||
},
|
||||
board: {
|
||||
type: 'ref',
|
||||
required: true,
|
||||
},
|
||||
list: {
|
||||
type: 'ref',
|
||||
required: true,
|
||||
},
|
||||
webhooks: {
|
||||
type: 'ref',
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
async fn(inputs) {
|
||||
const { arrayOfValues } = inputs;
|
||||
|
||||
const ids = await sails.helpers.utils.generateIds(arrayOfValues.length);
|
||||
const valuesById = {};
|
||||
|
||||
const notifications = await Notification.qm.create(
|
||||
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,
|
||||
creatorUserId: values.creatorUser.id,
|
||||
boardId: values.card.boardId,
|
||||
cardId: values.card.id,
|
||||
};
|
||||
|
||||
if (isCommentRelated) {
|
||||
nextValues.commentId = values.comment.id;
|
||||
} else {
|
||||
nextValues.actionId = values.action.id;
|
||||
}
|
||||
|
||||
valuesById[id] = { ...nextValues }; // FIXME: hack
|
||||
return nextValues;
|
||||
}),
|
||||
);
|
||||
|
||||
notifications.forEach((notification) => {
|
||||
const values = valuesById[notification.id];
|
||||
|
||||
sails.sockets.broadcast(`user:${notification.userId}`, 'notificationCreate', {
|
||||
item: notification,
|
||||
included: {
|
||||
users: [sails.helpers.users.presentOne(values.creatorUser, {})], // FIXME: hack
|
||||
},
|
||||
});
|
||||
|
||||
sails.helpers.utils.sendWebhooks.with({
|
||||
webhooks: inputs.webhooks,
|
||||
event: Webhook.Events.NOTIFICATION_CREATE,
|
||||
buildData: () => ({
|
||||
item: notification,
|
||||
included: {
|
||||
projects: [inputs.project],
|
||||
boards: [inputs.board],
|
||||
lists: [inputs.list],
|
||||
cards: [values.card],
|
||||
...(notification.commentId
|
||||
? {
|
||||
comments: [values.comment],
|
||||
}
|
||||
: {
|
||||
actions: [values.action],
|
||||
}),
|
||||
},
|
||||
}),
|
||||
user: values.creatorUser,
|
||||
});
|
||||
});
|
||||
|
||||
const notificationsByUserId = _.groupBy(notifications, 'userId');
|
||||
const userIds = Object.keys(notificationsByUserId);
|
||||
|
||||
const notificationServices = await NotificationService.qm.getByUserIds(userIds);
|
||||
const { transporter } = await sails.helpers.utils.makeSmtpTransporter();
|
||||
|
||||
if (notificationServices.length > 0 || transporter) {
|
||||
const users = await User.qm.getByIds(userIds);
|
||||
const userById = _.keyBy(users, 'id');
|
||||
|
||||
const notificationServicesByUserId = _.groupBy(notificationServices, 'userId');
|
||||
|
||||
Object.keys(notificationsByUserId).forEach(async (userId) => {
|
||||
const notifiableUser = userById[userId];
|
||||
const t = sails.helpers.utils.makeTranslator(notifiableUser.language);
|
||||
|
||||
const emails = notificationsByUserId[userId].flatMap((notification) => {
|
||||
const values = valuesById[notification.id];
|
||||
|
||||
if (notificationServicesByUserId[userId]) {
|
||||
const services = notificationServicesByUserId[userId].map((notificationService) =>
|
||||
_.pick(notificationService, ['url', 'format']),
|
||||
);
|
||||
|
||||
buildAndSendNotifications(
|
||||
services,
|
||||
inputs.board,
|
||||
values.card,
|
||||
notification,
|
||||
values.creatorUser,
|
||||
t,
|
||||
);
|
||||
}
|
||||
|
||||
if (transporter) {
|
||||
return buildEmail(
|
||||
inputs.board,
|
||||
values.card,
|
||||
notification,
|
||||
values.creatorUser,
|
||||
notifiableUser,
|
||||
t,
|
||||
);
|
||||
}
|
||||
|
||||
return [];
|
||||
});
|
||||
|
||||
if (emails.length > 0) {
|
||||
sendEmails(transporter, emails);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return notifications;
|
||||
},
|
||||
};
|
||||
@@ -137,7 +137,15 @@ const buildAndSendNotifications = async (services, board, card, notification, ac
|
||||
};
|
||||
|
||||
// TODO: use templates (views) to build html
|
||||
const buildAndSendEmail = async (board, card, notification, actorUser, notifiableUser, t) => {
|
||||
const buildAndSendEmail = async (
|
||||
transporter,
|
||||
board,
|
||||
card,
|
||||
notification,
|
||||
actorUser,
|
||||
notifiableUser,
|
||||
t,
|
||||
) => {
|
||||
const cardLink = `<a href="${sails.config.custom.baseUrl}/cards/${card.id}">${escapeHtml(card.name)}</a>`;
|
||||
const boardLink = `<a href="${sails.config.custom.baseUrl}/boards/${board.id}">${escapeHtml(board.name)}</a>`;
|
||||
|
||||
@@ -164,7 +172,7 @@ const buildAndSendEmail = async (board, card, notification, actorUser, notifiabl
|
||||
escapeHtml(actorUser.name),
|
||||
cardLink,
|
||||
boardLink,
|
||||
)}</p><p>${escapeHtml(notification.data.text)}</p>`;
|
||||
)}</p><p>${escapeHtml(mentionMarkupToText(notification.data.text))}</p>`;
|
||||
|
||||
break;
|
||||
case Notification.Types.ADD_MEMBER_TO_CARD:
|
||||
@@ -182,7 +190,7 @@ const buildAndSendEmail = async (board, card, notification, actorUser, notifiabl
|
||||
escapeHtml(actorUser.name),
|
||||
cardLink,
|
||||
boardLink,
|
||||
)}</p><p>${escapeHtml(notification.data.text)}</p>`;
|
||||
)}</p><p>${escapeHtml(mentionMarkupToText(notification.data.text))}</p>`;
|
||||
|
||||
break;
|
||||
default:
|
||||
@@ -190,10 +198,13 @@ const buildAndSendEmail = async (board, card, notification, actorUser, notifiabl
|
||||
}
|
||||
|
||||
await sails.helpers.utils.sendEmail.with({
|
||||
transporter,
|
||||
html,
|
||||
to: notifiableUser.email,
|
||||
subject: buildTitle(notification, t),
|
||||
});
|
||||
|
||||
transporter.close();
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
@@ -223,10 +234,6 @@ module.exports = {
|
||||
async fn(inputs) {
|
||||
const { values } = inputs;
|
||||
|
||||
if (values.user) {
|
||||
values.userId = values.user.id;
|
||||
}
|
||||
|
||||
const isCommentRelated =
|
||||
values.type === Notification.Types.COMMENT_CARD ||
|
||||
values.type === Notification.Types.MENTION_IN_COMMENT;
|
||||
@@ -274,9 +281,10 @@ module.exports = {
|
||||
});
|
||||
|
||||
const notificationServices = await NotificationService.qm.getByUserId(notification.userId);
|
||||
const { transporter } = await sails.helpers.utils.makeSmtpTransporter();
|
||||
|
||||
if (notificationServices.length > 0 || sails.hooks.smtp.isEnabled()) {
|
||||
const notifiableUser = values.user || (await User.qm.getOneById(notification.userId));
|
||||
if (notificationServices.length > 0 || transporter) {
|
||||
const notifiableUser = await User.qm.getOneById(notification.userId);
|
||||
const t = sails.helpers.utils.makeTranslator(notifiableUser.language);
|
||||
|
||||
if (notificationServices.length > 0) {
|
||||
@@ -294,8 +302,9 @@ module.exports = {
|
||||
);
|
||||
}
|
||||
|
||||
if (sails.hooks.smtp.isEnabled()) {
|
||||
if (transporter) {
|
||||
buildAndSendEmail(
|
||||
transporter,
|
||||
inputs.board,
|
||||
values.card,
|
||||
notification,
|
||||
|
||||
61
server/api/helpers/utils/make-smtp-transporter.js
Normal file
61
server/api/helpers/utils/make-smtp-transporter.js
Normal file
@@ -0,0 +1,61 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
const nodemailer = require('nodemailer');
|
||||
|
||||
module.exports = {
|
||||
inputs: {
|
||||
defaultOptions: {
|
||||
type: 'json',
|
||||
},
|
||||
},
|
||||
|
||||
async fn(inputs) {
|
||||
let config;
|
||||
let sourceConfig;
|
||||
|
||||
if (sails.config.custom.smtpHost) {
|
||||
sourceConfig = sails.config.custom;
|
||||
} else {
|
||||
config = await Config.qm.getOneMain();
|
||||
|
||||
if (config.smtpHost) {
|
||||
sourceConfig = config;
|
||||
}
|
||||
}
|
||||
|
||||
if (!sourceConfig) {
|
||||
return {
|
||||
config,
|
||||
transporter: null,
|
||||
};
|
||||
}
|
||||
|
||||
const transporter = nodemailer.createTransport(
|
||||
{
|
||||
...inputs.defaultOptions,
|
||||
host: sourceConfig.smtpHost,
|
||||
port: sourceConfig.smtpPort,
|
||||
name: sourceConfig.smtpName,
|
||||
secure: sourceConfig.smtpSecure,
|
||||
auth: sourceConfig.smtpUser && {
|
||||
user: sourceConfig.smtpUser,
|
||||
pass: sourceConfig.smtpPassword,
|
||||
},
|
||||
tls: {
|
||||
rejectUnauthorized: sourceConfig.smtpTlsRejectUnauthorized,
|
||||
},
|
||||
},
|
||||
{
|
||||
from: sourceConfig.smtpFrom,
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
transporter,
|
||||
config,
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -5,6 +5,10 @@
|
||||
|
||||
module.exports = {
|
||||
inputs: {
|
||||
transporter: {
|
||||
type: 'ref',
|
||||
required: true,
|
||||
},
|
||||
to: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
@@ -20,13 +24,8 @@ module.exports = {
|
||||
},
|
||||
|
||||
async fn(inputs) {
|
||||
const transporter = sails.hooks.smtp.getTransporter(); // TODO: check if enabled?
|
||||
|
||||
try {
|
||||
const info = await transporter.sendMail({
|
||||
...inputs,
|
||||
from: sails.config.custom.smtpFrom,
|
||||
});
|
||||
const info = await inputs.transporter.sendMail(inputs);
|
||||
|
||||
sails.log.info(`Email sent: ${info.messageId}`);
|
||||
} catch (error) {
|
||||
|
||||
@@ -59,6 +59,28 @@ module.exports = function defineOidcHook(sails) {
|
||||
return client;
|
||||
},
|
||||
|
||||
async getBootstrap() {
|
||||
const instance = await this.getClient();
|
||||
|
||||
if (!instance) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const authorizationUrlParams = {
|
||||
scope: sails.config.custom.oidcScopes,
|
||||
};
|
||||
|
||||
if (!sails.config.custom.oidcUseDefaultResponseMode) {
|
||||
authorizationUrlParams.response_mode = sails.config.custom.oidcResponseMode;
|
||||
}
|
||||
|
||||
return {
|
||||
authorizationUrl: instance.authorizationUrl(authorizationUrlParams),
|
||||
endSessionUrl: instance.issuer.end_session_endpoint ? instance.endSessionUrl({}) : null,
|
||||
isEnforced: sails.config.custom.oidcEnforced,
|
||||
};
|
||||
},
|
||||
|
||||
isEnabled() {
|
||||
return !!sails.config.custom.oidcIssuer;
|
||||
},
|
||||
|
||||
@@ -9,6 +9,37 @@ const defaultFind = (criteria) => Notification.find(criteria).sort('id DESC');
|
||||
|
||||
/* Query methods */
|
||||
|
||||
const create = (arrayOfValues) =>
|
||||
sails.getDatastore().transaction(async (db) => {
|
||||
const notifications = await Notification.createEach(arrayOfValues).fetch().usingConnection(db);
|
||||
const userIds = sails.helpers.utils.mapRecords(notifications, 'userId', true, true);
|
||||
|
||||
if (userIds.length > 0) {
|
||||
const queryValues = [];
|
||||
const inValues = userIds.map((userId) => {
|
||||
queryValues.push(userId);
|
||||
return `$${queryValues.length}`;
|
||||
});
|
||||
|
||||
queryValues.push(LIMIT);
|
||||
|
||||
const query = `
|
||||
WITH exceeded_notification AS (
|
||||
SELECT id, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY id DESC) AS rank
|
||||
FROM notification
|
||||
WHERE user_id IN (${inValues.join(', ')}) AND is_read = FALSE
|
||||
)
|
||||
UPDATE notification
|
||||
SET is_read = TRUE
|
||||
WHERE id IN (SELECT id FROM exceeded_notification WHERE rank > $${queryValues.length})
|
||||
`;
|
||||
|
||||
await sails.sendNativeQuery(query, queryValues).usingConnection(db);
|
||||
}
|
||||
|
||||
return notifications;
|
||||
});
|
||||
|
||||
const createOne = (values) => {
|
||||
if (values.userId) {
|
||||
return sails.getDatastore().transaction(async (db) => {
|
||||
@@ -26,7 +57,7 @@ const createOne = (values) => {
|
||||
)
|
||||
UPDATE notification
|
||||
SET is_read = TRUE
|
||||
WHERE id in (SELECT id FROM exceeded_notification)
|
||||
WHERE id IN (SELECT id FROM exceeded_notification)
|
||||
`;
|
||||
|
||||
await sails.sendNativeQuery(query, [values.userId, LIMIT]).usingConnection(db);
|
||||
@@ -66,6 +97,7 @@ const updateOne = (criteria, values) => Notification.updateOne(criteria).set({ .
|
||||
const delete_ = (criteria) => Notification.destroy(criteria).fetch();
|
||||
|
||||
module.exports = {
|
||||
create,
|
||||
createOne,
|
||||
getByIds,
|
||||
getUnreadByUserId,
|
||||
|
||||
@@ -14,6 +14,11 @@ const getByUserId = (userId) =>
|
||||
userId,
|
||||
});
|
||||
|
||||
const getByUserIds = (userIds) =>
|
||||
defaultFind({
|
||||
userId: userIds,
|
||||
});
|
||||
|
||||
const getByBoardId = (boardId) =>
|
||||
defaultFind({
|
||||
boardId,
|
||||
@@ -36,6 +41,7 @@ const deleteOne = (criteria) => NotificationService.destroyOne(criteria);
|
||||
module.exports = {
|
||||
createOne,
|
||||
getByUserId,
|
||||
getByUserIds,
|
||||
getByBoardId,
|
||||
getByBoardIds,
|
||||
getOneById,
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
/**
|
||||
* smtp hook
|
||||
*
|
||||
* @description :: A hook definition. Extends Sails by adding shadow routes, implicit actions,
|
||||
* and/or initialization logic.
|
||||
* @docs :: https://sailsjs.com/docs/concepts/extending-sails/hooks
|
||||
*/
|
||||
|
||||
const nodemailer = require('nodemailer');
|
||||
|
||||
module.exports = function defineSmtpHook(sails) {
|
||||
let transporter = null;
|
||||
|
||||
return {
|
||||
/**
|
||||
* Runs when this Sails app loads/lifts.
|
||||
*/
|
||||
|
||||
async initialize() {
|
||||
if (!this.isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
sails.log.info('Initializing custom hook (`smtp`)');
|
||||
|
||||
transporter = nodemailer.createTransport({
|
||||
pool: true,
|
||||
host: sails.config.custom.smtpHost,
|
||||
port: sails.config.custom.smtpPort,
|
||||
name: sails.config.custom.smtpName,
|
||||
secure: sails.config.custom.smtpSecure,
|
||||
auth: sails.config.custom.smtpUser && {
|
||||
user: sails.config.custom.smtpUser,
|
||||
pass: sails.config.custom.smtpPassword,
|
||||
},
|
||||
tls: {
|
||||
rejectUnauthorized: sails.config.custom.smtpTlsRejectUnauthorized,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
getTransporter() {
|
||||
return transporter;
|
||||
},
|
||||
|
||||
isEnabled() {
|
||||
return !!sails.config.custom.smtpHost;
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -17,54 +17,131 @@
|
||||
* Config:
|
||||
* type: object
|
||||
* required:
|
||||
* - version
|
||||
* - oidc
|
||||
* - id
|
||||
* - isInitialized
|
||||
* properties:
|
||||
* version:
|
||||
* id:
|
||||
* type: string
|
||||
* description: Current version of the PLANKA application
|
||||
* example: 2.0.0
|
||||
* activeUsersLimit:
|
||||
* description: Unique identifier for the config (always set to '1')
|
||||
* example: 1
|
||||
* smtpHost:
|
||||
* type: string
|
||||
* nullable: true
|
||||
* description: Hostname or IP address of the SMTP server
|
||||
* example: smtp.example.com
|
||||
* smtpPort:
|
||||
* type: number
|
||||
* nullable: true
|
||||
* description: Maximum number of active users allowed (conditionally added for admins if configured)
|
||||
* example: 100
|
||||
* oidc:
|
||||
* type: object
|
||||
* required:
|
||||
* - authorizationUrl
|
||||
* - endSessionUrl
|
||||
* - isEnforced
|
||||
* nullable: true
|
||||
* description: OpenID Connect configuration (null if not configured)
|
||||
* properties:
|
||||
* authorizationUrl:
|
||||
* description: Port number of the SMTP server
|
||||
* example: 587
|
||||
* smtpName:
|
||||
* type: string
|
||||
* format: uri
|
||||
* description: OIDC authorization URL for initiating authentication
|
||||
* example: https://oidc.example.com/auth
|
||||
* endSessionUrl:
|
||||
* type: string
|
||||
* format: uri
|
||||
* nullable: true
|
||||
* description: OIDC end session URL for logout (null if not supported by provider)
|
||||
* example: https://oidc.example.com/logout
|
||||
* isEnforced:
|
||||
* description: Client hostname used in the EHLO command for SMTP
|
||||
* example: localhost
|
||||
* smtpSecure:
|
||||
* type: boolean
|
||||
* description: Whether OIDC authentication is enforced (users must use OIDC to login)
|
||||
* description: Whether to use a secure connection for SMTP
|
||||
* example: false
|
||||
* smtpTlsRejectUnauthorized:
|
||||
* type: boolean
|
||||
* description: Whether to reject unauthorized or self-signed TLS certificates for SMTP connections
|
||||
* example: true
|
||||
* smtpUser:
|
||||
* type: string
|
||||
* nullable: true
|
||||
* description: Username for authenticating with the SMTP server
|
||||
* example: no-reply@example.com
|
||||
* smtpPassword:
|
||||
* type: string
|
||||
* nullable: true
|
||||
* description: Password for authenticating with the SMTP server
|
||||
* example: SecurePassword123!
|
||||
* smtpFrom:
|
||||
* type: string
|
||||
* nullable: true
|
||||
* description: Default "from" used for outgoing SMTP emails
|
||||
* example: no-reply@example.com
|
||||
* isInitialized:
|
||||
* type: boolean
|
||||
* description: Whether the PLANKA instance has been initialized
|
||||
* example: true
|
||||
* createdAt:
|
||||
* type: string
|
||||
* format: date-time
|
||||
* nullable: true
|
||||
* description: When the config was created
|
||||
* example: 2024-01-01T00:00:00.000Z
|
||||
* updatedAt:
|
||||
* type: string
|
||||
* format: date-time
|
||||
* nullable: true
|
||||
* description: When the config was last updated
|
||||
* example: 2024-01-01T00:00:00.000Z
|
||||
*/
|
||||
|
||||
const MAIN_ID = '1';
|
||||
|
||||
const SMTP_FIELD_NAMES = [
|
||||
'smtpHost',
|
||||
'smtpPort',
|
||||
'smtpName',
|
||||
'smtpSecure',
|
||||
'smtpTlsRejectUnauthorized',
|
||||
'smtpUser',
|
||||
'smtpPassword',
|
||||
'smtpFrom',
|
||||
];
|
||||
|
||||
module.exports = {
|
||||
MAIN_ID,
|
||||
SMTP_FIELD_NAMES,
|
||||
|
||||
attributes: {
|
||||
// ╔═╗╦═╗╦╔╦╗╦╔╦╗╦╦ ╦╔═╗╔═╗
|
||||
// ╠═╝╠╦╝║║║║║ ║ ║╚╗╔╝║╣ ╚═╗
|
||||
// ╩ ╩╚═╩╩ ╩╩ ╩ ╩ ╚╝ ╚═╝╚═╝
|
||||
|
||||
smtpHost: {
|
||||
type: 'string',
|
||||
allowNull: true,
|
||||
columnName: 'smtp_host',
|
||||
},
|
||||
smtpPort: {
|
||||
type: 'number',
|
||||
allowNull: true,
|
||||
columnName: 'smtp_port',
|
||||
},
|
||||
smtpName: {
|
||||
type: 'string',
|
||||
allowNull: true,
|
||||
columnName: 'smtp_name',
|
||||
},
|
||||
smtpSecure: {
|
||||
type: 'boolean',
|
||||
required: true,
|
||||
columnName: 'smtp_secure',
|
||||
},
|
||||
smtpTlsRejectUnauthorized: {
|
||||
type: 'boolean',
|
||||
required: true,
|
||||
columnName: 'smtp_tls_reject_unauthorized',
|
||||
},
|
||||
smtpUser: {
|
||||
type: 'string',
|
||||
allowNull: true,
|
||||
columnName: 'smtp_user',
|
||||
},
|
||||
smtpPassword: {
|
||||
type: 'string',
|
||||
allowNull: true,
|
||||
columnName: 'smtp_password',
|
||||
},
|
||||
smtpFrom: {
|
||||
type: 'string',
|
||||
allowNull: true,
|
||||
columnName: 'smtp_from',
|
||||
},
|
||||
isInitialized: {
|
||||
type: 'boolean',
|
||||
required: true,
|
||||
|
||||
@@ -107,6 +107,8 @@ const Events = {
|
||||
COMMENT_UPDATE: 'commentUpdate',
|
||||
COMMENT_DELETE: 'commentDelete',
|
||||
|
||||
CONFIG_UPDATE: 'configUpdate',
|
||||
|
||||
CUSTOM_FIELD_CREATE: 'customFieldCreate',
|
||||
CUSTOM_FIELD_UPDATE: 'customFieldUpdate',
|
||||
CUSTOM_FIELD_DELETE: 'customFieldDelete',
|
||||
|
||||
@@ -102,10 +102,10 @@ module.exports.custom = {
|
||||
smtpPort: process.env.SMTP_PORT || 587,
|
||||
smtpName: process.env.SMTP_NAME,
|
||||
smtpSecure: process.env.SMTP_SECURE === 'true',
|
||||
smtpTlsRejectUnauthorized: process.env.SMTP_TLS_REJECT_UNAUTHORIZED !== 'false',
|
||||
smtpUser: process.env.SMTP_USER,
|
||||
smtpPassword: process.env.SMTP_PASSWORD,
|
||||
smtpFrom: process.env.SMTP_FROM,
|
||||
smtpTlsRejectUnauthorized: process.env.SMTP_TLS_REJECT_UNAUTHORIZED !== 'false',
|
||||
|
||||
gravatarBaseUrl: process.env.GRAVATAR_BASE_URL,
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user