Socket
Socket
Sign inDemoInstall

strapi-plugin-content-manager

Package Overview
Dependencies
Maintainers
1
Versions
276
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

strapi-plugin-content-manager - npm Package Compare versions

Comparing version 3.0.0-alpha.10.3 to 3.0.0-alpha.11

admin/build/53771798877e88bccc275e15ba634a83.svg

19

admin/src/components/SelectMany/index.js

@@ -40,3 +40,5 @@ /**

// Request URL
const requestUrlSuffix = query && this.props.record.get(this.props.relation.alias).toJS() ? this.props.record.get(this.props.relation.alias).toJS() : '';
const requestUrlSuffix = query && this.props.record.get(this.props.relation.alias) ? this.props.record.get(this.props.relation.alias) : '';
// NOTE: keep this line if we rollback to the old container
// const requestUrlSuffix = query && this.props.record.get(this.props.relation.alias).toJS() ? this.props.record.get(this.props.relation.alias).toJS() : '';
const requestUrl = `/content-manager/explorer/${this.props.relation.model || this.props.relation.collection}/${requestUrlSuffix}`;

@@ -69,4 +71,11 @@

const filteredValue = value.filter((data, index ) => findIndex(value, (o) => o.value.id === data.value.id) === index);
const target = {
name: `record.${this.props.relation.alias}`,
type: 'select',
value: filteredValue,
};
this.props.setRecordAttribute(this.props.relation.alias, filteredValue);
this.props.setRecordAttribute({ target });
// NOTE: keep this line if we rollback to the old container
// this.props.setRecordAttribute(this.props.relation.alias, filteredValue);
}

@@ -79,3 +88,5 @@

const value = this.props.record.get(this.props.relation.alias);
const value = get(this.props.record, this.props.relation.alias);
// NOTE: keep this line if we rollback to the old container
// const value = this.props.record.get(this.props.relation.alias);

@@ -92,3 +103,3 @@ /* eslint-disable jsx-a11y/label-has-for */

multi
value={isNull(value) || isUndefined(value) || value.size === 0 ? null : value.toJS().map(item => {
value={isNull(value) || isUndefined(value) || value.size === 0 ? null : value.map(item => {
if (item) {

@@ -95,0 +106,0 @@ return {

@@ -67,3 +67,11 @@ /**

handleChange = (value) => {
this.props.setRecordAttribute(this.props.relation.alias, value);
const target = {
name: `record.${this.props.relation.alias}`,
value,
type: 'select',
};
this.props.setRecordAttribute({ target });
// NOTE: keep this line if we rollback to the old container
// this.props.setRecordAttribute(this.props.relation.alias, value);
}

@@ -76,3 +84,5 @@

const value = this.props.record.get(this.props.relation.alias);
const value = get(this.props.record, this.props.relation.alias);
// NOTE: keep this line if we rollback to the old container
// const value = this.props.record.get(this.props.relation.alias);

@@ -79,0 +89,0 @@ /* eslint-disable jsx-a11y/label-has-for */

@@ -36,2 +36,3 @@ /**

case 'email':
case 'enumeration':
return (value && !isEmpty(value.toString())) || name === 'id' ? value.toString() : '-';

@@ -38,0 +39,0 @@ case 'float':

@@ -20,7 +20,10 @@ /**

import Home from 'containers/Home';
import Edit from 'containers/Edit';
import List from 'containers/List';
import EditPage from 'containers/EditPage';
import ListPage from 'containers/ListPage';
import EmptyAttributesView from 'components/EmptyAttributesView';
import { emptyStore, getModelEntries, loadModels } from './actions';
import {
emptyStore,
loadModels,
} from './actions';
import { makeSelectLoading, makeSelectModels, makeSelectModelEntries } from './selectors';

@@ -33,18 +36,4 @@

this.props.loadModels();
const modelName = this.props.location.pathname.split('/')[3];
if (modelName) {
this.props.getModelEntries(modelName, getQueryParameters(this.props.location.search, 'source'));
}
}
componentDidUpdate(prevProps) {
const currentModelName = this.props.location.pathname.split('/')[3];
if (prevProps.location.pathname !== this.props.location.pathname && currentModelName) {
this.props.getModelEntries(currentModelName, getQueryParameters(this.props.location.search, 'source'));
}
}
componentWillUnmount() {

@@ -71,4 +60,4 @@ this.props.emptyStore();

<Switch>
<Route path="/plugins/content-manager/:slug/:id" component={Edit} />
<Route path="/plugins/content-manager/:slug" component={List} />
<Route path="/plugins/content-manager/:slug/:id" component={EditPage} />
<Route path="/plugins/content-manager/:slug" component={ListPage} />
<Route path="/plugins/content-manager" component={Home} />

@@ -87,3 +76,2 @@ </Switch>

emptyStore: PropTypes.func.isRequired,
getModelEntries: PropTypes.func.isRequired,
history: PropTypes.object.isRequired,

@@ -104,3 +92,3 @@ loading: PropTypes.bool.isRequired,

emptyStore,
getModelEntries,
// getModelEntries,
loadModels,

@@ -107,0 +95,0 @@ },

@@ -0,3 +1,4 @@

import { LOCATION_CHANGE } from 'react-router-redux';
import { map, omit } from 'lodash';
import { fork, put, select, call, takeLatest } from 'redux-saga/effects';
import { fork, put, select, call, takeLatest, take, cancel } from 'redux-saga/effects';

@@ -71,5 +72,11 @@ import request from 'utils/request';

export function* defaultSaga() {
yield fork(takeLatest, LOAD_MODELS, getModels);
yield fork(takeLatest, LOADED_MODELS, modelsLoaded);
yield fork(takeLatest, GET_MODEL_ENTRIES, modelEntriesGet);
const loadModelsWatcher = yield fork(takeLatest, LOAD_MODELS, getModels);
const loadedModelsWatcher = yield fork(takeLatest, LOADED_MODELS, modelsLoaded);
const loadEntriesWatcher = yield fork(takeLatest, GET_MODEL_ENTRIES, modelEntriesGet);
yield take(LOCATION_CHANGE);
yield cancel(loadModelsWatcher);
yield cancel(loadedModelsWatcher);
yield cancel(loadEntriesWatcher);
}

@@ -76,0 +83,0 @@

@@ -1,1 +0,55 @@

{}
{
"plugin.description.short": "Greife blitzschnell auf alle Daten in der Datenbank zu und manipuliere sie.",
"plugin.description.long": "Greife blitzschnell auf alle Daten in der Datenbank zu und manipuliere sie.",
"containers.Home.pluginHeaderTitle": "Inhalts-Manager",
"containers.Home.introduction": "Um deine Einträge zu verwalten, klicke auf den entsprechenden Link im Menü links. Dieses Plugin ist noch in aktiver Entwicklung und seine Einstellungen können nicht optimal angepasst werden.",
"containers.Home.pluginHeaderDescription": "Verwalte deine Einträge mithilfe eines mächtigen und wunderschönen Interfaces.",
"containers.Edit.submit": "Speichern",
"containers.Edit.editing": "Bearbeite...",
"containers.Edit.delete": "Löschen",
"containers.Edit.reset": "Abbrechen",
"containers.Edit.returnList": "Zu Liste zurückkehren",
"containers.List.addAnEntry": "Füge {entity} hinzu",
"containers.List.pluginHeaderDescription": "{label} Einträge gefunden",
"containers.List.pluginHeaderDescription.singular": "{label} Eintrag gefunden",
"components.LimitSelect.itemsPerPage": "Einträge pro Seite",
"containers.List.errorFetchRecords": "Fehler",
"EditRelations.title": "Relational data",
"emptyAttributes.title": "Es gibt noch keine Felder",
"emptyAttributes.description": "Füge deinem Content-Typen das erste Feld hinzu",
"emptyAttributes.button": "Den Content-Typ-Generator öffnen",
"error.schema.generation": "Bei der Generierung des Schemas ist ein Fehler aufgetreten.",
"error.records.count": "Beim Abruf von count records ist ein Fehler aufgetreten.",
"error.records.fetch": "Beim Abruf von Dokumenten ist ein Fehler aufgetreten.",
"error.record.fetch": "Beim Abruf eines Dokuments ist ein Fehler aufgetreten.",
"error.record.create": "Beim Anlegen eines Dokuments ist ein Fehler aufgetreten.",
"error.record.update": "Beim Aktualisieren eines Dokuments ist ein Fehler aufgetreten.",
"error.record.delete": "Beim Löschen eines Dokuments ist ein Fehler aufgetreten.",
"error.model.fetch": "Beim Abruf von model config fetch ist ein Fehler aufgetreten.",
"error.validation.required": "Dieser Wert ist erforderlich.",
"error.validation.regex": "Dieser Wert entspricht nicht dem RegEx.",
"error.validation.max": "Dieser Wert ist zu hoch.",
"error.validation.min": "Dieser Wert ist zu niedrig.",
"error.validation.maxLength": "Dieser Wert ist zu lang.",
"error.validation.minLength": "Dieser Wert ist zu kurz.",
"error.contentTypeName.taken": "Dieser Name existiert bereits",
"error.attribute.taken": "Dieser Feldname ist bereits vergeben",
"error.attribute.key.taken": "Dieser Wert existiert bereits",
"error.attribute.sameKeyAndName": "Darf nicht gleich sein",
"error.validation.minSupMax": "Darf nicht höher sein",
"notification.error.relationship.fetch": "Beim Abruf von Beziehungen ist ein Fehler aufgetreten.",
"success.record.delete": "Gelöscht",
"success.record.save": "Gespeichert",
"pageNotFound": "Seite nicht gefunden",
"popUpWarning.button.cancel": "Abbrechen",
"popUpWarning.button.confirm": "Bestätigen",
"popUpWarning.title": "Bitte bestätigen",
"popUpWarning.bodyMessage.contentType.delete": "Bist du sicher, dass du diesen Content-Typ löschen willst?"
}

@@ -10,3 +10,3 @@ {

"containers.Edit.delete": "Delete",
"containers.Edit.cancel": "Cancel",
"containers.Edit.reset": "Reset",
"containers.Edit.returnList": "Return to list",

@@ -19,2 +19,4 @@ "containers.List.addAnEntry": "Add New {entity}",

"EditRelations.title": "Relational data",
"emptyAttributes.title": "There are no fields yet",

@@ -21,0 +23,0 @@ "emptyAttributes.description": "Add your first field to your Content Type",

@@ -11,3 +11,3 @@ {

"containers.Edit.delete": "Supprimer",
"containers.Edit.cancel": "Annuler",
"containers.Edit.reset": "Annuler",
"containers.Edit.returnList": "Retourner à la liste",

@@ -20,2 +20,4 @@ "containers.List.addAnEntry": "Ajouter {entity}",

"EditRelations.title": "Données associées",
"emptyAttributes.title": "Il n'y a pas encore de champs",

@@ -22,0 +24,0 @@ "emptyAttributes.description": "Ajoutez votre premier champ a votre modèle",

@@ -79,3 +79,3 @@ import { forEach, isObject, isArray, map, mapKeys, includes, reject, isEmpty, findIndex, isUndefined } from 'lodash';

case 'required':
if (value.length === 0) {
if (validationValue === true && value.length === 0) {
errors.push({ id: 'content-manager.error.validation.required' });

@@ -82,0 +82,0 @@ }

@@ -45,5 +45,5 @@ const _ = require('lodash');

create: async function (params) {
const entry = await this
.forge(Object.keys(params.values).reduce((acc, current) => {
if (this._attributes[current].type) {
// Exclude relationships.
const values = Object.keys(params.values).reduce((acc, current) => {
if (this._attributes[current] && this._attributes[current].type) {
acc[current] = params.values[current];

@@ -53,12 +53,15 @@ }

return acc;
}, {}))
}, {});
const entry = await this
.forge(values)
.save()
.catch((err) => {
if (err.detail) {
const field = _.last(_.words(err.detail.split('=')[0]));
err = { message: `This ${field} is already taken`, field };
}
.catch((err) => {
if (err.detail) {
const field = _.last(_.words(err.detail.split('=')[0]));
err = { message: `This ${field} is already taken`, field };
}
throw err;
});
throw err;
});

@@ -180,2 +183,70 @@ return module.exports.update.call(this, {

break;
case 'manyMorphToMany':
case 'manyMorphToOne':
// Update the relational array.
params.values[current].forEach(obj => {
const model = obj.source && obj.source !== 'content-manager' ?
strapi.plugins[obj.source].models[obj.ref]:
strapi.models[obj.ref];
virtualFields.push(module.exports.addRelationMorph.call(this, {
id: response[this.primaryKey],
alias: association.alias,
ref: model.collectionName,
refId: obj.refId,
field: obj.field
}));
});
break;
case 'oneToManyMorph':
case 'manyToManyMorph':
const transformToArrayID = (array) => {
if(_.isArray(array)) {
return array.map(value => {
if (_.isPlainObject(value)) {
return value._id || value.id;
}
return value;
})
}
if (association.type === 'model') {
return _.isEmpty(array) ? [] : transformToArrayID([array]);
}
return [];
};
// Compare array of ID to find deleted files.
const currentValue = transformToArrayID(response[current]).map(id => id.toString());
const storedValue = transformToArrayID(params.values[current]).map(id => id.toString());
const toAdd = _.difference(storedValue, currentValue);
const toRemove = _.difference(currentValue, storedValue);
toAdd.forEach(id => {
virtualFields.push(strapi.query(details.model || details.collection, details.plugin).addRelationMorph({
id,
alias: association.via,
ref: this.collectionName,
refId: response.id,
field: association.alias
}));
});
// Update the relational array.
toRemove.forEach(id => {
virtualFields.push(strapi.query(details.model || details.collection, details.plugin).removeRelationMorph({
id,
alias: association.via,
ref: this.collectionName,
refId: response.id,
field: association.alias
}));
});
break;
case 'oneMorphToOne':
case 'oneMorphToMany':
break;
default:

@@ -258,3 +329,41 @@ }

}
},
addRelationMorph: async function (params) {
const record = await this.morph.forge()
.where({
[`${this.collectionName}_id`]: params.id,
[`${params.alias}_id`]: params.refId,
[`${params.alias}_type`]: params.ref,
field: params.field
})
.fetch({
withRelated: this.associations.map(x => x.alias)
});
const entry = record ? record.toJSON() : record;
if (entry) {
return Promise.resolve();
}
return await this.morph.forge({
[`${this.collectionName}_id`]: params.id,
[`${params.alias}_id`]: params.refId,
[`${params.alias}_type`]: params.ref,
field: params.field
})
.save();
},
removeRelationMorph: async function (params) {
return await this.morph.forge()
.where({
[`${this.collectionName}_id`]: params.id,
[`${params.alias}_id`]: params.refId,
[`${params.alias}_type`]: params.ref,
field: params.field
})
.destroy();
}
};

@@ -17,3 +17,3 @@ const _ = require('lodash');

findOne: async function (params) {
findOne: async function (params, populate) {
return this

@@ -23,8 +23,10 @@ .findOne({

})
.populate(this.associations.map(x => x.alias).join(' '));
.populate(populate || this.associations.map(x => x.alias).join(' '))
.lean();
},
create: async function (params) {
const entry = await this.create(Object.keys(params.values).reduce((acc, current) => {
if (this._attributes[current].type) {
// Exclude relationships.
const values = Object.keys(params.values).reduce((acc, current) => {
if (this._attributes[current] && this._attributes[current].type) {
acc[current] = params.values[current];

@@ -34,11 +36,13 @@ }

return acc;
}, {}))
.catch((err) => {
const message = err.message.split('index:');
const field = _.words(_.last(message).split('_')[0]);
const error = { message: `This ${field} is already taken`, field };
}, {});
throw error;
});
const entry = await this.create(values)
.catch((err) => {
const message = err.message.split('index:');
const field = _.words(_.last(message).split('_')[0]);
const error = { message: `This ${field} is already taken`, field };
throw error;
});
return module.exports.update.call(this, {

@@ -72,3 +76,2 @@ [this.primaryKey]: entry[this.primaryKey],

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];

@@ -174,2 +177,71 @@

break;
case 'manyMorphToMany':
case 'manyMorphToOne':
// Update the relational array.
acc[current] = params.values[current].map(obj => {
const globalId = obj.source && obj.source !== 'content-manager' ?
strapi.plugins[obj.source].models[_.toLower(obj.ref)].globalId:
strapi.models[_.toLower(obj.ref)].globalId;
// Define the object stored in database.
// The shape is this object is defined by the strapi-mongoose connector.
return {
ref: obj.refId,
kind: globalId,
[association.filter]: obj.field
}
});
break;
case 'oneToManyMorph':
case 'manyToManyMorph':
const transformToArrayID = (array) => {
if (_.isArray(array)) {
return array.map(value => {
if (_.isPlainObject(value)) {
return value._id || value.id;
}
return value;
})
}
if (association.type === 'model') {
return _.isEmpty(array) ? [] : transformToArrayID([array]);
}
return [];
};
// Compare array of ID to find deleted files.
const currentValue = transformToArrayID(response[current]).map(id => id.toString());
const storedValue = transformToArrayID(params.values[current]).map(id => id.toString());
const toAdd = _.difference(storedValue, currentValue);
const toRemove = _.difference(currentValue, storedValue);
// Remove relations in the other side.
toAdd.forEach(id => {
virtualFields.push(strapi.query(details.model || details.collection, details.plugin).addRelationMorph({
id,
alias: association.via,
ref: this.globalId,
refId: response._id,
field: association.alias
}));
});
// Remove relations in the other side.
toRemove.forEach(id => {
virtualFields.push(strapi.query(details.model || details.collection, details.plugin).removeRelationMorph({
id,
alias: association.via,
ref: this.globalId,
refId: response._id,
field: association.alias
}));
});
break;
case 'oneMorphToOne':
case 'oneMorphToMany':
break;
default:

@@ -190,5 +262,5 @@ }

// Update virtuals fields.
const process = await Promise.all(virtualFields);
await Promise.all(virtualFields);
return process[process.length - 1];
return await module.exports.findOne.call(this, params);
},

@@ -210,3 +282,67 @@

return module.exports.update.call(this, params);
},
addRelationMorph: async function (params) {
/*
TODO:
Test this part because it has been coded during the development of the upload feature.
However the upload doesn't need this method. It only uses the `removeRelationMorph`.
*/
const entry = await module.exports.findOne.call(this, params, []);
const value = entry[params.alias] || [];
// Retrieve association.
const association = this.associations.find(association => association.via === params.alias)[0];
if (!association) {
throw Error(`Impossible to create relationship with ${params.ref} (${params.refId})`);
}
// Resolve if the association is already existing.
const isExisting = entry[params.alias].find(obj => {
if (obj.kind === params.ref && obj.ref.toString() === params.refId.toString() && obj.field === params.field) {
return true;
}
return false;
});
// Avoid duplicate.
if (isExisting) {
return Promise.resolve();
}
// Push new relation to the association array.
value.push({
ref: params.refId,
kind: params.ref,
field: association.filter
});
entry[params.alias] = value;
return module.exports.update.call(this, {
id: params.id,
values: entry
});
},
removeRelationMorph: async function (params) {
const entry = await module.exports.findOne.call(this, params, []);
// Filter the association array and remove the association.
entry[params.alias] = entry[params.alias].filter(obj => {
if (obj.kind === params.ref && obj.ref.toString() === params.refId.toString() && obj.field === params.field) {
return false;
}
return true;
});
return module.exports.update.call(this, {
id: params.id,
values: entry
});
}
};

@@ -76,2 +76,3 @@ 'use strict';

} catch(error) {
strapi.log.error(error);
ctx.badRequest(null, ctx.request.admin ? [{ messages: [{ id: error.message, field: error.field }] }] : error.message);

@@ -89,2 +90,3 @@ }

// TODO handle error update
strapi.log.error(error);
ctx.badRequest(null, ctx.request.admin ? [{ messages: [{ id: error.message, field: error.field }] }] : error.message);

@@ -91,0 +93,0 @@ }

{
"name": "strapi-plugin-content-manager",
"version": "3.0.0-alpha.10.3",
"version": "3.0.0-alpha.11",
"description": "A powerful UI to easily manage your data.",

@@ -27,3 +27,3 @@ "strapi": {

"react-select": "^1.0.0-rc.5",
"strapi-helper-plugin": "3.0.0-alpha.10.3"
"strapi-helper-plugin": "3.0.0-alpha.11"
},

@@ -47,3 +47,3 @@ "author": {

"engines": {
"node": ">= 8.0.0",
"node": ">= 9.0.0",
"npm": ">= 3.0.0"

@@ -50,0 +50,0 @@ },

@@ -34,2 +34,43 @@ 'use strict';

add: async (params, values, source) => {
// Multipart/form-data.
if (values.hasOwnProperty('fields') && values.hasOwnProperty('files')) {
// Silent recursive parser.
const parser = (value) => {
try {
value = JSON.parse(value);
} catch (e) {
// Silent.
}
return _.isArray(value) ? value.map(obj => parser(obj)) : value;
};
const files = values.files;
// Parse stringify JSON data.
values = Object.keys(values.fields).reduce((acc, current) => {
acc[current] = parser(values.fields[current]);
return acc;
}, {});
// Update JSON fields.
const entry = await strapi.query(params.model, source).create({
values
});
// Then, request plugin upload.
if (strapi.plugins.upload) {
// Upload new files and attach them to this entity.
await strapi.plugins.upload.services.upload.uploadToEntity({
id: entry.id || entry._id,
model: params.model
}, files, source);
}
return strapi.query(params.model, source).findOne({
id: entry.id || entry._id
});
}
// Create an entry using `queries` system

@@ -42,2 +83,42 @@ return await strapi.query(params.model, source).create({

edit: async (params, values, source) => {
// Multipart/form-data.
if (values.hasOwnProperty('fields') && values.hasOwnProperty('files')) {
// Silent recursive parser.
const parser = (value) => {
try {
value = JSON.parse(value);
} catch (e) {
// Silent.
}
return _.isArray(value) ? value.map(obj => parser(obj)) : value;
};
const files = values.files;
// Parse stringify JSON data.
values = Object.keys(values.fields).reduce((acc, current) => {
acc[current] = parser(values.fields[current]);
return acc;
}, {});
// Update JSON fields.
await strapi.query(params.model, source).update({
id: params.id,
values
});
// Then, request plugin upload.
if (strapi.plugins.upload) {
// Upload new files and attach them to this entity.
await strapi.plugins.upload.services.upload.uploadToEntity(params, files, source);
}
return strapi.query(params.model, source).findOne({
id: params.id
});
}
// Raw JSON.
return strapi.query(params.model, source).update({

@@ -44,0 +125,0 @@ id: params.id,

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc