Created PluginAPI and improve plugin registration in order to add a boot lifecycle for enabling a plugin to interact with another internal API.

Update the ctb to expose a form api.

Signed-off-by: soupette <cyril.lpz@gmail.com>
This commit is contained in:
soupette 2021-01-26 16:18:26 +01:00
parent 2155b9842c
commit 46dc0e6a12
7 changed files with 175 additions and 8 deletions

View File

@ -62,6 +62,8 @@ const pluginsToLoad = [];
Object.keys(plugins).forEach(current => {
const registerPlugin = plugin => {
strapi.registerPlugin(plugin);
return plugin;
};
const currentPluginFn = plugins[current];
@ -115,6 +117,12 @@ const { dispatch } = store;
// Load plugins, this will be removed in the v4, temporary fix until the plugin API
// https://plugin-api-rfc.vercel.app/plugin-api/admin.html
pluginsToLoad.forEach(plugin => {
const bootPlugin = plugin.boot;
if (bootPlugin) {
bootPlugin(strapi);
}
dispatch(pluginLoaded(plugin));
});

View File

@ -0,0 +1,50 @@
class Plugin {
pluginId = null;
decorators = {};
injectedComponents = {};
internals = {};
constructor(pluginConf) {
this.pluginId = pluginConf.id;
this.decorators = pluginConf.decorators || {};
this.injectedComponents = pluginConf.injectedComponents || {};
this.internals = pluginConf.internals || {};
}
decorate(compoName, compo) {
if (this.decorators && this.decorators[compoName]) {
this.decorators[compoName] = compo;
}
}
getDecorator(compoName) {
if (this.decorators) {
return this.decorators[compoName] || null;
}
return null;
}
getInjectedComponents(containerName, blockName) {
try {
return this.injectedComponents[containerName][blockName] || {};
} catch (err) {
console.error('Cannot get injected component', err);
return err;
}
}
injectComponent(containerName, blockName, compo) {
try {
this.injectedComponents[containerName][blockName].push(compo);
} catch (err) {
console.error('Cannot inject component', err);
}
}
}
export default pluginConf => new Plugin(pluginConf);

View File

@ -1,6 +1,7 @@
import ComponentApi from './ComponentApi';
import FieldApi from './FieldApi';
import MiddlewareApi from './MiddlewareApi';
import PluginHandler from './Plugin';
class Strapi {
componentApi = ComponentApi();
@ -8,6 +9,18 @@ class Strapi {
fieldApi = FieldApi();
middlewares = MiddlewareApi();
plugins = {};
getPlugin = pluginId => {
return this.plugins[pluginId];
};
registerPlugin = pluginConf => {
if (pluginConf.id) {
this.plugins[pluginConf.id] = PluginHandler(pluginConf);
}
};
}
export default () => {

View File

@ -13,7 +13,8 @@ const forms = {
attributeType,
reservedNames,
alreadyTakenTargetContentTypeAttributes,
options
options,
extensions
) {
const attributes = get(currentSchema, ['schema', 'attributes'], {});
@ -21,22 +22,62 @@ const forms = {
return attr !== options.initialData.name;
});
const validators = [];
const attributeFormExtensions = extensions.attribute[attributeType];
if (attributeFormExtensions) {
attributeFormExtensions.forEach(({ validator }) => {
if (validator) {
validators.push(validator);
}
});
}
try {
return attributeTypes[attributeType](
let shape = attributeTypes[attributeType](
usedAttributeNames,
reservedNames.attributes,
alreadyTakenTargetContentTypeAttributes,
options
);
validators.forEach(validator => {
console.log({ validator });
shape = shape.shape(validator);
});
return shape;
} catch (err) {
console.error('form', err);
return attributeTypes.default(usedAttributeNames, reservedNames.attributes);
}
},
form: {
advanced(data, type, step) {
advanced(data, type, step, actionType, attributes, extensions) {
const attributeFormExtensions = extensions.attribute[type];
let customForms = [];
if (attributeFormExtensions) {
attributeFormExtensions.forEach(({ form }) => {
if (form.advanced) {
const blocksToAdd = form.advanced(data, type, step, actionType, attributes);
blocksToAdd.forEach(block => {
customForms.push(block);
});
}
});
}
try {
return attributesForm.advanced[type](data, step);
const baseForm = attributesForm.advanced[type](data, step).items;
return { items: [...baseForm, ...customForms] };
} catch (err) {
console.error(err);
return { items: [] };
}
},

View File

@ -10,6 +10,7 @@ import {
getYupInnerErrors,
useGlobalContext,
useQuery,
useStrapi,
InputsIndex,
} from 'strapi-helper-plugin';
import { Button, Text, Padded } from '@buffetjs/core';
@ -70,6 +71,12 @@ const FormModal = () => {
const { push } = useHistory();
const { search } = useLocation();
const { emitEvent, formatMessage } = useGlobalContext();
const {
strapi: { getPlugin },
} = useStrapi();
const ctbPlugin = getPlugin(pluginId);
const types = ctbPlugin.internals.forms.types;
const query = useQuery();
const attributeOptionRef = useRef();
@ -365,8 +372,6 @@ const FormModal = () => {
items: [],
}));
console.log({ modifiedData });
const headers = createHeadersArray(state);
const isCreatingContentType = state.modalType === 'contentType';
@ -458,7 +463,8 @@ const FormModal = () => {
type,
reservedNames,
alreadyTakenTargetContentTypeAttributes,
{ modifiedData, initialData }
{ modifiedData, initialData },
types
);
} else if (isEditingCategory) {
schema = forms.editCategory.schema(allComponentsCategories, initialData);
@ -1025,6 +1031,7 @@ const FormModal = () => {
});
} catch (err) {
const errors = getYupInnerErrors(err);
console.log({ err, errors });
dispatch({
type: SET_ERRORS,
@ -1199,7 +1206,8 @@ const FormModal = () => {
state.attributeType,
state.step,
state.actionType,
attributes
attributes,
types
).items.map((row, index) => {
return (
<div className="row" key={index}>

View File

@ -72,6 +72,31 @@ export default strapi => {
},
],
},
// Internal APIs exposed by the CTB for the other plugins to use
internals: {
forms: {
types: {
attribute: {
test: [],
},
contentType: {},
component: {},
},
extendFields(fields, extension) {
const formType = this.types.attribute;
fields.forEach(field => {
let currentField = formType[field];
if (currentField) {
currentField.push(extension);
} else {
formType[field] = [extension];
}
});
},
},
},
};
return strapi.registerPlugin(plugin);

View File

@ -1,4 +1,5 @@
import React from 'react';
import * as yup from 'yup';
import pluginPkg from '../../package.json';
import middlewares from './middlewares';
import pluginId from './pluginId';
@ -48,6 +49,27 @@ export default strapi => {
},
},
trads,
boot(app) {
const ctbPlugin = app.getPlugin('content-type-builder');
if (ctbPlugin) {
ctbPlugin.internals.forms.extendFields(['text', 'string'], {
validator: {
i18n: yup.string().required(),
},
form: {
advanced(args) {
console.log('advanced', args);
return [[{ name: 'i18n', type: 'text', label: { id: 'i18nTest' } }]];
},
base(args) {
console.log('base', args);
},
},
});
}
},
};
return strapi.registerPlugin(plugin);