From 70bf99a65bd79ccb513c7df27da048abc20aaf61 Mon Sep 17 00:00:00 2001 From: Alexandre Bodin Date: Wed, 24 Jul 2019 11:51:35 +0200 Subject: [PATCH] Update content-manager schemas --- .../controllers/ContentTypes.js | 14 +- .../controllers/Groups.js | 21 +- .../validation/model-configuration.js | 11 +- .../services/ContentTypes.js | 57 ++-- .../services/Groups.js | 23 -- .../__tests__/attributes.test.js | 255 +++++------------- .../utils/configuration/attributes.js | 51 ++-- .../services/utils/configuration/index.js | 19 +- .../services/utils/configuration/layouts.js | 99 ++++--- .../services/utils/configuration/metadatas.js | 68 +++-- .../services/utils/configuration/settings.js | 12 +- packages/strapi/lib/core/bootstrap.js | 5 + 12 files changed, 270 insertions(+), 365 deletions(-) diff --git a/packages/strapi-plugin-content-manager/controllers/ContentTypes.js b/packages/strapi-plugin-content-manager/controllers/ContentTypes.js index 29ee186181..d7e0570425 100644 --- a/packages/strapi-plugin-content-manager/controllers/ContentTypes.js +++ b/packages/strapi-plugin-content-manager/controllers/ContentTypes.js @@ -12,8 +12,7 @@ module.exports = { const userModels = Object.keys(strapi.models) .filter(key => key !== 'core_store') .map(uid => { - const { info } = strapi.models[uid]; - return service.formatContentType({ uid, info }); + return service.formatContentType(uid, strapi.models[uid]); }); const shouldDisplayPluginModel = uid => { @@ -28,11 +27,8 @@ module.exports = { const plugin = strapi.plugins[pluginKey]; return Object.keys(plugin.models || {}).map(uid => { - const { info } = plugin.models[uid]; - - return service.formatContentType({ + return service.formatContentType(uid, plugin.models[uid], { uid, - info, isDisplayed: shouldDisplayPluginModel(uid), source: pluginKey, }); @@ -41,11 +37,7 @@ module.exports = { .reduce((acc, models) => acc.concat(models), []); const adminModels = Object.keys(strapi.admin.models).map(uid => { - const { info } = strapi.admin.models[uid]; - - return service.formatContentType({ - uid, - info, + return service.formatContentType(uid, strapi.admin.models[uid], { isDisplayed: false, source: 'admin', }); diff --git a/packages/strapi-plugin-content-manager/controllers/Groups.js b/packages/strapi-plugin-content-manager/controllers/Groups.js index 8d9ddd75dc..36c54cf0c7 100644 --- a/packages/strapi-plugin-content-manager/controllers/Groups.js +++ b/packages/strapi-plugin-content-manager/controllers/Groups.js @@ -1,7 +1,5 @@ 'use strict'; -const _ = require('lodash'); -const pluralize = require('pluralize'); const { createModelConfigurationSchema } = require('./validation'); module.exports = { @@ -9,13 +7,11 @@ module.exports = { * Returns the list of available groups */ async listGroups(ctx) { - const data = Object.keys(strapi.groups).map(uid => ({ - uid, - source: null, - isDisplayed: true, - name: uid, - label: _.upperFirst(pluralize(uid)), - })); + const ctService = strapi.plugins['content-manager'].services.contenttypes; + + const data = Object.keys(strapi.groups).map(uid => { + return ctService.formatContentType(uid, strapi.groups[uid]); + }); ctx.body = { data }; }, /** @@ -35,12 +31,13 @@ module.exports = { return ctx.notFound('group.notFound'); } + const ctService = strapi.plugins['content-manager'].services.contenttypes; const groupService = strapi.plugins['content-manager'].services.groups; const configurations = await groupService.getConfiguration(uid); const data = { uid, - schema: groupService.formatGroupSchema(group), + schema: ctService.formatContentTypeSchema(group), ...configurations, }; @@ -80,12 +77,12 @@ module.exports = { const groupService = strapi.plugins['content-manager'].services.groups; await groupService.setConfiguration(uid, input); + const ctService = strapi.plugins['content-manager'].services.contenttypes; const configurations = await groupService.getConfiguration(uid); const data = { uid, - - schema: groupService.formatGroupSchema(group), + schema: ctService.formatContentTypeSchema(group), ...configurations, }; diff --git a/packages/strapi-plugin-content-manager/controllers/validation/model-configuration.js b/packages/strapi-plugin-content-manager/controllers/validation/model-configuration.js index e9acbb6f68..bfcb9dbd92 100644 --- a/packages/strapi-plugin-content-manager/controllers/validation/model-configuration.js +++ b/packages/strapi-plugin-content-manager/controllers/validation/model-configuration.js @@ -65,18 +65,19 @@ const createMetadasSchema = model => { edit: yup .object() .shape({ - label: yup.string().required(), + label: yup.string(), description: yup.string(), - editable: yup.boolean().required(), - visible: yup.boolean().required(), - mainField: yup.string(), // only for relations. TODO: to reset when the relation changes + placeholder: yup.string(), + editable: yup.boolean(), + visible: yup.boolean(), + mainField: yup.string(), }) .noUnknown() .required(), list: yup .object() .shape({ - label: yup.string().required(), + label: yup.string(), searchable: yup.boolean(), sortable: yup.boolean(), }) diff --git a/packages/strapi-plugin-content-manager/services/ContentTypes.js b/packages/strapi-plugin-content-manager/services/ContentTypes.js index bb31dc9b9a..fae8678b9e 100644 --- a/packages/strapi-plugin-content-manager/services/ContentTypes.js +++ b/packages/strapi-plugin-content-manager/services/ContentTypes.js @@ -40,15 +40,16 @@ module.exports = { return storeUtils.deleteKey(storeKey); }, - formatContentType(opts) { - const { uid, info, source = null, isDisplayed = true } = opts; + formatContentType(uid, contentType, opts = {}) { + const { source = null, isDisplayed = true } = opts; return { uid, name: uid, - label: formatContentTypeLabel(info.name || uid), + label: formatContentTypeLabel(_.get(contentType, ['info', 'name'], uid)), isDisplayed, source, + schema: this.formatContentTypeSchema(contentType), }; }, @@ -56,21 +57,41 @@ module.exports = { const { associations, allAttributes } = contentType; return { ...pickSchemaFields(contentType), - attributes: Object.keys(allAttributes).reduce((acc, key) => { - const attr = allAttributes[key]; - const assoc = associations.find(assoc => assoc.alias === key); - if (assoc) { - acc[key] = { - ...attr, - type: 'relation', - targetModel: attr.model || attr.collection, - relationType: assoc.nature, - }; - } else { - acc[key] = attr; - } - return acc; - }, {}), + attributes: { + [contentType.primaryKey]: { + type: contentType.primaryKeyType, + }, + id: { + type: contentType.primaryKeyType, + }, + ...Object.keys(allAttributes).reduce((acc, key) => { + const attribute = allAttributes[key]; + const assoc = associations.find(assoc => assoc.alias === key); + + if (assoc) { + const { plugin } = attribute; + let targetEntity = attribute.model || attribute.collection; + + if (plugin === 'upload' && targetEntity === 'file') { + acc[key] = { + type: 'media', + multiple: attribute.collection ? true : false, + required: attribute.required ? true : false, + }; + } else { + acc[key] = { + ...attribute, + type: 'relation', + targetModel: targetEntity, + relationType: assoc.nature, + }; + } + } else { + acc[key] = attribute; + } + return acc; + }, {}), + }, }; }, diff --git a/packages/strapi-plugin-content-manager/services/Groups.js b/packages/strapi-plugin-content-manager/services/Groups.js index 129720cb1d..4355e7a99e 100644 --- a/packages/strapi-plugin-content-manager/services/Groups.js +++ b/packages/strapi-plugin-content-manager/services/Groups.js @@ -1,7 +1,6 @@ 'use strict'; const storeUtils = require('./utils/store'); -const { pickSchemaFields } = require('./utils/schema'); const uidToStoreKey = uid => `groups::${uid}`; @@ -30,26 +29,4 @@ module.exports = { const storeKey = uidToStoreKey(uid); return storeUtils.deleteKey(storeKey); }, - - formatGroupSchema(group) { - const { associations, allAttributes } = group; - return { - ...pickSchemaFields(group), - attributes: Object.keys(allAttributes).reduce((acc, key) => { - const attr = allAttributes[key]; - const assoc = associations.find(assoc => assoc.alias === key); - if (assoc) { - acc[key] = { - ...attr, - type: 'relation', - targetModel: attr.model || attr.collection, - relationType: assoc.nature, - }; - } else { - acc[key] = attr; - } - return acc; - }, {}), - }; - }, }; diff --git a/packages/strapi-plugin-content-manager/services/utils/configuration/__tests__/attributes.test.js b/packages/strapi-plugin-content-manager/services/utils/configuration/__tests__/attributes.test.js index 8a3f04f092..be16f637bc 100644 --- a/packages/strapi-plugin-content-manager/services/utils/configuration/__tests__/attributes.test.js +++ b/packages/strapi-plugin-content-manager/services/utils/configuration/__tests__/attributes.test.js @@ -1,217 +1,92 @@ -const { - isSortable, - isEditable, - hasEditableAttribute, - hasListableAttribute, - hasRelationAttribute, -} = require('../attributes'); +const { isSortable, isVisible } = require('../attributes'); + +const createMockSchema = (attrs, timestamps = true) => { + return { + options: { + timestamps: timestamps ? ['createdAt', 'updatedAt'] : false, + }, + attributes: { + id: { + type: 'integer', + }, + ...attrs, + ...(timestamps + ? { + createdAt: { + type: 'timestamp', + }, + updatedAt: { + type: 'timestampUpdate', + }, + } + : {}), + }, + }; +}; describe('attributesUtils', () => { describe('isSortable', () => { test('The id attribute is always sortable', () => { - expect(isSortable({}, 'id')).toBe(true); + expect(isSortable(createMockSchema({}), 'id')).toBe(true); + }); + + test('Timestamps are sortable', () => { + expect(isSortable(createMockSchema({}, true), 'createdAt')).toBe(true); + expect(isSortable(createMockSchema({}, true), 'updatedAt')).toBe(true); + expect(isSortable(createMockSchema({}, false), 'createdAt')).toBe(false); }); test('Group fields are not sortable', () => { - expect( - isSortable( - { - allAttributes: { - someGroup: { - type: 'group', - }, - }, - }, - 'someGroup' - ) - ).toBe(false); + const schema = createMockSchema({ + someGroup: { + type: 'group', + }, + }); + + expect(isSortable(schema, 'someGroup')).toBe(false); }); test('Json fields are not sortable', () => { - expect( - isSortable( - { - allAttributes: { - jsonInput: { - type: 'json', - }, - }, - }, - 'jsonInput' - ) - ).toBe(false); + const schema = createMockSchema({ + jsonInput: { + type: 'json', + }, + }); + + expect(isSortable(schema, 'jsonInput')).toBe(false); }); test('Relations are not sortable', () => { - expect( - isSortable( - { - allAttributes: { - oneWayRel: { - model: 'someModel', - }, - }, - }, - 'oneWayRel' - ) - ).toBe(false); + const schema = createMockSchema({ + oneWayRel: { + type: 'relation', + targetModel: 'someModel', + }, + manyWayRel: { + type: 'relation', + targetModel: 'someModel', + }, + }); - expect( - isSortable( - { - allAttributes: { - manyWayRel: { - collection: 'someModel', - }, - }, - }, - 'manyWayRel' - ) - ).toBe(false); + expect(isSortable(schema, 'oneWayRel')).toBe(false); + expect(isSortable(schema, 'manyWayRel')).toBe(false); }); }); - describe('isEditable', () => { + describe('isVisible', () => { test('Check if the attribute is in a model attributes', () => { expect( - isEditable( - { - attributes: { - field: { - type: 'string', - }, + isVisible( + createMockSchema({ + field: { + type: 'string', }, - }, + }), 'field' ) ).toBe(true); - expect( - isEditable( - { - attributes: { - field: { - type: 'string', - }, - }, - }, - 'createdAt' - ) - ).toBe(false); - }); - }); - - describe('hasEditableAttribute', () => { - test('Check if the attribute exists and is not a relation', () => { - const model = { - allAttributes: { - rel1: { - model: 'someModel', - }, - rel2: { - collection: 'someModel', - }, - title: { - type: 'string', - }, - }, - }; - - expect(hasEditableAttribute(model, 'rel1')).toBe(false); - expect(hasEditableAttribute(model, 'rel2')).toBe(false); - expect(hasEditableAttribute(model, 'unkown')).toBe(false); - expect(hasEditableAttribute(model, 'title')).toBe(true); - }); - }); - - describe('hasListableAttribute', () => { - test('Ids are listable', () => { - expect(hasListableAttribute({}, 'id')).toBe(true); - }); - - test('Unknown attributes are not listable', () => { - const model = { - allAttributes: { - rel1: { - model: 'someModel', - }, - rel2: { - collection: 'someModel', - }, - title: { - type: 'string', - }, - }, - }; - - expect(hasListableAttribute(model, 'unkown')).toBe(false); - }); - - test('Group attributes are not listable', () => { - const model = { - allAttributes: { - someGroup: { - type: 'group', - }, - }, - }; - - expect(hasListableAttribute(model, 'someGroup')).toBe(false); - }); - - test('JSON attributes are not listable', () => { - const model = { - allAttributes: { - someJson: { - type: 'json', - }, - }, - }; - - expect(hasListableAttribute(model, 'someJson')).toBe(false); - }); - - test('Relations are not listable', () => { - const model = { - allAttributes: { - rel1: { - model: 'someModel', - }, - rel2: { - collection: 'someModel', - }, - title: { - type: 'string', - }, - }, - }; - - expect(hasListableAttribute(model, 'rel1')).toBe(false); - expect(hasListableAttribute(model, 'rel2')).toBe(false); - expect(hasListableAttribute(model, 'title')).toBe(true); - }); - }); - - describe('hasRelationAttribute', () => { - test('Only validate relational attributes', () => { - const model = { - allAttributes: { - rel1: { - model: 'someModel', - }, - rel2: { - collection: 'someModel', - }, - title: { - type: 'string', - }, - }, - }; - - expect(hasRelationAttribute(model, 'rel1')).toBe(true); - expect(hasRelationAttribute(model, 'rel2')).toBe(true); - expect(hasRelationAttribute(model, 'unkown')).toBe(false); - expect(hasRelationAttribute(model, 'title')).toBe(false); + expect(isVisible(createMockSchema({}), 'createdAt')).toBe(false); }); }); }); diff --git a/packages/strapi-plugin-content-manager/services/utils/configuration/attributes.js b/packages/strapi-plugin-content-manager/services/utils/configuration/attributes.js index 3305f966d2..573d9d88d9 100644 --- a/packages/strapi-plugin-content-manager/services/utils/configuration/attributes.js +++ b/packages/strapi-plugin-content-manager/services/utils/configuration/attributes.js @@ -2,15 +2,13 @@ const _ = require('lodash'); -const NON_SORTABLES = ['group', 'json']; -const isSortable = (model, name) => { - if (name === 'id') return true; - - const attribute = model.allAttributes[name]; - if (!_.has(attribute, 'type')) { +const NON_SORTABLES = ['group', 'json', 'relation']; +const isSortable = (schema, name) => { + if (!_.has(schema.attributes, name)) { return false; } + const attribute = schema.attributes[name]; if (NON_SORTABLES.includes(attribute.type)) { return false; } @@ -18,51 +16,42 @@ const isSortable = (model, name) => { return true; }; -// check it is in the attributes not in allAttributes -const isEditable = (model, name) => _.has(model.attributes, name); - -const hasRelationAttribute = (model, attr) => { - return ( - _.has(model.allAttributes[attr], 'model') || - _.has(model.allAttributes[attr], 'collection') - ); +const isSearchable = (schema, name) => { + return isSortable(schema, name); }; -const hasEditableAttribute = (model, attr) => { - if (!_.has(model.allAttributes, attr)) { +const isVisible = (schema, name) => { + if (!_.has(schema.attributes, name)) { return false; } - if (!_.has(model.allAttributes[attr], 'type')) { + if (isTimestamp(schema, name) || name === 'id') { return false; } return true; }; -const hasListableAttribute = (model, attr) => { - if (attr === 'id') return true; - - if (!_.has(model.allAttributes, attr)) { +const isTimestamp = (schema, name) => { + if (!_.has(schema.attributes, name)) { return false; } - if (!_.has(model.allAttributes[attr], 'type')) { + const timestampsOpt = _.get(schema, ['options', 'timestamps']); + if (!timestampsOpt || !Array.isArray(timestampsOpt)) { return false; } - if (NON_SORTABLES.includes(model.allAttributes[attr].type)) { - return false; + if (timestampsOpt.includes(name)) { + return true; } - - return true; }; +const isRelation = attribute => attribute.type === 'relation'; + module.exports = { isSortable, - isEditable, - - hasEditableAttribute, - hasListableAttribute, - hasRelationAttribute, + isVisible, + isSearchable, + isRelation, }; diff --git a/packages/strapi-plugin-content-manager/services/utils/configuration/index.js b/packages/strapi-plugin-content-manager/services/utils/configuration/index.js index f8a0920c5d..0084fb871c 100644 --- a/packages/strapi-plugin-content-manager/services/utils/configuration/index.js +++ b/packages/strapi-plugin-content-manager/services/utils/configuration/index.js @@ -1,20 +1,29 @@ const { createDefaultSettings, syncSettings } = require('./settings'); const { createDefaultMetadatas, syncMetadatas } = require('./metadatas'); const { createDefaultLayouts, syncLayouts } = require('./layouts'); +const { formatContentTypeSchema } = require('../../ContentTypes'); async function createDefaultConfiguration(model) { + // convert model to schema + + const schema = formatContentTypeSchema(model); + return { settings: await createDefaultSettings(), - metadatas: await createDefaultMetadatas(model), - layouts: await createDefaultLayouts(model), + metadatas: await createDefaultMetadatas(schema), + layouts: await createDefaultLayouts(schema), }; } async function syncConfiguration(conf, model) { + // convert model to schema + + const schema = formatContentTypeSchema(model); + return { - settings: await syncSettings(conf, model), - layouts: await syncLayouts(conf, model), - metadatas: await syncMetadatas(conf, model), + settings: await syncSettings(conf, schema), + layouts: await syncLayouts(conf, schema), + metadatas: await syncMetadatas(conf, schema), }; } diff --git a/packages/strapi-plugin-content-manager/services/utils/configuration/layouts.js b/packages/strapi-plugin-content-manager/services/utils/configuration/layouts.js index 35463ba28b..35ad71a055 100644 --- a/packages/strapi-plugin-content-manager/services/utils/configuration/layouts.js +++ b/packages/strapi-plugin-content-manager/services/utils/configuration/layouts.js @@ -1,11 +1,7 @@ 'use strict'; const _ = require('lodash'); -const { - hasListableAttribute, - hasRelationAttribute, - hasEditableAttribute, -} = require('./attributes'); +const { isSortable, isVisible, isRelation } = require('./attributes'); const DEFAULT_LIST_LENGTH = 4; const MAX_ROW_SIZE = 12; @@ -31,37 +27,39 @@ const typeToSize = type => { } }; -async function createDefaultLayouts(model) { +async function createDefaultLayouts(schema) { return { - list: createDefaultListLayout(model), - editRelations: createDefaultEditRelationsLayout(model), - edit: createDefaultEditLayout(model), + list: createDefaultListLayout(schema), + editRelations: createDefaultEditRelationsLayout(schema), + edit: createDefaultEditLayout(schema), }; } -function createDefaultListLayout(model) { - return ['id'] - .concat(Object.keys(model.allAttributes)) - .filter(name => hasListableAttribute(model, name)) +function createDefaultListLayout(schema) { + return Object.keys(schema.attributes) + .filter(name => isSortable(schema, name)) .slice(0, DEFAULT_LIST_LENGTH); } -function createDefaultEditRelationsLayout(model) { - return Object.keys(model.allAttributes).filter(name => - hasRelationAttribute(model, name) +function createDefaultEditRelationsLayout(schema) { + if (schema.modelType === 'group') return []; + + return Object.keys(schema.attributes).filter(name => + hasRelationAttribute(schema, name) ); } const rowSize = els => els.reduce((sum, el) => sum + el.size, 0); -function createDefaultEditLayout(model) { - const keys = Object.keys(model.attributes).filter(name => - hasEditableAttribute(model, name) + +function createDefaultEditLayout(schema) { + const keys = Object.keys(schema.attributes).filter(name => + hasEditableAttribute(schema, name) ); let layout = [[]]; let currentRowIndex = 0; for (let key of keys) { - const attribute = model.attributes[key]; + const attribute = schema.attributes[key]; const attributeSize = typeToSize(attribute.type); let currenRowSize = rowSize(layout[currentRowIndex]); @@ -81,20 +79,22 @@ function createDefaultEditLayout(model) { /** Synchronisation functions */ -function syncLayouts(configuration, model) { - if (_.isEmpty(configuration.layouts)) return createDefaultLayouts(model); +function syncLayouts(configuration, schema) { + if (_.isEmpty(configuration.layouts)) return createDefaultLayouts(schema); const { list = [], editRelations = [], edit = [] } = configuration.layouts || {}; - const cleanList = list.filter(attr => hasListableAttribute(model, attr)); + const cleanList = list.filter(attr => isSortable(schema, attr)); const cleanEditRelations = editRelations.filter(attr => - hasRelationAttribute(model, attr) + hasRelationAttribute(schema, attr) ); const cleanEdit = edit.reduce((acc, row) => { - let newRow = row.filter(el => hasEditableAttribute(model, el.name)); + let newRow = row.filter(el => hasEditableAttribute(schema, el.name)); + + // TODO: recompute row sizes if (newRow.length > 0) { acc.push(newRow); @@ -103,16 +103,16 @@ function syncLayouts(configuration, model) { }, []); let layout = { - list: cleanList.length > 0 ? cleanList : createDefaultListLayout(model), + list: cleanList.length > 0 ? cleanList : createDefaultListLayout(schema), editRelations: cleanEditRelations.length > 0 ? cleanEditRelations - : createDefaultEditRelationsLayout(model), - edit: cleanEdit.length > 0 ? cleanEdit : createDefaultEditLayout(model), + : createDefaultEditRelationsLayout(schema), + edit: cleanEdit.length > 0 ? cleanEdit : createDefaultEditLayout(schema), }; const newAttributes = _.difference( - Object.keys(model.allAttributes), + Object.keys(schema.attributes), Object.keys(configuration.metadatas) ); @@ -125,26 +125,28 @@ function syncLayouts(configuration, model) { // only add valid listable attributes layout.list = _.uniq( layout.list - .concat(newAttributes.filter(key => hasListableAttribute(model, key))) + .concat(newAttributes.filter(key => isSortable(schema, key))) .slice(0, DEFAULT_LIST_LENGTH) ); } // add new relations to layout - const newRelations = newAttributes.filter(key => - hasRelationAttribute(model, key) - ); + if (schema.type !== 'group') { + const newRelations = newAttributes.filter(key => + hasRelationAttribute(schema, key) + ); - layout.editRelations = _.uniq(layout.editRelations.concat(newRelations)); + layout.editRelations = _.uniq(layout.editRelations.concat(newRelations)); + } // add new attributes to edit view const newEditAttributes = newAttributes.filter( - key => hasEditableAttribute(model, key) && _.has(model.attributes, key) + key => hasEditableAttribute(schema, key) && isVisible(schema, key) ); let currentRowIndex = Math.max(layout.edit.length - 1, 0); for (let key of newEditAttributes) { - const attribute = model.attributes[key]; + const attribute = schema.attributes[key]; const attributeSize = typeToSize(attribute.type); let currenRowSize = rowSize(layout.edit[currentRowIndex]); @@ -162,6 +164,31 @@ function syncLayouts(configuration, model) { return layout; } +const hasRelationAttribute = (schema, name) => { + if (!_.has(schema.attributes, name)) { + return false; + } + + return isRelation(schema.attributes[name]); +}; + +const hasEditableAttribute = (schema, name) => { + if (!_.has(schema.attributes, name)) { + return false; + } + + if (!isVisible(schema, name)) { + return false; + } + + if (isRelation(schema.attributes[name])) { + if (schema.modelType === 'group') return true; + return false; + } + + return true; +}; + module.exports = { createDefaultLayouts, syncLayouts, diff --git a/packages/strapi-plugin-content-manager/services/utils/configuration/metadatas.js b/packages/strapi-plugin-content-manager/services/utils/configuration/metadatas.js index 571355be35..f22d6cd15a 100644 --- a/packages/strapi-plugin-content-manager/services/utils/configuration/metadatas.js +++ b/packages/strapi-plugin-content-manager/services/utils/configuration/metadatas.js @@ -2,13 +2,19 @@ const _ = require('lodash'); const { - isEditable, isSortable, - hasListableAttribute, + isSearchable, + isVisible, + isRelation, } = require('./attributes'); +const { formatContentTypeSchema } = require('../../ContentTypes'); -function createDefaultMetadatas(model) { +function createDefaultMetadatas(schema) { return { + ...Object.keys(schema.attributes).reduce((acc, name) => { + acc[name] = createDefaultMetadata(schema, name); + return acc; + }, {}), id: { edit: {}, list: { @@ -17,31 +23,26 @@ function createDefaultMetadatas(model) { sortable: true, }, }, - ...Object.keys(model.allAttributes).reduce((acc, name) => { - acc[name] = createDefaultMetadata(model, name); - return acc; - }, {}), }; } -function createDefaultMetadata(model, name) { - const attr = model.allAttributes[name]; +function createDefaultMetadata(schema, name) { const edit = { - label: name, + label: _.upperFirst(name), description: '', placeholder: '', - visible: true, - editable: isEditable(model, name), + visible: isVisible(schema, name), + editable: true, }; - if (_.has(attr, 'model') || _.has(attr, 'collection')) { + if (isRelation(schema.attributes[name])) { edit.mainField = 'id'; } const list = { - label: name, - searchable: true, - sortable: isSortable(model, name), + label: _.upperFirst(name), + searchable: isSearchable(schema, name), + sortable: isSortable(schema, name), }; return { edit, list }; @@ -49,39 +50,44 @@ function createDefaultMetadata(model, name) { /** Synchronisation functions */ -async function syncMetadatas(configuration, model) { +async function syncMetadatas(configuration, schema) { // clear all keys that do not exist anymore - if (_.isEmpty(configuration.metadatas)) return createDefaultMetadatas(model); + if (_.isEmpty(configuration.metadatas)) return createDefaultMetadatas(schema); // remove old keys const metasWithValidKeys = _.pick( configuration.metadatas, - ['id'].concat(Object.keys(model.allAttributes)) + Object.keys(schema.attributes) ); // add new keys and missing fields const metasWithDefaults = _.merge( {}, - createDefaultMetadatas(model), + createDefaultMetadatas(schema), metasWithValidKeys ); // clear the invalid mainFields const updatedMetas = Object.keys(metasWithDefaults).reduce((acc, key) => { - const meta = metasWithDefaults[key]; - const { edit, list } = meta; + const { edit, list } = metasWithDefaults[key]; + const attr = schema.attributes[key]; let updatedMeta = { edit, list }; // update sortable attr - if (list.sortable && !isSortable(model, key)) { + if (list.sortable && !isSortable(schema, key)) { _.set(updatedMeta, ['list', 'sortable'], false); _.set(acc, [key], updatedMeta); } + if (list.searchable && !isSearchable(schema, key)) { + _.set(updatedMeta, ['list', 'searchable'], false); + _.set(acc, [key], updatedMeta); + } + if (!_.has(edit, 'mainField')) return acc; // remove mainField if the attribute is not a relation anymore - if (_.has(model.allAttributes[key], 'type')) { + if (!isRelation(attr)) { _.set(updatedMeta, 'edit', _.omit(edit, ['mainField'])); _.set(acc, [key], updatedMeta); return acc; @@ -91,10 +97,11 @@ async function syncMetadatas(configuration, model) { if (edit.mainField === 'id') return acc; // check the mainField in the targetModel - const attr = model.allAttributes[key]; - const target = strapi.getModel(attr.model || attr.collection, attr.plugin); + const targetSchema = getTargetSchema(attr.targetModel, attr.plugin); - if (!hasListableAttribute(target, meta.mainField)) { + if (!targetSchema) return acc; + + if (!isSortable(targetSchema, edit.mainField)) { _.set(updatedMeta, ['edit', 'mainField'], 'id'); _.set(acc, [key], updatedMeta); return acc; @@ -106,6 +113,13 @@ async function syncMetadatas(configuration, model) { return _.assign(metasWithDefaults, updatedMetas); } +const getTargetSchema = (name, plugin) => { + const model = strapi.getModel(name, plugin); + if (!model) return null; + + return formatContentTypeSchema(model); +}; + module.exports = { createDefaultMetadatas, syncMetadatas, diff --git a/packages/strapi-plugin-content-manager/services/utils/configuration/settings.js b/packages/strapi-plugin-content-manager/services/utils/configuration/settings.js index d8dc2c676f..409a22bfac 100644 --- a/packages/strapi-plugin-content-manager/services/utils/configuration/settings.js +++ b/packages/strapi-plugin-content-manager/services/utils/configuration/settings.js @@ -1,7 +1,7 @@ 'use strict'; const _ = require('lodash'); -const { hasListableAttribute } = require('./attributes'); +const { isSortable } = require('./attributes'); /** * Retunrs a configuration default settings @@ -21,18 +21,16 @@ async function createDefaultSettings() { /** Synchronisation functions */ -async function syncSettings(configuration, model) { - if (_.isEmpty(configuration.settings)) return createDefaultSettings(model); +async function syncSettings(configuration, schema) { + if (_.isEmpty(configuration.settings)) return createDefaultSettings(schema); const { mainField = 'id', defaultSortBy = 'id' } = configuration.settings || {}; return { ...configuration.settings, - mainField: hasListableAttribute(model, mainField) ? mainField : 'id', - defaultSortBy: hasListableAttribute(model, defaultSortBy) - ? defaultSortBy - : 'id', + mainField: isSortable(schema, mainField) ? mainField : 'id', + defaultSortBy: isSortable(schema, defaultSortBy) ? defaultSortBy : 'id', }; } diff --git a/packages/strapi/lib/core/bootstrap.js b/packages/strapi/lib/core/bootstrap.js index 0ffd5531c4..e13489549f 100644 --- a/packages/strapi/lib/core/bootstrap.js +++ b/packages/strapi/lib/core/bootstrap.js @@ -51,6 +51,7 @@ module.exports = function(strapi) { throw new Error(`Group ${key} is missing a collectionName attribute`); return Object.assign(group, { + modelType: 'group', globalId: group.globalId || _.upperFirst(_.camelCase(`group_${key}`)), }); }); @@ -61,6 +62,7 @@ module.exports = function(strapi) { let model = strapi.api[key].models[index]; Object.assign(model, { + modelType: 'contentType', apiName: key, globalId: model.globalId || _.upperFirst(_.camelCase(index)), collectionName: model.collectionName || `${index}`.toLocaleLowerCase(), @@ -127,6 +129,7 @@ module.exports = function(strapi) { let model = strapi.admin.models[key]; Object.assign(model, { + modelType: 'contentType', identity: model.identity || _.upperFirst(key), globalId: model.globalId || _.upperFirst(_.camelCase(`admin-${key}`)), connection: @@ -155,6 +158,8 @@ module.exports = function(strapi) { let model = plugin.models[key]; Object.assign(model, { + modelType: 'contentType', + plugin: pluginName, collectionName: model.collectionName || `${pluginName}_${key}`.toLowerCase(), globalId: