Merge branch 'master' into fix-filter-doc

This commit is contained in:
Jim LAURIE 2018-02-09 20:25:32 +01:00 committed by GitHub
commit bde677735b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 551 additions and 138 deletions

View File

@ -33,6 +33,23 @@ The following types are currently available:
- `json` - `json`
- `email` - `email`
#### Validations
You can apply basic validations to the attributes. The following supported validations are *only supported by MongoDB* connection.
If you're using SQL databases, you should use the native SQL constraints to apply them.
- `required` (boolean) — if true adds a required validator for this property.
- `unique` (boolean) — whether to define a unique index on this property.
- `max` (integer) — checks if the value is greater than or equal to the given minimum.
- `min` (integer) — checks if the value is less than or equal to the given maximum.
**Security validations**
To improve the Developer eXperience when developing or using the administration panel, the framework enhances the attributes with these "security validations":
- `private` (boolean) — if true, the attribute will be removed from the server response (it's useful to hide sensitive data).
- `configurable` (boolean) - if false, the attribute isn't configurable from the Content Type Builder plugin.
#### Example #### Example
**Path —** `User.settings.json`. **Path —** `User.settings.json`.
@ -50,14 +67,23 @@ The following types are currently available:
"lastname": { "lastname": {
"type": "string" "type": "string"
}, },
"email": {
"type": "email",
"required": true,
"unique": true
},
"password": { "password": {
"type": "password" "type": "password",
"required": true,
"private": true
}, },
"about": { "about": {
"type": "description" "type": "description"
}, },
"age": { "age": {
"type": "integer" "type": "integer",
"min": 18,
"max": 99
}, },
"birthday": { "birthday": {
"type": "date" "type": "date"

View File

@ -71,7 +71,8 @@
flex-shrink: 0; flex-shrink: 0;
width: 144px; width: 144px;
height: 144px; height: 144px;
border: 1px solid #E8EEFC; background: #FAFAFB;
border: 1px solid #F3F3F7;
border-radius: 3px; border-radius: 3px;
background-size: contain; background-size: contain;
line-height: 144px; line-height: 144px;

View File

@ -41,7 +41,7 @@ function LeftMenuLinkContainer({ plugins }) {
<p className={styles.title}>{pluginsSections[current].name}</p> <p className={styles.title}>{pluginsSections[current].name}</p>
<ul className={styles.list}> <ul className={styles.list}>
{sortBy(pluginsSections[current].links, 'label').map((link, i) => {sortBy(pluginsSections[current].links, 'label').map((link, i) =>
<LeftMenuLink key={`${i}-${link.label}`} icon={link.icon || 'link'} label={link.label} destination={`/plugins/${link.plugin}/${link.destination}`} source={link.source} /> <LeftMenuLink key={`${i}-${link.label}`} icon={link.icon || 'caret-right'} label={link.label} destination={`/plugins/${link.plugin}/${link.destination}`} source={link.source} />
)} )}
</ul> </ul>
</div> </div>
@ -79,12 +79,12 @@ function LeftMenuLinkContainer({ plugins }) {
<p className={styles.title}><FormattedMessage {...messages.general} /></p> <p className={styles.title}><FormattedMessage {...messages.general} /></p>
<ul className={styles.list}> <ul className={styles.list}>
<LeftMenuLink <LeftMenuLink
icon="puzzle-piece" icon="list"
label={messages.listPlugins.id} label={messages.listPlugins.id}
destination="/list-plugins" destination="/list-plugins"
/> />
<LeftMenuLink <LeftMenuLink
icon="download" icon="shopping-basket"
label={messages.installNewPlugin.id} label={messages.installNewPlugin.id}
destination="/install-plugin" destination="/install-plugin"
/> />

View File

@ -1,15 +1,15 @@
{ {
"listPlugins": { "listPlugins": {
"id": "app.components.LeftMenuLinkContainer.listPlugins", "id": "app.components.LeftMenuLinkContainer.listPlugins",
"defaultMessage": "List plugins" "defaultMessage": "Plugins"
}, },
"installNewPlugin": { "installNewPlugin": {
"id": "app.components.LeftMenuLinkContainer.installNewPlugin", "id": "app.components.LeftMenuLinkContainer.installNewPlugin",
"defaultMessage": "Install new plugin" "defaultMessage": "Marketplace"
}, },
"configuration": { "configuration": {
"id": "app.components.LeftMenuLinkContainer.configuration", "id": "app.components.LeftMenuLinkContainer.configuration",
"defaultMessage": "Configuration" "defaultMessage": "Configurations"
}, },
"plugins": { "plugins": {
"id": "app.components.LeftMenuLinkContainer.plugins", "id": "app.components.LeftMenuLinkContainer.plugins",

View File

@ -86,13 +86,11 @@ class PluginCard extends React.Component {
buttonLabel = 'app.components.PluginCard.Button.label.support'; buttonLabel = 'app.components.PluginCard.Button.label.support';
} }
const pluginIcon = this.props.plugin.id !== 'email' ? ( const pluginIcon = (
<div className={styles.frame}> <div className={styles.frame}>
<span className={styles.helper} /> <span className={styles.helper} />
<img src={`${this.props.plugin.logo}`} alt="icon" /> <img src={`${this.props.plugin.logo}`} alt="icon" />
</div> </div>
) : (
<div className={styles.iconContainer}><i className={`fa fa-${this.props.plugin.icon}`} /></div>
); );
const descriptions = { const descriptions = {

View File

@ -88,7 +88,8 @@
height: 36px; height: 36px;
margin: auto 0; margin: auto 0;
text-align: center; text-align: center;
border: 1px solid #E8EEFC; background: #FAFAFB;
border: 1px solid #F3F3F7;
border-radius: 3px; border-radius: 3px;
white-space: nowrap; white-space: nowrap;
> img { > img {
@ -101,7 +102,8 @@
height: 36px; height: 36px;
width: 70px; width: 70px;
margin-right: 14px; margin-right: 14px;
border: 1px solid #E8EEFC; background: #FAFAFB;
border: 1px solid #F3F3F7;
border-radius: 3px; border-radius: 3px;
text-align: center; text-align: center;
font-size: 20px; font-size: 20px;

View File

@ -8,7 +8,8 @@
position: relative; position: relative;
margin: auto 0; margin: auto 0;
text-align: center; text-align: center;
border: 1px solid rgba(28,93,231,0.1); background: #FAFAFB;
border: 1px solid #F3F3F7;
border-radius: 3px; border-radius: 3px;
font-size: 20px; font-size: 20px;

File diff suppressed because one or more lines are too long

View File

@ -11,8 +11,8 @@
"app.components.HomePage.button": "Create your first content type", "app.components.HomePage.button": "Create your first content type",
"app.components.HomePage.feedback": "Feel free to ask questions or give us feedback by using one of the support channels below.", "app.components.HomePage.feedback": "Feel free to ask questions or give us feedback by using one of the support channels below.",
"app.components.InstallPluginPage.helmet": "Install plugins", "app.components.InstallPluginPage.helmet": "Marketplace - Plugins",
"app.components.InstallPluginPage.title": "Install new plugins", "app.components.InstallPluginPage.title": "Marketplace - Plugins",
"app.components.InstallPluginPage.description": "Extend your app with no efforts", "app.components.InstallPluginPage.description": "Extend your app with no efforts",
"app.components.InstallPluginPage.plugin.support-us.description": "Support us by buying the Strapi t-shirt. It will allow us to keep working on the project and try giving the best possible experience!", "app.components.InstallPluginPage.plugin.support-us.description": "Support us by buying the Strapi t-shirt. It will allow us to keep working on the project and try giving the best possible experience!",
"app.components.InstallPluginPage.InputSearch.label": " ", "app.components.InstallPluginPage.InputSearch.label": " ",
@ -26,10 +26,10 @@
"app.components.InstallPluginPopup.noDescription": "No description available", "app.components.InstallPluginPopup.noDescription": "No description available",
"app.components.LeftMenuFooter.poweredBy": "Proudly powered by", "app.components.LeftMenuFooter.poweredBy": "Proudly powered by",
"app.components.LeftMenuLinkContainer.configuration": "Configuration", "app.components.LeftMenuLinkContainer.configuration": "Configurations",
"app.components.LeftMenuLinkContainer.general": "General", "app.components.LeftMenuLinkContainer.general": "General",
"app.components.LeftMenuLinkContainer.installNewPlugin": "Install new plugin", "app.components.LeftMenuLinkContainer.installNewPlugin": "Marketplace",
"app.components.LeftMenuLinkContainer.listPlugins": "List plugins", "app.components.LeftMenuLinkContainer.listPlugins": "Plugins",
"app.components.LeftMenuLinkContainer.noPluginsInstalled": "No plugins installed yet", "app.components.LeftMenuLinkContainer.noPluginsInstalled": "No plugins installed yet",
"app.components.LeftMenuLinkContainer.plugins": "Plugins", "app.components.LeftMenuLinkContainer.plugins": "Plugins",

View File

@ -11,8 +11,8 @@
"app.components.HomePage.button": "Créez votre premier type de contenu", "app.components.HomePage.button": "Créez votre premier type de contenu",
"app.components.HomePage.feedback": "N'hésitez pas à utiliser un des channels ci-dessous pour poser vos questions ou nous donner vos retours.", "app.components.HomePage.feedback": "N'hésitez pas à utiliser un des channels ci-dessous pour poser vos questions ou nous donner vos retours.",
"app.components.InstallPluginPage.helmet": "Installez des plugins", "app.components.InstallPluginPage.helmet": "Marketplace - Plugins",
"app.components.InstallPluginPage.title": "Installez des nouveaux plugins", "app.components.InstallPluginPage.title": "Marketplace - Plugins",
"app.components.InstallPluginPage.description": "Améliorez votre app sans efforts", "app.components.InstallPluginPage.description": "Améliorez votre app sans efforts",
"app.components.InstallPluginPage.plugin.support-us.description": "Soutenez-nous en achetant un Strapi tee shirt. Cela nous aidera a continuer de travailler sur le projet pour vous donner la meilleure expérience possible!", "app.components.InstallPluginPage.plugin.support-us.description": "Soutenez-nous en achetant un Strapi tee shirt. Cela nous aidera a continuer de travailler sur le projet pour vous donner la meilleure expérience possible!",
"app.components.InstallPluginPage.InputSearch.label": " ", "app.components.InstallPluginPage.InputSearch.label": " ",
@ -26,10 +26,10 @@
"app.components.InstallPluginPopup.noDescription": "Aucune description disponible", "app.components.InstallPluginPopup.noDescription": "Aucune description disponible",
"app.components.LeftMenuFooter.poweredBy": "Propulsé par", "app.components.LeftMenuFooter.poweredBy": "Propulsé par",
"app.components.LeftMenuLinkContainer.configuration": "Configuration", "app.components.LeftMenuLinkContainer.configuration": "Configurations",
"app.components.LeftMenuLinkContainer.general": "Général", "app.components.LeftMenuLinkContainer.general": "Général",
"app.components.LeftMenuLinkContainer.installNewPlugin": "Installer un nouveau plugin", "app.components.LeftMenuLinkContainer.installNewPlugin": "Marketplaces",
"app.components.LeftMenuLinkContainer.listPlugins": "Liste des plugins", "app.components.LeftMenuLinkContainer.listPlugins": "Plugins",
"app.components.LeftMenuLinkContainer.noPluginsInstalled": "Aucun plugin installé", "app.components.LeftMenuLinkContainer.noPluginsInstalled": "Aucun plugin installé",
"app.components.LeftMenuLinkContainer.plugins": "Plugins", "app.components.LeftMenuLinkContainer.plugins": "Plugins",

View File

@ -28,6 +28,10 @@ module.exports = {
*/ */
findOne: async (ctx) => { findOne: async (ctx) => {
if (!ctx.params._id.match(/^[0-9a-fA-F]{24}$/)) {
return ctx.notFound();
}
const data = await strapi.services.<%= id %>.fetch(ctx.params); const data = await strapi.services.<%= id %>.fetch(ctx.params);
// Send 200 `ok` // Send 200 `ok`

View File

@ -1,5 +1,5 @@
{ {
"timeout": 1000, "timeout": 3000,
"load": { "load": {
"order": [ "order": [
"Define the hooks' load order by putting their names in this array in the right order" "Define the hooks' load order by putting their names in this array in the right order"

View File

@ -29,6 +29,7 @@ const logger = require('strapi-utils').logger;
module.exports = (scope, cb) => { module.exports = (scope, cb) => {
// App info. // App info.
const hasDatabaseConfig = !!scope.database; const hasDatabaseConfig = !!scope.database;
_.defaults(scope, { _.defaults(scope, {
name: scope.name === '.' || !scope.name ? scope.name : path.basename(process.cwd()), name: scope.name === '.' || !scope.name ? scope.name : path.basename(process.cwd()),
author: process.env.USER || 'A Strapi developer', author: process.env.USER || 'A Strapi developer',
@ -116,7 +117,6 @@ module.exports = (scope, cb) => {
} }
]) ])
.then(answers => { .then(answers => {
if (hasDatabaseConfig) { if (hasDatabaseConfig) {
const databaseChoice = _.find(databaseChoices, ['value.database', scope.database.settings.client]); const databaseChoice = _.find(databaseChoices, ['value.database', scope.database.settings.client]);
scope.database.connector = databaseChoice.value.connector; scope.database.connector = databaseChoice.value.connector;
@ -186,10 +186,11 @@ module.exports = (scope, cb) => {
}, },
{ {
when: !hasDatabaseConfig, when: !hasDatabaseConfig,
type: 'input', type: 'password',
prefix: '', prefix: '',
name: 'password', name: 'password',
message: 'Password:', message: 'Password:',
mask: '*',
default: _.get(scope.database, 'password', undefined) default: _.get(scope.database, 'password', undefined)
} }
]) ])

View File

@ -208,7 +208,10 @@ InputNumberWithErrors.propTypes = {
style: PropTypes.object, style: PropTypes.object,
tabIndex: PropTypes.string, tabIndex: PropTypes.string,
validations: PropTypes.object, validations: PropTypes.object,
value: PropTypes.string.isRequired, value: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
]),
}; };
export default InputNumberWithErrors; export default InputNumberWithErrors;

View File

@ -86,7 +86,7 @@ class InputSelectWithErrors extends React.Component {
disabled={disabled} disabled={disabled}
error={!isEmpty(this.state.errors)} error={!isEmpty(this.state.errors)}
name={name} name={name}
onBlur={onBlur} onBlur={isFunction(onBlur) ? onBlur : () => {}}
onChange={onChange} onChange={onChange}
onFocus={onFocus} onFocus={onFocus}
selectOptions={selectOptions} selectOptions={selectOptions}
@ -167,7 +167,10 @@ InputSelectWithErrors.propTypes = {
labelClassName: PropTypes.string, labelClassName: PropTypes.string,
labelStyle: PropTypes.object, labelStyle: PropTypes.object,
name: PropTypes.string.isRequired, name: PropTypes.string.isRequired,
onBlur: PropTypes.func, onBlur: PropTypes.oneOfType([
PropTypes.bool,
PropTypes.func,
]),
onChange: PropTypes.func.isRequired, onChange: PropTypes.func.isRequired,
onFocus: PropTypes.func, onFocus: PropTypes.func,
selectOptions: PropTypes.arrayOf( selectOptions: PropTypes.arrayOf(

View File

@ -1 +1 @@
<svg width="35" height="24" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path d="M1.148 20.03c.567-7.141 2.974-7.675 7.222-1.604 6.372 9.107 7.533-.667 9.19-2.017" stroke="#FFD2B6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><g fill-rule="nonzero"><path d="M21.313 16.48l1.081-1.082-2.792-2.792-1.081 1.081v1.271h1.52v1.521h1.272zm6.214-11.027c0-.174-.087-.26-.262-.26a.275.275 0 0 0-.202.082l-6.44 6.44a.275.275 0 0 0-.082.202c0 .174.087.261.261.261.08 0 .147-.028.202-.083l6.44-6.44a.275.275 0 0 0 .083-.202zm-.642-2.28l4.943 4.942L21.943 18H17v-4.943l9.885-9.885zM35 4.312c0 .42-.147.776-.44 1.07l-1.972 1.971-4.942-4.942 1.972-1.96A1.412 1.412 0 0 1 30.688 0c.419 0 .78.15 1.08.451l2.792 2.78c.293.31.44.67.44 1.082z" fill="#181D29"/><path d="M35 4.313c0 .42-.147.776-.44 1.07l-1.972 1.971-4.942-4.942 1.972-1.96A1.412 1.412 0 0 1 30.688 0c.419 0 .78.15 1.08.451l2.792 2.78c.293.31.44.67.44 1.082z" fill="#FF84B3"/></g></g></svg> <svg width="30" height="24" xmlns="http://www.w3.org/2000/svg"><text transform="translate(-20 -6)" fill="#4B515A" fill-rule="evenodd" font-size="30" font-family="AppleColorEmoji, Apple Color Emoji"><tspan x="20" y="31">📖</tspan></text></svg>

Before

Width:  |  Height:  |  Size: 980 B

After

Width:  |  Height:  |  Size: 244 B

View File

@ -1 +1 @@
<svg width="22" height="22" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path d="M8.604.607c.317.623.75 1.155 1.298 1.597.549.443 1.16.754 1.835.934l.012.873c.032 1.745-.494 3.166-1.579 4.264-1.084 1.098-2.5 1.647-4.247 1.647-1 0-1.885-.19-2.657-.571a4.852 4.852 0 0 1-1.858-1.567 7.325 7.325 0 0 1-1.055-2.25A9.927 9.927 0 0 1 0 2.832l.5.369c.276.205.528.387.755.547.228.16.468.309.72.448.251.14.438.21.56.21.333 0 .557-.152.67-.455a6.33 6.33 0 0 1 .701-1.383c.264-.381.547-.692.847-.934.3-.242.658-.436 1.073-.584A6.547 6.547 0 0 1 7.08.736c.422-.062.93-.105 1.523-.13z" id="a"/></defs><g fill="none" fill-rule="evenodd"><g transform="translate(0 12)"><mask id="b" fill="#fff"><use xlink:href="#a"/></mask><use fill="#EABE8D" fill-rule="nonzero" xlink:href="#a"/><path d="M10.18.264c4.086 6.335 2.315 9.584-5.314 9.747-7.629.163-6.185-1.797 4.331-5.88l.982-3.867z" fill="#DB1818" mask="url(#b)"/><path d="M10.564.16C11.422 6.26 8.9 9.507 2.998 9.904c-5.902.397-4.484-1.392 4.255-5.367L10.563.16z" fill="#F1D520" mask="url(#b)"/><path d="M10.196-1.556C9.168 5.281 6.272 8.728 1.507 8.786c-4.764.059-3.209-1.83 4.668-5.663l4.021-4.679z" fill="#32B167" mask="url(#b)"/><path d="M9.506-4.516C9.417 2.713 6.99 6.357 2.226 6.414-2.539 6.474-.784 4.262 7.49-.22l2.016-4.295z" fill="#1279EA" mask="url(#b)"/><path d="M9.449-4.392C8.72 1.871 5.974 5.032 1.209 5.09c-4.764.058-3.398-1.712 4.099-5.311l4.14-4.17z" fill="#4C2DCE" mask="url(#b)"/></g><path d="M19.88 0c.564 0 1.058.186 1.482.56.424.372.635.84.635 1.401 0 .505-.181 1.111-.544 1.817-2.679 5.045-4.555 8.06-5.628 9.047-.783.73-1.662 1.095-2.638 1.095-1.017 0-1.89-.37-2.62-1.113-.73-.742-1.096-1.622-1.096-2.64 0-1.027.371-1.877 1.113-2.551L18.306.65c.476-.433 1-.65 1.573-.65z" fill-rule="nonzero" fill="#553D38"/></g></svg> <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>

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 244 B

View File

@ -8,7 +8,7 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { isEmpty, map, findIndex } from 'lodash'; import { isEmpty, map, findIndex } from 'lodash';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import Input from 'components/Input'; import Input from 'components/InputsIndex';
import styles from './styles.scss'; import styles from './styles.scss';
class InputCheckboxWithNestedInputs extends React.Component { // eslint-disable-line react/prefer-stateless-function class InputCheckboxWithNestedInputs extends React.Component { // eslint-disable-line react/prefer-stateless-function
@ -69,7 +69,7 @@ class InputCheckboxWithNestedInputs extends React.Component { // eslint-disable-
<div className={`${styles.inputCheckboxWithNestedInputs} col-md-12`}> <div className={`${styles.inputCheckboxWithNestedInputs} col-md-12`}>
<div className="form-check" style={{ zIndex: '9999' }}> <div className="form-check" style={{ zIndex: '9999' }}>
{title} {title}
<FormattedMessage id={this.props.data.label}> <FormattedMessage id={this.props.data.label.id}>
{(message) => ( {(message) => (
<label className={`${styles.checkboxLabel} form-check-label`} htmlFor={this.props.data.name} style={{ cursor: 'pointer' }}> <label className={`${styles.checkboxLabel} form-check-label`} htmlFor={this.props.data.name} style={{ cursor: 'pointer' }}>
<input <input

View File

@ -9,13 +9,45 @@ import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { get, map, includes, split, isEmpty, findIndex } from 'lodash'; import { get, map, includes, split, isEmpty, findIndex } from 'lodash';
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
import Input from 'components/Input'; import Input from 'components/InputsIndex';
import PopUpHeaderNavLink from 'components/PopUpHeaderNavLink'; import PopUpHeaderNavLink from 'components/PopUpHeaderNavLink';
import styles from './styles.scss'; import styles from './styles.scss';
/* eslint-disable react/jsx-wrap-multilines */ /* eslint-disable react/jsx-wrap-multilines */
class PopUpForm extends React.Component { // eslint-disable-line react/prefer-stateless-function class PopUpForm extends React.Component { // eslint-disable-line react/prefer-stateless-function
createComponent = (el) => {
if (get(el, ['inputDescription', 'params', 'link', 'children', 'type'], '') === 'FormattedMessage') {
return (
<FormattedMessage id={get(el, ['inputDescription', 'params', 'link', 'children', 'attr', 'id'], 'default')} defaultMessage=" ">
{(message) => (
React.createElement(
// Create the wrapper component
// This line will create the link
get(el, ['inputDescription', 'params', 'link', 'parent', 'type'], 'span'),
// Set the attributes
get(el, ['inputDescription', 'params', 'link', 'parent', 'attr'], ''),
message,
)
)}
</FormattedMessage>
);
}
return (
React.createElement(
get(el, ['inputDescription', 'params', 'link', 'parent', 'type'], 'span'),
// Set the attributes
get(el, ['inputDescription', 'params', 'link', 'parent', 'attr'], ''),
React.createElement(
get(el, ['inputDescription', 'params', 'link', 'children', 'type'], 'span'),
get(el, ['inputDescription', 'params', 'link', 'children', 'attr'], ''),
get(el, ['inputDescription', 'params', 'link', 'children', 'innerHTML'], ''),
)
)
);
}
renderInput = (item, key) => { renderInput = (item, key) => {
// const customBootstrapClass = 'col-md-6' // const customBootstrapClass = 'col-md-6'
let customBootstrapClass = item.type === 'textarea' ? let customBootstrapClass = item.type === 'textarea' ?
@ -36,6 +68,12 @@ class PopUpForm extends React.Component { // eslint-disable-line react/prefer-st
const handleBlur = shouldOverrideHandleBlur ? this.props.onBlur : false; const handleBlur = shouldOverrideHandleBlur ? this.props.onBlur : false;
const errorIndex = findIndex(this.props.formErrors, ['name', item.name]); const errorIndex = findIndex(this.props.formErrors, ['name', item.name]);
const errors = errorIndex !== -1 ? this.props.formErrors[errorIndex].errors : []; const errors = errorIndex !== -1 ? this.props.formErrors[errorIndex].errors : [];
const inputDescription = {
id: get(item, ['inputDescription', 'id'], ''),
params: {
link: this.createComponent(item),
},
};
return ( return (
<Input <Input
@ -46,7 +84,7 @@ class PopUpForm extends React.Component { // eslint-disable-line react/prefer-st
label={item.label} label={item.label}
name={item.name} name={item.name}
validations={item.validations} validations={item.validations}
inputDescription={item.inputDescription} inputDescription={inputDescription}
value={value} value={value}
customBootstrapClass={customBootstrapClass} customBootstrapClass={customBootstrapClass}
selectOptions={this.props.selectOptions || []} selectOptions={this.props.selectOptions || []}
@ -54,8 +92,6 @@ class PopUpForm extends React.Component { // eslint-disable-line react/prefer-st
title={item.title} title={item.title}
errors={errors} errors={errors}
didCheckErrors={this.props.didCheckErrors} didCheckErrors={this.props.didCheckErrors}
pluginID={this.props.pluginID}
linkContent={item.linkContent}
autoFocus={key === 0} autoFocus={key === 0}
/> />
); );
@ -159,7 +195,6 @@ PopUpForm.propTypes = {
PropTypes.bool, PropTypes.bool,
PropTypes.func, PropTypes.func,
]), ]),
pluginID: PropTypes.string,
popUpHeaderNavLinks: PropTypes.array, popUpHeaderNavLinks: PropTypes.array,
popUpTitle: PropTypes.string.isRequired, popUpTitle: PropTypes.string.isRequired,
renderCustomPopUpHeader: PropTypes.oneOfType([ renderCustomPopUpHeader: PropTypes.oneOfType([
@ -189,7 +224,6 @@ PopUpForm.defaultProps = {
overrideHandleBlurCondition: false, overrideHandleBlurCondition: false,
overrideRenderInput: false, overrideRenderInput: false,
overrideRenderInputCondition: false, overrideRenderInputCondition: false,
pluginID: 'content-type-builder',
popUpHeaderNavLinks: [], popUpHeaderNavLinks: [],
renderCustomPopUpHeader: false, renderCustomPopUpHeader: false,
routePath: '', routePath: '',

View File

@ -10,7 +10,7 @@ import { findIndex, get, isEmpty, map, take, takeRight } from 'lodash';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
import Input from 'components/Input'; import Input from 'components/InputsIndex';
import PopUpHeaderNavLink from 'components/PopUpHeaderNavLink'; import PopUpHeaderNavLink from 'components/PopUpHeaderNavLink';
import RelationBox from 'components/RelationBox'; import RelationBox from 'components/RelationBox';
import RelationNaturePicker from 'components/RelationNaturePicker'; import RelationNaturePicker from 'components/RelationNaturePicker';

View File

@ -11,7 +11,7 @@ import { get, isEmpty, map, startCase } from 'lodash';
import pluralize from 'pluralize'; import pluralize from 'pluralize';
import { ButtonDropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap'; import { ButtonDropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap';
import Input from 'components/Input'; import Input from 'components/InputsIndex';
import styles from './styles.scss'; import styles from './styles.scss';
/* eslint-disable jsx-a11y/no-static-element-interactions */ /* eslint-disable jsx-a11y/no-static-element-interactions */

View File

@ -3,7 +3,9 @@
"baseSettings": { "baseSettings": {
"items": [ "items": [
{ {
"label": "content-type-builder.form.contentType.item.connections", "label": {
"id": "content-type-builder.form.contentType.item.connections"
},
"name": "connection", "name": "connection",
"type": "select", "type": "select",
"value": "default", "value": "default",
@ -13,21 +15,40 @@
} }
}, },
{ {
"label": "content-type-builder.form.contentType.item.name", "label": {
"id": "content-type-builder.form.contentType.item.name"
},
"name": "name", "name": "name",
"value": "", "value": "",
"type": "string", "type": "string",
"validations": { "validations": {
"required": true "required": true
}, },
"inputDescription": "content-type-builder.form.contentType.item.name.description", "inputDescription": {
"linkContent": { "id": "content-type-builder.form.contentType.item.name.description",
"description": "content-type-builder.form.contentType.item.name.link.description", "params": {
"link": "https://v3.strapi.io/documentation/concepts/concepts.html#models" "link": {
"parent": {
"type": "a",
"attr": {
"href": "https://v3.strapi.io/documentation/concepts/concepts.html#models",
"target": "_blank"
}
},
"children": {
"type": "FormattedMessage",
"attr": {
"id": "content-type-builder.form.contentType.item.name.link.description"
}
}
}
}
} }
}, },
{ {
"label": "content-type-builder.form.contentType.item.description", "label": {
"id": "content-type-builder.form.contentType.item.description"
},
"name": "description", "name": "description",
"value": "", "value": "",
"type": "textarea", "type": "textarea",
@ -39,12 +60,16 @@
"advancedSettings": { "advancedSettings": {
"items": [ "items": [
{ {
"label": "content-type-builder.form.contentType.item.collectionName", "label": {
"id": "content-type-builder.form.contentType.item.collectionName"
},
"name": "collectionName", "name": "collectionName",
"type": "string", "type": "string",
"value": "", "value": "",
"validations": {}, "validations": {},
"inputDescription": "content-type-builder.form.contentType.item.collectionName.inputDescription" "inputDescription": {
"id": "content-type-builder.form.contentType.item.collectionName.inputDescription"
}
} }
] ]
} }
@ -94,7 +119,9 @@
"baseSettings": { "baseSettings": {
"items": [ "items": [
{ {
"label": "content-type-builder.form.attribute.item.textarea.name", "label": {
"id": "content-type-builder.form.attribute.item.textarea.name"
},
"name": "name", "name": "name",
"type": "string", "type": "string",
"value": "", "value": "",
@ -107,24 +134,36 @@
"advancedSettings": { "advancedSettings": {
"items": [ "items": [
{ {
"title": "content-type-builder.form.attribute.item.settings.name", "title": {
"label": "content-type-builder.form.attribute.item.requiredField", "id": "content-type-builder.form.attribute.item.settings.name"
},
"label": {
"id": "content-type-builder.form.attribute.item.requiredField"
},
"name": "params.required", "name": "params.required",
"type": "checkbox", "type": "checkbox",
"value": false, "value": false,
"validations": {}, "validations": {},
"inputDescription": "content-type-builder.form.attribute.item.requiredField.description" "inputDescription": {
"id": "content-type-builder.form.attribute.item.requiredField.description"
}
}, },
{ {
"label": "content-type-builder.form.attribute.item.uniqueField", "label": {
"id": "content-type-builder.form.attribute.item.uniqueField"
},
"name": "params.unique", "name": "params.unique",
"type": "checkbox", "type": "checkbox",
"value": false, "value": false,
"validations": {}, "validations": {},
"inputDescription": "content-type-builder.form.attribute.item.uniqueField.description" "inputDescription": {
"id": "content-type-builder.form.attribute.item.uniqueField.description"
}
}, },
{ {
"label": "content-type-builder.form.attribute.item.minimumLength", "label": {
"id": "content-type-builder.form.attribute.item.minimumLength"
},
"name": "params.minLength", "name": "params.minLength",
"type": "checkbox", "type": "checkbox",
"value": false, "value": false,
@ -142,7 +181,9 @@
] ]
}, },
{ {
"label": "content-type-builder.form.attribute.item.maximumLength", "label": {
"id": "content-type-builder.form.attribute.item.maximumLength"
},
"name": "params.maxLength", "name": "params.maxLength",
"type": "checkbox", "type": "checkbox",
"value": false, "value": false,
@ -166,7 +207,9 @@
"baseSettings": { "baseSettings": {
"items": [ "items": [
{ {
"label": "content-type-builder.form.attribute.item.number.name", "label": {
"id": "content-type-builder.form.attribute.item.number.name"
},
"name": "name", "name": "name",
"type": "string", "type": "string",
"value": "", "value": "",
@ -175,7 +218,9 @@
} }
}, },
{ {
"label": "content-type-builder.form.attribute.item.number.type", "label": {
"id": "content-type-builder.form.attribute.item.number.type"
},
"name": "params.type", "name": "params.type",
"type": "select", "type": "select",
"value": "integer", "value": "integer",
@ -193,24 +238,36 @@
"advancedSettings": { "advancedSettings": {
"items": [ "items": [
{ {
"title": "content-type-builder.form.attribute.item.settings.name", "title": {
"label": "content-type-builder.form.attribute.item.requiredField", "id": "content-type-builder.form.attribute.item.settings.name"
},
"label": {
"id": "content-type-builder.form.attribute.item.requiredField"
},
"name": "params.required", "name": "params.required",
"type": "checkbox", "type": "checkbox",
"value": false, "value": false,
"validations": {}, "validations": {},
"inputDescription": "content-type-builder.form.attribute.item.requiredField.description" "inputDescription": {
"id": "content-type-builder.form.attribute.item.requiredField.description"
}
}, },
{ {
"label": "content-type-builder.form.attribute.item.uniqueField", "label": {
"id": "content-type-builder.form.attribute.item.uniqueField"
},
"name": "params.unique", "name": "params.unique",
"type": "checkbox", "type": "checkbox",
"value": false, "value": false,
"validations": {}, "validations": {},
"inputDescription": "content-type-builder.form.attribute.item.uniqueField.description" "inputDescription": {
"id": "content-type-builder.form.attribute.item.uniqueField.description"
}
}, },
{ {
"label": "content-type-builder.form.attribute.item.minimum", "label": {
"id": "content-type-builder.form.attribute.item.minimum"
},
"name": "params.min", "name": "params.min",
"type": "checkbox", "type": "checkbox",
"value": false, "value": false,
@ -228,7 +285,9 @@
] ]
}, },
{ {
"label": "content-type-builder.form.attribute.item.maximum", "label": {
"id": "content-type-builder.form.attribute.item.maximum"
},
"name": "params.max", "name": "params.max",
"type": "checkbox", "type": "checkbox",
"value": false, "value": false,
@ -252,7 +311,9 @@
"baseSettings": { "baseSettings": {
"items": [ "items": [
{ {
"label": "content-type-builder.form.attribute.item.string.name", "label": {
"id": "content-type-builder.form.attribute.item.string.name"
},
"name": "name", "name": "name",
"type": "string", "type": "string",
"value": "", "value": "",
@ -265,24 +326,36 @@
"advancedSettings": { "advancedSettings": {
"items": [ "items": [
{ {
"title": "content-type-builder.form.attribute.item.settings.name", "title": {
"label": "content-type-builder.form.attribute.item.requiredField", "id": "content-type-builder.form.attribute.item.settings.name"
},
"label": {
"id": "content-type-builder.form.attribute.item.requiredField"
},
"name": "params.required", "name": "params.required",
"type": "checkbox", "type": "checkbox",
"value": false, "value": false,
"validations": {}, "validations": {},
"inputDescription": "content-type-builder.form.attribute.item.requiredField.description" "inputDescription": {
"id": "content-type-builder.form.attribute.item.requiredField.description"
}
}, },
{ {
"label": "content-type-builder.form.attribute.item.uniqueField", "label": {
"id": "content-type-builder.form.attribute.item.uniqueField"
},
"name": "params.unique", "name": "params.unique",
"type": "checkbox", "type": "checkbox",
"value": false, "value": false,
"validations": {}, "validations": {},
"inputDescription": "content-type-builder.form.attribute.item.uniqueField.description" "inputDescription": {
"id": "content-type-builder.form.attribute.item.uniqueField.description"
}
}, },
{ {
"label": "content-type-builder.form.attribute.item.minimumLength", "label": {
"id": "content-type-builder.form.attribute.item.minimumLength"
},
"name": "params.minLength", "name": "params.minLength",
"type": "checkbox", "type": "checkbox",
"value": false, "value": false,
@ -300,7 +373,9 @@
] ]
}, },
{ {
"label": "content-type-builder.form.attribute.item.maximumLength", "label": {
"id": "content-type-builder.form.attribute.item.maximumLength"
},
"name": "params.maxLength", "name": "params.maxLength",
"type": "checkbox", "type": "checkbox",
"value": false, "value": false,
@ -324,7 +399,9 @@
"baseSettings": { "baseSettings": {
"items": [ "items": [
{ {
"label": "content-type-builder.form.attribute.item.string.name", "label": {
"id": "content-type-builder.form.attribute.item.string.name"
},
"name": "name", "name": "name",
"type": "string", "type": "string",
"value": "", "value": "",
@ -337,24 +414,36 @@
"advancedSettings": { "advancedSettings": {
"items": [ "items": [
{ {
"title": "content-type-builder.form.attribute.item.settings.name", "title": {
"label": "content-type-builder.form.attribute.item.requiredField", "id": "content-type-builder.form.attribute.item.settings.name"
},
"label": {
"id": "content-type-builder.form.attribute.item.requiredField"
},
"name": "params.required", "name": "params.required",
"type": "checkbox", "type": "checkbox",
"value": false, "value": false,
"validations": {}, "validations": {},
"inputDescription": "content-type-builder.form.attribute.item.requiredField.description" "inputDescription": {
"id": "content-type-builder.form.attribute.item.requiredField.description"
}
}, },
{ {
"label": "content-type-builder.form.attribute.item.uniqueField", "label": {
"id": "content-type-builder.form.attribute.item.uniqueField"
},
"name": "params.unique", "name": "params.unique",
"type": "checkbox", "type": "checkbox",
"value": false, "value": false,
"validations": {}, "validations": {},
"inputDescription": "content-type-builder.form.attribute.item.uniqueField.description" "inputDescription": {
"id": "content-type-builder.form.attribute.item.uniqueField.description"
}
}, },
{ {
"label": "content-type-builder.form.attribute.item.minimumLength", "label": {
"id": "content-type-builder.form.attribute.item.minimumLength"
},
"name": "params.minLength", "name": "params.minLength",
"type": "checkbox", "type": "checkbox",
"value": false, "value": false,
@ -372,7 +461,9 @@
] ]
}, },
{ {
"label": "content-type-builder.form.attribute.item.maximumLength", "label": {
"id": "content-type-builder.form.attribute.item.maximumLength"
},
"name": "params.maxLength", "name": "params.maxLength",
"type": "checkbox", "type": "checkbox",
"value": false, "value": false,
@ -396,7 +487,9 @@
"baseSettings": { "baseSettings": {
"items": [ "items": [
{ {
"label": "content-type-builder.form.attribute.item.string.name", "label": {
"id": "content-type-builder.form.attribute.item.string.name"
},
"name": "name", "name": "name",
"type": "string", "type": "string",
"value": "", "value": "",
@ -409,16 +502,24 @@
"advancedSettings": { "advancedSettings": {
"items": [ "items": [
{ {
"title": "content-type-builder.form.attribute.item.settings.name", "title": {
"label": "content-type-builder.form.attribute.item.requiredField", "id": "content-type-builder.form.attribute.item.settings.name"
},
"label": {
"id": "content-type-builder.form.attribute.item.requiredField"
},
"name": "params.required", "name": "params.required",
"type": "checkbox", "type": "checkbox",
"value": false, "value": false,
"validations": {}, "validations": {},
"inputDescription": "content-type-builder.form.attribute.item.requiredField.description" "inputDescription": {
"id": "content-type-builder.form.attribute.item.requiredField.description"
}
}, },
{ {
"label": "content-type-builder.form.attribute.item.minimumLength", "label": {
"id": "content-type-builder.form.attribute.item.minimumLength"
},
"name": "params.minLength", "name": "params.minLength",
"type": "checkbox", "type": "checkbox",
"value": false, "value": false,
@ -436,7 +537,9 @@
] ]
}, },
{ {
"label": "content-type-builder.form.attribute.item.maximumLength", "label": {
"id": "content-type-builder.form.attribute.item.maximumLength"
},
"name": "params.maxLength", "name": "params.maxLength",
"type": "checkbox", "type": "checkbox",
"value": false, "value": false,
@ -460,7 +563,9 @@
"baseSettings": { "baseSettings": {
"items": [ "items": [
{ {
"label": "content-type-builder.form.attribute.item.date.name", "label": {
"id": "content-type-builder.form.attribute.item.date.name"
},
"name": "name", "name": "name",
"type": "string", "type": "string",
"value": "", "value": "",
@ -473,21 +578,31 @@
"advancedSettings": { "advancedSettings": {
"items": [ "items": [
{ {
"title": "content-type-builder.form.attribute.item.settings.name", "title": {
"label": "content-type-builder.form.attribute.item.requiredField", "id": "content-type-builder.form.attribute.item.settings.name"
},
"label": {
"id": "content-type-builder.form.attribute.item.requiredField"
},
"name": "params.required", "name": "params.required",
"type": "checkbox", "type": "checkbox",
"value": false, "value": false,
"validations": {}, "validations": {},
"inputDescription": "content-type-builder.form.attribute.item.requiredField.description" "inputDescription": {
"id": "content-type-builder.form.attribute.item.requiredField.description"
}
}, },
{ {
"label": "content-type-builder.form.attribute.item.uniqueField", "label": {
"id": "content-type-builder.form.attribute.item.uniqueField"
},
"name": "params.unique", "name": "params.unique",
"type": "checkbox", "type": "checkbox",
"value": false, "value": false,
"validations": {}, "validations": {},
"inputDescription": "content-type-builder.form.attribute.item.uniqueField.description" "inputDescription": {
"id": "content-type-builder.form.attribute.item.uniqueField.description"
}
} }
] ]
} }
@ -496,7 +611,9 @@
"baseSettings": { "baseSettings": {
"items": [ "items": [
{ {
"label": "content-type-builder.form.attribute.item.boolean.name", "label": {
"id": "content-type-builder.form.attribute.item.boolean.name"
},
"name": "name", "name": "name",
"type": "string", "type": "string",
"value": "", "value": "",
@ -509,21 +626,31 @@
"advancedSettings": { "advancedSettings": {
"items": [ "items": [
{ {
"title": "content-type-builder.form.attribute.item.settings.name", "title": {
"label": "content-type-builder.form.attribute.item.requiredField", "id": "content-type-builder.form.attribute.item.settings.name"
},
"label": {
"id": "content-type-builder.form.attribute.item.requiredField"
},
"name": "params.required", "name": "params.required",
"type": "checkbox", "type": "checkbox",
"value": false, "value": false,
"validations": {}, "validations": {},
"inputDescription": "content-type-builder.form.attribute.item.requiredField.description" "inputDescription": {
"id": "content-type-builder.form.attribute.item.requiredField.description"
}
}, },
{ {
"label": "content-type-builder.form.attribute.item.uniqueField", "label": {
"id": "content-type-builder.form.attribute.item.uniqueField"
},
"name": "params.unique", "name": "params.unique",
"type": "checkbox", "type": "checkbox",
"value": false, "value": false,
"validations": {}, "validations": {},
"inputDescription": "content-type-builder.form.attribute.item.uniqueField.description" "inputDescription": {
"id": "content-type-builder.form.attribute.item.uniqueField.description"
}
} }
] ]
} }
@ -532,7 +659,9 @@
"baseSettings": { "baseSettings": {
"items": [ "items": [
{ {
"label": "content-type-builder.form.attribute.item.json.name", "label": {
"id": "content-type-builder.form.attribute.item.json.name"
},
"name": "name", "name": "name",
"type": "string", "type": "string",
"value": "", "value": "",
@ -545,21 +674,31 @@
"advancedSettings": { "advancedSettings": {
"items": [ "items": [
{ {
"title": "content-type-builder.form.attribute.item.settings.name", "title": {
"label": "content-type-builder.form.attribute.item.requiredField", "id": "content-type-builder.form.attribute.item.settings.name"
},
"label": {
"id": "content-type-builder.form.attribute.item.requiredField"
},
"name": "params.required", "name": "params.required",
"type": "checkbox", "type": "checkbox",
"value": false, "value": false,
"validations": {}, "validations": {},
"inputDescription": "content-type-builder.form.attribute.item.requiredField.description" "inputDescription": {
"id": "content-type-builder.form.attribute.item.requiredField.description"
}
}, },
{ {
"label": "content-type-builder.form.attribute.item.uniqueField", "label": {
"id": "content-type-builder.form.attribute.item.uniqueField"
},
"name": "params.unique", "name": "params.unique",
"type": "checkbox", "type": "checkbox",
"value": false, "value": false,
"validations": {}, "validations": {},
"inputDescription": "content-type-builder.form.attribute.item.uniqueField.description" "inputDescription": {
"id": "content-type-builder.form.attribute.item.uniqueField.description"
}
} }
] ]
} }
@ -568,7 +707,9 @@
"baseSettings": { "baseSettings": {
"items": [ "items": [
{ {
"label": "content-type-builder.form.attribute.item.media.name", "label": {
"id": "content-type-builder.form.attribute.item.media.name"
},
"name": "name", "name": "name",
"type": "string", "type": "string",
"value": "", "value": "",
@ -581,21 +722,31 @@
"advancedSettings": { "advancedSettings": {
"items": [ "items": [
{ {
"title": "content-type-builder.form.attribute.item.settings.name", "title": {
"label": "content-type-builder.form.attribute.item.requiredField", "id": "content-type-builder.form.attribute.item.settings.name"
},
"label": {
"id": "content-type-builder.form.attribute.item.requiredField"
},
"name": "params.required", "name": "params.required",
"type": "checkbox", "type": "checkbox",
"value": false, "value": false,
"validations": {}, "validations": {},
"inputDescription": "content-type-builder.form.attribute.item.requiredField.description" "inputDescription": {
"id": "content-type-builder.form.attribute.item.requiredField.description"
}
}, },
{ {
"label": "content-type-builder.form.attribute.item.uniqueField", "label": {
"id": "content-type-builder.form.attribute.item.uniqueField"
},
"name": "params.unique", "name": "params.unique",
"type": "checkbox", "type": "checkbox",
"value": false, "value": false,
"validations": {}, "validations": {},
"inputDescription": "content-type-builder.form.attribute.item.uniqueField.description" "inputDescription": {
"id": "content-type-builder.form.attribute.item.uniqueField.description"
}
} }
] ]
} }
@ -604,7 +755,9 @@
"defineRelation": { "defineRelation": {
"items": [ "items": [
{ {
"label": "content-type-builder.form.attribute.item.defineRelation.fieldName", "label": {
"id": "content-type-builder.form.attribute.item.defineRelation.fieldName"
},
"name": "name", "name": "name",
"type": "string", "type": "string",
"value": "", "value": "",
@ -614,7 +767,9 @@
} }
}, },
{ {
"label": "content-type-builder.form.attribute.item.defineRelation.fieldName", "label": {
"id": "content-type-builder.form.attribute.item.defineRelation.fieldName"
},
"name": "params.key", "name": "params.key",
"type": "string", "type": "string",
"value": "", "value": "",
@ -628,20 +783,28 @@
"advancedSettings": { "advancedSettings": {
"items": [ "items": [
{ {
"label": "content-type-builder.form.attribute.item.uniqueField", "label": {
"id": "content-type-builder.form.attribute.item.uniqueField"
},
"name": "params.unique", "name": "params.unique",
"type": "checkbox", "type": "checkbox",
"value": false, "value": false,
"validations": {}, "validations": {},
"inputDescription": "content-type-builder.form.attribute.item.uniqueField.description" "inputDescription": {
"id": "content-type-builder.form.attribute.item.uniqueField.description"
}
}, },
{ {
"label": "content-type-builder.form.attribute.item.customColumnName", "label": {
"id": "content-type-builder.form.attribute.item.customColumnName"
},
"name": "params.columnName", "name": "params.columnName",
"type": "string", "type": "string",
"value": "", "value": "",
"validations": {}, "validations": {},
"inputDescription": "content-type-builder.form.attribute.item.customColumnName.description" "inputDescription": {
"id": "content-type-builder.form.attribute.item.customColumnName.description"
}
}, },
{ {
"label": "", "label": "",

View File

@ -70,7 +70,7 @@
"form.contentType.item.connections": "Connection", "form.contentType.item.connections": "Connection",
"form.contentType.item.name": "Name", "form.contentType.item.name": "Name",
"form.contentType.item.name.description": "Content type name's should be singular", "form.contentType.item.name.description": "Content type name's should be singular, {link}",
"form.contentType.item.name.link.description": "check out our documentation", "form.contentType.item.name.link.description": "check out our documentation",
"form.contentType.item.description": "Description", "form.contentType.item.description": "Description",
"form.contentType.item.description.placeholder": "Write your little description here...", "form.contentType.item.description.placeholder": "Write your little description here...",

View File

@ -72,7 +72,7 @@
"form.contentType.item.connections": "Connexion", "form.contentType.item.connections": "Connexion",
"form.contentType.item.name": "Nom", "form.contentType.item.name": "Nom",
"form.contentType.item.name.description": "Les noms de modèles doivent être au singulier", "form.contentType.item.name.description": "Les noms de modèles doivent être au singulier, {link}",
"form.contentType.item.name.link.description": "regardez la documentation", "form.contentType.item.name.link.description": "regardez la documentation",
"form.contentType.item.description": "Description", "form.contentType.item.description": "Description",
"form.contentType.item.description.placeholder": "Ecrivez votre petite description ici...", "form.contentType.item.description.placeholder": "Ecrivez votre petite description ici...",

View File

@ -79,7 +79,7 @@ module.exports = {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const scope = { const scope = {
generatorType: 'api', generatorType: 'api',
id: name, id: name.toLowerCase(),
rootPath: strapi.config.appPath, rootPath: strapi.config.appPath,
args: { args: {
api: name, api: name,

View File

@ -0,0 +1 @@
<svg width="24" height="17" xmlns="http://www.w3.org/2000/svg"><text transform="translate(-23 -9)" fill="#4B515A" fill-rule="evenodd" font-size="24" font-family="AppleColorEmoji, Apple Color Emoji"><tspan x="23" y="28">✉️</tspan></text></svg>

After

Width:  |  Height:  |  Size: 246 B

View File

@ -1 +1 @@
<svg width="54" height="34" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><g fill-rule="nonzero"><path d="M11.392 30.3a1.797 1.797 0 0 0-.22-1.409 1.797 1.797 0 0 0-1.141-.857 1.797 1.797 0 0 0-1.41.22c-.449.27-.734.65-.857 1.142-.122.492-.049.962.22 1.41.27.449.65.734 1.142.857.491.122.961.049 1.41-.22.448-.27.734-.65.856-1.142zm21.224-7.353L8.464 37.459c-.874.525-1.812.663-2.813.413-.983-.245-1.756-.809-2.318-1.692L1.09 32.37c-.548-.86-.695-1.8-.44-2.82.249-1.002.822-1.772 1.72-2.311l24.117-14.492a14.885 14.885 0 0 0 2.02 5.728 14.885 14.885 0 0 0 4.108 4.472z" fill="#181D29"/><path d="M53.663 15.097c-.184.737-.65 1.684-1.401 2.842-1.52 2.31-3.587 3.978-6.2 5.003-2.615 1.024-5.254 1.204-7.918.54-3.497-.872-6.177-2.86-8.043-5.965-1.865-3.105-2.362-6.405-1.49-9.901.871-3.496 2.86-6.177 5.964-8.043 3.105-1.865 6.405-2.362 9.901-1.49 1.096.273 2.205.715 3.328 1.326 1.122.611 2.028 1.304 2.718 2.078.251.283.336.586.256.907-.08.321-.297.548-.651.68l-9.5 2.72-1.584 6.35 4.715 4.397c.109-.033.97-.305 2.582-.816 1.613-.511 3.084-.957 4.414-1.339 1.33-.38 2.08-.55 2.25-.508.283.071.481.22.595.45.113.229.135.485.064.769z" fill="#A1A1A1"/></g><path stroke="#C6C6C6" fill="#D8D8D8" d="M46.521 5.425l3.657 3.41-1.125 4.872-4.781 1.461-3.657-3.41 1.125-4.871z"/></g></svg> <svg width="40" height="28" xmlns="http://www.w3.org/2000/svg"><g fill="#4B515A" fill-rule="evenodd" font-family="AppleColorEmoji, Apple Color Emoji"><text font-size="14" transform="translate(0 -2)"><tspan x="0" y="27">⚙️</tspan></text><text font-size="24" transform="translate(0 -2)"><tspan x="16" y="24">⚙️</tspan></text></g></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 341 B

View File

@ -1 +1 @@
<svg width="28" height="26" xmlns="http://www.w3.org/2000/svg"><g fill-rule="nonzero" fill="none"><path d="M8.453 12.772c-1.54.047-2.799.656-3.778 1.824h-1.91c-.779 0-1.435-.192-1.967-.577C.266 13.634 0 13.071 0 12.33c0-3.354.59-5.032 1.768-5.032.057 0 .263.1.62.3.356.2.82.401 1.39.605.57.205 1.135.307 1.696.307.636 0 1.268-.11 1.896-.328a7.106 7.106 0 0 0-.072.94c0 1.322.385 2.538 1.155 3.65zm15.266 9.08c0 1.14-.347 2.04-1.04 2.701-.694.66-1.616.99-2.766.99H7.455c-1.15 0-2.072-.33-2.765-.99-.694-.66-1.04-1.56-1.04-2.701 0-.504.016-.995.049-1.475.033-.48.1-.998.2-1.554s.225-1.072.377-1.547c.152-.475.357-.938.613-1.39.257-.45.551-.836.884-1.154a3.718 3.718 0 0 1 1.219-.763 4.28 4.28 0 0 1 1.59-.285c.094 0 .298.102.612.307.314.204.66.432 1.04.684.38.252.89.48 1.526.684a6.264 6.264 0 0 0 1.924.307c.646 0 1.288-.103 1.925-.307.636-.204 1.145-.432 1.525-.684.38-.252.727-.48 1.04-.684.314-.205.518-.307.613-.307.58 0 1.11.095 1.59.285.48.19.886.445 1.218.763.333.318.628.703.884 1.155.257.45.461.914.613 1.39.152.474.278.99.378 1.546.1.556.166 1.074.2 1.554.033.48.05.971.05 1.475zm3.65-9.522c0 .741-.267 1.304-.799 1.69-.532.384-1.188.576-1.967.576h-1.91c-.979-1.168-2.238-1.777-3.777-1.824.77-1.112 1.154-2.328 1.154-3.65 0-.275-.024-.588-.071-.94.627.219 1.259.328 1.896.328.56 0 1.126-.102 1.696-.307a9.374 9.374 0 0 0 1.39-.605c.356-.2.563-.3.62-.3 1.178 0 1.767 1.678 1.767 5.032z" fill="#553D38"/><path d="M9.123 3.65a3.516 3.516 0 0 1-1.07 2.58 3.516 3.516 0 0 1-2.58 1.068 3.516 3.516 0 0 1-2.58-1.069 3.516 3.516 0 0 1-1.068-2.58c0-1.007.356-1.867 1.069-2.58A3.516 3.516 0 0 1 5.474 0C6.48 0 7.34.356 8.054 1.07a3.516 3.516 0 0 1 1.069 2.58zm10.035 5.473c0 1.51-.535 2.8-1.604 3.87-1.069 1.069-2.359 1.603-3.87 1.603-1.51 0-2.8-.534-3.87-1.603-1.069-1.07-1.603-2.36-1.603-3.87 0-1.511.534-2.801 1.603-3.87 1.07-1.07 2.36-1.604 3.87-1.604 1.511 0 2.801.535 3.87 1.604 1.07 1.069 1.604 2.359 1.604 3.87zm6.386-5.474a3.516 3.516 0 0 1-1.07 2.58 3.516 3.516 0 0 1-2.58 1.07 3.516 3.516 0 0 1-2.58-1.07 3.516 3.516 0 0 1-1.068-2.58c0-1.007.356-1.867 1.069-2.58A3.516 3.516 0 0 1 21.895 0c1.007 0 1.867.356 2.58 1.07a3.516 3.516 0 0 1 1.069 2.58z" fill="#EBBF8E"/><path d="M12.496 19.991h2.393v-.897c0-.33-.117-.612-.35-.846a1.153 1.153 0 0 0-.847-.35c-.33 0-.612.116-.846.35-.233.234-.35.516-.35.846v.897zm3.889.45v2.691c0 .125-.044.231-.131.318a.433.433 0 0 1-.318.131h-4.487a.433.433 0 0 1-.318-.13.433.433 0 0 1-.131-.319V20.44c0-.124.044-.23.13-.318a.433.433 0 0 1 .319-.13h.15v-.898c0-.573.205-1.066.616-1.477A2.015 2.015 0 0 1 13.692 17c.574 0 1.066.206 1.477.617.412.411.617.904.617 1.477v.897h.15c.125 0 .23.044.318.131.087.088.13.194.13.318z" fill="#FFF"/></g></svg> <svg width="24" height="24" xmlns="http://www.w3.org/2000/svg"><text transform="translate(-24 -6)" fill="#4B515A" fill-rule="evenodd" font-size="24" font-family="AppleColorEmoji, Apple Color Emoji"><tspan x="24" y="28">🔐</tspan></text></svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 244 B

View File

@ -86,7 +86,7 @@
"HeaderNav.link.providers": "Providers", "HeaderNav.link.providers": "Providers",
"HeaderNav.link.roles": "Roles", "HeaderNav.link.roles": "Roles",
"HomePage.header.title": "Auth & Permissions", "HomePage.header.title": "Users & Permissions",
"HomePage.header.description": "Define the roles and permissions for every one of them", "HomePage.header.description": "Define the roles and permissions for every one of them",
"InputSearch.placeholder": "Search for a user", "InputSearch.placeholder": "Search for a user",

View File

@ -86,7 +86,7 @@
"HeaderNav.link.providers": "Fournisseurs", "HeaderNav.link.providers": "Fournisseurs",
"HeaderNav.link.roles": "Rôles", "HeaderNav.link.roles": "Rôles",
"HomePage.header.title": "Auth & Permissions", "HomePage.header.title": "Utilisateurs & Permissions",
"HomePage.header.description": "Définissez les rôles et permissions pour chacun d'eux", "HomePage.header.description": "Définissez les rôles et permissions pour chacun d'eux",
"InputSearch.placeholder": "Recherez un utilisateur", "InputSearch.placeholder": "Recherez un utilisateur",

View File

@ -8,7 +8,6 @@
const _ = require('lodash'); const _ = require('lodash');
const crypto = require('crypto'); const crypto = require('crypto');
const Grant = require('grant-koa');
const emailRegExp = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; const emailRegExp = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
module.exports = { module.exports = {
@ -65,7 +64,7 @@ module.exports = {
return ctx.badRequest(null, ctx.request.admin ? [{ messages: [{ id: 'Auth.form.error.invalid' }] }] : 'Identifier or password invalid.'); return ctx.badRequest(null, ctx.request.admin ? [{ messages: [{ id: 'Auth.form.error.invalid' }] }] : 'Identifier or password invalid.');
} else { } else {
ctx.send({ ctx.send({
jwt: strapi.plugins['users-permissions'].services.jwt.issue(user), jwt: strapi.plugins['users-permissions'].services.jwt.issue(_.pick(user.toJSON ? user.toJSON() : user, ['_id', 'id'])),
user: _.omit(user.toJSON ? user.toJSON() : user, ['password', 'resetPasswordToken']) user: _.omit(user.toJSON ? user.toJSON() : user, ['password', 'resetPasswordToken'])
}); });
} }
@ -87,7 +86,7 @@ module.exports = {
} }
ctx.send({ ctx.send({
jwt: strapi.plugins['users-permissions'].services.jwt.issue(user), jwt: strapi.plugins['users-permissions'].services.jwt.issue(_.pick(user, ['_id', 'id'])),
user: _.omit(user.toJSON ? user.toJSON() : user, ['password', 'resetPasswordToken']) user: _.omit(user.toJSON ? user.toJSON() : user, ['password', 'resetPasswordToken'])
}); });
} }
@ -112,7 +111,7 @@ module.exports = {
await strapi.query('user', 'users-permissions').update(user); await strapi.query('user', 'users-permissions').update(user);
ctx.send({ ctx.send({
jwt: strapi.plugins['users-permissions'].services.jwt.issue(user), jwt: strapi.plugins['users-permissions'].services.jwt.issue(_.pick(user.toJSON ? user.toJSON() : user, ['_id', 'id'])),
user: _.omit(user.toJSON ? user.toJSON() : user, ['password', 'resetPasswordToken']) user: _.omit(user.toJSON ? user.toJSON() : user, ['password', 'resetPasswordToken'])
}); });
} else if (params.password && params.passwordConfirmation && params.password !== params.passwordConfirmation) { } else if (params.password && params.passwordConfirmation && params.password !== params.passwordConfirmation) {
@ -137,6 +136,7 @@ module.exports = {
return ctx.badRequest(null, 'This provider is disabled.'); return ctx.badRequest(null, 'This provider is disabled.');
} }
const Grant = require('grant-koa');
const grant = new Grant(strapi.plugins['users-permissions'].config.grant); const grant = new Grant(strapi.plugins['users-permissions'].config.grant);
return strapi.koaMiddlewares.compose(grant.middleware)(ctx, next); return strapi.koaMiddlewares.compose(grant.middleware)(ctx, next);
@ -250,7 +250,7 @@ module.exports = {
const user = await strapi.query('user', 'users-permissions').create(params); const user = await strapi.query('user', 'users-permissions').create(params);
ctx.send({ ctx.send({
jwt: strapi.plugins['users-permissions'].services.jwt.issue(user), jwt: strapi.plugins['users-permissions'].services.jwt.issue(_.pick(user.toJSON ? user.toJSON() : user, ['_id', 'id'])),
user: _.omit(user.toJSON ? user.toJSON() : user, ['password', 'resetPasswordToken']) user: _.omit(user.toJSON ? user.toJSON() : user, ['password', 'resetPasswordToken'])
}); });
} catch(err) { } catch(err) {

View File

@ -25,11 +25,13 @@
"password": { "password": {
"type": "password", "type": "password",
"minLength": 6, "minLength": 6,
"configurable": false "configurable": false,
"private": true
}, },
"resetPasswordToken": { "resetPasswordToken": {
"type": "string", "type": "string",
"configurable": false "configurable": false,
"private": true
}, },
"role": { "role": {
"model": "role", "model": "role",

View File

@ -3,7 +3,7 @@
"version": "3.0.0-alpha.9.3", "version": "3.0.0-alpha.9.3",
"description": "Protect your API with a full-authentication process based on JWT", "description": "Protect your API with a full-authentication process based on JWT",
"strapi": { "strapi": {
"name": "Auth & Permissions", "name": "Users & Permissions",
"icon": "users", "icon": "users",
"description": "users-permissions.plugin.description" "description": "users-permissions.plugin.description"
}, },

View File

@ -42,7 +42,15 @@ module.exports = function (name, cliArguments) {
developerMode developerMode
}; };
if (_.values(_.omit(cliArguments, ['dev'])).length) { const dbArguments = ['dbclient', 'dbhost', 'dbport', 'dbname', 'dbusername', 'dbpassword'];
const matchingDbArguments = _.intersection(_.keys(cliArguments), dbArguments);
if (matchingDbArguments.length) {
if (matchingDbArguments.length !== dbArguments.length) {
logger.warn(`Some database arguments are missing. Required arguments list: ${dbArguments}`);
return process.exit(1);
}
scope.database = { scope.database = {
settings: { settings: {
client: cliArguments.dbclient, client: cliArguments.dbclient,

View File

@ -244,6 +244,9 @@ module.exports.app = async function() {
boom: { boom: {
enabled: true enabled: true
}, },
mask: {
enabled: true
},
// Necessary middlewares for the administration panel. // Necessary middlewares for the administration panel.
cors: { cors: {
enabled: true enabled: true

View File

@ -0,0 +1,5 @@
{
"mask": {
"enabled": false
}
}

View File

@ -0,0 +1,158 @@
'use strict';
/**
* Module dependencies
*/
/**
* Mask filter middleware
*/
const _ = require('lodash');
module.exports = strapi => {
return {
/**
* Initialize the hook
*/
initialize: function (cb) {
// Enable the middleware if we need it.
const enabled = (() => {
const main = Object.keys(strapi.models).reduce((acc, current) => {
if (Object.values(strapi.models[current].attributes).find(attr => attr.private === true)) {
acc = true;
}
return acc;
}, false);
const plugins = Object.keys(strapi.plugins).reduce((acc, plugin) => {
const bool = Object.keys(strapi.plugins[plugin].models).reduce((acc, model) => {
if (Object.values(strapi.plugins[plugin].models[model].attributes).find(attr => attr.private === true)) {
acc = true;
}
return acc;
}, false);
if (bool) {
acc = true;
}
return acc;
}, false);
return main || plugins;
})();
if (enabled) {
strapi.app.use(async (ctx, next) => {
// Execute next middleware.
await next();
// Recursive to mask the private properties.
const mask = (payload) => {
if (_.isArray(payload)) {
return payload.map(mask);
} else if (_.isPlainObject(payload)) {
return this.mask(
ctx,
Object.keys(payload).reduce((acc, current) => {
acc[current] = _.isObjectLike(payload[current]) ? mask(payload[current]) : payload[current];
return acc;
}, {})
);
}
return payload;
};
// Only pick successful JSON requests.
if ([200, 201, 202].includes(ctx.status) && ctx.type === 'application/json') {
ctx.body = mask(ctx.body);
}
});
}
cb();
},
mask: function (ctx, value) {
const models = this.filteredModels(this.whichModels(value, ctx.request.route.plugin));
if (models.length === 0) {
return value;
}
const attributesToHide = models.reduce((acc, match) => {
const attributes = match.plugin ?
strapi.plugins[match.plugin].models[match.model].attributes:
strapi.models[match.model].attributes;
acc = acc.concat(Object.keys(attributes).filter(attr => attributes[attr].private === true));
return acc;
}, []);
// Hide attribute.
return _.omit(value, attributesToHide);
},
whichModels: function (value, plugin) {
const keys = Object.keys(value);
let maxMatch = 0;
let matchs = [];
const match = (model, plugin) => {
const attributes = plugin ?
Object.keys(strapi.plugins[plugin].models[model].attributes):
Object.keys(strapi.models[model].attributes);
const intersection = _.intersection(keys, attributes.filter(attr => ['id', '_id', '_v'].indexOf(attr) === -1 )).length;
// Most matched model.
if (intersection > maxMatch) {
maxMatch = intersection;
matchs = [{
plugin,
model,
intersection
}];
} else if (intersection === maxMatch && intersection > 0) {
matchs.push({
plugin,
model,
intersection
});
}
};
// Application models.
Object.keys(strapi.models).forEach(model => match(model));
// Plugins models.
Object.keys(strapi.plugins).forEach(plugin => {
Object.keys(strapi.plugins[plugin].models).forEach(model => match(model, plugin));
});
return matchs;
},
filteredModels: function (matchs) {
return matchs.reduce((acc, match, index) => {
const attributes = match.plugin ?
strapi.plugins[match.plugin].models[match.model].attributes:
strapi.models[match.model].attributes;
// Filtered model which have more than 50% of the attributes
// in common with the original model.
if (match.intersection >= Object.keys(attributes).length / 2) {
acc[index] = match;
}
return acc;
}, []);
}
};
};