2017-03-20 22:08:49 +01:00
|
|
|
'use strict';
|
|
|
|
|
2017-08-16 17:05:02 +02:00
|
|
|
const _ = require('lodash');
|
2020-08-18 17:09:21 +02:00
|
|
|
const { contentTypes: contentTypesUtils } = require('strapi-utils');
|
2020-02-19 10:54:44 +01:00
|
|
|
|
2019-08-01 17:19:49 +02:00
|
|
|
const parseMultipartBody = require('../utils/parse-multipart');
|
2019-08-01 08:52:35 +02:00
|
|
|
|
2020-08-18 17:09:21 +02:00
|
|
|
const {
|
|
|
|
PUBLISHED_AT_ATTRIBUTE,
|
|
|
|
CREATED_BY_ATTRIBUTE,
|
|
|
|
UPDATED_BY_ATTRIBUTE,
|
|
|
|
} = contentTypesUtils.constants;
|
|
|
|
|
2020-06-29 16:32:14 +02:00
|
|
|
const ACTIONS = {
|
2020-06-29 17:23:48 +02:00
|
|
|
read: 'plugins::content-manager.explorer.read',
|
|
|
|
create: 'plugins::content-manager.explorer.create',
|
|
|
|
edit: 'plugins::content-manager.explorer.update',
|
|
|
|
delete: 'plugins::content-manager.explorer.delete',
|
2020-08-21 16:19:11 +02:00
|
|
|
publish: 'plugins::content-manager.explorer.publish',
|
2020-06-29 16:32:14 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
const findEntityAndCheckPermissions = async (ability, action, model, id) => {
|
|
|
|
const contentManagerService = strapi.plugins['content-manager'].services.contentmanager;
|
|
|
|
const entity = await contentManagerService.fetch(model, id);
|
|
|
|
|
|
|
|
if (_.isNil(entity)) {
|
|
|
|
throw strapi.errors.notFound();
|
|
|
|
}
|
|
|
|
|
2020-07-06 16:25:25 +02:00
|
|
|
const roles = _.has(entity, 'created_by.id')
|
2020-10-08 19:50:39 +02:00
|
|
|
? await strapi.query('role', 'admin').find({ 'users.id': entity[CREATED_BY_ATTRIBUTE].id }, [])
|
2020-07-06 16:25:25 +02:00
|
|
|
: [];
|
2020-10-08 19:50:39 +02:00
|
|
|
const entityWithRoles = _.set(_.cloneDeep(entity), `${CREATED_BY_ATTRIBUTE}.roles`, roles);
|
2020-07-06 16:25:25 +02:00
|
|
|
|
2020-06-29 16:32:14 +02:00
|
|
|
const pm = strapi.admin.services.permission.createPermissionsManager(ability, action, model);
|
|
|
|
|
2020-07-06 16:25:25 +02:00
|
|
|
if (pm.ability.cannot(pm.action, pm.toSubject(entityWithRoles))) {
|
2020-06-29 16:32:14 +02:00
|
|
|
throw strapi.errors.forbidden();
|
|
|
|
}
|
|
|
|
|
2020-10-08 19:50:39 +02:00
|
|
|
return { pm, entity: entityWithRoles };
|
2020-06-29 16:32:14 +02:00
|
|
|
};
|
|
|
|
|
2017-03-20 22:08:49 +01:00
|
|
|
module.exports = {
|
2019-07-19 09:58:38 +02:00
|
|
|
/**
|
|
|
|
* Returns a list of entities of a content-type matching the query parameters
|
|
|
|
*/
|
|
|
|
async find(ctx) {
|
2020-07-02 18:10:54 +02:00
|
|
|
const { userAbility } = ctx.state;
|
|
|
|
const { model } = ctx.params;
|
|
|
|
const { query } = ctx.request;
|
|
|
|
|
2020-02-19 10:54:44 +01:00
|
|
|
const contentManagerService = strapi.plugins['content-manager'].services.contentmanager;
|
2020-07-02 18:10:54 +02:00
|
|
|
|
|
|
|
const { kind } = strapi.getModel(model);
|
2020-06-29 16:32:14 +02:00
|
|
|
const pm = strapi.admin.services.permission.createPermissionsManager(
|
|
|
|
userAbility,
|
|
|
|
ACTIONS.read,
|
|
|
|
model
|
|
|
|
);
|
2020-01-08 17:00:29 +01:00
|
|
|
|
2020-07-02 18:10:54 +02:00
|
|
|
if (kind === 'singleType') {
|
2020-07-02 19:24:14 +02:00
|
|
|
// fetchAll for a singleType only return one entity
|
2020-07-02 18:10:54 +02:00
|
|
|
const entity = await contentManagerService.fetchAll(model, query);
|
2020-07-01 13:03:30 +02:00
|
|
|
|
2020-07-02 18:10:54 +02:00
|
|
|
// allow user with create permission to know a single type is not created
|
|
|
|
if (!entity) {
|
|
|
|
if (pm.ability.cannot(ACTIONS.create, model)) {
|
|
|
|
return ctx.forbidden();
|
|
|
|
}
|
2020-07-02 16:40:40 +02:00
|
|
|
|
2020-07-02 18:10:54 +02:00
|
|
|
return ctx.notFound();
|
|
|
|
}
|
2020-07-02 16:40:40 +02:00
|
|
|
|
2020-07-02 19:24:14 +02:00
|
|
|
if (pm.ability.cannot(ACTIONS.read, pm.toSubject(entity))) {
|
2020-07-02 16:40:40 +02:00
|
|
|
return ctx.forbidden();
|
|
|
|
}
|
2020-07-02 18:10:54 +02:00
|
|
|
|
2020-07-02 18:11:51 +02:00
|
|
|
return (ctx.body = pm.sanitize(entity));
|
2020-07-02 16:40:40 +02:00
|
|
|
}
|
|
|
|
|
2020-07-02 18:10:54 +02:00
|
|
|
if (pm.ability.cannot(ACTIONS.read, model)) {
|
|
|
|
return ctx.forbidden();
|
|
|
|
}
|
|
|
|
|
|
|
|
const method = _.has(query, '_q') ? 'search' : 'fetchAll';
|
|
|
|
const queryParameters = pm.queryFrom(query);
|
|
|
|
|
|
|
|
const results = await contentManagerService[method](model, queryParameters);
|
2020-01-28 17:14:53 +01:00
|
|
|
|
2020-07-02 15:58:12 +02:00
|
|
|
if (!results) {
|
2020-06-01 12:01:12 +02:00
|
|
|
return ctx.notFound();
|
|
|
|
}
|
|
|
|
|
2020-07-02 15:58:12 +02:00
|
|
|
ctx.body = pm.sanitize(results);
|
2017-03-20 22:08:49 +01:00
|
|
|
},
|
|
|
|
|
2019-07-19 09:58:38 +02:00
|
|
|
/**
|
|
|
|
* Returns an entity of a content type by id
|
|
|
|
*/
|
|
|
|
async findOne(ctx) {
|
2020-06-29 16:32:14 +02:00
|
|
|
const {
|
|
|
|
state: { userAbility },
|
|
|
|
params: { model, id },
|
|
|
|
} = ctx;
|
2020-07-02 16:26:09 +02:00
|
|
|
|
|
|
|
const { pm, entity } = await findEntityAndCheckPermissions(
|
2020-06-29 16:32:14 +02:00
|
|
|
userAbility,
|
|
|
|
ACTIONS.read,
|
2020-07-02 16:26:09 +02:00
|
|
|
model,
|
|
|
|
id
|
2020-06-29 16:32:14 +02:00
|
|
|
);
|
2020-01-08 17:00:29 +01:00
|
|
|
|
2020-07-02 16:26:09 +02:00
|
|
|
ctx.body = pm.sanitize(entity);
|
2017-03-20 22:08:49 +01:00
|
|
|
},
|
|
|
|
|
2019-07-19 09:58:38 +02:00
|
|
|
/**
|
|
|
|
* Returns a count of entities of a content type matching query parameters
|
|
|
|
*/
|
|
|
|
async count(ctx) {
|
2020-07-02 15:58:12 +02:00
|
|
|
const {
|
|
|
|
state: { userAbility },
|
|
|
|
params: { model },
|
|
|
|
request,
|
|
|
|
} = ctx;
|
2020-02-19 10:54:44 +01:00
|
|
|
const contentManagerService = strapi.plugins['content-manager'].services.contentmanager;
|
2020-01-08 17:00:29 +01:00
|
|
|
|
2020-07-02 15:58:12 +02:00
|
|
|
const pm = strapi.admin.services.permission.createPermissionsManager(
|
|
|
|
userAbility,
|
|
|
|
ACTIONS.read,
|
|
|
|
model
|
|
|
|
);
|
|
|
|
const method = _.has(request.query, '_q') ? 'countSearch' : 'count';
|
|
|
|
const query = pm.queryFrom(request.query);
|
|
|
|
|
|
|
|
const count = await contentManagerService[method](model, query);
|
2017-04-11 11:34:59 +02:00
|
|
|
|
|
|
|
ctx.body = {
|
2019-07-19 09:58:38 +02:00
|
|
|
count: _.isNumber(count) ? count : _.toNumber(count),
|
2017-04-11 11:34:59 +02:00
|
|
|
};
|
|
|
|
},
|
|
|
|
|
2019-07-19 09:58:38 +02:00
|
|
|
/**
|
|
|
|
* Creates an entity of a content type
|
|
|
|
*/
|
|
|
|
async create(ctx) {
|
2020-06-29 16:32:14 +02:00
|
|
|
const {
|
|
|
|
state: { userAbility, user },
|
|
|
|
params: { model },
|
|
|
|
request: { body },
|
|
|
|
} = ctx;
|
2020-02-19 10:54:44 +01:00
|
|
|
const contentManagerService = strapi.plugins['content-manager'].services.contentmanager;
|
2020-09-22 12:31:26 +02:00
|
|
|
const modelDef = strapi.getModel(model);
|
2020-01-08 17:00:29 +01:00
|
|
|
|
2020-06-29 16:32:14 +02:00
|
|
|
const pm = strapi.admin.services.permission.createPermissionsManager(
|
|
|
|
userAbility,
|
|
|
|
ACTIONS.create,
|
|
|
|
model
|
|
|
|
);
|
|
|
|
|
2020-07-06 16:25:25 +02:00
|
|
|
if (!pm.isAllowed) {
|
2020-06-29 16:32:14 +02:00
|
|
|
throw strapi.errors.forbidden();
|
|
|
|
}
|
|
|
|
|
2020-07-01 13:03:30 +02:00
|
|
|
const sanitize = e => pm.pickPermittedFieldsOf(e, { subject: model });
|
2020-06-29 16:32:14 +02:00
|
|
|
|
2020-06-29 11:12:53 +02:00
|
|
|
const { data, files } = ctx.is('multipart') ? parseMultipartBody(ctx) : { data: body };
|
2017-11-27 17:27:16 +01:00
|
|
|
|
2020-09-22 12:31:26 +02:00
|
|
|
const writableData = _.omit(data, contentTypesUtils.getNonWritableAttributes(modelDef));
|
|
|
|
|
|
|
|
await strapi.entityValidator.validateEntityCreation(modelDef, writableData, { isDraft: true });
|
|
|
|
const isDraft = contentTypesUtils.hasDraftAndPublish(modelDef);
|
|
|
|
await strapi.entityValidator.validateEntityUpdate(modelDef, writableData, { isDraft });
|
|
|
|
|
2020-02-19 11:14:00 +01:00
|
|
|
try {
|
2020-07-01 13:03:30 +02:00
|
|
|
const result = await contentManagerService.create(
|
|
|
|
{
|
2020-08-18 17:09:21 +02:00
|
|
|
data: {
|
2020-09-22 12:31:26 +02:00
|
|
|
...sanitize(writableData),
|
2020-08-18 17:09:21 +02:00
|
|
|
[CREATED_BY_ATTRIBUTE]: user.id,
|
|
|
|
[UPDATED_BY_ATTRIBUTE]: user.id,
|
|
|
|
},
|
2020-07-01 13:03:30 +02:00
|
|
|
files,
|
|
|
|
},
|
|
|
|
{ model }
|
|
|
|
);
|
2020-06-29 16:32:14 +02:00
|
|
|
|
|
|
|
ctx.body = pm.sanitize(result, { action: ACTIONS.read });
|
2020-02-19 11:14:00 +01:00
|
|
|
|
2020-03-25 20:02:29 +01:00
|
|
|
await strapi.telemetry.send('didCreateFirstContentTypeEntry', { model });
|
2020-02-19 11:14:00 +01:00
|
|
|
} catch (error) {
|
|
|
|
strapi.log.error(error);
|
|
|
|
ctx.badRequest(null, [
|
|
|
|
{
|
|
|
|
messages: [{ id: error.message, message: error.message, field: error.field }],
|
2020-03-03 16:12:19 +01:00
|
|
|
errors: _.get(error, 'data.errors'),
|
2020-02-19 11:14:00 +01:00
|
|
|
},
|
|
|
|
]);
|
2017-06-17 17:01:50 +02:00
|
|
|
}
|
2019-07-19 09:58:38 +02:00
|
|
|
},
|
2017-03-20 22:08:49 +01:00
|
|
|
|
2019-07-19 09:58:38 +02:00
|
|
|
/**
|
|
|
|
* Updates an entity of a content type
|
|
|
|
*/
|
|
|
|
async update(ctx) {
|
2020-06-29 16:32:14 +02:00
|
|
|
const {
|
|
|
|
state: { userAbility, user },
|
|
|
|
params: { id, model },
|
|
|
|
request: { body },
|
|
|
|
} = ctx;
|
2019-07-19 09:58:38 +02:00
|
|
|
|
2020-02-19 10:54:44 +01:00
|
|
|
const contentManagerService = strapi.plugins['content-manager'].services.contentmanager;
|
2020-09-22 12:31:26 +02:00
|
|
|
const modelDef = strapi.getModel(model);
|
2020-02-19 10:54:44 +01:00
|
|
|
|
2020-06-29 16:32:14 +02:00
|
|
|
const { pm, entity } = await findEntityAndCheckPermissions(
|
|
|
|
userAbility,
|
|
|
|
ACTIONS.edit,
|
|
|
|
model,
|
|
|
|
id
|
|
|
|
);
|
|
|
|
|
2020-07-01 13:03:30 +02:00
|
|
|
const sanitize = e => pm.pickPermittedFieldsOf(e, { subject: pm.toSubject(entity) });
|
2020-06-29 16:32:14 +02:00
|
|
|
|
2020-06-29 11:12:53 +02:00
|
|
|
const { data, files } = ctx.is('multipart') ? parseMultipartBody(ctx) : { data: body };
|
|
|
|
|
2020-09-22 12:31:26 +02:00
|
|
|
const writableData = _.omit(data, contentTypesUtils.getNonWritableAttributes(modelDef));
|
|
|
|
|
|
|
|
const isDraft = contentTypesUtils.isDraft(entity, modelDef);
|
|
|
|
await strapi.entityValidator.validateEntityUpdate(modelDef, writableData, { isDraft });
|
2020-08-18 17:09:21 +02:00
|
|
|
|
2020-02-19 11:14:00 +01:00
|
|
|
try {
|
2020-06-30 12:03:13 +02:00
|
|
|
const result = await contentManagerService.edit(
|
|
|
|
{ id },
|
2020-08-18 17:09:21 +02:00
|
|
|
{
|
|
|
|
data: {
|
|
|
|
...sanitize(writableData),
|
|
|
|
[UPDATED_BY_ATTRIBUTE]: user.id,
|
|
|
|
},
|
|
|
|
files,
|
|
|
|
},
|
2020-06-30 12:03:13 +02:00
|
|
|
{ model }
|
|
|
|
);
|
2020-06-29 16:32:14 +02:00
|
|
|
|
|
|
|
ctx.body = pm.sanitize(result, { action: ACTIONS.read });
|
2020-02-19 11:14:00 +01:00
|
|
|
} catch (error) {
|
|
|
|
strapi.log.error(error);
|
|
|
|
ctx.badRequest(null, [
|
|
|
|
{
|
|
|
|
messages: [{ id: error.message, message: error.message, field: error.field }],
|
2020-03-03 16:12:19 +01:00
|
|
|
errors: _.get(error, 'data.errors'),
|
2020-02-19 11:14:00 +01:00
|
|
|
},
|
|
|
|
]);
|
2019-07-19 09:58:38 +02:00
|
|
|
}
|
|
|
|
},
|
2017-05-05 11:40:52 +02:00
|
|
|
|
2019-07-19 17:24:27 +02:00
|
|
|
/**
|
|
|
|
* Deletes one entity of a content type matching a query
|
|
|
|
*/
|
|
|
|
async delete(ctx) {
|
2020-06-29 16:32:14 +02:00
|
|
|
const {
|
|
|
|
state: { userAbility },
|
|
|
|
params: { id, model },
|
|
|
|
} = ctx;
|
2020-02-19 10:54:44 +01:00
|
|
|
const contentManagerService = strapi.plugins['content-manager'].services.contentmanager;
|
2020-01-08 17:00:29 +01:00
|
|
|
|
2020-06-29 16:32:14 +02:00
|
|
|
const { pm } = await findEntityAndCheckPermissions(userAbility, ACTIONS.delete, model, id);
|
|
|
|
|
2020-07-08 13:53:56 +02:00
|
|
|
const result = await contentManagerService.delete(model, { id });
|
2020-06-29 16:32:14 +02:00
|
|
|
|
|
|
|
ctx.body = pm.sanitize(result, { action: ACTIONS.read });
|
2018-07-05 17:57:30 +02:00
|
|
|
},
|
|
|
|
|
2019-07-19 17:24:27 +02:00
|
|
|
/**
|
|
|
|
* Deletes multiple entities of a content type matching a query
|
|
|
|
*/
|
|
|
|
async deleteMany(ctx) {
|
2020-06-29 16:32:14 +02:00
|
|
|
const {
|
2020-07-01 13:03:30 +02:00
|
|
|
state: { userAbility },
|
2020-06-29 16:32:14 +02:00
|
|
|
params: { model },
|
2020-07-02 15:58:12 +02:00
|
|
|
request,
|
2020-06-29 16:32:14 +02:00
|
|
|
} = ctx;
|
2020-02-19 10:54:44 +01:00
|
|
|
const contentManagerService = strapi.plugins['content-manager'].services.contentmanager;
|
2020-06-29 16:32:14 +02:00
|
|
|
const pm = strapi.admin.services.permission.createPermissionsManager(
|
|
|
|
userAbility,
|
|
|
|
ACTIONS.delete,
|
|
|
|
model
|
|
|
|
);
|
|
|
|
|
2020-07-02 15:58:12 +02:00
|
|
|
const results = await contentManagerService.deleteMany(
|
|
|
|
model,
|
|
|
|
Object.values(request.query),
|
|
|
|
pm.query
|
|
|
|
);
|
2020-01-08 17:00:29 +01:00
|
|
|
|
2020-06-29 16:32:14 +02:00
|
|
|
ctx.body = results.map(result => pm.sanitize(result, { action: ACTIONS.read }));
|
2019-07-19 09:58:38 +02:00
|
|
|
},
|
2020-07-01 16:37:44 +02:00
|
|
|
|
2020-08-21 16:19:11 +02:00
|
|
|
async publish(ctx) {
|
|
|
|
const {
|
|
|
|
state: { userAbility },
|
|
|
|
params: { model, id },
|
|
|
|
} = ctx;
|
|
|
|
|
|
|
|
const contentManagerService = strapi.plugins['content-manager'].services.contentmanager;
|
|
|
|
const { entity, pm } = await findEntityAndCheckPermissions(
|
|
|
|
userAbility,
|
|
|
|
ACTIONS.publish,
|
|
|
|
model,
|
|
|
|
id
|
|
|
|
);
|
|
|
|
|
2020-09-22 12:31:26 +02:00
|
|
|
await strapi.entityValidator.validateEntityCreation(strapi.getModel(model), entity);
|
2020-08-21 16:19:11 +02:00
|
|
|
|
2020-08-27 18:11:16 +02:00
|
|
|
if (entity[PUBLISHED_AT_ATTRIBUTE]) {
|
2020-08-21 16:19:11 +02:00
|
|
|
return ctx.badRequest('Already published');
|
|
|
|
}
|
|
|
|
|
|
|
|
const publishedEntry = await contentManagerService.publish({ id }, model);
|
|
|
|
|
|
|
|
ctx.body = pm.sanitize(publishedEntry, { action: ACTIONS.read });
|
|
|
|
},
|
|
|
|
|
2020-08-21 19:07:54 +02:00
|
|
|
async unpublish(ctx) {
|
|
|
|
const {
|
|
|
|
state: { userAbility },
|
|
|
|
params: { model, id },
|
|
|
|
} = ctx;
|
|
|
|
|
|
|
|
const contentManagerService = strapi.plugins['content-manager'].services.contentmanager;
|
|
|
|
const { entity, pm } = await findEntityAndCheckPermissions(
|
|
|
|
userAbility,
|
|
|
|
ACTIONS.publish,
|
|
|
|
model,
|
|
|
|
id
|
|
|
|
);
|
|
|
|
|
2020-08-27 18:11:16 +02:00
|
|
|
if (!entity[PUBLISHED_AT_ATTRIBUTE]) {
|
2020-08-21 19:07:54 +02:00
|
|
|
return ctx.badRequest('Already a draft');
|
|
|
|
}
|
|
|
|
|
|
|
|
const unpublishedEntry = await contentManagerService.unpublish({ id }, model);
|
|
|
|
|
|
|
|
ctx.body = pm.sanitize(unpublishedEntry, { action: ACTIONS.read });
|
|
|
|
},
|
|
|
|
|
2020-07-01 16:37:44 +02:00
|
|
|
async findRelationList(ctx) {
|
|
|
|
const { model, targetField } = ctx.params;
|
2020-07-02 15:21:56 +02:00
|
|
|
const { _component, ...query } = ctx.request.query;
|
2020-07-01 16:37:44 +02:00
|
|
|
|
2020-07-02 16:51:27 +02:00
|
|
|
const contentManagerServices = strapi.plugins['content-manager'].services;
|
|
|
|
|
2020-07-01 16:37:44 +02:00
|
|
|
if (!targetField) {
|
|
|
|
return ctx.badRequest();
|
|
|
|
}
|
|
|
|
|
2020-07-02 15:21:56 +02:00
|
|
|
const modelDef = _component ? strapi.db.getModel(_component) : strapi.db.getModel(model);
|
2020-07-01 16:37:44 +02:00
|
|
|
|
2020-07-02 14:00:07 +02:00
|
|
|
if (!modelDef) {
|
2020-07-01 16:37:44 +02:00
|
|
|
return ctx.notFound('model.notFound');
|
|
|
|
}
|
|
|
|
|
|
|
|
const attr = modelDef.attributes[targetField];
|
|
|
|
if (!attr) {
|
|
|
|
return ctx.badRequest('targetField.invalid');
|
|
|
|
}
|
|
|
|
|
|
|
|
const target = strapi.db.getModelByAssoc(attr);
|
|
|
|
|
2020-07-02 14:00:07 +02:00
|
|
|
if (!target) {
|
|
|
|
return ctx.notFound('target.notFound');
|
|
|
|
}
|
|
|
|
|
2020-07-02 16:51:27 +02:00
|
|
|
const contentManagerService = contentManagerServices.contentmanager;
|
2020-07-01 16:37:44 +02:00
|
|
|
|
|
|
|
let entities = [];
|
|
|
|
|
|
|
|
if (_.has(ctx.request.query, '_q')) {
|
|
|
|
entities = await contentManagerService.search(target.uid, query);
|
|
|
|
} else {
|
|
|
|
entities = await contentManagerService.fetchAll(target.uid, query);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!entities) {
|
|
|
|
return ctx.notFound();
|
|
|
|
}
|
|
|
|
|
2020-07-02 16:51:27 +02:00
|
|
|
const modelConfig = _component
|
|
|
|
? await contentManagerServices.components.getConfiguration(modelDef.uid)
|
|
|
|
: await contentManagerServices.contenttypes.getConfiguration(modelDef.uid);
|
2020-07-01 16:37:44 +02:00
|
|
|
|
|
|
|
const field = _.get(modelConfig, `metadatas.${targetField}.edit.mainField`, 'id');
|
2020-08-18 17:09:21 +02:00
|
|
|
const pickFields = [field, 'id', target.primaryKey, PUBLISHED_AT_ATTRIBUTE];
|
2020-07-01 16:37:44 +02:00
|
|
|
const sanitize = d => _.pick(d, pickFields);
|
|
|
|
|
|
|
|
ctx.body = _.isArray(entities) ? entities.map(sanitize) : sanitize(entities);
|
|
|
|
},
|
2017-03-20 22:08:49 +01:00
|
|
|
};
|