mirror of
https://github.com/plankanban/planka.git
synced 2025-07-18 20:59:44 +02:00
Merge branch 'plankanban:master' into master
This commit is contained in:
commit
dd91d2b9d4
26 changed files with 467 additions and 333 deletions
|
@ -13,26 +13,26 @@ env:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-and-push-docker-base-image:
|
build-and-push-docker-base-image:
|
||||||
runs-on: ubuntu-latest
|
runs-on: self-hosted
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v1
|
uses: docker/setup-qemu-action@v2
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v1
|
uses: docker/setup-buildx-action@v2
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@v1
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v4
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||||
|
|
34
.github/workflows/build-and-push-docker-image-dev.yml
vendored
Normal file
34
.github/workflows/build-and-push-docker-image-dev.yml
vendored
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
name: Build and push Docker DEV image
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [master]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-push-docker-image-dev:
|
||||||
|
runs-on: self-hosted
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v2
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.repository_owner }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v4
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
ghcr.io/plankanban/planka:dev
|
|
@ -6,19 +6,19 @@ on:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-and-push-docker-image:
|
build-and-push-docker-image:
|
||||||
runs-on: ubuntu-latest
|
runs-on: self-hosted
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v1
|
uses: docker/setup-qemu-action@v2
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v1
|
uses: docker/setup-buildx-action@v2
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@v1
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
|
@ -32,7 +32,7 @@ jobs:
|
||||||
script: return context.payload.release.tag_name.replace('v', '')
|
script: return context.payload.release.tag_name.replace('v', '')
|
||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v4
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||||
|
|
4
.github/workflows/helm-chart-release.yml
vendored
4
.github/workflows/helm-chart-release.yml
vendored
|
@ -13,10 +13,10 @@ jobs:
|
||||||
# see: https://docs.github.com/en/actions/security-guides/automatic-token-authentication#modifying-the-permissions-for-the-github_token
|
# see: https://docs.github.com/en/actions/security-guides/automatic-token-authentication#modifying-the-permissions-for-the-github_token
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
runs-on: ubuntu-latest
|
runs-on: self-hosted
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,8 @@ WORKDIR /app
|
||||||
COPY server/package.json server/package-lock.json .
|
COPY server/package.json server/package-lock.json .
|
||||||
|
|
||||||
RUN npm install npm@latest --global \
|
RUN npm install npm@latest --global \
|
||||||
&& npm clean-install --omit=dev
|
&& npm install pnpm --global \
|
||||||
|
&& pnpm install --prod
|
||||||
|
|
||||||
FROM node:lts AS client
|
FROM node:lts AS client
|
||||||
|
|
||||||
|
@ -14,7 +15,8 @@ WORKDIR /app
|
||||||
COPY client/package.json client/package-lock.json .
|
COPY client/package.json client/package-lock.json .
|
||||||
|
|
||||||
RUN npm install npm@latest --global \
|
RUN npm install npm@latest --global \
|
||||||
&& npm clean-install --omit=dev
|
&& npm install pnpm --global \
|
||||||
|
&& pnpm install --prod
|
||||||
|
|
||||||
COPY client .
|
COPY client .
|
||||||
RUN DISABLE_ESLINT_PLUGIN=true npm run build
|
RUN DISABLE_ESLINT_PLUGIN=true npm run build
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { dequal } from 'dequal';
|
import { dequal } from 'dequal';
|
||||||
|
import omit from 'lodash/omit';
|
||||||
import pickBy from 'lodash/pickBy';
|
import pickBy from 'lodash/pickBy';
|
||||||
import React, { useCallback, useMemo, useRef } from 'react';
|
import React, { useCallback, useMemo, useRef } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
@ -9,7 +10,7 @@ import { useForm } from '../../hooks';
|
||||||
|
|
||||||
import styles from './UserInformationEdit.module.scss';
|
import styles from './UserInformationEdit.module.scss';
|
||||||
|
|
||||||
const UserInformationEdit = React.memo(({ defaultData, onUpdate }) => {
|
const UserInformationEdit = React.memo(({ defaultData, isNameEditable, onUpdate }) => {
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
|
|
||||||
const [data, handleFieldChange] = useForm(() => ({
|
const [data, handleFieldChange] = useForm(() => ({
|
||||||
|
@ -32,13 +33,17 @@ const UserInformationEdit = React.memo(({ defaultData, onUpdate }) => {
|
||||||
const nameField = useRef(null);
|
const nameField = useRef(null);
|
||||||
|
|
||||||
const handleSubmit = useCallback(() => {
|
const handleSubmit = useCallback(() => {
|
||||||
if (!cleanData.name) {
|
if (isNameEditable) {
|
||||||
nameField.current.select();
|
if (!cleanData.name) {
|
||||||
return;
|
nameField.current.select();
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
onUpdate(cleanData);
|
onUpdate(cleanData);
|
||||||
}, [onUpdate, cleanData]);
|
} else {
|
||||||
|
onUpdate(omit(cleanData, 'name'));
|
||||||
|
}
|
||||||
|
}, [isNameEditable, onUpdate, cleanData]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form onSubmit={handleSubmit}>
|
<Form onSubmit={handleSubmit}>
|
||||||
|
@ -48,6 +53,7 @@ const UserInformationEdit = React.memo(({ defaultData, onUpdate }) => {
|
||||||
ref={nameField}
|
ref={nameField}
|
||||||
name="name"
|
name="name"
|
||||||
value={data.name}
|
value={data.name}
|
||||||
|
disabled={!isNameEditable}
|
||||||
className={styles.field}
|
className={styles.field}
|
||||||
onChange={handleFieldChange}
|
onChange={handleFieldChange}
|
||||||
/>
|
/>
|
||||||
|
@ -74,6 +80,7 @@ const UserInformationEdit = React.memo(({ defaultData, onUpdate }) => {
|
||||||
|
|
||||||
UserInformationEdit.propTypes = {
|
UserInformationEdit.propTypes = {
|
||||||
defaultData: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
defaultData: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||||
|
isNameEditable: PropTypes.bool.isRequired,
|
||||||
onUpdate: PropTypes.func.isRequired,
|
onUpdate: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -5,33 +5,40 @@ import { Popup } from '../lib/custom-ui';
|
||||||
|
|
||||||
import UserInformationEdit from './UserInformationEdit';
|
import UserInformationEdit from './UserInformationEdit';
|
||||||
|
|
||||||
const UserInformationEditStep = React.memo(({ defaultData, onUpdate, onBack, onClose }) => {
|
const UserInformationEditStep = React.memo(
|
||||||
const [t] = useTranslation();
|
({ defaultData, isNameEditable, onUpdate, onBack, onClose }) => {
|
||||||
|
const [t] = useTranslation();
|
||||||
|
|
||||||
const handleUpdate = useCallback(
|
const handleUpdate = useCallback(
|
||||||
(data) => {
|
(data) => {
|
||||||
onUpdate(data);
|
onUpdate(data);
|
||||||
onClose();
|
onClose();
|
||||||
},
|
},
|
||||||
[onUpdate, onClose],
|
[onUpdate, onClose],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Popup.Header onBack={onBack}>
|
<Popup.Header onBack={onBack}>
|
||||||
{t('common.editInformation', {
|
{t('common.editInformation', {
|
||||||
context: 'title',
|
context: 'title',
|
||||||
})}
|
})}
|
||||||
</Popup.Header>
|
</Popup.Header>
|
||||||
<Popup.Content>
|
<Popup.Content>
|
||||||
<UserInformationEdit defaultData={defaultData} onUpdate={handleUpdate} />
|
<UserInformationEdit
|
||||||
</Popup.Content>
|
defaultData={defaultData}
|
||||||
</>
|
isNameEditable={isNameEditable}
|
||||||
);
|
onUpdate={handleUpdate}
|
||||||
});
|
/>
|
||||||
|
</Popup.Content>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
UserInformationEditStep.propTypes = {
|
UserInformationEditStep.propTypes = {
|
||||||
defaultData: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
defaultData: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||||
|
isNameEditable: PropTypes.bool.isRequired,
|
||||||
onUpdate: PropTypes.func.isRequired,
|
onUpdate: PropTypes.func.isRequired,
|
||||||
onBack: PropTypes.func,
|
onBack: PropTypes.func,
|
||||||
onClose: PropTypes.func.isRequired,
|
onClose: PropTypes.func.isRequired,
|
||||||
|
|
|
@ -23,6 +23,7 @@ const AccountPane = React.memo(
|
||||||
phone,
|
phone,
|
||||||
organization,
|
organization,
|
||||||
language,
|
language,
|
||||||
|
isLocked,
|
||||||
isAvatarUpdating,
|
isAvatarUpdating,
|
||||||
usernameUpdateForm,
|
usernameUpdateForm,
|
||||||
emailUpdateForm,
|
emailUpdateForm,
|
||||||
|
@ -74,6 +75,7 @@ const AccountPane = React.memo(
|
||||||
phone,
|
phone,
|
||||||
organization,
|
organization,
|
||||||
}}
|
}}
|
||||||
|
isNameEditable={!isLocked}
|
||||||
onUpdate={onUpdate}
|
onUpdate={onUpdate}
|
||||||
/>
|
/>
|
||||||
<Divider horizontal section>
|
<Divider horizontal section>
|
||||||
|
@ -102,63 +104,67 @@ const AccountPane = React.memo(
|
||||||
value={language || 'auto'}
|
value={language || 'auto'}
|
||||||
onChange={handleLanguageChange}
|
onChange={handleLanguageChange}
|
||||||
/>
|
/>
|
||||||
<Divider horizontal section>
|
{!isLocked && (
|
||||||
<Header as="h4">
|
<>
|
||||||
{t('common.authentication', {
|
<Divider horizontal section>
|
||||||
context: 'title',
|
<Header as="h4">
|
||||||
})}
|
{t('common.authentication', {
|
||||||
</Header>
|
context: 'title',
|
||||||
</Divider>
|
})}
|
||||||
<div className={styles.action}>
|
</Header>
|
||||||
<UserUsernameEditPopup
|
</Divider>
|
||||||
usePasswordConfirmation
|
<div className={styles.action}>
|
||||||
defaultData={usernameUpdateForm.data}
|
<UserUsernameEditPopup
|
||||||
username={username}
|
usePasswordConfirmation
|
||||||
isSubmitting={usernameUpdateForm.isSubmitting}
|
defaultData={usernameUpdateForm.data}
|
||||||
error={usernameUpdateForm.error}
|
username={username}
|
||||||
onUpdate={onUsernameUpdate}
|
isSubmitting={usernameUpdateForm.isSubmitting}
|
||||||
onMessageDismiss={onUsernameUpdateMessageDismiss}
|
error={usernameUpdateForm.error}
|
||||||
>
|
onUpdate={onUsernameUpdate}
|
||||||
<Button className={styles.actionButton}>
|
onMessageDismiss={onUsernameUpdateMessageDismiss}
|
||||||
{t('action.editUsername', {
|
>
|
||||||
context: 'title',
|
<Button className={styles.actionButton}>
|
||||||
})}
|
{t('action.editUsername', {
|
||||||
</Button>
|
context: 'title',
|
||||||
</UserUsernameEditPopup>
|
})}
|
||||||
</div>
|
</Button>
|
||||||
<div className={styles.action}>
|
</UserUsernameEditPopup>
|
||||||
<UserEmailEditPopup
|
</div>
|
||||||
usePasswordConfirmation
|
<div className={styles.action}>
|
||||||
defaultData={emailUpdateForm.data}
|
<UserEmailEditPopup
|
||||||
email={email}
|
usePasswordConfirmation
|
||||||
isSubmitting={emailUpdateForm.isSubmitting}
|
defaultData={emailUpdateForm.data}
|
||||||
error={emailUpdateForm.error}
|
email={email}
|
||||||
onUpdate={onEmailUpdate}
|
isSubmitting={emailUpdateForm.isSubmitting}
|
||||||
onMessageDismiss={onEmailUpdateMessageDismiss}
|
error={emailUpdateForm.error}
|
||||||
>
|
onUpdate={onEmailUpdate}
|
||||||
<Button className={styles.actionButton}>
|
onMessageDismiss={onEmailUpdateMessageDismiss}
|
||||||
{t('action.editEmail', {
|
>
|
||||||
context: 'title',
|
<Button className={styles.actionButton}>
|
||||||
})}
|
{t('action.editEmail', {
|
||||||
</Button>
|
context: 'title',
|
||||||
</UserEmailEditPopup>
|
})}
|
||||||
</div>
|
</Button>
|
||||||
<div className={styles.action}>
|
</UserEmailEditPopup>
|
||||||
<UserPasswordEditPopup
|
</div>
|
||||||
usePasswordConfirmation
|
<div className={styles.action}>
|
||||||
defaultData={passwordUpdateForm.data}
|
<UserPasswordEditPopup
|
||||||
isSubmitting={passwordUpdateForm.isSubmitting}
|
usePasswordConfirmation
|
||||||
error={passwordUpdateForm.error}
|
defaultData={passwordUpdateForm.data}
|
||||||
onUpdate={onPasswordUpdate}
|
isSubmitting={passwordUpdateForm.isSubmitting}
|
||||||
onMessageDismiss={onPasswordUpdateMessageDismiss}
|
error={passwordUpdateForm.error}
|
||||||
>
|
onUpdate={onPasswordUpdate}
|
||||||
<Button className={styles.actionButton}>
|
onMessageDismiss={onPasswordUpdateMessageDismiss}
|
||||||
{t('action.editPassword', {
|
>
|
||||||
context: 'title',
|
<Button className={styles.actionButton}>
|
||||||
})}
|
{t('action.editPassword', {
|
||||||
</Button>
|
context: 'title',
|
||||||
</UserPasswordEditPopup>
|
})}
|
||||||
</div>
|
</Button>
|
||||||
|
</UserPasswordEditPopup>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</Tab.Pane>
|
</Tab.Pane>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -172,6 +178,7 @@ AccountPane.propTypes = {
|
||||||
phone: PropTypes.string,
|
phone: PropTypes.string,
|
||||||
organization: PropTypes.string,
|
organization: PropTypes.string,
|
||||||
language: PropTypes.string,
|
language: PropTypes.string,
|
||||||
|
isLocked: PropTypes.bool.isRequired,
|
||||||
isAvatarUpdating: PropTypes.bool.isRequired,
|
isAvatarUpdating: PropTypes.bool.isRequired,
|
||||||
/* eslint-disable react/forbid-prop-types */
|
/* eslint-disable react/forbid-prop-types */
|
||||||
usernameUpdateForm: PropTypes.object.isRequired,
|
usernameUpdateForm: PropTypes.object.isRequired,
|
||||||
|
|
|
@ -16,6 +16,7 @@ const UserSettingsModal = React.memo(
|
||||||
phone,
|
phone,
|
||||||
organization,
|
organization,
|
||||||
language,
|
language,
|
||||||
|
isLocked,
|
||||||
subscribeToOwnCards,
|
subscribeToOwnCards,
|
||||||
isAvatarUpdating,
|
isAvatarUpdating,
|
||||||
usernameUpdateForm,
|
usernameUpdateForm,
|
||||||
|
@ -48,6 +49,7 @@ const UserSettingsModal = React.memo(
|
||||||
phone={phone}
|
phone={phone}
|
||||||
organization={organization}
|
organization={organization}
|
||||||
language={language}
|
language={language}
|
||||||
|
isLocked={isLocked}
|
||||||
isAvatarUpdating={isAvatarUpdating}
|
isAvatarUpdating={isAvatarUpdating}
|
||||||
usernameUpdateForm={usernameUpdateForm}
|
usernameUpdateForm={usernameUpdateForm}
|
||||||
emailUpdateForm={emailUpdateForm}
|
emailUpdateForm={emailUpdateForm}
|
||||||
|
@ -104,6 +106,7 @@ UserSettingsModal.propTypes = {
|
||||||
phone: PropTypes.string,
|
phone: PropTypes.string,
|
||||||
organization: PropTypes.string,
|
organization: PropTypes.string,
|
||||||
language: PropTypes.string,
|
language: PropTypes.string,
|
||||||
|
isLocked: PropTypes.bool.isRequired,
|
||||||
subscribeToOwnCards: PropTypes.bool.isRequired,
|
subscribeToOwnCards: PropTypes.bool.isRequired,
|
||||||
isAvatarUpdating: PropTypes.bool.isRequired,
|
isAvatarUpdating: PropTypes.bool.isRequired,
|
||||||
/* eslint-disable react/forbid-prop-types */
|
/* eslint-disable react/forbid-prop-types */
|
||||||
|
|
|
@ -64,6 +64,7 @@ const ActionsStep = React.memo(
|
||||||
return (
|
return (
|
||||||
<UserInformationEditStep
|
<UserInformationEditStep
|
||||||
defaultData={pick(user, ['name', 'phone', 'organization'])}
|
defaultData={pick(user, ['name', 'phone', 'organization'])}
|
||||||
|
isNameEditable={!user.isLocked}
|
||||||
onUpdate={onUpdate}
|
onUpdate={onUpdate}
|
||||||
onBack={handleBack}
|
onBack={handleBack}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
|
@ -135,26 +136,30 @@ const ActionsStep = React.memo(
|
||||||
context: 'title',
|
context: 'title',
|
||||||
})}
|
})}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item className={styles.menuItem} onClick={handleEditUsernameClick}>
|
{!user.isLocked && (
|
||||||
{t('action.editUsername', {
|
<>
|
||||||
context: 'title',
|
<Menu.Item className={styles.menuItem} onClick={handleEditUsernameClick}>
|
||||||
})}
|
{t('action.editUsername', {
|
||||||
</Menu.Item>
|
context: 'title',
|
||||||
<Menu.Item className={styles.menuItem} onClick={handleEditEmailClick}>
|
})}
|
||||||
{t('action.editEmail', {
|
</Menu.Item>
|
||||||
context: 'title',
|
<Menu.Item className={styles.menuItem} onClick={handleEditEmailClick}>
|
||||||
})}
|
{t('action.editEmail', {
|
||||||
</Menu.Item>
|
context: 'title',
|
||||||
<Menu.Item className={styles.menuItem} onClick={handleEditPasswordClick}>
|
})}
|
||||||
{t('action.editPassword', {
|
</Menu.Item>
|
||||||
context: 'title',
|
<Menu.Item className={styles.menuItem} onClick={handleEditPasswordClick}>
|
||||||
})}
|
{t('action.editPassword', {
|
||||||
</Menu.Item>
|
context: 'title',
|
||||||
<Menu.Item className={styles.menuItem} onClick={handleDeleteClick}>
|
})}
|
||||||
{t('action.deleteUser', {
|
</Menu.Item>
|
||||||
context: 'title',
|
<Menu.Item className={styles.menuItem} onClick={handleDeleteClick}>
|
||||||
})}
|
{t('action.deleteUser', {
|
||||||
</Menu.Item>
|
context: 'title',
|
||||||
|
})}
|
||||||
|
</Menu.Item>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</Menu>
|
</Menu>
|
||||||
</Popup.Content>
|
</Popup.Content>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -17,6 +17,7 @@ const Item = React.memo(
|
||||||
organization,
|
organization,
|
||||||
phone,
|
phone,
|
||||||
isAdmin,
|
isAdmin,
|
||||||
|
isLocked,
|
||||||
emailUpdateForm,
|
emailUpdateForm,
|
||||||
passwordUpdateForm,
|
passwordUpdateForm,
|
||||||
usernameUpdateForm,
|
usernameUpdateForm,
|
||||||
|
@ -46,7 +47,7 @@ const Item = React.memo(
|
||||||
<Table.Cell>{username || '-'}</Table.Cell>
|
<Table.Cell>{username || '-'}</Table.Cell>
|
||||||
<Table.Cell>{email}</Table.Cell>
|
<Table.Cell>{email}</Table.Cell>
|
||||||
<Table.Cell>
|
<Table.Cell>
|
||||||
<Radio toggle checked={isAdmin} onChange={handleIsAdminChange} />
|
<Radio toggle checked={isAdmin} disabled={isLocked} onChange={handleIsAdminChange} />
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
<Table.Cell textAlign="right">
|
<Table.Cell textAlign="right">
|
||||||
<ActionsPopup
|
<ActionsPopup
|
||||||
|
@ -57,6 +58,7 @@ const Item = React.memo(
|
||||||
organization,
|
organization,
|
||||||
phone,
|
phone,
|
||||||
isAdmin,
|
isAdmin,
|
||||||
|
isLocked,
|
||||||
emailUpdateForm,
|
emailUpdateForm,
|
||||||
passwordUpdateForm,
|
passwordUpdateForm,
|
||||||
usernameUpdateForm,
|
usernameUpdateForm,
|
||||||
|
@ -88,6 +90,7 @@ Item.propTypes = {
|
||||||
organization: PropTypes.string,
|
organization: PropTypes.string,
|
||||||
phone: PropTypes.string,
|
phone: PropTypes.string,
|
||||||
isAdmin: PropTypes.bool.isRequired,
|
isAdmin: PropTypes.bool.isRequired,
|
||||||
|
isLocked: PropTypes.bool.isRequired,
|
||||||
/* eslint-disable react/forbid-prop-types */
|
/* eslint-disable react/forbid-prop-types */
|
||||||
emailUpdateForm: PropTypes.object.isRequired,
|
emailUpdateForm: PropTypes.object.isRequired,
|
||||||
passwordUpdateForm: PropTypes.object.isRequired,
|
passwordUpdateForm: PropTypes.object.isRequired,
|
||||||
|
|
|
@ -110,6 +110,7 @@ const UsersModal = React.memo(
|
||||||
organization={item.organization}
|
organization={item.organization}
|
||||||
phone={item.phone}
|
phone={item.phone}
|
||||||
isAdmin={item.isAdmin}
|
isAdmin={item.isAdmin}
|
||||||
|
isLocked={item.isLocked}
|
||||||
emailUpdateForm={item.emailUpdateForm}
|
emailUpdateForm={item.emailUpdateForm}
|
||||||
passwordUpdateForm={item.passwordUpdateForm}
|
passwordUpdateForm={item.passwordUpdateForm}
|
||||||
usernameUpdateForm={item.usernameUpdateForm}
|
usernameUpdateForm={item.usernameUpdateForm}
|
||||||
|
|
|
@ -14,6 +14,7 @@ const mapStateToProps = (state) => {
|
||||||
phone,
|
phone,
|
||||||
organization,
|
organization,
|
||||||
language,
|
language,
|
||||||
|
isLocked,
|
||||||
subscribeToOwnCards,
|
subscribeToOwnCards,
|
||||||
isAvatarUpdating,
|
isAvatarUpdating,
|
||||||
emailUpdateForm,
|
emailUpdateForm,
|
||||||
|
@ -29,6 +30,7 @@ const mapStateToProps = (state) => {
|
||||||
phone,
|
phone,
|
||||||
organization,
|
organization,
|
||||||
language,
|
language,
|
||||||
|
isLocked,
|
||||||
subscribeToOwnCards,
|
subscribeToOwnCards,
|
||||||
isAvatarUpdating,
|
isAvatarUpdating,
|
||||||
emailUpdateForm,
|
emailUpdateForm,
|
||||||
|
|
|
@ -24,201 +24,196 @@ export default {
|
||||||
all: 'Tümü',
|
all: 'Tümü',
|
||||||
allChangesWillBeAutomaticallySavedAfterConnectionRestored:
|
allChangesWillBeAutomaticallySavedAfterConnectionRestored:
|
||||||
'Bağlantı yeniden kurulduğunda tüm değişiklikler kaydedilecektir.',
|
'Bağlantı yeniden kurulduğunda tüm değişiklikler kaydedilecektir.',
|
||||||
areYouSureYouWantToDeleteThisAttachment:
|
areYouSureYouWantToDeleteThisAttachment: 'Bu eki silmek istediğinize emin misiniz?',
|
||||||
'Bu eki silmek istediğinize emin misiniz?',
|
areYouSureYouWantToDeleteThisBoard: 'Bu panoyu silmek istediğinizden emin misiniz?',
|
||||||
areYouSureYouWantToDeleteThisBoard: 'Bu panoyu silmek istediğinizden emin misiniz?',
|
areYouSureYouWantToDeleteThisCard: 'Bu kartı silmek istediğinizden emin misiniz?',
|
||||||
areYouSureYouWantToDeleteThisCard: 'Bu kartı silmek istediğinizden emin misiniz?',
|
areYouSureYouWantToDeleteThisComment: 'Bu yorumu silmek istediğinizden emin misiniz?',
|
||||||
areYouSureYouWantToDeleteThisComment:
|
areYouSureYouWantToDeleteThisLabel: 'Bu etiketi silmek istediğinizden emin misiniz?',
|
||||||
'Bu yorumu silmek istediğinizden emin misiniz?',
|
areYouSureYouWantToDeleteThisList: 'Bu listeyi silmek istediğinizden emin misiniz?',
|
||||||
areYouSureYouWantToDeleteThisLabel: 'Bu etiketi silmek istediğinizden emin misiniz?',
|
areYouSureYouWantToDeleteThisProject: 'Bu projeyi silmek istediğinizden emin misiniz?',
|
||||||
areYouSureYouWantToDeleteThisList: 'Bu listeyi silmek istediğinizden emin misiniz?',
|
areYouSureYouWantToDeleteThisTask: 'Bu görevi silmek istediğinizden emin misiniz?',
|
||||||
areYouSureYouWantToDeleteThisProject:
|
areYouSureYouWantToDeleteThisUser: 'Bu kullanıcıyı silmek istediğinizden emin misiniz?',
|
||||||
'Bu projeyi silmek istediğinizden emin misiniz?',
|
areYouSureYouWantToLeaveBoard: 'Panodan ayrılmak istediğinizden emin misiniz?',
|
||||||
areYouSureYouWantToDeleteThisTask: 'Bu görevi silmek istediğinizden emin misiniz?',
|
areYouSureYouWantToLeaveProject: 'Projeden ayrılmak istediğinizden emin misiniz?',
|
||||||
areYouSureYouWantToDeleteThisUser:
|
areYouSureYouWantToRemoveThisManagerFromProject:
|
||||||
'Bu kullanıcıyı silmek istediğinizden emin misiniz?',
|
'Bu yöneticiyi projeden çıkarmak istediğinizden emin misiniz?',
|
||||||
areYouSureYouWantToLeaveBoard: 'Panodan ayrılmak istediğinizden emin misiniz?',
|
areYouSureYouWantToRemoveThisMemberFromBoard:
|
||||||
areYouSureYouWantToLeaveProject: 'Projeden ayrılmak istediğinizden emin misiniz?',
|
'Bu üyeyi panodan çıkarmak istediğinizden emin misiniz?',
|
||||||
areYouSureYouWantToRemoveThisManagerFromProject:
|
attachment: 'ek',
|
||||||
'Bu yöneticiyi projeden çıkarmak istediğinizden emin misiniz?',
|
attachments: 'ekler',
|
||||||
areYouSureYouWantToRemoveThisMemberFromBoard:
|
authentication: 'kimlik doğrulama',
|
||||||
'Bu üyeyi panodan çıkarmak istediğinizden emin misiniz?',
|
background: 'arka plan',
|
||||||
attachment: 'ek',
|
board: 'pano',
|
||||||
attachments: 'ekler',
|
boardNotFound_title: 'Pano bulunamadı',
|
||||||
authentication: 'kimlik doğrulama',
|
cardActions_title: 'Kart İşlemleri',
|
||||||
background: 'arka plan',
|
cardNotFound_title: 'Kart bulunamadı',
|
||||||
board: 'pano',
|
cardOrActionAreDeleted: 'Kart veya işlem silindi',
|
||||||
boardNotFound_title: 'Pano bulunamadı',
|
color: 'renk',
|
||||||
cardActions_title: 'Kart İşlemleri',
|
createBoard_title: 'Pano Oluştur',
|
||||||
cardNotFound_title: 'Kart bulunamadı',
|
createLabel_title: 'Etiket Oluştur',
|
||||||
cardOrActionAreDeleted: 'Kart veya işlem silindi',
|
createNewOneOrSelectExistingOne: 'Yeni bir tane oluşturun veya mevcut bir tanesini seçin.',
|
||||||
color: 'renk',
|
createProject_title: 'Proje Oluştur',
|
||||||
createBoard_title: 'Pano Oluştur',
|
createTextFile_title: 'Metin Dosyası Oluştur',
|
||||||
createLabel_title: 'Etiket Oluştur',
|
currentPassword: 'Geçerli şifre',
|
||||||
createNewOneOrSelectExistingOne:
|
dangerZone_title: 'Tehlikeli Bölge',
|
||||||
'Yeni bir tane oluşturun veya mevcut bir tanesini seçin.',
|
date: 'tarih',
|
||||||
createProject_title: 'Proje Oluştur',
|
dueDate_title: 'Termin Tarihi',
|
||||||
createTextFile_title: 'Metin Dosyası Oluştur',
|
deleteAttachment_title: 'Eki Sil',
|
||||||
currentPassword: 'Geçerli şifre',
|
deleteBoard_title: 'Panoyu Sil',
|
||||||
dangerZone_title: 'Tehlikeli Bölge',
|
deleteCard_title: 'Kartı Sil',
|
||||||
date: 'tarih',
|
deleteComment_title: 'Yorumu Sil',
|
||||||
dueDate_title: 'Termin Tarihi',
|
deleteLabel_title: 'Etiketi Sil',
|
||||||
deleteAttachment_title: 'Eki Sil',
|
deleteList_title: 'Listeyi Sil',
|
||||||
deleteBoard_title: 'Panoyu Sil',
|
deleteProject_title: 'Projeyi Sil',
|
||||||
deleteCard_title: 'Kartı Sil',
|
deleteTask_title: 'Görevi Sil',
|
||||||
deleteComment_title: 'Yorumu Sil',
|
deleteUser_title: 'Kullanıcıyı Sil',
|
||||||
deleteLabel_title: 'Etiketi Sil',
|
description: 'açıklama',
|
||||||
deleteList_title: 'Listeyi Sil',
|
detectAutomaically: 'Otomatik olarak algıla',
|
||||||
deleteProject_title: 'Projeyi Sil',
|
dropFileToUpload: 'Yüklenecek dosyayı buraya bırakın',
|
||||||
deleteTask_title: 'Görevi Sil',
|
editAttachment_title: 'Eki Düzenle',
|
||||||
deleteUser_title: 'Kullanıcıyı Sil',
|
editAvatar_title: 'Avatarı Düzenle',
|
||||||
description: 'açıklama',
|
editBoard_title: 'Panoyu Düzenle',
|
||||||
detectAutomaically: 'Otomatik olarak algıla',
|
editDueDate_title: 'Son Tarihi Düzenle',
|
||||||
dropFileToUpload: 'Yüklenecek dosyayı buraya bırakın',
|
editEmail_title: 'E-posta Adresini Düzenle',
|
||||||
editAttachment_title: 'Eki Düzenle',
|
editLabel_title: 'Etiketi Düzenle',
|
||||||
editAvatar_title: 'Avatarı Düzenle',
|
editPassword_title: 'Şifreyi Değiştir',
|
||||||
editBoard_title: 'Panoyu Düzenle',
|
editStopwatch_title: 'Kronometreyi Düzenle',
|
||||||
editDueDate_title: 'Son Tarihi Düzenle',
|
editUsername_title: 'Kullanıcı Adını Düzenle',
|
||||||
editEmail_title: 'E-posta Adresini Düzenle',
|
email: 'e-posta',
|
||||||
editLabel_title: 'Etiketi Düzenle',
|
emailAlreadyInUse: 'E-posta adresi zaten kullanımda',
|
||||||
editPassword_title: 'Şifreyi Değiştir',
|
enterCardTitle: 'Kart başlığını girin',
|
||||||
editStopwatch_title: 'Kronometreyi Düzenle',
|
enterDescription: 'Açıklamayı girin',
|
||||||
editUsername_title: 'Kullanıcı Adını Düzenle',
|
enterFilename: 'Dosya adını girin',
|
||||||
email: 'e-posta',
|
enterListTitle: 'Liste başlığını girin',
|
||||||
emailAlreadyInUse: 'E-posta adresi zaten kullanımda',
|
enterProjectTitle: 'Proje başlığını girin',
|
||||||
enterCardTitle: 'Kart başlığını girin',
|
enterTaskDescription: 'Görev açıklamasını girin',
|
||||||
enterDescription: 'Açıklamayı girin',
|
filterByLabels_title: 'Etikete Göre Filtrele',
|
||||||
enterFilename: 'Dosya adını girin',
|
filterByMembers_title: 'Üyelere göre filtrele',
|
||||||
enterListTitle: 'Liste başlığını girin',
|
fromComputer_title: 'Bilgisayardan',
|
||||||
enterProjectTitle: 'Proje başlığını girin',
|
general: 'Genel',
|
||||||
enterTaskDescription: 'Görev açıklamasını girin',
|
hours: 'saat',
|
||||||
filterByLabels_title: 'Etikete Göre Filtrele',
|
invalidCurrentPassword: 'Mevcut şifre yanlış',
|
||||||
filterByMembers_title: 'Üyelere göre filtrele',
|
labels: 'etiketler',
|
||||||
fromComputer_title: 'Bilgisayardan',
|
language: 'dil',
|
||||||
general: 'Genel',
|
leaveBoard_title: 'Panodan Ayrıl',
|
||||||
hours: 'saat',
|
leaveProject_title: 'Projeden Ayrıl',
|
||||||
invalidCurrentPassword: 'Mevcut şifre yanlış',
|
list: 'Listeler',
|
||||||
labels: 'etiketler',
|
listActions_title: 'Görevleri Listele',
|
||||||
language: 'dil',
|
managers: 'Yöneticiler',
|
||||||
leaveBoard_title: 'Panodan Ayrıl',
|
members: 'Üyeler',
|
||||||
leaveProject_title: 'Projeden Ayrıl',
|
minutes: 'dakika',
|
||||||
list: 'Listeler',
|
moveCard_title: 'Kartı Taşı',
|
||||||
listActions_title: 'Görevleri Listele',
|
name: 'isim',
|
||||||
managers: 'Yöneticiler',
|
newEmail: 'Yeni e-posta adresi',
|
||||||
members: 'Üyeler',
|
newPassword: 'Yeni şifre',
|
||||||
minutes: 'dakika',
|
newUsername: 'Yeni kullanıcı adı',
|
||||||
moveCard_title: 'Kartı Taşı',
|
noConnectionToServer: 'Sunucuya bağlantı yok',
|
||||||
name: 'isim',
|
noBoards: 'Pano yok',
|
||||||
newEmail: 'Yeni e-posta adresi',
|
noLists: 'Liste yok',
|
||||||
newPassword: 'Yeni şifre',
|
noProjects: 'Proje yok',
|
||||||
newUsername: 'Yeni kullanıcı adı',
|
notifications: 'Bildirimler',
|
||||||
noConnectionToServer: 'Sunucuya bağlantı yok',
|
noUnreadNotifications: 'Okunmamış bildirim yok',
|
||||||
noBoards: 'Pano yok',
|
openBoard_title: 'Açık Pano',
|
||||||
noLists: 'Liste yok',
|
optional_inline: 'İsteğe bağlı',
|
||||||
noProjects: 'Proje yok',
|
organization: 'Organizasyon',
|
||||||
notifications: 'Bildirimler',
|
phone: 'telefon',
|
||||||
noUnreadNotifications: 'Okunmamış bildirim yok',
|
preferences: 'Tercihler',
|
||||||
openBoard_title: 'Açık Pano',
|
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||||
optional_inline: 'İsteğe bağlı',
|
'İpucu: Panodan bir ek eklemek için CTRL-V ye (Macte Cmd-V) basın',
|
||||||
organization: 'Organizasyon',
|
project: 'Proje',
|
||||||
phone: 'telefon',
|
projectNotFound_title: 'Proje bulunamadı',
|
||||||
preferences: 'Tercihler',
|
removeManager_title: 'Yöneticiyi Kaldır',
|
||||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
removeMember_title: 'Üyeyi Kaldır',
|
||||||
'İpucu: Panodan bir ek eklemek için CTRL-V ye (Macte Cmd-V) basın',
|
seconds: 'saniye',
|
||||||
project: 'Proje',
|
selectBoard: 'Pano Seç',
|
||||||
projectNotFound_title: 'Proje bulunamadı',
|
selectList: 'Liste seç',
|
||||||
removeManager_title: 'Yöneticiyi Kaldır',
|
selectProject: 'Proje seç',
|
||||||
removeMember_title: 'Üyeyi Kaldır',
|
settings: 'Ayarlar',
|
||||||
seconds: 'saniye',
|
stopwatch: 'kronometre',
|
||||||
selectBoard: 'Pano Seç',
|
subscribeToMyOwnCardsByDefault: 'Varsayılan olarak kendi kartlarıma abone ol',
|
||||||
selectList: 'Liste seç',
|
taskActions_title: 'Görev Eylemleri',
|
||||||
selectProject: 'Proje seç',
|
tasks: 'Görevler',
|
||||||
settings: 'Ayarlar',
|
thereIsNoPreviewAvailableForThisAttachment: 'Bu ek için önizleme mevcut değil',
|
||||||
stopwatch: 'kronometre',
|
time: 'zaman',
|
||||||
subscribeToMyOwnCardsByDefault: 'Varsayılan olarak kendi kartlarıma abone ol',
|
title: 'başlık',
|
||||||
taskActions_title: 'Görev Eylemleri',
|
userActions_title: 'Kullanıcı İşlemleri',
|
||||||
tasks: 'Görevler',
|
userAddedThisCardToList: '<0>{{user}}</0><1> bu kartı {{list}</1> listesine ekledi',
|
||||||
thereIsNoPreviewAvailableForThisAttachment: 'Bu ek için önizleme mevcut değil',
|
userLeftNewCommentToCard:
|
||||||
time: 'zaman',
|
'{{user}} yeni bir yorum yazdı: <2>{{card}</2> kartına «{{comment}}»',
|
||||||
title: 'başlık',
|
userMovedCardFromListToList:
|
||||||
userActions_title: 'Kullanıcı İşlemleri',
|
'{{user}}, <2>{{card}></2> kartını {{fromList}} listesinden {{toList}} listesine taşıdı',
|
||||||
userAddedThisCardToList: '<0>{{user}}</0><1> bu kartı {{list}</1> listesine ekledi',
|
userMovedThisCardFromListToList:
|
||||||
userLeftNewCommentToCard:
|
'<0>{{user}}</0><1> bu kartı {{fromList}} konumundan {{toList}}</1> konumuna taşıdı',
|
||||||
'{{user}} yeni bir yorum yazdı: <2>{{card}</2> kartına «{{comment}}»',
|
username: 'kullanıcı adı',
|
||||||
userMovedCardFromListToList:
|
usernameAlreadyInUse: 'Kullanıcı adı zaten kullanımda',
|
||||||
'{{user}}, <2>{{card}></2> kartını {{fromList}} listesinden {{toList}} listesine taşıdı',
|
users: 'kullanıcı',
|
||||||
userMovedThisCardFromListToList:
|
writeComment: 'Yorum yazın',
|
||||||
'<0>{{user}}</0><1> bu kartı {{fromList}} konumundan {{toList}}</1> konumuna taşıdı',
|
},
|
||||||
username: 'kullanıcı adı',
|
action: {
|
||||||
usernameAlreadyInUse: 'Kullanıcı adı zaten kullanımda',
|
addAnotherCard: 'Başka bir kart ekle',
|
||||||
users: 'kullanıcı',
|
addAnotherList: 'Başka bir liste ekle',
|
||||||
writeComment: 'Yorum yazın',
|
addAnotherTask: 'Başka bir görev ekle',
|
||||||
},
|
addCard: 'Kart ekle',
|
||||||
action: {
|
addCard_title: 'Kart Ekle',
|
||||||
addAnotherCard: 'Başka bir kart ekle',
|
addComment: 'Yorum ekle',
|
||||||
addAnotherList: 'Başka bir liste ekle',
|
addList: 'Liste ekle',
|
||||||
addAnotherTask: 'Başka bir görev ekle',
|
addMoreDetailedDescription: 'Ayrıntılı bir açıklama ekleyin',
|
||||||
addCard: 'Kart ekle',
|
addTask: 'Görev ekle',
|
||||||
addCard_title: 'Kart Ekle',
|
addToCard: 'Karta ekle',
|
||||||
addComment: 'Yorum ekle',
|
addUser: 'Kullanıcı ekle',
|
||||||
addList: 'Liste ekle',
|
createBoard: 'Pano oluştur',
|
||||||
addMoreDetailedDescription: 'Ayrıntılı bir açıklama ekleyin',
|
createFile: 'Dosya oluştur',
|
||||||
addTask: 'Görev ekle',
|
createLabel: 'Etiket Oluştur',
|
||||||
addToCard: 'Karta ekle',
|
createNewLabel: 'Yeni etiket oluştur',
|
||||||
addUser: 'Kullanıcı ekle',
|
createProject: 'Proje oluştur',
|
||||||
createBoard: 'Pano oluştur',
|
delete: 'Sil',
|
||||||
createFile: 'Dosya oluştur',
|
deleteAttachment: 'Eki sil',
|
||||||
createLabel: 'Etiket Oluştur',
|
deleteAvatar: 'Avatarı sil',
|
||||||
createNewLabel: 'Yeni etiket oluştur',
|
deleteBoard: 'Panoyu Sil',
|
||||||
createProject: 'Proje oluştur',
|
deleteCard: 'Kartı sil',
|
||||||
delete: 'Sil',
|
deleteCard_title: 'Kartı Sil',
|
||||||
deleteAttachment: 'Eki sil',
|
deleteComment: 'Yorumu sil',
|
||||||
deleteAvatar: 'Avatarı sil',
|
deleteImage: 'Resmi sil',
|
||||||
deleteBoard: 'Panoyu Sil',
|
deleteLabel: 'Etiketi sil',
|
||||||
deleteCard: 'Kartı sil',
|
deleteList: 'Listeyi sil',
|
||||||
deleteCard_title: 'Kartı Sil',
|
deleteList_title: 'Listeyi Sil',
|
||||||
deleteComment: 'Yorumu sil',
|
deleteProject: 'Projeyi sil',
|
||||||
deleteImage: 'Resmi sil',
|
deleteProject_title: 'Projeyi Sil',
|
||||||
deleteLabel: 'Etiketi sil',
|
deleteTask: 'Görevi sil',
|
||||||
deleteList: 'Listeyi sil',
|
deleteTask_title: 'Görevi Sil',
|
||||||
deleteList_title: 'Listeyi Sil',
|
deleteUser: 'Kullanıcıyı sil',
|
||||||
deleteProject: 'Projeyi sil',
|
edit: 'Düzenle',
|
||||||
deleteProject_title: 'Projeyi Sil',
|
editDueDate_title: 'Son Tarihi Düzenle',
|
||||||
deleteTask: 'Görevi sil',
|
editDescription_title: 'Açıklamayı Düzenle',
|
||||||
deleteTask_title: 'Görevi Sil',
|
editEmail_title: 'E-posta Adresini Düzenle',
|
||||||
deleteUser: 'Kullanıcıyı sil',
|
editPassword_title: 'Şifreyi Değiştir',
|
||||||
edit: 'Düzenle',
|
editStopwatch_title: 'Kronometreyi Düzenle',
|
||||||
editDueDate_title: 'Son Tarihi Düzenle',
|
editTitle_title: 'Başlığı düzenle',
|
||||||
editDescription_title: 'Açıklamayı Düzenle',
|
editUsername_title: 'Kullanıcı Adını Düzenle',
|
||||||
editEmail_title: 'E-posta Adresini Düzenle',
|
hideDetails: 'Ayrıntıları gizle',
|
||||||
editPassword_title: 'Şifreyi Değiştir',
|
leaveBoard: 'Panodan Ayrıl',
|
||||||
editStopwatch_title: 'Kronometreyi Düzenle',
|
leaveProject: 'Projeden ayrıl',
|
||||||
editTitle_title: 'Başlığı düzenle',
|
logOut_title: 'Çıkış',
|
||||||
editUsername_title: 'Kullanıcı Adını Düzenle',
|
makeCover_title: 'Önizleme olarak ayarla',
|
||||||
hideDetails: 'Ayrıntıları gizle',
|
move: 'Taşı',
|
||||||
leaveBoard: 'Panodan Ayrıl',
|
moveCard_title: 'Kartı Taşı',
|
||||||
leaveProject: 'Projeden ayrıl',
|
remove: 'Sil',
|
||||||
logOut_title: 'Çıkış',
|
removeBackground: 'Arka planı kaldır',
|
||||||
makeCover_title: 'Önizleme olarak ayarla',
|
removeCover_title: 'Önizlemeyi Kaldır',
|
||||||
move: 'Taşı',
|
removeFromBoard: 'Panodan kaldır',
|
||||||
moveCard_title: 'Kartı Taşı',
|
removeFromProject: 'Projeden kaldır',
|
||||||
remove: 'Sil',
|
removeManager: 'Yöneticiyi kaldır',
|
||||||
removeBackground: 'Arka planı kaldır',
|
removeMember: 'Üyeyi kaldır',
|
||||||
removeCover_title: 'Önizlemeyi Kaldır',
|
save: 'Kaydet',
|
||||||
removeFromBoard: 'Panodan kaldır',
|
showAllAttachments: 'Tüm ekleri göster ({{hidden}} gizli)',
|
||||||
removeFromProject: 'Projeden kaldır',
|
showDetails: 'Ayrıntıları göster',
|
||||||
removeManager: 'Yöneticiyi kaldır',
|
showFewerAttachments: 'Daha az ek göster',
|
||||||
removeMember: 'Üyeyi kaldır',
|
start: 'başlat',
|
||||||
save: 'Kaydet',
|
stop: 'dur',
|
||||||
showAllAttachments: 'Tüm ekleri göster ({{hidden}} gizli)',
|
subscribe: 'Abone ol',
|
||||||
showDetails: 'Ayrıntıları göster',
|
unsubscribe: 'Abonelikten çık',
|
||||||
showFewerAttachments: 'Daha az ek göster',
|
uploadNewAvatar: 'Yeni avatar yükle',
|
||||||
start: 'başlat',
|
uploadNewImage: 'Yeni resim yükle',
|
||||||
stop: 'dur',
|
|
||||||
subscribe: 'Abone ol',
|
|
||||||
unsubscribe: 'Abonelikten çık',
|
|
||||||
uploadNewAvatar: 'Yeni avatar yükle',
|
|
||||||
uploadNewImage: 'Yeni resim yükle',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -50,6 +50,9 @@ export default class extends BaseModel {
|
||||||
isAdmin: attr({
|
isAdmin: attr({
|
||||||
getDefault: () => false,
|
getDefault: () => false,
|
||||||
}),
|
}),
|
||||||
|
isLocked: attr({
|
||||||
|
getDefault: () => false,
|
||||||
|
}),
|
||||||
isAvatarUpdating: attr({
|
isAvatarUpdating: attr({
|
||||||
getDefault: () => false,
|
getDefault: () => false,
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -24,6 +24,12 @@ services:
|
||||||
- DATABASE_URL=postgresql://postgres@postgres/planka
|
- DATABASE_URL=postgresql://postgres@postgres/planka
|
||||||
- SECRET_KEY=notsecretkey
|
- SECRET_KEY=notsecretkey
|
||||||
|
|
||||||
|
# Can be removed after installation
|
||||||
|
- DEFAULT_ADMIN_EMAIL=demo@demo.demo # Do not remove if you want to prevent this user from being edited/deleted
|
||||||
|
- DEFAULT_ADMIN_PASSWORD=demo
|
||||||
|
- DEFAULT_ADMIN_NAME=Demo Demo
|
||||||
|
- DEFAULT_ADMIN_USERNAME=demo
|
||||||
|
|
||||||
# related: https://github.com/knex/knex/issues/2354
|
# related: https://github.com/knex/knex/issues/2354
|
||||||
# As knex does not pass query parameters from the connection string we
|
# As knex does not pass query parameters from the connection string we
|
||||||
# have to use environment variables in order to pass the desired values, e.g.
|
# have to use environment variables in order to pass the desired values, e.g.
|
||||||
|
|
|
@ -4,6 +4,13 @@ BASE_URL=http://localhost:1337
|
||||||
DATABASE_URL=postgresql://postgres@localhost/planka
|
DATABASE_URL=postgresql://postgres@localhost/planka
|
||||||
SECRET_KEY=notsecretkey
|
SECRET_KEY=notsecretkey
|
||||||
|
|
||||||
|
## Can be removed after installation
|
||||||
|
|
||||||
|
DEFAULT_ADMIN_EMAIL=demo@demo.demo # Do not remove if you want to prevent this user from being edited/deleted
|
||||||
|
DEFAULT_ADMIN_PASSWORD=demo
|
||||||
|
DEFAULT_ADMIN_NAME=Demo Demo
|
||||||
|
DEFAULT_ADMIN_USERNAME=demo
|
||||||
|
|
||||||
## Optional
|
## Optional
|
||||||
|
|
||||||
# TRUST_PROXY=0
|
# TRUST_PROXY=0
|
||||||
|
|
|
@ -26,6 +26,10 @@ module.exports = {
|
||||||
throw Errors.USER_NOT_FOUND;
|
throw Errors.USER_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (user.email === sails.config.custom.defaultAdminEmail) {
|
||||||
|
throw Errors.USER_NOT_FOUND; // Forbidden
|
||||||
|
}
|
||||||
|
|
||||||
user = await sails.helpers.users.deleteOne.with({
|
user = await sails.helpers.users.deleteOne.with({
|
||||||
record: user,
|
record: user,
|
||||||
request: this.req,
|
request: this.req,
|
||||||
|
|
|
@ -59,6 +59,10 @@ module.exports = {
|
||||||
throw Errors.USER_NOT_FOUND;
|
throw Errors.USER_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (user.email === sails.config.custom.defaultAdminEmail) {
|
||||||
|
throw Errors.USER_NOT_FOUND; // Forbidden
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
inputs.id === currentUser.id &&
|
inputs.id === currentUser.id &&
|
||||||
!bcrypt.compareSync(inputs.currentPassword, user.password)
|
!bcrypt.compareSync(inputs.currentPassword, user.password)
|
||||||
|
|
|
@ -58,6 +58,10 @@ module.exports = {
|
||||||
throw Errors.USER_NOT_FOUND;
|
throw Errors.USER_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (user.email === sails.config.custom.defaultAdminEmail) {
|
||||||
|
throw Errors.USER_NOT_FOUND; // Forbidden
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
inputs.id === currentUser.id &&
|
inputs.id === currentUser.id &&
|
||||||
!bcrypt.compareSync(inputs.currentPassword, user.password)
|
!bcrypt.compareSync(inputs.currentPassword, user.password)
|
||||||
|
|
|
@ -61,6 +61,10 @@ module.exports = {
|
||||||
throw Errors.USER_NOT_FOUND;
|
throw Errors.USER_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (user.email === sails.config.custom.defaultAdminEmail) {
|
||||||
|
throw Errors.USER_NOT_FOUND; // Forbidden
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
inputs.id === currentUser.id &&
|
inputs.id === currentUser.id &&
|
||||||
!bcrypt.compareSync(inputs.currentPassword, user.password)
|
!bcrypt.compareSync(inputs.currentPassword, user.password)
|
||||||
|
|
|
@ -67,6 +67,13 @@ module.exports = {
|
||||||
throw Errors.USER_NOT_FOUND;
|
throw Errors.USER_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (user.email === sails.config.custom.defaultAdminEmail) {
|
||||||
|
/* eslint-disable no-param-reassign */
|
||||||
|
delete inputs.isAdmin;
|
||||||
|
delete inputs.name;
|
||||||
|
/* eslint-enable no-param-reassign */
|
||||||
|
}
|
||||||
|
|
||||||
const values = {
|
const values = {
|
||||||
..._.pick(inputs, [
|
..._.pick(inputs, [
|
||||||
'isAdmin',
|
'isAdmin',
|
||||||
|
|
|
@ -114,6 +114,7 @@ module.exports = {
|
||||||
avatarUrl:
|
avatarUrl:
|
||||||
this.avatar &&
|
this.avatar &&
|
||||||
`${sails.config.custom.userAvatarsUrl}/${this.avatar.dirname}/square-100.${this.avatar.extension}`,
|
`${sails.config.custom.userAvatarsUrl}/${this.avatar.dirname}/square-100.${this.avatar.extension}`,
|
||||||
|
isLocked: this.email === sails.config.custom.defaultAdminEmail,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -35,9 +35,11 @@ module.exports.custom = {
|
||||||
oidcAudience: process.env.OIDC_AUDIENCE,
|
oidcAudience: process.env.OIDC_AUDIENCE,
|
||||||
oidcClientId: process.env.OIDC_CLIENT_ID,
|
oidcClientId: process.env.OIDC_CLIENT_ID,
|
||||||
oidcRolesAttribute: process.env.OIDC_ROLES_ATTRIBUTE || 'groups',
|
oidcRolesAttribute: process.env.OIDC_ROLES_ATTRIBUTE || 'groups',
|
||||||
oidcAdminRoles: process.env.OIDC_ADMIN_ROLES.split(',') || [],
|
oidcAdminRoles: process.env.OIDC_ADMIN_ROLES ? process.env.OIDC_ADMIN_ROLES.split(',') : [],
|
||||||
oidcredirectUri: process.env.OIDC_REDIRECT_URI,
|
oidcredirectUri: process.env.OIDC_REDIRECT_URI,
|
||||||
oidcJwksUri: process.env.OIDC_JWKS_URI,
|
oidcJwksUri: process.env.OIDC_JWKS_URI,
|
||||||
oidcScopes: process.env.OIDC_SCOPES || 'openid profile email',
|
oidcScopes: process.env.OIDC_SCOPES || 'openid profile email',
|
||||||
oidcSkipUserInfo: process.env.OIDC_SKIP_USER_INFO === 'true',
|
oidcSkipUserInfo: process.env.OIDC_SKIP_USER_INFO === 'true',
|
||||||
|
|
||||||
|
defaultAdminEmail: process.env.DEFAULT_ADMIN_EMAIL,
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,12 +6,8 @@ const knex = initKnex(knexfile);
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
const isExists = await knex.schema.hasTable(knexfile.migrations.tableName);
|
|
||||||
|
|
||||||
await knex.migrate.latest();
|
await knex.migrate.latest();
|
||||||
if (!isExists) {
|
await knex.seed.run();
|
||||||
await knex.seed.run();
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
process.exitCode = 1;
|
process.exitCode = 1;
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,42 @@
|
||||||
const bcrypt = require('bcrypt');
|
const bcrypt = require('bcrypt');
|
||||||
|
|
||||||
exports.seed = (knex) =>
|
const buildData = () => {
|
||||||
knex('user_account').insert({
|
const data = {
|
||||||
email: 'demo@demo.demo',
|
|
||||||
password: bcrypt.hashSync('demo', 10),
|
|
||||||
isAdmin: true,
|
isAdmin: true,
|
||||||
name: 'Demo Demo',
|
};
|
||||||
username: 'demo',
|
|
||||||
subscribeToOwnCards: false,
|
if (process.env.DEFAULT_ADMIN_PASSWORD) {
|
||||||
createdAt: new Date().toISOString(),
|
data.password = bcrypt.hashSync(process.env.DEFAULT_ADMIN_PASSWORD, 10);
|
||||||
});
|
}
|
||||||
|
if (process.env.DEFAULT_ADMIN_NAME) {
|
||||||
|
data.name = process.env.DEFAULT_ADMIN_NAME;
|
||||||
|
}
|
||||||
|
if (process.env.DEFAULT_ADMIN_USERNAME) {
|
||||||
|
data.username = process.env.DEFAULT_ADMIN_USERNAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.seed = async (knex) => {
|
||||||
|
if (!process.env.DEFAULT_ADMIN_EMAIL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = buildData();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await knex('user_account').insert({
|
||||||
|
...data,
|
||||||
|
email: process.env.DEFAULT_ADMIN_EMAIL,
|
||||||
|
subscribeToOwnCards: false,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
if (Object.keys(data).length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await knex('user_account').update(data).where('email', process.env.DEFAULT_ADMIN_EMAIL);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue