@mattisvensson/strapi-plugin-webatlas 0.11.5 → 0.11.6
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.
- package/README.md +5 -0
- package/dist/admin/RoutePicker-C6omysLv.mjs +101 -0
- package/dist/admin/RoutePicker-CZKBOemS.js +101 -0
- package/dist/admin/{SettingTitle-UPbQCuTC.mjs → SettingTitle-Bu_8jLRE.mjs} +1 -1
- package/dist/admin/{SettingTitle-DGSkTF7e.js → SettingTitle-DcQMSkno.js} +1 -1
- package/dist/admin/{index-b3yS9Dm7.mjs → index-Bp-XFH1V.mjs} +1 -1
- package/dist/admin/{index-CyzPvvul.js → index-CbllvYIN.js} +2 -2
- package/dist/admin/{index-BiXgA5Ru.js → index-CoLBgxOP.js} +1 -1
- package/dist/admin/{index-D4B4s4XO.mjs → index-DLMGEpc0.mjs} +2 -2
- package/dist/admin/{index-DLoZpHxU.mjs → index-DjLi4gIu.mjs} +1 -1
- package/dist/admin/{index-DcUmtUyp.js → index-Dok2SBFT.js} +2 -2
- package/dist/admin/{index-CrgTYzgl.mjs → index-DqA1gNsu.mjs} +2 -2
- package/dist/admin/{index-Dx-EVn-t.mjs → index-OkabmPb4.mjs} +47 -5
- package/dist/admin/{index-BztvKcfc.js → index-e8zVLBgW.js} +1 -1
- package/dist/admin/{index-Bm8lkiNL.js → index-ik3u6iCE.js} +47 -5
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/server/index.js +154 -13
- package/dist/server/index.mjs +154 -13
- package/dist/server/src/index.d.ts +3 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -34,6 +34,7 @@ This plugin is still in the early stages of development. Many features are plann
|
|
|
34
34
|
- **🧭 Multiple Navigations** Support for creating and managing multiple navigation structures. Whether it's a main menu, footer links, or a custom mobile drawer — organize your content into any number of navigations with drag-and-drop sorting, nested items, and visibility toggles.
|
|
35
35
|
- **🧩 Composable Component Integration** Use plugin-generated slugs and navigations directly in your frontend. Fetch routes and navigation data by slug and with a consistent API response, optimized for dynamic rendering.
|
|
36
36
|
- **🧠 Conflict Detection & Validation** Webatlas prevents slug collisions and helps avoid route conflicts by validating changes in real time. Get clear error messages and automatic suggestions when something doesn’t align.
|
|
37
|
+
- **🔗 RoutePicker Custom Field** Use the RoutePicker custom field to select routes within your content types, ensuring consistent linking and navigation throughout your application.
|
|
37
38
|
|
|
38
39
|
## ⏳ Installation
|
|
39
40
|
|
|
@@ -104,6 +105,10 @@ After selecting the source field for path generation in settings, you'll see the
|
|
|
104
105
|
|
|
105
106
|
**UID Path (UID Path)**: A permanent identifier path that never changes, used as a backup URL.
|
|
106
107
|
|
|
108
|
+
### RoutePicker Custom Field
|
|
109
|
+
|
|
110
|
+
The RoutePicker custom field allows you to select a route from the existing routes in your Strapi application. This ensures that links between content types are consistent and automatically updated when the route changes. You can use it for example in a "Button" component to link to a different page.
|
|
111
|
+
|
|
107
112
|
### API endpoints
|
|
108
113
|
|
|
109
114
|
Webatlas provides two API endpoints. One to fetch routes and one to fetch navigations.
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, useState, useEffect } from "react";
|
|
3
|
+
import { Field, Combobox, ComboboxOption, Box } from "@strapi/design-system";
|
|
4
|
+
import { ExternalLink, Link } from "@strapi/icons";
|
|
5
|
+
import { u as useApi } from "./index-OkabmPb4.mjs";
|
|
6
|
+
import "@strapi/strapi/admin";
|
|
7
|
+
const Input = forwardRef((props, ref) => {
|
|
8
|
+
const { attribute, disabled, placeholder, label, name, onChange, required, value, hint } = props;
|
|
9
|
+
const { getAllRoutes } = useApi();
|
|
10
|
+
const [routes, setRoutes] = useState([]);
|
|
11
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
12
|
+
const [displayValue, setDisplayValue] = useState("");
|
|
13
|
+
const isDisabled = disabled || isLoading;
|
|
14
|
+
const handleChange = (newValue) => {
|
|
15
|
+
onChange({
|
|
16
|
+
target: { name, type: attribute.type, value: encodeURI(newValue) }
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
const handleOptionCreate = (inputValue) => {
|
|
20
|
+
const linkValue = typeof inputValue === "string" ? inputValue : inputValue?.target?.value || String(inputValue || "");
|
|
21
|
+
if (linkValue) {
|
|
22
|
+
handleChange(linkValue);
|
|
23
|
+
setDisplayValue(encodeURI(linkValue));
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
const getStartIcon = () => {
|
|
27
|
+
if (!value) return null;
|
|
28
|
+
const matchedRoute = routes.find((route) => route.documentId === value);
|
|
29
|
+
if (matchedRoute) {
|
|
30
|
+
return /* @__PURE__ */ jsx(Link, { fill: "neutral500" });
|
|
31
|
+
}
|
|
32
|
+
return /* @__PURE__ */ jsx(ExternalLink, { fill: "neutral500" });
|
|
33
|
+
};
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
const fetchRoutes = async () => {
|
|
36
|
+
setIsLoading(true);
|
|
37
|
+
const fetchedRoutes = await getAllRoutes();
|
|
38
|
+
setRoutes(fetchedRoutes);
|
|
39
|
+
setIsLoading(false);
|
|
40
|
+
if (value) {
|
|
41
|
+
const matchedRoute = fetchedRoutes.find((route) => route.documentId === value);
|
|
42
|
+
if (matchedRoute) {
|
|
43
|
+
setDisplayValue(`${matchedRoute.title} (${matchedRoute.path})`);
|
|
44
|
+
} else {
|
|
45
|
+
setDisplayValue(value);
|
|
46
|
+
}
|
|
47
|
+
} else {
|
|
48
|
+
setDisplayValue("");
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
fetchRoutes();
|
|
52
|
+
}, [value]);
|
|
53
|
+
return /* @__PURE__ */ jsxs(Field.Root, { id: name, required, hint, disabled: isDisabled, children: [
|
|
54
|
+
/* @__PURE__ */ jsx(Field.Label, { children: label }),
|
|
55
|
+
/* @__PURE__ */ jsx(
|
|
56
|
+
Combobox,
|
|
57
|
+
{
|
|
58
|
+
ref,
|
|
59
|
+
placeholder,
|
|
60
|
+
"aria-label": label,
|
|
61
|
+
"aria-disabled": isDisabled,
|
|
62
|
+
value: value || "",
|
|
63
|
+
textValue: displayValue || "",
|
|
64
|
+
creatable: true,
|
|
65
|
+
onCreateOption: handleOptionCreate,
|
|
66
|
+
createMessage: (inputValue) => `Use external link: ${encodeURI(inputValue)}`,
|
|
67
|
+
creatableStartIcon: /* @__PURE__ */ jsx(ExternalLink, { fill: "neutral500" }),
|
|
68
|
+
startIcon: getStartIcon(),
|
|
69
|
+
onChange: (routeId) => {
|
|
70
|
+
if (!routeId) {
|
|
71
|
+
setDisplayValue("");
|
|
72
|
+
handleChange("");
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const selectedRoute = routes.find((r) => r.documentId === routeId);
|
|
76
|
+
if (selectedRoute) {
|
|
77
|
+
setDisplayValue(`${selectedRoute.title} (${selectedRoute.path})`);
|
|
78
|
+
handleChange(routeId);
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
onInputChange: (inputValue) => {
|
|
82
|
+
const textValue = typeof inputValue === "string" ? inputValue : inputValue?.target?.value || "";
|
|
83
|
+
setDisplayValue(textValue);
|
|
84
|
+
},
|
|
85
|
+
children: routes.map((route) => /* @__PURE__ */ jsxs(ComboboxOption, { value: route.documentId, children: [
|
|
86
|
+
route.title,
|
|
87
|
+
" (",
|
|
88
|
+
route.path,
|
|
89
|
+
")"
|
|
90
|
+
] }, route.documentId))
|
|
91
|
+
}
|
|
92
|
+
),
|
|
93
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
94
|
+
/* @__PURE__ */ jsx(Field.Hint, {}),
|
|
95
|
+
/* @__PURE__ */ jsx(Field.Error, {})
|
|
96
|
+
] })
|
|
97
|
+
] });
|
|
98
|
+
});
|
|
99
|
+
export {
|
|
100
|
+
Input as default
|
|
101
|
+
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
4
|
+
const React = require("react");
|
|
5
|
+
const designSystem = require("@strapi/design-system");
|
|
6
|
+
const icons = require("@strapi/icons");
|
|
7
|
+
const index = require("./index-ik3u6iCE.js");
|
|
8
|
+
require("@strapi/strapi/admin");
|
|
9
|
+
const Input = React.forwardRef((props, ref) => {
|
|
10
|
+
const { attribute, disabled, placeholder, label, name, onChange, required, value, hint } = props;
|
|
11
|
+
const { getAllRoutes } = index.useApi();
|
|
12
|
+
const [routes, setRoutes] = React.useState([]);
|
|
13
|
+
const [isLoading, setIsLoading] = React.useState(true);
|
|
14
|
+
const [displayValue, setDisplayValue] = React.useState("");
|
|
15
|
+
const isDisabled = disabled || isLoading;
|
|
16
|
+
const handleChange = (newValue) => {
|
|
17
|
+
onChange({
|
|
18
|
+
target: { name, type: attribute.type, value: encodeURI(newValue) }
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
const handleOptionCreate = (inputValue) => {
|
|
22
|
+
const linkValue = typeof inputValue === "string" ? inputValue : inputValue?.target?.value || String(inputValue || "");
|
|
23
|
+
if (linkValue) {
|
|
24
|
+
handleChange(linkValue);
|
|
25
|
+
setDisplayValue(encodeURI(linkValue));
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
const getStartIcon = () => {
|
|
29
|
+
if (!value) return null;
|
|
30
|
+
const matchedRoute = routes.find((route) => route.documentId === value);
|
|
31
|
+
if (matchedRoute) {
|
|
32
|
+
return /* @__PURE__ */ jsxRuntime.jsx(icons.Link, { fill: "neutral500" });
|
|
33
|
+
}
|
|
34
|
+
return /* @__PURE__ */ jsxRuntime.jsx(icons.ExternalLink, { fill: "neutral500" });
|
|
35
|
+
};
|
|
36
|
+
React.useEffect(() => {
|
|
37
|
+
const fetchRoutes = async () => {
|
|
38
|
+
setIsLoading(true);
|
|
39
|
+
const fetchedRoutes = await getAllRoutes();
|
|
40
|
+
setRoutes(fetchedRoutes);
|
|
41
|
+
setIsLoading(false);
|
|
42
|
+
if (value) {
|
|
43
|
+
const matchedRoute = fetchedRoutes.find((route) => route.documentId === value);
|
|
44
|
+
if (matchedRoute) {
|
|
45
|
+
setDisplayValue(`${matchedRoute.title} (${matchedRoute.path})`);
|
|
46
|
+
} else {
|
|
47
|
+
setDisplayValue(value);
|
|
48
|
+
}
|
|
49
|
+
} else {
|
|
50
|
+
setDisplayValue("");
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
fetchRoutes();
|
|
54
|
+
}, [value]);
|
|
55
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { id: name, required, hint, disabled: isDisabled, children: [
|
|
56
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: label }),
|
|
57
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
58
|
+
designSystem.Combobox,
|
|
59
|
+
{
|
|
60
|
+
ref,
|
|
61
|
+
placeholder,
|
|
62
|
+
"aria-label": label,
|
|
63
|
+
"aria-disabled": isDisabled,
|
|
64
|
+
value: value || "",
|
|
65
|
+
textValue: displayValue || "",
|
|
66
|
+
creatable: true,
|
|
67
|
+
onCreateOption: handleOptionCreate,
|
|
68
|
+
createMessage: (inputValue) => `Use external link: ${encodeURI(inputValue)}`,
|
|
69
|
+
creatableStartIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.ExternalLink, { fill: "neutral500" }),
|
|
70
|
+
startIcon: getStartIcon(),
|
|
71
|
+
onChange: (routeId) => {
|
|
72
|
+
if (!routeId) {
|
|
73
|
+
setDisplayValue("");
|
|
74
|
+
handleChange("");
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const selectedRoute = routes.find((r) => r.documentId === routeId);
|
|
78
|
+
if (selectedRoute) {
|
|
79
|
+
setDisplayValue(`${selectedRoute.title} (${selectedRoute.path})`);
|
|
80
|
+
handleChange(routeId);
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
onInputChange: (inputValue) => {
|
|
84
|
+
const textValue = typeof inputValue === "string" ? inputValue : inputValue?.target?.value || "";
|
|
85
|
+
setDisplayValue(textValue);
|
|
86
|
+
},
|
|
87
|
+
children: routes.map((route) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.ComboboxOption, { value: route.documentId, children: [
|
|
88
|
+
route.title,
|
|
89
|
+
" (",
|
|
90
|
+
route.path,
|
|
91
|
+
")"
|
|
92
|
+
] }, route.documentId))
|
|
93
|
+
}
|
|
94
|
+
),
|
|
95
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
|
|
96
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, {}),
|
|
97
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Error, {})
|
|
98
|
+
] })
|
|
99
|
+
] });
|
|
100
|
+
});
|
|
101
|
+
exports.default = Input;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Button, Flex, Typography, Box } from "@strapi/design-system";
|
|
3
3
|
import { Page, Layouts } from "@strapi/strapi/admin";
|
|
4
|
-
import { f as PLUGIN_NAME, g as getTranslation } from "./index-
|
|
4
|
+
import { f as PLUGIN_NAME, g as getTranslation } from "./index-OkabmPb4.mjs";
|
|
5
5
|
import { useIntl } from "react-intl";
|
|
6
6
|
import { Check } from "@strapi/icons";
|
|
7
7
|
function PageWrapper({
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
const jsxRuntime = require("react/jsx-runtime");
|
|
3
3
|
const designSystem = require("@strapi/design-system");
|
|
4
4
|
const admin = require("@strapi/strapi/admin");
|
|
5
|
-
const index = require("./index-
|
|
5
|
+
const index = require("./index-ik3u6iCE.js");
|
|
6
6
|
const reactIntl = require("react-intl");
|
|
7
7
|
const icons = require("@strapi/icons");
|
|
8
8
|
function PageWrapper({
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useState, useEffect, useMemo } from "react";
|
|
3
|
-
import { g as getTranslation, u as useApi, d as debounce, p as pluginPermissions } from "./index-
|
|
3
|
+
import { g as getTranslation, u as useApi, d as debounce, p as pluginPermissions } from "./index-OkabmPb4.mjs";
|
|
4
4
|
import { Page, Layouts, useNotification } from "@strapi/strapi/admin";
|
|
5
5
|
import { Grid, Box, Field, Thead, Tr, Th, Typography, VisuallyHidden, Td, Flex, LinkButton, Table, Tbody, EmptyStateLayout } from "@strapi/design-system";
|
|
6
6
|
import "@strapi/icons/symbols";
|
|
@@ -4,11 +4,11 @@ const jsxRuntime = require("react/jsx-runtime");
|
|
|
4
4
|
const React = require("react");
|
|
5
5
|
const designSystem = require("@strapi/design-system");
|
|
6
6
|
const admin = require("@strapi/strapi/admin");
|
|
7
|
-
const index = require("./index-
|
|
7
|
+
const index = require("./index-ik3u6iCE.js");
|
|
8
8
|
const reactIntl = require("react-intl");
|
|
9
9
|
require("@strapi/icons/symbols");
|
|
10
10
|
const FullLoader = require("./FullLoader-Da2n70bJ.js");
|
|
11
|
-
const SettingTitle = require("./SettingTitle-
|
|
11
|
+
const SettingTitle = require("./SettingTitle-DcQMSkno.js");
|
|
12
12
|
function reducer(newConfig, action) {
|
|
13
13
|
switch (action.type) {
|
|
14
14
|
case "SET_MAX_DEPTH":
|
|
@@ -7,7 +7,7 @@ const designSystem = require("@strapi/design-system");
|
|
|
7
7
|
const React = require("react");
|
|
8
8
|
const ReactDOM = require("react-dom");
|
|
9
9
|
const reactIntl = require("react-intl");
|
|
10
|
-
const index = require("./index-
|
|
10
|
+
const index = require("./index-ik3u6iCE.js");
|
|
11
11
|
const admin = require("@strapi/strapi/admin");
|
|
12
12
|
const FullLoader = require("./FullLoader-Da2n70bJ.js");
|
|
13
13
|
const symbols = require("@strapi/icons/symbols");
|
|
@@ -2,11 +2,11 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { useReducer, useState, useRef, useEffect } from "react";
|
|
3
3
|
import { Field } from "@strapi/design-system";
|
|
4
4
|
import { useNotification, Page } from "@strapi/strapi/admin";
|
|
5
|
-
import { c as usePluginConfig, g as getTranslation, p as pluginPermissions } from "./index-
|
|
5
|
+
import { c as usePluginConfig, g as getTranslation, p as pluginPermissions } from "./index-OkabmPb4.mjs";
|
|
6
6
|
import { useIntl } from "react-intl";
|
|
7
7
|
import "@strapi/icons/symbols";
|
|
8
8
|
import { F as FullLoader } from "./FullLoader-Btjb2W2p.mjs";
|
|
9
|
-
import { P as PageWrapper, C as ContentBox, S as SettingTitle } from "./SettingTitle-
|
|
9
|
+
import { P as PageWrapper, C as ContentBox, S as SettingTitle } from "./SettingTitle-Bu_8jLRE.mjs";
|
|
10
10
|
function reducer(newConfig, action) {
|
|
11
11
|
switch (action.type) {
|
|
12
12
|
case "SET_MAX_DEPTH":
|
|
@@ -5,7 +5,7 @@ import { Box, Typography, Dialog, Button, Modal, Flex, SingleSelect, SingleSelec
|
|
|
5
5
|
import { useState, useEffect, createContext, useRef, useContext, useMemo, useReducer, useCallback, forwardRef } from "react";
|
|
6
6
|
import { createPortal } from "react-dom";
|
|
7
7
|
import { useIntl } from "react-intl";
|
|
8
|
-
import { u as useApi, g as getTranslation, P as PLUGIN_ID, d as debounce, a as duplicateCheck, T as Tooltip, b as PathInfo, c as usePluginConfig, p as pluginPermissions } from "./index-
|
|
8
|
+
import { u as useApi, g as getTranslation, P as PLUGIN_ID, d as debounce, a as duplicateCheck, T as Tooltip, b as PathInfo, c as usePluginConfig, p as pluginPermissions } from "./index-OkabmPb4.mjs";
|
|
9
9
|
import { useNotification, useFetchClient, Page, Layouts } from "@strapi/strapi/admin";
|
|
10
10
|
import { C as Center, F as FullLoader } from "./FullLoader-Btjb2W2p.mjs";
|
|
11
11
|
import { EmptyDocuments } from "@strapi/icons/symbols";
|
|
@@ -4,11 +4,11 @@ const jsxRuntime = require("react/jsx-runtime");
|
|
|
4
4
|
const React = require("react");
|
|
5
5
|
const designSystem = require("@strapi/design-system");
|
|
6
6
|
const admin = require("@strapi/strapi/admin");
|
|
7
|
-
const index = require("./index-
|
|
7
|
+
const index = require("./index-ik3u6iCE.js");
|
|
8
8
|
const reactIntl = require("react-intl");
|
|
9
9
|
require("@strapi/icons/symbols");
|
|
10
10
|
const FullLoader = require("./FullLoader-Da2n70bJ.js");
|
|
11
|
-
const SettingTitle = require("./SettingTitle-
|
|
11
|
+
const SettingTitle = require("./SettingTitle-DcQMSkno.js");
|
|
12
12
|
const icons = require("@strapi/icons");
|
|
13
13
|
function ContentTypeAccordion({
|
|
14
14
|
contentType,
|
|
@@ -2,11 +2,11 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { useReducer, useState, useRef, useEffect } from "react";
|
|
3
3
|
import { Box, Accordion, Field, SingleSelect, SingleSelectOption, Typography, Link } from "@strapi/design-system";
|
|
4
4
|
import { useNotification, Page } from "@strapi/strapi/admin";
|
|
5
|
-
import { g as getTranslation, c as usePluginConfig, e as useAllContentTypes, p as pluginPermissions, f as PLUGIN_NAME, h as PLUGIN_VERSION } from "./index-
|
|
5
|
+
import { g as getTranslation, c as usePluginConfig, e as useAllContentTypes, p as pluginPermissions, f as PLUGIN_NAME, h as PLUGIN_VERSION } from "./index-OkabmPb4.mjs";
|
|
6
6
|
import { useIntl } from "react-intl";
|
|
7
7
|
import "@strapi/icons/symbols";
|
|
8
8
|
import { F as FullLoader } from "./FullLoader-Btjb2W2p.mjs";
|
|
9
|
-
import { P as PageWrapper, C as ContentBox, S as SettingTitle } from "./SettingTitle-
|
|
9
|
+
import { P as PageWrapper, C as ContentBox, S as SettingTitle } from "./SettingTitle-Bu_8jLRE.mjs";
|
|
10
10
|
import { ExternalLink } from "@strapi/icons";
|
|
11
11
|
function ContentTypeAccordion({
|
|
12
12
|
contentType,
|
|
@@ -52,7 +52,7 @@ function transformToUrl(input, replaceSlash = true) {
|
|
|
52
52
|
input = input.replace(/-+/g, "-");
|
|
53
53
|
return input;
|
|
54
54
|
}
|
|
55
|
-
const version = "0.11.
|
|
55
|
+
const version = "0.11.5";
|
|
56
56
|
const strapi$1 = { "name": "webatlas", "displayName": "Webatlas" };
|
|
57
57
|
const pluginPkg = {
|
|
58
58
|
version,
|
|
@@ -4151,7 +4151,7 @@ const index = {
|
|
|
4151
4151
|
id: `${PLUGIN_ID}.link.paths`,
|
|
4152
4152
|
defaultMessage: "Paths"
|
|
4153
4153
|
},
|
|
4154
|
-
Component: () => import("./index-
|
|
4154
|
+
Component: () => import("./index-Bp-XFH1V.mjs"),
|
|
4155
4155
|
permissions: [pluginPermissions["page.routes"][0]]
|
|
4156
4156
|
});
|
|
4157
4157
|
app.addMenuLink({
|
|
@@ -4161,7 +4161,7 @@ const index = {
|
|
|
4161
4161
|
id: `${PLUGIN_ID}.link.navigation`,
|
|
4162
4162
|
defaultMessage: "Navigation"
|
|
4163
4163
|
},
|
|
4164
|
-
Component: () => import("./index-
|
|
4164
|
+
Component: () => import("./index-DjLi4gIu.mjs"),
|
|
4165
4165
|
permissions: [pluginPermissions["page.navigation"][0]]
|
|
4166
4166
|
});
|
|
4167
4167
|
app.addSettingsLink(
|
|
@@ -4179,7 +4179,7 @@ const index = {
|
|
|
4179
4179
|
},
|
|
4180
4180
|
id: `${PLUGIN_ID}-general`,
|
|
4181
4181
|
to: `${PLUGIN_ID}/general`,
|
|
4182
|
-
Component: () => import("./index-
|
|
4182
|
+
Component: () => import("./index-DqA1gNsu.mjs"),
|
|
4183
4183
|
permissions: [pluginPermissions["settings.general"][0]]
|
|
4184
4184
|
}
|
|
4185
4185
|
);
|
|
@@ -4190,9 +4190,51 @@ const index = {
|
|
|
4190
4190
|
},
|
|
4191
4191
|
id: `${PLUGIN_ID}-navigation`,
|
|
4192
4192
|
to: `${PLUGIN_ID}/navigation`,
|
|
4193
|
-
Component: () => import("./index-
|
|
4193
|
+
Component: () => import("./index-DLMGEpc0.mjs"),
|
|
4194
4194
|
permissions: [pluginPermissions["settings.navigation"][0]]
|
|
4195
4195
|
});
|
|
4196
|
+
app.customFields.register({
|
|
4197
|
+
name: "route-picker",
|
|
4198
|
+
pluginId: PLUGIN_ID,
|
|
4199
|
+
type: "string",
|
|
4200
|
+
intlLabel: {
|
|
4201
|
+
id: `${PLUGIN_ID}.route-picker.label`,
|
|
4202
|
+
defaultMessage: "Route Picker"
|
|
4203
|
+
},
|
|
4204
|
+
intlDescription: {
|
|
4205
|
+
id: `${PLUGIN_ID}.route-picker.description`,
|
|
4206
|
+
defaultMessage: "Select a route"
|
|
4207
|
+
},
|
|
4208
|
+
components: {
|
|
4209
|
+
Input: async () => import("./RoutePicker-C6omysLv.mjs").then((module) => ({
|
|
4210
|
+
default: module.default
|
|
4211
|
+
}))
|
|
4212
|
+
},
|
|
4213
|
+
options: {
|
|
4214
|
+
advanced: [
|
|
4215
|
+
{
|
|
4216
|
+
sectionTitle: {
|
|
4217
|
+
id: "global.settings",
|
|
4218
|
+
defaultMessage: "Settings"
|
|
4219
|
+
},
|
|
4220
|
+
items: [
|
|
4221
|
+
{
|
|
4222
|
+
name: "required",
|
|
4223
|
+
type: "checkbox",
|
|
4224
|
+
intlLabel: {
|
|
4225
|
+
id: "content-type-builder.form.attribute.item.requiredField",
|
|
4226
|
+
defaultMessage: "Required field"
|
|
4227
|
+
},
|
|
4228
|
+
description: {
|
|
4229
|
+
id: "content-type-builder.form.attribute.item.requiredField.description",
|
|
4230
|
+
defaultMessage: "You won't be able to create an entry if this field is empty"
|
|
4231
|
+
}
|
|
4232
|
+
}
|
|
4233
|
+
]
|
|
4234
|
+
}
|
|
4235
|
+
]
|
|
4236
|
+
}
|
|
4237
|
+
});
|
|
4196
4238
|
app.registerPlugin({
|
|
4197
4239
|
id: PLUGIN_ID,
|
|
4198
4240
|
initializer: Initializer,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const jsxRuntime = require("react/jsx-runtime");
|
|
4
4
|
const React = require("react");
|
|
5
|
-
const index = require("./index-
|
|
5
|
+
const index = require("./index-ik3u6iCE.js");
|
|
6
6
|
const admin = require("@strapi/strapi/admin");
|
|
7
7
|
const designSystem = require("@strapi/design-system");
|
|
8
8
|
require("@strapi/icons/symbols");
|
|
@@ -70,7 +70,7 @@ function transformToUrl(input, replaceSlash = true) {
|
|
|
70
70
|
input = input.replace(/-+/g, "-");
|
|
71
71
|
return input;
|
|
72
72
|
}
|
|
73
|
-
const version = "0.11.
|
|
73
|
+
const version = "0.11.5";
|
|
74
74
|
const strapi$1 = { "name": "webatlas", "displayName": "Webatlas" };
|
|
75
75
|
const pluginPkg = {
|
|
76
76
|
version,
|
|
@@ -4169,7 +4169,7 @@ const index = {
|
|
|
4169
4169
|
id: `${PLUGIN_ID}.link.paths`,
|
|
4170
4170
|
defaultMessage: "Paths"
|
|
4171
4171
|
},
|
|
4172
|
-
Component: () => Promise.resolve().then(() => require("./index-
|
|
4172
|
+
Component: () => Promise.resolve().then(() => require("./index-e8zVLBgW.js")),
|
|
4173
4173
|
permissions: [pluginPermissions["page.routes"][0]]
|
|
4174
4174
|
});
|
|
4175
4175
|
app.addMenuLink({
|
|
@@ -4179,7 +4179,7 @@ const index = {
|
|
|
4179
4179
|
id: `${PLUGIN_ID}.link.navigation`,
|
|
4180
4180
|
defaultMessage: "Navigation"
|
|
4181
4181
|
},
|
|
4182
|
-
Component: () => Promise.resolve().then(() => require("./index-
|
|
4182
|
+
Component: () => Promise.resolve().then(() => require("./index-CoLBgxOP.js")),
|
|
4183
4183
|
permissions: [pluginPermissions["page.navigation"][0]]
|
|
4184
4184
|
});
|
|
4185
4185
|
app.addSettingsLink(
|
|
@@ -4197,7 +4197,7 @@ const index = {
|
|
|
4197
4197
|
},
|
|
4198
4198
|
id: `${PLUGIN_ID}-general`,
|
|
4199
4199
|
to: `${PLUGIN_ID}/general`,
|
|
4200
|
-
Component: () => Promise.resolve().then(() => require("./index-
|
|
4200
|
+
Component: () => Promise.resolve().then(() => require("./index-Dok2SBFT.js")),
|
|
4201
4201
|
permissions: [pluginPermissions["settings.general"][0]]
|
|
4202
4202
|
}
|
|
4203
4203
|
);
|
|
@@ -4208,9 +4208,51 @@ const index = {
|
|
|
4208
4208
|
},
|
|
4209
4209
|
id: `${PLUGIN_ID}-navigation`,
|
|
4210
4210
|
to: `${PLUGIN_ID}/navigation`,
|
|
4211
|
-
Component: () => Promise.resolve().then(() => require("./index-
|
|
4211
|
+
Component: () => Promise.resolve().then(() => require("./index-CbllvYIN.js")),
|
|
4212
4212
|
permissions: [pluginPermissions["settings.navigation"][0]]
|
|
4213
4213
|
});
|
|
4214
|
+
app.customFields.register({
|
|
4215
|
+
name: "route-picker",
|
|
4216
|
+
pluginId: PLUGIN_ID,
|
|
4217
|
+
type: "string",
|
|
4218
|
+
intlLabel: {
|
|
4219
|
+
id: `${PLUGIN_ID}.route-picker.label`,
|
|
4220
|
+
defaultMessage: "Route Picker"
|
|
4221
|
+
},
|
|
4222
|
+
intlDescription: {
|
|
4223
|
+
id: `${PLUGIN_ID}.route-picker.description`,
|
|
4224
|
+
defaultMessage: "Select a route"
|
|
4225
|
+
},
|
|
4226
|
+
components: {
|
|
4227
|
+
Input: async () => Promise.resolve().then(() => require("./RoutePicker-CZKBOemS.js")).then((module2) => ({
|
|
4228
|
+
default: module2.default
|
|
4229
|
+
}))
|
|
4230
|
+
},
|
|
4231
|
+
options: {
|
|
4232
|
+
advanced: [
|
|
4233
|
+
{
|
|
4234
|
+
sectionTitle: {
|
|
4235
|
+
id: "global.settings",
|
|
4236
|
+
defaultMessage: "Settings"
|
|
4237
|
+
},
|
|
4238
|
+
items: [
|
|
4239
|
+
{
|
|
4240
|
+
name: "required",
|
|
4241
|
+
type: "checkbox",
|
|
4242
|
+
intlLabel: {
|
|
4243
|
+
id: "content-type-builder.form.attribute.item.requiredField",
|
|
4244
|
+
defaultMessage: "Required field"
|
|
4245
|
+
},
|
|
4246
|
+
description: {
|
|
4247
|
+
id: "content-type-builder.form.attribute.item.requiredField.description",
|
|
4248
|
+
defaultMessage: "You won't be able to create an entry if this field is empty"
|
|
4249
|
+
}
|
|
4250
|
+
}
|
|
4251
|
+
]
|
|
4252
|
+
}
|
|
4253
|
+
]
|
|
4254
|
+
}
|
|
4255
|
+
});
|
|
4214
4256
|
app.registerPlugin({
|
|
4215
4257
|
id: PLUGIN_ID,
|
|
4216
4258
|
initializer: Initializer,
|
package/dist/admin/index.js
CHANGED
package/dist/admin/index.mjs
CHANGED
package/dist/server/index.js
CHANGED
|
@@ -4665,8 +4665,6 @@ function calculateParentAndOrder({
|
|
|
4665
4665
|
};
|
|
4666
4666
|
}
|
|
4667
4667
|
async function enrichWebatlasData(data, contentTypeUid) {
|
|
4668
|
-
console.log("Enriching data with webatlas fields...", { contentTypeUid });
|
|
4669
|
-
console.log("Original data:", JSON.stringify(data, null, 2));
|
|
4670
4668
|
if (!data || typeof data !== "object") return data;
|
|
4671
4669
|
const collectDocumentIds = (obj, uid) => {
|
|
4672
4670
|
if (!obj || typeof obj !== "object") return [];
|
|
@@ -4714,7 +4712,7 @@ async function enrichWebatlasData(data, contentTypeUid) {
|
|
|
4714
4712
|
select: ["relatedDocumentId", "relatedContentType", "path", "canonicalPath", "slug", "uidPath"]
|
|
4715
4713
|
});
|
|
4716
4714
|
const routeMap = new Map(routes2?.map((route2) => [route2.relatedDocumentId, route2]) || []);
|
|
4717
|
-
const
|
|
4715
|
+
const enrichEntity2 = (entity, uid) => {
|
|
4718
4716
|
if (!entity || typeof entity !== "object") return entity;
|
|
4719
4717
|
const ct = uid ? strapi.contentTypes[uid] : null;
|
|
4720
4718
|
const isWebatlasEnabled = ct?.pluginOptions?.webatlas?.enabled === true;
|
|
@@ -4732,37 +4730,138 @@ async function enrichWebatlasData(data, contentTypeUid) {
|
|
|
4732
4730
|
const attribute = attr;
|
|
4733
4731
|
if (attribute.type === "relation" && entity[key]) {
|
|
4734
4732
|
const targetUid = attribute.target;
|
|
4735
|
-
strapi.log.debug("relation found");
|
|
4736
4733
|
if (Array.isArray(entity[key])) {
|
|
4737
4734
|
entity[key] = entity[key].map(
|
|
4738
|
-
(item) => item ?
|
|
4735
|
+
(item) => item ? enrichEntity2(item, targetUid) : item
|
|
4739
4736
|
);
|
|
4740
4737
|
} else {
|
|
4741
|
-
entity[key] =
|
|
4738
|
+
entity[key] = enrichEntity2(entity[key], targetUid);
|
|
4742
4739
|
}
|
|
4743
4740
|
}
|
|
4744
4741
|
if (attribute.type === "component" && entity[key]) {
|
|
4745
4742
|
const componentUid = attribute.component;
|
|
4746
4743
|
if (Array.isArray(entity[key])) {
|
|
4747
4744
|
entity[key] = entity[key].map(
|
|
4748
|
-
(item) => item ?
|
|
4745
|
+
(item) => item ? enrichEntity2(item, componentUid) : item
|
|
4749
4746
|
);
|
|
4750
4747
|
} else {
|
|
4751
|
-
entity[key] =
|
|
4748
|
+
entity[key] = enrichEntity2(entity[key], componentUid);
|
|
4752
4749
|
}
|
|
4753
4750
|
}
|
|
4754
4751
|
if (attribute.type === "dynamiczone" && Array.isArray(entity[key])) {
|
|
4755
4752
|
entity[key] = entity[key].map((item) => {
|
|
4756
4753
|
if (!item || !item.__component) return item;
|
|
4757
|
-
return
|
|
4754
|
+
return enrichEntity2(item, item.__component);
|
|
4758
4755
|
});
|
|
4759
4756
|
}
|
|
4760
4757
|
}
|
|
4761
4758
|
}
|
|
4762
|
-
strapi.log.debug("finished - returning");
|
|
4763
4759
|
return entity;
|
|
4764
4760
|
};
|
|
4765
|
-
return
|
|
4761
|
+
return enrichEntity2(data, contentTypeUid);
|
|
4762
|
+
}
|
|
4763
|
+
function isExternalUrl(value) {
|
|
4764
|
+
return !/^[a-z0-9]+$/.test(value);
|
|
4765
|
+
}
|
|
4766
|
+
function collectRoutePickerIds(obj, uid) {
|
|
4767
|
+
if (!obj || typeof obj !== "object") return [];
|
|
4768
|
+
const ids = [];
|
|
4769
|
+
if (!uid) return ids;
|
|
4770
|
+
const ct = strapi.contentTypes[uid] || strapi.components?.[uid];
|
|
4771
|
+
if (!ct?.attributes) return ids;
|
|
4772
|
+
for (const [key, attr] of Object.entries(ct.attributes)) {
|
|
4773
|
+
const attribute = attr;
|
|
4774
|
+
if (attribute.customField === "plugin::webatlas.route-picker" && obj[key]) {
|
|
4775
|
+
const value = obj[key];
|
|
4776
|
+
if (typeof value === "string" && !isExternalUrl(value)) {
|
|
4777
|
+
ids.push(value);
|
|
4778
|
+
}
|
|
4779
|
+
}
|
|
4780
|
+
if (attribute.type === "relation" && obj[key]) {
|
|
4781
|
+
const targetUid = attribute.target;
|
|
4782
|
+
if (Array.isArray(obj[key])) {
|
|
4783
|
+
ids.push(...obj[key].flatMap((item) => collectRoutePickerIds(item, targetUid)));
|
|
4784
|
+
} else {
|
|
4785
|
+
ids.push(...collectRoutePickerIds(obj[key], targetUid));
|
|
4786
|
+
}
|
|
4787
|
+
}
|
|
4788
|
+
if (attribute.type === "component" && obj[key]) {
|
|
4789
|
+
const componentUid = attribute.component;
|
|
4790
|
+
if (Array.isArray(obj[key])) {
|
|
4791
|
+
ids.push(...obj[key].flatMap((item) => collectRoutePickerIds(item, componentUid)));
|
|
4792
|
+
} else {
|
|
4793
|
+
ids.push(...collectRoutePickerIds(obj[key], componentUid));
|
|
4794
|
+
}
|
|
4795
|
+
}
|
|
4796
|
+
if (attribute.type === "dynamiczone" && Array.isArray(obj[key])) {
|
|
4797
|
+
ids.push(
|
|
4798
|
+
...obj[key].flatMap((item) => {
|
|
4799
|
+
if (!item || !item.__component) return [];
|
|
4800
|
+
return collectRoutePickerIds(item, item.__component);
|
|
4801
|
+
})
|
|
4802
|
+
);
|
|
4803
|
+
}
|
|
4804
|
+
}
|
|
4805
|
+
return ids;
|
|
4806
|
+
}
|
|
4807
|
+
function enrichEntity(entity, uid, routeMap) {
|
|
4808
|
+
if (!entity || typeof entity !== "object") return entity;
|
|
4809
|
+
const ct = strapi.contentTypes[uid] || strapi.components?.[uid];
|
|
4810
|
+
if (!ct?.attributes) return entity;
|
|
4811
|
+
for (const [key, attr] of Object.entries(ct.attributes)) {
|
|
4812
|
+
const attribute = attr;
|
|
4813
|
+
if (attribute.customField === "plugin::webatlas.route-picker" && entity[key]) {
|
|
4814
|
+
const value = entity[key];
|
|
4815
|
+
if (typeof value === "string" && !isExternalUrl(value)) {
|
|
4816
|
+
const route2 = routeMap.get(value);
|
|
4817
|
+
if (route2) {
|
|
4818
|
+
entity[key] = route2.canonicalPath || route2.path || value;
|
|
4819
|
+
}
|
|
4820
|
+
}
|
|
4821
|
+
}
|
|
4822
|
+
if (attribute.type === "relation" && entity[key]) {
|
|
4823
|
+
const targetUid = attribute.target;
|
|
4824
|
+
if (Array.isArray(entity[key])) {
|
|
4825
|
+
entity[key] = entity[key].map(
|
|
4826
|
+
(item) => item ? enrichEntity(item, targetUid, routeMap) : item
|
|
4827
|
+
);
|
|
4828
|
+
} else {
|
|
4829
|
+
entity[key] = enrichEntity(entity[key], targetUid, routeMap);
|
|
4830
|
+
}
|
|
4831
|
+
}
|
|
4832
|
+
if (attribute.type === "component" && entity[key]) {
|
|
4833
|
+
const componentUid = attribute.component;
|
|
4834
|
+
if (Array.isArray(entity[key])) {
|
|
4835
|
+
entity[key] = entity[key].map(
|
|
4836
|
+
(item) => item ? enrichEntity(item, componentUid, routeMap) : item
|
|
4837
|
+
);
|
|
4838
|
+
} else {
|
|
4839
|
+
entity[key] = enrichEntity(entity[key], componentUid, routeMap);
|
|
4840
|
+
}
|
|
4841
|
+
}
|
|
4842
|
+
if (attribute.type === "dynamiczone" && Array.isArray(entity[key])) {
|
|
4843
|
+
entity[key] = entity[key].map((item) => {
|
|
4844
|
+
if (!item || !item.__component) return item;
|
|
4845
|
+
return enrichEntity(item, item.__component, routeMap);
|
|
4846
|
+
});
|
|
4847
|
+
}
|
|
4848
|
+
}
|
|
4849
|
+
return entity;
|
|
4850
|
+
}
|
|
4851
|
+
async function enrichRoutePickerFields(data, contentTypeUid) {
|
|
4852
|
+
if (!data || typeof data !== "object") return data;
|
|
4853
|
+
const documentIds = collectRoutePickerIds(data, contentTypeUid);
|
|
4854
|
+
if (documentIds.length === 0) return data;
|
|
4855
|
+
const uniqueDocumentIds = [...new Set(documentIds)];
|
|
4856
|
+
const routes2 = await strapi.db?.query(waRoute).findMany({
|
|
4857
|
+
where: {
|
|
4858
|
+
documentId: { $in: uniqueDocumentIds }
|
|
4859
|
+
},
|
|
4860
|
+
select: ["documentId", "path", "canonicalPath"]
|
|
4861
|
+
});
|
|
4862
|
+
const routeMap = new Map(routes2?.map((route2) => [route2.documentId, route2]) || []);
|
|
4863
|
+
const enriched = enrichEntity(data, contentTypeUid, routeMap);
|
|
4864
|
+
return enriched;
|
|
4766
4865
|
}
|
|
4767
4866
|
const migration_001_canonical_path = {
|
|
4768
4867
|
version: "001",
|
|
@@ -5186,7 +5285,7 @@ const addWebatlasField = ({ strapi: strapi2 }) => {
|
|
|
5186
5285
|
([uid, ct]) => ct.info?.pluralName === apiEndpoint
|
|
5187
5286
|
);
|
|
5188
5287
|
if (!contentTypeEntry) return;
|
|
5189
|
-
const [contentTypeUid
|
|
5288
|
+
const [contentTypeUid] = contentTypeEntry;
|
|
5190
5289
|
if (ctx.body.data) {
|
|
5191
5290
|
if (Array.isArray(ctx.body.data)) {
|
|
5192
5291
|
ctx.body.data = await Promise.all(
|
|
@@ -5202,8 +5301,43 @@ const addWebatlasField = ({ strapi: strapi2 }) => {
|
|
|
5202
5301
|
}
|
|
5203
5302
|
};
|
|
5204
5303
|
};
|
|
5304
|
+
const enrichRoutePicker = ({ strapi: strapi2 }) => {
|
|
5305
|
+
return async (ctx, next) => {
|
|
5306
|
+
await next();
|
|
5307
|
+
if (!ctx.request.url.startsWith("/api/")) {
|
|
5308
|
+
return;
|
|
5309
|
+
}
|
|
5310
|
+
if (!ctx.body || ctx.status !== 200) {
|
|
5311
|
+
return;
|
|
5312
|
+
}
|
|
5313
|
+
const urlMatch = ctx.request.url.match(/^\/api\/([^/?]+)/);
|
|
5314
|
+
if (!urlMatch) return;
|
|
5315
|
+
const apiEndpoint = urlMatch[1];
|
|
5316
|
+
const contentTypeEntry = Object.entries(strapi2.contentTypes).find(
|
|
5317
|
+
([uid, ct]) => ct.info?.pluralName === apiEndpoint
|
|
5318
|
+
);
|
|
5319
|
+
if (!contentTypeEntry) return;
|
|
5320
|
+
const [contentTypeUid] = contentTypeEntry;
|
|
5321
|
+
if (ctx.body.data) {
|
|
5322
|
+
if (Array.isArray(ctx.body.data)) {
|
|
5323
|
+
ctx.body.data = await Promise.all(
|
|
5324
|
+
ctx.body.data.map((item) => enrichRoutePickerFields(item, contentTypeUid))
|
|
5325
|
+
);
|
|
5326
|
+
} else {
|
|
5327
|
+
ctx.body.data = await enrichRoutePickerFields(ctx.body.data, contentTypeUid);
|
|
5328
|
+
}
|
|
5329
|
+
} else if (Array.isArray(ctx.body)) {
|
|
5330
|
+
ctx.body = await Promise.all(
|
|
5331
|
+
ctx.body.map((item) => enrichRoutePickerFields(item, contentTypeUid))
|
|
5332
|
+
);
|
|
5333
|
+
} else if (typeof ctx.body === "object") {
|
|
5334
|
+
ctx.body = await enrichRoutePickerFields(ctx.body, contentTypeUid);
|
|
5335
|
+
}
|
|
5336
|
+
};
|
|
5337
|
+
};
|
|
5205
5338
|
const middlewares = {
|
|
5206
|
-
addWebatlasField
|
|
5339
|
+
addWebatlasField,
|
|
5340
|
+
enrichRoutePicker
|
|
5207
5341
|
};
|
|
5208
5342
|
const bootstrap = async ({ strapi: strapi2 }) => {
|
|
5209
5343
|
try {
|
|
@@ -5217,6 +5351,7 @@ const bootstrap = async ({ strapi: strapi2 }) => {
|
|
|
5217
5351
|
documentMiddleware(strapi2, enabledContentTypes, config2);
|
|
5218
5352
|
webatlasMiddleware(strapi2);
|
|
5219
5353
|
strapi2.server.use(middlewares.addWebatlasField({ strapi: strapi2 }));
|
|
5354
|
+
strapi2.server.use(middlewares.enrichRoutePicker({ strapi: strapi2 }));
|
|
5220
5355
|
} catch (error) {
|
|
5221
5356
|
strapi2.log.error(`Bootstrap failed. ${String(error)}`);
|
|
5222
5357
|
}
|
|
@@ -5224,6 +5359,11 @@ const bootstrap = async ({ strapi: strapi2 }) => {
|
|
|
5224
5359
|
const destroy = ({ strapi: strapi2 }) => {
|
|
5225
5360
|
};
|
|
5226
5361
|
const register = ({ strapi: strapi2 }) => {
|
|
5362
|
+
strapi2.customFields.register({
|
|
5363
|
+
name: "route-picker",
|
|
5364
|
+
plugin: PLUGIN_ID,
|
|
5365
|
+
type: "string"
|
|
5366
|
+
});
|
|
5227
5367
|
};
|
|
5228
5368
|
const config = {
|
|
5229
5369
|
default: {
|
|
@@ -6087,6 +6227,7 @@ const client = ({ strapi: strapi2 }) => ({
|
|
|
6087
6227
|
let cleanEntity = cleanRootKeys(entity);
|
|
6088
6228
|
cleanEntity = removeWaFields(cleanEntity);
|
|
6089
6229
|
cleanEntity = await enrichWebatlasData(cleanEntity, route2.relatedContentType);
|
|
6230
|
+
cleanEntity = await enrichRoutePickerFields(cleanEntity, contentTypeKey);
|
|
6090
6231
|
return {
|
|
6091
6232
|
contentType: contentType.info.singularName,
|
|
6092
6233
|
...cleanEntity
|
package/dist/server/index.mjs
CHANGED
|
@@ -4663,8 +4663,6 @@ function calculateParentAndOrder({
|
|
|
4663
4663
|
};
|
|
4664
4664
|
}
|
|
4665
4665
|
async function enrichWebatlasData(data, contentTypeUid) {
|
|
4666
|
-
console.log("Enriching data with webatlas fields...", { contentTypeUid });
|
|
4667
|
-
console.log("Original data:", JSON.stringify(data, null, 2));
|
|
4668
4666
|
if (!data || typeof data !== "object") return data;
|
|
4669
4667
|
const collectDocumentIds = (obj, uid) => {
|
|
4670
4668
|
if (!obj || typeof obj !== "object") return [];
|
|
@@ -4712,7 +4710,7 @@ async function enrichWebatlasData(data, contentTypeUid) {
|
|
|
4712
4710
|
select: ["relatedDocumentId", "relatedContentType", "path", "canonicalPath", "slug", "uidPath"]
|
|
4713
4711
|
});
|
|
4714
4712
|
const routeMap = new Map(routes2?.map((route2) => [route2.relatedDocumentId, route2]) || []);
|
|
4715
|
-
const
|
|
4713
|
+
const enrichEntity2 = (entity, uid) => {
|
|
4716
4714
|
if (!entity || typeof entity !== "object") return entity;
|
|
4717
4715
|
const ct = uid ? strapi.contentTypes[uid] : null;
|
|
4718
4716
|
const isWebatlasEnabled = ct?.pluginOptions?.webatlas?.enabled === true;
|
|
@@ -4730,37 +4728,138 @@ async function enrichWebatlasData(data, contentTypeUid) {
|
|
|
4730
4728
|
const attribute = attr;
|
|
4731
4729
|
if (attribute.type === "relation" && entity[key]) {
|
|
4732
4730
|
const targetUid = attribute.target;
|
|
4733
|
-
strapi.log.debug("relation found");
|
|
4734
4731
|
if (Array.isArray(entity[key])) {
|
|
4735
4732
|
entity[key] = entity[key].map(
|
|
4736
|
-
(item) => item ?
|
|
4733
|
+
(item) => item ? enrichEntity2(item, targetUid) : item
|
|
4737
4734
|
);
|
|
4738
4735
|
} else {
|
|
4739
|
-
entity[key] =
|
|
4736
|
+
entity[key] = enrichEntity2(entity[key], targetUid);
|
|
4740
4737
|
}
|
|
4741
4738
|
}
|
|
4742
4739
|
if (attribute.type === "component" && entity[key]) {
|
|
4743
4740
|
const componentUid = attribute.component;
|
|
4744
4741
|
if (Array.isArray(entity[key])) {
|
|
4745
4742
|
entity[key] = entity[key].map(
|
|
4746
|
-
(item) => item ?
|
|
4743
|
+
(item) => item ? enrichEntity2(item, componentUid) : item
|
|
4747
4744
|
);
|
|
4748
4745
|
} else {
|
|
4749
|
-
entity[key] =
|
|
4746
|
+
entity[key] = enrichEntity2(entity[key], componentUid);
|
|
4750
4747
|
}
|
|
4751
4748
|
}
|
|
4752
4749
|
if (attribute.type === "dynamiczone" && Array.isArray(entity[key])) {
|
|
4753
4750
|
entity[key] = entity[key].map((item) => {
|
|
4754
4751
|
if (!item || !item.__component) return item;
|
|
4755
|
-
return
|
|
4752
|
+
return enrichEntity2(item, item.__component);
|
|
4756
4753
|
});
|
|
4757
4754
|
}
|
|
4758
4755
|
}
|
|
4759
4756
|
}
|
|
4760
|
-
strapi.log.debug("finished - returning");
|
|
4761
4757
|
return entity;
|
|
4762
4758
|
};
|
|
4763
|
-
return
|
|
4759
|
+
return enrichEntity2(data, contentTypeUid);
|
|
4760
|
+
}
|
|
4761
|
+
function isExternalUrl(value) {
|
|
4762
|
+
return !/^[a-z0-9]+$/.test(value);
|
|
4763
|
+
}
|
|
4764
|
+
function collectRoutePickerIds(obj, uid) {
|
|
4765
|
+
if (!obj || typeof obj !== "object") return [];
|
|
4766
|
+
const ids = [];
|
|
4767
|
+
if (!uid) return ids;
|
|
4768
|
+
const ct = strapi.contentTypes[uid] || strapi.components?.[uid];
|
|
4769
|
+
if (!ct?.attributes) return ids;
|
|
4770
|
+
for (const [key, attr] of Object.entries(ct.attributes)) {
|
|
4771
|
+
const attribute = attr;
|
|
4772
|
+
if (attribute.customField === "plugin::webatlas.route-picker" && obj[key]) {
|
|
4773
|
+
const value = obj[key];
|
|
4774
|
+
if (typeof value === "string" && !isExternalUrl(value)) {
|
|
4775
|
+
ids.push(value);
|
|
4776
|
+
}
|
|
4777
|
+
}
|
|
4778
|
+
if (attribute.type === "relation" && obj[key]) {
|
|
4779
|
+
const targetUid = attribute.target;
|
|
4780
|
+
if (Array.isArray(obj[key])) {
|
|
4781
|
+
ids.push(...obj[key].flatMap((item) => collectRoutePickerIds(item, targetUid)));
|
|
4782
|
+
} else {
|
|
4783
|
+
ids.push(...collectRoutePickerIds(obj[key], targetUid));
|
|
4784
|
+
}
|
|
4785
|
+
}
|
|
4786
|
+
if (attribute.type === "component" && obj[key]) {
|
|
4787
|
+
const componentUid = attribute.component;
|
|
4788
|
+
if (Array.isArray(obj[key])) {
|
|
4789
|
+
ids.push(...obj[key].flatMap((item) => collectRoutePickerIds(item, componentUid)));
|
|
4790
|
+
} else {
|
|
4791
|
+
ids.push(...collectRoutePickerIds(obj[key], componentUid));
|
|
4792
|
+
}
|
|
4793
|
+
}
|
|
4794
|
+
if (attribute.type === "dynamiczone" && Array.isArray(obj[key])) {
|
|
4795
|
+
ids.push(
|
|
4796
|
+
...obj[key].flatMap((item) => {
|
|
4797
|
+
if (!item || !item.__component) return [];
|
|
4798
|
+
return collectRoutePickerIds(item, item.__component);
|
|
4799
|
+
})
|
|
4800
|
+
);
|
|
4801
|
+
}
|
|
4802
|
+
}
|
|
4803
|
+
return ids;
|
|
4804
|
+
}
|
|
4805
|
+
function enrichEntity(entity, uid, routeMap) {
|
|
4806
|
+
if (!entity || typeof entity !== "object") return entity;
|
|
4807
|
+
const ct = strapi.contentTypes[uid] || strapi.components?.[uid];
|
|
4808
|
+
if (!ct?.attributes) return entity;
|
|
4809
|
+
for (const [key, attr] of Object.entries(ct.attributes)) {
|
|
4810
|
+
const attribute = attr;
|
|
4811
|
+
if (attribute.customField === "plugin::webatlas.route-picker" && entity[key]) {
|
|
4812
|
+
const value = entity[key];
|
|
4813
|
+
if (typeof value === "string" && !isExternalUrl(value)) {
|
|
4814
|
+
const route2 = routeMap.get(value);
|
|
4815
|
+
if (route2) {
|
|
4816
|
+
entity[key] = route2.canonicalPath || route2.path || value;
|
|
4817
|
+
}
|
|
4818
|
+
}
|
|
4819
|
+
}
|
|
4820
|
+
if (attribute.type === "relation" && entity[key]) {
|
|
4821
|
+
const targetUid = attribute.target;
|
|
4822
|
+
if (Array.isArray(entity[key])) {
|
|
4823
|
+
entity[key] = entity[key].map(
|
|
4824
|
+
(item) => item ? enrichEntity(item, targetUid, routeMap) : item
|
|
4825
|
+
);
|
|
4826
|
+
} else {
|
|
4827
|
+
entity[key] = enrichEntity(entity[key], targetUid, routeMap);
|
|
4828
|
+
}
|
|
4829
|
+
}
|
|
4830
|
+
if (attribute.type === "component" && entity[key]) {
|
|
4831
|
+
const componentUid = attribute.component;
|
|
4832
|
+
if (Array.isArray(entity[key])) {
|
|
4833
|
+
entity[key] = entity[key].map(
|
|
4834
|
+
(item) => item ? enrichEntity(item, componentUid, routeMap) : item
|
|
4835
|
+
);
|
|
4836
|
+
} else {
|
|
4837
|
+
entity[key] = enrichEntity(entity[key], componentUid, routeMap);
|
|
4838
|
+
}
|
|
4839
|
+
}
|
|
4840
|
+
if (attribute.type === "dynamiczone" && Array.isArray(entity[key])) {
|
|
4841
|
+
entity[key] = entity[key].map((item) => {
|
|
4842
|
+
if (!item || !item.__component) return item;
|
|
4843
|
+
return enrichEntity(item, item.__component, routeMap);
|
|
4844
|
+
});
|
|
4845
|
+
}
|
|
4846
|
+
}
|
|
4847
|
+
return entity;
|
|
4848
|
+
}
|
|
4849
|
+
async function enrichRoutePickerFields(data, contentTypeUid) {
|
|
4850
|
+
if (!data || typeof data !== "object") return data;
|
|
4851
|
+
const documentIds = collectRoutePickerIds(data, contentTypeUid);
|
|
4852
|
+
if (documentIds.length === 0) return data;
|
|
4853
|
+
const uniqueDocumentIds = [...new Set(documentIds)];
|
|
4854
|
+
const routes2 = await strapi.db?.query(waRoute).findMany({
|
|
4855
|
+
where: {
|
|
4856
|
+
documentId: { $in: uniqueDocumentIds }
|
|
4857
|
+
},
|
|
4858
|
+
select: ["documentId", "path", "canonicalPath"]
|
|
4859
|
+
});
|
|
4860
|
+
const routeMap = new Map(routes2?.map((route2) => [route2.documentId, route2]) || []);
|
|
4861
|
+
const enriched = enrichEntity(data, contentTypeUid, routeMap);
|
|
4862
|
+
return enriched;
|
|
4764
4863
|
}
|
|
4765
4864
|
const migration_001_canonical_path = {
|
|
4766
4865
|
version: "001",
|
|
@@ -5184,7 +5283,7 @@ const addWebatlasField = ({ strapi: strapi2 }) => {
|
|
|
5184
5283
|
([uid, ct]) => ct.info?.pluralName === apiEndpoint
|
|
5185
5284
|
);
|
|
5186
5285
|
if (!contentTypeEntry) return;
|
|
5187
|
-
const [contentTypeUid
|
|
5286
|
+
const [contentTypeUid] = contentTypeEntry;
|
|
5188
5287
|
if (ctx.body.data) {
|
|
5189
5288
|
if (Array.isArray(ctx.body.data)) {
|
|
5190
5289
|
ctx.body.data = await Promise.all(
|
|
@@ -5200,8 +5299,43 @@ const addWebatlasField = ({ strapi: strapi2 }) => {
|
|
|
5200
5299
|
}
|
|
5201
5300
|
};
|
|
5202
5301
|
};
|
|
5302
|
+
const enrichRoutePicker = ({ strapi: strapi2 }) => {
|
|
5303
|
+
return async (ctx, next) => {
|
|
5304
|
+
await next();
|
|
5305
|
+
if (!ctx.request.url.startsWith("/api/")) {
|
|
5306
|
+
return;
|
|
5307
|
+
}
|
|
5308
|
+
if (!ctx.body || ctx.status !== 200) {
|
|
5309
|
+
return;
|
|
5310
|
+
}
|
|
5311
|
+
const urlMatch = ctx.request.url.match(/^\/api\/([^/?]+)/);
|
|
5312
|
+
if (!urlMatch) return;
|
|
5313
|
+
const apiEndpoint = urlMatch[1];
|
|
5314
|
+
const contentTypeEntry = Object.entries(strapi2.contentTypes).find(
|
|
5315
|
+
([uid, ct]) => ct.info?.pluralName === apiEndpoint
|
|
5316
|
+
);
|
|
5317
|
+
if (!contentTypeEntry) return;
|
|
5318
|
+
const [contentTypeUid] = contentTypeEntry;
|
|
5319
|
+
if (ctx.body.data) {
|
|
5320
|
+
if (Array.isArray(ctx.body.data)) {
|
|
5321
|
+
ctx.body.data = await Promise.all(
|
|
5322
|
+
ctx.body.data.map((item) => enrichRoutePickerFields(item, contentTypeUid))
|
|
5323
|
+
);
|
|
5324
|
+
} else {
|
|
5325
|
+
ctx.body.data = await enrichRoutePickerFields(ctx.body.data, contentTypeUid);
|
|
5326
|
+
}
|
|
5327
|
+
} else if (Array.isArray(ctx.body)) {
|
|
5328
|
+
ctx.body = await Promise.all(
|
|
5329
|
+
ctx.body.map((item) => enrichRoutePickerFields(item, contentTypeUid))
|
|
5330
|
+
);
|
|
5331
|
+
} else if (typeof ctx.body === "object") {
|
|
5332
|
+
ctx.body = await enrichRoutePickerFields(ctx.body, contentTypeUid);
|
|
5333
|
+
}
|
|
5334
|
+
};
|
|
5335
|
+
};
|
|
5203
5336
|
const middlewares = {
|
|
5204
|
-
addWebatlasField
|
|
5337
|
+
addWebatlasField,
|
|
5338
|
+
enrichRoutePicker
|
|
5205
5339
|
};
|
|
5206
5340
|
const bootstrap = async ({ strapi: strapi2 }) => {
|
|
5207
5341
|
try {
|
|
@@ -5215,6 +5349,7 @@ const bootstrap = async ({ strapi: strapi2 }) => {
|
|
|
5215
5349
|
documentMiddleware(strapi2, enabledContentTypes, config2);
|
|
5216
5350
|
webatlasMiddleware(strapi2);
|
|
5217
5351
|
strapi2.server.use(middlewares.addWebatlasField({ strapi: strapi2 }));
|
|
5352
|
+
strapi2.server.use(middlewares.enrichRoutePicker({ strapi: strapi2 }));
|
|
5218
5353
|
} catch (error) {
|
|
5219
5354
|
strapi2.log.error(`Bootstrap failed. ${String(error)}`);
|
|
5220
5355
|
}
|
|
@@ -5222,6 +5357,11 @@ const bootstrap = async ({ strapi: strapi2 }) => {
|
|
|
5222
5357
|
const destroy = ({ strapi: strapi2 }) => {
|
|
5223
5358
|
};
|
|
5224
5359
|
const register = ({ strapi: strapi2 }) => {
|
|
5360
|
+
strapi2.customFields.register({
|
|
5361
|
+
name: "route-picker",
|
|
5362
|
+
plugin: PLUGIN_ID,
|
|
5363
|
+
type: "string"
|
|
5364
|
+
});
|
|
5225
5365
|
};
|
|
5226
5366
|
const config = {
|
|
5227
5367
|
default: {
|
|
@@ -6085,6 +6225,7 @@ const client = ({ strapi: strapi2 }) => ({
|
|
|
6085
6225
|
let cleanEntity = cleanRootKeys(entity);
|
|
6086
6226
|
cleanEntity = removeWaFields(cleanEntity);
|
|
6087
6227
|
cleanEntity = await enrichWebatlasData(cleanEntity, route2.relatedContentType);
|
|
6228
|
+
cleanEntity = await enrichRoutePickerFields(cleanEntity, contentTypeKey);
|
|
6088
6229
|
return {
|
|
6089
6230
|
contentType: contentType.info.singularName,
|
|
6090
6231
|
...cleanEntity
|
|
@@ -300,6 +300,9 @@ declare const _default: {
|
|
|
300
300
|
addWebatlasField: ({ strapi }: {
|
|
301
301
|
strapi: import('@strapi/types/dist/core').Strapi;
|
|
302
302
|
}) => (ctx: any, next: any) => Promise<void>;
|
|
303
|
+
enrichRoutePicker: ({ strapi }: {
|
|
304
|
+
strapi: import('@strapi/types/dist/core').Strapi;
|
|
305
|
+
}) => (ctx: any, next: any) => Promise<void>;
|
|
303
306
|
};
|
|
304
307
|
};
|
|
305
308
|
export default _default;
|