mirror of
https://github.com/strapi/strapi.git
synced 2025-12-02 01:52:21 +00:00
Merge branch 'master' into master
This commit is contained in:
commit
d9af887dc9
@ -82,4 +82,4 @@ For general help using Strapi, please refer to [the official Strapi documentatio
|
||||
|
||||
## License
|
||||
|
||||
[MIT License](LICENSE.md) Copyright (c) 2015-2017 [Strapi Solutions](http://strapi.io/).
|
||||
[MIT License](LICENSE.md) Copyright (c) 2015-2018 [Strapi Solutions](http://strapi.io/).
|
||||
|
||||
@ -16,8 +16,10 @@
|
||||
* [Table of contents](concepts/concepts.md)
|
||||
|
||||
### Guides
|
||||
* [Authentification](guides/authentification.md)
|
||||
* [Configurations](configurations/configurations.md)
|
||||
* [Controllers](guides/controllers.md)
|
||||
* [Deployment](guides/deployment.md)
|
||||
* [Filters](guides/filters.md)
|
||||
* [Internationalization](guides/i18n.md)
|
||||
* [Models](guides/models.md)
|
||||
@ -27,8 +29,6 @@
|
||||
* [Responses](guides/responses.md)
|
||||
* [Routing](guides/routing.md)
|
||||
* [Services](guides/services.md)
|
||||
* [Authentification](guides/authentification.md)
|
||||
* [Deployment](guides/deployment.md)
|
||||
|
||||
### Plugins
|
||||
* [Quick start](plugins/quick-start.md)
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
## How to create a policy?
|
||||
|
||||
There is several ways to create a policy.
|
||||
There are several ways to create a policy.
|
||||
- Using the CLI `strapi generate:policy isAuthenticated`. Read the [CLI documentation](../cli/CLI.md) for more information.
|
||||
- Manually create a JavaScript file named `isAuthenticated.js` in `./config/policies/`.
|
||||
|
||||
@ -20,7 +20,7 @@ module.exports = async (ctx, next) => {
|
||||
};
|
||||
```
|
||||
|
||||
In this example, we are verifying that a session is open. If it is the case, we are calling the `next()` method that will execute the next policy or controller's action. Otherwise, a 401 error is returned.
|
||||
In this example, we are verifying that a session is open. If it is the case, we call the `next()` method that will execute the next policy or controller's action. Otherwise, a 401 error is returned.
|
||||
|
||||
> Note: You can access to any controllers, services or models thanks to the global variable `strapi` in a policy.
|
||||
|
||||
@ -82,7 +82,7 @@ The policy `isAuthenticated` located in `./plugins/auth/config/policies/isAuthen
|
||||
|
||||
### Scoped Policies
|
||||
|
||||
The scoped policies can only be associated to the routes defining in the API where they have been declared.
|
||||
The scoped policies can only be associated to the routes defined in the API where they have been declared.
|
||||
|
||||
**Path —** `./api/car/config/policies/isAdmin.js`.
|
||||
```js
|
||||
|
||||
@ -68,6 +68,24 @@ Please refer to the [Controllers documentation](../guides/controllers.md) for mo
|
||||
|
||||
A plugin can have its own models.
|
||||
|
||||
##### Table/Collection naming
|
||||
|
||||
Sometimes it happens that the plugins inject models that have the same name as yours. Let's take a quick example.
|
||||
|
||||
You already have `User` model defining in your `./api/user/models/User.settings.json` API. And you decide to install the `Users & Permissions` plugin. This plugin also contains a `User` model. To avoid the conflicts, the plugins' models are not globally exposed which means you cannot access to the plugin's model like this:
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
findUser: async function (params) {
|
||||
// This `User` global variable will always make a reference the User model defining in your `./api/xxx/models/User.settings.json`.
|
||||
return await User.find();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Also, the table/collection name won't be `users` because you already have a `User` model. That's why, the framework will automatically prefix the table/collection name for this model with the name of the plugin. Which means in our example, the table/collection name of the `User` model of our plugin `Users & Permissions` will be `users-permissions_users`. If you want to force the table/collection name of the plugin's model, you can add the `collectionName` attribute in your model.
|
||||
|
||||
|
||||
Please refer to the [Models documentation](../guides/models.md) for more informations.
|
||||
|
||||
### Policies
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"private": true,
|
||||
"version": "3.0.0-alpha.7.2",
|
||||
"version": "3.0.0-alpha.7.3",
|
||||
"devDependencies": {
|
||||
"assert": "~1.3.0",
|
||||
"babel-eslint": "^6.1.2",
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { startsWith } from 'lodash';
|
||||
import { startsWith, upperFirst } from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { Link } from 'react-router-dom';
|
||||
@ -17,6 +17,10 @@ class LeftMenuLink extends React.Component { // eslint-disable-line react/prefer
|
||||
// We need to create our own active url checker,
|
||||
// because of the two levels router.
|
||||
const isLinkActive = startsWith(window.location.pathname.replace('/admin', ''), this.props.destination);
|
||||
const plugin = this.props.source !== 'content-manager' && this.props.source !== '' ?
|
||||
(<div className={styles.plugin}>
|
||||
<span>{upperFirst(this.props.source.split('-').join(' '))}</span>
|
||||
</div>) : '';
|
||||
|
||||
return (
|
||||
<li className={styles.item}>
|
||||
@ -32,11 +36,12 @@ class LeftMenuLink extends React.Component { // eslint-disable-line react/prefer
|
||||
id={this.props.label}
|
||||
defaultMessage='{label}'
|
||||
values={{
|
||||
label: this.props.label,
|
||||
label: `${this.props.label}`,
|
||||
}}
|
||||
className={styles.linkLabel}
|
||||
/>
|
||||
</Link>
|
||||
{plugin}
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
@ -2,9 +2,41 @@
|
||||
@import "../../styles/variables/variables";
|
||||
|
||||
.item {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.plugin {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 10px; left: calc(100% - 4px);
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
height: 20px;
|
||||
transition: right 1s ease-in-out;
|
||||
|
||||
span{
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
width: auto;
|
||||
height: 20px;
|
||||
padding: 0 14px 0 10px;
|
||||
color: #ffffff;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
background: #0097f7;
|
||||
border-radius: 3px;
|
||||
transition: transform .3s ease-in-out;
|
||||
white-space: pre;
|
||||
|
||||
&:hover{
|
||||
transform: translateX(calc(-100% + 9px));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.link {
|
||||
|
||||
@ -40,8 +40,8 @@ function LeftMenuLinkContainer({ plugins }) {
|
||||
<div key={j}>
|
||||
<p className={styles.title}>{pluginsSections[current].name}</p>
|
||||
<ul className={styles.list}>
|
||||
{sortBy(pluginsSections[current].links, 'label').map(link =>
|
||||
<LeftMenuLink key={link.label} icon={link.icon || 'link'} label={link.label} destination={`/plugins/${link.plugin}/${link.destination}`} source={link.source} />
|
||||
{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} />
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-admin",
|
||||
"version": "3.0.0-alpha.7.2",
|
||||
"version": "3.0.0-alpha.7.3",
|
||||
"description": "Strapi Admin",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -26,8 +26,8 @@
|
||||
"devDependencies": {
|
||||
"sanitize.css": "^4.1.0",
|
||||
"shelljs": "^0.7.8",
|
||||
"strapi-helper-plugin": "3.0.0-alpha.7.2",
|
||||
"strapi-utils": "3.0.0-alpha.7.2"
|
||||
"strapi-helper-plugin": "3.0.0-alpha.7.3",
|
||||
"strapi-utils": "3.0.0-alpha.7.3"
|
||||
},
|
||||
"author": {
|
||||
"name": "Strapi",
|
||||
|
||||
@ -19,6 +19,7 @@ const utils = require('./utils/');
|
||||
const utilsModels = require('strapi-utils').models;
|
||||
|
||||
const PIVOT_PREFIX = '_pivot_';
|
||||
const GLOBALS = {};
|
||||
|
||||
/**
|
||||
* Bookshelf hook
|
||||
@ -40,18 +41,9 @@ module.exports = function(strapi) {
|
||||
*/
|
||||
|
||||
initialize: cb => {
|
||||
let globalName;
|
||||
const connections = _.pickBy(strapi.config.connections, { connector: 'strapi-bookshelf' });
|
||||
|
||||
// Initialize collections
|
||||
_.set(strapi, 'bookshelf.collections', {});
|
||||
|
||||
const connections = _.pickBy(strapi.config.connections, {
|
||||
connector: 'strapi-bookshelf'
|
||||
});
|
||||
|
||||
const done = _.after(_.size(connections), () => {
|
||||
cb();
|
||||
});
|
||||
const done = _.after(_.size(connections), cb);
|
||||
|
||||
_.forEach(connections, (connection, connectionName) => {
|
||||
// Apply defaults
|
||||
@ -73,55 +65,38 @@ module.exports = function(strapi) {
|
||||
}
|
||||
|
||||
// Load plugins
|
||||
if (_.get(connection, 'options.plugins') !== false) {
|
||||
if (_.get(connection, 'options.plugins', true) !== false) {
|
||||
ORM.plugin('visibility');
|
||||
ORM.plugin('pagination');
|
||||
}
|
||||
|
||||
// Select models concerned by this connection
|
||||
let models = _.pickBy(strapi.models, {
|
||||
connection: connectionName
|
||||
});
|
||||
if (connectionName === strapi.config.currentEnvironment.database.defaultConnection) {
|
||||
_.assign(models, _.pickBy(strapi.models, (model) => model.connection === undefined));
|
||||
}
|
||||
|
||||
const loadedHook = _.after(_.size(models), () => {
|
||||
done();
|
||||
});
|
||||
const models = _.pickBy(strapi.models, { connection: connectionName });
|
||||
// Will call the done() method when every models will be loaded.
|
||||
const loadedHook = _.after(_.size(models), done);
|
||||
|
||||
const mountModels = (models, target, plugin = false) => {
|
||||
// Parse every registered model.
|
||||
_.forEach(models, (definition, model) => {
|
||||
if (plugin) {
|
||||
definition.globalId = _.upperFirst(_.camelCase(`${plugin}-${model}`));
|
||||
}
|
||||
|
||||
globalName = _.upperFirst(_.camelCase(definition.globalId));
|
||||
definition.globalName = _.upperFirst(_.camelCase(definition.globalId));
|
||||
|
||||
_.defaults(definition, {
|
||||
primaryKey: 'id'
|
||||
});
|
||||
|
||||
// Make sure the model has a table name.
|
||||
// If not, use the model name.
|
||||
if (_.isEmpty(definition.collectionName)) {
|
||||
definition.collectionName = model;
|
||||
}
|
||||
// Define local GLOBALS to expose every models in this file.
|
||||
GLOBALS[definition.globalId] = {};
|
||||
|
||||
// Add some informations about ORM & client connection
|
||||
// Add some informations about ORM & client connection & tableName
|
||||
definition.orm = 'bookshelf';
|
||||
definition.client = _.get(connection.settings, 'client');
|
||||
|
||||
// Register the final model for Bookshelf.
|
||||
const loadedModel = _.assign(
|
||||
{
|
||||
const loadedModel = _.assign({
|
||||
tableName: definition.collectionName,
|
||||
hasTimestamps: _.get(definition, 'options.timestamps') === true,
|
||||
idAttribute: _.get(definition, 'options.idAttribute') || 'id'
|
||||
},
|
||||
definition.options
|
||||
);
|
||||
idAttribute: _.get(definition, 'options.idAttribute', 'id')
|
||||
}, definition.options);
|
||||
|
||||
if (_.isString(_.get(connection, 'options.pivot_prefix'))) {
|
||||
loadedModel.toJSON = function(options = {}) {
|
||||
@ -129,20 +104,13 @@ module.exports = function(strapi) {
|
||||
const attributes = this.serialize(options);
|
||||
|
||||
if (!shallow) {
|
||||
const pivot = this.pivot &&
|
||||
!omitPivot &&
|
||||
this.pivot.attributes;
|
||||
const pivot = this.pivot && !omitPivot && this.pivot.attributes;
|
||||
|
||||
// Remove pivot attributes with prefix.
|
||||
_.keys(pivot).forEach(
|
||||
key => delete attributes[`${PIVOT_PREFIX}${key}`]
|
||||
);
|
||||
_.keys(pivot).forEach(key => delete attributes[`${PIVOT_PREFIX}${key}`]);
|
||||
|
||||
// Add pivot attributes without prefix.
|
||||
const pivotAttributes = _.mapKeys(
|
||||
pivot,
|
||||
(value, key) => `${connection.options.pivot_prefix}${key}`
|
||||
);
|
||||
const pivotAttributes = _.mapKeys(pivot, (value, key) => `${connection.options.pivot_prefix}${key}`);
|
||||
|
||||
return Object.assign({}, attributes, pivotAttributes);
|
||||
}
|
||||
@ -154,7 +122,7 @@ module.exports = function(strapi) {
|
||||
// Initialize the global variable with the
|
||||
// capitalized model name.
|
||||
if (!plugin) {
|
||||
global[globalName] = {};
|
||||
global[definition.globalName] = {};
|
||||
}
|
||||
|
||||
// Call this callback function after we are done parsing
|
||||
@ -222,26 +190,24 @@ module.exports = function(strapi) {
|
||||
)
|
||||
);
|
||||
|
||||
if (!plugin) {
|
||||
global[globalName] = ORM.Model.extend(loadedModel);
|
||||
global[pluralize(globalName)] = ORM.Collection.extend({
|
||||
model: global[globalName]
|
||||
});
|
||||
GLOBALS[definition.globalId] = ORM.Model.extend(loadedModel);
|
||||
|
||||
// Expose ORM functions through the `target` object.
|
||||
target[model] = _.assign(global[globalName], target[model]);
|
||||
} else {
|
||||
target[model] = _.assign(ORM.Model.extend(loadedModel), target[model]);
|
||||
if (!plugin) {
|
||||
// Only expose as real global variable the models which
|
||||
// are not scoped in a plugin.
|
||||
global[definition.globalId] = GLOBALS[definition.globalId];
|
||||
}
|
||||
|
||||
// Expose ORM functions through the `strapi.models[xxx]`
|
||||
// or `strapi.plugins[xxx].models[yyy]` object.
|
||||
target[model] = _.assign(GLOBALS[definition.globalId], target[model]);
|
||||
|
||||
// Push attributes to be aware of model schema.
|
||||
target[model]._attributes = definition.attributes;
|
||||
|
||||
loadedHook();
|
||||
} catch (err) {
|
||||
strapi.log.error(
|
||||
'Impossible to register the `' + model + '` model.'
|
||||
);
|
||||
strapi.log.error('Impossible to register the `' + model + '` model.');
|
||||
strapi.log.error(err);
|
||||
strapi.stop();
|
||||
}
|
||||
@ -261,82 +227,90 @@ module.exports = function(strapi) {
|
||||
|
||||
// Build associations key
|
||||
utilsModels.defineAssociations(
|
||||
globalName,
|
||||
definition.globalName,
|
||||
definition,
|
||||
details,
|
||||
name
|
||||
);
|
||||
|
||||
const globalId = details.plugin ?
|
||||
_.get(strapi.plugins,`${details.plugin}.models.${(details.model || details.collection || '').toLowerCase()}.globalId`):
|
||||
_.get(strapi.models, `${(details.model || details.collection || '').toLowerCase()}.globalId`);
|
||||
|
||||
switch (verbose) {
|
||||
case 'hasOne': {
|
||||
const FK = _.findKey(
|
||||
strapi.models[details.model].attributes,
|
||||
details => {
|
||||
if (
|
||||
details.hasOwnProperty('model') &&
|
||||
details.model === model &&
|
||||
details.hasOwnProperty('via') &&
|
||||
details.via === name
|
||||
) {
|
||||
return details;
|
||||
const FK = details.plugin ?
|
||||
_.findKey(
|
||||
strapi.plugins[details.plugin].models[details.model].attributes,
|
||||
details => {
|
||||
if (
|
||||
details.hasOwnProperty('model') &&
|
||||
details.model === model &&
|
||||
details.hasOwnProperty('via') &&
|
||||
details.via === name
|
||||
) {
|
||||
return details;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
):
|
||||
_.findKey(
|
||||
strapi.models[details.model].attributes,
|
||||
details => {
|
||||
if (
|
||||
details.hasOwnProperty('model') &&
|
||||
details.model === model &&
|
||||
details.hasOwnProperty('via') &&
|
||||
details.via === name
|
||||
) {
|
||||
return details;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const globalId = _.get(
|
||||
strapi.models,
|
||||
`${details.model.toLowerCase()}.globalId`
|
||||
);
|
||||
const columnName = details.plugin ?
|
||||
_.get(strapi.plugins, `${details.plugin}.models.${details.model}.attributes.${FK}.columnName`, FK):
|
||||
_.get(strapi.models, `${details.model}.attributes.${FK}.columnName`, FK);
|
||||
|
||||
loadedModel[name] = function() {
|
||||
return this.hasOne(
|
||||
global[globalId],
|
||||
_.get(
|
||||
strapi.models[details.model].attributes,
|
||||
`${FK}.columnName`
|
||||
) || FK
|
||||
GLOBALS[globalId],
|
||||
columnName
|
||||
);
|
||||
};
|
||||
break;
|
||||
}
|
||||
case 'hasMany': {
|
||||
const globalId = _.get(
|
||||
strapi.models,
|
||||
`${details.collection.toLowerCase()}.globalId`
|
||||
);
|
||||
const FKTarget = _.get(
|
||||
strapi.models[globalId.toLowerCase()].attributes,
|
||||
`${details.via}.columnName`
|
||||
) || details.via;
|
||||
const columnName = details.plugin ?
|
||||
_.get(strapi.plugins, `${details.plugin}.models.${globalId.toLowerCase()}.attributes.${details.via}.columnName`, details.via):
|
||||
_.get(strapi.models[globalId.toLowerCase()].attributes, `${details.via}.columnName`, details.via);
|
||||
|
||||
// Set this info to be able to see if this field is a real database's field.
|
||||
details.isVirtual = true;
|
||||
|
||||
loadedModel[name] = function() {
|
||||
return this.hasMany(global[globalId], FKTarget);
|
||||
return this.hasMany(GLOBALS[globalId], columnName);
|
||||
};
|
||||
break;
|
||||
}
|
||||
case 'belongsTo': {
|
||||
const globalId = _.get(
|
||||
strapi.models,
|
||||
`${details.model.toLowerCase()}.globalId`
|
||||
);
|
||||
|
||||
loadedModel[name] = function() {
|
||||
return this.belongsTo(
|
||||
global[globalId],
|
||||
_.get(details, 'columnName') || name
|
||||
GLOBALS[globalId],
|
||||
_.get(details, 'columnName', name)
|
||||
);
|
||||
};
|
||||
break;
|
||||
}
|
||||
case 'belongsToMany': {
|
||||
const collection = details.plugin ?
|
||||
strapi.plugins[details.plugin].models[details.collection]:
|
||||
strapi.models[details.collection];
|
||||
|
||||
const collectionName = _.get(details, 'collectionName') ||
|
||||
_.map(
|
||||
_.sortBy(
|
||||
[
|
||||
strapi.models[details.collection].attributes[
|
||||
collection.attributes[
|
||||
details.via
|
||||
],
|
||||
details
|
||||
@ -353,7 +327,7 @@ module.exports = function(strapi) {
|
||||
).join('__');
|
||||
|
||||
const relationship = _.clone(
|
||||
strapi.models[details.collection].attributes[details.via]
|
||||
collection.attributes[details.via]
|
||||
);
|
||||
|
||||
// Force singular foreign key
|
||||
@ -378,11 +352,6 @@ module.exports = function(strapi) {
|
||||
relationship.attribute = pluralize.singular(details.via);
|
||||
}
|
||||
|
||||
const globalId = _.get(
|
||||
strapi.models,
|
||||
`${details.collection.toLowerCase()}.globalId`
|
||||
);
|
||||
|
||||
// Set this info to be able to see if this field is a real database's field.
|
||||
details.isVirtual = true;
|
||||
|
||||
@ -392,7 +361,7 @@ module.exports = function(strapi) {
|
||||
!_.isEmpty(details.withPivot)
|
||||
) {
|
||||
return this.belongsToMany(
|
||||
global[globalId],
|
||||
GLOBALS[globalId],
|
||||
collectionName,
|
||||
relationship.attribute + '_' + relationship.column,
|
||||
details.attribute + '_' + details.column
|
||||
@ -400,7 +369,7 @@ module.exports = function(strapi) {
|
||||
}
|
||||
|
||||
return this.belongsToMany(
|
||||
global[globalId],
|
||||
GLOBALS[globalId],
|
||||
collectionName,
|
||||
relationship.attribute + '_' + relationship.column,
|
||||
details.attribute + '_' + details.column
|
||||
@ -418,15 +387,12 @@ module.exports = function(strapi) {
|
||||
});
|
||||
};
|
||||
|
||||
mountModels(models, strapi.models);
|
||||
// Mount `./api` models.
|
||||
mountModels(_.pickBy(strapi.models, { connection: connectionName }), strapi.models);
|
||||
|
||||
// Mount `./plugins` models.
|
||||
_.forEach(strapi.plugins, (plugin, name) => {
|
||||
models = _.pickBy(strapi.plugins[name].models, { connection: connectionName })
|
||||
if (connectionName === strapi.config.currentEnvironment.database.defaultConnection) {
|
||||
_.assign(models, _.pickBy(strapi.plugins[name].models, (model) => model.connection === undefined));
|
||||
}
|
||||
|
||||
mountModels(models, plugin.models, name);
|
||||
mountModels(_.pickBy(strapi.plugins[name].models, { connection: connectionName }), plugin.models, name);
|
||||
});
|
||||
});
|
||||
},
|
||||
@ -549,7 +515,7 @@ module.exports = function(strapi) {
|
||||
.then(response => {
|
||||
const record = response ? response.toJSON() : response;
|
||||
|
||||
if (record && _.isObject(record[details.via])) {
|
||||
if (record && _.isObject(record[details.via]) && record[details.via][current] !== value[current]) {
|
||||
return this.manageRelations(model, {
|
||||
id: record[details.via][models[details.model || details.collection].primaryKey] || record[details.via].id,
|
||||
values: {
|
||||
|
||||
@ -36,7 +36,7 @@ module.exports = {
|
||||
} catch (e) {
|
||||
// Collection undefined try to get the collection based on collectionIdentity
|
||||
if (typeof strapi !== 'undefined') {
|
||||
collection = _.get(strapi.bookshelf.collections, collectionIdentity);
|
||||
collection = _.get(strapi, `bookshelf.collections.${collectionIdentity}`);
|
||||
}
|
||||
|
||||
// Impossible to match collectionIdentity before, try to use idAttribute
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-bookshelf",
|
||||
"version": "3.0.0-alpha.7.2",
|
||||
"version": "3.0.0-alpha.7.3",
|
||||
"description": "Bookshelf hook for the Strapi framework",
|
||||
"homepage": "http://strapi.io",
|
||||
"keywords": [
|
||||
@ -19,8 +19,8 @@
|
||||
"bookshelf": "^0.10.3",
|
||||
"lodash": "^4.17.4",
|
||||
"pluralize": "^6.0.0",
|
||||
"strapi-knex": "3.0.0-alpha.7.2",
|
||||
"strapi-utils": "3.0.0-alpha.7.2"
|
||||
"strapi-knex": "3.0.0-alpha.7.3",
|
||||
"strapi-utils": "3.0.0-alpha.7.3"
|
||||
},
|
||||
"strapi": {
|
||||
"isHook": true,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-ejs",
|
||||
"version": "3.0.0-alpha.7.2",
|
||||
"version": "3.0.0-alpha.7.3",
|
||||
"description": "EJS hook for the Strapi framework",
|
||||
"homepage": "http://strapi.io",
|
||||
"keywords": [
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-generate-admin",
|
||||
"version": "3.0.0-alpha.7.2",
|
||||
"version": "3.0.0-alpha.7.3",
|
||||
"description": "Generate the default admin panel for a Strapi application.",
|
||||
"homepage": "http://strapi.io",
|
||||
"keywords": [
|
||||
@ -15,7 +15,7 @@
|
||||
"dependencies": {
|
||||
"fs-extra": "^4.0.1",
|
||||
"lodash": "^4.17.4",
|
||||
"strapi-admin": "3.0.0-alpha.7.2"
|
||||
"strapi-admin": "3.0.0-alpha.7.3"
|
||||
},
|
||||
"author": {
|
||||
"email": "hi@strapi.io",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-generate-api",
|
||||
"version": "3.0.0-alpha.7.2",
|
||||
"version": "3.0.0-alpha.7.3",
|
||||
"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-alpha.7.2",
|
||||
"version": "3.0.0-alpha.7.3",
|
||||
"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-alpha.7.2",
|
||||
"version": "3.0.0-alpha.7.3",
|
||||
"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-alpha.7.2",
|
||||
"version": "3.0.0-alpha.7.3",
|
||||
"description": "Generate a new Strapi application.",
|
||||
"homepage": "http://strapi.io",
|
||||
"keywords": [
|
||||
@ -17,7 +17,7 @@
|
||||
"fs-extra": "^4.0.0",
|
||||
"get-installed-path": "^3.0.1",
|
||||
"lodash": "^4.17.4",
|
||||
"strapi-utils": "3.0.0-alpha.7.2",
|
||||
"strapi-utils": "3.0.0-alpha.7.3",
|
||||
"uuid": "^3.1.0"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-generate-plugin",
|
||||
"version": "3.0.0-alpha.7.2",
|
||||
"version": "3.0.0-alpha.7.3",
|
||||
"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-alpha.7.2",
|
||||
"version": "3.0.0-alpha.7.3",
|
||||
"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-alpha.7.2",
|
||||
"version": "3.0.0-alpha.7.3",
|
||||
"description": "Generate a service for a Strapi API.",
|
||||
"homepage": "http://strapi.io",
|
||||
"keywords": [
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-generate",
|
||||
"version": "3.0.0-alpha.7.2",
|
||||
"version": "3.0.0-alpha.7.3",
|
||||
"description": "Master of ceremonies for the Strapi generators.",
|
||||
"homepage": "http://strapi.io",
|
||||
"keywords": [
|
||||
@ -17,7 +17,7 @@
|
||||
"fs-extra": "^4.0.0",
|
||||
"lodash": "^4.17.4",
|
||||
"reportback": "^2.0.1",
|
||||
"strapi-utils": "3.0.0-alpha.7.2"
|
||||
"strapi-utils": "3.0.0-alpha.7.3"
|
||||
},
|
||||
"author": {
|
||||
"name": "Strapi team",
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { isEmpty, upperFirst } from 'lodash';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import cn from 'classnames';
|
||||
|
||||
|
||||
@ -26,7 +26,6 @@
|
||||
height: 3rem;
|
||||
position: relative;
|
||||
border-radius: 0.3rem;
|
||||
text-transform: capitalize;
|
||||
margin-right: 1.8rem;
|
||||
cursor: pointer;
|
||||
font-family: Lato;
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
import React from 'react';
|
||||
import moment from 'moment';
|
||||
import PropTypes from 'prop-types';
|
||||
import { get, isEmpty, map, mapKeys, isObject, reject, includes } from 'lodash';
|
||||
import { get, isEmpty, map, mapKeys, isObject, reject, includes, upperFirst } from 'lodash';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import DateTime from 'react-datetime';
|
||||
import DateTimeStyle from 'react-datetime/css/react-datetime.css';
|
||||
@ -159,12 +159,13 @@ class Input extends React.Component { // eslint-disable-line react/prefer-statel
|
||||
return (
|
||||
<div className={`${styles.inputDate} ${styles.input} ${this.props.customBootstrapClass || 'col-md-4'} ${requiredClass}`}>
|
||||
<label htmlFor={this.props.label}>
|
||||
<FormattedMessage id={`${this.props.label}`} defaultMessage={this.props.label} />
|
||||
<FormattedMessage id={`${this.props.label}`} defaultMessage={upperFirst(this.props.label)} />
|
||||
</label>
|
||||
<DateTime
|
||||
value={value}
|
||||
dateFormat='YYYY-MM-DD'
|
||||
timeFormat='HH:mm:ss'
|
||||
tabIndex={this.props.tabIndex}
|
||||
utc={true}
|
||||
inputProps={{
|
||||
placeholder: this.props.placeholder,
|
||||
@ -214,6 +215,7 @@ class Input extends React.Component { // eslint-disable-line react/prefer-statel
|
||||
disabled={this.props.disabled}
|
||||
type="email"
|
||||
autoFocus={this.props.autoFocus}
|
||||
tabIndex={this.props.tabIndex}
|
||||
/>
|
||||
)}
|
||||
</FormattedMessage>
|
||||
@ -244,6 +246,7 @@ class Input extends React.Component { // eslint-disable-line react/prefer-statel
|
||||
autoComplete="off"
|
||||
disabled={this.props.disabled}
|
||||
autoFocus={this.props.autoFocus}
|
||||
tabIndex={this.props.tabIndex}
|
||||
/>
|
||||
)}
|
||||
</FormattedMessage>
|
||||
@ -262,7 +265,7 @@ class Input extends React.Component { // eslint-disable-line react/prefer-statel
|
||||
return (
|
||||
<div className={`${styles.input} ${this.props.customBootstrapClass || 'col-md-6'} ${requiredClass}`}>
|
||||
<label htmlFor={this.props.label}>
|
||||
<FormattedMessage id={`${this.props.label}`} defaultMessage={this.props.label} />
|
||||
<FormattedMessage id={`${this.props.label}`} defaultMessage={upperFirst(this.props.label)} />
|
||||
</label>
|
||||
<FormattedMessage id={this.props.placeholder || this.props.label} values={this.props.labelValues}>
|
||||
{(placeholder) => (
|
||||
@ -278,6 +281,7 @@ class Input extends React.Component { // eslint-disable-line react/prefer-statel
|
||||
disabled={this.props.disabled}
|
||||
type={type}
|
||||
autoFocus={this.props.autoFocus}
|
||||
tabIndex={this.props.tabIndex}
|
||||
/>
|
||||
)}
|
||||
</FormattedMessage>
|
||||
@ -348,7 +352,7 @@ class Input extends React.Component { // eslint-disable-line react/prefer-statel
|
||||
return (
|
||||
<div className={`${styles.input} ${this.props.customBootstrapClass || 'col-md-6'} ${requiredClass}`}>
|
||||
<label htmlFor={this.props.label}>
|
||||
<FormattedMessage id={`${this.props.label}`} defaultMessage={this.props.label} />
|
||||
<FormattedMessage id={`${this.props.label}`} defaultMessage={upperFirst(this.props.label)} />
|
||||
</label>
|
||||
<div className={`input-group ${styles.inputSearch}`} style={{ marginBottom: '1rem'}}>
|
||||
<span className={`input-group-addon ${styles.addonSearch}`} />
|
||||
@ -366,6 +370,7 @@ class Input extends React.Component { // eslint-disable-line react/prefer-statel
|
||||
disabled={this.props.disabled}
|
||||
type="text"
|
||||
autoFocus={this.props.autoFocus}
|
||||
tabIndex={this.props.tabIndex}
|
||||
/>
|
||||
)}
|
||||
</FormattedMessage>
|
||||
@ -391,7 +396,7 @@ class Input extends React.Component { // eslint-disable-line react/prefer-statel
|
||||
return (
|
||||
<div className={`${styles.inputTextArea} ${this.props.customBootstrapClass || 'col-md-6'} ${requiredClass}`}>
|
||||
<label htmlFor={this.props.label}>
|
||||
<FormattedMessage id={`${this.props.label}`} defaultMessage={this.props.label} />
|
||||
<FormattedMessage id={`${this.props.label}`} defaultMessage={upperFirst(this.props.label)} />
|
||||
</label>
|
||||
<FormattedMessage id={this.props.placeholder || this.props.label}>
|
||||
{(placeholder) => (
|
||||
@ -406,6 +411,7 @@ class Input extends React.Component { // eslint-disable-line react/prefer-statel
|
||||
placeholder={placeholder}
|
||||
disabled={this.props.disabled}
|
||||
autoFocus={this.props.autoFocus}
|
||||
tabIndex={this.props.tabIndex}
|
||||
/>
|
||||
)}
|
||||
</FormattedMessage>
|
||||
@ -448,7 +454,7 @@ class Input extends React.Component { // eslint-disable-line react/prefer-statel
|
||||
const handleBlur = this.props.onBlur || this.handleBlur;
|
||||
const placeholder = this.props.placeholder || this.props.label;
|
||||
const label = this.props.label ?
|
||||
<label htmlFor={this.props.label}><FormattedMessage id={`${this.props.label}`} defaultMessage={this.props.label} /></label>
|
||||
<label htmlFor={this.props.label}><FormattedMessage id={`${this.props.label}`} defaultMessage={upperFirst(this.props.label)} /></label>
|
||||
: <label htmlFor={this.props.label} />;
|
||||
|
||||
const requiredClass = get(this.props.validations, 'required') && this.props.addRequiredInputDesign ?
|
||||
@ -467,6 +473,7 @@ class Input extends React.Component { // eslint-disable-line react/prefer-statel
|
||||
placeholder={placeholder}
|
||||
disabled={this.props.disabled}
|
||||
autoFocus={this.props.autoFocus}
|
||||
tabIndex={this.props.tabIndex}
|
||||
/>;
|
||||
|
||||
const link = !isEmpty(this.props.linkContent) ? <a href={this.props.linkContent.link} target="_blank"><FormattedMessage id={this.props.linkContent.description} /></a> : '';
|
||||
@ -616,6 +623,7 @@ Input.propTypes = {
|
||||
search: PropTypes.bool,
|
||||
selectOptions: PropTypes.array,
|
||||
selectOptionsFetchSucceeded: PropTypes.bool,
|
||||
tabIndex: PropTypes.string,
|
||||
title: PropTypes.string,
|
||||
type: PropTypes.string.isRequired,
|
||||
validations: PropTypes.object.isRequired,
|
||||
@ -644,6 +652,7 @@ Input.defaultProps = {
|
||||
search: false,
|
||||
selectOptions: [],
|
||||
selectOptionsFetchSucceeded: false,
|
||||
tabIndex: '0',
|
||||
value: ''
|
||||
};
|
||||
|
||||
|
||||
@ -131,7 +131,6 @@
|
||||
label {
|
||||
margin-bottom: 0;
|
||||
font-weight: 500;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
input {
|
||||
@ -229,7 +228,6 @@
|
||||
> label {
|
||||
margin-bottom: 0;
|
||||
font-weight: 500;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
> textarea {
|
||||
height: 10.6rem;
|
||||
@ -282,7 +280,6 @@
|
||||
label {
|
||||
margin-bottom: 0;
|
||||
font-weight: 500;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
input {
|
||||
@ -359,7 +356,6 @@
|
||||
.toggleLabel {
|
||||
margin-bottom: 0;
|
||||
font-weight: 500;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.gradientOff {
|
||||
|
||||
@ -93,7 +93,7 @@
|
||||
.secondary {
|
||||
// height: 32px !important;
|
||||
color: #F64D0A !important;
|
||||
border: 0.1rem solid #F64D0A;
|
||||
border: 0.1rem solid #F64D0A !important;
|
||||
position: relative;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-helper-plugin",
|
||||
"version": "3.0.0-alpha.7.2",
|
||||
"version": "3.0.0-alpha.7.3",
|
||||
"description": "Helper for Strapi plugins development",
|
||||
"engines": {
|
||||
"node": ">= 8.0.0",
|
||||
|
||||
@ -95,6 +95,9 @@ module.exports = strapi => {
|
||||
charset: _.get(connection.settings, 'charset'),
|
||||
schema: _.get(connection.settings, 'schema') || 'public',
|
||||
port: _.get(connection.settings, 'port'),
|
||||
socket: _.get(connection.settings, 'socketPath'),
|
||||
ssl: _.get(connection.settings, 'ssl') || false
|
||||
|
||||
},
|
||||
debug: _.get(connection.options, 'debug') || false,
|
||||
acquireConnectionTimeout: _.get(connection.options, 'acquireConnectionTimeout'),
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-knex",
|
||||
"version": "3.0.0-alpha.7.2",
|
||||
"version": "3.0.0-alpha.7.3",
|
||||
"description": "Knex hook for the Strapi framework",
|
||||
"homepage": "http://strapi.io",
|
||||
"keywords": [
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-middleware-views",
|
||||
"version": "3.0.0-alpha.7.2",
|
||||
"version": "3.0.0-alpha.7.3",
|
||||
"description": "Views hook to enable server-side rendering for the Strapi framework",
|
||||
"homepage": "http://strapi.io",
|
||||
"keywords": [
|
||||
|
||||
@ -38,10 +38,9 @@ module.exports = function (strapi) {
|
||||
*/
|
||||
|
||||
initialize: cb => {
|
||||
let globalName;
|
||||
_.forEach(_.pickBy(strapi.config.connections, {connector: 'strapi-mongoose'}), (connection, connectionName) => {
|
||||
const instance = new Mongoose();
|
||||
const {host, port, username, password, database} = _.defaults(connection.settings, strapi.config.hook.settings.mongoose);
|
||||
const { host, port, username, password, database } = _.defaults(connection.settings, strapi.config.hook.settings.mongoose);
|
||||
|
||||
// Connect to mongo database
|
||||
if (_.isEmpty(username) || _.isEmpty(password)) {
|
||||
@ -65,12 +64,6 @@ module.exports = function (strapi) {
|
||||
|
||||
// Handle success
|
||||
instance.connection.on('open', () => {
|
||||
// Select models concerned by this connection
|
||||
let models = _.pickBy(strapi.models, { connection: connectionName });
|
||||
if (connectionName === strapi.config.currentEnvironment.database.defaultConnection) {
|
||||
_.assign(models, _.pickBy(strapi.models, (model) => model.connection === undefined));
|
||||
}
|
||||
|
||||
const mountModels = (models, target, plugin = false) => {
|
||||
if (!target) return;
|
||||
|
||||
@ -141,11 +134,10 @@ module.exports = function (strapi) {
|
||||
virtuals: true
|
||||
});
|
||||
|
||||
|
||||
if (!plugin) {
|
||||
global[definition.globalName] = instance.model(definition.globalName, collection.schema);
|
||||
global[definition.globalName] = instance.model(definition.globalName, collection.schema, definition.collectionName);
|
||||
} else {
|
||||
instance.model(definition.globalName, collection.schema);
|
||||
instance.model(definition.globalName, collection.schema, definition.collectionName);
|
||||
}
|
||||
|
||||
// Expose ORM functions through the `target` object.
|
||||
@ -166,10 +158,6 @@ module.exports = function (strapi) {
|
||||
|
||||
// Parse every registered model.
|
||||
_.forEach(models, (definition, model) => {
|
||||
if (plugin) {
|
||||
definition.globalId = _.upperFirst(_.camelCase(_.get(strapi.config.hook.settings.mongoose.collections, mongooseUtils.toCollectionName(model)) ? `${plugin}-${model}` : model));
|
||||
}
|
||||
|
||||
definition.globalName = _.upperFirst(_.camelCase(definition.globalId));
|
||||
|
||||
// Make sure the model has a connection.
|
||||
@ -227,22 +215,24 @@ module.exports = function (strapi) {
|
||||
definition.loadedModel[name].type = utils(instance).convertType(details.type);
|
||||
}
|
||||
|
||||
let FK;
|
||||
|
||||
switch (verbose) {
|
||||
case 'hasOne':
|
||||
case 'hasOne': {
|
||||
const ref = details.plugin ? strapi.plugins[details.plugin].models[details.model].globalId : strapi.models[details.model].globalId;
|
||||
|
||||
definition.loadedModel[name] = {
|
||||
type: instance.Schema.Types.ObjectId,
|
||||
ref: _.capitalize(details.model)
|
||||
ref
|
||||
};
|
||||
break;
|
||||
case 'hasMany':
|
||||
FK = _.find(definition.associations, {alias: name});
|
||||
}
|
||||
case 'hasMany': {
|
||||
const FK = _.find(definition.associations, {alias: name});
|
||||
const ref = details.plugin ? strapi.plugins[details.plugin].models[details.collection].globalId : strapi.models[details.collection].globalId;
|
||||
|
||||
if (FK) {
|
||||
definition.loadedModel[name] = {
|
||||
type: 'virtual',
|
||||
ref: _.capitalize(details.collection),
|
||||
ref,
|
||||
via: FK.via,
|
||||
justOne: false
|
||||
};
|
||||
@ -252,17 +242,19 @@ module.exports = function (strapi) {
|
||||
} else {
|
||||
definition.loadedModel[name] = [{
|
||||
type: instance.Schema.Types.ObjectId,
|
||||
ref: _.capitalize(details.collection)
|
||||
ref
|
||||
}];
|
||||
}
|
||||
break;
|
||||
case 'belongsTo':
|
||||
FK = _.find(definition.associations, {alias: name});
|
||||
}
|
||||
case 'belongsTo': {
|
||||
const FK = _.find(definition.associations, {alias: name});
|
||||
const ref = details.plugin ? strapi.plugins[details.plugin].models[details.model].globalId : strapi.models[details.model].globalId;
|
||||
|
||||
if (FK && FK.nature !== 'oneToOne' && FK.nature !== 'manyToOne') {
|
||||
definition.loadedModel[name] = {
|
||||
type: 'virtual',
|
||||
ref: _.capitalize(details.model),
|
||||
ref,
|
||||
via: FK.via,
|
||||
justOne: true
|
||||
};
|
||||
@ -272,19 +264,21 @@ module.exports = function (strapi) {
|
||||
} else {
|
||||
definition.loadedModel[name] = {
|
||||
type: instance.Schema.Types.ObjectId,
|
||||
ref: _.capitalize(details.model)
|
||||
ref
|
||||
};
|
||||
}
|
||||
|
||||
break;
|
||||
case 'belongsToMany':
|
||||
FK = _.find(definition.associations, {alias: name});
|
||||
}
|
||||
case 'belongsToMany': {
|
||||
const FK = _.find(definition.associations, {alias: name});
|
||||
const ref = details.plugin ? strapi.plugins[details.plugin].models[details.collection].globalId : strapi.models[details.collection].globalId;
|
||||
|
||||
// One-side of the relationship has to be a virtual field to be bidirectional.
|
||||
if ((FK && _.isUndefined(FK.via)) || details.dominant !== true) {
|
||||
definition.loadedModel[name] = {
|
||||
type: 'virtual',
|
||||
ref: _.capitalize(FK.collection),
|
||||
ref,
|
||||
via: FK.via
|
||||
};
|
||||
|
||||
@ -293,10 +287,11 @@ module.exports = function (strapi) {
|
||||
} else {
|
||||
definition.loadedModel[name] = [{
|
||||
type: instance.Schema.Types.ObjectId,
|
||||
ref: _.capitalize(details.collection)
|
||||
ref
|
||||
}];
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -306,15 +301,12 @@ module.exports = function (strapi) {
|
||||
});
|
||||
};
|
||||
|
||||
mountModels(models, strapi.models);
|
||||
// Mount `./api` models.
|
||||
mountModels(_.pickBy(strapi.models, { connection: connectionName }), strapi.models);
|
||||
|
||||
// Mount `./plugins` models.
|
||||
_.forEach(strapi.plugins, (plugin, name) => {
|
||||
models = _.pickBy(strapi.plugins[name].models, { connection: connectionName })
|
||||
if (connectionName === strapi.config.currentEnvironment.database.defaultConnection) {
|
||||
_.assign(models, _.pickBy(strapi.plugins[name].models, (model) => model.connection === undefined));
|
||||
}
|
||||
|
||||
mountModels(models, plugin.models, name);
|
||||
mountModels(_.pickBy(strapi.plugins[name].models, { connection: connectionName }), plugin.models, name);
|
||||
});
|
||||
|
||||
cb();
|
||||
@ -418,7 +410,7 @@ module.exports = function (strapi) {
|
||||
.findOne({ id : recordId })
|
||||
.populate(_.keys(_.groupBy(_.reject(models[details.model || details.collection].associations, {autoPopulate: false}), 'alias')).join(' '))
|
||||
.then(record => {
|
||||
if (record && _.isObject(record[details.via])) {
|
||||
if (record && _.isObject(record[details.via]) && record[details.via][current] !== value[current]) {
|
||||
return this.manageRelations(details.model || details.collection, {
|
||||
id: record[details.via][Model.primaryKey] || record[details.via].id,
|
||||
values: {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-mongoose",
|
||||
"version": "3.0.0-alpha.7.2",
|
||||
"version": "3.0.0-alpha.7.3",
|
||||
"description": "Mongoose hook for the Strapi framework",
|
||||
"homepage": "http://strapi.io",
|
||||
"keywords": [
|
||||
@ -19,7 +19,7 @@
|
||||
"mongoose": "^4.11.10",
|
||||
"mongoose-float": "^1.0.2",
|
||||
"pluralize": "^6.0.0",
|
||||
"strapi-utils": "3.0.0-alpha.7.2"
|
||||
"strapi-utils": "3.0.0-alpha.7.3"
|
||||
},
|
||||
"strapi": {
|
||||
"isHook": true
|
||||
|
||||
@ -50,14 +50,14 @@ class EditForm extends React.Component {
|
||||
|
||||
render() {
|
||||
const source = getQueryParameters(this.props.location.search, 'source');
|
||||
const currentSchema = get(this.props.schema, [this.props.currentModelName]) || get(this.props.schema, ['plugins', source, this.props.currentModelName]);
|
||||
const currentSchema = source !== 'content-manager' ? get(this.props.schema, ['plugins', source, this.props.currentModelName]) : get(this.props.schema, [this.props.currentModelName]);
|
||||
const currentLayout = get(this.props.layout, [this.props.currentModelName, 'attributes']);
|
||||
|
||||
// Remove `id` field
|
||||
const displayedFields = merge(get(currentLayout), omit(currentSchema.fields, 'id'));
|
||||
|
||||
// List fields inputs
|
||||
const fields = Object.keys(displayedFields).map(attr => {
|
||||
const fields = Object.keys(displayedFields).map((attr, key) => {
|
||||
const details = displayedFields[attr];
|
||||
const errorIndex = findIndex(this.props.formErrors, ['name', attr]);
|
||||
const errors = errorIndex !== -1 ? this.props.formErrors[errorIndex].errors : [];
|
||||
@ -74,6 +74,7 @@ class EditForm extends React.Component {
|
||||
|
||||
return (
|
||||
<Input
|
||||
autoFocus={key === 0}
|
||||
key={attr}
|
||||
type={get(layout, 'type', this.getInputType(details.type))}
|
||||
label={get(layout, 'label') || details.label || ''}
|
||||
|
||||
@ -20,14 +20,17 @@ import styles from './styles.scss';
|
||||
|
||||
class EditFormRelations extends React.Component { // eslint-disable-line react/prefer-stateless-function
|
||||
componentDidMount() {
|
||||
if (size(get(this.props.schema, [this.props.currentModelName, 'relations'])) === 0 && !this.props.isNull) {
|
||||
const source = getQueryParameters(this.props.location.search, 'source');
|
||||
const currentSchema = source !== 'content-manager' ? get(this.props.schema, ['plugins', source, this.props.currentModelName]) : get(this.props.schema, [this.props.currentModelName]);
|
||||
|
||||
if (size(get(currentSchema, ['relations'])) === 0 && !this.props.isNull) {
|
||||
this.props.toggleNull();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const source = getQueryParameters(this.props.location.search, 'source');
|
||||
const currentSchema = get(this.props.schema, [this.props.currentModelName]) || get(this.props.schema, ['plugins', source, this.props.currentModelName]);
|
||||
const currentSchema = source !== 'content-manager' ? get(this.props.schema, ['plugins', source, this.props.currentModelName]) : get(this.props.schema, [this.props.currentModelName]);
|
||||
|
||||
const relations = map(currentSchema.relations, (relation, i) => {
|
||||
|
||||
@ -43,6 +46,7 @@ class EditFormRelations extends React.Component { // eslint-disable-line react/p
|
||||
relation={relation}
|
||||
schema={this.props.schema}
|
||||
setRecordAttribute={this.props.setRecordAttribute}
|
||||
location={this.props.location}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -57,6 +61,7 @@ class EditFormRelations extends React.Component { // eslint-disable-line react/p
|
||||
relation={relation}
|
||||
schema={this.props.schema}
|
||||
setRecordAttribute={this.props.setRecordAttribute}
|
||||
location={this.props.location}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
|
||||
@ -27,6 +27,7 @@ class SelectMany extends React.Component { // eslint-disable-line react/prefer-s
|
||||
getOptions = (query) => {
|
||||
const params = {
|
||||
limit: 20,
|
||||
source: this.props.relation.plugin || 'content-manager',
|
||||
};
|
||||
|
||||
// Set `query` parameter if necessary
|
||||
|
||||
@ -8,7 +8,7 @@ import React from 'react';
|
||||
import Select from 'react-select';
|
||||
import PropTypes from 'prop-types';
|
||||
import 'react-select/dist/react-select.css';
|
||||
import { map, isArray, isNull, isUndefined } from 'lodash';
|
||||
import { map, isArray, isNull, isUndefined, isFunction, get } from 'lodash';
|
||||
|
||||
import request from 'utils/request';
|
||||
import templateObject from 'utils/templateObject';
|
||||
@ -27,6 +27,7 @@ class SelectOne extends React.Component { // eslint-disable-line react/prefer-st
|
||||
getOptions = (query) => {
|
||||
const params = {
|
||||
limit: 20,
|
||||
source: this.props.relation.plugin || 'content-manager',
|
||||
};
|
||||
|
||||
// Set `query` parameter if necessary
|
||||
@ -83,8 +84,8 @@ class SelectOne extends React.Component { // eslint-disable-line react/prefer-st
|
||||
loadOptions={this.getOptions}
|
||||
simpleValue
|
||||
value={isNull(value) || isUndefined(value) ? null : {
|
||||
value: value.toJS(),
|
||||
label: templateObject({ mainField: this.props.relation.displayedAttribute }, value.toJS()).mainField || value.toJS().id,
|
||||
value: isFunction(value.toJS) ? value.toJS() : value,
|
||||
label: templateObject({ mainField: this.props.relation.displayedAttribute }, isFunction(value.toJS) ? value.toJS() : value).mainField || (isFunction(value.toJS) ? get(value.toJS(), 'id') : get(value, 'id')),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -28,12 +28,12 @@ class TableRow extends React.Component {
|
||||
* @param value {*} Value stored in database
|
||||
* @returns {*}
|
||||
*/
|
||||
getDisplayedValue(type, value) {
|
||||
getDisplayedValue(type, value, name) {
|
||||
switch (type.toLowerCase()) {
|
||||
case 'string':
|
||||
case 'text':
|
||||
case 'email':
|
||||
return value && !isEmpty(value.toString()) ? value.toString() : '-';
|
||||
return (value && !isEmpty(value.toString())) || name === 'id' ? value.toString() : '-';
|
||||
case 'float':
|
||||
case 'integer':
|
||||
case 'biginteger':
|
||||
@ -71,7 +71,8 @@ class TableRow extends React.Component {
|
||||
<div className={styles.truncated}>
|
||||
{this.getDisplayedValue(
|
||||
header.type,
|
||||
this.props.record[header.name]
|
||||
this.props.record[header.name],
|
||||
header.name,
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -153,7 +153,7 @@ export class Edit extends React.Component {
|
||||
}
|
||||
|
||||
handleChange = (e) => {
|
||||
const currentSchema = get(this.props.schema, [this.props.currentModelName]) || get(this.props.schema, ['plugins', this.source, this.props.currentModelName]);
|
||||
const currentSchema = this.source !== 'content-manager' ? get(this.props.schema, ['plugins', this.source, this.props.currentModelName]) : get(this.props.schema, [this.props.currentModelName]);
|
||||
|
||||
let formattedValue = e.target.value;
|
||||
|
||||
|
||||
@ -75,6 +75,8 @@ export class List extends React.Component {
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
this.source = getQueryParameters(nextProps.location.search, 'source');
|
||||
|
||||
const locationChanged = nextProps.location.pathname !== this.props.location.pathname;
|
||||
|
||||
if (locationChanged) {
|
||||
|
||||
@ -11,9 +11,9 @@ module.exports = {
|
||||
qb.orderBy(params.sort);
|
||||
}
|
||||
|
||||
qb.offset(params.skip);
|
||||
qb.offset(_.toNumber(params.skip));
|
||||
|
||||
qb.limit(params.limit);
|
||||
qb.limit(_.toNumber(params.limit));
|
||||
}).fetchAll({
|
||||
withRelated: this.associations.map(x => x.alias)
|
||||
});
|
||||
@ -80,12 +80,11 @@ module.exports = {
|
||||
case 'oneToOne':
|
||||
if (response[current] !== params.values[current]) {
|
||||
const value = _.isNull(params.values[current]) ? response[current] : params.values;
|
||||
|
||||
const recordId = _.isNull(params.values[current]) ? value[this.primaryKey] || value.id || value._id : value[current];
|
||||
|
||||
if (response[current] && _.isObject(response[current]) && response[current][this.primaryKey] !== value[current]) {
|
||||
virtualFields.push(
|
||||
strapi.query(details.collection || details.model).update({
|
||||
strapi.query(details.collection || details.model, details.plugin).update({
|
||||
id: response[current][this.primaryKey],
|
||||
values: {
|
||||
[details.via]: null
|
||||
@ -97,9 +96,9 @@ module.exports = {
|
||||
|
||||
// Remove previous relationship asynchronously if it exists.
|
||||
virtualFields.push(
|
||||
strapi.query(details.model || details.collection).findOne({ id : recordId })
|
||||
strapi.query(details.model || details.collection, details.plugin).findOne({ id : recordId })
|
||||
.then(record => {
|
||||
if (record && _.isObject(record[details.via])) {
|
||||
if (record && _.isObject(record[details.via]) && record[details.via][current] !== value[current]) {
|
||||
return module.exports.update.call(this, {
|
||||
id: record[details.via][this.primaryKey] || record[details.via].id,
|
||||
values: {
|
||||
@ -115,7 +114,7 @@ module.exports = {
|
||||
|
||||
// Update the record on the other side.
|
||||
// When params.values[current] is null this means that we are removing the relation.
|
||||
virtualFields.push(strapi.query(details.model || details.collection).update({
|
||||
virtualFields.push(strapi.query(details.model || details.collection, details.plugin).update({
|
||||
id: recordId,
|
||||
values: {
|
||||
[details.via]: _.isNull(params.values[current]) ? null : value[this.primaryKey] || value.id || value._id
|
||||
@ -147,7 +146,7 @@ module.exports = {
|
||||
toAdd.forEach(value => {
|
||||
value[details.via] = params.values[this.primaryKey] || params[this.primaryKey];
|
||||
|
||||
virtualFields.push(strapi.query(details.model || details.collection).addRelation({
|
||||
virtualFields.push(strapi.query(details.model || details.collection, details.plugin).addRelation({
|
||||
id: value[this.primaryKey] || value.id || value._id,
|
||||
values: association.nature === 'manyToMany' ? params.values : value,
|
||||
foreignKey: current
|
||||
@ -157,7 +156,7 @@ module.exports = {
|
||||
toRemove.forEach(value => {
|
||||
value[details.via] = null;
|
||||
|
||||
virtualFields.push(strapi.query(details.model || details.collection).removeRelation({
|
||||
virtualFields.push(strapi.query(details.model || details.collection, details.plugin).removeRelation({
|
||||
id: value[this.primaryKey] || value.id || value._id,
|
||||
values: association.nature === 'manyToMany' ? params.values : value,
|
||||
foreignKey: current
|
||||
@ -212,6 +211,7 @@ module.exports = {
|
||||
switch (association.nature) {
|
||||
case 'oneToOne':
|
||||
case 'oneToMany':
|
||||
case 'manyToOne':
|
||||
return module.exports.update.call(this, params);
|
||||
case 'manyToMany':
|
||||
return this.forge({
|
||||
@ -234,6 +234,7 @@ module.exports = {
|
||||
switch (association.nature) {
|
||||
case 'oneToOne':
|
||||
case 'oneToMany':
|
||||
case 'manyToOne':
|
||||
return module.exports.update.call(this, params);
|
||||
case 'manyToMany':
|
||||
return this.forge({
|
||||
|
||||
@ -66,7 +66,7 @@ module.exports = {
|
||||
|
||||
if (response[current] && _.isObject(response[current]) && response[current][this.primaryKey] !== value[current]) {
|
||||
virtualFields.push(
|
||||
strapi.query(details.collection || details.model).update({
|
||||
strapi.query(details.collection || details.model, details.plugin).update({
|
||||
id: response[current][this.primaryKey],
|
||||
values: {
|
||||
[details.via]: null
|
||||
@ -78,7 +78,7 @@ module.exports = {
|
||||
|
||||
// Remove previous relationship asynchronously if it exists.
|
||||
virtualFields.push(
|
||||
strapi.query(details.model || details.collection).findOne({ id : recordId })
|
||||
strapi.query(details.model || details.collection, details.plugin).findOne({ id : recordId })
|
||||
.then(record => {
|
||||
if (record && _.isObject(record[details.via])) {
|
||||
return module.exports.update.call(this, {
|
||||
@ -96,7 +96,7 @@ module.exports = {
|
||||
|
||||
// Update the record on the other side.
|
||||
// When params.values[current] is null this means that we are removing the relation.
|
||||
virtualFields.push(strapi.query(details.model || details.collection).update({
|
||||
virtualFields.push(strapi.query(details.model || details.collection, details.plugin).update({
|
||||
id: recordId,
|
||||
values: {
|
||||
[details.via]: _.isNull(params.values[current]) ? null : value[this.primaryKey] || value.id || value._id
|
||||
@ -127,14 +127,16 @@ module.exports = {
|
||||
// Push the work into the flow process.
|
||||
toAdd.forEach(value => {
|
||||
if (association.nature === 'manyToMany' && !_.isArray(params.values[this.primaryKey] || params[this.primaryKey])) {
|
||||
value[details.via] = (value[details.via] || []).concat([(params.values[this.primaryKey] || params[this.primaryKey])]).filter(x => {
|
||||
return x !== null && x !== undefined;
|
||||
});
|
||||
value[details.via] = (value[details.via] || [])
|
||||
.concat([(params.values[this.primaryKey] || params[this.primaryKey])])
|
||||
.filter(x => {
|
||||
return x !== null && x !== undefined;
|
||||
});
|
||||
} else {
|
||||
value[details.via] = params[this.primaryKey] || params.id;
|
||||
}
|
||||
|
||||
virtualFields.push(strapi.query(details.model || details.collection).addRelation({
|
||||
virtualFields.push(strapi.query(details.model || details.collection, details.plugin).addRelation({
|
||||
id: value[this.primaryKey] || value.id || value._id,
|
||||
values: value,
|
||||
foreignKey: current
|
||||
@ -148,7 +150,7 @@ module.exports = {
|
||||
value[details.via] = null;
|
||||
}
|
||||
|
||||
virtualFields.push(strapi.query(details.model || details.collection).removeRelation({
|
||||
virtualFields.push(strapi.query(details.model || details.collection, details.plugin).removeRelation({
|
||||
id: value[this.primaryKey] || value.id || value._id,
|
||||
values: value,
|
||||
foreignKey: current
|
||||
|
||||
@ -35,25 +35,14 @@ module.exports = {
|
||||
},
|
||||
|
||||
find: async ctx => {
|
||||
const { limit, skip = 0, sort, query, queryAttribute, source, page } = ctx.request.query;
|
||||
|
||||
// Find entries using `queries` system
|
||||
const entries = await strapi.query(ctx.params.model, source).find({
|
||||
limit,
|
||||
skip,
|
||||
sort,
|
||||
query,
|
||||
queryAttribute
|
||||
});
|
||||
|
||||
ctx.body = entries;
|
||||
ctx.body = await strapi.plugins['content-manager'].services['contentmanager'].fetchAll(ctx.params, ctx.request.query);
|
||||
},
|
||||
|
||||
count: async ctx => {
|
||||
const { source } = ctx.request.query;
|
||||
|
||||
// Count using `queries` system
|
||||
const count = await strapi.query(ctx.params.model, source).count();
|
||||
const count = await strapi.plugins['content-manager'].services['contentmanager'].count(ctx.params, source);
|
||||
|
||||
ctx.body = {
|
||||
count: _.isNumber(count) ? count : _.toNumber(count)
|
||||
@ -64,9 +53,7 @@ module.exports = {
|
||||
const { source } = ctx.request.query;
|
||||
|
||||
// Find an entry using `queries` system
|
||||
const entry = await strapi.query(ctx.params.model, source).findOne({
|
||||
id: ctx.params.id
|
||||
});
|
||||
const entry = await strapi.plugins['content-manager'].services['contentmanager'].fetch(ctx.params, source);
|
||||
|
||||
// Entry not found
|
||||
if (!entry) {
|
||||
@ -81,12 +68,9 @@ module.exports = {
|
||||
|
||||
try {
|
||||
// Create an entry using `queries` system
|
||||
const entryCreated = await strapi.query(ctx.params.model, source).create({
|
||||
values: ctx.request.body
|
||||
});
|
||||
|
||||
ctx.body = entryCreated;
|
||||
ctx.body = await strapi.plugins['content-manager'].services['contentmanager'].add(ctx.params, ctx.request.body, source);
|
||||
} catch(error) {
|
||||
console.log(error);
|
||||
ctx.badRequest(null, ctx.request.admin ? [{ messages: [{ id: error.message, field: error.field }] }] : error.message);
|
||||
}
|
||||
},
|
||||
@ -95,14 +79,8 @@ module.exports = {
|
||||
const { source } = ctx.request.query;
|
||||
|
||||
try {
|
||||
// Add current model to the flow of updates.
|
||||
const entry = strapi.query(ctx.params.model, source).update({
|
||||
id: ctx.params.id,
|
||||
values: ctx.request.body
|
||||
});
|
||||
|
||||
// Return the last one which is the current model.
|
||||
ctx.body = entry;
|
||||
ctx.body = await strapi.plugins['content-manager'].services['contentmanager'].edit(ctx.params, ctx.request.body, source);
|
||||
} catch(error) {
|
||||
// TODO handle error update
|
||||
ctx.badRequest(null, ctx.request.admin ? [{ messages: [{ id: error.message, field: error.field }] }] : error.message);
|
||||
@ -110,34 +88,6 @@ module.exports = {
|
||||
},
|
||||
|
||||
delete: async ctx => {
|
||||
const { source } = ctx.request.query;
|
||||
const params = ctx.params;
|
||||
|
||||
const response = await strapi.query(params.model, source).findOne({
|
||||
id: params.id
|
||||
});
|
||||
|
||||
params.values = Object.keys(JSON.parse(JSON.stringify(response))).reduce((acc, current) => {
|
||||
const association = (strapi.models[params.model] || strapi.plugins[source].models[params.model]).associations.filter(x => x.alias === current)[0];
|
||||
|
||||
// Remove relationships.
|
||||
if (association) {
|
||||
acc[current] = _.isArray(response[current]) ? [] : null;
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
if (!_.isEmpty(params.values)) {
|
||||
// Run update to remove all relationships.
|
||||
await strapi.query(params.model, source).update(params);
|
||||
}
|
||||
|
||||
// Delete an entry using `queries` system
|
||||
const entryDeleted = await strapi.query(params.model, source).delete({
|
||||
id: params.id
|
||||
});
|
||||
|
||||
ctx.body = entryDeleted;
|
||||
ctx.body = await strapi.plugins['content-manager'].services['contentmanager'].delete(ctx.params, ctx.request.query);
|
||||
},
|
||||
};
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-plugin-content-manager",
|
||||
"version": "3.0.0-alpha.7.2",
|
||||
"version": "3.0.0-alpha.7.3",
|
||||
"description": "A powerful UI to easily manage your data.",
|
||||
"engines": {
|
||||
"node": ">= 8.0.0",
|
||||
@ -46,6 +46,6 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"react-select": "^1.0.0-rc.5",
|
||||
"strapi-helper-plugin": "3.0.0-alpha.7.2"
|
||||
"strapi-helper-plugin": "3.0.0-alpha.7.3"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,73 @@
|
||||
'use strict';
|
||||
|
||||
const _ = require('lodash');
|
||||
|
||||
/**
|
||||
* A set of functions called "actions" for `ContentManager`
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
fetchAll: async (params, query) => {
|
||||
const { limit, skip = 0, sort, query : request, queryAttribute, source, page } = query;
|
||||
|
||||
// Find entries using `queries` system
|
||||
return await strapi.query(params.model, source).find({
|
||||
limit,
|
||||
skip,
|
||||
sort,
|
||||
query: request,
|
||||
queryAttribute
|
||||
});
|
||||
},
|
||||
|
||||
count: async (params, source) => {
|
||||
return await strapi.query(params.model, source).count();
|
||||
},
|
||||
|
||||
fetch: async (params, source) => {
|
||||
return await strapi.query(params.model, source).findOne({
|
||||
id: params.id
|
||||
});
|
||||
},
|
||||
|
||||
add: async (params, values, source) => {
|
||||
// Create an entry using `queries` system
|
||||
return await strapi.query(params.model, source).create({
|
||||
values
|
||||
});
|
||||
},
|
||||
|
||||
edit: async (params, values, source) => {
|
||||
return strapi.query(params.model, source).update({
|
||||
id: params.id,
|
||||
values
|
||||
});
|
||||
},
|
||||
|
||||
delete: async (params, { source }) => {
|
||||
const response = await strapi.query(params.model, source).findOne({
|
||||
id: params.id
|
||||
});
|
||||
|
||||
params.values = Object.keys(JSON.parse(JSON.stringify(response))).reduce((acc, current) => {
|
||||
const association = (strapi.models[params.model] || strapi.plugins[source].models[params.model]).associations.filter(x => x.alias === current)[0];
|
||||
|
||||
// Remove relationships.
|
||||
if (association) {
|
||||
acc[current] = _.isArray(response[current]) ? [] : null;
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
if (!_.isEmpty(params.values)) {
|
||||
// Run update to remove all relationships.
|
||||
await strapi.query(params.model, source).update(params);
|
||||
}
|
||||
|
||||
// Delete an entry using `queries` system
|
||||
return await strapi.query(params.model, source).delete({
|
||||
id: params.id
|
||||
});
|
||||
},
|
||||
};
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 879 B |
Binary file not shown.
|
After Width: | Height: | Size: 356 B |
@ -10,8 +10,10 @@ import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import IcoBoolean from '../../assets/images/icon_boolean.png';
|
||||
import IcoDate from '../../assets/images/icon_date.png';
|
||||
import IcoEmail from '../../assets/images/icon_email.png';
|
||||
import IcoImage from '../../assets/images/icon_image.png';
|
||||
import IcoJson from '../../assets/images/icon_json.png';
|
||||
import IcoPassword from '../../assets/images/icon_password.png';
|
||||
import IcoNumber from '../../assets/images/icon_number.png';
|
||||
import IcoRelation from '../../assets/images/icon_relation.png';
|
||||
import IcoString from '../../assets/images/icon_string.png';
|
||||
@ -23,9 +25,11 @@ import styles from './styles.scss';
|
||||
const asset = {
|
||||
'boolean': IcoBoolean,
|
||||
'date': IcoDate,
|
||||
'email': IcoEmail,
|
||||
'media': IcoImage,
|
||||
'number': IcoNumber,
|
||||
'json': IcoJson,
|
||||
'password': IcoPassword,
|
||||
'relation': IcoRelation,
|
||||
'string': IcoString,
|
||||
'text': IcoText,
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
align-items: center;
|
||||
height: 4rem;
|
||||
margin-top: .6rem;
|
||||
margin-bottom: .9rem;
|
||||
margin-bottom: .8rem;
|
||||
padding: 0 1rem 0 1rem;
|
||||
border: 1px solid #E3E9F3;
|
||||
border-radius: 0.25rem;
|
||||
|
||||
@ -7,16 +7,18 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { capitalize } from 'lodash';
|
||||
import { capitalize, has } from 'lodash';
|
||||
|
||||
import PopUpWarning from 'components/PopUpWarning';
|
||||
import IcoContainer from 'components/IcoContainer';
|
||||
|
||||
import IcoBoolean from '../../assets/images/icon_boolean.png';
|
||||
import IcoDate from '../../assets/images/icon_date.png';
|
||||
import IcoEmail from '../../assets/images/icon_email.png';
|
||||
import IcoImage from '../../assets/images/icon_image.png';
|
||||
import IcoNumber from '../../assets/images/icon_number.png';
|
||||
import IcoJson from '../../assets/images/icon_json.png';
|
||||
import IcoPassword from '../../assets/images/icon_password.png';
|
||||
import IcoRelation from '../../assets/images/icon_relation.png';
|
||||
import IcoString from '../../assets/images/icon_string.png';
|
||||
import IcoText from '../../assets/images/icon_text.png';
|
||||
@ -38,6 +40,8 @@ class AttributeRow extends React.Component { // eslint-disable-line react/prefer
|
||||
'integer': IcoNumber,
|
||||
'float': IcoNumber,
|
||||
'decimal': IcoNumber,
|
||||
'email': IcoEmail,
|
||||
'password': IcoPassword,
|
||||
};
|
||||
this.state = {
|
||||
showWarning: false,
|
||||
@ -62,15 +66,38 @@ class AttributeRow extends React.Component { // eslint-disable-line react/prefer
|
||||
}
|
||||
|
||||
render() {
|
||||
const isNotEditable = has(this.props.row.params, 'configurable') && !this.props.row.params.configurable;
|
||||
const relationType = this.props.row.params.type ?
|
||||
<FormattedMessage id={`content-type-builder.attribute.${this.props.row.params.type}`} />
|
||||
: <div><FormattedMessage id="content-type-builder.modelPage.attribute.relationWith" /> <span style={{ fontStyle: 'italic' }}>{capitalize(this.props.row.params.target)}</span></div>;
|
||||
: (
|
||||
<div>
|
||||
<FormattedMessage id="content-type-builder.modelPage.attribute.relationWith" />
|
||||
|
||||
<FormattedMessage id="content-type-builder.from">
|
||||
{(message) => (
|
||||
<span style={{ fontStyle: 'italic' }}>
|
||||
{capitalize(this.props.row.params.target)}
|
||||
{this.props.row.params.pluginValue ? (
|
||||
`(${message}: ${this.props.row.params.pluginValue})`
|
||||
) : ''}
|
||||
</span>
|
||||
|
||||
)}
|
||||
</FormattedMessage>
|
||||
</div>
|
||||
);
|
||||
|
||||
const relationStyle = !this.props.row.params.type ? styles.relation : '';
|
||||
const icons = [{ icoType: 'pencil', onClick: this.handleEdit }, { icoType: 'trash', onClick: () => this.setState({ showWarning: !this.state.showWarning }) }];
|
||||
|
||||
const icons = isNotEditable ? [{ icoType: 'lock' }] : [{ icoType: 'pencil', onClick: this.handleEdit }, { icoType: 'trash', onClick: () => this.setState({ showWarning: !this.state.showWarning }) }];
|
||||
const editableStyle = isNotEditable ? '' : styles.editable;
|
||||
|
||||
return (
|
||||
<li className={`${styles.attributeRow} ${relationStyle}`} onClick={this.handleEdit}>
|
||||
<li
|
||||
className={`${styles.attributeRow} ${editableStyle} ${relationStyle}`}
|
||||
onClick={() => {
|
||||
isNotEditable ? () => {} : this.handleEdit();
|
||||
}}
|
||||
>
|
||||
<div className={styles.flex}>
|
||||
<div className={styles.nameContainer}>
|
||||
{this.renderAttributesBox()}
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
.attributeRow { /* stylelint-disable */
|
||||
cursor: pointer;
|
||||
min-height: 5.3rem;
|
||||
margin-top: 0!important;
|
||||
list-style: none;
|
||||
@ -10,9 +9,14 @@
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.editable {
|
||||
&:hover {
|
||||
background-color: #F7F8F8;
|
||||
}
|
||||
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.flex {
|
||||
|
||||
@ -84,6 +84,7 @@ class PopUpForm extends React.Component { // eslint-disable-line react/prefer-st
|
||||
|
||||
render() {
|
||||
const navContainer = this.props.noNav ? '' : this.renderNavContainer();
|
||||
const modalBodyStyle = this.props.renderModalBody ? { paddingTop: '2.3rem' } : {};
|
||||
const modalBody = this.props.renderModalBody ? this.props.renderModalBody()
|
||||
: map(this.props.form.items, (item, key ) => this.renderInput(item, key));
|
||||
|
||||
@ -108,7 +109,7 @@ class PopUpForm extends React.Component { // eslint-disable-line react/prefer-st
|
||||
</div>
|
||||
{navContainer}
|
||||
</div>
|
||||
<ModalBody className={styles.modalBody}>
|
||||
<ModalBody className={styles.modalBody} style={modalBodyStyle}>
|
||||
<form onSubmit={this.props.onSubmit}>
|
||||
<div className="container-fluid">
|
||||
<div className={`row ${this.props.renderModalBody ? 'justify-content-center' : ''}`}>
|
||||
|
||||
@ -28,25 +28,33 @@ class PopUpRelations extends React.Component { // eslint-disable-line react/pref
|
||||
|
||||
componentDidMount() {
|
||||
if (!isEmpty(this.props.dropDownItems) && !this.props.isEditting) {
|
||||
const target = {
|
||||
name: 'params.target',
|
||||
type: 'string',
|
||||
value: get(this.props.dropDownItems[0], 'name'),
|
||||
};
|
||||
|
||||
this.props.onChange({ target });
|
||||
this.init(this.props);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (isEmpty(this.props.dropDownItems) && !isEmpty(nextProps.dropDownItems) && !this.props.isEditting) {
|
||||
const target = {
|
||||
name: 'params.target',
|
||||
type: 'string',
|
||||
value: get(nextProps.dropDownItems[0], 'name'),
|
||||
};
|
||||
this.init(nextProps);
|
||||
}
|
||||
}
|
||||
|
||||
this.props.onChange({ target });
|
||||
init = (props) => {
|
||||
const target = {
|
||||
name: 'params.target',
|
||||
type: 'string',
|
||||
value: get(props.dropDownItems[0], 'name'),
|
||||
};
|
||||
|
||||
this.props.onChange({ target });
|
||||
|
||||
if (get(props.dropDownItems[0], 'source')) {
|
||||
this.props.onChange({
|
||||
target: {
|
||||
type: 'string',
|
||||
name: 'params.pluginValue',
|
||||
value: get(props.dropDownItems[0], 'source'),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -111,42 +119,49 @@ class PopUpRelations extends React.Component { // eslint-disable-line react/pref
|
||||
</ModalBody>
|
||||
)
|
||||
|
||||
renderModalBodyRelations = () => (
|
||||
<ModalBody className={`${styles.modalBody} ${styles.flex}`}>
|
||||
<RelationBox
|
||||
tabIndex="1"
|
||||
relationType={get(this.props.values, ['params', 'nature'])}
|
||||
contentTypeTargetPlaceholder={get(this.props.values, ['params', 'target'])}
|
||||
isFirstContentType
|
||||
header={this.props.contentType}
|
||||
input={get(this.props.form, ['items', '0'])}
|
||||
value={get(this.props.values, 'name')}
|
||||
onSubmit={this.props.onSubmit}
|
||||
onChange={this.props.onChange}
|
||||
didCheckErrors={this.props.didCheckErrors}
|
||||
errors={findIndex(this.props.formErrors, ['name', get(this.props.form, ['items', '0', 'name'])]) !== -1 ? this.props.formErrors[findIndex(this.props.formErrors, ['name', get(this.props.form, ['items', '0', 'name'])])].errors : []}
|
||||
/>
|
||||
<RelationNaturePicker
|
||||
selectedIco={get(this.props.values, ['params', 'nature'])}
|
||||
onChange={this.props.onChange}
|
||||
contentTypeName={get(this.props.contentType, 'name')}
|
||||
contentTypeTarget={get(this.props.values, ['params', 'target'])}
|
||||
/>
|
||||
<RelationBox
|
||||
tabIndex="2"
|
||||
contentTypeTargetPlaceholder={get(this.props.contentType, 'name')}
|
||||
relationType={get(this.props.values, ['params', 'nature'])}
|
||||
onSubmit={this.props.onSubmit}
|
||||
header={get(this.props.dropDownItems, [findIndex(this.props.dropDownItems, ['name', get(this.props.values, ['params', 'target'])])])}
|
||||
input={get(this.props.form, ['items', '1'])}
|
||||
value={get(this.props.values, ['params', 'key'])}
|
||||
onChange={this.props.onChange}
|
||||
didCheckErrors={this.props.didCheckErrors}
|
||||
errors={findIndex(this.props.formErrors, ['name', get(this.props.form, ['items', '1', 'name'])]) !== -1 ? this.props.formErrors[findIndex(this.props.formErrors, ['name', get(this.props.form, ['items', '1', 'name'])])].errors : []}
|
||||
dropDownItems={this.props.dropDownItems}
|
||||
/>
|
||||
</ModalBody>
|
||||
)
|
||||
renderModalBodyRelations = () => {
|
||||
const header = get(this.props.values, ['params', 'pluginValue']) ?
|
||||
get(this.props.dropDownItems, [findIndex(this.props.dropDownItems, {'name': get(this.props.values, ['params', 'target']), source: get(this.props.values, ['params', 'pluginValue']) })])
|
||||
: get(this.props.dropDownItems, [findIndex(this.props.dropDownItems, ['name', get(this.props.values, ['params', 'target'])])]);
|
||||
|
||||
return (
|
||||
<ModalBody className={`${styles.modalBody} ${styles.flex}`}>
|
||||
<RelationBox
|
||||
autoFocus
|
||||
tabIndex="1"
|
||||
relationType={get(this.props.values, ['params', 'nature'])}
|
||||
contentTypeTargetPlaceholder={get(this.props.values, ['params', 'target'])}
|
||||
isFirstContentType
|
||||
header={this.props.contentType}
|
||||
input={get(this.props.form, ['items', '0'])}
|
||||
value={get(this.props.values, 'name')}
|
||||
onSubmit={this.props.onSubmit}
|
||||
onChange={this.props.onChange}
|
||||
didCheckErrors={this.props.didCheckErrors}
|
||||
errors={findIndex(this.props.formErrors, ['name', get(this.props.form, ['items', '0', 'name'])]) !== -1 ? this.props.formErrors[findIndex(this.props.formErrors, ['name', get(this.props.form, ['items', '0', 'name'])])].errors : []}
|
||||
/>
|
||||
<RelationNaturePicker
|
||||
selectedIco={get(this.props.values, ['params', 'nature'])}
|
||||
onChange={this.props.onChange}
|
||||
contentTypeName={get(this.props.contentType, 'name')}
|
||||
contentTypeTarget={get(this.props.values, ['params', 'target'])}
|
||||
/>
|
||||
<RelationBox
|
||||
tabIndex="2"
|
||||
contentTypeTargetPlaceholder={get(this.props.contentType, 'name')}
|
||||
relationType={get(this.props.values, ['params', 'nature'])}
|
||||
onSubmit={this.props.onSubmit}
|
||||
header={header}
|
||||
input={get(this.props.form, ['items', '1'])}
|
||||
value={get(this.props.values, ['params', 'key'])}
|
||||
onChange={this.props.onChange}
|
||||
didCheckErrors={this.props.didCheckErrors}
|
||||
errors={findIndex(this.props.formErrors, ['name', get(this.props.form, ['items', '1', 'name'])]) !== -1 ? this.props.formErrors[findIndex(this.props.formErrors, ['name', get(this.props.form, ['items', '1', 'name'])])].errors : []}
|
||||
dropDownItems={this.props.dropDownItems}
|
||||
/>
|
||||
</ModalBody>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const loader = this.props.showLoader ?
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { get, isEmpty, map, startCase } from 'lodash';
|
||||
import pluralize from 'pluralize';
|
||||
|
||||
@ -25,13 +26,22 @@ class RelationBox extends React.Component { // eslint-disable-line react/prefer-
|
||||
}
|
||||
|
||||
handleClick = (e) => {
|
||||
const value = e.target.id.split('.');
|
||||
const target = {
|
||||
type: 'string',
|
||||
value: e.target.id,
|
||||
value: value[0],
|
||||
name: 'params.target',
|
||||
};
|
||||
|
||||
this.props.onChange({ target });
|
||||
|
||||
this.props.onChange({
|
||||
target: {
|
||||
type: 'string',
|
||||
value: value[1] !== 'undefined' ? value[1] : '',
|
||||
name: 'params.pluginValue',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
toggle = () => this.setState({ showMenu: !this.state.showMenu });
|
||||
@ -43,13 +53,30 @@ class RelationBox extends React.Component { // eslint-disable-line react/prefer-
|
||||
</DropdownToggle>
|
||||
<DropdownMenu className={styles.dropDownContent}>
|
||||
{map(this.props.dropDownItems, (value, key) => {
|
||||
const divStyle = get(this.props.header, 'name') === value.name ? { color: '#323740', fontWeight: 'bold'} : { color: 'rgba(50,55,64, 0.75)'};
|
||||
const id = value.source ? `${value.name}.${value.source}` : `${value.name}. `;
|
||||
let divStyle;
|
||||
|
||||
if (get(this.props.header, 'name') === value.name && !isEmpty(get(this.props.header,'source')) && value.source) {
|
||||
divStyle = { color: '#323740', fontWeight: 'bold'};
|
||||
} else if (value.source === get(this.props.header, 'source') && value.name === get(this.props.header, 'name')) {
|
||||
divStyle = { color: '#323740', fontWeight: 'bold'};
|
||||
} else {
|
||||
divStyle = { color: 'rgba(50,55,64,0.75)' };
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{ height: '3.6rem'}} key={key}>
|
||||
<DropdownItem onClick={this.handleClick} id={value.name}>
|
||||
<div style={divStyle} id={value.name}>
|
||||
<i className={`fa ${value.icon}`} style={divStyle} />
|
||||
{value.name}
|
||||
<DropdownItem onClick={this.handleClick} id={id}>
|
||||
<div style={divStyle} id={`${value.name}.${value.source}`}>
|
||||
<i className={`fa ${value.icon}`} style={divStyle} id={id} />
|
||||
{value.name}
|
||||
{value.source ? (
|
||||
<FormattedMessage id="content-type-builder.from">
|
||||
{(message) => (
|
||||
<span style={{ fontStyle: 'italic' }} id={id}>({message}: {value.source})</span>
|
||||
)}
|
||||
</FormattedMessage>
|
||||
) : ''}
|
||||
</div>
|
||||
</DropdownItem>
|
||||
</div>
|
||||
@ -92,6 +119,7 @@ class RelationBox extends React.Component { // eslint-disable-line react/prefer-
|
||||
errors={this.props.errors}
|
||||
didCheckErrors={this.props.didCheckErrors}
|
||||
pluginID="content-type-builder"
|
||||
autoFocus={this.props.autoFocus}
|
||||
/>;
|
||||
|
||||
const dropDown = !isEmpty(this.props.dropDownItems) ? this.renderDropdownMenu() : '';
|
||||
@ -100,7 +128,12 @@ class RelationBox extends React.Component { // eslint-disable-line react/prefer-
|
||||
<div className={styles.relationBox}>
|
||||
<div className={styles.headerContainer}>
|
||||
<i className={`fa ${get(this.props.header, 'icon')}`} />
|
||||
{startCase(get(this.props.header, 'name'))}
|
||||
{startCase(get(this.props.header, 'name'))}
|
||||
<span style={{ fontStyle: 'italic', fontWeight: '500' }}>
|
||||
{get(this.props.header, 'source') ? (
|
||||
`(${get(this.props.header, 'source')})`
|
||||
): ''}
|
||||
</span>
|
||||
{dropDown}
|
||||
</div>
|
||||
<div className={styles.inputContainer}>
|
||||
@ -118,6 +151,7 @@ class RelationBox extends React.Component { // eslint-disable-line react/prefer-
|
||||
}
|
||||
|
||||
RelationBox.propTypes = {
|
||||
autoFocus: PropTypes.bool,
|
||||
contentTypeTargetPlaceholder: PropTypes.string,
|
||||
didCheckErrors: PropTypes.bool.isRequired,
|
||||
dropDownItems: PropTypes.array,
|
||||
@ -133,6 +167,7 @@ RelationBox.propTypes = {
|
||||
};
|
||||
|
||||
RelationBox.defaultProps = {
|
||||
autoFocus: false,
|
||||
contentTypeTargetPlaceholder: '',
|
||||
dropDownItems: [],
|
||||
errors: [],
|
||||
|
||||
@ -35,7 +35,7 @@ class TableListRow extends React.Component { // eslint-disable-line react/prefer
|
||||
}
|
||||
|
||||
handleGoTo = () => {
|
||||
router.push(`/plugins/content-type-builder/models/${this.props.rowItem.name}`);
|
||||
router.push(`/plugins/content-type-builder/models/${this.props.rowItem.name}${this.props.rowItem.source ? `&source=${this.props.rowItem.source}`: ''}`);
|
||||
}
|
||||
|
||||
toggleModalWarning = () => this.setState({ showWarning: !this.state.showWarning });
|
||||
@ -43,15 +43,16 @@ class TableListRow extends React.Component { // eslint-disable-line react/prefer
|
||||
handleShowModalWarning = () => this.setState({ showWarning: !this.state.showWarning });
|
||||
|
||||
render() {
|
||||
const pluginSource = this.props.rowItem.source ? <FormattedMessage id="content-type-builder.from">{(message) =><span style={{ fontStyle: 'italic', color: '#787E8F', fontWeight: '500' }}>({message}: {this.props.rowItem.source})</span>}</FormattedMessage> : '';
|
||||
const temporary = this.props.rowItem.isTemporary ? <FormattedMessage id="content-type-builder.contentType.temporaryDisplay" /> : '';
|
||||
const description = isEmpty(this.props.rowItem.description) ? '-' : this.props.rowItem.description;
|
||||
const spanStyle = this.props.rowItem.isTemporary ? '60%' : '100%';
|
||||
const icons = [{ icoType: 'pencil', onClick: this.handleEdit }, { icoType: 'trash', onClick: this.handleShowModalWarning }];
|
||||
const icons = this.props.rowItem.source ? [] : [{ icoType: 'pencil', onClick: this.handleEdit }, { icoType: 'trash', onClick: this.handleShowModalWarning }];
|
||||
|
||||
return (
|
||||
<ListRow onClick={this.handleGoTo}>
|
||||
<div className="col-md-1"><i className={`fa ${this.props.rowItem.icon}`} /></div>
|
||||
<div className={`col-md-3 ${styles.italic} ${styles.nameContainer}`}><span style={{ width: spanStyle }}>{startCase(this.props.rowItem.name)}</span> {temporary}</div>
|
||||
<div className={`col-md-3 ${styles.italic} ${styles.nameContainer}`}><span style={{ width: spanStyle }}>{startCase(this.props.rowItem.name)} {pluginSource}</span> {temporary}</div>
|
||||
<div className="col-md-5 text-center">{description}</div>
|
||||
<div className="col-md-2 text-center">{this.props.rowItem.fields}</div>
|
||||
<div className="col-md-1">
|
||||
|
||||
@ -65,7 +65,7 @@ export function modelsFetch() {
|
||||
export function modelsFetchSucceeded(models) {
|
||||
const modelNumber = size(models.models) > 1 ? 'plural' : 'singular';
|
||||
|
||||
const sections = storeData.getMenu() || map(models.models, (model) => ({icon: 'fa-caret-square-o-right', name: model.name }));
|
||||
const sections = storeData.getMenu() || map(models.models, (model) => ({icon: 'fa-caret-square-o-right', name: model.name, source: model.source }));
|
||||
|
||||
if (!storeData.getMenu()){
|
||||
sections.push({ icon: 'fa-plus', name: 'button.contentType.add' });
|
||||
|
||||
@ -75,6 +75,14 @@
|
||||
"type": "json",
|
||||
"description": "content-type-builder.popUpForm.attributes.json.description"
|
||||
},
|
||||
{
|
||||
"type": "email",
|
||||
"description": "content-type-builder.popUpForm.attributes.email.description"
|
||||
},
|
||||
{
|
||||
"type": "password",
|
||||
"description": "content-type-builder.popUpForm.attributes.password.description"
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"description": "content-type-builder.popUpForm.attributes.relation.description"
|
||||
@ -312,6 +320,142 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"email": {
|
||||
"baseSettings": {
|
||||
"items": [
|
||||
{
|
||||
"label": "content-type-builder.form.attribute.item.string.name",
|
||||
"name": "name",
|
||||
"type": "string",
|
||||
"value": "",
|
||||
"validations": {
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"advancedSettings": {
|
||||
"items": [
|
||||
{
|
||||
"title": "content-type-builder.form.attribute.item.settings.name",
|
||||
"label": "content-type-builder.form.attribute.item.requiredField",
|
||||
"name": "params.required",
|
||||
"type": "checkbox",
|
||||
"value": false,
|
||||
"validations": {},
|
||||
"inputDescription": "content-type-builder.form.attribute.item.requiredField.description"
|
||||
},
|
||||
{
|
||||
"label": "content-type-builder.form.attribute.item.uniqueField",
|
||||
"name": "params.unique",
|
||||
"type": "checkbox",
|
||||
"value": false,
|
||||
"validations": {},
|
||||
"inputDescription": "content-type-builder.form.attribute.item.uniqueField.description"
|
||||
},
|
||||
{
|
||||
"label": "content-type-builder.form.attribute.item.minimumLength",
|
||||
"name": "params.minLength",
|
||||
"type": "checkbox",
|
||||
"value": false,
|
||||
"validations": {},
|
||||
"items": [
|
||||
{
|
||||
"label": "",
|
||||
"name": "params.minLengthValue",
|
||||
"value": "",
|
||||
"type": "number",
|
||||
"validations": {
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "content-type-builder.form.attribute.item.maximumLength",
|
||||
"name": "params.maxLength",
|
||||
"type": "checkbox",
|
||||
"value": false,
|
||||
"validations": {},
|
||||
"items": [
|
||||
{
|
||||
"label": "",
|
||||
"name": "params.maxLengthValue",
|
||||
"value": "",
|
||||
"type": "number",
|
||||
"validations": {
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"password": {
|
||||
"baseSettings": {
|
||||
"items": [
|
||||
{
|
||||
"label": "content-type-builder.form.attribute.item.string.name",
|
||||
"name": "name",
|
||||
"type": "string",
|
||||
"value": "",
|
||||
"validations": {
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"advancedSettings": {
|
||||
"items": [
|
||||
{
|
||||
"title": "content-type-builder.form.attribute.item.settings.name",
|
||||
"label": "content-type-builder.form.attribute.item.requiredField",
|
||||
"name": "params.required",
|
||||
"type": "checkbox",
|
||||
"value": false,
|
||||
"validations": {},
|
||||
"inputDescription": "content-type-builder.form.attribute.item.requiredField.description"
|
||||
},
|
||||
{
|
||||
"label": "content-type-builder.form.attribute.item.minimumLength",
|
||||
"name": "params.minLength",
|
||||
"type": "checkbox",
|
||||
"value": false,
|
||||
"validations": {},
|
||||
"items": [
|
||||
{
|
||||
"label": "",
|
||||
"name": "params.minLengthValue",
|
||||
"value": "",
|
||||
"type": "number",
|
||||
"validations": {
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "content-type-builder.form.attribute.item.maximumLength",
|
||||
"name": "params.maxLength",
|
||||
"type": "checkbox",
|
||||
"value": false,
|
||||
"validations": {},
|
||||
"items": [
|
||||
{
|
||||
"label": "",
|
||||
"name": "params.maxLengthValue",
|
||||
"value": "",
|
||||
"type": "number",
|
||||
"validations": {
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"date": {
|
||||
"baseSettings": {
|
||||
"items": [
|
||||
|
||||
@ -530,6 +530,9 @@ export class Form extends React.Component { // eslint-disable-line react/prefer-
|
||||
const selectOptions = includes(this.props.hash, 'attributenumber') ? get(this.props.form, ['items', '1', 'items']) : this.props.selectOptions;
|
||||
|
||||
if (includes(popUpFormType, 'relation')) {
|
||||
const contentType = this.props.modelName.split('&source=');
|
||||
const contentTypeIndex = contentType.length === 2 ? { name: contentType[0], source: contentType[1] } : { name: contentType[0] };
|
||||
|
||||
return (
|
||||
<PopUpRelations
|
||||
isOpen={this.state.showModal}
|
||||
@ -537,7 +540,7 @@ export class Form extends React.Component { // eslint-disable-line react/prefer-
|
||||
renderCustomPopUpHeader={renderCustomPopUpHeader}
|
||||
popUpTitle={popUpTitle}
|
||||
routePath={`${this.props.routePath}/${this.props.hash}`}
|
||||
contentType={get(dropDownItems, [findIndex(dropDownItems, ['name', this.props.modelName])])}
|
||||
contentType={get(dropDownItems, [findIndex(dropDownItems, contentTypeIndex)])}
|
||||
form={this.props.form}
|
||||
showRelation={includes(this.props.hash, 'defineRelation')}
|
||||
onChange={this.handleChange}
|
||||
|
||||
@ -57,7 +57,7 @@ export function* editContentType(action) {
|
||||
strapi.notification.success('content-type-builder.notification.success.message.contentType.edit');
|
||||
}
|
||||
} catch(error) {
|
||||
strapi.notification.error(error);
|
||||
strapi.notification.error(get(error, ['response', 'payload', 'message'], 'notification.error'));
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,10 +75,15 @@ export function* fetchConnections() {
|
||||
|
||||
export function* fetchContentType(action) {
|
||||
try {
|
||||
const requestUrl = `/content-type-builder/models/${action.contentTypeName.split('&source=')[0]}`;
|
||||
const params = {};
|
||||
const source = action.contentTypeName.split('&source=')[1];
|
||||
|
||||
const requestUrl = `/content-type-builder/models/${action.contentTypeName}`;
|
||||
if (source) {
|
||||
params.source = source;
|
||||
}
|
||||
|
||||
const data = yield call(request, requestUrl, { method: 'GET' });
|
||||
const data = yield call(request, requestUrl, { method: 'GET', params });
|
||||
|
||||
yield put(contentTypeFetchSucceeded(data));
|
||||
|
||||
|
||||
@ -155,10 +155,11 @@ export function setButtonLoader() {
|
||||
};
|
||||
}
|
||||
|
||||
export function submit(context) {
|
||||
export function submit(context, modelName) {
|
||||
return {
|
||||
type: SUBMIT,
|
||||
context,
|
||||
modelName,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createStructuredSelector } from 'reselect';
|
||||
import { bindActionCreators, compose } from 'redux';
|
||||
import { get, has, isEmpty, size, replace, startCase, findIndex } from 'lodash';
|
||||
import { get, has, includes, isEmpty, size, replace, startCase, findIndex } from 'lodash';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
@ -97,7 +97,7 @@ export class ModelPage extends React.Component { // eslint-disable-line react/pr
|
||||
return acc.concat(models);
|
||||
}, []);
|
||||
|
||||
const shouldRedirect = allowedPaths.filter(el => el === this.props.match.params.modelName).length === 0;
|
||||
const shouldRedirect = allowedPaths.filter(el => el === this.props.match.params.modelName.split('&')[0]).length === 0;
|
||||
|
||||
if (shouldRedirect) {
|
||||
this.props.history.push('/404');
|
||||
@ -176,7 +176,6 @@ export class ModelPage extends React.Component { // eslint-disable-line react/pr
|
||||
const index = findIndex(this.props.modelPage.model.attributes, ['name', attributeName]);
|
||||
const attribute = this.props.modelPage.model.attributes[index];
|
||||
const settingsType = attribute.params.type ? 'baseSettings' : 'defineRelation';
|
||||
|
||||
const parallelAttributeIndex = findIndex(this.props.modelPage.model.attributes, ['name', attribute.params.key]);
|
||||
const hasParallelAttribute = settingsType === 'defineRelation' && parallelAttributeIndex !== -1 ? `::${parallelAttributeIndex}` : '';
|
||||
|
||||
@ -188,6 +187,10 @@ export class ModelPage extends React.Component { // eslint-disable-line react/pr
|
||||
case 'decimal':
|
||||
attributeType = 'number';
|
||||
break;
|
||||
case 'email':
|
||||
case 'password':
|
||||
attributeType = 'string';
|
||||
break;
|
||||
default:
|
||||
attributeType = attribute.params.type ? attribute.params.type : 'relation';
|
||||
}
|
||||
@ -196,7 +199,7 @@ export class ModelPage extends React.Component { // eslint-disable-line react/pr
|
||||
}
|
||||
|
||||
handleSubmit = () => {
|
||||
this.props.submit(this.context);
|
||||
this.props.submit(this.context, this.props.match.params.modelName);
|
||||
}
|
||||
|
||||
toggleModal = () => {
|
||||
@ -218,21 +221,25 @@ export class ModelPage extends React.Component { // eslint-disable-line react/pr
|
||||
renderCustomLi = (row, key) => <AttributeRow key={key} row={row} onEditAttribute={this.handleEditAttribute} onDelete={this.handleDelete} />
|
||||
|
||||
renderCustomLink = (props, linkStyles) => {
|
||||
if (props.link.name === 'button.contentType.add') return this.renderAddLink(props, linkStyles);
|
||||
if (props.link.name === 'button.contentType.add') {
|
||||
return this.renderAddLink(props, linkStyles);
|
||||
}
|
||||
|
||||
const temporary = props.link.isTemporary || this.props.modelPage.showButtons && props.link.name === this.props.match.params.modelName ? <FormattedMessage id="content-type-builder.contentType.temporaryDisplay" /> : '';
|
||||
const spanStyle = props.link.isTemporary || this.props.modelPage.showButtons && props.link.name === this.props.match.params.modelName ? styles.leftMenuSpan : '';
|
||||
const linkName = props.link.source ? `${props.link.name}&source=${props.link.source}` : props.link.name;
|
||||
const temporary = props.link.isTemporary || this.props.modelPage.showButtons && linkName === this.props.match.params.modelName ? <FormattedMessage id="content-type-builder.contentType.temporaryDisplay" /> : '';
|
||||
const spanStyle = props.link.isTemporary || this.props.modelPage.showButtons && linkName === this.props.match.params.modelName || isEmpty(temporary) && props.link.source ? styles.leftMenuSpan : '';
|
||||
const pluginSource = isEmpty(temporary) && props.link.source ? <FormattedMessage id="content-type-builder.from">{(message) => <span style={{ marginRight: '10px' }}>({message}: {props.link.source})</span>}</FormattedMessage>: '';
|
||||
|
||||
return (
|
||||
<li className={linkStyles.pluginLeftMenuLink}>
|
||||
<NavLink className={linkStyles.link} to={`/plugins/content-type-builder/models/${props.link.name}`} activeClassName={linkStyles.linkActive}>
|
||||
<NavLink className={linkStyles.link} to={`/plugins/content-type-builder/models/${props.link.name}${props.link.source ? `&source=${props.link.source}` : ''}`} activeClassName={linkStyles.linkActive}>
|
||||
<div>
|
||||
<i className={`fa fa-caret-square-o-right`} />
|
||||
</div>
|
||||
<div className={styles.contentContainer}>
|
||||
|
||||
<span className={spanStyle}>{startCase(props.link.name)}</span>
|
||||
<span style={{ marginLeft: '1rem', fontStyle: 'italic' }}>{temporary}</span>
|
||||
<span style={{ marginLeft: '1rem', fontStyle: 'italic' }}>{temporary}{pluginSource}</span>
|
||||
</div>
|
||||
</NavLink>
|
||||
</li>
|
||||
@ -283,7 +290,7 @@ export class ModelPage extends React.Component { // eslint-disable-line react/pr
|
||||
renderCustomLi={this.renderCustomLi}
|
||||
onButtonClick={this.handleClickAddAttribute}
|
||||
/>;
|
||||
|
||||
const icoType = includes(this.props.match.params.modelName, '&source=') ? '' : 'pencil';
|
||||
return (
|
||||
<div className={styles.modelPage}>
|
||||
<div className="container-fluid">
|
||||
@ -299,7 +306,7 @@ export class ModelPage extends React.Component { // eslint-disable-line react/pr
|
||||
<ContentHeader
|
||||
name={this.props.modelPage.model.name}
|
||||
description={contentHeaderDescription}
|
||||
icoType="pencil"
|
||||
icoType={icoType}
|
||||
editIcon
|
||||
editPath={`${redirectRoute}/${this.props.match.params.modelName}#edit${this.props.match.params.modelName}::contentType::baseSettings`}
|
||||
addButtons={addButtons}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { LOCATION_CHANGE } from 'react-router-redux';
|
||||
import {
|
||||
capitalize,
|
||||
cloneDeep,
|
||||
forEach,
|
||||
get,
|
||||
includes,
|
||||
@ -35,6 +36,7 @@ import { makeSelectModel } from './selectors';
|
||||
|
||||
export function* getTableExistance() {
|
||||
try {
|
||||
// TODO check table existance for plugin model
|
||||
const model = yield select(makeSelectModel());
|
||||
const modelName = !isEmpty(model.collectionName) ? model.collectionName : model.name;
|
||||
const requestUrl = `/content-type-builder/checkTableExists/${model.connection}/${modelName}`;
|
||||
@ -49,9 +51,15 @@ export function* getTableExistance() {
|
||||
|
||||
export function* fetchModel(action) {
|
||||
try {
|
||||
const requestUrl = `/content-type-builder/models/${action.modelName}`;
|
||||
const requestUrl = `/content-type-builder/models/${action.modelName.split('&source=')[0]}`;
|
||||
const params = {};
|
||||
const source = action.modelName.split('&source=')[1];
|
||||
|
||||
const data = yield call(request, requestUrl, { method: 'GET' });
|
||||
if (source) {
|
||||
params.source = source;
|
||||
}
|
||||
|
||||
const data = yield call(request, requestUrl, { method: 'GET', params });
|
||||
|
||||
yield put(modelFetchSucceeded(data));
|
||||
|
||||
@ -68,8 +76,8 @@ export function* submitChanges(action) {
|
||||
yield put(setButtonLoader());
|
||||
|
||||
const modelName = get(storeData.getContentType(), 'name');
|
||||
|
||||
const body = yield select(makeSelectModel());
|
||||
const data = yield select(makeSelectModel());
|
||||
const body = cloneDeep(data);
|
||||
|
||||
map(body.attributes, (attribute, index) => {
|
||||
// Remove the connection key from attributes
|
||||
@ -82,18 +90,27 @@ export function* submitChanges(action) {
|
||||
delete body.attributes[index].params.dominant;
|
||||
}
|
||||
|
||||
if (includes(key, 'Value')) {
|
||||
if (includes(key, 'Value') && key !== 'pluginValue') {
|
||||
// Remove and set needed keys for params
|
||||
set(body.attributes[index].params, replace(key, 'Value', ''), value);
|
||||
unset(body.attributes[index].params, key);
|
||||
}
|
||||
|
||||
if (key === 'pluginValue' && value) {
|
||||
set(body.attributes[index].params, 'plugin', true);
|
||||
}
|
||||
|
||||
if (!value) {
|
||||
const paramsKey = includes(key, 'Value') ? replace(key,'Value', '') : key;
|
||||
unset(body.attributes[index].params, paramsKey);
|
||||
}
|
||||
});
|
||||
});
|
||||
const pluginModel = action.modelName.split('&source=')[1];
|
||||
|
||||
if (pluginModel) {
|
||||
set(body, 'plugin', pluginModel);
|
||||
}
|
||||
|
||||
const method = modelName === body.name ? 'POST' : 'PUT';
|
||||
const baseUrl = '/content-type-builder/models/';
|
||||
@ -130,7 +147,8 @@ export function* submitChanges(action) {
|
||||
}
|
||||
|
||||
} catch(error) {
|
||||
strapi.notification.error(error);
|
||||
strapi.notification.error(get(error, ['response', 'payload', 'message'], 'notification.error'));
|
||||
yield put(unsetButtonLoader());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -9,9 +9,12 @@
|
||||
"attribute.date": "Date",
|
||||
"attribute.json": "JSON",
|
||||
"attribute.media": "Media",
|
||||
"attribute.email": "Email",
|
||||
"attribute.password": "Password",
|
||||
"attribute.relation": "Relation",
|
||||
|
||||
"contentType.temporaryDisplay": "(Not saved)",
|
||||
"from": "from",
|
||||
"home.contentTypeBuilder.name": "Content Types",
|
||||
"home.contentTypeBuilder.description": "Create, update your own content types.",
|
||||
"home.emptyContentType.title": "There are no Content Types Available",
|
||||
@ -107,6 +110,8 @@
|
||||
"popUpForm.attributes.json.description": "Data in JSON format",
|
||||
"popUpForm.attributes.media.description": "Images, videos, PDFs and other files",
|
||||
"popUpForm.attributes.relation.description": "Refers to a Content Type",
|
||||
"popUpForm.attributes.email.description": "User's email...",
|
||||
"popUpForm.attributes.password.description": "User password...",
|
||||
|
||||
"popUpForm.attributes.string.name": "String",
|
||||
"popUpForm.attributes.text.name": "Text",
|
||||
@ -116,6 +121,8 @@
|
||||
"popUpForm.attributes.media.name": "Media",
|
||||
"popUpForm.attributes.number.name": "Number",
|
||||
"popUpForm.attributes.relation.name": "Relation",
|
||||
"popUpForm.attributes.email.name": "Email",
|
||||
"popUpForm.attributes.password.name": "Password",
|
||||
"popUpForm.create": "Add New",
|
||||
"popUpForm.edit": "Edit",
|
||||
"popUpForm.field": "Field",
|
||||
|
||||
@ -9,9 +9,14 @@
|
||||
"attribute.date": "Date",
|
||||
"attribute.json": "JSON",
|
||||
"attribute.media": "Media",
|
||||
"attribute.password": "Mot de passe",
|
||||
"attribute.email": "Email",
|
||||
"attribute.relation": "Relation",
|
||||
|
||||
"contentType.temporaryDisplay": "(Non sauvegardé)",
|
||||
|
||||
"from": "De",
|
||||
|
||||
"home.contentTypeBuilder.name": "Content Types",
|
||||
"home.contentTypeBuilder.description": "Créez, éditer vos modèles.",
|
||||
"home.emptyContentType.title": "Il n'y a pas de model disponible",
|
||||
@ -108,6 +113,8 @@
|
||||
"popUpForm.attributes.json.description": "Données au format JSON",
|
||||
"popUpForm.attributes.media.description": "Images, vidéos, PDFs et autres fichiers",
|
||||
"popUpForm.attributes.relation.description": "Pointe vers un autre Modèle",
|
||||
"popUpForm.attributes.password.description": "Mot de passe utilisateur...",
|
||||
"popUpForm.attributes.email.description": "Email utilisateurs",
|
||||
|
||||
"popUpForm.attributes.string.name": "Chaîne de caractères",
|
||||
"popUpForm.attributes.text.name": "Text",
|
||||
@ -117,6 +124,8 @@
|
||||
"popUpForm.attributes.json.name": "JSON",
|
||||
"popUpForm.attributes.media.name": "Media",
|
||||
"popUpForm.attributes.relation.name": "Relation",
|
||||
"popUpForm.attributes.email.name": "Email",
|
||||
"popUpForm.attributes.password.name": "Mot de passe",
|
||||
"popUpForm.create": "Ajouter un Nouveau",
|
||||
"popUpForm.edit": "Modifer",
|
||||
"popUpForm.field": "Champ",
|
||||
|
||||
@ -13,13 +13,19 @@ module.exports = {
|
||||
|
||||
getModel: async ctx => {
|
||||
const Service = strapi.plugins['content-type-builder'].services.contenttypebuilder;
|
||||
const { source } = ctx.request.query;
|
||||
|
||||
let { model } = ctx.params;
|
||||
|
||||
model = _.toLower(model);
|
||||
|
||||
if (!_.get(strapi.models, model)) return ctx.badRequest(null, [{ messages: [{ id: 'request.error.model.unknow' }] }]);
|
||||
if (!source && !_.get(strapi.models, model)) return ctx.badRequest(null, [{ messages: [{ id: 'request.error.model.unknow' }] }]);
|
||||
|
||||
ctx.send({ model: Service.getModel(model) });
|
||||
if (source && !_.get(strapi.plugins, [source, 'models', model])) {
|
||||
return ctx.badRequest(null, [{ messages: [{ id: 'request.error.model.unknow' }] }]);
|
||||
}
|
||||
|
||||
ctx.send({ model: Service.getModel(model, source) });
|
||||
},
|
||||
|
||||
getConnections: async ctx => {
|
||||
@ -27,14 +33,14 @@ module.exports = {
|
||||
},
|
||||
|
||||
createModel: async ctx => {
|
||||
const { name, description, connection, collectionName, attributes = [] } = ctx.request.body;
|
||||
const { name, description, connection, collectionName, attributes = [], plugin } = ctx.request.body;
|
||||
|
||||
if (!name) return ctx.badRequest(null, [{ messages: [{ id: 'request.error.name.missing' }] }]);
|
||||
if (!_.includes(Service.getConnections(), connection)) return ctx.badRequest(null, [{ messages: [{ id: 'request.error.connection.unknow' }] }]);
|
||||
if (strapi.models[name]) return ctx.badRequest(null, [{ messages: [{ id: 'request.error.model.exist' }] }]);
|
||||
if (!_.isNaN(parseFloat(name[0]))) return ctx.badRequest(null, [{ messages: [{ id: 'request.error.model.name' }] }]);
|
||||
|
||||
const [formatedAttributes, attributesErrors] = Service.formatAttributes(attributes);
|
||||
const [formatedAttributes, attributesErrors] = Service.formatAttributes(attributes, name, plugin);
|
||||
|
||||
if (!_.isEmpty(attributesErrors)) {
|
||||
return ctx.badRequest(null, [{ messages: attributesErrors }]);
|
||||
@ -44,24 +50,20 @@ module.exports = {
|
||||
|
||||
await Service.generateAPI(name, description, connection, collectionName, []);
|
||||
|
||||
const [modelFilePath, modelFilePathErrors] = Service.getModelPath(name);
|
||||
|
||||
if (!_.isEmpty(modelFilePathErrors)) {
|
||||
return ctx.badRequest(null, [{ messages: modelFilePathErrors }]);
|
||||
}
|
||||
const modelFilePath = Service.getModelPath(name, plugin);
|
||||
|
||||
try {
|
||||
const modelJSON = require(modelFilePath);
|
||||
const modelJSON = _.cloneDeep(require(modelFilePath));
|
||||
|
||||
modelJSON.attributes = formatedAttributes;
|
||||
|
||||
const clearRelationsErrors = Service.clearRelations(name);
|
||||
const clearRelationsErrors = Service.clearRelations(name, plugin);
|
||||
|
||||
if (!_.isEmpty(clearRelationsErrors)) {
|
||||
return ctx.badRequest(null, [{ messages: clearRelationsErrors }]);
|
||||
}
|
||||
|
||||
const createRelationsErrors = Service.createRelations(name, attributes);
|
||||
const createRelationsErrors = Service.createRelations(name, attributes, plugin);
|
||||
|
||||
if (!_.isEmpty(createRelationsErrors)) {
|
||||
return ctx.badRequest(null, [{ messages: createRelationsErrors }]);
|
||||
@ -83,25 +85,23 @@ module.exports = {
|
||||
|
||||
updateModel: async ctx => {
|
||||
const { model } = ctx.params;
|
||||
const { name, description, connection, collectionName, attributes = [] } = ctx.request.body;
|
||||
const { name, description, connection, collectionName, attributes = [], plugin } = ctx.request.body;
|
||||
|
||||
if (!name) return ctx.badRequest(null, [{ messages: [{ id: 'request.error.name.missing' }] }]);
|
||||
if (!_.includes(Service.getConnections(), connection)) return ctx.badRequest(null, [{ messages: [{ id: 'request.error.connection.unknow' }] }]);
|
||||
if (strapi.models[_.toLower(name)] && name !== model) return ctx.badRequest(null, [{ messages: [{ id: 'request.error.model.exist' }] }]);
|
||||
if (!strapi.models[_.toLower(model)]) return ctx.badRequest(null, [{ messages: [{ id: 'request.error.model.unknow' }] }]);
|
||||
if (!_.isNaN(parseFloat(name[0]))) return ctx.badRequest(null, [{ messages: [{ id: 'request.error.model.name' }] }]);
|
||||
if (plugin && !strapi.plugins[_.toLower(plugin)]) return ctx.badRequest(null, [{ message: [{ id: 'request.error.plugin.name' }] }]);
|
||||
if (plugin && !strapi.plugins[_.toLower(plugin)].models[_.toLower(model)]) return ctx.badRequest(null, [{ message: [{ id: 'request.error.model.unknow' }] }]);
|
||||
|
||||
const [formatedAttributes, attributesErrors] = Service.formatAttributes(attributes);
|
||||
const [formatedAttributes, attributesErrors] = Service.formatAttributes(attributes, name.toLowerCase(), plugin);
|
||||
|
||||
if (!_.isEmpty(attributesErrors)) {
|
||||
return ctx.badRequest(null, [{ messages: attributesErrors }]);
|
||||
}
|
||||
|
||||
let [modelFilePath, modelFilePathErrors] = Service.getModelPath(model);
|
||||
|
||||
if (!_.isEmpty(modelFilePathErrors)) {
|
||||
return ctx.badRequest(null, [{ messages: modelFilePathErrors }]);
|
||||
}
|
||||
let modelFilePath = Service.getModelPath(model, plugin);
|
||||
|
||||
strapi.reload.isWatching = false;
|
||||
|
||||
@ -110,7 +110,7 @@ module.exports = {
|
||||
}
|
||||
|
||||
try {
|
||||
const modelJSON = require(modelFilePath);
|
||||
const modelJSON = _.cloneDeep(require(modelFilePath));
|
||||
|
||||
modelJSON.attributes = formatedAttributes;
|
||||
modelJSON.info = {
|
||||
@ -120,13 +120,13 @@ module.exports = {
|
||||
modelJSON.connection = connection;
|
||||
modelJSON.collectionName = collectionName;
|
||||
|
||||
const clearRelationsErrors = Service.clearRelations(model);
|
||||
const clearRelationsErrors = Service.clearRelations(model, plugin);
|
||||
|
||||
if (!_.isEmpty(clearRelationsErrors)) {
|
||||
return ctx.badRequest(null, [{ messages: clearRelationsErrors }]);
|
||||
}
|
||||
|
||||
const createRelationsErrors = Service.createRelations(name, attributes);
|
||||
const createRelationsErrors = Service.createRelations(name, attributes, plugin);
|
||||
|
||||
if (!_.isEmpty(createRelationsErrors)) {
|
||||
return ctx.badRequest(null, [{ messages: createRelationsErrors }]);
|
||||
@ -139,11 +139,7 @@ module.exports = {
|
||||
return ctx.badRequest(null, [{ messages: removeModelErrors }]);
|
||||
}
|
||||
|
||||
[modelFilePath, modelFilePathErrors] = Service.getModelPath(name);
|
||||
|
||||
if (!_.isEmpty(modelFilePathErrors)) {
|
||||
return ctx.badRequest(null, [{ messages: modelFilePathErrors }]);
|
||||
}
|
||||
modelFilePath = Service.getModelPath(name, plugin);
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-plugin-content-type-builder",
|
||||
"version": "3.0.0-alpha.7.2",
|
||||
"version": "3.0.0-alpha.7.3",
|
||||
"description": "Strapi plugin to create content type (API).",
|
||||
"strapi": {
|
||||
"name": "Content Type Builder",
|
||||
@ -25,11 +25,11 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"pluralize": "^7.0.0",
|
||||
"strapi-generate": "3.0.0-alpha.7.2",
|
||||
"strapi-generate-api": "3.0.0-alpha.7.2"
|
||||
"strapi-generate": "3.0.0-alpha.7.3",
|
||||
"strapi-generate-api": "3.0.0-alpha.7.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"strapi-helper-plugin": "3.0.0-alpha.7.2"
|
||||
"strapi-helper-plugin": "3.0.0-alpha.7.3"
|
||||
},
|
||||
"author": {
|
||||
"name": "Strapi team",
|
||||
|
||||
@ -18,13 +18,29 @@ module.exports = {
|
||||
});
|
||||
});
|
||||
|
||||
return models;
|
||||
const pluginModels = Object.keys(strapi.plugins).reduce((acc, current) => {
|
||||
_.forEach(strapi.plugins[current].models, (model, name) => {
|
||||
acc.push({
|
||||
icon: 'fa-cube',
|
||||
name: _.get(model, 'info.name', 'model.name.missing'),
|
||||
description: _.get(model, 'info.description', 'model.description.missing'),
|
||||
fields: _.keys(model.attributes).length,
|
||||
source: current,
|
||||
});
|
||||
});
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
return models.concat(pluginModels);
|
||||
},
|
||||
|
||||
getModel: name => {
|
||||
getModel: (name, source) => {
|
||||
name = _.toLower(name);
|
||||
|
||||
const model = _.get(strapi.models, name);
|
||||
const model = source ? _.get(strapi.plugins, [source, 'models', name]) : _.get(strapi.models, name);
|
||||
|
||||
// const model = _.get(strapi.models, name);
|
||||
|
||||
const attributes = [];
|
||||
_.forEach(model.attributes, (params, name) => {
|
||||
@ -58,8 +74,6 @@ module.exports = {
|
||||
},
|
||||
|
||||
generateAPI: (name, description, connection, collectionName, attributes) => {
|
||||
description = _.replace(description, /\"/g, '\\"');
|
||||
|
||||
const template = _.get(strapi.config.currentEnvironment, `database.connections.${connection}.connector`, 'strapi-mongoose').split('-')[1];
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
@ -69,7 +83,7 @@ module.exports = {
|
||||
rootPath: strapi.config.appPath,
|
||||
args: {
|
||||
api: name,
|
||||
description,
|
||||
description: _.replace(description, /\"/g, '\\"'),
|
||||
attributes,
|
||||
connection,
|
||||
collectionName: !_.isEmpty(collectionName) ? collectionName : undefined,
|
||||
@ -81,59 +95,49 @@ module.exports = {
|
||||
success: () => {
|
||||
resolve();
|
||||
},
|
||||
error: () => {
|
||||
reject();
|
||||
error: (err) => {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
getModelPath: model => {
|
||||
model = _.toLower(model);
|
||||
getModelPath: (model, plugin) => {
|
||||
// Retrieve where is located the model.
|
||||
// Note: The target is not found when we are creating a new API. That's why, we are returning the lowercased model.
|
||||
const target = Object.keys((plugin ? strapi.plugins : strapi.api) || {})
|
||||
.filter(x => _.includes(Object.keys((plugin ? strapi.plugins : strapi.api)[x].models), model.toLowerCase()))[0] || model.toLowerCase();
|
||||
|
||||
let searchFilePath;
|
||||
const errors = [];
|
||||
const searchFileName = `${model}.settings.json`;
|
||||
const apiPath = path.join(strapi.config.appPath, 'api');
|
||||
// Retrieve the filename of the model.
|
||||
const filename = fs.readdirSync(plugin ? path.join(strapi.config.appPath, 'plugins', target, 'models') : path.join(strapi.config.appPath, 'api', target, 'models'))
|
||||
.filter(x => x[0] !== '.')
|
||||
.filter(x => x.split('.settings.json')[0].toLowerCase() === model.toLowerCase())[0];
|
||||
|
||||
try {
|
||||
const apis = fs.readdirSync(apiPath).filter(x => x[0] !== '.');
|
||||
|
||||
_.forEach(apis, api => {
|
||||
const modelsPath = path.join(apiPath, api, 'models');
|
||||
|
||||
try {
|
||||
const models = fs.readdirSync(modelsPath).filter(x => x[0] !== '.');
|
||||
|
||||
const modelIndex = _.indexOf(_.map(models, model => _.toLower(model)), searchFileName);
|
||||
|
||||
if (modelIndex !== -1) searchFilePath = `${modelsPath}/${models[modelIndex]}`;
|
||||
} catch (e) {
|
||||
errors.push({
|
||||
id: 'request.error.folder.read',
|
||||
params: {
|
||||
folderPath: modelsPath
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
errors.push({
|
||||
id: 'request.error.folder.read',
|
||||
params: {
|
||||
folderPath: apiPath
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return [searchFilePath, errors];
|
||||
return plugin ?
|
||||
path.resolve(strapi.config.appPath, 'plugins', target, 'models', filename):
|
||||
path.resolve(strapi.config.appPath, 'api', target, 'models', filename);
|
||||
},
|
||||
|
||||
formatAttributes: attributes => {
|
||||
formatAttributes: (attributes, name, plugin) => {
|
||||
const errors = [];
|
||||
const attrs = {};
|
||||
|
||||
_.forEach(attributes, attribute => {
|
||||
const target = Object.keys((plugin ? strapi.plugins : strapi.api) || {})
|
||||
.filter(x => _.includes(Object.keys((plugin ? strapi.plugins : strapi.api)[x].models), name))[0] || name.toLowerCase();
|
||||
|
||||
const model = (plugin ? _.get(strapi.plugins, [target, 'models', name]) : _.get(strapi.api, [target, 'models', name])) || {};
|
||||
|
||||
// Only select configurable attributes.
|
||||
const attributesConfigurable = attributes.filter(attribute => _.get(model, ['attributes', attribute.name, 'configurable'], true) !== false);
|
||||
const attributesNotConfigurable = Object.keys(model.attributes || {})
|
||||
.filter(attribute => _.get(model, ['attributes', attribute, 'configurable'], true) === false)
|
||||
.reduce((acc, attribute) => {
|
||||
acc[attribute] = model.attributes[attribute];
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
_.forEach(attributesConfigurable, attribute => {
|
||||
if (_.has(attribute, 'params.type')) {
|
||||
attrs[attribute.name] = attribute.params;
|
||||
} else if (_.has(attribute, 'params.target')) {
|
||||
@ -158,6 +162,7 @@ module.exports = {
|
||||
|
||||
attr.via = relation.key;
|
||||
attr.dominant = relation.dominant;
|
||||
attr.plugin = relation.pluginValue;
|
||||
|
||||
attrs[attribute.name] = attr;
|
||||
}
|
||||
@ -172,171 +177,166 @@ module.exports = {
|
||||
}
|
||||
});
|
||||
|
||||
return [attrs, errors];
|
||||
Object.assign(attributesNotConfigurable, attrs);
|
||||
|
||||
return [attributesNotConfigurable, errors];
|
||||
},
|
||||
|
||||
clearRelations: model => {
|
||||
model = _.toLower(model);
|
||||
|
||||
clearRelations: (model, source) => {
|
||||
const errors = [];
|
||||
const apiPath = path.join(strapi.config.appPath, 'api');
|
||||
const structure = {
|
||||
models: strapi.models,
|
||||
plugins: Object.keys(strapi.plugins).reduce((acc, current) => {
|
||||
acc[current] = {
|
||||
models: strapi.plugins[current].models
|
||||
};
|
||||
|
||||
try {
|
||||
const apis = fs.readdirSync(apiPath).filter(x => x[0] !== '.');
|
||||
return acc;
|
||||
}, {})
|
||||
};
|
||||
|
||||
_.forEach(apis, api => {
|
||||
const modelsPath = path.join(apiPath, api, 'models');
|
||||
// Method to delete the association of the models.
|
||||
const deleteAssociations = (models, plugin) => {
|
||||
Object.keys(models).forEach(name => {
|
||||
const relationsToDelete = _.get(plugin ? strapi.plugins[plugin].models[name] : strapi.models[name], 'associations', []).filter(association => {
|
||||
if (source) {
|
||||
return association[association.type] === model && association.plugin === source;
|
||||
}
|
||||
|
||||
try {
|
||||
const models = fs.readdirSync(modelsPath).filter(x => x[0] !== '.');
|
||||
return association[association.type] === model;
|
||||
});
|
||||
|
||||
_.forEach(models, modelPath => {
|
||||
if (_.endsWith(modelPath, '.settings.json')) {
|
||||
const modelObject = strapi.models[_.lowerCase(_.first(modelPath.split('.')))];
|
||||
if (!_.isEmpty(relationsToDelete)) {
|
||||
// Retrieve where is located the model.
|
||||
const target = Object.keys((plugin ? strapi.plugins : strapi.api) || {})
|
||||
.filter(x => _.includes(Object.keys((plugin ? strapi.plugins : strapi.api)[x].models), name))[0];
|
||||
|
||||
const relationsToDelete = _.filter(_.get(modelObject, 'associations', []), association => {
|
||||
return association[association.type] === model;
|
||||
});
|
||||
// Retrieve the filename of the model.
|
||||
const filename = fs.readdirSync(plugin ? path.join(strapi.config.appPath, 'plugins', target, 'models') : path.join(strapi.config.appPath, 'api', target, 'models'))
|
||||
.filter(x => x[0] !== '.')
|
||||
.filter(x => x.split('.settings.json')[0].toLowerCase() === name)[0];
|
||||
|
||||
const modelFilePath = path.join(modelsPath, modelPath);
|
||||
// Path to access to the model.
|
||||
const pathToModel = plugin ?
|
||||
path.resolve(strapi.config.appPath, 'plugins', target, 'models', filename):
|
||||
path.resolve(strapi.config.appPath, 'api', target, 'models', filename);
|
||||
|
||||
try {
|
||||
const modelJSON = require(modelFilePath);
|
||||
// Require the model.
|
||||
const modelJSON = require(pathToModel);
|
||||
|
||||
_.forEach(relationsToDelete, relation => {
|
||||
modelJSON.attributes[relation.alias] = undefined;
|
||||
});
|
||||
_.forEach(relationsToDelete, relation => {
|
||||
modelJSON.attributes[relation.alias] = undefined;
|
||||
});
|
||||
|
||||
try {
|
||||
fs.writeFileSync(modelFilePath, JSON.stringify(modelJSON, null, 2), 'utf8');
|
||||
} catch (e) {
|
||||
errors.push({
|
||||
id: 'request.error.model.write',
|
||||
params: {
|
||||
filePath: modelFilePath
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
errors.push({
|
||||
id: 'request.error.model.read',
|
||||
params: {
|
||||
filePath: modelFilePath
|
||||
}
|
||||
});
|
||||
try {
|
||||
fs.writeFileSync(pathToModel, JSON.stringify(modelJSON, null, 2), 'utf8');
|
||||
} catch (e) {
|
||||
errors.push({
|
||||
id: 'request.error.model.write',
|
||||
params: {
|
||||
filePath: pathToModel
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
errors.push({
|
||||
id: 'request.error.folder.read',
|
||||
params: {
|
||||
folderPath: modelsPath
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
errors.push({
|
||||
id: 'request.error.folder.read',
|
||||
params: {
|
||||
folderPath: apiPath
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Update `./api` models.
|
||||
deleteAssociations(structure.models);
|
||||
|
||||
Object.keys(structure.plugins).forEach(name => {
|
||||
// Update `./plugins/${name}` models.
|
||||
deleteAssociations(structure.plugins[name].models, name);
|
||||
});
|
||||
|
||||
return errors;
|
||||
},
|
||||
|
||||
createRelations: (model, attributes) => {
|
||||
model = _.toLower(model);
|
||||
|
||||
createRelations: (model, attributes, source) => {
|
||||
const errors = [];
|
||||
const apiPath = path.join(strapi.config.appPath, 'api');
|
||||
const structure = {
|
||||
models: strapi.models,
|
||||
plugins: Object.keys(strapi.plugins).reduce((acc, current) => {
|
||||
acc[current] = {
|
||||
models: strapi.plugins[current].models
|
||||
};
|
||||
|
||||
try {
|
||||
const apis = fs.readdirSync(apiPath).filter(x => x[0] !== '.');
|
||||
return acc;
|
||||
}, {})
|
||||
};
|
||||
|
||||
_.forEach(apis, api => {
|
||||
const modelsPath = path.join(apiPath, api, 'models');
|
||||
// Method to update the model
|
||||
const update = (models, plugin) => {
|
||||
Object.keys(models).forEach(name => {
|
||||
const relationsToCreate = attributes.filter(attribute => {
|
||||
if (plugin) {
|
||||
return _.get(attribute, 'params.target') === name && _.get(attribute, 'params.pluginValue') === plugin;
|
||||
}
|
||||
|
||||
try {
|
||||
const models = fs.readdirSync(modelsPath).filter(x => x[0] !== '.');
|
||||
return _.get(attribute, 'params.target') === name && _.isEmpty(_.get(attribute, 'params.pluginValue', ''));
|
||||
});
|
||||
|
||||
_.forEach(models, modelPath => {
|
||||
if (_.endsWith(modelPath, '.settings.json')) {
|
||||
const modelName = _.lowerCase(_.first(modelPath.split('.')));
|
||||
if (!_.isEmpty(relationsToCreate)) {
|
||||
// Retrieve where is located the model.
|
||||
const target = Object.keys((plugin ? strapi.plugins : strapi.api) || {})
|
||||
.filter(x => _.includes(Object.keys((plugin ? strapi.plugins : strapi.api)[x].models), name))[0];
|
||||
|
||||
const relationsToCreate = _.filter(attributes, attribute => {
|
||||
return _.get(attribute, 'params.target') === modelName;
|
||||
});
|
||||
// Retrieve the filename of the model.
|
||||
const filename = fs.readdirSync(plugin ? path.join(strapi.config.appPath, 'plugins', target, 'models') : path.join(strapi.config.appPath, 'api', target, 'models'))
|
||||
.filter(x => x[0] !== '.')
|
||||
.filter(x => x.split('.settings.json')[0].toLowerCase() === name)[0];
|
||||
|
||||
if (!_.isEmpty(relationsToCreate)) {
|
||||
const modelFilePath = path.join(modelsPath, modelPath);
|
||||
// Path to access to the model.
|
||||
const pathToModel = plugin ?
|
||||
path.resolve(strapi.config.appPath, 'plugins', target, 'models', filename):
|
||||
path.resolve(strapi.config.appPath, 'api', target, 'models', filename);
|
||||
|
||||
try {
|
||||
const modelJSON = require(modelFilePath);
|
||||
const modelJSON = require(pathToModel);
|
||||
|
||||
_.forEach(relationsToCreate, ({ name, params }) => {
|
||||
const attr = {
|
||||
columnName: params.targetColumnName,
|
||||
};
|
||||
_.forEach(relationsToCreate, ({ name, params }) => {
|
||||
const attr = {};
|
||||
|
||||
switch (params.nature) {
|
||||
case 'oneToOne':
|
||||
case 'oneToMany':
|
||||
attr.model = model;
|
||||
break;
|
||||
case 'manyToOne':
|
||||
case 'manyToMany':
|
||||
attr.collection = model;
|
||||
break;
|
||||
default:
|
||||
}
|
||||
switch (params.nature) {
|
||||
case 'oneToOne':
|
||||
case 'oneToMany':
|
||||
attr.model = model.toLowerCase();
|
||||
break;
|
||||
case 'manyToOne':
|
||||
case 'manyToMany':
|
||||
attr.collection = model.toLowerCase();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
attr.via = name;
|
||||
attr.via = name;
|
||||
attr.columnName = params.targetColumnName;
|
||||
attr.plugin = source;
|
||||
|
||||
modelJSON.attributes[params.key] = attr;
|
||||
modelJSON.attributes[params.key] = attr;
|
||||
|
||||
try {
|
||||
fs.writeFileSync(modelFilePath, JSON.stringify(modelJSON, null, 2), 'utf8');
|
||||
} catch (e) {
|
||||
errors.push({
|
||||
id: 'request.error.model.write',
|
||||
params: {
|
||||
filePath: modelFilePath
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
errors.push({
|
||||
id: 'request.error.model.read',
|
||||
params: {
|
||||
filePath: modelFilePath
|
||||
}
|
||||
});
|
||||
try {
|
||||
fs.writeFileSync(pathToModel, JSON.stringify(modelJSON, null, 2), 'utf8');
|
||||
} catch (e) {
|
||||
errors.push({
|
||||
id: 'request.error.model.write',
|
||||
params: {
|
||||
filePath: pathToModel
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
errors.push({
|
||||
id: 'request.error.folder.read',
|
||||
params: {
|
||||
folderPath: modelsPath
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
errors.push({
|
||||
id: 'request.error.folder.read',
|
||||
params: {
|
||||
folderPath: apiPath
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Update `./api` models.
|
||||
update(structure.models);
|
||||
|
||||
Object.keys(structure.plugins).forEach(name => {
|
||||
// Update `./plugins/${name}` models.
|
||||
update(structure.plugins[name].models, name);
|
||||
});
|
||||
|
||||
return errors;
|
||||
},
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-plugin-email",
|
||||
"version": "3.0.0-alpha.7.2",
|
||||
"version": "3.0.0-alpha.7.3",
|
||||
"description": "This is the description of the plugin.",
|
||||
"strapi": {
|
||||
"name": "Email",
|
||||
@ -27,7 +27,7 @@
|
||||
"sendmail": "^1.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"strapi-helper-plugin": "3.0.0-alpha.7.2"
|
||||
"strapi-helper-plugin": "3.0.0-alpha.7.3"
|
||||
},
|
||||
"author": {
|
||||
"name": "A Strapi developer",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-plugin-settings-manager",
|
||||
"version": "3.0.0-alpha.7.2",
|
||||
"version": "3.0.0-alpha.7.3",
|
||||
"description": "Strapi plugin to manage settings.",
|
||||
"strapi": {
|
||||
"name": "Settings Manager",
|
||||
@ -26,7 +26,7 @@
|
||||
"devDependencies": {
|
||||
"flag-icon-css": "^2.8.0",
|
||||
"react-select": "^1.0.0-rc.5",
|
||||
"strapi-helper-plugin": "3.0.0-alpha.7.2"
|
||||
"strapi-helper-plugin": "3.0.0-alpha.7.3"
|
||||
},
|
||||
"author": {
|
||||
"name": "Strapi team",
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { findIndex, has, includes, isEmpty, map, toLower } from 'lodash';
|
||||
import { findIndex, has, includes, isEmpty, map, toLower, upperFirst } from 'lodash';
|
||||
import cn from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
@ -77,7 +77,7 @@ class InputSearch extends React.Component { // eslint-disable-line react/prefer-
|
||||
return (
|
||||
<div className={cn(styles.inputSearch, 'col-md-6')}>
|
||||
<label htmlFor={this.props.name}>
|
||||
<FormattedMessage id={this.props.label} values={this.props.labelValues} />
|
||||
<FormattedMessage id={this.props.label} values={upperFirst(this.props.labelValues)} />
|
||||
</label>
|
||||
<div className={cn('input-group')}>
|
||||
<span className={cn('input-group-addon', styles.addon)} />
|
||||
|
||||
@ -38,7 +38,6 @@
|
||||
label {
|
||||
margin-bottom: 0;
|
||||
font-weight: 500;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
input {
|
||||
|
||||
@ -8,25 +8,35 @@
|
||||
"username": {
|
||||
"type": "string",
|
||||
"minLength": 3,
|
||||
"unique": true
|
||||
"unique": true,
|
||||
"configurable": false,
|
||||
"required": true
|
||||
},
|
||||
"email": {
|
||||
"type": "email",
|
||||
"minLength": 6,
|
||||
"unique": true
|
||||
"unique": true,
|
||||
"configurable": false,
|
||||
"required": true
|
||||
},
|
||||
"provider": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"configurable": false
|
||||
},
|
||||
"password": {
|
||||
"type": "password",
|
||||
"minLength": 6
|
||||
"minLength": 6,
|
||||
"configurable": false,
|
||||
"required": true
|
||||
},
|
||||
"resetPasswordToken": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"configurable": false
|
||||
},
|
||||
"role": {
|
||||
"type": "integer"
|
||||
"type": "integer",
|
||||
"configurable": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"connection": "default"
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-plugin-users-permissions",
|
||||
"version": "3.0.0-alpha.7.2",
|
||||
"version": "3.0.0-alpha.7.3",
|
||||
"description": "This is the description of the plugin.",
|
||||
"strapi": {
|
||||
"name": "Auth & Permissions",
|
||||
@ -29,7 +29,7 @@
|
||||
"uuid": "^3.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"strapi-helper-plugin": "3.0.0-alpha.7.2"
|
||||
"strapi-helper-plugin": "3.0.0-alpha.7.3"
|
||||
},
|
||||
"author": {
|
||||
"name": "Strapi team",
|
||||
|
||||
@ -50,7 +50,7 @@ module.exports = {
|
||||
process.env.JWT_SECRET || _.get(strapi.plugins['users-permissions'], 'config.jwtSecret') || 'oursecret',
|
||||
{},
|
||||
function (err, user) {
|
||||
if (err || !user || !user.id) {
|
||||
if (err || !user || !_.get(user, 'id', '').toString()) {
|
||||
return reject('Invalid token.');
|
||||
}
|
||||
resolve(user);
|
||||
|
||||
@ -30,6 +30,13 @@ module.exports = {
|
||||
values.role = '1';
|
||||
}
|
||||
|
||||
// Use Content Manager business logic to handle relation.
|
||||
if (strapi.plugins['content-manager']) {
|
||||
return await strapi.plugins['content-manager'].services['contentmanager'].add({
|
||||
model: 'user'
|
||||
}, values, 'users-permissions');
|
||||
}
|
||||
|
||||
return strapi.query('user', 'users-permissions').create(values);
|
||||
},
|
||||
|
||||
@ -47,6 +54,11 @@ module.exports = {
|
||||
values.password = await strapi.plugins['users-permissions'].services.user.hashPassword(values);
|
||||
}
|
||||
|
||||
// Use Content Manager business logic to handle relation.
|
||||
if (strapi.plugins['content-manager']) {
|
||||
return await strapi.plugins['content-manager'].services['contentmanager'].edit(params, values, 'users-permissions');
|
||||
}
|
||||
|
||||
return strapi.query('user', 'users-permissions').update(_.assign(params, values));
|
||||
},
|
||||
|
||||
@ -97,6 +109,11 @@ module.exports = {
|
||||
*/
|
||||
|
||||
remove: async params => {
|
||||
// Use Content Manager business logic to handle relation.
|
||||
if (strapi.plugins['content-manager']) {
|
||||
await strapi.plugins['content-manager'].services['contentmanager'].delete(params, 'users-permissions');
|
||||
}
|
||||
|
||||
return strapi.query('user', 'users-permissions').delete(params);
|
||||
},
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-redis",
|
||||
"version": "3.0.0-alpha.7.2",
|
||||
"version": "3.0.0-alpha.7.3",
|
||||
"description": "Redis hook for the Strapi framework",
|
||||
"homepage": "http://strapi.io",
|
||||
"keywords": [
|
||||
@ -18,7 +18,7 @@
|
||||
"ioredis": "^3.1.2",
|
||||
"lodash": "^4.17.4",
|
||||
"stack-trace": "0.0.10",
|
||||
"strapi-utils": "3.0.0-alpha.7.2"
|
||||
"strapi-utils": "3.0.0-alpha.7.3"
|
||||
},
|
||||
"strapi": {
|
||||
"isHook": true
|
||||
|
||||
@ -73,138 +73,143 @@ module.exports = {
|
||||
*/
|
||||
|
||||
getNature: (association, key, models, currentModelName) => {
|
||||
const types = {
|
||||
current: '',
|
||||
other: ''
|
||||
};
|
||||
try {
|
||||
const types = {
|
||||
current: '',
|
||||
other: ''
|
||||
};
|
||||
|
||||
if (_.isUndefined(models)) {
|
||||
models = global['strapi'].models;
|
||||
}
|
||||
|
||||
if (association.hasOwnProperty('via') && association.hasOwnProperty('collection')) {
|
||||
const relatedAttribute = models[association.collection].attributes[association.via];
|
||||
|
||||
types.current = 'collection';
|
||||
|
||||
if (relatedAttribute.hasOwnProperty('collection') && relatedAttribute.hasOwnProperty('via')) {
|
||||
types.other = 'collection';
|
||||
} else if (relatedAttribute.hasOwnProperty('collection') && !relatedAttribute.hasOwnProperty('via')) {
|
||||
types.other = 'collectionD';
|
||||
} else if (relatedAttribute.hasOwnProperty('model')) {
|
||||
types.other = 'model';
|
||||
if (_.isUndefined(models)) {
|
||||
models = association.plugin ? strapi.plugins[association.plugin].models : strapi.models;
|
||||
}
|
||||
} else if (association.hasOwnProperty('via') && association.hasOwnProperty('model')) {
|
||||
types.current = 'modelD';
|
||||
|
||||
// We have to find if they are a model linked to this key
|
||||
_.forIn(_.omit(models, currentModelName || ''), model => {
|
||||
_.forIn(model.attributes, attribute => {
|
||||
if (attribute.hasOwnProperty('via') && attribute.via === key && attribute.hasOwnProperty('collection')) {
|
||||
types.other = 'collection';
|
||||
if (association.hasOwnProperty('via') && association.hasOwnProperty('collection')) {
|
||||
const relatedAttribute = models[association.collection].attributes[association.via];
|
||||
|
||||
// Break loop
|
||||
return false;
|
||||
} else if (attribute.hasOwnProperty('model')) {
|
||||
types.other = 'model';
|
||||
types.current = 'collection';
|
||||
|
||||
// Break loop
|
||||
return false;
|
||||
}
|
||||
});
|
||||
});
|
||||
} else if (association.hasOwnProperty('model')) {
|
||||
types.current = 'model';
|
||||
if (relatedAttribute.hasOwnProperty('collection') && relatedAttribute.hasOwnProperty('via')) {
|
||||
types.other = 'collection';
|
||||
} else if (relatedAttribute.hasOwnProperty('collection') && !relatedAttribute.hasOwnProperty('via')) {
|
||||
types.other = 'collectionD';
|
||||
} else if (relatedAttribute.hasOwnProperty('model')) {
|
||||
types.other = 'model';
|
||||
}
|
||||
} else if (association.hasOwnProperty('via') && association.hasOwnProperty('model')) {
|
||||
types.current = 'modelD';
|
||||
|
||||
// We have to find if they are a model linked to this key
|
||||
_.forIn(models, model => {
|
||||
_.forIn(model.attributes, attribute => {
|
||||
if (attribute.hasOwnProperty('via') && attribute.via === key) {
|
||||
if (attribute.hasOwnProperty('collection')) {
|
||||
// We have to find if they are a model linked to this key
|
||||
_.forIn(_.omit(models, currentModelName || ''), model => {
|
||||
_.forIn(model.attributes, attribute => {
|
||||
if (attribute.hasOwnProperty('via') && attribute.via === key && attribute.hasOwnProperty('collection')) {
|
||||
types.other = 'collection';
|
||||
|
||||
// Break loop
|
||||
return false;
|
||||
} else if (attribute.hasOwnProperty('model')) {
|
||||
types.other = 'modelD';
|
||||
types.other = 'model';
|
||||
|
||||
// Break loop
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
} else if (association.hasOwnProperty('collection')) {
|
||||
types.current = 'collectionD';
|
||||
} else if (association.hasOwnProperty('model')) {
|
||||
types.current = 'model';
|
||||
|
||||
// We have to find if they are a model linked to this key
|
||||
_.forIn(models, model => {
|
||||
_.forIn(model.attributes, attribute => {
|
||||
if (attribute.hasOwnProperty('via') && attribute.via === key) {
|
||||
if (attribute.hasOwnProperty('collection')) {
|
||||
types.other = 'collection';
|
||||
// We have to find if they are a model linked to this key
|
||||
_.forIn(models, model => {
|
||||
_.forIn(model.attributes, attribute => {
|
||||
if (attribute.hasOwnProperty('via') && attribute.via === key) {
|
||||
if (attribute.hasOwnProperty('collection')) {
|
||||
types.other = 'collection';
|
||||
|
||||
// Break loop
|
||||
return false;
|
||||
} else if (attribute.hasOwnProperty('model')) {
|
||||
types.other = 'modelD';
|
||||
// Break loop
|
||||
return false;
|
||||
} else if (attribute.hasOwnProperty('model')) {
|
||||
types.other = 'modelD';
|
||||
|
||||
// Break loop
|
||||
return false;
|
||||
// Break loop
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
} else if (association.hasOwnProperty('collection')) {
|
||||
types.current = 'collectionD';
|
||||
|
||||
if (types.current === 'modelD' && types.other === 'model') {
|
||||
return {
|
||||
nature: 'oneToOne',
|
||||
verbose: 'belongsTo'
|
||||
};
|
||||
} else if (types.current === 'model' && types.other === 'modelD') {
|
||||
return {
|
||||
nature: 'oneToOne',
|
||||
verbose: 'hasOne'
|
||||
};
|
||||
} else if ((types.current === 'model' || types.current === 'modelD') && types.other === 'collection') {
|
||||
return {
|
||||
nature: 'manyToOne',
|
||||
verbose: 'belongsTo'
|
||||
};
|
||||
} else if (types.current === 'modelD' && types.other === 'collection') {
|
||||
return {
|
||||
nature: 'oneToMany',
|
||||
verbose: 'hasMany'
|
||||
};
|
||||
} else if (types.current === 'collection' && types.other === 'model') {
|
||||
return {
|
||||
nature: 'oneToMany',
|
||||
verbose: 'hasMany'
|
||||
};
|
||||
} else if (types.current === 'collection' && types.other === 'collection') {
|
||||
return {
|
||||
nature: 'manyToMany',
|
||||
verbose: 'belongsToMany'
|
||||
};
|
||||
} else if (types.current === 'collectionD' && types.other === 'collection' || types.current === 'collection' && types.other === 'collectionD') {
|
||||
return {
|
||||
nature: 'manyToMany',
|
||||
verbose: 'belongsToMany'
|
||||
};
|
||||
} else if (types.current === 'collectionD' && types.other === '') {
|
||||
return {
|
||||
nature: 'manyWay',
|
||||
verbose: 'belongsToMany'
|
||||
};
|
||||
} else if (types.current === 'model' && types.other === '') {
|
||||
return {
|
||||
nature: 'oneWay',
|
||||
verbose: 'belongsTo'
|
||||
};
|
||||
}
|
||||
// We have to find if they are a model linked to this key
|
||||
_.forIn(models, model => {
|
||||
_.forIn(model.attributes, attribute => {
|
||||
if (attribute.hasOwnProperty('via') && attribute.via === key) {
|
||||
if (attribute.hasOwnProperty('collection')) {
|
||||
types.other = 'collection';
|
||||
|
||||
return undefined;
|
||||
// Break loop
|
||||
return false;
|
||||
} else if (attribute.hasOwnProperty('model')) {
|
||||
types.other = 'modelD';
|
||||
|
||||
// Break loop
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (types.current === 'modelD' && types.other === 'model') {
|
||||
return {
|
||||
nature: 'oneToOne',
|
||||
verbose: 'belongsTo'
|
||||
};
|
||||
} else if (types.current === 'model' && types.other === 'modelD') {
|
||||
return {
|
||||
nature: 'oneToOne',
|
||||
verbose: 'hasOne'
|
||||
};
|
||||
} else if ((types.current === 'model' || types.current === 'modelD') && types.other === 'collection') {
|
||||
return {
|
||||
nature: 'manyToOne',
|
||||
verbose: 'belongsTo'
|
||||
};
|
||||
} else if (types.current === 'modelD' && types.other === 'collection') {
|
||||
return {
|
||||
nature: 'oneToMany',
|
||||
verbose: 'hasMany'
|
||||
};
|
||||
} else if (types.current === 'collection' && types.other === 'model') {
|
||||
return {
|
||||
nature: 'oneToMany',
|
||||
verbose: 'hasMany'
|
||||
};
|
||||
} else if (types.current === 'collection' && types.other === 'collection') {
|
||||
return {
|
||||
nature: 'manyToMany',
|
||||
verbose: 'belongsToMany'
|
||||
};
|
||||
} else if (types.current === 'collectionD' && types.other === 'collection' || types.current === 'collection' && types.other === 'collectionD') {
|
||||
return {
|
||||
nature: 'manyToMany',
|
||||
verbose: 'belongsToMany'
|
||||
};
|
||||
} else if (types.current === 'collectionD' && types.other === '') {
|
||||
return {
|
||||
nature: 'manyWay',
|
||||
verbose: 'belongsToMany'
|
||||
};
|
||||
} else if (types.current === 'model' && types.other === '') {
|
||||
return {
|
||||
nature: 'oneWay',
|
||||
verbose: 'belongsTo'
|
||||
};
|
||||
}
|
||||
|
||||
return undefined;
|
||||
} catch (e) {
|
||||
strapi.log.error(`Something went wrong in the model \`${_.upperFirst(currentModelName)}\` with the attribute \`${key}\``);
|
||||
strapi.stop();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -220,41 +225,48 @@ module.exports = {
|
||||
*/
|
||||
|
||||
defineAssociations: function (model, definition, association, key) {
|
||||
// Initialize associations object
|
||||
if (definition.associations === undefined) {
|
||||
definition.associations = [];
|
||||
}
|
||||
try {
|
||||
// Initialize associations object
|
||||
if (definition.associations === undefined) {
|
||||
definition.associations = [];
|
||||
}
|
||||
|
||||
// Exclude non-relational attribute
|
||||
if (!association.hasOwnProperty('collection') && !association.hasOwnProperty('model')) {
|
||||
return undefined;
|
||||
}
|
||||
// Exclude non-relational attribute
|
||||
if (!association.hasOwnProperty('collection') && !association.hasOwnProperty('model')) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Get relation nature
|
||||
const infos = this.getNature(association, key, undefined, model.toLowerCase());
|
||||
const details = _.get(strapi.models, `${association.model || association.collection}.attributes.${association.via}`, {});
|
||||
// Get relation nature
|
||||
const infos = this.getNature(association, key, undefined, model.toLowerCase());
|
||||
const details = _.get(strapi.models, `${association.model || association.collection}.attributes.${association.via}`, {});
|
||||
|
||||
// Build associations object
|
||||
if (association.hasOwnProperty('collection')) {
|
||||
definition.associations.push({
|
||||
alias: key,
|
||||
type: 'collection',
|
||||
collection: association.collection,
|
||||
via: association.via || undefined,
|
||||
nature: infos.nature,
|
||||
autoPopulate: _.get(association, 'autoPopulate', true),
|
||||
dominant: details.dominant !== true
|
||||
});
|
||||
} else if (association.hasOwnProperty('model')) {
|
||||
definition.associations.push({
|
||||
alias: key,
|
||||
type: 'model',
|
||||
model: association.model,
|
||||
via: association.via || undefined,
|
||||
nature: infos.nature,
|
||||
autoPopulate: _.get(association, 'autoPopulate', true),
|
||||
dominant: details.dominant !== true
|
||||
});
|
||||
// Build associations object
|
||||
if (association.hasOwnProperty('collection')) {
|
||||
definition.associations.push({
|
||||
alias: key,
|
||||
type: 'collection',
|
||||
collection: association.collection,
|
||||
via: association.via || undefined,
|
||||
nature: infos.nature,
|
||||
autoPopulate: _.get(association, 'autoPopulate', true),
|
||||
dominant: details.dominant !== true,
|
||||
plugin: association.plugin || undefined,
|
||||
});
|
||||
} else if (association.hasOwnProperty('model')) {
|
||||
definition.associations.push({
|
||||
alias: key,
|
||||
type: 'model',
|
||||
model: association.model,
|
||||
via: association.via || undefined,
|
||||
nature: infos.nature,
|
||||
autoPopulate: _.get(association, 'autoPopulate', true),
|
||||
dominant: details.dominant !== true,
|
||||
plugin: association.plugin || undefined,
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
strapi.log.error(`Something went wrong in the model \`${_.upperFirst(model)}\` with the attribute \`${key}\``);
|
||||
strapi.stop();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi-utils",
|
||||
"version": "3.0.0-alpha.7.2",
|
||||
"version": "3.0.0-alpha.7.3",
|
||||
"description": "Shared utilities for the Strapi packages",
|
||||
"homepage": "http://strapi.io",
|
||||
"keywords": [
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
const path = require('path');
|
||||
const glob = require('glob');
|
||||
const utils = require('../utils');
|
||||
const {merge, setWith, get, upperFirst, isString, isEmpty, isObject, pullAll, defaults, isPlainObject, forEach, assign, clone, cloneDeep} = require('lodash');
|
||||
const {merge, setWith, get, upperFirst, isString, isEmpty, isObject, pullAll, defaults, isPlainObject, forEach, assign, clone, cloneDeep, camelCase} = require('lodash');
|
||||
|
||||
module.exports.nested = function() {
|
||||
return Promise.all([
|
||||
@ -112,13 +112,17 @@ module.exports.app = async function() {
|
||||
this.models = Object.keys(this.api || []).reduce((acc, key) => {
|
||||
for (let index in this.api[key].models) {
|
||||
if (!this.api[key].models[index].globalId) {
|
||||
this.api[key].models[index].globalId = upperFirst(index);
|
||||
this.api[key].models[index].globalId = upperFirst(camelCase(index));
|
||||
}
|
||||
|
||||
if (!this.api[key].models[index].connection) {
|
||||
this.api[key].models[index].connection = this.config.currentEnvironment.database.defaultConnection;
|
||||
}
|
||||
|
||||
if (!this.api[key].models[index].collectionName) {
|
||||
this.api[key].models[index].collectionName = (`${index}`).toLowerCase();
|
||||
}
|
||||
|
||||
acc[index] = this.api[key].models[index];
|
||||
}
|
||||
return acc;
|
||||
@ -155,8 +159,12 @@ module.exports.app = async function() {
|
||||
this.admin.models[key].identity = upperFirst(key);
|
||||
}
|
||||
|
||||
if (!this.admin.models[key].identity) {
|
||||
this.admin.models[key].identity = this.config.currentEnvironment.database.defaultConnection;
|
||||
if (!this.admin.models[key].globalId) {
|
||||
this.admin.models[key].globalId = upperFirst(camelCase(`admin-${key}`));
|
||||
}
|
||||
|
||||
if (!this.admin.models[key].connection) {
|
||||
this.admin.models[key].connection = this.config.currentEnvironment.database.defaultConnection;
|
||||
}
|
||||
|
||||
acc[key] = this.admin.models[key];
|
||||
@ -180,6 +188,18 @@ module.exports.app = async function() {
|
||||
this.plugins[key].models[index].connection = this.config.currentEnvironment.database.defaultConnection;
|
||||
}
|
||||
|
||||
if (!this.plugins[key].models[index].globalId) {
|
||||
this.plugins[key].models[index].globalId = this.models[index] ?
|
||||
upperFirst(camelCase(`${key}-${index}`)):
|
||||
upperFirst(camelCase(`${index}`));
|
||||
}
|
||||
|
||||
if (!this.plugins[key].models[index].collectionName) {
|
||||
this.plugins[key].models[index].collectionName = this.models[index] ?
|
||||
(`${key}_${index}`).toLowerCase():
|
||||
(`${index}`).toLowerCase();
|
||||
}
|
||||
|
||||
sum[index] = this.plugins[key].models[index];
|
||||
|
||||
return sum;
|
||||
|
||||
@ -9,5 +9,9 @@ global.startedAt = Date.now();
|
||||
*/
|
||||
|
||||
module.exports = function(global) {
|
||||
return global.strapi = require('./Strapi'); // Strapi instance instanciated
|
||||
try {
|
||||
return global.strapi = require('./Strapi'); // Strapi instance instanciated
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}.call(this, global);
|
||||
|
||||
@ -4,8 +4,7 @@
|
||||
"origin": true,
|
||||
"expose": [
|
||||
"WWW-Authenticate",
|
||||
"Server-Authorization",
|
||||
"X-Forwarded-Host"
|
||||
"Server-Authorization"
|
||||
],
|
||||
"maxAge": 31536000,
|
||||
"credentials": true,
|
||||
|
||||
@ -9,7 +9,7 @@ module.exports = async function() {
|
||||
// Set if is admin destination for middleware application.
|
||||
this.app.use(async (ctx, next) => {
|
||||
if (ctx.request.header['origin'] === 'http://localhost:4000') {
|
||||
ctx.request.header['x-forwarded-host'] = 'strapi';
|
||||
ctx.request.header['x-forwarded-host'] = 'strapi';
|
||||
}
|
||||
|
||||
ctx.request.admin = ctx.request.header['x-forwarded-host'] === 'strapi';
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strapi",
|
||||
"version": "3.0.0-alpha.7.2",
|
||||
"version": "3.0.0-alpha.7.3",
|
||||
"description": "An open source solution to create and manage your own API. It provides a powerful dashboard and features to make your life easier.",
|
||||
"homepage": "http://strapi.io",
|
||||
"keywords": [
|
||||
@ -55,14 +55,14 @@
|
||||
"rimraf": "^2.6.2",
|
||||
"semver": "^5.4.1",
|
||||
"stack-trace": "0.0.10",
|
||||
"strapi-generate": "3.0.0-alpha.7.2",
|
||||
"strapi-generate-admin": "3.0.0-alpha.7.2",
|
||||
"strapi-generate-api": "3.0.0-alpha.7.2",
|
||||
"strapi-generate-new": "3.0.0-alpha.7.2",
|
||||
"strapi-generate-plugin": "3.0.0-alpha.7.2",
|
||||
"strapi-generate-policy": "3.0.0-alpha.7.2",
|
||||
"strapi-generate-service": "3.0.0-alpha.7.2",
|
||||
"strapi-utils": "3.0.0-alpha.7.2"
|
||||
"strapi-generate": "3.0.0-alpha.7.3",
|
||||
"strapi-generate-admin": "3.0.0-alpha.7.3",
|
||||
"strapi-generate-api": "3.0.0-alpha.7.3",
|
||||
"strapi-generate-new": "3.0.0-alpha.7.3",
|
||||
"strapi-generate-plugin": "3.0.0-alpha.7.3",
|
||||
"strapi-generate-policy": "3.0.0-alpha.7.3",
|
||||
"strapi-generate-service": "3.0.0-alpha.7.3",
|
||||
"strapi-utils": "3.0.0-alpha.7.3"
|
||||
},
|
||||
"author": {
|
||||
"email": "hi@strapi.io",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user