mirror of
https://github.com/strapi/strapi.git
synced 2025-09-16 03:48:22 +00:00
Merge branch 'master' into docs-client
This commit is contained in:
commit
d44b88a0d2
@ -132,6 +132,16 @@ Run the following statement in your database:
|
||||
:::
|
||||
::::
|
||||
|
||||
## Date type changes
|
||||
|
||||
We introduced new types in the admin panel: `date`, `datetime` and `time`. Before all of those types where saved as `datetime`.
|
||||
|
||||
You will need to change the type of your fields from `date` to `datetime` if you don't want to migrate your data.
|
||||
|
||||
- To migrate yout old `date` to `datetime`, change the field type from `date` to `datetime`. NO data migration is required.
|
||||
- To migrate your old `date` to new `date`, you will need to migrate yout data to be of the format: `YYYY-MM-DD`
|
||||
- To migrate your old `date` to the new `time`, change the field type from `date` to `time`. You will also need to transform them to be of the format: `HH:mm:ss.SSS`
|
||||
|
||||
## Groups become Components
|
||||
|
||||
If you were using the groups feature you will need to apply some changes:
|
||||
@ -370,10 +380,19 @@ _Repeat this query for every join table where you are using this component._
|
||||
|
||||
```sql
|
||||
UPDATE restaurant_components
|
||||
SET component_type = 'groups_old_table_name'
|
||||
WHERE component_type = 'components_new_table_name';
|
||||
SET component_type = 'components_new_table_name'
|
||||
WHERE component_type = 'groups_old_table_name';
|
||||
```
|
||||
|
||||
**4. If you store files in groups, update the `related_type` values**
|
||||
|
||||
```sql
|
||||
UPDATE upload_file_morph
|
||||
SET related_type = 'components_new_table_name'
|
||||
WHERE related_type = 'groups_old_table_name';
|
||||
```
|
||||
|
||||
|
||||
#### Mongo
|
||||
|
||||
In `mongo` the relation between a content type and its components is held in an array of references. To know which component type it referes to, the array also contains a `kind` attribute containing the component Schema name.
|
||||
@ -450,6 +469,45 @@ We created new home pages when your go to your api url.
|
||||
You will need to copy `index.html` and `production.html` into your `public` folder.
|
||||
You can find those two files [here](https://github.com/strapi/strapi/tree/master/packages/strapi-generate-new/lib/resources/files/public).
|
||||
|
||||
## Updating `csp` options
|
||||
|
||||
The admin panel contains certain assets that use `data:img;base64` images. To allow rendering of those assets you can update the files `./config/environments/{env}/security.json` as follows:
|
||||
|
||||
**Before**
|
||||
|
||||
```json
|
||||
{
|
||||
"csp": {
|
||||
"enabled": true,
|
||||
"policy": [
|
||||
{
|
||||
"img-src": "'self' http:"
|
||||
},
|
||||
"block-all-mixed-content"
|
||||
]
|
||||
}
|
||||
//....
|
||||
}
|
||||
```
|
||||
|
||||
**After**
|
||||
|
||||
```json
|
||||
{
|
||||
"csp": {
|
||||
"enabled": true,
|
||||
"policy": ["block-all-mixed-content"]
|
||||
}
|
||||
//....
|
||||
}
|
||||
```
|
||||
|
||||
If you need more fine control you can also simply add the `data:` option to the `img-src` option.
|
||||
|
||||
## Rebuilding your administration panel
|
||||
|
||||
Now delete the `.cache` and `build` folders. Then run `yarn develop`.
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
|
@ -43,11 +43,6 @@
|
||||
"full_name": {
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"dz": {
|
||||
"type": "dynamiczone",
|
||||
"components": [],
|
||||
"min": 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "getstarted",
|
||||
"private": true,
|
||||
"version": "3.0.0-beta.18.1",
|
||||
"version": "3.0.0-beta.18.2",
|
||||
"description": "A Strapi application.",
|
||||
"scripts": {
|
||||
"develop": "strapi develop",
|
||||
@ -15,21 +15,21 @@
|
||||
"mysql": "^2.17.1",
|
||||
"pg": "^7.10.0",
|
||||
"sqlite3": "^4.0.6",
|
||||
"strapi": "3.0.0-beta.18.1",
|
||||
"strapi-admin": "3.0.0-beta.18.1",
|
||||
"strapi-connector-bookshelf": "3.0.0-beta.18.1",
|
||||
"strapi-connector-mongoose": "3.0.0-beta.18.1",
|
||||
"strapi-middleware-views": "3.0.0-beta.18.1",
|
||||
"strapi-plugin-content-manager": "3.0.0-beta.18.1",
|
||||
"strapi-plugin-content-type-builder": "3.0.0-beta.18.1",
|
||||
"strapi-plugin-documentation": "3.0.0-beta.18.1",
|
||||
"strapi-plugin-email": "3.0.0-beta.18.1",
|
||||
"strapi-plugin-graphql": "3.0.0-beta.18.1",
|
||||
"strapi-plugin-upload": "3.0.0-beta.18.1",
|
||||
"strapi-plugin-users-permissions": "3.0.0-beta.18.1",
|
||||
"strapi-provider-email-mailgun": "3.0.0-beta.18.1",
|
||||
"strapi-provider-upload-aws-s3": "3.0.0-beta.18.1",
|
||||
"strapi-utils": "3.0.0-beta.18.1"
|
||||
"strapi": "3.0.0-beta.18.2",
|
||||
"strapi-admin": "3.0.0-beta.18.2",
|
||||
"strapi-connector-bookshelf": "3.0.0-beta.18.2",
|
||||
"strapi-connector-mongoose": "3.0.0-beta.18.2",
|
||||
"strapi-middleware-views": "3.0.0-beta.18.2",
|
||||
"strapi-plugin-content-manager": "3.0.0-beta.18.2",
|
||||
"strapi-plugin-content-type-builder": "3.0.0-beta.18.2",
|
||||
"strapi-plugin-documentation": "3.0.0-beta.18.2",
|
||||
"strapi-plugin-email": "3.0.0-beta.18.2",
|
||||
"strapi-plugin-graphql": "3.0.0-beta.18.2",
|
||||
"strapi-plugin-upload": "3.0.0-beta.18.2",
|
||||
"strapi-plugin-users-permissions": "3.0.0-beta.18.2",
|
||||
"strapi-provider-email-mailgun": "3.0.0-beta.18.2",
|
||||
"strapi-provider-upload-aws-s3": "3.0.0-beta.18.2",
|
||||
"strapi-utils": "3.0.0-beta.18.2"
|
||||
},
|
||||
"strapi": {
|
||||
"uuid": "getstarted"
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "3.0.0-beta.18.1",
|
||||
"version": "3.0.0-beta.18.2",
|
||||
"packages": [
|
||||
"packages/*",
|
||||
"examples/*"
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "create-strapi-app",
|
||||
"version": "3.0.0-beta.18.1",
|
||||
"version": "3.0.0-beta.18.2",
|
||||
"description": "Generate a new Strapi application.",
|
||||
"license": "MIT",
|
||||
"homepage": "http://strapi.io",
|
||||
@ -21,7 +21,7 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"commander": "^2.20.0",
|
||||
"strapi-generate-new": "3.0.0-beta.18.1"
|
||||
"strapi-generate-new": "3.0.0-beta.18.2"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"no tests yet\""
|
||||
|
@ -26,9 +26,9 @@ const Logout = ({ history: { push } }) => {
|
||||
const id = get(auth.getUserInfo(), 'id');
|
||||
|
||||
push({
|
||||
pathname: `/plugins/content-manager/administrator/${id}`,
|
||||
pathname: `/plugins/content-manager/strapi::administrator/${id}`,
|
||||
search:
|
||||
'?redirectUrl=/plugins/content-manager/administrator/?page=0&limit=0&sort=id&source=admin',
|
||||
'?redirectUrl=/plugins/content-manager/strapi::administrator/?_page=0&_limit=0&_sort=id',
|
||||
});
|
||||
};
|
||||
const handleGoToAdministrator = () => {
|
||||
|
@ -48,7 +48,7 @@ function App(props) {
|
||||
|
||||
if (uuid) {
|
||||
try {
|
||||
await fetch('https://analytics.strapi.io/track', {
|
||||
fetch('https://analytics.strapi.io/track', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
event: 'didInitializeAdministration',
|
||||
|
@ -130,6 +130,7 @@
|
||||
"components.Input.error.validation.required": "This value is required.",
|
||||
"components.Input.error.validation.unique": "This value is already used.",
|
||||
"components.InputSelect.option.placeholder": "Choose here",
|
||||
"component.Input.error.validation.integer": "The value must be an integer",
|
||||
"components.ListRow.empty": "There is no data to be shown.",
|
||||
"components.OverlayBlocker.description": "You're using a feature that needs the server to restart. Please wait until the server is up.",
|
||||
"components.OverlayBlocker.description.serverError": "The server should have restarted, please check your logs in the terminal.",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-admin",
|
||||
"version": "3.0.0-beta.18.1",
|
||||
"version": "3.0.0-beta.18.2",
|
||||
"description": "Strapi Admin",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -80,8 +80,8 @@
|
||||
"reselect": "^3.0.1",
|
||||
"sanitize.css": "^4.1.0",
|
||||
"shelljs": "^0.7.8",
|
||||
"strapi-helper-plugin": "3.0.0-beta.18.1",
|
||||
"strapi-utils": "3.0.0-beta.18.1",
|
||||
"strapi-helper-plugin": "3.0.0-beta.18.2",
|
||||
"strapi-utils": "3.0.0-beta.18.2",
|
||||
"style-loader": "^0.23.1",
|
||||
"styled-components": "^4.2.0",
|
||||
"terser-webpack-plugin": "^1.2.3",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-connector-bookshelf",
|
||||
"version": "3.0.0-beta.18.1",
|
||||
"version": "3.0.0-beta.18.2",
|
||||
"description": "Bookshelf hook for the Strapi framework",
|
||||
"homepage": "http://strapi.io",
|
||||
"keywords": [
|
||||
@ -22,7 +22,7 @@
|
||||
"lodash": "^4.17.11",
|
||||
"pluralize": "^7.0.0",
|
||||
"rimraf": "^2.6.3",
|
||||
"strapi-utils": "3.0.0-beta.18.1"
|
||||
"strapi-utils": "3.0.0-beta.18.2"
|
||||
},
|
||||
"strapi": {
|
||||
"dependencies": [
|
||||
|
@ -364,6 +364,14 @@ const createOnFetchPopulateFn = ({
|
||||
}
|
||||
});
|
||||
|
||||
if (definition.modelType === 'component') {
|
||||
definition.associations
|
||||
.filter(ast => ast.autoPopulate !== false)
|
||||
.forEach(ast => {
|
||||
this.populate({ path: ast.alias });
|
||||
});
|
||||
}
|
||||
|
||||
componentAttributes.forEach(key => {
|
||||
this.populate({ path: `${key}.ref` });
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-connector-mongoose",
|
||||
"version": "3.0.0-beta.18.1",
|
||||
"version": "3.0.0-beta.18.2",
|
||||
"description": "Mongoose hook for the Strapi framework",
|
||||
"homepage": "http://strapi.io",
|
||||
"keywords": [
|
||||
@ -20,7 +20,7 @@
|
||||
"mongoose-float": "^1.0.4",
|
||||
"mongoose-long": "^0.2.1",
|
||||
"pluralize": "^7.0.0",
|
||||
"strapi-utils": "3.0.0-beta.18.1"
|
||||
"strapi-utils": "3.0.0-beta.18.2"
|
||||
},
|
||||
"author": {
|
||||
"email": "hi@strapi.io",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-database",
|
||||
"version": "3.0.0-beta.18.1",
|
||||
"version": "3.0.0-beta.18.2",
|
||||
"description": "Strapi's database layer",
|
||||
"homepage": "http://strapi.io",
|
||||
"main": "./lib/index.js",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-generate-api",
|
||||
"version": "3.0.0-beta.18.1",
|
||||
"version": "3.0.0-beta.18.2",
|
||||
"description": "Generate an API for a Strapi application.",
|
||||
"homepage": "http://strapi.io",
|
||||
"keywords": [
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-generate-controller",
|
||||
"version": "3.0.0-beta.18.1",
|
||||
"version": "3.0.0-beta.18.2",
|
||||
"description": "Generate a controller for a Strapi API.",
|
||||
"homepage": "http://strapi.io",
|
||||
"keywords": [
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-generate-model",
|
||||
"version": "3.0.0-beta.18.1",
|
||||
"version": "3.0.0-beta.18.2",
|
||||
"description": "Generate a model for a Strapi API.",
|
||||
"homepage": "http://strapi.io",
|
||||
"keywords": [
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-generate-new",
|
||||
"version": "3.0.0-beta.18.1",
|
||||
"version": "3.0.0-beta.18.2",
|
||||
"description": "Generate a new Strapi application.",
|
||||
"homepage": "http://strapi.io",
|
||||
"keywords": [
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-generate-plugin",
|
||||
"version": "3.0.0-beta.18.1",
|
||||
"version": "3.0.0-beta.18.2",
|
||||
"description": "Generate an plugin for a Strapi application.",
|
||||
"homepage": "http://strapi.io",
|
||||
"keywords": [
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-generate-policy",
|
||||
"version": "3.0.0-beta.18.1",
|
||||
"version": "3.0.0-beta.18.2",
|
||||
"description": "Generate a policy for a Strapi API.",
|
||||
"homepage": "http://strapi.io",
|
||||
"keywords": [
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-generate-service",
|
||||
"version": "3.0.0-beta.18.1",
|
||||
"version": "3.0.0-beta.18.2",
|
||||
"description": "Generate a service for a Strapi API.",
|
||||
"homepage": "http://strapi.io",
|
||||
"keywords": [
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-generate",
|
||||
"version": "3.0.0-beta.18.1",
|
||||
"version": "3.0.0-beta.18.2",
|
||||
"description": "Master of ceremonies for the Strapi generators.",
|
||||
"homepage": "http://strapi.io",
|
||||
"keywords": [
|
||||
@ -20,7 +20,7 @@
|
||||
"fs-extra": "^8.0.1",
|
||||
"lodash": "^4.17.11",
|
||||
"reportback": "^2.0.2",
|
||||
"strapi-utils": "3.0.0-beta.18.1"
|
||||
"strapi-utils": "3.0.0-beta.18.2"
|
||||
},
|
||||
"author": {
|
||||
"name": "Strapi team",
|
||||
|
@ -8,6 +8,7 @@ const errorsTrads = {
|
||||
regex: 'components.Input.error.validation.regex',
|
||||
required: 'components.Input.error.validation.required',
|
||||
unique: 'components.Input.error.validation.unique',
|
||||
integer: 'component.Input.error.validation.integer',
|
||||
};
|
||||
|
||||
export default errorsTrads;
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-helper-plugin",
|
||||
"version": "3.0.0-beta.18.1",
|
||||
"version": "3.0.0-beta.18.2",
|
||||
"description": "Helper for Strapi plugins development",
|
||||
"files": [
|
||||
"dist"
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-hook-ejs",
|
||||
"version": "3.0.0-beta.18.1",
|
||||
"version": "3.0.0-beta.18.2",
|
||||
"description": "EJS hook for the Strapi framework",
|
||||
"homepage": "http://strapi.io",
|
||||
"keywords": [
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-hook-redis",
|
||||
"version": "3.0.0-beta.18.1",
|
||||
"version": "3.0.0-beta.18.2",
|
||||
"description": "Redis hook for the Strapi framework",
|
||||
"homepage": "http://strapi.io",
|
||||
"keywords": [
|
||||
@ -19,7 +19,7 @@
|
||||
"lodash": "^4.17.11",
|
||||
"rimraf": "^2.6.3",
|
||||
"stack-trace": "0.0.10",
|
||||
"strapi-utils": "3.0.0-beta.18.1"
|
||||
"strapi-utils": "3.0.0-beta.18.2"
|
||||
},
|
||||
"author": {
|
||||
"email": "hi@strapi.io",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-middleware-views",
|
||||
"version": "3.0.0-beta.18.1",
|
||||
"version": "3.0.0-beta.18.2",
|
||||
"description": "Views middleware to enable server-side rendering for the Strapi framework",
|
||||
"homepage": "http://strapi.io",
|
||||
"keywords": [
|
||||
|
@ -8,6 +8,7 @@ import Tooltip from './Tooltip';
|
||||
const DynamicComponent = ({
|
||||
componentUid,
|
||||
friendlyName,
|
||||
icon,
|
||||
setIsOverDynamicZone,
|
||||
}) => {
|
||||
const [state, setState] = useState(false);
|
||||
@ -22,6 +23,7 @@ const DynamicComponent = ({
|
||||
<DynamicComponentCard
|
||||
componentUid={componentUid}
|
||||
friendlyName={friendlyName}
|
||||
icon={icon}
|
||||
isOver={state}
|
||||
onClick={() => {
|
||||
push(
|
||||
@ -38,11 +40,13 @@ const DynamicComponent = ({
|
||||
|
||||
DynamicComponent.defaultProps = {
|
||||
friendlyName: '',
|
||||
icon: 'smile',
|
||||
};
|
||||
|
||||
DynamicComponent.propTypes = {
|
||||
componentUid: PropTypes.string.isRequired,
|
||||
friendlyName: PropTypes.string,
|
||||
icon: PropTypes.string,
|
||||
setIsOverDynamicZone: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
|
@ -46,8 +46,8 @@ const DraggedFieldWithPreview = forwardRef(
|
||||
'dynamiczone',
|
||||
];
|
||||
const withLongerHeight = higherFields.includes(type) && !dragStart;
|
||||
const getCompoFriendlyName = uid =>
|
||||
get(componentLayouts, [uid, 'schema', 'info', 'name'], '');
|
||||
const getCompoInfos = uid =>
|
||||
get(componentLayouts, [uid, 'schema', 'info'], { name: '', icon: '' });
|
||||
|
||||
const componentData = get(componentLayouts, [componentUid], {});
|
||||
const componentLayout = get(componentData, ['layouts', 'edit'], []);
|
||||
@ -146,11 +146,14 @@ const DraggedFieldWithPreview = forwardRef(
|
||||
{type === 'dynamiczone' && (
|
||||
<DynamicZoneWrapper>
|
||||
{dynamicZoneComponents.map(compo => {
|
||||
const { name, icon } = getCompoInfos(compo);
|
||||
|
||||
return (
|
||||
<DynamicComponent
|
||||
key={compo}
|
||||
componentUid={compo}
|
||||
friendlyName={getCompoFriendlyName(compo)}
|
||||
friendlyName={name}
|
||||
icon={icon}
|
||||
setIsOverDynamicZone={setIsOverDynamicZone}
|
||||
/>
|
||||
);
|
||||
|
@ -14,7 +14,6 @@ const getInputType = (type = '') => {
|
||||
switch (toLower(type)) {
|
||||
case 'boolean':
|
||||
return 'bool';
|
||||
case 'biginteger':
|
||||
case 'decimal':
|
||||
case 'float':
|
||||
case 'integer':
|
||||
@ -114,6 +113,14 @@ function Inputs({ autoFocus, keys, layout, name, onBlur }) {
|
||||
inputValue = [];
|
||||
}
|
||||
|
||||
let step;
|
||||
|
||||
if (type === 'float' || type === 'decimal') {
|
||||
step = 'any';
|
||||
} else {
|
||||
step = '1';
|
||||
}
|
||||
|
||||
return (
|
||||
<FormattedMessage id={errorId}>
|
||||
{error => {
|
||||
@ -141,6 +148,7 @@ function Inputs({ autoFocus, keys, layout, name, onBlur }) {
|
||||
onBlur={onBlur}
|
||||
onChange={onChange}
|
||||
options={get(attribute, 'enum', [])}
|
||||
step={step}
|
||||
type={getInputType(type)}
|
||||
validations={validations}
|
||||
value={inputValue}
|
||||
|
@ -52,6 +52,7 @@ const RepeatableComponent = ({
|
||||
const errorsArray = componentErrorKeys.map(key =>
|
||||
get(formErrors, [key, 'id'], '')
|
||||
);
|
||||
|
||||
const hasMinError =
|
||||
get(errorsArray, [0], '').includes('min') &&
|
||||
!collapses.some(obj => obj.isOpen === true);
|
||||
|
@ -196,6 +196,10 @@ const EditViewDataManagerProvider = ({
|
||||
inputValue = null;
|
||||
}
|
||||
|
||||
if (type === 'biginteger' && value === '') {
|
||||
inputValue = null;
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: 'ON_CHANGE',
|
||||
keys: name.split('.'),
|
||||
@ -262,7 +266,7 @@ const EditViewDataManagerProvider = ({
|
||||
});
|
||||
redirectToPreviousPage();
|
||||
} catch (err) {
|
||||
console.log({ err });
|
||||
console.error({ err });
|
||||
const error = get(
|
||||
err,
|
||||
['response', 'payload', 'message', '0', 'messages', '0', 'id'],
|
||||
@ -277,6 +281,7 @@ const EditViewDataManagerProvider = ({
|
||||
}
|
||||
} catch (err) {
|
||||
const errors = getYupInnerErrors(err);
|
||||
console.error({ err, errors });
|
||||
|
||||
dispatch({
|
||||
type: 'SUBMIT_ERRORS',
|
||||
|
@ -7,6 +7,7 @@ import {
|
||||
isArray,
|
||||
isEmpty,
|
||||
isNaN,
|
||||
toNumber,
|
||||
} from 'lodash';
|
||||
import * as yup from 'yup';
|
||||
import { translatedErrors as errorsTrads } from 'strapi-helper-plugin';
|
||||
@ -28,6 +29,34 @@ yup.addMethod(yup.array, 'notEmptyMin', function(min) {
|
||||
});
|
||||
});
|
||||
|
||||
yup.addMethod(yup.string, 'isInferior', function(message, max) {
|
||||
return this.test('isInferior', message, function(value) {
|
||||
if (!value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Number.isNaN(toNumber(value))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return toNumber(max) >= toNumber(value);
|
||||
});
|
||||
});
|
||||
|
||||
yup.addMethod(yup.string, 'isSuperior', function(message, min) {
|
||||
return this.test('isSuperior', message, function(value) {
|
||||
if (!value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Number.isNaN(toNumber(value))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return toNumber(value) >= toNumber(min);
|
||||
});
|
||||
});
|
||||
|
||||
const getAttributes = data => get(data, ['schema', 'attributes'], {});
|
||||
|
||||
const createYupSchema = (model, { components }) => {
|
||||
@ -80,7 +109,26 @@ const createYupSchema = (model, { components }) => {
|
||||
.nullable();
|
||||
|
||||
if (min) {
|
||||
componentSchema = componentSchema.min(min, errorsTrads.min);
|
||||
componentSchema = yup.lazy(array => {
|
||||
if (attribute.required) {
|
||||
return yup
|
||||
.array()
|
||||
.of(componentFieldSchema)
|
||||
.defined()
|
||||
.min(min, errorsTrads.min);
|
||||
}
|
||||
|
||||
let schema = yup
|
||||
.array()
|
||||
.of(componentFieldSchema)
|
||||
.nullable();
|
||||
|
||||
if (array && !isEmpty(array)) {
|
||||
schema = schema.min(min, errorsTrads.min);
|
||||
}
|
||||
|
||||
return schema;
|
||||
});
|
||||
}
|
||||
|
||||
if (max) {
|
||||
@ -146,6 +194,7 @@ const createYupSchema = (model, { components }) => {
|
||||
|
||||
const createYupSchemaAttribute = (type, validations) => {
|
||||
let schema = yup.mixed();
|
||||
|
||||
if (
|
||||
['string', 'text', 'richtext', 'email', 'password', 'enumeration'].includes(
|
||||
type
|
||||
@ -153,6 +202,7 @@ const createYupSchemaAttribute = (type, validations) => {
|
||||
) {
|
||||
schema = yup.string();
|
||||
}
|
||||
|
||||
if (type === 'json') {
|
||||
schema = yup
|
||||
.mixed(errorsTrads.json)
|
||||
@ -183,16 +233,22 @@ const createYupSchemaAttribute = (type, validations) => {
|
||||
if (type === 'email') {
|
||||
schema = schema.email(errorsTrads.email);
|
||||
}
|
||||
|
||||
if (['number', 'integer', 'biginteger', 'float', 'decimal'].includes(type)) {
|
||||
schema = yup
|
||||
.number()
|
||||
.transform(cv => (isNaN(cv) ? undefined : cv))
|
||||
.typeError();
|
||||
}
|
||||
|
||||
if (['date', 'datetime'].includes(type)) {
|
||||
schema = yup.date();
|
||||
}
|
||||
|
||||
if (type === 'biginteger') {
|
||||
schema = yup.string().matches(/^\d*$/);
|
||||
}
|
||||
|
||||
Object.keys(validations).forEach(validation => {
|
||||
const validationValue = validations[validation];
|
||||
if (
|
||||
@ -205,15 +261,25 @@ const createYupSchemaAttribute = (type, validations) => {
|
||||
case 'required':
|
||||
schema = schema.required(errorsTrads.required);
|
||||
break;
|
||||
case 'max':
|
||||
schema = schema.max(validationValue, errorsTrads.max);
|
||||
case 'max': {
|
||||
if (type === 'biginteger') {
|
||||
schema = schema.isInferior(errorsTrads.max, validationValue);
|
||||
} else {
|
||||
schema = schema.max(validationValue, errorsTrads.max);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'maxLength':
|
||||
schema = schema.max(validationValue, errorsTrads.maxLength);
|
||||
break;
|
||||
case 'min':
|
||||
schema = schema.min(validationValue, errorsTrads.min);
|
||||
case 'min': {
|
||||
if (type === 'biginteger') {
|
||||
schema = schema.isSuperior(errorsTrads.min, validationValue);
|
||||
} else {
|
||||
schema = schema.min(validationValue, errorsTrads.min);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'minLength':
|
||||
schema = schema.min(validationValue, errorsTrads.minLength);
|
||||
break;
|
||||
@ -249,6 +315,7 @@ const createYupSchemaAttribute = (type, validations) => {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return schema;
|
||||
};
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-plugin-content-manager",
|
||||
"version": "3.0.0-beta.18.1",
|
||||
"version": "3.0.0-beta.18.2",
|
||||
"description": "A powerful UI to easily manage your data.",
|
||||
"strapi": {
|
||||
"name": "Content Manager",
|
||||
@ -32,8 +32,8 @@
|
||||
"redux-immutable": "^4.0.0",
|
||||
"reselect": "^3.0.1",
|
||||
"showdown": "^1.9.0",
|
||||
"strapi-helper-plugin": "3.0.0-beta.18.1",
|
||||
"strapi-utils": "3.0.0-beta.18.1",
|
||||
"strapi-helper-plugin": "3.0.0-beta.18.2",
|
||||
"strapi-utils": "3.0.0-beta.18.2",
|
||||
"styled-components": "^4.2.0",
|
||||
"yup": "^0.27.0"
|
||||
},
|
||||
|
@ -0,0 +1 @@
|
||||
<svg width="30" height="30" xmlns="http://www.w3.org/2000/svg"><text transform="translate(-19 -4)" fill="#4B515A" fill-rule="evenodd" font-size="30" font-family="AppleColorEmoji, Apple Color Emoji"><tspan x="19" y="32">🏭</tspan></text></svg>
|
After Width: | Height: | Size: 244 B |
@ -1,29 +1,98 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { components } from 'react-select';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { get } from 'lodash';
|
||||
import { Checkbox, CheckboxWrapper, Label } from '@buffetjs/styles';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import useDataManager from '../../hooks/useDataManager';
|
||||
import useQuery from '../../hooks/useQuery';
|
||||
import getTrad from '../../utils/getTrad';
|
||||
import UpperFirst from '../UpperFirst';
|
||||
import SubUl from './SubUl';
|
||||
import Ul from './Ul';
|
||||
import hasSubArray from './utils/hasSubArray';
|
||||
|
||||
const MultipleMenuList = ({
|
||||
selectProps: { name, addComponentsToDynamicZone, /*refState, */ value },
|
||||
selectProps: { name, addComponentsToDynamicZone, inputValue, value },
|
||||
...rest
|
||||
}) => {
|
||||
const { componentsGroupedByCategory } = useDataManager();
|
||||
const collapsesObject = Object.keys(componentsGroupedByCategory).reduce(
|
||||
(acc, current) => {
|
||||
acc[current] = false;
|
||||
const { componentsGroupedByCategory, modifiedData } = useDataManager();
|
||||
const query = useQuery();
|
||||
const dzName = query.get('dynamicZoneTarget');
|
||||
const alreadyUsedComponents = get(
|
||||
modifiedData,
|
||||
['contentType', 'schema', 'attributes', dzName, 'components'],
|
||||
[]
|
||||
);
|
||||
const filteredComponentsGroupedByCategory = Object.keys(
|
||||
componentsGroupedByCategory
|
||||
).reduce((acc, current) => {
|
||||
const filteredComponents = componentsGroupedByCategory[current].filter(
|
||||
({ uid }) => {
|
||||
return !alreadyUsedComponents.includes(uid);
|
||||
}
|
||||
);
|
||||
|
||||
if (filteredComponents.length > 0) {
|
||||
acc[current] = filteredComponents;
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const collapsesObject = Object.keys(
|
||||
filteredComponentsGroupedByCategory
|
||||
).reduce((acc, current) => {
|
||||
acc[current] = false;
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
const [collapses, setCollapses] = useState(collapsesObject);
|
||||
const [options, setOptions] = useState(filteredComponentsGroupedByCategory);
|
||||
|
||||
// Search for component
|
||||
useEffect(() => {
|
||||
const formattedOptions = Object.keys(
|
||||
filteredComponentsGroupedByCategory
|
||||
).reduce((acc, current) => {
|
||||
const filteredComponents = filteredComponentsGroupedByCategory[
|
||||
current
|
||||
].filter(({ schema: { name } }) => {
|
||||
return name.includes(inputValue);
|
||||
});
|
||||
|
||||
if (filteredComponents.length > 0) {
|
||||
acc[current] = filteredComponents;
|
||||
}
|
||||
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
const [collapses, setCollapses] = useState(collapsesObject);
|
||||
}, {});
|
||||
|
||||
setOptions(formattedOptions);
|
||||
|
||||
const categoriesToOpen = Object.keys(formattedOptions);
|
||||
|
||||
if (inputValue !== '') {
|
||||
// Close collapses
|
||||
Object.keys(filteredComponentsGroupedByCategory)
|
||||
.filter(cat => categoriesToOpen.indexOf(cat) === -1)
|
||||
.forEach(catName => {
|
||||
setCollapses(prevState => ({ ...prevState, [catName]: false }));
|
||||
});
|
||||
|
||||
categoriesToOpen.forEach(catName => {
|
||||
setCollapses(prevState => ({ ...prevState, [catName]: true }));
|
||||
});
|
||||
} else {
|
||||
// Close all collapses
|
||||
categoriesToOpen.forEach(catName => {
|
||||
setCollapses(prevState => ({ ...prevState, [catName]: false }));
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [inputValue]);
|
||||
|
||||
const toggleCollapse = catName => {
|
||||
setCollapses(prevState => ({
|
||||
...prevState,
|
||||
@ -33,18 +102,15 @@ const MultipleMenuList = ({
|
||||
|
||||
const Component = components.MenuList;
|
||||
|
||||
const allComponentsCategory = Object.keys(componentsGroupedByCategory).reduce(
|
||||
(acc, current) => {
|
||||
const categoryCompos = componentsGroupedByCategory[current].map(compo => {
|
||||
return compo.uid;
|
||||
});
|
||||
const allComponentsCategory = Object.keys(options).reduce((acc, current) => {
|
||||
const categoryCompos = options[current].map(compo => {
|
||||
return compo.uid;
|
||||
});
|
||||
|
||||
acc[current] = categoryCompos;
|
||||
acc[current] = categoryCompos;
|
||||
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const getCategoryValue = categoryName => {
|
||||
const componentsCategory = allComponentsCategory[categoryName];
|
||||
@ -79,7 +145,18 @@ const MultipleMenuList = ({
|
||||
maxHeight: 150,
|
||||
}}
|
||||
>
|
||||
{Object.keys(componentsGroupedByCategory).map(categoryName => {
|
||||
{Object.keys(options).length === 0 && (
|
||||
<FormattedMessage
|
||||
id={getTrad(
|
||||
`components.componentSelect.no-component-available${
|
||||
inputValue === '' ? '' : '.with-search'
|
||||
}`
|
||||
)}
|
||||
>
|
||||
{msg => <li style={{ paddingTop: 11 }}>{msg}</li>}
|
||||
</FormattedMessage>
|
||||
)}
|
||||
{Object.keys(options).map(categoryName => {
|
||||
const isChecked = getCategoryValue(categoryName);
|
||||
const target = { name: categoryName, value: !isChecked };
|
||||
|
||||
@ -124,7 +201,7 @@ const MultipleMenuList = ({
|
||||
</CheckboxWrapper>
|
||||
</div>
|
||||
<SubUl tag="ul" isOpen={collapses[categoryName]}>
|
||||
{componentsGroupedByCategory[categoryName].map(component => {
|
||||
{options[categoryName].map(component => {
|
||||
const isChecked = get(value, 'value', []).includes(
|
||||
component.uid
|
||||
);
|
||||
@ -165,6 +242,7 @@ const MultipleMenuList = ({
|
||||
|
||||
MultipleMenuList.defaultProps = {
|
||||
selectProps: {
|
||||
inputValue: '',
|
||||
refState: {
|
||||
current: {
|
||||
select: {
|
||||
@ -179,6 +257,7 @@ MultipleMenuList.defaultProps = {
|
||||
MultipleMenuList.propTypes = {
|
||||
selectProps: PropTypes.shape({
|
||||
addComponentsToDynamicZone: PropTypes.func.isRequired,
|
||||
inputValue: PropTypes.string,
|
||||
name: PropTypes.string.isRequired,
|
||||
refState: PropTypes.object,
|
||||
value: PropTypes.object,
|
||||
|
@ -10,8 +10,18 @@ import { isEmpty, isNumber } from 'lodash';
|
||||
import { Inputs } from '@buffetjs/custom';
|
||||
import StyledCustomCheckbox from './StyledCustomCheckbox';
|
||||
|
||||
const CustomCheckbox = ({ label, name, onChange, value, ...rest }) => {
|
||||
const CustomCheckbox = ({
|
||||
label,
|
||||
modifiedData,
|
||||
name,
|
||||
onChange,
|
||||
value,
|
||||
...rest
|
||||
}) => {
|
||||
const [checked, setChecked] = useState(isNumber(value) || !isEmpty(value));
|
||||
const type = modifiedData.type === 'biginteger' ? 'text' : 'number';
|
||||
const step = ['decimal', 'float'].includes(modifiedData.type) ? 'any' : '1';
|
||||
const disabled = !modifiedData.type;
|
||||
|
||||
return (
|
||||
<StyledCustomCheckbox>
|
||||
@ -38,8 +48,10 @@ const CustomCheckbox = ({ label, name, onChange, value, ...rest }) => {
|
||||
{...rest}
|
||||
name={name}
|
||||
onChange={onChange}
|
||||
step={step}
|
||||
disabled={disabled}
|
||||
value={value}
|
||||
type="number"
|
||||
type={type}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
@ -49,12 +61,14 @@ const CustomCheckbox = ({ label, name, onChange, value, ...rest }) => {
|
||||
|
||||
CustomCheckbox.defaultProps = {
|
||||
label: null,
|
||||
modifiedData: {},
|
||||
name: '',
|
||||
value: null,
|
||||
};
|
||||
|
||||
CustomCheckbox.propTypes = {
|
||||
label: PropTypes.string,
|
||||
modifiedData: PropTypes.object,
|
||||
name: PropTypes.string,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
||||
|
@ -132,7 +132,9 @@ const reducer = (state, action) => {
|
||||
dynamicZoneTarget,
|
||||
'components',
|
||||
],
|
||||
() => fromJS(makeUnique(newComponents))
|
||||
list => {
|
||||
return fromJS(makeUnique([...list.toJS(), ...newComponents]));
|
||||
}
|
||||
)
|
||||
.updateIn(['modifiedData', 'components'], old => {
|
||||
const componentsSchema = newComponents.reduce((acc, current) => {
|
||||
|
@ -213,8 +213,10 @@ const FormModal = () => {
|
||||
);
|
||||
const attributeToEdit = {
|
||||
...attributeToEditNotFormatted,
|
||||
// We filter the available components
|
||||
// Because this modal is only used for adding components
|
||||
components: [],
|
||||
name: dynamicZoneTarget,
|
||||
// createComponent: true,
|
||||
createComponent: false,
|
||||
componentToCreate: { type: 'component' },
|
||||
};
|
||||
@ -1238,6 +1240,7 @@ const FormModal = () => {
|
||||
>
|
||||
<Inputs
|
||||
{...input}
|
||||
modifiedData={modifiedData}
|
||||
addComponentsToDynamicZone={
|
||||
handleClickAddComponentsToDynamicZone
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
const NAME_REGEX = new RegExp('^[A-Za-z][_0-9A-Za-z]*$');
|
||||
const CATEGORY_NAME_REGEX = new RegExp('^[A-Za-z][-_0-9A-Za-z]*$');
|
||||
const ENUM_REGEX = new RegExp('^[_A-Za-z][_0-9A-Za-z]*$');
|
||||
const NAME_REGEX = new RegExp('^[A-Za-z][_0-9A-Za-z]*$');
|
||||
|
||||
export { ENUM_REGEX, NAME_REGEX };
|
||||
export { CATEGORY_NAME_REGEX, ENUM_REGEX, NAME_REGEX };
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import * as yup from 'yup';
|
||||
import { get, isEmpty, toLower, trim } from 'lodash';
|
||||
import { get, isEmpty, toLower, trim, toNumber } from 'lodash';
|
||||
import { translatedErrors as errorsTrads } from 'strapi-helper-plugin';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import pluginId from '../../../pluginId';
|
||||
@ -8,7 +8,11 @@ import getTrad from '../../../utils/getTrad';
|
||||
import { createComponentUid, createUid, nameToSlug } from './createUid';
|
||||
import componentForm from './componentForm';
|
||||
import fields from './staticFields';
|
||||
import { NAME_REGEX, ENUM_REGEX } from './attributesRegexes';
|
||||
import {
|
||||
NAME_REGEX,
|
||||
ENUM_REGEX,
|
||||
CATEGORY_NAME_REGEX,
|
||||
} from './attributesRegexes';
|
||||
import RESERVED_NAMES from './reservedNames';
|
||||
|
||||
yup.addMethod(yup.mixed, 'defined', function() {
|
||||
@ -52,6 +56,20 @@ yup.addMethod(yup.string, 'isAllowed', function(message) {
|
||||
});
|
||||
});
|
||||
|
||||
yup.addMethod(yup.string, 'isInferior', function(message, max) {
|
||||
return this.test('isInferior', message, function(min) {
|
||||
if (!min) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Number.isNaN(toNumber(min))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return toNumber(max) >= toNumber(min);
|
||||
});
|
||||
});
|
||||
|
||||
const ATTRIBUTES_THAT_DONT_HAVE_MIN_MAX_SETTINGS = [
|
||||
'boolean',
|
||||
'date',
|
||||
@ -210,11 +228,50 @@ const forms = {
|
||||
case 'integer':
|
||||
case 'biginteger':
|
||||
case 'float':
|
||||
case 'decimal':
|
||||
case 'decimal': {
|
||||
if (dataToValidate.type === 'biginteger') {
|
||||
return yup.object().shape({
|
||||
...commonShape,
|
||||
default: yup
|
||||
.string()
|
||||
.nullable()
|
||||
.matches(/^\d*$/),
|
||||
min: yup
|
||||
.string()
|
||||
.nullable()
|
||||
.matches(/^\d*$/)
|
||||
.when('max', (max, schema) => {
|
||||
if (max) {
|
||||
return schema.isInferior(
|
||||
getTrad('error.validation.minSupMax'),
|
||||
max
|
||||
);
|
||||
} else {
|
||||
return schema;
|
||||
}
|
||||
}),
|
||||
|
||||
max: yup
|
||||
.string()
|
||||
.nullable()
|
||||
.matches(/^\d*$/),
|
||||
});
|
||||
}
|
||||
|
||||
let defaultType = yup.number();
|
||||
|
||||
if (dataToValidate.type === 'integer') {
|
||||
defaultType = yup
|
||||
.number()
|
||||
.integer('component.Input.error.validation.integer');
|
||||
}
|
||||
|
||||
return yup.object().shape({
|
||||
...commonShape,
|
||||
default: defaultType.nullable(),
|
||||
...numberTypeShape,
|
||||
});
|
||||
}
|
||||
case 'relation':
|
||||
return yup.object().shape({
|
||||
name: yup
|
||||
@ -312,6 +369,24 @@ const forms = {
|
||||
|
||||
const items = defaultItems.slice();
|
||||
|
||||
if (type === 'number' && data.type !== 'biginteger') {
|
||||
const step =
|
||||
data.type === 'decimal' || data.type === 'float' ? 'any' : '1';
|
||||
|
||||
items.splice(0, 1, [
|
||||
{
|
||||
autoFocus: true,
|
||||
name: 'default',
|
||||
type: 'number',
|
||||
step,
|
||||
label: {
|
||||
id: getTrad('form.attribute.settings.default'),
|
||||
},
|
||||
validations: {},
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
if (type === 'media') {
|
||||
items.splice(0, 1);
|
||||
} else if (type === 'boolean') {
|
||||
@ -758,7 +833,7 @@ const forms = {
|
||||
.required(errorsTrads.required),
|
||||
category: yup
|
||||
.string()
|
||||
.matches(NAME_REGEX, errorsTrads.regex)
|
||||
.matches(CATEGORY_NAME_REGEX, errorsTrads.regex)
|
||||
.required(errorsTrads.required),
|
||||
icon: yup.string().required(errorsTrads.required),
|
||||
collectionName: yup.string().nullable(),
|
||||
@ -821,6 +896,7 @@ const forms = {
|
||||
return yup.object().shape({
|
||||
name: yup
|
||||
.string()
|
||||
.matches(CATEGORY_NAME_REGEX, errorsTrads.regex)
|
||||
.unique(errorsTrads.unique, allowedCategories, toLower)
|
||||
.required(errorsTrads.required),
|
||||
});
|
||||
|
@ -34,7 +34,9 @@
|
||||
"button.component.add": "Add a component",
|
||||
"button.component.create": "Create new component",
|
||||
"button.model.create": "Create new content-type",
|
||||
"components.componentSelect.value-component": "{number} component selected",
|
||||
"components.componentSelect.no-component-available": "You have already added all your components",
|
||||
"components.componentSelect.no-component-available.with-search": "There is no component matching your search",
|
||||
"components.componentSelect.value-component": "{number} component selected (type to search for a component)",
|
||||
"components.componentSelect.value-components": "{number} components selected",
|
||||
"component.repeatable": "(repeatable)",
|
||||
"configurations": "configurations",
|
||||
|
@ -17,6 +17,7 @@ const validators = {
|
||||
|
||||
const NAME_REGEX = new RegExp('^[A-Za-z][_0-9A-Za-z]*$');
|
||||
const COLLECTION_NAME_REGEX = new RegExp('^[A-Za-z][-_0-9A-Za-z]*$');
|
||||
const CATEGORY_NAME_REGEX = new RegExp('^[A-Za-z][-_0-9A-Za-z]*$');
|
||||
const ENUM_REGEX = new RegExp('^[_A-Za-z][_0-9A-Za-z]*$');
|
||||
const ICON_REGEX = new RegExp('^[A-Za-z0-9][-A-Za-z0-9]*$');
|
||||
|
||||
@ -26,6 +27,12 @@ const isValidName = {
|
||||
test: val => val === '' || NAME_REGEX.test(val),
|
||||
};
|
||||
|
||||
const isValidCategoryName = {
|
||||
name: 'isValidCategoryName',
|
||||
message: '${path} must match the following regex: /^[A-Za-z][_-0-9A-Za-z]*$/',
|
||||
test: val => val === '' || CATEGORY_NAME_REGEX.test(val),
|
||||
};
|
||||
|
||||
const isValidCollectionName = {
|
||||
name: 'isValidCollectionName',
|
||||
message: '${path} must match the following regex: /^[A-Za-z][-_0-9A-Za-z]*$/',
|
||||
@ -55,6 +62,7 @@ module.exports = {
|
||||
validators,
|
||||
|
||||
isValidCollectionName,
|
||||
isValidCategoryName,
|
||||
isValidName,
|
||||
isValidIcon,
|
||||
isValidKey,
|
||||
|
@ -3,7 +3,7 @@
|
||||
const yup = require('yup');
|
||||
const formatYupErrors = require('./yup-formatter');
|
||||
|
||||
const { isValidName } = require('./common');
|
||||
const { isValidCategoryName } = require('./common');
|
||||
|
||||
module.exports = data => {
|
||||
return componentCategorySchema
|
||||
@ -19,7 +19,7 @@ const componentCategorySchema = yup
|
||||
name: yup
|
||||
.string()
|
||||
.min(3)
|
||||
.test(isValidName)
|
||||
.test(isValidCategoryName)
|
||||
.required('name.required'),
|
||||
})
|
||||
.noUnknown();
|
||||
|
@ -3,7 +3,7 @@
|
||||
const _ = require('lodash');
|
||||
const yup = require('yup');
|
||||
|
||||
const { isValidName, isValidIcon } = require('./common');
|
||||
const { isValidCategoryName, isValidIcon } = require('./common');
|
||||
const formatYupErrors = require('./yup-formatter');
|
||||
const createSchema = require('./model-schema');
|
||||
const { modelTypes, DEFAULT_TYPES } = require('./constants');
|
||||
@ -23,7 +23,7 @@ const componentSchema = createSchema(VALID_TYPES, VALID_RELATIONS, {
|
||||
category: yup
|
||||
.string()
|
||||
.nullable()
|
||||
.test(isValidName)
|
||||
.test(isValidCategoryName)
|
||||
.required('category.required'),
|
||||
})
|
||||
.required()
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-plugin-content-type-builder",
|
||||
"version": "3.0.0-beta.18.1",
|
||||
"version": "3.0.0-beta.18.2",
|
||||
"description": "Strapi plugin to create content type (API).",
|
||||
"strapi": {
|
||||
"name": "Content Type Builder",
|
||||
@ -29,9 +29,9 @@
|
||||
"redux": "^4.0.1",
|
||||
"redux-immutable": "^4.0.0",
|
||||
"reselect": "^3.0.1",
|
||||
"strapi-generate": "3.0.0-beta.18.1",
|
||||
"strapi-generate-api": "3.0.0-beta.18.1",
|
||||
"strapi-helper-plugin": "3.0.0-beta.18.1",
|
||||
"strapi-generate": "3.0.0-beta.18.2",
|
||||
"strapi-generate-api": "3.0.0-beta.18.2",
|
||||
"strapi-helper-plugin": "3.0.0-beta.18.2",
|
||||
"yup": "^0.27.0"
|
||||
},
|
||||
"author": {
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-plugin-documentation",
|
||||
"version": "3.0.0-beta.18.1",
|
||||
"version": "3.0.0-beta.18.2",
|
||||
"description": "This is the description of the plugin.",
|
||||
"strapi": {
|
||||
"name": "Documentation",
|
||||
@ -32,7 +32,7 @@
|
||||
"redux": "^4.0.1",
|
||||
"redux-immutable": "^4.0.0",
|
||||
"reselect": "^4.0.0",
|
||||
"strapi-helper-plugin": "3.0.0-beta.18.1",
|
||||
"strapi-helper-plugin": "3.0.0-beta.18.2",
|
||||
"swagger-ui-dist": "3.24.3"
|
||||
},
|
||||
"author": {
|
||||
|
@ -115,7 +115,8 @@
|
||||
"required": false,
|
||||
"description": "Get records that matches any value in the array of values",
|
||||
"schema": {
|
||||
"type": "array"
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
},
|
||||
"deprecated": false
|
||||
},
|
||||
@ -125,8 +126,9 @@
|
||||
"required": false,
|
||||
"description": "Get records that doesn't match any value in the array of values",
|
||||
"schema": {
|
||||
"type": "array"
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
]
|
||||
]
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-plugin-email",
|
||||
"version": "3.0.0-beta.18.1",
|
||||
"version": "3.0.0-beta.18.2",
|
||||
"description": "This is the description of the plugin.",
|
||||
"strapi": {
|
||||
"name": "Email",
|
||||
@ -12,13 +12,13 @@
|
||||
"test": "echo \"no tests yet\""
|
||||
},
|
||||
"dependencies": {
|
||||
"strapi-provider-email-sendmail": "3.0.0-beta.18.1",
|
||||
"strapi-utils": "3.0.0-beta.18.1"
|
||||
"strapi-provider-email-sendmail": "3.0.0-beta.18.2",
|
||||
"strapi-utils": "3.0.0-beta.18.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"react-copy-to-clipboard": "5.0.1",
|
||||
"rimraf": "^2.6.3",
|
||||
"strapi-helper-plugin": "3.0.0-beta.18.1"
|
||||
"strapi-helper-plugin": "3.0.0-beta.18.2"
|
||||
},
|
||||
"author": {
|
||||
"name": "Strapi team",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-plugin-graphql",
|
||||
"version": "3.0.0-beta.18.1",
|
||||
"version": "3.0.0-beta.18.2",
|
||||
"description": "This is the description of the plugin.",
|
||||
"strapi": {
|
||||
"name": "graphql",
|
||||
@ -23,7 +23,7 @@
|
||||
"graphql-type-long": "^0.1.1",
|
||||
"koa-compose": "^4.1.0",
|
||||
"pluralize": "^7.0.0",
|
||||
"strapi-utils": "3.0.0-beta.18.1"
|
||||
"strapi-utils": "3.0.0-beta.18.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cross-env": "^5.2.0",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-plugin-upload",
|
||||
"version": "3.0.0-beta.18.1",
|
||||
"version": "3.0.0-beta.18.2",
|
||||
"description": "This is the description of the plugin.",
|
||||
"strapi": {
|
||||
"name": "Files Upload",
|
||||
@ -23,9 +23,9 @@
|
||||
"react-router-dom": "^5.0.0",
|
||||
"react-transition-group": "^2.5.0",
|
||||
"reactstrap": "^5.0.0",
|
||||
"strapi-helper-plugin": "3.0.0-beta.18.1",
|
||||
"strapi-provider-upload-local": "3.0.0-beta.18.1",
|
||||
"strapi-utils": "3.0.0-beta.18.1",
|
||||
"strapi-helper-plugin": "3.0.0-beta.18.2",
|
||||
"strapi-provider-upload-local": "3.0.0-beta.18.2",
|
||||
"strapi-utils": "3.0.0-beta.18.2",
|
||||
"stream-to-array": "^2.3.0",
|
||||
"uuid": "^3.2.1"
|
||||
},
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-plugin-users-permissions",
|
||||
"version": "3.0.0-beta.18.1",
|
||||
"version": "3.0.0-beta.18.2",
|
||||
"description": "Protect your API with a full-authentication process based on JWT",
|
||||
"strapi": {
|
||||
"name": "Roles & Permissions",
|
||||
@ -31,8 +31,8 @@
|
||||
"reactstrap": "^5.0.0",
|
||||
"redux-saga": "^0.16.0",
|
||||
"request": "^2.83.0",
|
||||
"strapi-helper-plugin": "3.0.0-beta.18.1",
|
||||
"strapi-utils": "3.0.0-beta.18.1",
|
||||
"strapi-helper-plugin": "3.0.0-beta.18.2",
|
||||
"strapi-utils": "3.0.0-beta.18.2",
|
||||
"uuid": "^3.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-provider-email-amazon-ses",
|
||||
"version": "3.0.0-beta.18.1",
|
||||
"version": "3.0.0-beta.18.2",
|
||||
"description": "Amazon SES provider for strapi email",
|
||||
"homepage": "http://strapi.io",
|
||||
"keywords": [
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-provider-email-mailgun",
|
||||
"version": "3.0.0-beta.18.1",
|
||||
"version": "3.0.0-beta.18.2",
|
||||
"description": "Mailgun provider for strapi email plugin",
|
||||
"homepage": "http://strapi.io",
|
||||
"keywords": [
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-provider-email-sendgrid",
|
||||
"version": "3.0.0-beta.18.1",
|
||||
"version": "3.0.0-beta.18.2",
|
||||
"description": "Sendgrid provider for strapi email",
|
||||
"homepage": "http://strapi.io",
|
||||
"keywords": [
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-provider-email-sendmail",
|
||||
"version": "3.0.0-beta.18.1",
|
||||
"version": "3.0.0-beta.18.2",
|
||||
"description": "Sendmail provider for strapi email",
|
||||
"homepage": "http://strapi.io",
|
||||
"keywords": [
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-provider-upload-aws-s3",
|
||||
"version": "3.0.0-beta.18.1",
|
||||
"version": "3.0.0-beta.18.2",
|
||||
"description": "AWS S3 provider for strapi upload",
|
||||
"homepage": "http://strapi.io",
|
||||
"keywords": [
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-provider-upload-cloudinary",
|
||||
"version": "3.0.0-beta.18.1",
|
||||
"version": "3.0.0-beta.18.2",
|
||||
"description": "Cloudinary provider for strapi upload",
|
||||
"homepage": "http://strapi.io",
|
||||
"keywords": [
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-provider-upload-local",
|
||||
"version": "3.0.0-beta.18.1",
|
||||
"version": "3.0.0-beta.18.2",
|
||||
"description": "Local provider for strapi upload",
|
||||
"homepage": "http://strapi.io",
|
||||
"keywords": [
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-provider-upload-rackspace",
|
||||
"version": "3.0.0-beta.18.1",
|
||||
"version": "3.0.0-beta.18.2",
|
||||
"description": "Rackspace provider for strapi upload",
|
||||
"main": "./lib",
|
||||
"keywords": [],
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-utils",
|
||||
"version": "3.0.0-beta.18.1",
|
||||
"version": "3.0.0-beta.18.2",
|
||||
"description": "Shared utilities for the Strapi packages",
|
||||
"homepage": "http://strapi.io",
|
||||
"keywords": [
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi",
|
||||
"version": "3.0.0-beta.18.1",
|
||||
"version": "3.0.0-beta.18.2",
|
||||
"description": "An open source headless CMS solution to create and manage your own API. It provides a powerful dashboard and features to make your life easier. Databases supported: MongoDB, MySQL, MariaDB, PostgreSQL, SQLite",
|
||||
"homepage": "http://strapi.io",
|
||||
"directories": {
|
||||
@ -49,16 +49,16 @@
|
||||
"resolve-cwd": "^3.0.0",
|
||||
"rimraf": "^2.6.2",
|
||||
"shelljs": "^0.8.3",
|
||||
"strapi-database": "3.0.0-beta.18.1",
|
||||
"strapi-generate": "3.0.0-beta.18.1",
|
||||
"strapi-generate-api": "3.0.0-beta.18.1",
|
||||
"strapi-generate-controller": "3.0.0-beta.18.1",
|
||||
"strapi-generate-model": "3.0.0-beta.18.1",
|
||||
"strapi-generate-new": "3.0.0-beta.18.1",
|
||||
"strapi-generate-plugin": "3.0.0-beta.18.1",
|
||||
"strapi-generate-policy": "3.0.0-beta.18.1",
|
||||
"strapi-generate-service": "3.0.0-beta.18.1",
|
||||
"strapi-utils": "3.0.0-beta.18.1"
|
||||
"strapi-database": "3.0.0-beta.18.2",
|
||||
"strapi-generate": "3.0.0-beta.18.2",
|
||||
"strapi-generate-api": "3.0.0-beta.18.2",
|
||||
"strapi-generate-controller": "3.0.0-beta.18.2",
|
||||
"strapi-generate-model": "3.0.0-beta.18.2",
|
||||
"strapi-generate-new": "3.0.0-beta.18.2",
|
||||
"strapi-generate-plugin": "3.0.0-beta.18.2",
|
||||
"strapi-generate-policy": "3.0.0-beta.18.2",
|
||||
"strapi-generate-service": "3.0.0-beta.18.2",
|
||||
"strapi-utils": "3.0.0-beta.18.2"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "jest --verbose",
|
||||
|
Loading…
x
Reference in New Issue
Block a user