multi-content-type-relation 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.md +86 -0
  2. package/TODO.md +4 -0
  3. package/admin/src/components/Input/InputContentSuggestions.tsx +162 -0
  4. package/admin/src/components/Input/MainInput.tsx +135 -0
  5. package/admin/src/components/Input/PublicationState.tsx +28 -0
  6. package/admin/src/components/Input/TableItem.tsx +109 -0
  7. package/admin/src/components/Input/index.tsx +27 -0
  8. package/admin/src/components/PluginIcon/index.tsx +12 -0
  9. package/admin/src/helpers/content.ts +60 -0
  10. package/admin/src/helpers/storage.ts +32 -0
  11. package/admin/src/hooks/useSearchedEntries.ts +41 -0
  12. package/admin/src/index.tsx +140 -0
  13. package/admin/src/interface.ts +37 -0
  14. package/admin/src/pluginId.ts +5 -0
  15. package/admin/src/translations/en.json +1 -0
  16. package/admin/src/translations/fr.json +1 -0
  17. package/admin/src/utils/getTrad.ts +5 -0
  18. package/dist/server/bootstrap.js +5 -0
  19. package/dist/server/config/index.js +27 -0
  20. package/dist/server/content-types/index.js +3 -0
  21. package/dist/server/controllers/controller.js +92 -0
  22. package/dist/server/controllers/index.js +9 -0
  23. package/dist/server/destroy.js +5 -0
  24. package/dist/server/index.js +27 -0
  25. package/dist/server/interface.js +2 -0
  26. package/dist/server/middlewares/index.js +9 -0
  27. package/dist/server/middlewares/middleware.js +163 -0
  28. package/dist/server/policies/index.js +3 -0
  29. package/dist/server/register.js +15 -0
  30. package/dist/server/routes/index.js +29 -0
  31. package/dist/server/services/index.js +9 -0
  32. package/dist/server/services/service.js +8 -0
  33. package/dist/server/utils.js +15 -0
  34. package/dist/tsconfig.server.tsbuildinfo +1 -0
  35. package/package.json +53 -0
  36. package/server/bootstrap.ts +5 -0
  37. package/server/config/index.ts +28 -0
  38. package/server/content-types/index.ts +1 -0
  39. package/server/controllers/controller.ts +107 -0
  40. package/server/controllers/index.ts +5 -0
  41. package/server/destroy.ts +5 -0
  42. package/server/index.ts +23 -0
  43. package/server/interface.ts +50 -0
  44. package/server/middlewares/index.ts +5 -0
  45. package/server/middlewares/middleware.ts +197 -0
  46. package/server/policies/index.ts +1 -0
  47. package/server/register.ts +14 -0
  48. package/server/routes/index.ts +27 -0
  49. package/server/services/index.ts +5 -0
  50. package/server/services/service.ts +11 -0
  51. package/server/utils.ts +14 -0
  52. package/strapi-admin.js +3 -0
  53. package/strapi-server.js +3 -0
  54. package/tsconfig.json +20 -0
  55. package/tsconfig.server.json +25 -0
@@ -0,0 +1,41 @@
1
+ import { useEffect, useMemo, useState } from "react"
2
+
3
+ import { MatchingContent } from "../interface"
4
+ import { fetchMatchingContent } from "../helpers/content"
5
+
6
+ export function useSearchedEntries(keyword: string, contentTypes: string, locale: string) {
7
+ const [loading, setLoading] = useState(false)
8
+ const [results, setResults] = useState<MatchingContent[]>([])
9
+ const [total, setTotal] = useState(0)
10
+
11
+ async function fetchEntries() {
12
+ setResults([])
13
+ setTotal(0)
14
+
15
+ if (loading || !keyword) return
16
+
17
+ try {
18
+ const { data, total } = await fetchMatchingContent(keyword, contentTypes, locale)
19
+
20
+ setResults(data)
21
+ setTotal(total)
22
+ } finally {
23
+ setLoading(false)
24
+ }
25
+ }
26
+
27
+ useEffect(() => {
28
+ const timeout = setTimeout(() => fetchEntries(), 500)
29
+
30
+ return () => clearTimeout(timeout)
31
+ }, [keyword])
32
+
33
+ return useMemo(
34
+ () => ({
35
+ loading,
36
+ results,
37
+ total
38
+ }),
39
+ [results, total]
40
+ )
41
+ }
@@ -0,0 +1,140 @@
1
+ import React, { ComponentType } from "react"
2
+ import { prefixPluginTranslations, CustomField } from "@strapi/helper-plugin"
3
+
4
+ import pluginPkg from "../../package.json"
5
+
6
+ import PluginIcon from "./components/PluginIcon"
7
+ import pluginId from "./pluginId"
8
+ import getTrad from "./utils/getTrad"
9
+ import { listContentTypes } from "./helpers/content"
10
+ import { setContentTypes } from "./helpers/storage"
11
+
12
+ const name = pluginPkg.strapi.name
13
+
14
+ export default {
15
+ async register(app) {
16
+ const contentTypes = await listContentTypes()
17
+ setContentTypes(contentTypes)
18
+
19
+ app.customFields.register({
20
+ name,
21
+ pluginId,
22
+ type: "richtext",
23
+ intlLabel: {
24
+ id: "multi-content-type-relation.text-ai.label",
25
+ defaultMessage: "Multi Content Type Relation"
26
+ },
27
+ intlDescription: {
28
+ id: "multi-content-type-relation.text-ai.description",
29
+ defaultMessage: "Write content types separated by commas"
30
+ },
31
+ icon: PluginIcon, // don't forget to create/import your icon component
32
+ components: {
33
+ Input: async () =>
34
+ import(/* webpackChunkName: "input-component" */ "./components/Input") as unknown as ComponentType
35
+ },
36
+ inputSize: {
37
+ default: 12,
38
+ isResizable: false
39
+ },
40
+ options: {
41
+ base: [
42
+ /*
43
+ Declare settings to be added to the "Base settings" section
44
+ of the field in the Content-Type Builder
45
+ */
46
+ {
47
+ sectionTitle: {
48
+ id: "multi-content-type-relation.text-ai.length",
49
+ defaultMessage: "Content types"
50
+ },
51
+ items: contentTypes.map((contentType) => {
52
+ const value = contentType.info.singularName
53
+
54
+ return {
55
+ intlLabel: {
56
+ id: `multi-content-type-relation.options.${contentType.uid}`,
57
+ defaultMessage: contentType.info.displayName
58
+ },
59
+ type: "checkbox",
60
+ name: `options.contentTypes.${value}`
61
+ }
62
+ })
63
+ }
64
+ ],
65
+ advanced: [
66
+ {
67
+ sectionTitle: {
68
+ id: "global.settings",
69
+ defaultMessage: "Settings"
70
+ },
71
+ items: [
72
+ {
73
+ name: "required",
74
+ type: "checkbox",
75
+ intlLabel: {
76
+ id: getTrad("content-type-relation-select.options.advanced.requiredField"),
77
+ defaultMessage: "Required field"
78
+ },
79
+ description: {
80
+ id: getTrad("content-type-relation-select.options.advanced.requiredField.description"),
81
+ defaultMessage: "You won't be able to create an entry if this field is empty"
82
+ }
83
+ },
84
+ {
85
+ name: "options.min",
86
+ type: "number",
87
+ intlLabel: {
88
+ id: getTrad("content-type-relation-select.options.advanced.minField"),
89
+ defaultMessage: "Minimum values"
90
+ },
91
+ description: {
92
+ id: getTrad("content-type-relation-select.options.advanced.minField.description"),
93
+ defaultMessage: "Minimum number of entries"
94
+ }
95
+ },
96
+ {
97
+ name: "options.max",
98
+ type: "number",
99
+ intlLabel: {
100
+ id: getTrad("content-type-relation-select.options.advanced.maxField"),
101
+ defaultMessage: "Maximum values"
102
+ },
103
+ description: {
104
+ id: getTrad("content-type-relation-select.options.advanced.maxField.description"),
105
+ defaultMessage: "Maximum number of entries"
106
+ }
107
+ }
108
+ ]
109
+ }
110
+ ]
111
+ }
112
+ } as CustomField)
113
+ },
114
+
115
+ bootstrap(app: any) {},
116
+
117
+ async registerTrads(app: any) {
118
+ const { locales } = app
119
+
120
+ const importedTrads = await Promise.all(
121
+ (locales as any[]).map((locale) => {
122
+ return import(`./translations/${locale}.json`)
123
+ .then(({ default: data }) => {
124
+ return {
125
+ data: prefixPluginTranslations(data, pluginId),
126
+ locale
127
+ }
128
+ })
129
+ .catch(() => {
130
+ return {
131
+ data: {},
132
+ locale
133
+ }
134
+ })
135
+ })
136
+ )
137
+
138
+ return Promise.resolve(importedTrads)
139
+ }
140
+ }
@@ -0,0 +1,37 @@
1
+ export type PluginOption = {
2
+ options: {
3
+ min: number
4
+ max: number
5
+ contentTypes: string
6
+ }
7
+ }
8
+
9
+ export type MatchingContent = {
10
+ uid: string
11
+ displayName: string
12
+ searchableField: string
13
+ results: {
14
+ id: string
15
+ [key: string]: any
16
+ }[]
17
+ }
18
+
19
+ export type MatchingContentResponse = {
20
+ data: MatchingContent[]
21
+ total: number
22
+ }
23
+
24
+ export type SelectedEntry = {
25
+ displayName: string
26
+ searchableField: string
27
+ uid: string
28
+ item: {
29
+ id: string
30
+ [key: string]: any
31
+ }
32
+ }
33
+
34
+ export type FormattedStrapiEntry = {
35
+ uid: string
36
+ id: string
37
+ }
@@ -0,0 +1,5 @@
1
+ import pluginPkg from '../../package.json';
2
+
3
+ const pluginId = pluginPkg.name.replace(/^(@[^-,.][\w,-]+\/|strapi-)plugin-/i, '');
4
+
5
+ export default pluginId;
@@ -0,0 +1 @@
1
+ {}
@@ -0,0 +1 @@
1
+ {}
@@ -0,0 +1,5 @@
1
+ import pluginId from '../pluginId';
2
+
3
+ const getTrad = (id: string) => `${pluginId}.${id}`;
4
+
5
+ export default getTrad;
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = ({ strapi }) => {
4
+ // bootstrap phase
5
+ };
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = {
4
+ default: ({ env }) => {
5
+ return {
6
+ recursive: {
7
+ enabled: false,
8
+ maxDepth: 1
9
+ },
10
+ debug: false
11
+ };
12
+ },
13
+ validator(config) {
14
+ if (typeof config.recursive !== "object") {
15
+ throw new Error("recursive must be an object");
16
+ }
17
+ if (typeof config.recursive.enabled !== "boolean") {
18
+ throw new Error("recursive.enabled must be a boolean");
19
+ }
20
+ if (typeof config.recursive.maxDepth !== "number") {
21
+ throw new Error("recursive.maxDepth must be a number");
22
+ }
23
+ if (typeof config.debug !== "boolean") {
24
+ throw new Error("Debug must be a boolean");
25
+ }
26
+ }
27
+ };
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = {};
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = ({ strapi }) => ({
4
+ getMatchingContent(ctx) {
5
+ const contentTypes = strapi.contentTypes;
6
+ const body = ctx.request.body;
7
+ const requestedContentTypes = body.contentTypes;
8
+ const keyword = body.keyword;
9
+ const locale = body.locale;
10
+ const mapping = requestedContentTypes.reduce((accumulator, contentType) => {
11
+ Object.keys(contentTypes).forEach((model) => {
12
+ const strapiContentType = contentTypes[model];
13
+ if (strapiContentType.info.singularName === contentType || strapiContentType.info.pluralName === contentType) {
14
+ accumulator[contentType] = {
15
+ uid: model,
16
+ displayName: contentTypes[model].info.displayName,
17
+ searchableField: strapi
18
+ .plugin("multi-content-type-relation")
19
+ .service("service")
20
+ .getFirstStringFieldInContentType(contentTypes[model])
21
+ };
22
+ }
23
+ });
24
+ return accumulator;
25
+ }, {});
26
+ const promises = Object.keys(mapping).map((contentType) => {
27
+ const uid = mapping[contentType].uid;
28
+ return strapi
29
+ .entityService.findMany(uid, {
30
+ filters: {
31
+ [mapping[contentType].searchableField]: {
32
+ $containsi: keyword
33
+ }
34
+ },
35
+ locale
36
+ })
37
+ .then((results) => {
38
+ var _a;
39
+ let contents = Array.isArray(results) ? results : typeof results === "object" && results ? [results] : [];
40
+ const contentTypeDefinition = strapi.contentType(uid);
41
+ if ((_a = contentTypeDefinition === null || contentTypeDefinition === void 0 ? void 0 : contentTypeDefinition.options) === null || _a === void 0 ? void 0 : _a.draftAndPublish) {
42
+ contents = contents.filter((content) => content.publishedAt !== null);
43
+ }
44
+ return {
45
+ uid,
46
+ displayName: mapping[contentType].displayName,
47
+ searchableField: mapping[contentType].searchableField,
48
+ results: contents
49
+ };
50
+ });
51
+ });
52
+ return Promise.all(promises);
53
+ },
54
+ validateRelations: async function (ctx) {
55
+ const contentTypes = strapi.contentTypes;
56
+ const body = ctx.request.body;
57
+ const entries = body.entries;
58
+ const promises = entries.map((entry) => {
59
+ return strapi
60
+ .entityService.findOne(entry.uid, entry.id, { populate: "deep" })
61
+ .then((result) => {
62
+ return {
63
+ uid: entry.uid,
64
+ result
65
+ };
66
+ });
67
+ });
68
+ const responses = await Promise.all(promises);
69
+ return responses
70
+ .map((response) => {
71
+ return {
72
+ displayName: contentTypes[response.uid].info.displayName,
73
+ uid: response.uid,
74
+ searchableField: strapi
75
+ .plugin("multi-content-type-relation")
76
+ .service("service")
77
+ .getFirstStringFieldInContentType(contentTypes[response.uid]),
78
+ item: response.result
79
+ };
80
+ })
81
+ .filter((entry) => entry.item);
82
+ },
83
+ listContentTypes: async function (ctx) {
84
+ const contentTypes = [];
85
+ for (const contentType of Object.values(strapi.contentTypes)) {
86
+ if ((contentType.kind === "collectionType" || contentType.kind === "singleType") && !contentType.plugin) {
87
+ contentTypes.push(contentType);
88
+ }
89
+ }
90
+ return contentTypes;
91
+ }
92
+ });
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const controller_1 = __importDefault(require("./controller"));
7
+ exports.default = {
8
+ controller: controller_1.default
9
+ };
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = ({ strapi }) => {
4
+ // destroy phase
5
+ };
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const register_1 = __importDefault(require("./register"));
7
+ const bootstrap_1 = __importDefault(require("./bootstrap"));
8
+ const destroy_1 = __importDefault(require("./destroy"));
9
+ const config_1 = __importDefault(require("./config"));
10
+ const content_types_1 = __importDefault(require("./content-types"));
11
+ const controllers_1 = __importDefault(require("./controllers"));
12
+ const routes_1 = __importDefault(require("./routes"));
13
+ const middlewares_1 = __importDefault(require("./middlewares"));
14
+ const policies_1 = __importDefault(require("./policies"));
15
+ const services_1 = __importDefault(require("./services"));
16
+ exports.default = {
17
+ register: register_1.default,
18
+ bootstrap: bootstrap_1.default,
19
+ destroy: destroy_1.default,
20
+ config: config_1.default,
21
+ controllers: controllers_1.default,
22
+ routes: routes_1.default,
23
+ services: services_1.default,
24
+ contentTypes: content_types_1.default,
25
+ policies: policies_1.default,
26
+ middlewares: middlewares_1.default,
27
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const middleware_1 = __importDefault(require("./middleware"));
7
+ exports.default = {
8
+ middleware: middleware_1.default
9
+ };
@@ -0,0 +1,163 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const utils_1 = require("../utils");
4
+ exports.default = async (ctx, next) => {
5
+ var _a, _b, _c, _d, _e;
6
+ await next();
7
+ if (!((_b = (_a = ctx === null || ctx === void 0 ? void 0 : ctx.request) === null || _a === void 0 ? void 0 : _a.url) === null || _b === void 0 ? void 0 : _b.startsWith("/api")))
8
+ return;
9
+ if (ctx.request.method !== "GET")
10
+ return;
11
+ if (!ctx.body)
12
+ return;
13
+ const configuration = (0, utils_1.getPluginConfiguration)();
14
+ const handler = ctx.state.route.handler;
15
+ const contentTypes = Object.keys(strapi.contentTypes);
16
+ (0, utils_1.log)(`URL: ${ctx.request.url} (${ctx.request.method})`);
17
+ (0, utils_1.log)(`Strapi Route: ${JSON.stringify(ctx.state.route, null, 2)}`);
18
+ const validHandler = contentTypes
19
+ .filter((contentType) => contentType.startsWith("api::"))
20
+ .some((contentType) => handler.includes(`${contentType}.findOne`) ||
21
+ handler.includes(`${contentType}.findMany`) ||
22
+ handler.includes(`${contentType}.find`));
23
+ (0, utils_1.log)(`Is valid handler: ${validHandler}`);
24
+ // Allow only findOne/findMany for native contentypes that have api::
25
+ if (!validHandler)
26
+ return;
27
+ const context = {
28
+ configuration,
29
+ publicationState: (_d = (_c = ctx.request.query) === null || _c === void 0 ? void 0 : _c["publicationState"]) !== null && _d !== void 0 ? _d : "live"
30
+ };
31
+ (0, utils_1.log)(" ----- ");
32
+ (0, utils_1.log)(`Context Body: ${JSON.stringify(ctx.body, null, 2)}`);
33
+ if (ctx.body.error || !((_e = ctx.body) === null || _e === void 0 ? void 0 : _e.data.attributes))
34
+ return;
35
+ const hydratedData = await augmentMRCT(ctx.body, 1, context);
36
+ ctx.body.data = hydratedData;
37
+ };
38
+ const augmentMRCT = async (strapiResponse, currentDepth, context) => {
39
+ if (Array.isArray(strapiResponse.data)) {
40
+ const promises = strapiResponse.data.map((item) => hydrateMRCT(item, currentDepth, context));
41
+ return await Promise.all(promises);
42
+ }
43
+ else {
44
+ return await hydrateMRCT(strapiResponse.data, currentDepth, context);
45
+ }
46
+ };
47
+ const hydrateMRCT = async (content, currentDepth, context) => {
48
+ const eligibleProperties = new Set();
49
+ const contentsToFetch = new Set();
50
+ const { configuration } = context;
51
+ const flattenedProperties = flattenObj(content.attributes, null);
52
+ for (const [key, value] of Object.entries(flattenedProperties)) {
53
+ if (typeof value !== "string" || !value.includes("MRCT"))
54
+ continue;
55
+ try {
56
+ const field = JSON.parse(value);
57
+ if (!Array.isArray(field))
58
+ continue;
59
+ for (const item of field) {
60
+ if (Object.keys(item).length !== 3 || (!item.uid && typeof item.uid !== "string") || !item.id)
61
+ continue;
62
+ const compositeID = `${item.uid}####${item.id}`;
63
+ eligibleProperties.add(key);
64
+ if (contentsToFetch.has(compositeID))
65
+ continue;
66
+ else
67
+ contentsToFetch.add(compositeID);
68
+ }
69
+ }
70
+ catch (e) {
71
+ continue;
72
+ }
73
+ }
74
+ if (!contentsToFetch.size)
75
+ return content;
76
+ (0, utils_1.log)(`Depth: ${currentDepth}, Hydrating MCTR for ID ${content.id}`);
77
+ const promises = [];
78
+ for (const item of Array.from(contentsToFetch)) {
79
+ const [uid, id] = item.split("####");
80
+ const promise = strapi.entityService
81
+ .findOne(uid, id, { populate: "deep" })
82
+ .then(async (response) => {
83
+ if (!response)
84
+ return { uid, response };
85
+ if (configuration.recursive.enabled && currentDepth < configuration.recursive.maxDepth) {
86
+ // Entity service serve the content flattened, so we need to rebuild the API format for the hydrate recursion
87
+ const hydratedResponse = await hydrateMRCT({
88
+ id: response.id,
89
+ attributes: response
90
+ }, currentDepth + 1, context);
91
+ return {
92
+ uid,
93
+ response: {
94
+ id: response.id,
95
+ attributes: hydratedResponse.attributes
96
+ }
97
+ };
98
+ }
99
+ else {
100
+ return {
101
+ uid,
102
+ response: {
103
+ id: response.id,
104
+ attributes: response
105
+ }
106
+ };
107
+ }
108
+ });
109
+ promises.push(promise);
110
+ }
111
+ const linkedEntries = await Promise.all(promises);
112
+ const filteredLinkedEntries = linkedEntries
113
+ .filter((linkedEntry) => Boolean(linkedEntry.response))
114
+ .filter((linkedEntry) => {
115
+ var _a, _b;
116
+ const contentTypeConfiguration = strapi.contentTypes[linkedEntry.uid];
117
+ if (!contentTypeConfiguration)
118
+ return true;
119
+ if (!((_a = contentTypeConfiguration.options) === null || _a === void 0 ? void 0 : _a.draftAndPublish))
120
+ return true;
121
+ if (context.publicationState === "preview")
122
+ return true;
123
+ return typeof ((_b = linkedEntry.response) === null || _b === void 0 ? void 0 : _b.attributes.publishedAt) === "string";
124
+ });
125
+ for (const key of Array.from(eligibleProperties)) {
126
+ const hydratedArray = [];
127
+ const unhydratedField = JSON.parse(flattenedProperties[key]);
128
+ for (const item of unhydratedField) {
129
+ const matchingContent = filteredLinkedEntries.find((linkedEntry) => item.uid === linkedEntry.uid && item.id === linkedEntry.response.id);
130
+ if (matchingContent) {
131
+ hydratedArray.push(matchingContent.response);
132
+ }
133
+ }
134
+ flattenedProperties[key] = hydratedArray;
135
+ }
136
+ const newContent = unflatten(flattenedProperties);
137
+ return {
138
+ ...content,
139
+ attributes: newContent
140
+ };
141
+ };
142
+ const flattenObj = (obj, parent, res = {}) => {
143
+ for (let key in obj) {
144
+ let propName = parent ? parent + "." + key : key;
145
+ if (typeof obj[key] == "object") {
146
+ flattenObj(obj[key], propName, res);
147
+ }
148
+ else {
149
+ res[propName] = obj[key];
150
+ }
151
+ }
152
+ return res;
153
+ };
154
+ const unflatten = (data) => {
155
+ var result = {};
156
+ for (var i in data) {
157
+ var keys = i.split(".");
158
+ keys.reduce(function (r, e, j) {
159
+ return r[e] || (r[e] = isNaN(Number(keys[j + 1])) ? (keys.length - 1 == j ? data[i] : {}) : []);
160
+ }, result);
161
+ }
162
+ return result;
163
+ };
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = {};
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const middlewares_1 = __importDefault(require("./middlewares"));
7
+ exports.default = ({ strapi }) => {
8
+ // register phase
9
+ strapi.customFields.register({
10
+ name: "multi-content-type-relation",
11
+ plugin: "multi-content-type-relation",
12
+ type: "richtext"
13
+ });
14
+ strapi.server.use(middlewares_1.default.middleware);
15
+ };
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = [
4
+ {
5
+ method: "GET",
6
+ path: "/list-content-types",
7
+ handler: "controller.listContentTypes",
8
+ config: {
9
+ policies: [],
10
+ auth: false
11
+ }
12
+ },
13
+ {
14
+ method: "POST",
15
+ path: "/get-content",
16
+ handler: "controller.getMatchingContent",
17
+ config: {
18
+ policies: []
19
+ }
20
+ },
21
+ {
22
+ method: "POST",
23
+ path: "/validate-relations",
24
+ handler: "controller.validateRelations",
25
+ config: {
26
+ policies: []
27
+ }
28
+ }
29
+ ];
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const service_1 = __importDefault(require("./service"));
7
+ exports.default = {
8
+ service: service_1.default
9
+ };
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = ({ strapi }) => ({
4
+ getFirstStringFieldInContentType(contentType) {
5
+ const result = Object.keys(contentType.attributes).find((attribute) => contentType.attributes[attribute].type === "string");
6
+ return result;
7
+ }
8
+ });