mirror of
https://github.com/strapi/strapi.git
synced 2025-12-27 07:03:38 +00:00
Merge branch 'master' into improve-ctb-ux
This commit is contained in:
commit
ddad496f87
@ -7,6 +7,7 @@ language: node_js
|
||||
|
||||
node_js:
|
||||
- "9"
|
||||
- "10"
|
||||
|
||||
before_install:
|
||||
- export CHROME_BIN=chromium-browser
|
||||
@ -21,4 +22,3 @@ install:
|
||||
|
||||
script:
|
||||
- npm run doc
|
||||
- npm run test
|
||||
|
||||
@ -64,8 +64,9 @@ Every folder that follows this name pattern `strapi-*` in your `./node_modules`
|
||||
A hook needs to follow the structure below:
|
||||
|
||||
```
|
||||
/lib
|
||||
- index.js
|
||||
/hook
|
||||
└─── lib
|
||||
- index.js
|
||||
- LICENSE.md
|
||||
- package.json
|
||||
- README.md
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"version": "3.0.0-alpha.12.3",
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"assert": "~1.3.0",
|
||||
"axios": "^0.18.0",
|
||||
|
||||
@ -20,11 +20,14 @@ const localStorageKey = 'strapi-admin-language';
|
||||
// Detect user language.
|
||||
const userLanguage = window.localStorage.getItem(localStorageKey) || window.navigator.language || window.navigator.userLanguage;
|
||||
|
||||
// Split user language in a correct format.
|
||||
const userLanguageShort = get(split(userLanguage, '-'), '0');
|
||||
let foundLanguage = includes(languages, userLanguage) && userLanguage;
|
||||
if (!foundLanguage) {
|
||||
// Split user language in a correct format.
|
||||
const userLanguageShort = get(split(userLanguage, '-'), '0');
|
||||
|
||||
// Check that the language is included in the admin configuration.
|
||||
const foundLanguage = includes(languages, userLanguageShort) && userLanguageShort;
|
||||
// Check that the language is included in the admin configuration.
|
||||
foundLanguage = includes(languages, userLanguageShort) && userLanguageShort;
|
||||
}
|
||||
|
||||
const initialState = fromJS({
|
||||
locale: foundLanguage || first(languages) || 'en',
|
||||
|
||||
@ -918,6 +918,13 @@ module.exports = function(strapi) {
|
||||
value: `%${value}%`
|
||||
};
|
||||
break;
|
||||
case '_in':
|
||||
result.key = `where.${key}`;
|
||||
result.value = {
|
||||
symbol: 'IN',
|
||||
value,
|
||||
};
|
||||
break;
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@ -179,7 +179,7 @@ module.exports = {
|
||||
);
|
||||
});
|
||||
} else if (_.get(this._attributes, `${current}.isVirtual`) !== true) {
|
||||
if (typeof params.values[current] === 'object') {
|
||||
if (params.values[current] && typeof params.values[current] === 'object') {
|
||||
acc[current] = _.get(params.values[current], this.primaryKey);
|
||||
} else {
|
||||
acc[current] = params.values[current];
|
||||
|
||||
@ -28,15 +28,15 @@ module.exports = scope => {
|
||||
}
|
||||
}, {
|
||||
method: 'GET',
|
||||
path: '/' + scope.humanizeId + '/:' + tokenID,
|
||||
handler: scope.globalID + '.findOne',
|
||||
path: '/' + scope.humanizeId + '/count',
|
||||
handler: scope.globalID + '.count',
|
||||
config: {
|
||||
policies: []
|
||||
}
|
||||
}, {
|
||||
method: 'GET',
|
||||
path: '/' + scope.humanizeId + '/count',
|
||||
handler: scope.globalID + '.count',
|
||||
path: '/' + scope.humanizeId + '/:' + tokenID,
|
||||
handler: scope.globalID + '.findOne',
|
||||
config: {
|
||||
policies: []
|
||||
}
|
||||
|
||||
@ -12,7 +12,6 @@ const { execSync } = require('child_process');
|
||||
const _ = require('lodash');
|
||||
const fs = require('fs-extra');
|
||||
const npm = require('enpeem');
|
||||
const getInstalledPath = require('get-installed-path');
|
||||
|
||||
// Logger.
|
||||
const logger = require('strapi-utils').logger;
|
||||
@ -39,16 +38,17 @@ module.exports = (scope, cb) => {
|
||||
const dependencies = _.get(packageJSON, 'dependencies');
|
||||
const strapiDependencies = Object.keys(dependencies).filter(key => key.indexOf('strapi') !== -1);
|
||||
const othersDependencies = Object.keys(dependencies).filter(key => key.indexOf('strapi') === -1);
|
||||
const globalRootPath = execSync('npm root -g');
|
||||
|
||||
// Verify if the dependencies are available into the global
|
||||
_.forEach(strapiDependencies, (key) => {
|
||||
try {
|
||||
const isInstalled = getInstalledPath.sync(key);
|
||||
fs.accessSync(path.resolve(_.trim(globalRootPath.toString()), key), fs.constants.R_OK | fs.constants.F_OK);
|
||||
|
||||
availableDependencies.push({
|
||||
key,
|
||||
global: true,
|
||||
path: isInstalled
|
||||
path: path.resolve(_.trim(globalRootPath.toString()), key)
|
||||
});
|
||||
} catch (e) {
|
||||
othersDependencies.push(key);
|
||||
@ -104,9 +104,6 @@ module.exports = (scope, cb) => {
|
||||
},{
|
||||
name: 'upload',
|
||||
core: true
|
||||
}, {
|
||||
name: 'analytics',
|
||||
core: false
|
||||
}];
|
||||
|
||||
// Install each plugin.
|
||||
@ -143,6 +140,12 @@ module.exports = (scope, cb) => {
|
||||
|
||||
logger.info('Your new application `' + scope.name + '` is ready at `' + scope.rootPath + '`.');
|
||||
|
||||
logger.info('We are almost there !!!');
|
||||
logger.info('cd ' + scope.name);
|
||||
logger.info('strapi start');
|
||||
logger.info('Open your browser to http://localhost:1337');
|
||||
logger.info('Enjoy your strapi project :)');
|
||||
|
||||
cb();
|
||||
}
|
||||
};
|
||||
|
||||
@ -15,7 +15,6 @@
|
||||
"dependencies": {
|
||||
"enpeem": "^2.2.0",
|
||||
"fs-extra": "^4.0.0",
|
||||
"get-installed-path": "^3.0.1",
|
||||
"inquirer": "^4.0.2",
|
||||
"lodash": "^4.17.4",
|
||||
"strapi-utils": "3.0.0-alpha.12.3",
|
||||
|
||||
@ -144,6 +144,7 @@ InputSelectWithErrors.defaultProps = {
|
||||
labelStyle: {},
|
||||
onBlur: false,
|
||||
onFocus: () => {},
|
||||
selectOptions: [],
|
||||
style: {},
|
||||
tabIndex: '0',
|
||||
validations: {},
|
||||
@ -198,7 +199,7 @@ InputSelectWithErrors.propTypes = {
|
||||
}),
|
||||
PropTypes.string,
|
||||
]),
|
||||
).isRequired,
|
||||
),
|
||||
style: PropTypes.object,
|
||||
tabIndex: PropTypes.string,
|
||||
validations: PropTypes.object,
|
||||
|
||||
@ -510,6 +510,10 @@ module.exports = function (strapi) {
|
||||
result.key = `where.${key}.$regex`;
|
||||
result.value = value;
|
||||
break;
|
||||
case '_in':
|
||||
result.key = `where.${key}.$in`;
|
||||
result.value = value;
|
||||
break;
|
||||
default:
|
||||
result = undefined;
|
||||
}
|
||||
|
||||
@ -0,0 +1,54 @@
|
||||
/**
|
||||
*
|
||||
* CustomInputCheckbox
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import cn from 'classnames';
|
||||
|
||||
import styles from './styles.scss';
|
||||
|
||||
function CustomInputCheckbox({ isAll, name, onChange, value }) {
|
||||
return (
|
||||
<span className={cn('form-check', styles.customSpan)}>
|
||||
<label
|
||||
className={cn(
|
||||
'form-check-label',
|
||||
styles.customLabel,
|
||||
isAll ? styles.customLabelHeader : styles.customLabelRow,
|
||||
value && isAll && styles.customLabelCheckedHeader,
|
||||
value && !isAll && styles.customLabelCheckedRow,
|
||||
)}
|
||||
htmlFor={name}
|
||||
>
|
||||
<input
|
||||
className="form-check-input"
|
||||
checked={value}
|
||||
id={name}
|
||||
name={name}
|
||||
onChange={onChange}
|
||||
type="checkbox"
|
||||
/>
|
||||
</label>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
CustomInputCheckbox.defaultProps = {
|
||||
isAll: false,
|
||||
name: '',
|
||||
value: false,
|
||||
};
|
||||
|
||||
CustomInputCheckbox.propTypes = {
|
||||
isAll: PropTypes.bool,
|
||||
name: PropTypes.oneOfType([
|
||||
PropTypes.number,
|
||||
PropTypes.string,
|
||||
]),
|
||||
onChange: PropTypes.func.isRequired,
|
||||
value: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default CustomInputCheckbox;
|
||||
@ -0,0 +1,62 @@
|
||||
.customSpan {
|
||||
margin-left: -15px;
|
||||
}
|
||||
|
||||
.customLabel {
|
||||
cursor: pointer;
|
||||
> input {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.customLabelHeader {
|
||||
&:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left:15px; top: 5px;
|
||||
width: 14px; height: 14px;
|
||||
border: 1px solid rgba(16,22,34,0.15);
|
||||
background-color: #FDFDFD;
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
.customLabelRow {
|
||||
&:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left:15px; top: 19px;
|
||||
width: 14px; height: 14px;
|
||||
border: 1px solid rgba(16,22,34,0.15);
|
||||
background-color: #FDFDFD;
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
.customLabelCheckedHeader {
|
||||
&:after {
|
||||
content: '\f00c';
|
||||
position: absolute;
|
||||
top: 5px; left: 17px;
|
||||
font-size: 10px;
|
||||
font-family: 'FontAwesome';
|
||||
font-weight: 100;
|
||||
color: #1C5DE7;
|
||||
transition: all .2s;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.customLabelCheckedRow {
|
||||
&:after {
|
||||
content: '\f00c';
|
||||
position: absolute;
|
||||
top: 0px; left: 17px;
|
||||
font-size: 10px;
|
||||
font-family: 'FontAwesome';
|
||||
font-weight: 100;
|
||||
color: #1C5DE7;
|
||||
transition: all .2s;
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,36 +1,86 @@
|
||||
const FILTER_TYPES = [
|
||||
{
|
||||
id: 'content-manager.components.FilterOptions.FILTER_TYPES.=',
|
||||
value: '=',
|
||||
},
|
||||
{
|
||||
id: 'content-manager.components.FilterOptions.FILTER_TYPES._ne',
|
||||
value: '_ne',
|
||||
},
|
||||
{
|
||||
id: 'content-manager.components.FilterOptions.FILTER_TYPES._lt',
|
||||
value: '_lt',
|
||||
},
|
||||
{
|
||||
id: 'content-manager.components.FilterOptions.FILTER_TYPES._lte',
|
||||
value: '_lte',
|
||||
},
|
||||
{
|
||||
id: 'content-manager.components.FilterOptions.FILTER_TYPES._gt',
|
||||
value: '_gt',
|
||||
},
|
||||
{
|
||||
id: 'content-manager.components.FilterOptions.FILTER_TYPES._gte',
|
||||
value: '_gte',
|
||||
},
|
||||
{
|
||||
id: 'content-manager.components.FilterOptions.FILTER_TYPES._contains',
|
||||
value: '_contains',
|
||||
},
|
||||
{
|
||||
id: 'content-manager.components.FilterOptions.FILTER_TYPES._containss',
|
||||
value: '_containss',
|
||||
},
|
||||
];
|
||||
const getFilters = (type) => {
|
||||
switch(type) {
|
||||
case 'string':
|
||||
case 'text':
|
||||
case 'password':
|
||||
case 'email':
|
||||
return [
|
||||
{
|
||||
id: 'content-manager.components.FilterOptions.FILTER_TYPES.=',
|
||||
value: '=',
|
||||
},
|
||||
{
|
||||
id: 'content-manager.components.FilterOptions.FILTER_TYPES._ne',
|
||||
value: '_ne',
|
||||
},
|
||||
{
|
||||
id: 'content-manager.components.FilterOptions.FILTER_TYPES._lt',
|
||||
value: '_lt',
|
||||
},
|
||||
{
|
||||
id: 'content-manager.components.FilterOptions.FILTER_TYPES._lte',
|
||||
value: '_lte',
|
||||
},
|
||||
{
|
||||
id: 'content-manager.components.FilterOptions.FILTER_TYPES._gt',
|
||||
value: '_gt',
|
||||
},
|
||||
{
|
||||
id: 'content-manager.components.FilterOptions.FILTER_TYPES._gte',
|
||||
value: '_gte',
|
||||
},
|
||||
{
|
||||
id: 'content-manager.components.FilterOptions.FILTER_TYPES._contains',
|
||||
value: '_contains',
|
||||
},
|
||||
{
|
||||
id: 'content-manager.components.FilterOptions.FILTER_TYPES._containss',
|
||||
value: '_containss',
|
||||
},
|
||||
];
|
||||
case 'integer':
|
||||
case 'float':
|
||||
case 'decimal':
|
||||
case 'date':
|
||||
return [
|
||||
{
|
||||
id: 'content-manager.components.FilterOptions.FILTER_TYPES.=',
|
||||
value: '=',
|
||||
},
|
||||
{
|
||||
id: 'content-manager.components.FilterOptions.FILTER_TYPES._ne',
|
||||
value: '_ne',
|
||||
},
|
||||
{
|
||||
id: 'content-manager.components.FilterOptions.FILTER_TYPES._lt',
|
||||
value: '_lt',
|
||||
},
|
||||
{
|
||||
id: 'content-manager.components.FilterOptions.FILTER_TYPES._lte',
|
||||
value: '_lte',
|
||||
},
|
||||
{
|
||||
id: 'content-manager.components.FilterOptions.FILTER_TYPES._gt',
|
||||
value: '_gt',
|
||||
},
|
||||
{
|
||||
id: 'content-manager.components.FilterOptions.FILTER_TYPES._gte',
|
||||
value: '_gte',
|
||||
},
|
||||
];
|
||||
default:
|
||||
return [
|
||||
{
|
||||
id: 'content-manager.components.FilterOptions.FILTER_TYPES.=',
|
||||
value: '=',
|
||||
},
|
||||
{
|
||||
id: 'content-manager.components.FilterOptions.FILTER_TYPES._ne',
|
||||
value: '_ne',
|
||||
},
|
||||
];
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
export default FILTER_TYPES;
|
||||
export default getFilters;
|
||||
@ -17,7 +17,7 @@ import InputWithAutoFocus from './InputWithAutoFocus';
|
||||
import Remove from './Remove';
|
||||
import styles from './styles.scss';
|
||||
|
||||
import FILTER_TYPES from './filterTypes';
|
||||
import getFilters from './filterTypes';
|
||||
|
||||
const defaultInputStyle = { width: '210px', marginRight: '10px', paddingTop: '4px' };
|
||||
const midSelectStyle = { minWidth: '130px', maxWidth: '200px', marginLeft: '10px', marginRight: '10px' };
|
||||
@ -51,7 +51,7 @@ function FilterOptions({ filter, filterToFocus, index, onChange, onClickAdd, onC
|
||||
onChange={onChange}
|
||||
name={`${index}.filter`}
|
||||
value={get(filter, 'filter', '=')}
|
||||
selectOptions={FILTER_TYPES}
|
||||
selectOptions={getFilters(attrType)}
|
||||
style={midSelectStyle}
|
||||
/>
|
||||
<div className={cn(isDate ? styles.filterOptionsInputWrapper : '')}>
|
||||
|
||||
@ -6,10 +6,12 @@
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { toString } from 'lodash';
|
||||
|
||||
import TableHeader from '../TableHeader';
|
||||
import TableRow from '../TableRow';
|
||||
import TableEmpty from '../TableEmpty';
|
||||
import TableDelete from 'components/TableDelete';
|
||||
import TableHeader from 'components/TableHeader';
|
||||
import TableRow from 'components/TableRow';
|
||||
import TableEmpty from 'components/TableEmpty';
|
||||
|
||||
import styles from './styles.scss';
|
||||
|
||||
@ -19,12 +21,13 @@ class Table extends React.Component {
|
||||
(
|
||||
<TableEmpty
|
||||
filters={this.props.filters}
|
||||
colspan={this.props.headers.length}
|
||||
colspan={this.props.headers.length + 1}
|
||||
contentType={this.props.routeParams.slug}
|
||||
/>
|
||||
) :
|
||||
this.props.records.map((record, key) => (
|
||||
<TableRow
|
||||
onChange={this.props.onClickSelect}
|
||||
key={key}
|
||||
destination={`${this.props.route.path.replace(':slug', this.props.routeParams.slug)}/${record[this.props.primaryKey]}`}
|
||||
headers={this.props.headers}
|
||||
@ -33,18 +36,29 @@ class Table extends React.Component {
|
||||
primaryKey={this.props.primaryKey}
|
||||
onDelete={this.props.handleDelete}
|
||||
redirectUrl={this.props.redirectUrl}
|
||||
value={this.props.entriesToDelete.indexOf(toString(record.id)) !== -1}
|
||||
/>
|
||||
));
|
||||
const entriesToDeleteNumber = this.props.entriesToDelete.length;
|
||||
|
||||
return (
|
||||
<table className={`table ${styles.table}`}>
|
||||
<TableHeader
|
||||
onClickSelectAll={this.props.onClickSelectAll}
|
||||
value={this.props.deleteAllValue}
|
||||
headers={this.props.headers}
|
||||
onChangeSort={this.props.onChangeSort}
|
||||
sort={this.props.sort}
|
||||
primaryKey={this.props.primaryKey}
|
||||
/>
|
||||
<tbody>
|
||||
{ entriesToDeleteNumber > 0 && (
|
||||
<TableDelete
|
||||
colspan={this.props.headers.length + 1}
|
||||
number={entriesToDeleteNumber}
|
||||
onToggleDeleteAll={this.props.onToggleDeleteAll}
|
||||
/>
|
||||
)}
|
||||
{rows}
|
||||
</tbody>
|
||||
</table>
|
||||
@ -56,12 +70,22 @@ Table.contextTypes = {
|
||||
router: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
Table.defaultProps = {
|
||||
entriesToDelete: [],
|
||||
handleDelete: () => {},
|
||||
};
|
||||
|
||||
Table.propTypes = {
|
||||
deleteAllValue: PropTypes.bool.isRequired,
|
||||
entriesToDelete: PropTypes.array,
|
||||
filters: PropTypes.array.isRequired,
|
||||
handleDelete: PropTypes.func,
|
||||
headers: PropTypes.array.isRequired,
|
||||
history: PropTypes.object.isRequired,
|
||||
onChangeSort: PropTypes.func.isRequired,
|
||||
onClickSelect: PropTypes.func.isRequired,
|
||||
onClickSelectAll: PropTypes.func.isRequired,
|
||||
onToggleDeleteAll: PropTypes.func.isRequired,
|
||||
primaryKey: PropTypes.string.isRequired,
|
||||
records: PropTypes.oneOfType([
|
||||
PropTypes.array,
|
||||
@ -73,8 +97,4 @@ Table.propTypes = {
|
||||
sort: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
Table.defaultProps = {
|
||||
handleDelete: () => {},
|
||||
};
|
||||
|
||||
export default Table;
|
||||
|
||||
@ -0,0 +1,45 @@
|
||||
/**
|
||||
*
|
||||
* TableDelete
|
||||
*
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import styles from './styles.scss';
|
||||
|
||||
function TableDelete({ colspan, number, onToggleDeleteAll }) {
|
||||
const suffix = number > 1 ? 'plural' : 'singular';
|
||||
|
||||
return (
|
||||
<tr className={styles.tableDelete}>
|
||||
<td colSpan={colspan + 1}>
|
||||
<FormattedMessage
|
||||
id={`content-manager.components.TableDelete.entries.${suffix}`}
|
||||
values={{ number }}
|
||||
>
|
||||
{message => <span className={styles.tableDeleteSpan}>{message}</span>}
|
||||
</FormattedMessage>
|
||||
<FormattedMessage
|
||||
id="content-manager.components.TableDelete.delete"
|
||||
>
|
||||
{message => <span className={styles.deleteAll} onClick={onToggleDeleteAll}>{message}</span>}
|
||||
</FormattedMessage>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
TableDelete.defaultProps = {
|
||||
colspan: 0,
|
||||
};
|
||||
|
||||
TableDelete.propTypes = {
|
||||
colspan: PropTypes.number,
|
||||
number: PropTypes.number.isRequired,
|
||||
onToggleDeleteAll: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default TableDelete;
|
||||
@ -0,0 +1,44 @@
|
||||
.tableDelete {
|
||||
width: 100%;
|
||||
height: 36px;
|
||||
background: #F7F8F8;
|
||||
|
||||
td{
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
font-size: 1.3rem;
|
||||
font-weight: 400;
|
||||
color: #333740;
|
||||
text-align: left;
|
||||
border-collapse: collapse;
|
||||
border-top: 1px solid #F1F1F2 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.tableDeleteSpan {
|
||||
font-weight: 600;
|
||||
&:after {
|
||||
content: '—';
|
||||
margin: 0 7px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
.deleteAll {
|
||||
position: absolute;
|
||||
color: #F64D0A;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
&:after {
|
||||
position: relative;
|
||||
top: -1px;
|
||||
content: '\f1f8';
|
||||
margin-left: 7px;
|
||||
// margin-top: -10px;
|
||||
font-size: 13px;
|
||||
font-family: FontAwesome;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
}
|
||||
@ -7,6 +7,8 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import CustomInputCheckbox from 'components/CustomInputCheckbox';
|
||||
|
||||
import styles from './styles.scss';
|
||||
|
||||
class TableHeader extends React.Component {
|
||||
@ -20,6 +22,17 @@ class TableHeader extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
renderBulk = () => (
|
||||
<th key="bulk_action">
|
||||
<CustomInputCheckbox
|
||||
isAll
|
||||
name="all"
|
||||
onChange={this.props.onClickSelectAll}
|
||||
value={this.props.value}
|
||||
/>
|
||||
</th>
|
||||
);
|
||||
|
||||
render() {
|
||||
// Generate headers list
|
||||
const headers = this.props.headers.map((header, i) => {
|
||||
@ -52,18 +65,24 @@ class TableHeader extends React.Component {
|
||||
return (
|
||||
<thead className={styles.tableHeader}>
|
||||
<tr >
|
||||
{headers}
|
||||
{[this.renderBulk()].concat(headers)}
|
||||
</tr>
|
||||
</thead>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TableHeader.defaultProps = {
|
||||
value: false,
|
||||
};
|
||||
|
||||
TableHeader.propTypes = {
|
||||
headers: PropTypes.array.isRequired,
|
||||
onChangeSort: PropTypes.func.isRequired,
|
||||
onClickSelectAll: PropTypes.func.isRequired,
|
||||
primaryKey: PropTypes.string.isRequired,
|
||||
sort: PropTypes.string.isRequired,
|
||||
value: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default TableHeader;
|
||||
|
||||
@ -14,6 +14,12 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
> tr {
|
||||
th:first-child {
|
||||
width: 50px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.iconAsc{
|
||||
|
||||
@ -9,6 +9,7 @@ import PropTypes from 'prop-types';
|
||||
import moment from 'moment';
|
||||
import { isEmpty, isObject, toString } from 'lodash';
|
||||
|
||||
import CustomInputCheckbox from 'components/CustomInputCheckbox';
|
||||
import IcoContainer from 'components/IcoContainer';
|
||||
|
||||
import styles from './styles.scss';
|
||||
@ -68,31 +69,51 @@ class TableRow extends React.Component {
|
||||
this.context.router.history.push(`${this.props.destination}${this.props.redirectUrl}`);
|
||||
}
|
||||
|
||||
renderAction = () => (
|
||||
<td key='action' className={styles.actions}>
|
||||
<IcoContainer
|
||||
icons={[
|
||||
{ icoType: 'pencil', onClick: () => this.handleClick(this.props.destination) },
|
||||
{ id: this.props.record.id, icoType: 'trash', onClick: this.props.onDelete },
|
||||
]}
|
||||
/>
|
||||
</td>
|
||||
);
|
||||
|
||||
renderCells = () => {
|
||||
const { headers } = this.props;
|
||||
return [this.renderDelete()]
|
||||
.concat(
|
||||
headers.map((header, i) => (
|
||||
<td key={i}>
|
||||
<div className={styles.truncate}>
|
||||
<div className={styles.truncated}>
|
||||
{this.getDisplayedValue(
|
||||
header.type,
|
||||
this.props.record[header.name],
|
||||
header.name,
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
)))
|
||||
.concat([this.renderAction()]);
|
||||
}
|
||||
|
||||
renderDelete = () => (
|
||||
<td onClick={(e) => e.stopPropagation()} key="i">
|
||||
<CustomInputCheckbox
|
||||
name={this.props.record.id}
|
||||
onChange={this.props.onChange}
|
||||
value={this.props.value}
|
||||
/>
|
||||
</td>
|
||||
);
|
||||
|
||||
render() {
|
||||
// Generate cells
|
||||
const cells = this.props.headers.map((header, i) => (
|
||||
<td key={i}>
|
||||
<div className={styles.truncate}>
|
||||
<div className={styles.truncated}>
|
||||
{this.getDisplayedValue(
|
||||
header.type,
|
||||
this.props.record[header.name],
|
||||
header.name,
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
));
|
||||
|
||||
cells.push(
|
||||
<td key='action' className={styles.actions}>
|
||||
<IcoContainer icons={[{ icoType: 'pencil', onClick: () => this.handleClick(this.props.destination) }, { id: this.props.record.id, icoType: 'trash', onClick: this.props.onDelete }]} />
|
||||
</td>
|
||||
);
|
||||
|
||||
return (
|
||||
<tr className={styles.tableRow} onClick={() => this.handleClick(this.props.destination)}>
|
||||
{cells}
|
||||
{this.renderCells()}
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
@ -102,12 +123,18 @@ TableRow.contextTypes = {
|
||||
router: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
TableRow.defaultProps = {
|
||||
value: false,
|
||||
};
|
||||
|
||||
TableRow.propTypes = {
|
||||
destination: PropTypes.string.isRequired,
|
||||
headers: PropTypes.array.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
onDelete: PropTypes.func,
|
||||
record: PropTypes.object.isRequired,
|
||||
redirectUrl: PropTypes.string.isRequired,
|
||||
value: PropTypes.bool,
|
||||
};
|
||||
|
||||
TableRow.defaultProps = {
|
||||
|
||||
@ -16,6 +16,9 @@
|
||||
border-collapse: collapse;
|
||||
border-top: 1px solid #F1F1F2 !important;
|
||||
}
|
||||
> td:first-child {
|
||||
width: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
.truncate {
|
||||
|
||||
@ -9,10 +9,15 @@ import {
|
||||
CHANGE_PARAMS,
|
||||
DELETE_DATA,
|
||||
DELETE_DATA_SUCCESS,
|
||||
DELETE_SEVERAL_DATA,
|
||||
DELETE_SEVERAL_DATA_SUCCESS,
|
||||
GET_DATA,
|
||||
GET_DATA_SUCCEEDED,
|
||||
ON_CHANGE,
|
||||
ON_CLICK_REMOVE,
|
||||
ON_CLICK_SELECT,
|
||||
ON_CLICK_SELECT_ALL,
|
||||
ON_TOGGLE_DELETE_ALL,
|
||||
ON_TOGGLE_FILTERS,
|
||||
OPEN_FILTERS_WITH_SELECTION,
|
||||
REMOVE_ALL_FILTERS,
|
||||
@ -52,6 +57,21 @@ export function deleteDataSuccess(id) {
|
||||
};
|
||||
}
|
||||
|
||||
export function deleteSeveralData(entriesToDelete, model, source) {
|
||||
return {
|
||||
type: DELETE_SEVERAL_DATA,
|
||||
entriesToDelete,
|
||||
model,
|
||||
source,
|
||||
};
|
||||
}
|
||||
|
||||
export function deleteSeveralDataSuccess() {
|
||||
return {
|
||||
type: DELETE_SEVERAL_DATA_SUCCESS,
|
||||
};
|
||||
}
|
||||
|
||||
export function getData(currentModel, source) {
|
||||
return {
|
||||
type: GET_DATA,
|
||||
@ -83,6 +103,19 @@ export function onClickRemove(index) {
|
||||
};
|
||||
}
|
||||
|
||||
export function onClickSelect({ target }) {
|
||||
return {
|
||||
type: ON_CLICK_SELECT,
|
||||
id: target.name,
|
||||
};
|
||||
}
|
||||
|
||||
export function onClickSelectAll() {
|
||||
return {
|
||||
type: ON_CLICK_SELECT_ALL,
|
||||
};
|
||||
}
|
||||
|
||||
export function openFiltersWithSelections(index) {
|
||||
return {
|
||||
type: OPEN_FILTERS_WITH_SELECTION,
|
||||
@ -90,6 +123,12 @@ export function openFiltersWithSelections(index) {
|
||||
};
|
||||
}
|
||||
|
||||
export function onToggleDeleteAll() {
|
||||
return {
|
||||
type: ON_TOGGLE_DELETE_ALL,
|
||||
};
|
||||
}
|
||||
|
||||
export function onToggleFilters() {
|
||||
return {
|
||||
type: ON_TOGGLE_FILTERS,
|
||||
|
||||
@ -9,10 +9,15 @@ export const ADD_FILTER = 'ContentManager/ListPage/ADD_FILTER';
|
||||
export const CHANGE_PARAMS = 'ContentManager/ListPage/CHANGE_PARAMS';
|
||||
export const DELETE_DATA = 'ContentManager/ListPage/DELETE_DATA';
|
||||
export const DELETE_DATA_SUCCESS = 'ContentManager/ListPage/DELETE_DATA_SUCCESS';
|
||||
export const DELETE_SEVERAL_DATA = 'ContentManager/ListPage/DELETE_SEVERAL_DATA';
|
||||
export const DELETE_SEVERAL_DATA_SUCCESS = 'ContentManager/ListPage/DELETE_SEVERAL_DATA_SUCCESS';
|
||||
export const GET_DATA = 'ContentManager/ListPage/GET_DATA';
|
||||
export const GET_DATA_SUCCEEDED = 'ContentManager/ListPage/GET_DATA_SUCCEEDED';
|
||||
export const ON_CHANGE = 'ContentManager/ListPage/ON_CHANGE';
|
||||
export const ON_CLICK_REMOVE = 'ContentManager/ListPage/ON_CLICK_REMOVE';
|
||||
export const ON_CLICK_SELECT = 'ContentManager/ListPage/ON_CLICK_SELECT';
|
||||
export const ON_CLICK_SELECT_ALL = 'ContentManager/ListPage/ON_CLICK_SELECT_ALL';
|
||||
export const ON_TOGGLE_DELETE_ALL = 'ContentManager/ListPage/ON_TOGGLE_DELETE_ALL';
|
||||
export const ON_TOGGLE_FILTERS = 'ContentManager/ListPage/ON_TOGGLE_FILTERS';
|
||||
export const OPEN_FILTERS_WITH_SELECTION = 'ContentManager/ListPage/OPEN_FILTERS_WITH_SELECTION';
|
||||
export const REMOVE_ALL_FILTERS = 'ContentManager/ListPage/REMOVE_ALL_FILTERS';
|
||||
|
||||
@ -39,9 +39,13 @@ import {
|
||||
addFilter,
|
||||
changeParams,
|
||||
deleteData,
|
||||
deleteSeveralData,
|
||||
getData,
|
||||
onChange,
|
||||
onClickRemove,
|
||||
onClickSelect,
|
||||
onClickSelectAll,
|
||||
onToggleDeleteAll,
|
||||
onToggleFilters,
|
||||
openFiltersWithSelections,
|
||||
removeAllFilters,
|
||||
@ -141,11 +145,11 @@ export class ListPage extends React.Component {
|
||||
get(this.props.schema, [this.getCurrentModelName(), 'fields']) ||
|
||||
get(this.props.schema, ['plugins', this.getSource(), this.getCurrentModelName(), 'fields']);
|
||||
|
||||
shouldHideFilters = () => {
|
||||
if (this.props.listPage.showFilter) {
|
||||
this.props.onToggleFilters();
|
||||
}
|
||||
};
|
||||
getPopUpDeleteAllMsg = () => (
|
||||
this.props.listPage.entriesToDelete.length > 1 ?
|
||||
'content-manager.popUpWarning.bodyMessage.contentType.delete.all'
|
||||
: 'content-manager.popUpWarning.bodyMessage.contentType.delete'
|
||||
);
|
||||
|
||||
/**
|
||||
* Generate the redirect URI when editing an entry
|
||||
@ -185,6 +189,12 @@ export class ListPage extends React.Component {
|
||||
return tableHeaders;
|
||||
};
|
||||
|
||||
areAllEntriesSelected = () => {
|
||||
const { listPage: { entriesToDelete, records } } = this.props;
|
||||
|
||||
return entriesToDelete.length === records.length && records.length > 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* [findPageSort description]
|
||||
* @param {Object} props [description]
|
||||
@ -268,6 +278,12 @@ export class ListPage extends React.Component {
|
||||
}
|
||||
};
|
||||
|
||||
shouldHideFilters = () => {
|
||||
if (this.props.listPage.showFilter) {
|
||||
this.props.onToggleFilters();
|
||||
}
|
||||
};
|
||||
|
||||
toggleModalWarning = e => {
|
||||
if (!isUndefined(e)) {
|
||||
e.preventDefault();
|
||||
@ -277,16 +293,32 @@ export class ListPage extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({ showWarning: !this.state.showWarning });
|
||||
if (this.props.listPage.entriesToDelete.length > 0) {
|
||||
this.props.onClickSelectAll();
|
||||
}
|
||||
this.setState(prevState => ({ showWarning: !prevState.showWarning }));
|
||||
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
addFilter,
|
||||
deleteSeveralData,
|
||||
listPage,
|
||||
listPage: { appliedFilters, filters, filterToFocus, params, showFilter },
|
||||
listPage: {
|
||||
appliedFilters,
|
||||
entriesToDelete,
|
||||
filters,
|
||||
filterToFocus,
|
||||
params,
|
||||
showFilter,
|
||||
showWarningDeleteAll,
|
||||
},
|
||||
onChange,
|
||||
onClickRemove,
|
||||
onClickSelect,
|
||||
onClickSelectAll,
|
||||
onToggleDeleteAll,
|
||||
onToggleFilters,
|
||||
openFiltersWithSelections,
|
||||
removeAllFilters,
|
||||
@ -363,17 +395,22 @@ export class ListPage extends React.Component {
|
||||
<div className={cn('row', styles.row)}>
|
||||
<div className="col-md-12">
|
||||
<Table
|
||||
deleteAllValue={this.areAllEntriesSelected()}
|
||||
entriesToDelete={entriesToDelete}
|
||||
filters={filters}
|
||||
handleDelete={this.toggleModalWarning}
|
||||
headers={this.generateTableHeaders()}
|
||||
history={this.props.history}
|
||||
onChangeSort={this.handleChangeSort}
|
||||
onClickSelectAll={onClickSelectAll}
|
||||
onClickSelect={onClickSelect}
|
||||
onToggleDeleteAll={onToggleDeleteAll}
|
||||
primaryKey={this.getCurrentModel().primaryKey || 'id'}
|
||||
records={listPage.records}
|
||||
redirectUrl={this.generateRedirectURI()}
|
||||
route={this.props.match}
|
||||
routeParams={this.props.match.params}
|
||||
headers={this.generateTableHeaders()}
|
||||
filters={filters}
|
||||
onChangeSort={this.handleChangeSort}
|
||||
sort={params._sort}
|
||||
history={this.props.history}
|
||||
primaryKey={this.getCurrentModel().primaryKey || 'id'}
|
||||
handleDelete={this.toggleModalWarning}
|
||||
redirectUrl={this.generateRedirectURI()}
|
||||
/>
|
||||
<PopUpWarning
|
||||
isOpen={this.state.showWarning}
|
||||
@ -387,6 +424,20 @@ export class ListPage extends React.Component {
|
||||
popUpWarningType="danger"
|
||||
onConfirm={this.handleDelete}
|
||||
/>
|
||||
<PopUpWarning
|
||||
isOpen={showWarningDeleteAll}
|
||||
toggleModal={onToggleDeleteAll}
|
||||
content={{
|
||||
title: 'content-manager.popUpWarning.title',
|
||||
message: this.getPopUpDeleteAllMsg(),
|
||||
cancel: 'content-manager.popUpWarning.button.cancel',
|
||||
confirm: 'content-manager.popUpWarning.button.confirm',
|
||||
}}
|
||||
popUpWarningType="danger"
|
||||
onConfirm={() => {
|
||||
deleteSeveralData(entriesToDelete, this.getCurrentModelName(), this.getSource());
|
||||
}}
|
||||
/>
|
||||
<PageFooter
|
||||
count={listPage.count}
|
||||
onChangeParams={this.handleChangeParams}
|
||||
@ -406,6 +457,7 @@ ListPage.propTypes = {
|
||||
addFilter: PropTypes.func.isRequired,
|
||||
changeParams: PropTypes.func.isRequired,
|
||||
deleteData: PropTypes.func.isRequired,
|
||||
deleteSeveralData: PropTypes.func.isRequired,
|
||||
getData: PropTypes.func.isRequired,
|
||||
history: PropTypes.object.isRequired,
|
||||
listPage: PropTypes.object.isRequired,
|
||||
@ -414,6 +466,9 @@ ListPage.propTypes = {
|
||||
models: PropTypes.object.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
onClickRemove: PropTypes.func.isRequired,
|
||||
onClickSelect: PropTypes.func.isRequired,
|
||||
onClickSelectAll: PropTypes.func.isRequired,
|
||||
onToggleDeleteAll: PropTypes.func.isRequired,
|
||||
onToggleFilters: PropTypes.func.isRequired,
|
||||
openFiltersWithSelections: PropTypes.func.isRequired,
|
||||
removeAllFilters: PropTypes.func.isRequired,
|
||||
@ -429,9 +484,13 @@ function mapDispatchToProps(dispatch) {
|
||||
addFilter,
|
||||
changeParams,
|
||||
deleteData,
|
||||
deleteSeveralData,
|
||||
getData,
|
||||
onChange,
|
||||
onClickRemove,
|
||||
onClickSelect,
|
||||
onClickSelectAll,
|
||||
onToggleDeleteAll,
|
||||
onToggleFilters,
|
||||
openFiltersWithSelections,
|
||||
removeAllFilters,
|
||||
|
||||
@ -5,26 +5,32 @@
|
||||
*/
|
||||
|
||||
import { fromJS, List, Map } from 'immutable';
|
||||
import { toString } from 'lodash';
|
||||
|
||||
// ListPage constants
|
||||
import {
|
||||
ADD_FILTER,
|
||||
CHANGE_PARAMS,
|
||||
DELETE_DATA_SUCCESS,
|
||||
DELETE_SEVERAL_DATA_SUCCESS,
|
||||
GET_DATA_SUCCEEDED,
|
||||
ON_CHANGE,
|
||||
ON_CLICK_REMOVE,
|
||||
ON_CLICK_SELECT,
|
||||
ON_CLICK_SELECT_ALL,
|
||||
ON_TOGGLE_FILTERS,
|
||||
OPEN_FILTERS_WITH_SELECTION,
|
||||
REMOVE_ALL_FILTERS,
|
||||
REMOVE_FILTER,
|
||||
SET_PARAMS,
|
||||
SUBMIT,
|
||||
ON_TOGGLE_DELETE_ALL,
|
||||
} from './constants';
|
||||
|
||||
const initialState = fromJS({
|
||||
appliedFilters: List([]),
|
||||
count: 0,
|
||||
entriesToDelete: List([]),
|
||||
filters: List([]),
|
||||
filtersUpdated: false,
|
||||
filterToFocus: null,
|
||||
@ -35,6 +41,7 @@ const initialState = fromJS({
|
||||
}),
|
||||
records: List([]),
|
||||
showFilter: false,
|
||||
showWarningDeleteAll: false,
|
||||
});
|
||||
|
||||
function listPageReducer(state = initialState, action) {
|
||||
@ -53,10 +60,15 @@ function listPageReducer(state = initialState, action) {
|
||||
})
|
||||
))
|
||||
.update('count', (v) => v = v - 1);
|
||||
case DELETE_SEVERAL_DATA_SUCCESS:
|
||||
return state
|
||||
.update('showWarningDeleteAll', () => false)
|
||||
.update('entriesToDelete', () => List([]));
|
||||
case CHANGE_PARAMS:
|
||||
return state.updateIn(action.keys, () => action.value);
|
||||
case GET_DATA_SUCCEEDED:
|
||||
return state
|
||||
.update('entriesToDelete', () => List([]))
|
||||
.update('count', () => action.data[0].count)
|
||||
.update('records', () => List(action.data[1]));
|
||||
case ON_CHANGE:
|
||||
@ -66,6 +78,26 @@ function listPageReducer(state = initialState, action) {
|
||||
.update('appliedFilters', list => list.splice(action.index, 1))
|
||||
.update('filters', list => list.splice(action.index, 1))
|
||||
.update('filtersUpdated', v => v = !v);
|
||||
case ON_CLICK_SELECT:
|
||||
return state.update('entriesToDelete', list => {
|
||||
const index = state.get('entriesToDelete').indexOf(toString(action.id));
|
||||
|
||||
if (index !== -1) {
|
||||
return list.splice(index, 1);
|
||||
}
|
||||
|
||||
return list.concat(toString(action.id));
|
||||
});
|
||||
case ON_CLICK_SELECT_ALL:
|
||||
return state.update('entriesToDelete', () => {
|
||||
if (state.get('entriesToDelete').size === 0) {
|
||||
return state
|
||||
.get('records')
|
||||
.reduce((acc, current) => acc.concat(List([toString(current.id)])), List([]));
|
||||
}
|
||||
|
||||
return List([]);
|
||||
});
|
||||
case ON_TOGGLE_FILTERS:
|
||||
return state
|
||||
.update('filterToFocus', () => null)
|
||||
@ -94,6 +126,8 @@ function listPageReducer(state = initialState, action) {
|
||||
.update('appliedFilters', (list) => list.filter(filter => filter.get('value') !== ''))
|
||||
.update('showFilter', () => false)
|
||||
.update('filtersUpdated', v => v = !v);
|
||||
case ON_TOGGLE_DELETE_ALL:
|
||||
return state.update('showWarningDeleteAll', v => v = !v);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
||||
@ -16,11 +16,13 @@ import request from 'utils/request';
|
||||
// Actions
|
||||
import {
|
||||
deleteDataSuccess,
|
||||
deleteSeveralDataSuccess,
|
||||
getDataSucceeded,
|
||||
} from './actions';
|
||||
// Constants
|
||||
import {
|
||||
DELETE_DATA,
|
||||
DELETE_SEVERAL_DATA,
|
||||
GET_DATA,
|
||||
} from './constants';
|
||||
// Selectors
|
||||
@ -68,7 +70,7 @@ export function* dataGet(action) {
|
||||
}
|
||||
}
|
||||
|
||||
export function* dataDelete({ id, modelName, source}) {
|
||||
export function* dataDelete({ id, modelName, source }) {
|
||||
try {
|
||||
const requestUrl = `/content-manager/explorer/${modelName}/${id}`;
|
||||
const params = {};
|
||||
@ -90,10 +92,26 @@ export function* dataDelete({ id, modelName, source}) {
|
||||
}
|
||||
}
|
||||
|
||||
export function* dataDeleteAll({ entriesToDelete, model, source }) {
|
||||
try {
|
||||
const params = Object.assign(entriesToDelete, source !== undefined ? { source } : {});
|
||||
yield call(request, `/content-manager/explorer/deleteAll/${model}`, {
|
||||
method: 'DELETE',
|
||||
params,
|
||||
});
|
||||
|
||||
yield put(deleteSeveralDataSuccess());
|
||||
yield call(dataGet, { currentModel: model, source });
|
||||
} catch(err) {
|
||||
strapi.notification.error('content-manager.error.record.delete');
|
||||
}
|
||||
}
|
||||
|
||||
// All sagas to be loaded
|
||||
function* defaultSaga() {
|
||||
const loadDataWatcher = yield fork(takeLatest, GET_DATA, dataGet);
|
||||
yield fork(takeLatest, DELETE_DATA, dataDelete);
|
||||
yield fork(takeLatest, DELETE_SEVERAL_DATA, dataDeleteAll);
|
||||
|
||||
yield take(LOCATION_CHANGE);
|
||||
|
||||
|
||||
@ -33,6 +33,11 @@
|
||||
"components.FilterOptions.FILTER_TYPES._contains": "contains",
|
||||
"components.FilterOptions.FILTER_TYPES._containss": "contains (case sensitive)",
|
||||
|
||||
"components.TableDelete.entries.plural": "{number} entries selected",
|
||||
"components.TableDelete.entries.singular": "{number} entry selected",
|
||||
"components.TableDelete.delete": "Delete all",
|
||||
|
||||
|
||||
"components.TableEmpty.withFilters": "There is no {contentType} with the applied filters...",
|
||||
"components.TableEmpty.withoutFilter": "There is no {contentType}...",
|
||||
|
||||
@ -72,5 +77,6 @@
|
||||
"popUpWarning.button.cancel": "Cancel",
|
||||
"popUpWarning.button.confirm": "Confirm",
|
||||
"popUpWarning.title": "Please confirm",
|
||||
"popUpWarning.bodyMessage.contentType.delete": "Are you sure you want to delete this entry?"
|
||||
"popUpWarning.bodyMessage.contentType.delete": "Are you sure you want to delete this entry?",
|
||||
"popUpWarning.bodyMessage.contentType.delete.all": "Are you sure you want to delete theses entries?"
|
||||
}
|
||||
|
||||
@ -25,6 +25,10 @@
|
||||
"components.FiltersPickWrapper.PluginHeader.title.filter": "Filtres",
|
||||
"components.FiltersPickWrapper.hide": "Fermer",
|
||||
|
||||
"components.TableDelete.entries.plural": "{number} entrées sélectionnées",
|
||||
"components.TableDelete.entries.singular": "{number} entrée sélectionnée",
|
||||
"components.TableDelete.delete": "Tout supprimer",
|
||||
|
||||
"components.TableEmpty.withFilters": "Aucun {contentType} n'a été trouvé avec ces filtres...",
|
||||
"components.TableEmpty.withoutFilter": "Aucun {contentType} n'a été trouvé...",
|
||||
|
||||
@ -71,5 +75,6 @@
|
||||
"popUpWarning.button.cancel": "Annuler",
|
||||
"popUpWarning.button.confirm": "Confirmer",
|
||||
"popUpWarning.title": "Confirmation requise",
|
||||
"popUpWarning.bodyMessage.contentType.delete": "Êtes-vous sûr de vouloir supprimer cette entrée ?"
|
||||
"popUpWarning.bodyMessage.contentType.delete": "Êtes-vous sûr de vouloir supprimer cette entrée ?",
|
||||
"popUpWarning.bodyMessage.contentType.delete.all": "Êtes-vous sûr de vouloir supprimer ces entrées ?"
|
||||
}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
const _ = require('lodash');
|
||||
|
||||
module.exports = {
|
||||
find: async function (params, populate) {
|
||||
find: async function (params, populate, raw = false) {
|
||||
return this.query(function(qb) {
|
||||
_.forEach(params.where, (where, key) => {
|
||||
if (_.isArray(where.value)) {
|
||||
if (_.isArray(where.value) && where.symbol !== 'IN') {
|
||||
for (const value in where.value) {
|
||||
qb[value ? 'where' : 'orWhere'](key, where.symbol, where.value[value]);
|
||||
}
|
||||
@ -26,7 +26,7 @@ module.exports = {
|
||||
}
|
||||
}).fetchAll({
|
||||
withRelated: populate || this.associations.map(x => x.alias)
|
||||
});
|
||||
}).then(data => raw ? data.toJSON() : data);
|
||||
},
|
||||
|
||||
count: async function (params = {}) {
|
||||
@ -128,5 +128,13 @@ module.exports = {
|
||||
[this.primaryKey]: params.id
|
||||
})
|
||||
.destroy();
|
||||
},
|
||||
|
||||
deleteMany: async function (params) {
|
||||
return await this
|
||||
.query(function(qb) {
|
||||
return qb.whereIn('id', params.id);
|
||||
})
|
||||
.destroy();
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,13 +1,15 @@
|
||||
const _ = require('lodash');
|
||||
|
||||
module.exports = {
|
||||
find: async function (params, populate) {
|
||||
return this
|
||||
find: async function (params, populate, raw = false) {
|
||||
const query = this
|
||||
.find(params.where)
|
||||
.limit(Number(params.limit))
|
||||
.sort(params.sort)
|
||||
.skip(Number(params.skip))
|
||||
.populate(populate || this.associations.map(x => x.alias).join(' '));
|
||||
|
||||
return raw ? query.lean() : query;
|
||||
},
|
||||
|
||||
count: async function (params) {
|
||||
@ -77,5 +79,14 @@ module.exports = {
|
||||
.remove({
|
||||
[this.primaryKey]: params.id
|
||||
});
|
||||
},
|
||||
|
||||
deleteMany: async function (params) {
|
||||
return this
|
||||
.remove({
|
||||
[this.primaryKey]: {
|
||||
$in: params[this.primaryKey] || params.id
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -55,6 +55,14 @@
|
||||
"policies": ["routing"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "DELETE",
|
||||
"path": "/explorer/deleteAll/:model",
|
||||
"handler": "ContentManager.deleteAll",
|
||||
"config": {
|
||||
"policies": ["routing"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "DELETE",
|
||||
"path": "/explorer/:model/:id",
|
||||
|
||||
@ -98,4 +98,8 @@ module.exports = {
|
||||
delete: async ctx => {
|
||||
ctx.body = await strapi.plugins['content-manager'].services['contentmanager'].delete(ctx.params, ctx.request.query);
|
||||
},
|
||||
|
||||
deleteAll: async ctx => {
|
||||
ctx.body = await strapi.plugins['content-manager'].services['contentmanager'].deleteMany(ctx.params, ctx.request.query);
|
||||
}
|
||||
};
|
||||
|
||||
@ -159,4 +159,53 @@ module.exports = {
|
||||
id: params.id
|
||||
});
|
||||
},
|
||||
|
||||
deleteMany: async (params, query) => {
|
||||
const { source } = query;
|
||||
const { model } = params;
|
||||
const primaryKey = strapi.query(model, source).primaryKey;
|
||||
const toRemove = Object.keys(query).reduce((acc, curr) => {
|
||||
if (curr !== 'source') {
|
||||
return acc.concat([query[curr]]);
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
const filters = strapi.utils.models.convertParams(model, { [`${primaryKey}_in`]: toRemove });
|
||||
const entries = await strapi.query(model, source).find({ where: filters.where }, null, true);
|
||||
const associations = strapi.query(model, source).associations;
|
||||
|
||||
for (let i = 0; i < entries.length; ++i) {
|
||||
const entry = entries[i];
|
||||
|
||||
associations.forEach(association => {
|
||||
if (entry[association.alias]) {
|
||||
switch (association.nature) {
|
||||
case 'oneWay':
|
||||
case 'oneToOne':
|
||||
case 'manyToOne':
|
||||
case 'oneToManyMorph':
|
||||
entry[association.alias] = null;
|
||||
break;
|
||||
case 'oneToMany':
|
||||
case 'manyToMany':
|
||||
case 'manyToManyMorph':
|
||||
entry[association.alias] = [];
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await strapi.query(model, source).update({
|
||||
[primaryKey]: entry[primaryKey],
|
||||
values: _.pick(entry, associations.map(a => a.alias))
|
||||
});
|
||||
}
|
||||
|
||||
return strapi.query(model, source).deleteMany({
|
||||
[primaryKey]: toRemove,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="70" height="40" viewBox="0 0 70 40"><style>.st1{fill:#fff}</style><path fill="#f8b195" d="M67 40H3c-1.6 0-3-1.3-3-3V3c0-1.6 1.4-3 3-3h64c1.7 0 3 1.4 3 3v34c0 1.7-1.3 3-3 3z"/><circle cx="22" cy="10" r="2" class="st1"/><circle cx="22" cy="20" r="2" class="st1"/><circle cx="22" cy="30" r="2" class="st1"/><path d="M49 12H28c-1.1 0-2-.9-2-2s.9-2 2-2h21c1.1 0 2 .9 2 2s-.9 2-2 2zM49 22H28c-1.1 0-2-.9-2-2s.9-2 2-2h21c1.1 0 2 .9 2 2s-.9 2-2 2zM49 32H28c-1.1 0-2-.9-2-2s.9-2 2-2h21c1.1 0 2 .9 2 2s-.9 2-2 2z" class="st1"/></svg>
|
||||
|
After Width: | Height: | Size: 571 B |
@ -18,6 +18,7 @@ import IcoNumber from '../../assets/images/icon_number.png';
|
||||
import IcoRelation from '../../assets/images/icon_relation.png';
|
||||
import IcoString from '../../assets/images/icon_string.png';
|
||||
import IcoText from '../../assets/images/icon_text.png';
|
||||
import IcoEnum from '../../assets/images/icon_enum.svg';
|
||||
|
||||
import styles from './styles.scss';
|
||||
|
||||
@ -34,6 +35,7 @@ const asset = {
|
||||
'relation': IcoRelation,
|
||||
'string': IcoString,
|
||||
'text': IcoText,
|
||||
'enum': IcoEnum,
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -22,6 +22,7 @@ import IcoPassword from '../../assets/images/icon_password.png';
|
||||
import IcoRelation from '../../assets/images/icon_relation.png';
|
||||
import IcoString from '../../assets/images/icon_string.png';
|
||||
import IcoText from '../../assets/images/icon_text.png';
|
||||
import IcoEnum from '../../assets/images/icon_enum.svg';
|
||||
import styles from './styles.scss';
|
||||
|
||||
/* eslint-disable jsx-a11y/no-static-element-interactions */
|
||||
@ -45,8 +46,7 @@ class AttributeRow extends React.Component {
|
||||
decimal: IcoNumber,
|
||||
email: IcoEmail,
|
||||
password: IcoPassword,
|
||||
// TODO add Enumeration icon
|
||||
enumeration: IcoJson,
|
||||
enumeration: IcoEnum,
|
||||
};
|
||||
this.state = {
|
||||
showWarning: false,
|
||||
|
||||
@ -163,7 +163,7 @@ class PopUpForm extends React.Component { // eslint-disable-line react/prefer-st
|
||||
<ModalBody className={styles.modalBody} style={modalBodyStyle}>
|
||||
<form onSubmit={this.props.onSubmit}>
|
||||
<div className="container-fluid">
|
||||
<div className={`row ${this.props.renderModalBody ? 'justify-content-center' : ''}`}>
|
||||
<div className="row">
|
||||
{modalBody}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -115,6 +115,10 @@
|
||||
{
|
||||
"type": "relation",
|
||||
"description": "content-type-builder.popUpForm.attributes.relation.description"
|
||||
},
|
||||
{
|
||||
"type": "enumeration",
|
||||
"description": "content-type-builder.popUpForm.attributes.enumeration.description"
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -912,6 +916,84 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"enumeration": {
|
||||
"baseSettings": {
|
||||
"items": [
|
||||
{
|
||||
"label": {
|
||||
"id": "content-type-builder.form.attribute.item.enumeration.name"
|
||||
},
|
||||
"name": "name",
|
||||
"type": "string",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"label": {
|
||||
"id": "content-type-builder.form.attribute.item.enumeration.rules"
|
||||
},
|
||||
"name": "params.enumValue",
|
||||
"type": "textarea",
|
||||
"placeholder": "content-type-builder.form.attribute.item.enumeration.placeholder",
|
||||
"value": false,
|
||||
"validations": {
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"advancedSettings": {
|
||||
"items": [
|
||||
{
|
||||
"label": {
|
||||
"id": "content-type-builder.form.attribute.settings.default"
|
||||
},
|
||||
"name": "params.default",
|
||||
"type": "string",
|
||||
"value": "",
|
||||
"validations": {}
|
||||
},
|
||||
{
|
||||
"label": {
|
||||
"id": "content-type-builder.form.attribute.item.enumeration.graphql"
|
||||
},
|
||||
"name": "params.enumName",
|
||||
"type": "string",
|
||||
"value": "",
|
||||
"validations": {},
|
||||
"inputDescription": {
|
||||
"id": "content-type-builder.form.attribute.item.enumeration.graphql.description"
|
||||
}
|
||||
},
|
||||
{
|
||||
"title": {
|
||||
"id": "content-type-builder.form.attribute.item.settings.name"
|
||||
},
|
||||
"label": {
|
||||
"id": "content-type-builder.form.attribute.item.requiredField"
|
||||
},
|
||||
"name": "params.required",
|
||||
"type": "checkbox",
|
||||
"value": false,
|
||||
"validations": {},
|
||||
"inputDescription": {
|
||||
"id": "content-type-builder.form.attribute.item.requiredField.description"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": {
|
||||
"id": "content-type-builder.form.attribute.item.uniqueField"
|
||||
},
|
||||
"name": "params.unique",
|
||||
"type": "checkbox",
|
||||
"value": false,
|
||||
"validations": {},
|
||||
"inputDescription": {
|
||||
"id": "content-type-builder.form.attribute.item.uniqueField.description"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -387,6 +387,11 @@ export class Form extends React.Component { // eslint-disable-line react/prefer-
|
||||
handleChange = ({ target }) => {
|
||||
let value = target.type === 'number' && target.value !== '' ? toNumber(target.value) : target.value;
|
||||
|
||||
// Parse enumeration textarea to transform it into a array
|
||||
if (target.name === 'params.enumValue') {
|
||||
value = target.value.split(',');
|
||||
}
|
||||
|
||||
if (isObject(target.value) && target.value._isAMomentObject === true) {
|
||||
value = moment(target.value, 'YYYY-MM-DD HH:mm:ss').format();
|
||||
}
|
||||
@ -394,11 +399,11 @@ export class Form extends React.Component { // eslint-disable-line react/prefer-
|
||||
if (includes(this.props.hash.split('::')[1], 'attribute')) {
|
||||
this.props.changeInputAttribute(target.name, value);
|
||||
|
||||
if (target.name === 'params.nature' && target.value === "manyToMany") {
|
||||
if (target.name === 'params.nature' && target.value === 'manyToMany') {
|
||||
this.props.changeInputAttribute('params.dominant', true);
|
||||
}
|
||||
|
||||
if (target.name === 'params.nature' && target.value === "oneWay") {
|
||||
if (target.name === 'params.nature' && target.value === 'oneWay') {
|
||||
this.props.changeInputAttribute('params.key', '-');
|
||||
}
|
||||
|
||||
|
||||
@ -146,9 +146,12 @@ export class ModelPage extends React.Component { // eslint-disable-line react/pr
|
||||
}
|
||||
|
||||
handleDelete = (attributeName) => {
|
||||
const index = findIndex(this.props.modelPage.model.attributes, ['name', attributeName]);
|
||||
const parallelAttributeIndex = findIndex(this.props.modelPage.model.attributes, (attr) => attr.params.key === attributeName);
|
||||
|
||||
const { modelPage: { model } } = this.props;
|
||||
const index = findIndex(model.attributes, ['name', attributeName]);
|
||||
const attributeToRemove = get(model, ['attributes', index]);
|
||||
const parallelAttributeIndex = attributeToRemove.name === attributeToRemove.params.key ?
|
||||
-1 : findIndex(model.attributes, (attr) => attr.params.key === attributeName);
|
||||
|
||||
this.props.deleteAttribute(index, this.props.match.params.modelName, parallelAttributeIndex !== -1);
|
||||
}
|
||||
|
||||
@ -158,7 +161,7 @@ export class ModelPage extends React.Component { // eslint-disable-line react/pr
|
||||
|
||||
// Display a notification if the attribute is not present in the ones that the ctb handles
|
||||
if (!has(attribute.params, 'nature') && !includes(availableAttributes, attribute.params.type)) {
|
||||
return strapi.notification.info('content-type-builder.notification.info.enumeration');
|
||||
return strapi.notification.info('content-type-builder.notification.info.disable');
|
||||
}
|
||||
const settingsType = attribute.params.type ? 'baseSettings' : 'defineRelation';
|
||||
const parallelAttributeIndex = findIndex(this.props.modelPage.model.attributes, ['name', attribute.params.key]);
|
||||
|
||||
@ -1,164 +1,173 @@
|
||||
{
|
||||
"plugin.description.short": "Modelliere die Datenstruktur deiner API.",
|
||||
"plugin.description.long": "Modelliere die Datenstruktur deiner API. Lege neue Felder und Beziehungen innerhalb von einer Minute an. Erforderliche Dateien werden automatisch in deinem Projekt angelegt und aktualisiert.",
|
||||
"attribute.string": "String",
|
||||
"attribute.text": "Text",
|
||||
"attribute.boolean": "Boolean",
|
||||
"attribute.float": "Float",
|
||||
"attribute.integer": "Integer",
|
||||
"attribute.decimal": "Decimal",
|
||||
"attribute.date": "Datum",
|
||||
"attribute.json": "JSON",
|
||||
"attribute.media": "Medien",
|
||||
"attribute.email": "E-Mail",
|
||||
"attribute.password": "Passwort",
|
||||
"attribute.relation": "Beziehung",
|
||||
"attribute.enumeration": "Enumeration",
|
||||
"plugin.description.short": "Modelliere die Datenstruktur deiner API.",
|
||||
"plugin.description.long":
|
||||
"Modelliere die Datenstruktur deiner API. Lege neue Felder und Beziehungen innerhalb von einer Minute an. Erforderliche Dateien werden automatisch in deinem Projekt angelegt und aktualisiert.",
|
||||
"attribute.string": "String",
|
||||
"attribute.text": "Text",
|
||||
"attribute.boolean": "Boolean",
|
||||
"attribute.float": "Float",
|
||||
"attribute.integer": "Integer",
|
||||
"attribute.decimal": "Decimal",
|
||||
"attribute.date": "Datum",
|
||||
"attribute.json": "JSON",
|
||||
"attribute.media": "Medien",
|
||||
"attribute.email": "E-Mail",
|
||||
"attribute.password": "Passwort",
|
||||
"attribute.relation": "Beziehung",
|
||||
"attribute.enumeration": "Enumeration",
|
||||
"attribute.WYSIWYG": "Text (WYSIWYG)",
|
||||
|
||||
"contentType.temporaryDisplay": "(Nicht gespeichert)",
|
||||
"from": "aus",
|
||||
"home.contentTypeBuilder.name": "Content-Typen",
|
||||
"home.contentTypeBuilder.description": "Verwalte deine Content-Typen.",
|
||||
"home.emptyContentType.title": "Es sind keine Content-Typen verfügbar",
|
||||
"home.emptyContentType.description": "Lege deinen ersten Content-Typ an, Daten deiner API abrufen zu können.",
|
||||
"contentType.temporaryDisplay": "(Nicht gespeichert)",
|
||||
"from": "aus",
|
||||
"home.contentTypeBuilder.name": "Content-Typen",
|
||||
"home.contentTypeBuilder.description": "Verwalte deine Content-Typen.",
|
||||
"home.emptyContentType.title": "Es sind keine Content-Typen verfügbar",
|
||||
"home.emptyContentType.description": "Lege deinen ersten Content-Typ an, Daten deiner API abrufen zu können.",
|
||||
|
||||
"home.emptyAttributes.title": "Es gibt noch keine Felder",
|
||||
"home.emptyAttributes.description": "Füge deinem Content-Typen das erste Feld hinzu",
|
||||
"home.emptyAttributes.title": "Es gibt noch keine Felder",
|
||||
"home.emptyAttributes.description": "Füge deinem Content-Typen das erste Feld hinzu",
|
||||
|
||||
"button.contentType.create": "Lege einen Content-Typ an",
|
||||
"button.contentType.add": "Neuer Content-Typ",
|
||||
"button.attributes.add": "Neues Feld",
|
||||
"button.contentType.create": "Lege einen Content-Typ an",
|
||||
"button.contentType.add": "Neuer Content-Typ",
|
||||
"button.attributes.add": "Neues Feld",
|
||||
|
||||
"error.validation.required": "Dieser Wert ist erforderlich.",
|
||||
"error.validation.regex": "Dieser Wert entspricht nicht dem RegEx.",
|
||||
"error.validation.max": "Dieser Wert ist zu hoch.",
|
||||
"error.validation.min": "Dieser Wert ist zu niedrig.",
|
||||
"error.validation.maxLength": "Dieser Wert ist zu lang.",
|
||||
"error.validation.minLength": "Dieser Wert ist zu kurz.",
|
||||
"error.contentTypeName.taken": "Dieser Name existiert bereits",
|
||||
"error.attribute.taken": "Dieser Feldname ist bereits vergeben",
|
||||
"error.attribute.key.taken": "Dieser Wert existiert bereits",
|
||||
"error.attribute.sameKeyAndName": "Darf nicht gleich sein",
|
||||
"error.validation.minSupMax": "Darf nicht höher sein",
|
||||
"error.validation.required": "Dieser Wert ist erforderlich.",
|
||||
"error.validation.regex": "Dieser Wert entspricht nicht dem RegEx.",
|
||||
"error.validation.max": "Dieser Wert ist zu hoch.",
|
||||
"error.validation.min": "Dieser Wert ist zu niedrig.",
|
||||
"error.validation.maxLength": "Dieser Wert ist zu lang.",
|
||||
"error.validation.minLength": "Dieser Wert ist zu kurz.",
|
||||
"error.contentTypeName.taken": "Dieser Name existiert bereits",
|
||||
"error.attribute.taken": "Dieser Feldname ist bereits vergeben",
|
||||
"error.attribute.key.taken": "Dieser Wert existiert bereits",
|
||||
"error.attribute.sameKeyAndName": "Darf nicht gleich sein",
|
||||
"error.validation.minSupMax": "Darf nicht höher sein",
|
||||
|
||||
"form.attribute.item.textarea.name": "Name",
|
||||
"form.attribute.item.number.name": "Name",
|
||||
"form.attribute.item.date.name": "Name",
|
||||
"form.attribute.item.media.name": "Name",
|
||||
"form.attribute.item.media.multiple": "Erlaube mehrere Dateien",
|
||||
"form.attribute.item.json.name": "Name",
|
||||
"form.attribute.item.boolean.name": "Name",
|
||||
"form.attribute.item.string.name": "Name",
|
||||
"form.attribute.item.settings.name": "Einstellungen",
|
||||
"form.attribute.item.requiredField": "Benötigtes Feld",
|
||||
"form.attribute.item.uniqueField": "Einzigartiges Feld",
|
||||
"form.attribute.item.minimum": "Mindestwert",
|
||||
"form.attribute.item.minimumLength": "Mindestlänge",
|
||||
"form.attribute.item.maximumLength": "Maximallänge",
|
||||
"form.attribute.item.maximum": "Maximalwert",
|
||||
"form.attribute.item.requiredField.description": "Du wirst keinen Eintrag anlegen können, wenn dieses Feld leer ist",
|
||||
"form.attribute.item.uniqueField.description": "Du wirst keinen Eintrag anlegen können, wenn es bereits einen Eintrag mit identischem Inhalt gibt",
|
||||
"form.attribute.item.defineRelation.fieldName": "Feldname",
|
||||
"form.attribute.item.customColumnName": "Eigener Spaltenname",
|
||||
"form.attribute.item.customColumnName.description": "Dies ist nützlich, um Spalten in der Datenbank für Antworten der API umzubenennen",
|
||||
"form.attribute.item.number.type": "Zahlenformat",
|
||||
"form.attribute.item.number.type.integer": "integer (z.B.: 10)",
|
||||
"form.attribute.item.number.type.float": "float (z.B.: 3.33333333)",
|
||||
"form.attribute.item.number.type.decimal": "decimal (z.B.: 2.22)",
|
||||
"form.attribute.settings.default": "Standardwert",
|
||||
"form.attribute.settings.default.checkboxLabel": "Set to true",
|
||||
"form.attribute.item.textarea.name": "Name",
|
||||
"form.attribute.item.number.name": "Name",
|
||||
"form.attribute.item.date.name": "Name",
|
||||
"form.attribute.item.media.name": "Name",
|
||||
"form.attribute.item.media.multiple": "Erlaube mehrere Dateien",
|
||||
"form.attribute.item.json.name": "Name",
|
||||
"form.attribute.item.boolean.name": "Name",
|
||||
"form.attribute.item.string.name": "Name",
|
||||
"form.attribute.item.enumeration.name": "Name",
|
||||
"form.attribute.item.enumeration.rules": "Werte (trennen Sie sie mit einem Komma)",
|
||||
"form.attribute.item.enumeration.placeholder": "Ex: Morgen, Mittag, Abend",
|
||||
"form.attribute.item.enumeration.graphql": "Name override for GraphQL",
|
||||
"form.attribute.item.enumeration.graphql.description": "Allows you to override the default generated name for GraphQL",
|
||||
"form.attribute.item.settings.name": "Einstellungen",
|
||||
"form.attribute.item.requiredField": "Benötigtes Feld",
|
||||
"form.attribute.item.uniqueField": "Einzigartiges Feld",
|
||||
"form.attribute.item.minimum": "Mindestwert",
|
||||
"form.attribute.item.minimumLength": "Mindestlänge",
|
||||
"form.attribute.item.maximumLength": "Maximallänge",
|
||||
"form.attribute.item.maximum": "Maximalwert",
|
||||
"form.attribute.item.requiredField.description": "Du wirst keinen Eintrag anlegen können, wenn dieses Feld leer ist",
|
||||
"form.attribute.item.uniqueField.description": "Du wirst keinen Eintrag anlegen können, wenn es bereits einen Eintrag mit identischem Inhalt gibt",
|
||||
"form.attribute.item.defineRelation.fieldName": "Feldname",
|
||||
"form.attribute.item.customColumnName": "Eigener Spaltenname",
|
||||
"form.attribute.item.customColumnName.description": "Dies ist nützlich, um Spalten in der Datenbank für Antworten der API umzubenennen",
|
||||
"form.attribute.item.number.type": "Zahlenformat",
|
||||
"form.attribute.item.number.type.integer": "integer (z.B.: 10)",
|
||||
"form.attribute.item.number.type.float": "float (z.B.: 3.33333333)",
|
||||
"form.attribute.item.number.type.decimal": "decimal (z.B.: 2.22)",
|
||||
"form.attribute.settings.default": "Standardwert",
|
||||
"form.attribute.settings.default.checkboxLabel": "Set to true",
|
||||
|
||||
"form.button.cancel": "Abbrechen",
|
||||
"form.button.continue": "Weiter",
|
||||
"form.button.save": "Speichern",
|
||||
"form.button.cancel": "Abbrechen",
|
||||
"form.button.continue": "Weiter",
|
||||
"form.button.save": "Speichern",
|
||||
|
||||
"form.contentType.item.connections": "Verbindung",
|
||||
"form.contentType.item.name": "Name",
|
||||
"form.contentType.item.name.description": "Der Name des Content-Typs sollte Singular sein. {link}",
|
||||
"form.contentType.item.name.link.description": "Schau dir unsere Dokumentation an.",
|
||||
"form.contentType.item.description": "Beschreibung",
|
||||
"form.contentType.item.description.placeholder": "Beschreibe deinen Content-Typ",
|
||||
"form.contentType.item.collectionName": "Name des Dokuments in der Datenbank",
|
||||
"form.contentType.item.collectionName.inputDescription": "Nützlich, wenn Content-Typ und Datenbankname unterschiedlich sind",
|
||||
"form.contentType.item.connections": "Verbindung",
|
||||
"form.contentType.item.name": "Name",
|
||||
"form.contentType.item.name.description": "Der Name des Content-Typs sollte Singular sein. {link}",
|
||||
"form.contentType.item.name.link.description": "Schau dir unsere Dokumentation an.",
|
||||
"form.contentType.item.description": "Beschreibung",
|
||||
"form.contentType.item.description.placeholder": "Beschreibe deinen Content-Typ",
|
||||
"form.contentType.item.collectionName": "Name des Dokuments in der Datenbank",
|
||||
"form.contentType.item.collectionName.inputDescription": "Nützlich, wenn Content-Typ und Datenbankname unterschiedlich sind",
|
||||
|
||||
"menu.section.contentTypeBuilder.name.plural": "Content-Typen",
|
||||
"menu.section.contentTypeBuilder.name.singular": "Content-Typ",
|
||||
"menu.section.documentation.name": "Dokumentation",
|
||||
"menu.section.documentation.guide": "Mehr über Content-Typen findest du in unserer",
|
||||
"menu.section.documentation.guideLink": "Anleitung.",
|
||||
"menu.section.documentation.tutorial": "Schau dir unser",
|
||||
"menu.section.documentation.tutorialLink": "Tutorial an.",
|
||||
"menu.section.contentTypeBuilder.name.plural": "Content-Typen",
|
||||
"menu.section.contentTypeBuilder.name.singular": "Content-Typ",
|
||||
"menu.section.documentation.name": "Dokumentation",
|
||||
"menu.section.documentation.guide": "Mehr über Content-Typen findest du in unserer",
|
||||
"menu.section.documentation.guideLink": "Anleitung.",
|
||||
"menu.section.documentation.tutorial": "Schau dir unser",
|
||||
"menu.section.documentation.tutorialLink": "Tutorial an.",
|
||||
|
||||
"modelPage.contentHeader.emptyDescription.description": "Dieser Content-Typ hat keine Beschreibung",
|
||||
"modelPage.contentType.list.title.plural": "Felder",
|
||||
"modelPage.contentType.list.title.singular": "Feld",
|
||||
"modelPage.contentType.list.title.including": "schließt ein",
|
||||
"modelPage.contentType.list.relationShipTitle.plural": "Beziehungen",
|
||||
"modelPage.contentType.list.relationShipTitle.singular": "Beziehung",
|
||||
"modelPage.attribute.relationWith": "Beziehung mit",
|
||||
"modelPage.contentHeader.emptyDescription.description": "Dieser Content-Typ hat keine Beschreibung",
|
||||
"modelPage.contentType.list.title.plural": "Felder",
|
||||
"modelPage.contentType.list.title.singular": "Feld",
|
||||
"modelPage.contentType.list.title.including": "schließt ein",
|
||||
"modelPage.contentType.list.relationShipTitle.plural": "Beziehungen",
|
||||
"modelPage.contentType.list.relationShipTitle.singular": "Beziehung",
|
||||
"modelPage.attribute.relationWith": "Beziehung mit",
|
||||
|
||||
"noTableWarning.description": "Vergiss nicht, die Tabelle `{modelName}` in deiner Datenbank zu erstellen",
|
||||
"noTableWarning.infos": "Mehr Informationen",
|
||||
"noTableWarning.description": "Vergiss nicht, die Tabelle `{modelName}` in deiner Datenbank zu erstellen",
|
||||
"noTableWarning.infos": "Mehr Informationen",
|
||||
|
||||
"notification.error.message": "Ein Fehler ist aufgetreten",
|
||||
"notification.info.contentType.creating.notSaved": "Bitte speichere zuerst diesen Content-Typ bevor du einen neuen anlegst",
|
||||
"notification.info.optimized": "Dieses Plugin ist auf deinen localStorage optimiert",
|
||||
"notification.success.message.contentType.edit": "Der Content-Typ wurde aktualisiert",
|
||||
"notification.success.message.contentType.create": "Der Content-Typ wurde angelegt",
|
||||
"notification.success.contentTypeDeleted": "Der Content-Typ wurde gelöscht",
|
||||
"notification.info.enumeration": "Dieses Feld ist momentan nicht editierbar...😮",
|
||||
"notification.error.message": "Ein Fehler ist aufgetreten",
|
||||
"notification.info.contentType.creating.notSaved": "Bitte speichere zuerst diesen Content-Typ bevor du einen neuen anlegst",
|
||||
"notification.info.disable": "Dieses Feld ist momentan nicht editierbar...😮",
|
||||
"notification.info.optimized": "Dieses Plugin ist auf deinen localStorage optimiert",
|
||||
"notification.success.message.contentType.edit": "Der Content-Typ wurde aktualisiert",
|
||||
"notification.success.message.contentType.create": "Der Content-Typ wurde angelegt",
|
||||
"notification.success.contentTypeDeleted": "Der Content-Typ wurde gelöscht",
|
||||
|
||||
"popUpForm.attributes.string.description": "Titel, Namen, Namenslisten",
|
||||
"popUpForm.attributes.text.description": "Beschreibungen, Paragraphen, Artikel",
|
||||
"popUpForm.attributes.boolean.description": "Ja/Nein, 1 oder 0, Wahr/Falsch",
|
||||
"popUpForm.attributes.number.description": "Jegliche Zahlen",
|
||||
"popUpForm.attributes.date.description": "Event-Daten, Öffnungszeiten",
|
||||
"popUpForm.attributes.json.description": "Daten in JSON-Format",
|
||||
"popUpForm.attributes.media.description": "Bilder, Videos, PDFs und andere",
|
||||
"popUpForm.attributes.relation.description": "Bezieht sich auf einen Content-Typ",
|
||||
"popUpForm.attributes.email.description": "E-Mail-Adressen von Benutzern",
|
||||
"popUpForm.attributes.password.description": "Passwörter von Benutzers",
|
||||
"popUpForm.attributes.string.description": "Titel, Namen, Namenslisten",
|
||||
"popUpForm.attributes.text.description": "Beschreibungen, Paragraphen, Artikel",
|
||||
"popUpForm.attributes.boolean.description": "Ja/Nein, 1 oder 0, Wahr/Falsch",
|
||||
"popUpForm.attributes.number.description": "Jegliche Zahlen",
|
||||
"popUpForm.attributes.date.description": "Event-Daten, Öffnungszeiten",
|
||||
"popUpForm.attributes.json.description": "Daten in JSON-Format",
|
||||
"popUpForm.attributes.media.description": "Bilder, Videos, PDFs und andere",
|
||||
"popUpForm.attributes.relation.description": "Bezieht sich auf einen Content-Typ",
|
||||
"popUpForm.attributes.email.description": "E-Mail-Adressen von Benutzern",
|
||||
"popUpForm.attributes.password.description": "Passwörter von Benutzers",
|
||||
"popUpForm.attributes.enumeration.description": "Liste der Auswahlmöglichkeiten",
|
||||
|
||||
"popUpForm.attributes.string.name": "String",
|
||||
"popUpForm.attributes.text.name": "Text",
|
||||
"popUpForm.attributes.boolean.name": "Boolean",
|
||||
"popUpForm.attributes.date.name": "Datum",
|
||||
"popUpForm.attributes.json.name": "JSON",
|
||||
"popUpForm.attributes.media.name": "Medien",
|
||||
"popUpForm.attributes.number.name": "Zahl",
|
||||
"popUpForm.attributes.relation.name": "Beziehung",
|
||||
"popUpForm.attributes.email.name": "E-Mail",
|
||||
"popUpForm.attributes.password.name": "Passwort",
|
||||
"popUpForm.attributes.enumeration.name": "Enumeration",
|
||||
"popUpForm.create": "Neu",
|
||||
"popUpForm.edit": "Bearbeiten",
|
||||
"popUpForm.field": "Feld",
|
||||
"popUpForm.create.contentType.header.title": "Neuer Content-Typ",
|
||||
"popUpForm.choose.attributes.header.title": "Neues Feld hinzufügen",
|
||||
"popUpForm.edit.contentType.header.title": "Content-Typen bearbeiten",
|
||||
|
||||
"popUpForm.attributes.string.name": "String",
|
||||
"popUpForm.attributes.text.name": "Text",
|
||||
"popUpForm.attributes.boolean.name": "Boolean",
|
||||
"popUpForm.attributes.date.name": "Datum",
|
||||
"popUpForm.attributes.json.name": "JSON",
|
||||
"popUpForm.attributes.media.name": "Medien",
|
||||
"popUpForm.attributes.number.name": "Zahl",
|
||||
"popUpForm.attributes.relation.name": "Beziehung",
|
||||
"popUpForm.attributes.email.name": "E-Mail",
|
||||
"popUpForm.attributes.password.name": "Passwort",
|
||||
"popUpForm.create": "Neu",
|
||||
"popUpForm.edit": "Bearbeiten",
|
||||
"popUpForm.field": "Feld",
|
||||
"popUpForm.create.contentType.header.title": "Neuer Content-Typ",
|
||||
"popUpForm.choose.attributes.header.title": "Neues Feld hinzufügen",
|
||||
"popUpForm.edit.contentType.header.title": "Content-Typen bearbeiten",
|
||||
"popUpForm.navContainer.relation": "Beziehung definieren",
|
||||
"popUpForm.navContainer.base": "Grundeinstellungen",
|
||||
"popUpForm.navContainer.advanced": "Fortgeschrittene Einstellungen",
|
||||
|
||||
"popUpForm.navContainer.relation": "Beziehung definieren",
|
||||
"popUpForm.navContainer.base": "Grundeinstellungen",
|
||||
"popUpForm.navContainer.advanced": "Fortgeschrittene Einstellungen",
|
||||
"popUpRelation.title": "Beziehung",
|
||||
|
||||
"popUpRelation.title": "Beziehung",
|
||||
|
||||
"popUpWarning.button.cancel": "Abbrechen",
|
||||
"popUpWarning.button.confirm": "Bestätigen",
|
||||
"popUpWarning.title": "Bitte bestätigen",
|
||||
"popUpWarning.bodyMessage.contentType.delete": "Bist du sicher, dass du diesen Content-Typ löschen willst?",
|
||||
"popUpWarning.bodyMessage.attribute.delete": "Bist du sicher, dass du dieses Feld löschen willst?",
|
||||
"popUpWarning.button.cancel": "Abbrechen",
|
||||
"popUpWarning.button.confirm": "Bestätigen",
|
||||
"popUpWarning.title": "Bitte bestätigen",
|
||||
"popUpWarning.bodyMessage.contentType.delete": "Bist du sicher, dass du diesen Content-Typ löschen willst?",
|
||||
"popUpWarning.bodyMessage.attribute.delete": "Bist du sicher, dass du dieses Feld löschen willst?",
|
||||
|
||||
|
||||
"table.contentType.title.plural": "Content-Typen sind verfügbar",
|
||||
"table.contentType.title.singular": "Content-Typ ist verfügbar",
|
||||
"table.contentType.head.name": "Name",
|
||||
"table.contentType.head.description": "Beschreibung",
|
||||
"table.contentType.head.fields": "Felder",
|
||||
"table.contentType.title.plural": "Content-Typen sind verfügbar",
|
||||
"table.contentType.title.singular": "Content-Typ ist verfügbar",
|
||||
"table.contentType.head.name": "Name",
|
||||
"table.contentType.head.description": "Beschreibung",
|
||||
"table.contentType.head.fields": "Felder",
|
||||
|
||||
"relation.oneToOne": "hat ein(en)",
|
||||
"relation.oneToMany": "gehört zu vielen",
|
||||
"relation.manyToOne": "hat viele",
|
||||
"relation.manyToMany": "hat und gehört zu vielen",
|
||||
"relation.attributeName.placeholder": "z.B.: Autor, Kategorie"
|
||||
"relation.oneToOne": "hat ein(en)",
|
||||
"relation.oneToMany": "gehört zu vielen",
|
||||
"relation.manyToOne": "hat viele",
|
||||
"relation.manyToMany": "hat und gehört zu vielen",
|
||||
"relation.attributeName.placeholder": "z.B.: Autor, Kategorie"
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,6 +53,11 @@
|
||||
"form.attribute.item.json.name": "Name",
|
||||
"form.attribute.item.boolean.name": "Name",
|
||||
"form.attribute.item.string.name": "Name",
|
||||
"form.attribute.item.enumeration.name": "Name",
|
||||
"form.attribute.item.enumeration.rules": "Values (separate them with a comma)",
|
||||
"form.attribute.item.enumeration.graphql": "Name override for GraphQL",
|
||||
"form.attribute.item.enumeration.graphql.description": "Allows you to override the default generated name for GraphQL",
|
||||
"form.attribute.item.enumeration.placeholder": "Ex: morning,noon,evening",
|
||||
"form.attribute.item.appearance.name": "Appearance",
|
||||
"form.attribute.item.appearance.label": "Display as a WYSIWYG",
|
||||
"form.attribute.item.appearance.description":
|
||||
@ -116,11 +121,11 @@
|
||||
"notification.error.message": "An error occurred",
|
||||
"notification.info.contentType.creating.notSaved":
|
||||
"Please save your current Content Type before creating a new one",
|
||||
"notification.info.disable": "This field is not editable for the moment...😮",
|
||||
"notification.info.optimized": "This plugin is optimized with your localStorage",
|
||||
"notification.success.message.contentType.edit": "Your Content Type has been updated",
|
||||
"notification.success.message.contentType.create": "Your Content Type has been created",
|
||||
"notification.success.contentTypeDeleted": "The Content Type has been deleted",
|
||||
"notification.info.enumeration": "This field is not editable for the moment...😮",
|
||||
|
||||
"popUpForm.attributes.string.description": "Titles, names, paragraphs, list of names",
|
||||
"popUpForm.attributes.text.description": "Descriptions, text paragraphs, articles ",
|
||||
@ -132,6 +137,7 @@
|
||||
"popUpForm.attributes.relation.description": "Refers to a Content Type",
|
||||
"popUpForm.attributes.email.description": "User's email...",
|
||||
"popUpForm.attributes.password.description": "User password...",
|
||||
"popUpForm.attributes.enumeration.description": "List of choices",
|
||||
|
||||
"popUpForm.attributes.string.name": "String",
|
||||
"popUpForm.attributes.text.name": "Text",
|
||||
@ -143,6 +149,7 @@
|
||||
"popUpForm.attributes.relation.name": "Relation",
|
||||
"popUpForm.attributes.email.name": "Email",
|
||||
"popUpForm.attributes.password.name": "Password",
|
||||
"popUpForm.attributes.enumeration.name": "Enumeration",
|
||||
"popUpForm.create": "Add New",
|
||||
"popUpForm.edit": "Edit",
|
||||
"popUpForm.field": "Field",
|
||||
|
||||
@ -1,20 +1,20 @@
|
||||
{
|
||||
"plugin.description.short": "Modelisez la structure de données de votre API.",
|
||||
"plugin.description.short": "Modélisez la structure de données de votre API.",
|
||||
"plugin.description.long":
|
||||
"Modelisez la structure de données de votre API. Créer des nouveaux champs et relations en un instant. Les fichiers se créent et se mettent à jour automatiquement.",
|
||||
"Modélisez la structure de données de votre API. Créer des nouveaux champs et relations en un instant. Les fichiers se créent et se mettent à jour automatiquement.",
|
||||
"attribute.string": "Chaîne de caractères",
|
||||
"attribute.text": "Text",
|
||||
"attribute.text": "Texte",
|
||||
"attribute.boolean": "Booléen",
|
||||
"attribute.float": "Décimal approximatif",
|
||||
"attribute.integer": "Entier",
|
||||
"attribute.decimal": "Décimal",
|
||||
"attribute.date": "Date",
|
||||
"attribute.json": "JSON",
|
||||
"attribute.media": "Media",
|
||||
"attribute.media": "Média",
|
||||
"attribute.password": "Mot de passe",
|
||||
"attribute.email": "Email",
|
||||
"attribute.relation": "Relation",
|
||||
"attribute.enumeration": "Enumération",
|
||||
"attribute.enumeration": "Énumération",
|
||||
"attribute.WYSIWYG": "Text (WYSIWYG)",
|
||||
|
||||
"contentType.temporaryDisplay": "(Non sauvegardé)",
|
||||
@ -26,7 +26,7 @@
|
||||
"home.emptyContentType.title": "Il n'y a pas de model disponible",
|
||||
"home.emptyContentType.description": "Créez votre premier modèle...",
|
||||
|
||||
"home.emptyAttributes.title": "Il n'y a pas encore de champs",
|
||||
"home.emptyAttributes.title": "Il n'y a pas encore de champ",
|
||||
"home.emptyAttributes.description": "Ajoutez votre premier champ a votre modèle",
|
||||
|
||||
"button.contentType.create": "Créer un modèle",
|
||||
@ -54,6 +54,11 @@
|
||||
"form.attribute.item.media.name": "Nom",
|
||||
"form.attribute.item.media.multiple": "Peut être relié à plusieurs fichiers",
|
||||
"form.attribute.item.string.name": "Nom",
|
||||
"form.attribute.item.enumeration.name": "Nom",
|
||||
"form.attribute.item.enumeration.rules": "Valeurs (les séparer par une virgule)",
|
||||
"form.attribute.item.enumeration.graphql": "Surchage du nom pour GraphQL",
|
||||
"form.attribute.item.enumeration.graphql.description": "Vous permet de remplacer le nom généré par défaut pour GraphQL",
|
||||
"form.attribute.item.enumeration.placeholder": "Ex: matin,midi,soir",
|
||||
"form.attribute.item.appearance.name": "Apparence",
|
||||
"form.attribute.item.appearance.label": "Editable avec un WYSIWYG",
|
||||
"form.attribute.item.appearance.description":
|
||||
@ -118,11 +123,11 @@
|
||||
"notification.error.message": "Une erreur est survenue",
|
||||
"notification.info.contentType.creating.notSaved":
|
||||
"Sauvegardez votre Modèle en cours avant d'en créer un nouveau",
|
||||
"notification.info.disable": "Ce champ n'est pas modifiable pour le moment...😮",
|
||||
"notification.info.optimized": "Ce plugin est optimisé pour votre localStorage",
|
||||
"notification.success.message.contentType.edit": "Votre modèle a bien été modifié",
|
||||
"notification.success.message.contentType.create": "Votre modèle a bien été créée",
|
||||
"notification.success.contentTypeDeleted": "Le modèle a bien été supprimé.",
|
||||
"notification.info.enumeration": "Ce champs n'est pas modifiable pour le moment...😮",
|
||||
|
||||
"popUpForm.attributes.string.description": "Titres, noms,...",
|
||||
"popUpForm.attributes.text.description": "Descriptions, paragraphes texte, articles ",
|
||||
@ -134,22 +139,24 @@
|
||||
"popUpForm.attributes.relation.description": "Pointe vers un autre Modèle",
|
||||
"popUpForm.attributes.password.description": "Mot de passe utilisateur...",
|
||||
"popUpForm.attributes.email.description": "Email utilisateurs",
|
||||
"popUpForm.attributes.enumeration.description": "Liste de choix",
|
||||
|
||||
"popUpForm.attributes.string.name": "Chaîne de caractères",
|
||||
"popUpForm.attributes.text.name": "Text",
|
||||
"popUpForm.attributes.text.name": "Texte",
|
||||
"popUpForm.attributes.boolean.name": "Booléen",
|
||||
"popUpForm.attributes.number.name": "Nombre",
|
||||
"popUpForm.attributes.date.name": "Date",
|
||||
"popUpForm.attributes.json.name": "JSON",
|
||||
"popUpForm.attributes.media.name": "Media",
|
||||
"popUpForm.attributes.media.name": "Média",
|
||||
"popUpForm.attributes.relation.name": "Relation",
|
||||
"popUpForm.attributes.email.name": "Email",
|
||||
"popUpForm.attributes.password.name": "Mot de passe",
|
||||
"popUpForm.attributes.enumeration": "Énumération",
|
||||
"popUpForm.create": "Ajouter un Nouveau",
|
||||
"popUpForm.edit": "Modifer",
|
||||
"popUpForm.field": "Champ",
|
||||
"popUpForm.create.contentType.header.title": "Ajouter un Nouveau Modèle",
|
||||
"popUpForm.choose.attributes.header.title": "Ajouter un Nouveau Champs",
|
||||
"popUpForm.choose.attributes.header.title": "Ajouter un Nouveau Champ",
|
||||
"popUpForm.edit.contentType.header.title": "Modifier un Modèle",
|
||||
"popUpForm.navContainer.relation": "Définir relation",
|
||||
"popUpForm.navContainer.base": "Réglages de base",
|
||||
|
||||
@ -52,6 +52,11 @@
|
||||
"form.attribute.item.json.name": "Nazwa",
|
||||
"form.attribute.item.boolean.name": "Nazwa",
|
||||
"form.attribute.item.string.name": "Nazwa",
|
||||
"form.attribute.item.enumeration.name": "Nazwa",
|
||||
"form.attribute.item.enumeration.rules": "Values (separate them with a comma)",
|
||||
"form.attribute.item.enumeration.placeholder": "Ex: morning,noon,evening",
|
||||
"form.attribute.item.enumeration.graphql": "Name override for GraphQL",
|
||||
"form.attribute.item.enumeration.graphql.description": "Allows you to override the default generated name for GraphQL",
|
||||
"form.attribute.item.settings.name": "Ustawienia",
|
||||
"form.attribute.item.requiredField": "Wymagany",
|
||||
"form.attribute.item.uniqueField": "Unikalny",
|
||||
@ -110,11 +115,11 @@
|
||||
"notification.error.message": "Wystąpił błąd",
|
||||
"notification.info.contentType.creating.notSaved":
|
||||
"Zapisz proszę aktualny model zanim stworzysz nowy",
|
||||
"notification.info.disable": "Tego pola nie można obecnie edytować... 😮",
|
||||
"notification.info.optimized": "Ta wtyczka jest zoptymalizowana z localStorage",
|
||||
"notification.success.message.contentType.edit": "Model został zmieniony",
|
||||
"notification.success.message.contentType.create": "Model został utworzony",
|
||||
"notification.success.contentTypeDeleted": "Model został usunięty",
|
||||
"notification.info.enumeration": "Tego pola nie można obecnie edytować... 😮",
|
||||
|
||||
"popUpForm.attributes.string.description": "Tytuły, nazwy, paragrafy, lista nazwisk",
|
||||
"popUpForm.attributes.text.description": "Opisy, paragrafy, artykuły ",
|
||||
@ -126,7 +131,8 @@
|
||||
"popUpForm.attributes.relation.description": "Odnosi się do modelu",
|
||||
"popUpForm.attributes.email.description": "Email użytkownika...",
|
||||
"popUpForm.attributes.password.description": "Hasło użytkownika...",
|
||||
|
||||
"popUpForm.attributes.enumeration.description": "List of choices",
|
||||
|
||||
"popUpForm.attributes.string.name": "Ciąg",
|
||||
"popUpForm.attributes.text.name": "Tekst",
|
||||
"popUpForm.attributes.boolean.name": "Typ logiczny",
|
||||
@ -137,6 +143,7 @@
|
||||
"popUpForm.attributes.relation.name": "Relacja",
|
||||
"popUpForm.attributes.email.name": "Email",
|
||||
"popUpForm.attributes.password.name": "Hasło",
|
||||
"popUpForm.attributes.enumeration.name": "Wyliczenie",
|
||||
"popUpForm.create": "Nowy",
|
||||
"popUpForm.edit": "Zmień",
|
||||
"popUpForm.field": "Atrybut",
|
||||
|
||||
@ -1,176 +1,183 @@
|
||||
{
|
||||
"plugin.description.short": "Моделируйте структуру данных вашего API.",
|
||||
"plugin.description.long":
|
||||
"Моделируйте структуру данных вашего API. Создавайте новые поля и связи всего за минуту. Файлы автоматически создаются и обновляются в вашем проекте.",
|
||||
"attribute.string": "String",
|
||||
"attribute.text": "Text",
|
||||
"attribute.boolean": "Boolean",
|
||||
"attribute.float": "Float",
|
||||
"attribute.integer": "integer",
|
||||
"attribute.decimal": "Decimal",
|
||||
"attribute.date": "Date",
|
||||
"attribute.json": "JSON",
|
||||
"attribute.media": "Media",
|
||||
"attribute.email": "Email",
|
||||
"attribute.password": "Password",
|
||||
"attribute.relation": "Связь",
|
||||
"attribute.enumeration": "Enumeration",
|
||||
"attribute.WYSIWYG": "Text (WYSIWYG)",
|
||||
"plugin.description.short": "Моделируйте структуру данных вашего API.",
|
||||
"plugin.description.long":
|
||||
"Моделируйте структуру данных вашего API. Создавайте новые поля и связи всего за минуту. Файлы автоматически создаются и обновляются в вашем проекте.",
|
||||
"attribute.string": "String",
|
||||
"attribute.text": "Text",
|
||||
"attribute.boolean": "Boolean",
|
||||
"attribute.float": "Float",
|
||||
"attribute.integer": "Integer",
|
||||
"attribute.decimal": "Decimal",
|
||||
"attribute.date": "Date",
|
||||
"attribute.json": "JSON",
|
||||
"attribute.media": "Media",
|
||||
"attribute.email": "Email",
|
||||
"attribute.password": "Password",
|
||||
"attribute.relation": "Связь",
|
||||
"attribute.enumeration": "Enumeration",
|
||||
"attribute.WYSIWYG": "Text (WYSIWYG)",
|
||||
|
||||
"contentType.temporaryDisplay": "(Не сохранено)",
|
||||
"from": "from",
|
||||
"home.contentTypeBuilder.name": "Типы Данных",
|
||||
"home.contentTypeBuilder.description": "Создавайте и обновляйте ваш Тип Данных.",
|
||||
"home.emptyContentType.title": "Нет Типов Данных",
|
||||
"home.emptyContentType.description":
|
||||
"Создайте ваш первый Тип Данных и у вас появится возможность загружать ваши данные при помощи API.",
|
||||
|
||||
"home.emptyAttributes.title": "Пока ни одного поля не создано",
|
||||
"home.emptyAttributes.description": "Добавте первое поле в ваш новый Тип Данных",
|
||||
|
||||
"button.contentType.create": "Создать Тип Данных",
|
||||
"button.contentType.add": "Добавить Тип Данных",
|
||||
"button.attributes.add": "Добавить Новое Поле",
|
||||
|
||||
"error.validation.required": "Это поле является обязательным.",
|
||||
"error.validation.regex": "Не соответствует регулярному выражению.",
|
||||
"error.validation.max": "Слишком большое.",
|
||||
"error.validation.min": "Слишком маленькое.",
|
||||
"error.validation.maxLength": "Слишком длинное.",
|
||||
"error.validation.minLength": "Слишком короткое.",
|
||||
"error.contentTypeName.taken": "Это название уже существует",
|
||||
"error.attribute.taken": "Поле с таким названием уже существует",
|
||||
"error.attribute.key.taken": "Это значение уже существует",
|
||||
"error.attribute.sameKeyAndName": "Не может быть одинаковым",
|
||||
"error.validation.minSupMax": "Не может быть выше",
|
||||
|
||||
"form.attribute.item.textarea.name": "Название",
|
||||
"form.attribute.item.number.name": "Название",
|
||||
"form.attribute.item.date.name": "Название",
|
||||
"form.attribute.item.media.name": "Название",
|
||||
"form.attribute.item.media.multiple": "Возможно несколько файлов",
|
||||
"form.attribute.item.json.name": "Название",
|
||||
"form.attribute.item.boolean.name": "Название",
|
||||
"form.attribute.item.string.name": "Название",
|
||||
"form.attribute.item.enumeration.name": "Название",
|
||||
"form.attribute.item.enumeration.rules": "Values (separate them with a comma)",
|
||||
"form.attribute.item.enumeration.graphql": "Name override for GraphQL",
|
||||
"form.attribute.item.enumeration.graphql.description": "Allows you to override the default generated name for GraphQL",
|
||||
"form.attribute.item.enumeration.placeholder": "Ex: morning,noon,evening",
|
||||
"form.attribute.item.appearance.name": "Отображение",
|
||||
"form.attribute.item.appearance.label": "Показывать WYSIWYG",
|
||||
"form.attribute.item.appearance.description":
|
||||
"В противном случае значение будет доступно для редактирования как обычное текстовое поле",
|
||||
"form.attribute.item.settings.name": "Настройки",
|
||||
"form.attribute.item.requiredField": "Обязательное поле",
|
||||
"form.attribute.item.uniqueField": "Уникальное поле",
|
||||
"form.attribute.item.minimum": "Минимальное значение",
|
||||
"form.attribute.item.minimumLength": "Минимальная длина",
|
||||
"form.attribute.item.maximumLength": "Максимальная длина",
|
||||
"form.attribute.item.maximum": "Максимальное значение",
|
||||
"form.attribute.item.requiredField.description":
|
||||
"Вы не сможете создать запись если это поле останенься пустым",
|
||||
"form.attribute.item.uniqueField.description":
|
||||
"Вы не сможете создать запись если существует запись с аналогичным содержанием",
|
||||
"form.attribute.item.defineRelation.fieldName": "Название поля",
|
||||
"form.attribute.item.customColumnName": "Настраиваемые названия столбца",
|
||||
"form.attribute.item.customColumnName.description":
|
||||
"Это удобно иметь возможность переименовывать название столбцов для настройки ответов от API.",
|
||||
"form.attribute.item.number.type": "Числовой формат",
|
||||
"form.attribute.item.number.type.integer": "integer (ex: 10)",
|
||||
"form.attribute.item.number.type.float": "float (ex: 3.33333333)",
|
||||
"form.attribute.item.number.type.decimal": "decimal (ex: 2.22)",
|
||||
"form.attribute.settings.default": "Стандартное значение",
|
||||
"form.attribute.settings.default.checkboxLabel": "Установить значение — true",
|
||||
|
||||
"form.button.cancel": "Отменить",
|
||||
"form.button.continue": "Продолжить",
|
||||
"form.button.save": "Сохранить",
|
||||
|
||||
"form.contentType.item.connections": "Соединение",
|
||||
"form.contentType.item.name": "Название",
|
||||
"form.contentType.item.name.description": "Название Типов Данных должны быть уникальными: {link}",
|
||||
"form.contentType.item.name.link.description": "Ознакомьтесь с нашей документацией",
|
||||
"form.contentType.item.description": "Описание",
|
||||
"form.contentType.item.description.placeholder": "Добавте ваше короткое описание...",
|
||||
"form.contentType.item.collectionName": "Название коллекции",
|
||||
"form.contentType.item.collectionName.inputDescription":
|
||||
"Полезно, когда название вашего Типа Данных и название вашей таблицы различаются",
|
||||
|
||||
"menu.section.contentTypeBuilder.name.plural": "Типы Данных",
|
||||
"menu.section.contentTypeBuilder.name.singular": "Тип Данных",
|
||||
"menu.section.documentation.name": "Документация",
|
||||
"menu.section.documentation.guide": "Прочтите больше о Типах Данных в нашем",
|
||||
"menu.section.documentation.guideLink": "руководстве.",
|
||||
"menu.section.documentation.tutorial": "Посмотрите наши",
|
||||
"menu.section.documentation.tutorialLink": "обучающие видео.",
|
||||
|
||||
"modelPage.contentHeader.emptyDescription.description":
|
||||
"Нет описания для этого Типа Данных",
|
||||
"modelPage.contentType.list.title.plural": "поля",
|
||||
"modelPage.contentType.list.title.singular": "поле",
|
||||
"modelPage.contentType.list.title.including": "включает",
|
||||
"modelPage.contentType.list.relationShipTitle.plural": "связи",
|
||||
"modelPage.contentType.list.relationShipTitle.singular": "связь",
|
||||
"modelPage.attribute.relationWith": "Связан с",
|
||||
|
||||
"noTableWarning.description": "Не забудте создать таблицу `{modelName}` в вашей базе данных",
|
||||
"noTableWarning.infos": "Больше информации",
|
||||
|
||||
"notification.error.message": "Возникла ошибка",
|
||||
"notification.info.contentType.creating.notSaved":
|
||||
"Пожалуйста сохраните ваш текущий Тип Данных перед тем как создавать новый",
|
||||
"notification.info.disable": "Это поле в данный момент не редактируемо...😮",
|
||||
"notification.info.optimized": "Плагин оптимизирован с вашим localstorage",
|
||||
"notification.success.message.contentType.edit": "Ваш Тип Данных обновлен",
|
||||
"notification.success.message.contentType.create": "Ваш Тип Данных создан",
|
||||
"notification.success.contentTypeDeleted": "Ваш Тип Данных удален",
|
||||
|
||||
"popUpForm.attributes.string.description": "Загаловки, названия, имена, перечень названий",
|
||||
"popUpForm.attributes.text.description": "Описания, текстовые параграфы, статьи",
|
||||
"popUpForm.attributes.boolean.description": "Yes или no, 1 или 0, true или false",
|
||||
"popUpForm.attributes.number.description": "Все что является числом",
|
||||
"popUpForm.attributes.date.description": "Дата события, рабочие часы",
|
||||
"popUpForm.attributes.json.description": "Данные в JSON формате",
|
||||
"popUpForm.attributes.media.description": "Картинки, видео, PDF и другие виды файлов",
|
||||
"popUpForm.attributes.relation.description": "Связан с Типом Данных",
|
||||
"popUpForm.attributes.email.description": "Пользовательский email...",
|
||||
"popUpForm.attributes.password.description": "Пароль пользователя...",
|
||||
"popUpForm.attributes.enumeration.description": "List of choices",
|
||||
|
||||
"contentType.temporaryDisplay": "(Не сохранено)",
|
||||
"from": "from",
|
||||
"home.contentTypeBuilder.name": "Типы Данных",
|
||||
"home.contentTypeBuilder.description": "Создавайте и обновляйте ваш Тип Данных.",
|
||||
"home.emptyContentType.title": "Нет Типов Данных",
|
||||
"home.emptyContentType.description":
|
||||
"Создайте ваш первый Тип Данных и у вас появится возможность загружать ваши данные при помощи API.",
|
||||
|
||||
"home.emptyAttributes.title": "Пока ни одного поля не создано",
|
||||
"home.emptyAttributes.description": "Добавте первое поле в ваш новый Тип Данных",
|
||||
|
||||
"button.contentType.create": "Создать Тип Данных",
|
||||
"button.contentType.add": "Добавить Тип Данных",
|
||||
"button.attributes.add": "Добавить Новое Поле",
|
||||
|
||||
"error.validation.required": "Это поле является обязательным.",
|
||||
"error.validation.regex": "Не соответствует регулярному выражению.",
|
||||
"error.validation.max": "Слишком большое.",
|
||||
"error.validation.min": "Слишком маленькое.",
|
||||
"error.validation.maxLength": "Слишком длинное.",
|
||||
"error.validation.minLength": "Слишком короткое.",
|
||||
"error.contentTypeName.taken": "Это название уже существует",
|
||||
"error.attribute.taken": "Поле с таким названием уже существует",
|
||||
"error.attribute.key.taken": "Это значение уже существует",
|
||||
"error.attribute.sameKeyAndName": "Не может быть одинаковым",
|
||||
"error.validation.minSupMax": "Не может быть выше",
|
||||
|
||||
"form.attribute.item.textarea.name": "Название",
|
||||
"form.attribute.item.number.name": "Название",
|
||||
"form.attribute.item.date.name": "Название",
|
||||
"form.attribute.item.media.name": "Название",
|
||||
"form.attribute.item.media.multiple": "Возможно несколько файлов",
|
||||
"form.attribute.item.json.name": "Название",
|
||||
"form.attribute.item.boolean.name": "Название",
|
||||
"form.attribute.item.string.name": "Название",
|
||||
"form.attribute.item.appearance.name": "Отображение",
|
||||
"form.attribute.item.appearance.label": "Показывать WYSIWYG",
|
||||
"form.attribute.item.appearance.description":
|
||||
"В противном случае значение будет доступно для редактирования как обычное текстовое поле",
|
||||
"form.attribute.item.settings.name": "Настройки",
|
||||
"form.attribute.item.requiredField": "Обязательное поле",
|
||||
"form.attribute.item.uniqueField": "Уникальное поле",
|
||||
"form.attribute.item.minimum": "Минимальное значение",
|
||||
"form.attribute.item.minimumLength": "Минимальная длина",
|
||||
"form.attribute.item.maximumLength": "Максимальная длина",
|
||||
"form.attribute.item.maximum": "Максимальное значение",
|
||||
"form.attribute.item.requiredField.description":
|
||||
"Вы не сможете создать запись если это поле останенься пустым",
|
||||
"form.attribute.item.uniqueField.description":
|
||||
"Вы не сможете создать запись если существует запись с аналогичным содержанием",
|
||||
"form.attribute.item.defineRelation.fieldName": "Название поля",
|
||||
"form.attribute.item.customColumnName": "Настраиваемые названия столбца",
|
||||
"form.attribute.item.customColumnName.description":
|
||||
"Это удобно иметь возможность переименовывать название столбцов для настройки ответов от API.",
|
||||
"form.attribute.item.number.type": "Числовой формат",
|
||||
"form.attribute.item.number.type.integer": "integer (ex: 10)",
|
||||
"form.attribute.item.number.type.float": "float (ex: 3.33333333)",
|
||||
"form.attribute.item.number.type.decimal": "decimal (ex: 2.22)",
|
||||
"form.attribute.settings.default": "Стандартное значение",
|
||||
"form.attribute.settings.default.checkboxLabel": "Установить значение — true",
|
||||
|
||||
"form.button.cancel": "Отменить",
|
||||
"form.button.continue": "Продолжить",
|
||||
"form.button.save": "Сохранить",
|
||||
|
||||
"form.contentType.item.connections": "Соединение",
|
||||
"form.contentType.item.name": "Название",
|
||||
"form.contentType.item.name.description": "Название Типов Данных должны быть уникальными: {link}",
|
||||
"form.contentType.item.name.link.description": "Ознакомьтесь с нашей документацией",
|
||||
"form.contentType.item.description": "Описание",
|
||||
"form.contentType.item.description.placeholder": "Добавте ваше короткое описание...",
|
||||
"form.contentType.item.collectionName": "Название коллекции",
|
||||
"form.contentType.item.collectionName.inputDescription":
|
||||
"Полезно, когда название вашего Типа Данных и название вашей таблицы различаются",
|
||||
|
||||
"menu.section.contentTypeBuilder.name.plural": "Типы Данных",
|
||||
"menu.section.contentTypeBuilder.name.singular": "Тип Данных",
|
||||
"menu.section.documentation.name": "Документация",
|
||||
"menu.section.documentation.guide": "Прочтите больше о Типах Данных в нашем",
|
||||
"menu.section.documentation.guideLink": "руководстве.",
|
||||
"menu.section.documentation.tutorial": "Посмотрите наши",
|
||||
"menu.section.documentation.tutorialLink": "обучающие видео.",
|
||||
|
||||
"modelPage.contentHeader.emptyDescription.description":
|
||||
"Нет описания для этого Типа Данных",
|
||||
"modelPage.contentType.list.title.plural": "поля",
|
||||
"modelPage.contentType.list.title.singular": "поле",
|
||||
"modelPage.contentType.list.title.including": "включает",
|
||||
"modelPage.contentType.list.relationShipTitle.plural": "связи",
|
||||
"modelPage.contentType.list.relationShipTitle.singular": "связь",
|
||||
"modelPage.attribute.relationWith": "Связан с",
|
||||
|
||||
"noTableWarning.description": "Не забудте создать таблицу `{modelName}` в вашей базе данных",
|
||||
"noTableWarning.infos": "Больше информации",
|
||||
|
||||
"notification.error.message": "Возникла ошибка",
|
||||
"notification.info.contentType.creating.notSaved":
|
||||
"Пожалуйста сохраните ваш текущий Тип Данных перед тем как создавать новый",
|
||||
"notification.info.optimized": "Плагин оптимизирован с вашим localstorage",
|
||||
"notification.success.message.contentType.edit": "Ваш Тип Данных обновлен",
|
||||
"notification.success.message.contentType.create": "Ваш Тип Данных создан",
|
||||
"notification.success.contentTypeDeleted": "Ваш Тип Данных удален",
|
||||
"notification.info.enumeration": "Это поле в данный момент не редактируемо...😮",
|
||||
|
||||
"popUpForm.attributes.string.description": "Загаловки, названия, имена, перечень названий",
|
||||
"popUpForm.attributes.text.description": "Описания, текстовые параграфы, статьи",
|
||||
"popUpForm.attributes.boolean.description": "Yes или no, 1 или 0, true или false",
|
||||
"popUpForm.attributes.number.description": "Все что является числом",
|
||||
"popUpForm.attributes.date.description": "Дата события, рабочие часы",
|
||||
"popUpForm.attributes.json.description": "Данные в JSON формате",
|
||||
"popUpForm.attributes.media.description": "Картинки, видео, PDF и другие виды файлов",
|
||||
"popUpForm.attributes.relation.description": "Связан с Типом Данных",
|
||||
"popUpForm.attributes.email.description": "Пользовательский email...",
|
||||
"popUpForm.attributes.password.description": "Пароль пользователя...",
|
||||
|
||||
"popUpForm.attributes.string.name": "String",
|
||||
"popUpForm.attributes.text.name": "Text",
|
||||
"popUpForm.attributes.boolean.name": "Boolean",
|
||||
"popUpForm.attributes.date.name": "Date",
|
||||
"popUpForm.attributes.json.name": "JSON",
|
||||
"popUpForm.attributes.media.name": "Media",
|
||||
"popUpForm.attributes.number.name": "Number",
|
||||
"popUpForm.attributes.relation.name": "Relation",
|
||||
"popUpForm.attributes.email.name": "Email",
|
||||
"popUpForm.attributes.password.name": "Password",
|
||||
"popUpForm.create": "Добавить новое",
|
||||
"popUpForm.edit": "Отредактировать",
|
||||
"popUpForm.field": "Поле",
|
||||
"popUpForm.create.contentType.header.title": "Добавить новый Тип Данных",
|
||||
"popUpForm.choose.attributes.header.title": "Добавить новое поле",
|
||||
"popUpForm.edit.contentType.header.title": "Отредактировать Тип Данных",
|
||||
|
||||
"popUpForm.navContainer.relation": "Определить связь",
|
||||
"popUpForm.navContainer.base": "Базовый настройки",
|
||||
"popUpForm.navContainer.advanced": "Расширенные настройки",
|
||||
|
||||
"popUpRelation.title": "Связь",
|
||||
|
||||
"popUpWarning.button.cancel": "Отменить",
|
||||
"popUpWarning.button.confirm": "Подтвердить",
|
||||
"popUpWarning.title": "Пожалуйста подтвердите",
|
||||
"popUpWarning.bodyMessage.contentType.delete":
|
||||
"Вы уверены, что хотите удалить этот Тип Данных?",
|
||||
"popUpWarning.bodyMessage.attribute.delete": "Вы уверены, что хотите удалить это поле?",
|
||||
|
||||
"table.contentType.title.plural": "Типы Данных доступны",
|
||||
"table.contentType.title.singular": "Тип Данных доступен",
|
||||
"table.contentType.head.name": "Название",
|
||||
"table.contentType.head.description": "Описание",
|
||||
"table.contentType.head.fields": "Поля",
|
||||
|
||||
"relation.oneToOne": "имеет один",
|
||||
"relation.oneToMany": "принадлежит многим",
|
||||
"relation.manyToOne": "имеет много",
|
||||
"relation.manyToMany": "имеет и принадлежит многим",
|
||||
"relation.attributeName.placeholder": "Пример: автор, категория, тег"
|
||||
}
|
||||
"popUpForm.attributes.string.name": "String",
|
||||
"popUpForm.attributes.text.name": "Text",
|
||||
"popUpForm.attributes.boolean.name": "Boolean",
|
||||
"popUpForm.attributes.date.name": "Date",
|
||||
"popUpForm.attributes.json.name": "JSON",
|
||||
"popUpForm.attributes.media.name": "Media",
|
||||
"popUpForm.attributes.number.name": "Number",
|
||||
"popUpForm.attributes.relation.name": "Relation",
|
||||
"popUpForm.attributes.email.name": "Email",
|
||||
"popUpForm.attributes.password.name": "Password",
|
||||
"popUpForm.attributes.enumeration.name": "Enumeration",
|
||||
"popUpForm.create": "Добавить новое",
|
||||
"popUpForm.edit": "Отредактировать",
|
||||
"popUpForm.field": "Поле",
|
||||
"popUpForm.create.contentType.header.title": "Добавить новый Тип Данных",
|
||||
"popUpForm.choose.attributes.header.title": "Добавить новое поле",
|
||||
"popUpForm.edit.contentType.header.title": "Отредактировать Тип Данных",
|
||||
|
||||
"popUpForm.navContainer.relation": "Определить связь",
|
||||
"popUpForm.navContainer.base": "Базовый настройки",
|
||||
"popUpForm.navContainer.advanced": "Расширенные настройки",
|
||||
|
||||
"popUpRelation.title": "Связь",
|
||||
|
||||
"popUpWarning.button.cancel": "Отменить",
|
||||
"popUpWarning.button.confirm": "Подтвердить",
|
||||
"popUpWarning.title": "Пожалуйста подтвердите",
|
||||
"popUpWarning.bodyMessage.contentType.delete":
|
||||
"Вы уверены, что хотите удалить этот Тип Данных?",
|
||||
"popUpWarning.bodyMessage.attribute.delete": "Вы уверены, что хотите удалить это поле?",
|
||||
|
||||
"table.contentType.title.plural": "Типы Данных доступны",
|
||||
"table.contentType.title.singular": "Тип Данных доступен",
|
||||
"table.contentType.head.name": "Название",
|
||||
"table.contentType.head.description": "Описание",
|
||||
"table.contentType.head.fields": "Поля",
|
||||
|
||||
"relation.oneToOne": "имеет один",
|
||||
"relation.oneToMany": "принадлежит многим",
|
||||
"relation.manyToOne": "имеет много",
|
||||
"relation.manyToMany": "имеет и принадлежит многим",
|
||||
"relation.attributeName.placeholder": "Пример: автор, категория, тег"
|
||||
}
|
||||
@ -52,6 +52,11 @@
|
||||
"form.attribute.item.json.name": "İsim",
|
||||
"form.attribute.item.boolean.name": "İsim",
|
||||
"form.attribute.item.string.name": "İsim",
|
||||
"form.attribute.item.enumeration.name": "İsim",
|
||||
"form.attribute.item.enumeration.rules": "Values (separate them with a comma)",
|
||||
"form.attribute.item.enumeration.placeholder": "Ex: morning,noon,evening",
|
||||
"form.attribute.item.enumeration.graphql": "Name override for GraphQL",
|
||||
"form.attribute.item.enumeration.graphql.description": "Allows you to override the default generated name for GraphQL",
|
||||
"form.attribute.item.appearance.name": "Görünüm",
|
||||
"form.attribute.item.appearance.label": "WYSIWYG olarak görüntüle",
|
||||
"form.attribute.item.appearance.description":
|
||||
@ -114,11 +119,11 @@
|
||||
"notification.error.message": "Bir hata oluştu.",
|
||||
"notification.info.contentType.creating.notSaved":
|
||||
"Lütfen yeni bir tane oluşturmadan önce mevcut İçerik Türünüzü kaydedin",
|
||||
"notification.info.disable": "This field is not editable for the moment...😮",
|
||||
"notification.info.optimized": "Bu eklenti, localStorage ile optimize edilmiştir",
|
||||
"notification.success.message.contentType.edit": "İçerik Türünüz güncellendi",
|
||||
"notification.success.message.contentType.create": "İçerik Türünüz oluşturuldu",
|
||||
"notification.success.contentTypeDeleted": "İçerik Türü silindi",
|
||||
"notification.info.enumeration": "This field is not editable for the moment...😮",
|
||||
|
||||
"popUpForm.attributes.string.description": "Başlıklar, adlar, paragraflar, isim listesi",
|
||||
"popUpForm.attributes.text.description": "Tanımlar, metin paragrafları, makaleler ",
|
||||
@ -130,7 +135,8 @@
|
||||
"popUpForm.attributes.relation.description": "Bir İçerik Türünü Belirtiyor",
|
||||
"popUpForm.attributes.email.description": "Kullanıcının e-postası...",
|
||||
"popUpForm.attributes.password.description": "Kullanıcı şifresi...",
|
||||
|
||||
"popUpForm.attributes.enumeration.description": "List of choices",
|
||||
|
||||
"popUpForm.attributes.string.name": "String",
|
||||
"popUpForm.attributes.text.name": "Yazı",
|
||||
"popUpForm.attributes.boolean.name": "Mantıksal",
|
||||
@ -141,6 +147,7 @@
|
||||
"popUpForm.attributes.relation.name": "İlişki",
|
||||
"popUpForm.attributes.email.name": "E-posta",
|
||||
"popUpForm.attributes.password.name": "Parola",
|
||||
"popUpForm.attributes.enumeration.name": "Enumeration",
|
||||
"popUpForm.create": "Yeni ekle",
|
||||
"popUpForm.edit": "Düzenle",
|
||||
"popUpForm.field": "Alan",
|
||||
|
||||
@ -52,6 +52,11 @@
|
||||
"form.attribute.item.json.name": "Name",
|
||||
"form.attribute.item.boolean.name": "Name",
|
||||
"form.attribute.item.string.name": "Name",
|
||||
"form.attribute.item.enumeration.name": "Name",
|
||||
"form.attribute.item.enumeration.rules": "Values (separate them with a comma)",
|
||||
"form.attribute.item.enumeration.placeholder": "Ex: morning,noon,evening",
|
||||
"form.attribute.item.enumeration.graphql": "Name override for GraphQL",
|
||||
"form.attribute.item.enumeration.graphql.description": "Allows you to override the default generated name for GraphQL",
|
||||
"form.attribute.item.appearance.name": "Appearance",
|
||||
"form.attribute.item.appearance.label": "Display as a WYSIWYG",
|
||||
"form.attribute.item.appearance.description":
|
||||
@ -113,11 +118,11 @@
|
||||
"notification.error.message": "发生错误",
|
||||
"notification.info.contentType.creating.notSaved":
|
||||
"在创建新Content Type之前,请保存当前Content Type",
|
||||
"notification.info.disable": "这个字段暂时不可编辑...😮",
|
||||
"notification.info.optimized": "这个插件是用本地存储优化的",
|
||||
"notification.success.message.contentType.edit": "你的Content Type已更新",
|
||||
"notification.success.message.contentType.create": "你的Content Type已创建",
|
||||
"notification.success.contentTypeDeleted": "这个Content Type已被删除",
|
||||
"notification.info.enumeration": "这个字段暂时不可编辑...😮",
|
||||
|
||||
"popUpForm.attributes.string.description": "标题、名称、段落、名称列表",
|
||||
"popUpForm.attributes.text.description": "描述、文本段落、文章",
|
||||
@ -129,7 +134,8 @@
|
||||
"popUpForm.attributes.relation.description": "引用其它 Content Type",
|
||||
"popUpForm.attributes.email.description": "用户email...",
|
||||
"popUpForm.attributes.password.description": "用户密码...",
|
||||
|
||||
"popUpForm.attributes.enumeration.description": "List of choices",
|
||||
|
||||
"popUpForm.attributes.string.name": "String",
|
||||
"popUpForm.attributes.text.name": "Text",
|
||||
"popUpForm.attributes.boolean.name": "Boolean",
|
||||
@ -140,6 +146,7 @@
|
||||
"popUpForm.attributes.relation.name": "Relation",
|
||||
"popUpForm.attributes.email.name": "Email",
|
||||
"popUpForm.attributes.password.name": "Password",
|
||||
"popUpForm.attributes.enumeration.name": "Enumeration",
|
||||
"popUpForm.create": "增加新的",
|
||||
"popUpForm.edit": "编辑",
|
||||
"popUpForm.field": "字段",
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
"attribute.email": "Email",
|
||||
"attribute.password": "密碼",
|
||||
"attribute.relation": "關聯其他結構",
|
||||
"attribute.enumeration": "Enumeration",
|
||||
"attribute.WYSIWYG": "Text (WYSIWYG)",
|
||||
|
||||
"contentType.temporaryDisplay": "(未儲存)",
|
||||
@ -49,6 +50,11 @@
|
||||
"form.attribute.item.json.name": "名稱",
|
||||
"form.attribute.item.boolean.name": "名稱",
|
||||
"form.attribute.item.string.name": "名稱",
|
||||
"form.attribute.item.enumeration.name": "名稱",
|
||||
"form.attribute.item.enumeration.rules": "Values (separate them with a comma)",
|
||||
"form.attribute.item.enumeration.placeholder": "Ex: morning,noon,evening",
|
||||
"form.attribute.item.enumeration.graphql": "Name override for GraphQL",
|
||||
"form.attribute.item.enumeration.graphql.description": "Allows you to override the default generated name for GraphQL",
|
||||
"form.attribute.item.settings.name": "設定",
|
||||
"form.attribute.item.requiredField": "必填欄位",
|
||||
"form.attribute.item.uniqueField": "唯一欄位",
|
||||
@ -105,6 +111,7 @@
|
||||
|
||||
"notification.error.message": "有錯誤發生了",
|
||||
"notification.info.contentType.creating.notSaved": "建立新的資料結構前,請先儲存現在的。",
|
||||
"notification.info.disable": "这个字段暂时不可编辑...😮",
|
||||
"notification.info.optimized": "這個擴充功能使用您的 localstorage 來最佳化",
|
||||
"notification.success.message.contentType.edit": "您的資料結構已更新",
|
||||
"notification.success.message.contentType.create": "您的資料結構已建立",
|
||||
@ -120,7 +127,8 @@
|
||||
"popUpForm.attributes.relation.description": "關聯到一個資料結構",
|
||||
"popUpForm.attributes.email.description": "使用者的 email...",
|
||||
"popUpForm.attributes.password.description": "使用者密碼...",
|
||||
|
||||
"popUpForm.attributes.enumeration.description": "List of choices",
|
||||
|
||||
"popUpForm.attributes.string.name": "字串",
|
||||
"popUpForm.attributes.text.name": "文字",
|
||||
"popUpForm.attributes.boolean.name": "是/否",
|
||||
@ -131,6 +139,7 @@
|
||||
"popUpForm.attributes.relation.name": "關聯其他結構",
|
||||
"popUpForm.attributes.email.name": "Email",
|
||||
"popUpForm.attributes.password.name": "密碼",
|
||||
"popUpForm.attributes.enumeration.name": "Enumeration",
|
||||
"popUpForm.create": "增加新的",
|
||||
"popUpForm.edit": "編輯",
|
||||
"popUpForm.field": "欄位",
|
||||
|
||||
@ -86,7 +86,7 @@ module.exports = {
|
||||
|
||||
updateModel: async ctx => {
|
||||
const { model } = ctx.params;
|
||||
const { name, description, connection, collectionName, attributes = [], plugin } = ctx.request.body;
|
||||
const { name, description, mainField, connection, collectionName, attributes = [], plugin } = ctx.request.body;
|
||||
|
||||
if (!name) return ctx.badRequest(null, [{ messages: [{ id: 'request.error.name.missing' }] }]);
|
||||
if (!_.includes(Service.getConnections(), connection)) return ctx.badRequest(null, [{ messages: [{ id: 'request.error.connection.unknow' }] }]);
|
||||
@ -122,6 +122,10 @@ module.exports = {
|
||||
};
|
||||
modelJSON.attributes = formatedAttributes;
|
||||
|
||||
if (mainField) {
|
||||
modelJSON.info.mainField = mainField;
|
||||
}
|
||||
|
||||
const clearRelationsErrors = Service.clearRelations(model, plugin);
|
||||
|
||||
if (!_.isEmpty(clearRelationsErrors)) {
|
||||
|
||||
@ -105,6 +105,7 @@ module.exports = {
|
||||
return {
|
||||
name: _.get(model, 'info.name', 'model.name.missing'),
|
||||
description: _.get(model, 'info.description', 'model.description.missing'),
|
||||
mainField: _.get(model, 'info.mainField', ''),
|
||||
connection: model.connection,
|
||||
collectionName: model.collectionName,
|
||||
attributes: attributes
|
||||
|
||||
@ -144,12 +144,15 @@ module.exports = {
|
||||
|
||||
/**
|
||||
* Convert Strapi type to GraphQL type.
|
||||
*
|
||||
* @param {Object} attribute Information about the attribute.
|
||||
* @param {Object} attribute.definition Definition of the attribute.
|
||||
* @param {String} attribute.modelName Name of the model which owns the attribute.
|
||||
* @param {String} attribute.attributeName Name of the attribute.
|
||||
* @return String
|
||||
*/
|
||||
|
||||
convertType: (definition = {}) => {
|
||||
// Type.
|
||||
convertType: function ({ definition = {}, modelName = '', attributeName = '' }) {
|
||||
// Type
|
||||
if (definition.type) {
|
||||
let type = 'String';
|
||||
|
||||
@ -167,6 +170,9 @@ module.exports = {
|
||||
case 'float':
|
||||
type = 'Float';
|
||||
break;
|
||||
case 'enumeration':
|
||||
type = this.convertEnumType(definition, modelName, attributeName);
|
||||
break;
|
||||
}
|
||||
|
||||
if (definition.required) {
|
||||
@ -178,9 +184,9 @@ module.exports = {
|
||||
|
||||
const ref = definition.model || definition.collection;
|
||||
|
||||
// Association.
|
||||
// Association
|
||||
if (ref && ref !== '*') {
|
||||
// Add bracket or not.
|
||||
// Add bracket or not
|
||||
const globalId = definition.plugin ?
|
||||
strapi.plugins[definition.plugin].models[ref].globalId:
|
||||
strapi.models[ref].globalId;
|
||||
@ -196,6 +202,16 @@ module.exports = {
|
||||
return definition.model ? 'Morph' : '[Morph]';
|
||||
},
|
||||
|
||||
/**
|
||||
* Convert Strapi enumeration to GraphQL Enum.
|
||||
* @param {Object} definition Definition of the attribute.
|
||||
* @param {String} model Name of the model which owns the attribute.
|
||||
* @param {String} field Name of the attribute.
|
||||
* @return String
|
||||
*/
|
||||
|
||||
convertEnumType: (definition, model, field) => definition.enumName ? definition.enumName : `ENUM_${model.toUpperCase()}_${field.toUpperCase()}`,
|
||||
|
||||
/**
|
||||
* Execute policies before the specified resolver.
|
||||
*
|
||||
@ -459,11 +475,26 @@ module.exports = {
|
||||
.filter(attribute => model.attributes[attribute].private !== true)
|
||||
.reduce((acc, attribute) => {
|
||||
// Convert our type to the GraphQL type.
|
||||
acc[attribute] = this.convertType(model.attributes[attribute]);
|
||||
acc[attribute] = this.convertType({
|
||||
definition: model.attributes[attribute],
|
||||
modelName: globalId,
|
||||
attributeName: attribute,
|
||||
});
|
||||
|
||||
return acc;
|
||||
}, initialState);
|
||||
|
||||
// Detect enum and generate it for the schema definition
|
||||
const enums = Object.keys(model.attributes)
|
||||
.filter(definition => definition.type === 'enumeration')
|
||||
.map((attribute) => {
|
||||
const definition = model.attributes[attribute];
|
||||
|
||||
return `enum ${this.convertEnumType(definition, globalId, attribute)} { ${definition.enum.join(' \n ')} }`;
|
||||
}).join(' ');
|
||||
|
||||
acc.definition += enums;
|
||||
|
||||
// Add parameters to optimize association query.
|
||||
(model.associations || [])
|
||||
.filter(association => association.type === 'collection')
|
||||
|
||||
@ -3,7 +3,8 @@ module.exports = {
|
||||
actions: {
|
||||
create: 'User.create', // Use the User plugin's controller.
|
||||
update: 'User.update',
|
||||
destroy: 'User.destroy'
|
||||
destroy: 'User.destroy',
|
||||
deleteall: 'User.destroyAll',
|
||||
},
|
||||
attributes: {
|
||||
username: {
|
||||
|
||||
@ -70,12 +70,14 @@ module.exports = {
|
||||
*/
|
||||
|
||||
create: async (ctx) => {
|
||||
if ((await strapi.store({
|
||||
const advanced = await strapi.store({
|
||||
environment: '',
|
||||
type: 'plugin',
|
||||
name: 'users-permissions',
|
||||
key: 'advanced'
|
||||
}).get()).unique_email && ctx.request.body.email) {
|
||||
}).get();
|
||||
|
||||
if (advanced.unique_email && ctx.request.body.email) {
|
||||
const user = await strapi.query('user', 'users-permissions').findOne({ email: ctx.request.body.email });
|
||||
|
||||
if (user) {
|
||||
@ -83,6 +85,12 @@ module.exports = {
|
||||
}
|
||||
}
|
||||
|
||||
if (!ctx.request.body.role) {
|
||||
const defaultRole = await strapi.query('role', 'users-permissions').findOne({ type: advanced.default_role }, []);
|
||||
|
||||
ctx.request.body.role = defaultRole._id || defaultRole.id;
|
||||
}
|
||||
|
||||
try {
|
||||
const data = await strapi.plugins['users-permissions'].services.user.add(ctx.request.body);
|
||||
// Send 201 `created`
|
||||
@ -153,6 +161,13 @@ module.exports = {
|
||||
destroy: async (ctx) => {
|
||||
const data = await strapi.plugins['users-permissions'].services.user.remove(ctx.params);
|
||||
|
||||
// Send 200 `ok`
|
||||
ctx.send(data);
|
||||
},
|
||||
|
||||
destroyAll: async (ctx) => {
|
||||
const data = await strapi.plugins['users-permissions'].services.user.removeAll(ctx.params, ctx.request.query);
|
||||
|
||||
// Send 200 `ok`
|
||||
ctx.send(data);
|
||||
}
|
||||
|
||||
@ -40,4 +40,4 @@
|
||||
"configurable": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,13 +70,13 @@ exports.connect = (provider, query) => {
|
||||
return resolve([null, [{ messages: [{ id: 'Auth.form.error.email.taken' }] }], 'Email is already taken.']);
|
||||
}
|
||||
|
||||
// Retrieve role `public`.
|
||||
const publicRole = await strapi.query('role', 'users-permissions').findOne({ type: 'public' }, []);
|
||||
// Retrieve default role.
|
||||
const defaultRole = await strapi.query('role', 'users-permissions').findOne({ type: advanced.default_role }, []);
|
||||
|
||||
// Create the new user.
|
||||
const params = _.assign(profile, {
|
||||
provider: provider,
|
||||
role: publicRole._id || publicRole.id
|
||||
role: defaultRole._id || defaultRole.id
|
||||
});
|
||||
|
||||
const createdUser = await strapi.query('user', 'users-permissions').create(params);
|
||||
|
||||
@ -115,6 +115,30 @@ module.exports = {
|
||||
return strapi.query('user', 'users-permissions').delete(params);
|
||||
},
|
||||
|
||||
removeAll: async (params, query) => {
|
||||
// Use Content Manager business logic to handle relation.
|
||||
if (strapi.plugins['content-manager']) {
|
||||
params.model = 'user';
|
||||
query.source = 'users-permissions';
|
||||
|
||||
return await strapi.plugins['content-manager'].services['contentmanager'].deleteMany(params, query);
|
||||
}
|
||||
|
||||
// TODO remove this logic when we develop plugins' dependencies
|
||||
const primaryKey = strapi.query('user', 'users-permissions').primaryKey;
|
||||
const toRemove = Object.keys(query).reduce((acc, curr) => {
|
||||
if (curr !== 'source') {
|
||||
return acc.concat([query[curr]]);
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
return strapi.query('user', 'users-permissions').deleteMany({
|
||||
[primaryKey]: toRemove,
|
||||
});
|
||||
},
|
||||
|
||||
validatePassword: (password, hash) => {
|
||||
return bcrypt.compareSync(password, hash);
|
||||
}
|
||||
|
||||
@ -474,7 +474,7 @@ module.exports = {
|
||||
|
||||
let type;
|
||||
|
||||
if (_.includes(['ne', 'lt', 'gt', 'lte', 'gte', 'contains', 'containss'], _.last(suffix))) {
|
||||
if (_.includes(['ne', 'lt', 'gt', 'lte', 'gte', 'contains', 'containss', 'in'], _.last(suffix))) {
|
||||
type = `_${_.last(suffix)}`;
|
||||
key = _.dropRight(suffix).join('_');
|
||||
} else {
|
||||
|
||||
@ -125,5 +125,43 @@
|
||||
"name":"reference",
|
||||
"description":"",
|
||||
"collectionName":""
|
||||
},
|
||||
"product": {
|
||||
"attributes":[
|
||||
{
|
||||
"name":"name",
|
||||
"params":{
|
||||
"appearance":{
|
||||
"WYSIWYG":false
|
||||
},
|
||||
"multiple":false,
|
||||
"type":"string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name":"description",
|
||||
"params":{
|
||||
"appearance":{
|
||||
"WYSIWYG":true
|
||||
},
|
||||
"multiple":false,
|
||||
"type":"text"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name":"published",
|
||||
"params":{
|
||||
"appearance":{
|
||||
"WYSIWYG":false
|
||||
},
|
||||
"multiple":false,
|
||||
"type":"boolean"
|
||||
}
|
||||
}
|
||||
],
|
||||
"connection":"default",
|
||||
"name":"product",
|
||||
"description":"",
|
||||
"collectionName":""
|
||||
}
|
||||
}
|
||||
|
||||
@ -736,6 +736,84 @@ describe('Test oneToOne relation (article - reference) with Content Manager', ()
|
||||
);
|
||||
});
|
||||
|
||||
describe('Test route /count from generated API', () => {
|
||||
beforeAll(() => {
|
||||
data = {
|
||||
articles: [],
|
||||
tags: []
|
||||
};
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await restart(rq);
|
||||
}, 60000);
|
||||
|
||||
test(
|
||||
'Create new product API',
|
||||
async () => {
|
||||
await rq({
|
||||
url: `/content-type-builder/models`,
|
||||
method: 'POST',
|
||||
body: form.product,
|
||||
json: true
|
||||
});
|
||||
}
|
||||
);
|
||||
test(
|
||||
'Create product1',
|
||||
async () => {
|
||||
let body = await rq({
|
||||
url: `/content-manager/explorer/product/?source=content-manager`,
|
||||
method: 'POST',
|
||||
formData: {
|
||||
name: 'product1'
|
||||
}
|
||||
});
|
||||
|
||||
body = JSON.parse(body);
|
||||
|
||||
data.tags.push(body);
|
||||
|
||||
expect(body.id);
|
||||
expect(body.name).toBe('product1');
|
||||
}
|
||||
);
|
||||
test(
|
||||
'Create product2',
|
||||
async () => {
|
||||
let body = await rq({
|
||||
url: `/content-manager/explorer/product/?source=content-manager`,
|
||||
method: 'POST',
|
||||
formData: {
|
||||
name: 'product2'
|
||||
}
|
||||
});
|
||||
|
||||
body = JSON.parse(body);
|
||||
|
||||
data.tags.push(body);
|
||||
|
||||
expect(body.id);
|
||||
expect(body.name).toBe('product2');
|
||||
}
|
||||
);
|
||||
test(
|
||||
'Count products',
|
||||
async () => {
|
||||
let body = await rq({
|
||||
url: `/product/count`,
|
||||
method: 'GET'
|
||||
});
|
||||
|
||||
body = JSON.parse(body);
|
||||
|
||||
data.tags.push(body);
|
||||
|
||||
expect(body).toBe(2);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
describe('Delete test APIs', () => {
|
||||
beforeEach(async () => {
|
||||
await restart(rq);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user