@webbio/strapi-plugin-page-builder 0.0.1
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/.prettierignore +17 -0
- package/README.md +3 -0
- package/admin/src/api/collection-type.ts +110 -0
- package/admin/src/api/has-page-relation.ts +34 -0
- package/admin/src/api/page-type.ts +31 -0
- package/admin/src/api/template.ts +25 -0
- package/admin/src/components/Combobox/index.tsx +77 -0
- package/admin/src/components/Combobox/react-select-custom-styles.tsx +111 -0
- package/admin/src/components/Combobox/styles.ts +22 -0
- package/admin/src/components/ConfirmModal/index.tsx +90 -0
- package/admin/src/components/EditView/CollectionTypeSearch/index.tsx +118 -0
- package/admin/src/components/EditView/CollectionTypeSettings/CreatePageButton/index.tsx +95 -0
- package/admin/src/components/EditView/CollectionTypeSettings/CreatePageButton/styles.ts +26 -0
- package/admin/src/components/EditView/CollectionTypeSettings/index.tsx +53 -0
- package/admin/src/components/EditView/Details/index.tsx +47 -0
- package/admin/src/components/EditView/Details/styles.ts +51 -0
- package/admin/src/components/EditView/PageSettings/index.tsx +104 -0
- package/admin/src/components/EditView/Template/TemplateConfirmModal/index.tsx +36 -0
- package/admin/src/components/EditView/Template/TemplateSelect/index.tsx +64 -0
- package/admin/src/components/EditView/Template/TemplateSelect/use-template-modules.ts +30 -0
- package/admin/src/components/EditView/index.tsx +27 -0
- package/admin/src/components/EditView/page-type-select.tsx +30 -0
- package/admin/src/components/EditView/wrapper.tsx +35 -0
- package/admin/src/components/Initializer/index.tsx +24 -0
- package/admin/src/components/PageTypeFilter/index.tsx +17 -0
- package/admin/src/components/PageTypeFilter/page-type-filter.tsx +130 -0
- package/admin/src/components/PluginIcon/index.tsx +12 -0
- package/admin/src/constants.ts +1 -0
- package/admin/src/index.tsx +115 -0
- package/admin/src/pages/App/index.tsx +25 -0
- package/admin/src/pages/HomePage/index.tsx +19 -0
- package/admin/src/pluginId.ts +5 -0
- package/admin/src/redux/initialData.reducer.ts +0 -0
- package/admin/src/translations/en.json +6 -0
- package/admin/src/translations/nl.json +6 -0
- package/admin/src/utils/getRequestUrl.ts +11 -0
- package/admin/src/utils/getTrad.ts +5 -0
- package/admin/src/utils/hooks/useDebounce.ts +17 -0
- package/admin/src/utils/hooks/useGetLocaleFromUrl.ts +9 -0
- package/admin/src/utils/hooks/usePrevious.ts +12 -0
- package/admin/src/utils/sanitizeModules.ts +10 -0
- package/custom.d.ts +5 -0
- package/package.json +71 -0
- package/server/bootstrap.ts +106 -0
- package/server/config/index.ts +4 -0
- package/server/content-types/index.ts +1 -0
- package/server/controllers/collection-types.ts +27 -0
- package/server/controllers/index.ts +9 -0
- package/server/controllers/page-type.ts +12 -0
- package/server/controllers/page.ts +20 -0
- package/server/destroy.ts +5 -0
- package/server/index.ts +23 -0
- package/server/middlewares/index.ts +1 -0
- package/server/policies/index.ts +1 -0
- package/server/register.ts +5 -0
- package/server/routes/index.ts +42 -0
- package/server/schema/page-end.json +97 -0
- package/server/schema/page-start.json +87 -0
- package/server/schema/page-type-end.json +49 -0
- package/server/schema/page-type-start.json +44 -0
- package/server/schema/template.json +35 -0
- package/server/services/builder.ts +121 -0
- package/server/services/collection-types.ts +49 -0
- package/server/services/index.ts +11 -0
- package/server/services/page-type.ts +22 -0
- package/server/services/page.ts +24 -0
- package/server/utils/graphql.ts +110 -0
- package/server/utils/reload-strapi-on-load.ts +13 -0
- package/shared/utils/constants.ts +4 -0
- package/shared/utils/sleep.ts +1 -0
- package/strapi-admin.js +3 -0
- package/strapi-server.js +3 -0
- package/tsconfig.json +20 -0
- package/tsconfig.server.json +25 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import set from 'lodash/set';
|
|
3
|
+
|
|
4
|
+
import { SingleSelectOption, SingleSelect } from '@strapi/design-system';
|
|
5
|
+
import { useQueryParams } from '@strapi/helper-plugin';
|
|
6
|
+
|
|
7
|
+
import { useGetPageTypes } from '../../api/page-type';
|
|
8
|
+
import { PAGE_TYPE_NO_FILTER } from '../../constants';
|
|
9
|
+
import { PAGE_TYPE_PAGE } from '../../../../shared/utils/constants';
|
|
10
|
+
|
|
11
|
+
const PageTypeFilter = () => {
|
|
12
|
+
const [{ query }, setQuery] = useQueryParams();
|
|
13
|
+
const { data: pageTypes } = useGetPageTypes({});
|
|
14
|
+
const initialPageTypeUid = getInitialPageType(query, pageTypes);
|
|
15
|
+
const [selectedPageTypeUid, setSelectedPageTypeUid] = useState(initialPageTypeUid);
|
|
16
|
+
|
|
17
|
+
const removeFilters = () => {
|
|
18
|
+
const newAndFilters = query.filters?.$and?.filter(
|
|
19
|
+
(x?: Record<string, any>) => Object.keys(x || {})?.[0] !== 'pageType'
|
|
20
|
+
);
|
|
21
|
+
const filters = { ...query.filters, $and: newAndFilters };
|
|
22
|
+
|
|
23
|
+
if (newAndFilters && newAndFilters.length === 0) {
|
|
24
|
+
delete filters.$and;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
setQuery({
|
|
28
|
+
page: 1,
|
|
29
|
+
filters
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const handleFilterChange = (filters: Record<string, any>) => {
|
|
34
|
+
const currentFilters = query.filters?.$and ? query.filters : { ...query.filters, $and: [] }; // Make sure $and exists.
|
|
35
|
+
const pageTypeFilterIndex = currentFilters.$and.findIndex(
|
|
36
|
+
(x?: Record<string, any>) => Object.keys(x || {})?.[0] === 'pageType'
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
if (pageTypeFilterIndex > -1) {
|
|
40
|
+
set(currentFilters, `$and[${pageTypeFilterIndex}]`, filters); // If the pageType filter already exists, replace it
|
|
41
|
+
} else {
|
|
42
|
+
currentFilters.$and.push(filters);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
setQuery({
|
|
46
|
+
page: 1,
|
|
47
|
+
filters: currentFilters
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const handleSelect = (value: string) => {
|
|
52
|
+
setSelectedPageTypeUid(value);
|
|
53
|
+
|
|
54
|
+
if (value === 'all') {
|
|
55
|
+
removeFilters();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (value === PAGE_TYPE_PAGE) {
|
|
60
|
+
handleFilterChange(nullPageTypeQueryFilter);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
handleFilterChange(getPageTypeQueryFilter(value));
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
setSelectedPageTypeUid(getInitialPageType(query, pageTypes));
|
|
69
|
+
}, [pageTypes]);
|
|
70
|
+
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
if (!query?.filters) {
|
|
73
|
+
setSelectedPageTypeUid(null);
|
|
74
|
+
}
|
|
75
|
+
}, [query]);
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<SingleSelect placeholder="Select page type" onChange={handleSelect} size="S" value={selectedPageTypeUid}>
|
|
79
|
+
<SingleSelectOption key={PAGE_TYPE_NO_FILTER} value={PAGE_TYPE_NO_FILTER}>
|
|
80
|
+
All Pagetypes
|
|
81
|
+
</SingleSelectOption>
|
|
82
|
+
<SingleSelectOption key={PAGE_TYPE_PAGE} value={PAGE_TYPE_PAGE}>
|
|
83
|
+
Page
|
|
84
|
+
</SingleSelectOption>
|
|
85
|
+
{pageTypes?.map((pageType) => (
|
|
86
|
+
<SingleSelectOption key={pageType.uid} value={pageType.uid}>
|
|
87
|
+
{pageType.title || pageType.uid}
|
|
88
|
+
</SingleSelectOption>
|
|
89
|
+
))}
|
|
90
|
+
</SingleSelect>
|
|
91
|
+
);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export { PageTypeFilter };
|
|
95
|
+
|
|
96
|
+
const getPageTypeQueryFilter = (pageType: string) => ({
|
|
97
|
+
pageType: {
|
|
98
|
+
uid: {
|
|
99
|
+
$eq: pageType
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const nullPageTypeQueryFilter = {
|
|
105
|
+
pageType: {
|
|
106
|
+
uid: {
|
|
107
|
+
$null: true
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const getInitialPageType = (query: any, pageTypes: any) => {
|
|
113
|
+
const pageTypeFromQuery = query?.filters?.$and?.find(
|
|
114
|
+
(x?: Record<string, any>) => Object.keys(x || {})?.[0] === 'pageType'
|
|
115
|
+
)?.pageType;
|
|
116
|
+
|
|
117
|
+
if (pageTypeFromQuery?.uid?.$null === 'true') {
|
|
118
|
+
return PAGE_TYPE_PAGE;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (pageTypeFromQuery?.uid?.$eq) {
|
|
122
|
+
const matchingPageType = pageTypes?.find((pageType: any) => pageType?.uid === pageTypeFromQuery.uid.$eq);
|
|
123
|
+
|
|
124
|
+
if (matchingPageType) {
|
|
125
|
+
return matchingPageType.uid;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return PAGE_TYPE_NO_FILTER;
|
|
130
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const PAGE_TYPE_NO_FILTER = 'all';
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { prefixPluginTranslations } from '@strapi/helper-plugin';
|
|
2
|
+
|
|
3
|
+
import { getFetchClient } from '@strapi/helper-plugin';
|
|
4
|
+
|
|
5
|
+
import pluginPkg from '../../package.json';
|
|
6
|
+
import pluginId from './pluginId';
|
|
7
|
+
import Initializer from './components/Initializer';
|
|
8
|
+
import PluginIcon from './components/PluginIcon';
|
|
9
|
+
import PageTypeFilter from './components/PageTypeFilter';
|
|
10
|
+
import { EditView } from './components/EditView';
|
|
11
|
+
import getRequestUrl from './utils/getRequestUrl';
|
|
12
|
+
import { PAGE_UID } from '../../shared/utils/constants';
|
|
13
|
+
|
|
14
|
+
const name = pluginPkg.strapi.name;
|
|
15
|
+
|
|
16
|
+
const middleware =
|
|
17
|
+
() =>
|
|
18
|
+
({ getState }: any) =>
|
|
19
|
+
(next: any) =>
|
|
20
|
+
async (action: any) => {
|
|
21
|
+
// If data is being fetched or after a save
|
|
22
|
+
if (
|
|
23
|
+
action.type === 'ContentManager/CrudReducer/GET_DATA_SUCCEEDED' ||
|
|
24
|
+
action.type === 'ContentManager/CrudReducer/SUBMIT_SUCCEEDED'
|
|
25
|
+
) {
|
|
26
|
+
const store = getState();
|
|
27
|
+
const layoutSlice = store['content-manager_editViewLayoutManager'];
|
|
28
|
+
const uid = layoutSlice.currentLayout.contentType.uid;
|
|
29
|
+
|
|
30
|
+
if (uid !== PAGE_UID) {
|
|
31
|
+
return next(action);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const { get } = await getFetchClient();
|
|
35
|
+
const { data: pageData } = await get(`${getRequestUrl('page')}/${action.data.id}`);
|
|
36
|
+
|
|
37
|
+
action.data.collectionTypeTitle = pageData?.collectionTypeData?.title;
|
|
38
|
+
action.data.collectionTypeId = pageData?.collectionTypeData?.id;
|
|
39
|
+
action.data.initialPageType = pageData?.pageType ? pageData.pageType : undefined;
|
|
40
|
+
|
|
41
|
+
return next(action);
|
|
42
|
+
}
|
|
43
|
+
return next(action);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const middlewares = [middleware];
|
|
47
|
+
|
|
48
|
+
export default {
|
|
49
|
+
register(app: any) {
|
|
50
|
+
app.addMiddlewares(middlewares);
|
|
51
|
+
app.addMenuLink({
|
|
52
|
+
to: `/plugins/${pluginId}`,
|
|
53
|
+
icon: PluginIcon,
|
|
54
|
+
intlLabel: {
|
|
55
|
+
id: `${pluginId}.plugin.name`,
|
|
56
|
+
defaultMessage: name
|
|
57
|
+
},
|
|
58
|
+
Component: async () => {
|
|
59
|
+
const component = await import(/* webpackChunkName: "[request]" */ './pages/App');
|
|
60
|
+
|
|
61
|
+
return component;
|
|
62
|
+
},
|
|
63
|
+
permissions: [
|
|
64
|
+
// Uncomment to set the permissions of the plugin here
|
|
65
|
+
// {
|
|
66
|
+
// action: '', // the action name should be plugin::plugin-name.actionType
|
|
67
|
+
// subject: null,
|
|
68
|
+
// },
|
|
69
|
+
]
|
|
70
|
+
});
|
|
71
|
+
const plugin = {
|
|
72
|
+
id: pluginId,
|
|
73
|
+
initializer: Initializer,
|
|
74
|
+
isReady: false,
|
|
75
|
+
name
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
app.registerPlugin(plugin);
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
bootstrap(app: any) {
|
|
82
|
+
app.injectContentManagerComponent('editView', 'right-links', {
|
|
83
|
+
name: 'collectionType-picker',
|
|
84
|
+
Component: EditView
|
|
85
|
+
});
|
|
86
|
+
app.injectContentManagerComponent('listView', 'actions', {
|
|
87
|
+
name: 'collectionType-picker',
|
|
88
|
+
Component: PageTypeFilter
|
|
89
|
+
});
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
async registerTrads(app: any) {
|
|
93
|
+
const { locales } = app;
|
|
94
|
+
|
|
95
|
+
const importedTrads = await Promise.all(
|
|
96
|
+
(locales as string[]).map((locale) => {
|
|
97
|
+
return import(`./translations/${locale}.json`)
|
|
98
|
+
.then(({ default: data }) => {
|
|
99
|
+
return {
|
|
100
|
+
data: prefixPluginTranslations(data, pluginId),
|
|
101
|
+
locale
|
|
102
|
+
};
|
|
103
|
+
})
|
|
104
|
+
.catch(() => {
|
|
105
|
+
return {
|
|
106
|
+
data: {},
|
|
107
|
+
locale
|
|
108
|
+
};
|
|
109
|
+
});
|
|
110
|
+
})
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
return Promise.resolve(importedTrads);
|
|
114
|
+
}
|
|
115
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* This component is the skeleton around the actual pages, and should only
|
|
4
|
+
* contain code that should be seen on all pages. (e.g. navigation bar)
|
|
5
|
+
*
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React from 'react';
|
|
9
|
+
import { Switch, Route } from 'react-router-dom';
|
|
10
|
+
import { AnErrorOccurred } from '@strapi/helper-plugin';
|
|
11
|
+
import pluginId from '../../pluginId';
|
|
12
|
+
import HomePage from '../HomePage';
|
|
13
|
+
|
|
14
|
+
const App = () => {
|
|
15
|
+
return (
|
|
16
|
+
<div>
|
|
17
|
+
<Switch>
|
|
18
|
+
<Route path={`/plugins/${pluginId}`} component={HomePage} exact />
|
|
19
|
+
<Route component={AnErrorOccurred} />
|
|
20
|
+
</Switch>
|
|
21
|
+
</div>
|
|
22
|
+
);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default App;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/*
|
|
2
|
+
*
|
|
3
|
+
* HomePage
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import pluginId from '../../pluginId';
|
|
9
|
+
|
|
10
|
+
const HomePage = () => {
|
|
11
|
+
return (
|
|
12
|
+
<div>
|
|
13
|
+
<h1>{pluginId}'s HomePage</h1>
|
|
14
|
+
<p>Happy coding</p>
|
|
15
|
+
</div>
|
|
16
|
+
);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export default HomePage;
|
|
File without changes
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{
|
|
2
|
+
"template.confirmModal.title": "Replace all page modules",
|
|
3
|
+
"template.confirmModal.body": "You are about to replace all modules on this page. Are you sure you want to continue?",
|
|
4
|
+
"template.confirmModal.buttons.cancel": "No, cancel",
|
|
5
|
+
"template.confirmModal.buttons.submit": "Yes, replace all"
|
|
6
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{
|
|
2
|
+
"template.confirmModal.title": "Pagina modules vervangen",
|
|
3
|
+
"template.confirmModal.body": "Weet je zeker dat je alle modules op deze pagina wilt vervangen?",
|
|
4
|
+
"template.confirmModal.buttons.cancel": "Nee, annuleer",
|
|
5
|
+
"template.confirmModal.buttons.submit": "Ja, vervang modules"
|
|
6
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
export function useDebounce<T>(value: T, delay: number): T {
|
|
4
|
+
const [debouncedValue, setDebouncedValue] = React.useState(value);
|
|
5
|
+
|
|
6
|
+
React.useEffect(() => {
|
|
7
|
+
const handler = setTimeout(() => {
|
|
8
|
+
setDebouncedValue(value);
|
|
9
|
+
}, delay);
|
|
10
|
+
|
|
11
|
+
return () => {
|
|
12
|
+
clearTimeout(handler);
|
|
13
|
+
};
|
|
14
|
+
}, [value, delay]);
|
|
15
|
+
|
|
16
|
+
return debouncedValue;
|
|
17
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { useRef, useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
// Hook
|
|
4
|
+
export const usePrevious = <T>(value: T): T | null => {
|
|
5
|
+
const ref: React.MutableRefObject<T | null> = useRef(null);
|
|
6
|
+
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
ref.current = value;
|
|
9
|
+
}, [value]);
|
|
10
|
+
|
|
11
|
+
return ref.current;
|
|
12
|
+
};
|
package/custom.d.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@webbio/strapi-plugin-page-builder",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "This is the description of the plugin.",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"develop": "tsc -p tsconfig.server.json -w",
|
|
7
|
+
"build": "tsc -p tsconfig.server.json",
|
|
8
|
+
"spu": "yarn upgrade-interactive --latest",
|
|
9
|
+
"prepublish": "tsc",
|
|
10
|
+
"format": "prettier --write ."
|
|
11
|
+
},
|
|
12
|
+
"strapi": {
|
|
13
|
+
"name": "page-builder",
|
|
14
|
+
"description": "Description of page-builder plugin",
|
|
15
|
+
"displayName": "Page Builder",
|
|
16
|
+
"kind": "plugin"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/webbio/strapi-plugin-internal-links.git"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@strapi/design-system": "^1.8.2",
|
|
24
|
+
"@strapi/helper-plugin": "^4.12.0",
|
|
25
|
+
"@strapi/icons": "^1.8.2",
|
|
26
|
+
"@strapi/typescript-utils": "^4.12.0",
|
|
27
|
+
"@strapi/utils": "^4.12.0",
|
|
28
|
+
"react-select": "^5.7.4"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@strapi/typescript-utils": "^4.12.0",
|
|
32
|
+
"@types/react": "^17.0.53",
|
|
33
|
+
"@types/react-dom": "^18.0.28",
|
|
34
|
+
"@types/react-router-dom": "^5.3.3",
|
|
35
|
+
"@types/styled-components": "^5.1.26",
|
|
36
|
+
"react": "^18.2.0",
|
|
37
|
+
"react-dom": "^18.2.0",
|
|
38
|
+
"react-router-dom": "^5.3.4",
|
|
39
|
+
"styled-components": "^5.3.6",
|
|
40
|
+
"typescript": "5.1.6"
|
|
41
|
+
},
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"@strapi/strapi": "^4.12.0",
|
|
44
|
+
"@webbio/strapi-plugin-slug": "^0.1.0",
|
|
45
|
+
"react": "^17.0.0 || ^18.0.0",
|
|
46
|
+
"react-dom": "^17.0.0 || ^18.0.0",
|
|
47
|
+
"react-router-dom": "^5.3.4",
|
|
48
|
+
"styled-components": "^5.3.6"
|
|
49
|
+
},
|
|
50
|
+
"author": {
|
|
51
|
+
"name": "Webbio B.V.",
|
|
52
|
+
"email": "info@webbio.nl",
|
|
53
|
+
"url": "https://webbio.nl"
|
|
54
|
+
},
|
|
55
|
+
"maintainers": [
|
|
56
|
+
{
|
|
57
|
+
"name": "Webbio B.V.",
|
|
58
|
+
"email": "info@webbio.nl",
|
|
59
|
+
"url": "https://webbio.nl"
|
|
60
|
+
}
|
|
61
|
+
],
|
|
62
|
+
"engines": {
|
|
63
|
+
"node": ">=14.19.1 <=18.x.x",
|
|
64
|
+
"npm": ">=6.0.0"
|
|
65
|
+
},
|
|
66
|
+
"license": "MIT",
|
|
67
|
+
"publishConfig": {
|
|
68
|
+
"access": "public",
|
|
69
|
+
"registry": "https://registry.npmjs.org"
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { Strapi } from '@strapi/strapi';
|
|
2
|
+
import { errors } from '@strapi/utils';
|
|
3
|
+
|
|
4
|
+
import { PAGE_UID } from '../shared/utils/constants';
|
|
5
|
+
|
|
6
|
+
export default async ({ strapi }: { strapi: Strapi }) => {
|
|
7
|
+
// TODO: refactor to own function
|
|
8
|
+
try {
|
|
9
|
+
const pageTypes = await strapi.entityService.findMany('api::page-type.page-type', {
|
|
10
|
+
limit: -1
|
|
11
|
+
});
|
|
12
|
+
const pagePermissions = pageTypes.map((pageType) => {
|
|
13
|
+
const name = `page-type-is-${pageType.uid}`;
|
|
14
|
+
const displayName = pageType.title;
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
plugin: 'page-builder',
|
|
18
|
+
name,
|
|
19
|
+
displayName,
|
|
20
|
+
category: 'Page type',
|
|
21
|
+
handler: async () => {
|
|
22
|
+
return {
|
|
23
|
+
$or: [
|
|
24
|
+
{
|
|
25
|
+
'pageType.uid': {
|
|
26
|
+
$eq: pageType.uid
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
uid: {
|
|
31
|
+
$eq: pageType.uid
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
// @ts-ignore shitty types
|
|
40
|
+
await strapi.admin.services.permission.conditionProvider.registerMany(pagePermissions);
|
|
41
|
+
} catch {
|
|
42
|
+
console.log('Cannot set page permissions');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const updateCollectionTypeData = (data: any, collectionTypeId: number, uid: string) => {
|
|
46
|
+
data.collectionTypeData = {
|
|
47
|
+
id: collectionTypeId,
|
|
48
|
+
__type: uid,
|
|
49
|
+
__pivot: {
|
|
50
|
+
field: 'page'
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
return data;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
strapi.db.lifecycles.subscribe({
|
|
58
|
+
// @ts-ignore
|
|
59
|
+
models: [PAGE_UID],
|
|
60
|
+
async beforeCreate(event) {
|
|
61
|
+
let { data } = event.params;
|
|
62
|
+
const collectionTypeId = data?.collectionTypeId;
|
|
63
|
+
const pageTypeId = data?.pageType.connect?.[0]?.id || data.initialPageType;
|
|
64
|
+
|
|
65
|
+
if (collectionTypeId && pageTypeId) {
|
|
66
|
+
const { uid } = await strapi.entityService.findOne('api::page-type.page-type', pageTypeId);
|
|
67
|
+
const collectionToConnect = await strapi.entityService.findOne(uid, collectionTypeId, {
|
|
68
|
+
populate: { page: true }
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
if (collectionToConnect.page && collectionToConnect.page.length > 0) {
|
|
72
|
+
throw new errors.ValidationError('You can only link one CollectionType to one page');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
data = updateCollectionTypeData(data, collectionTypeId, uid);
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
async beforeUpdate(event) {
|
|
79
|
+
let data = event?.params?.data;
|
|
80
|
+
const collectionTypeId = data?.collectionTypeId;
|
|
81
|
+
const pageTypeId = data?.pageType?.connect?.[0]?.id || data?.initialPageType;
|
|
82
|
+
const removedPageType = data?.pageType?.disconnect?.[0]?.id;
|
|
83
|
+
|
|
84
|
+
if (removedPageType || collectionTypeId === null) {
|
|
85
|
+
data.collectionTypeData = undefined;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (collectionTypeId && pageTypeId && !removedPageType) {
|
|
89
|
+
const { uid } = await strapi.entityService.findOne('api::page-type.page-type', pageTypeId);
|
|
90
|
+
const collectionToConnect = await strapi.entityService.findOne(uid, collectionTypeId, {
|
|
91
|
+
populate: { page: true }
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
if (
|
|
95
|
+
collectionToConnect.page &&
|
|
96
|
+
collectionToConnect.page.length > 0 &&
|
|
97
|
+
!collectionToConnect?.page.some((p) => p.id === data.id)
|
|
98
|
+
) {
|
|
99
|
+
throw new errors.ValidationError('You can only link one CollectionType to one page');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
data = updateCollectionTypeData(data, collectionTypeId, uid);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
async hasPageRelation(ctx: any) {
|
|
3
|
+
const uid = ctx.params?.uid;
|
|
4
|
+
|
|
5
|
+
// @ts-ignore
|
|
6
|
+
return strapi.service('plugin::page-builder.collection-types').hasPageRelation(uid);
|
|
7
|
+
},
|
|
8
|
+
async getTranslationPageLinks(ctx: any): Promise<IGetTranslationPageLinks[] | undefined> {
|
|
9
|
+
const { uid, ids } = ctx.params || {};
|
|
10
|
+
const idsArr = (ids as string)
|
|
11
|
+
?.split(',')
|
|
12
|
+
.map(Number)
|
|
13
|
+
.filter((x) => !isNaN(x));
|
|
14
|
+
|
|
15
|
+
if (!uid || idsArr?.length === 0) {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// @ts-ignore
|
|
20
|
+
return strapi.service('plugin::page-builder.collection-types').getTranslationPageLinks(uid, idsArr);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export interface IGetTranslationPageLinks {
|
|
25
|
+
id: number;
|
|
26
|
+
locale: string;
|
|
27
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
async findOneByUid(ctx: any) {
|
|
3
|
+
const uid = ctx?.params?.uid;
|
|
4
|
+
|
|
5
|
+
if (!uid) {
|
|
6
|
+
return ctx.badRequest('uid is missing.');
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// @ts-ignore strapi typings
|
|
10
|
+
return await strapi.service('plugin::page-builder.page-type').findOneByUid(uid);
|
|
11
|
+
}
|
|
12
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Strapi } from '@strapi/strapi';
|
|
2
|
+
|
|
3
|
+
import { PAGE_UID } from '../../shared/utils/constants';
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
async getPage(ctx) {
|
|
7
|
+
const id = ctx.params?.id;
|
|
8
|
+
|
|
9
|
+
return (strapi as Strapi).service('plugin::page-builder.page')?.getPage(id);
|
|
10
|
+
},
|
|
11
|
+
|
|
12
|
+
async findOneBySlug(ctx) {
|
|
13
|
+
const { slug = '' } = ctx.params;
|
|
14
|
+
|
|
15
|
+
const entity = await (strapi as Strapi).service(PAGE_UID)?.find({ filters: { slug } });
|
|
16
|
+
const sanitizedEntity = await this.sanitizeOutput(entity, ctx);
|
|
17
|
+
|
|
18
|
+
return this.transformResponse(sanitizedEntity);
|
|
19
|
+
}
|
|
20
|
+
};
|