strapi-plugin-generic-custom-fields
Version:
This plugin allows you to easily add custom fields to Strapi.
357 lines (356 loc) • 9.76 kB
JavaScript
import slugify from "slugify";
import { z } from "zod";
const bootstrap = ({ strapi: _strapi }) => {
};
const destroy = ({ strapi: _strapi }) => {
};
const strapi = {
name: "generic-custom-fields"
};
const packageJson = {
strapi
};
const PLUGIN_ID = packageJson.strapi.name;
const register = ({ strapi: strapi2 }) => {
const configCustomFields = strapi2.plugin(PLUGIN_ID).config("customFields");
for (const customField of configCustomFields) {
strapi2.customFields.register({
name: slugify(customField.name, { lower: true }),
plugin: PLUGIN_ID,
type: "string",
inputSize: customField.inputSize
});
}
};
const itemResponseSchema = z.object({
value: z.string().min(1),
label: z.string().min(1),
icon: z.object({
src: z.string(),
colorMask: z.boolean().optional()
}).optional()
});
const itemsResponseSchema = z.object({
items: z.array(itemResponseSchema)
// total: z.number().int().min(0).optional(),
});
const customFieldSchema = z.object({
name: z.string().min(1),
description: z.string().min(1).optional(),
icon: z.union([
z.literal("Alien"),
z.literal("Archive"),
z.literal("ArrowClockwise"),
z.literal("ArrowDown"),
z.literal("ArrowLeft"),
z.literal("ArrowLineLeft"),
z.literal("ArrowLineRight"),
z.literal("ArrowRight"),
z.literal("ArrowUp"),
z.literal("ArrowsCounterClockwise"),
z.literal("ArrowsOut"),
z.literal("Bell"),
z.literal("Bold"),
z.literal("Book"),
z.literal("Briefcase"),
z.literal("BulletList"),
z.literal("Calendar"),
z.literal("Car"),
z.literal("CaretDown"),
z.literal("CaretUp"),
z.literal("Cast"),
z.literal("CastleTurret"),
z.literal("ChartBubble"),
z.literal("ChartCircle"),
z.literal("ChartPie"),
z.literal("Check"),
z.literal("CheckCircle"),
z.literal("CheckCircleEmpty"),
z.literal("ChevronDown"),
z.literal("ChevronLeft"),
z.literal("ChevronRight"),
z.literal("ChevronUp"),
z.literal("Clock"),
z.literal("ClockCounterClockwise"),
z.literal("Cloud"),
z.literal("CloudUpload"),
z.literal("Code"),
z.literal("CodeBlock"),
z.literal("Coffee"),
z.literal("Cog"),
z.literal("Collapse"),
z.literal("Command"),
z.literal("Crop"),
z.literal("Cross"),
z.literal("CrossCircle"),
z.literal("Crown"),
z.literal("Cursor"),
z.literal("Database"),
z.literal("Discuss"),
z.literal("Download"),
z.literal("Drag"),
z.literal("Duplicate"),
z.literal("Earth"),
z.literal("EarthStriked"),
z.literal("EmotionHappy"),
z.literal("EmotionUnhappy"),
z.literal("Expand"),
z.literal("ExternalLink"),
z.literal("Eye"),
z.literal("EyeStriked"),
z.literal("Faders"),
z.literal("Feather"),
z.literal("File"),
z.literal("FileCsv"),
z.literal("FileError"),
z.literal("FilePdf"),
z.literal("FileXls"),
z.literal("FileZip"),
z.literal("Filter"),
z.literal("Folder"),
z.literal("Gift"),
z.literal("Globe"),
z.literal("GraphQl"),
z.literal("GridFour"),
z.literal("GridNine"),
z.literal("HandHeart"),
z.literal("Hashtag"),
z.literal("HeadingFive"),
z.literal("HeadingFour"),
z.literal("HeadingOne"),
z.literal("HeadingSix"),
z.literal("HeadingThree"),
z.literal("HeadingTwo"),
z.literal("Headphones"),
z.literal("Heart"),
z.literal("House"),
z.literal("Image"),
z.literal("Images"),
z.literal("IndentDecrease"),
z.literal("IndentIncrease"),
z.literal("Information"),
z.literal("Italic"),
z.literal("Key"),
z.literal("Layout"),
z.literal("Lightbulb"),
z.literal("Lightning"),
z.literal("Link"),
z.literal("List"),
z.literal("ListPlus"),
z.literal("ListSearch"),
z.literal("Loader"),
z.literal("Lock"),
z.literal("Magic"),
z.literal("Mail"),
z.literal("ManyToMany"),
z.literal("ManyToOne"),
z.literal("ManyWays"),
z.literal("Message"),
z.literal("Microphone"),
z.literal("Minus"),
z.literal("MinusCircle"),
z.literal("Monitor"),
z.literal("Moon"),
z.literal("More"),
z.literal("Move"),
z.literal("MusicNotes"),
z.literal("NumberList"),
z.literal("OneToMany"),
z.literal("OneToOne"),
z.literal("OneWay"),
z.literal("PaintBrush"),
z.literal("PaintRoller"),
z.literal("Palette"),
z.literal("PaperPlane"),
z.literal("Paperclip"),
z.literal("Paragraph"),
z.literal("Pencil"),
z.literal("Phone"),
z.literal("Pin"),
z.literal("PinMap"),
z.literal("Plane"),
z.literal("Plant"),
z.literal("Play"),
z.literal("Plus"),
z.literal("PlusCircle"),
z.literal("PresentationChart"),
z.literal("PriceTag"),
z.literal("PuzzlePiece"),
z.literal("Question"),
z.literal("Quotes"),
z.literal("Restaurant"),
z.literal("Rocket"),
z.literal("Scissors"),
z.literal("SealCheck"),
z.literal("Search"),
z.literal("Server"),
z.literal("Shield"),
z.literal("Shirt"),
z.literal("ShoppingCart"),
z.literal("SignOut"),
z.literal("SlidersHorizontal"),
z.literal("Sparkle"),
z.literal("SquaresFour"),
z.literal("Stack"),
z.literal("Star"),
z.literal("Stethoscope"),
z.literal("Stop"),
z.literal("Store"),
z.literal("StrikeThrough"),
z.literal("Sun"),
z.literal("Television"),
z.literal("ThumbDown"),
z.literal("ThumbUp"),
z.literal("Train"),
z.literal("Trash"),
z.literal("Typhoon"),
z.literal("Underline"),
z.literal("Upload"),
z.literal("User"),
z.literal("VolumeMute"),
z.literal("VolumeUp"),
z.literal("Walk"),
z.literal("WarningCircle"),
z.literal("Wheelchair")
]).optional(),
inputSize: z.object({
default: z.union([z.literal(4), z.literal(6), z.literal(8), z.literal(12)]),
isResizable: z.boolean()
}).optional(),
searchable: z.boolean().optional(),
fetchItems: z.function().args(z.object({
query: z.string().optional()
})).returns(z.union([itemsResponseSchema, z.promise(itemsResponseSchema)])),
fetchItem: z.function().args(z.object({
value: z.string()
})).returns(z.union([itemResponseSchema, z.promise(itemResponseSchema)]))
});
const configSchema = z.object({
customFields: z.array(customFieldSchema).refine(
(fields) => {
const slugifiedNames = fields.map((field) => slugify(field.name, { lower: true }));
return new Set(slugifiedNames).size === slugifiedNames.length;
},
{
message: "Each custom field name must be unique after slugification"
}
)
});
const config = {
default: {
customFields: []
},
validator(config2) {
configSchema.parse(config2);
}
};
const contentTypes = {};
const controller = ({ strapi: strapi2 }) => ({
getConfigCustomFields() {
return strapi2.plugin(PLUGIN_ID).config("customFields");
},
getCustomFieldUID(name) {
return `plugin::${PLUGIN_ID}.${slugify(name, { lower: true })}`;
},
configCustomFields() {
return this.getConfigCustomFields();
},
configCustomField(ctx) {
const customFields = this.getConfigCustomFields();
const customField = customFields.find((field) => ctx.params.uid === this.getCustomFieldUID(field.name));
if (!customField) {
ctx.throw(404, `Custom field ${ctx.params.uid} not found`);
}
return customField;
},
async customFieldItems(ctx) {
try {
const customFields = this.getConfigCustomFields();
const customField = customFields.find((field) => ctx.params.uid === this.getCustomFieldUID(field.name));
if (!customField) {
ctx.throw(404, `Custom field ${ctx.params.uid} not found`);
}
const query = ctx.request.query.query;
return itemsResponseSchema.parse(await customField.fetchItems({
query
// page: page ? parseInt(page, 10) : undefined,
}));
} catch (error) {
throw error instanceof Error ? error : new Error(`Error fetching CustomField[${ctx.params.uid}] items: ${error}`);
}
},
async customFieldItem(ctx) {
try {
const customFields = this.getConfigCustomFields();
const customField = customFields.find((field) => ctx.params.uid === this.getCustomFieldUID(field.name));
if (!customField) {
ctx.throw(404, `Custom field ${ctx.params.uid} not found`);
}
const value = ctx.request.query.value;
return itemResponseSchema.parse(await customField.fetchItem({ value }));
} catch (error) {
throw error instanceof Error ? error : new Error(`Error fetching CustomField[${ctx.params.uid}] item: ${error}`);
}
}
});
const controllers = {
admin: controller
};
const middlewares = {};
const policies = {};
const adminRoutes = [
{
method: "GET",
path: "/config/custom-fields",
handler: "admin.configCustomFields",
config: {
policies: ["admin::isAuthenticatedAdmin"]
}
},
{
method: "GET",
path: "/config/custom-fields/:uid",
handler: "admin.configCustomField",
config: {
policies: ["admin::isAuthenticatedAdmin"]
}
},
{
method: "GET",
path: "/custom-fields/:uid/items",
handler: "admin.customFieldItems",
config: {
policies: ["admin::isAuthenticatedAdmin"]
}
},
{
method: "GET",
path: "/custom-fields/:uid/item",
handler: "admin.customFieldItem",
config: {
policies: ["admin::isAuthenticatedAdmin"]
}
}
];
const routes = {
admin: {
type: "admin",
routes: adminRoutes
}
};
const services = {};
const index = {
register,
bootstrap,
destroy,
config,
controllers,
routes,
services,
contentTypes,
policies,
middlewares
};
export {
index as default
};