@griddo/ax 11.15.9 → 11.16.0-rc.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.
- package/package.json +2 -2
- package/src/__tests__/components/RestrictionsModal/CurrentAccessList/CurrentAccessList.test.tsx +173 -0
- package/src/__tests__/components/RestrictionsModal/OptionsStep/OptionsStep.test.tsx +160 -0
- package/src/__tests__/components/RestrictionsModal/RestrictionsModal.test.tsx +134 -0
- package/src/__tests__/components/RestrictionsModal/RolesList/RolesList.test.tsx +234 -0
- package/src/__tests__/components/RestrictionsModal/UsersList/UsersList.test.tsx +288 -0
- package/src/__tests__/hooks/users.test.tsx +382 -0
- package/src/api/pages.tsx +24 -1
- package/src/api/sites.tsx +14 -14
- package/src/api/structuredData.tsx +17 -7
- package/src/components/Browser/index.tsx +12 -0
- package/src/components/Browser/style.tsx +5 -1
- package/src/components/ConfigPanel/Form/index.tsx +11 -10
- package/src/components/ContentItem/index.tsx +31 -0
- package/src/components/ContentItem/style.tsx +43 -0
- package/src/components/Fields/ImageField/index.tsx +2 -1
- package/src/components/Fields/RadioField/index.tsx +3 -3
- package/src/components/Fields/RadioField/style.tsx +1 -1
- package/src/components/FloatingNote/index.tsx +12 -1
- package/src/components/FloatingNote/style.tsx +2 -1
- package/src/components/Modal/style.tsx +11 -0
- package/src/components/PageFinder/SelectionListItem/index.tsx +27 -17
- package/src/components/PageFinder/index.tsx +65 -28
- package/src/components/PageFinder/style.tsx +0 -1
- package/src/components/RestrictionsModal/CurrentAccessList/index.tsx +74 -0
- package/src/components/RestrictionsModal/CurrentAccessList/style.tsx +155 -0
- package/src/components/RestrictionsModal/OptionsStep/index.tsx +55 -0
- package/src/components/RestrictionsModal/OptionsStep/style.tsx +29 -0
- package/src/components/RestrictionsModal/RolesList/RoleItem/index.tsx +54 -0
- package/src/components/RestrictionsModal/RolesList/RoleItem/style.tsx +34 -0
- package/src/components/RestrictionsModal/RolesList/index.tsx +107 -0
- package/src/components/RestrictionsModal/RolesList/style.tsx +76 -0
- package/src/components/RestrictionsModal/UsersList/UserItem/index.tsx +78 -0
- package/src/components/RestrictionsModal/UsersList/UserItem/style.tsx +73 -0
- package/src/components/RestrictionsModal/UsersList/index.tsx +151 -0
- package/src/components/RestrictionsModal/UsersList/style.tsx +55 -0
- package/src/components/RestrictionsModal/index.tsx +181 -0
- package/src/components/RestrictionsModal/style.tsx +31 -0
- package/src/components/RestrictionsModal/utils.tsx +24 -0
- package/src/components/ScheduleModal/index.tsx +32 -8
- package/src/components/SelectionComponent/index.tsx +65 -0
- package/src/components/SelectionComponent/style.tsx +69 -0
- package/src/components/TableFilters/RoleFilter/index.tsx +3 -3
- package/src/components/TableList/TableItem/style.tsx +7 -13
- package/src/components/Tabs/index.tsx +2 -1
- package/src/components/UserRolesAndSites/index.tsx +2 -2
- package/src/components/index.tsx +6 -0
- package/src/containers/PageEditor/actions.tsx +43 -0
- package/src/containers/PageEditor/constants.tsx +1 -0
- package/src/containers/PageEditor/interfaces.tsx +26 -19
- package/src/containers/PageEditor/reducer.tsx +32 -19
- package/src/containers/Users/reducer.tsx +1 -0
- package/src/helpers/forms.tsx +3 -1
- package/src/hooks/index.tsx +2 -1
- package/src/hooks/users.tsx +154 -14
- package/src/modules/ActivityLog/LogFilters/UserFilter/index.tsx +11 -12
- package/src/modules/Content/PageItem/index.tsx +17 -10
- package/src/modules/Content/index.tsx +1 -3
- package/src/modules/GlobalEditor/Editor/index.tsx +6 -0
- package/src/modules/GlobalEditor/PageBrowser/index.tsx +6 -0
- package/src/modules/GlobalEditor/Preview/index.tsx +4 -2
- package/src/modules/GlobalEditor/index.tsx +72 -45
- package/src/modules/Navigation/Menus/List/Table/ConfigPanel/Form/index.tsx +3 -5
- package/src/modules/Navigation/Menus/List/Table/ConfigPanel/index.tsx +4 -4
- package/src/modules/Navigation/Menus/List/Table/SidePanel/Form/ConnectedField/index.tsx +2 -3
- package/src/modules/Navigation/Menus/List/Table/SidePanel/Form/index.tsx +3 -4
- package/src/modules/Navigation/Menus/List/Table/SidePanel/index.tsx +5 -6
- package/src/modules/PageEditor/Editor/index.tsx +6 -0
- package/src/modules/PageEditor/PageBrowser/index.tsx +6 -0
- package/src/modules/PageEditor/Preview/index.tsx +9 -2
- package/src/modules/PageEditor/atoms.tsx +110 -3
- package/src/modules/PageEditor/index.tsx +76 -95
- package/src/modules/Settings/Integrations/IntegrationForm/index.tsx +1 -3
- package/src/modules/Sites/SitesList/GridView/GridSiteItem/style.tsx +19 -2
- package/src/modules/Sites/SitesList/WelcomeModal/FinalStep/index.tsx +2 -2
- package/src/modules/Sites/SitesList/WelcomeModal/utils.ts +2 -2
- package/src/modules/StructuredData/Form/ConnectedField/index.tsx +2 -2
- package/src/modules/StructuredData/Form/index.tsx +120 -75
- package/src/modules/StructuredData/Form/style.tsx +7 -1
- package/src/modules/StructuredData/StructuredDataList/GlobalPageItem/index.tsx +15 -8
- package/src/modules/StructuredData/StructuredDataList/StructuredDataItem/index.tsx +15 -9
- package/src/modules/StructuredData/{StructuredDataList/StructuredDataItem/utils.tsx → utils.tsx} +16 -2
- package/src/modules/Users/Profile/atoms.tsx +62 -4
- package/src/modules/Users/Profile/index.tsx +83 -4
- package/src/modules/Users/Profile/style.tsx +60 -0
- package/src/modules/Users/UserCreate/SiteItem/RolesModal/RoleItem/index.tsx +3 -5
- package/src/modules/Users/UserCreate/SiteItem/index.tsx +2 -2
- package/src/modules/Users/UserCreate/atoms.tsx +69 -0
- package/src/modules/Users/UserCreate/index.tsx +149 -70
- package/src/modules/Users/UserCreate/style.tsx +24 -1
- package/src/modules/Users/UserForm/index.tsx +260 -146
- package/src/modules/Users/UserForm/style.tsx +13 -1
- package/src/modules/Users/UserList/UserItem/index.tsx +3 -3
- package/src/types/index.tsx +19 -6
- package/src/types/logs.tsx +1 -1
|
@@ -1,30 +1,32 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { IBreadcrumbItem, IErrorItem, IPage, ISchema, IUserEditing, PageAccessGrantsSummary } from "@ax/types";
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
RESET_PAGE_EDITOR_STORE,
|
|
2
5
|
SET_BREADCRUMB,
|
|
3
|
-
|
|
4
|
-
SET_TAB,
|
|
5
|
-
SET_SELECTED_EDITOR_ID,
|
|
6
|
-
SET_EDITOR_CONTENT,
|
|
7
|
-
SET_TEMPLATE,
|
|
8
|
-
SET_SELECTED_CONTENT,
|
|
9
|
-
SET_SELECTED_PARENT,
|
|
6
|
+
SET_COPY_MODULE,
|
|
10
7
|
SET_CURRENT_PAGE_ID,
|
|
11
|
-
SET_CURRENT_PAGE_STATUS,
|
|
12
|
-
SET_CURRENT_PAGE_NAME,
|
|
13
8
|
SET_CURRENT_PAGE_LANGUAGES,
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
SET_CURRENT_PAGE_NAME,
|
|
10
|
+
SET_CURRENT_PAGE_STATUS,
|
|
11
|
+
SET_EDITOR_CONTENT,
|
|
16
12
|
SET_ERRORS,
|
|
17
|
-
SET_VALIDATED,
|
|
18
|
-
SET_SITE_PAGE_ID,
|
|
19
|
-
SET_USER_EDITING,
|
|
20
|
-
SET_LAST_ELEMENT_ADDED_ID,
|
|
21
|
-
SET_COPY_MODULE,
|
|
22
13
|
SET_IS_IA_TRANSLATED,
|
|
14
|
+
SET_IS_NEW_TRANSLATION,
|
|
15
|
+
SET_LAST_ELEMENT_ADDED_ID,
|
|
23
16
|
SET_LAST_TIMEOUT,
|
|
24
|
-
|
|
17
|
+
SET_PAGE_ACCESS_GRANTS,
|
|
18
|
+
SET_SCHEMA,
|
|
25
19
|
SET_SCROLL_EDITOR_ID,
|
|
20
|
+
SET_SELECTED_CONTENT,
|
|
21
|
+
SET_SELECTED_EDITOR_ID,
|
|
22
|
+
SET_SELECTED_PARENT,
|
|
23
|
+
SET_SITE_PAGE_ID,
|
|
24
|
+
SET_TAB,
|
|
25
|
+
SET_TEMPLATE,
|
|
26
|
+
SET_TEMPLATE_CONFIG,
|
|
27
|
+
SET_USER_EDITING,
|
|
28
|
+
SET_VALIDATED,
|
|
26
29
|
} from "./constants";
|
|
27
|
-
import { IBreadcrumbItem, ISchema, IPage, IErrorItem, IUserEditing } from "@ax/types";
|
|
28
30
|
|
|
29
31
|
export interface ISetEditorContent {
|
|
30
32
|
type: typeof SET_EDITOR_CONTENT;
|
|
@@ -145,6 +147,11 @@ export interface ISetScrollEditorID {
|
|
|
145
147
|
payload: { scrollEditorID: number | null };
|
|
146
148
|
}
|
|
147
149
|
|
|
150
|
+
export interface ISetPageAccessGrants {
|
|
151
|
+
type: typeof SET_PAGE_ACCESS_GRANTS;
|
|
152
|
+
payload: { accessGrants: PageAccessGrantsSummary };
|
|
153
|
+
}
|
|
154
|
+
|
|
148
155
|
export interface IFieldProps {
|
|
149
156
|
id: number;
|
|
150
157
|
key: string;
|
|
@@ -1,31 +1,41 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
IBreadcrumbItem,
|
|
3
|
+
IErrorItem,
|
|
4
|
+
IModule,
|
|
5
|
+
IPage,
|
|
6
|
+
IPageLanguage,
|
|
7
|
+
ISchema,
|
|
8
|
+
IUserEditing,
|
|
9
|
+
PageAccessGrantsSummary,
|
|
10
|
+
} from "@ax/types";
|
|
2
11
|
|
|
3
12
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
13
|
+
INITIAL_TEMPLATE,
|
|
14
|
+
RESET_PAGE_EDITOR_STORE,
|
|
6
15
|
SET_BREADCRUMB,
|
|
7
|
-
|
|
8
|
-
SET_SELECTED_CONTENT,
|
|
9
|
-
SET_TAB,
|
|
10
|
-
SET_SELECTED_EDITOR_ID,
|
|
16
|
+
SET_COPY_MODULE,
|
|
11
17
|
SET_CURRENT_PAGE_ID,
|
|
12
|
-
SET_CURRENT_PAGE_STATUS,
|
|
13
|
-
SET_CURRENT_PAGE_NAME,
|
|
14
18
|
SET_CURRENT_PAGE_LANGUAGES,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
SET_SELECTED_PARENT,
|
|
19
|
+
SET_CURRENT_PAGE_NAME,
|
|
20
|
+
SET_CURRENT_PAGE_STATUS,
|
|
21
|
+
SET_EDITOR_CONTENT,
|
|
19
22
|
SET_ERRORS,
|
|
20
|
-
SET_VALIDATED,
|
|
21
|
-
SET_SITE_PAGE_ID,
|
|
22
|
-
SET_USER_EDITING,
|
|
23
|
-
SET_LAST_ELEMENT_ADDED_ID,
|
|
24
|
-
SET_COPY_MODULE,
|
|
25
23
|
SET_IS_IA_TRANSLATED,
|
|
24
|
+
SET_IS_NEW_TRANSLATION,
|
|
25
|
+
SET_LAST_ELEMENT_ADDED_ID,
|
|
26
26
|
SET_LAST_TIMEOUT,
|
|
27
|
-
|
|
27
|
+
SET_PAGE_ACCESS_GRANTS,
|
|
28
|
+
SET_SCHEMA,
|
|
28
29
|
SET_SCROLL_EDITOR_ID,
|
|
30
|
+
SET_SELECTED_CONTENT,
|
|
31
|
+
SET_SELECTED_EDITOR_ID,
|
|
32
|
+
SET_SELECTED_PARENT,
|
|
33
|
+
SET_SITE_PAGE_ID,
|
|
34
|
+
SET_TAB,
|
|
35
|
+
SET_TEMPLATE,
|
|
36
|
+
SET_TEMPLATE_CONFIG,
|
|
37
|
+
SET_USER_EDITING,
|
|
38
|
+
SET_VALIDATED,
|
|
29
39
|
} from "./constants";
|
|
30
40
|
|
|
31
41
|
export interface IPageEditorState {
|
|
@@ -52,6 +62,7 @@ export interface IPageEditorState {
|
|
|
52
62
|
isIATranslated: boolean;
|
|
53
63
|
lastTimeout: NodeJS.Timeout | null;
|
|
54
64
|
scrollEditorID: number | null;
|
|
65
|
+
accessGrants: PageAccessGrantsSummary;
|
|
55
66
|
}
|
|
56
67
|
|
|
57
68
|
export const initialState = {
|
|
@@ -78,6 +89,7 @@ export const initialState = {
|
|
|
78
89
|
isIATranslated: false,
|
|
79
90
|
lastTimeout: null,
|
|
80
91
|
scrollEditorID: null,
|
|
92
|
+
accessGrants: { users: [], roles: [] },
|
|
81
93
|
};
|
|
82
94
|
|
|
83
95
|
export function reducer(state = initialState, action: any): IPageEditorState {
|
|
@@ -106,6 +118,7 @@ export function reducer(state = initialState, action: any): IPageEditorState {
|
|
|
106
118
|
case SET_IS_IA_TRANSLATED:
|
|
107
119
|
case SET_LAST_TIMEOUT:
|
|
108
120
|
case SET_SCROLL_EDITOR_ID:
|
|
121
|
+
case SET_PAGE_ACCESS_GRANTS:
|
|
109
122
|
return { ...state, ...action.payload };
|
|
110
123
|
case RESET_PAGE_EDITOR_STORE:
|
|
111
124
|
return { ...initialState, moduleCopy: state.moduleCopy };
|
package/src/helpers/forms.tsx
CHANGED
|
@@ -15,4 +15,6 @@ const protectFormKeys = (content: any, key: string, editorID: number) => {
|
|
|
15
15
|
return formComponent;
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
const isValidEmail = (email: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
|
|
19
|
+
|
|
20
|
+
export { protectFormKeys, isValidEmail };
|
package/src/hooks/index.tsx
CHANGED
|
@@ -13,7 +13,7 @@ import { useURLSearchParam } from "./location";
|
|
|
13
13
|
import { useHandleClickOutside, useModal, useModals, useToast } from "./modals";
|
|
14
14
|
import { useNetworkStatus } from "./network";
|
|
15
15
|
import { useResizable } from "./resize";
|
|
16
|
-
import { useGlobalPermission, usePermission, usePermissions, usePermissionsForSite } from "./users";
|
|
16
|
+
import { useGlobalPermission, usePermission, usePermissions, usePermissionsForPage, usePermissionsForSite } from "./users";
|
|
17
17
|
import { useFirefoxScrollLock, useWindowSize } from "./window";
|
|
18
18
|
|
|
19
19
|
export {
|
|
@@ -35,6 +35,7 @@ export {
|
|
|
35
35
|
useOnMessageReceivedFromOutside,
|
|
36
36
|
usePermission,
|
|
37
37
|
usePermissions,
|
|
38
|
+
usePermissionsForPage,
|
|
38
39
|
usePermissionsForSite,
|
|
39
40
|
usePrevious,
|
|
40
41
|
useResizable,
|
package/src/hooks/users.tsx
CHANGED
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
import { useMemo } from "react";
|
|
2
2
|
import { useSelector } from "react-redux";
|
|
3
3
|
|
|
4
|
-
import type { IRootState } from "@ax/types";
|
|
4
|
+
import type { IDataLanguage, IPageLanguage, IRootState, PageAccessGrantsSummary } from "@ax/types";
|
|
5
|
+
|
|
6
|
+
const useUserState = () =>
|
|
7
|
+
useSelector((state: IRootState) => ({
|
|
8
|
+
currentUser: state.users.currentUser,
|
|
9
|
+
userPermissions: state.users.currentPermissions,
|
|
10
|
+
globalPermissions: state.users.globalPermissions,
|
|
11
|
+
roles: state.users.roles,
|
|
12
|
+
isSuperAdmin: state.users.currentUser?.isSuperAdmin,
|
|
13
|
+
}));
|
|
5
14
|
|
|
6
15
|
const usePermission = (permission: string | string[] | undefined): boolean => {
|
|
7
|
-
const currentUser
|
|
8
|
-
const userPermissions = useSelector((state: IRootState) => state.users.currentPermissions);
|
|
9
|
-
const isSuperAdmin = useSelector((state: IRootState) => state.users.currentUser?.isSuperAdmin);
|
|
16
|
+
const { currentUser, userPermissions, isSuperAdmin } = useUserState();
|
|
10
17
|
|
|
11
18
|
const isAllowedTo = (permissions: string[]) =>
|
|
12
19
|
userPermissions && permissions.some((permission: string) => userPermissions.includes(permission));
|
|
@@ -29,9 +36,7 @@ const usePermission = (permission: string | string[] | undefined): boolean => {
|
|
|
29
36
|
};
|
|
30
37
|
|
|
31
38
|
const usePermissions = <T extends Record<string, string | string[]>>(permissions: T): Record<keyof T, boolean> => {
|
|
32
|
-
const currentUser
|
|
33
|
-
const userPermissions = useSelector((state: IRootState) => state.users.currentPermissions);
|
|
34
|
-
const isSuperAdmin = useSelector((state: IRootState) => state.users.currentUser?.isSuperAdmin);
|
|
39
|
+
const { currentUser, userPermissions, isSuperAdmin } = useUserState();
|
|
35
40
|
|
|
36
41
|
return useMemo(() => {
|
|
37
42
|
if (!currentUser) {
|
|
@@ -59,9 +64,7 @@ const usePermissions = <T extends Record<string, string | string[]>>(permissions
|
|
|
59
64
|
};
|
|
60
65
|
|
|
61
66
|
const useGlobalPermission = (permission: string | string[] | undefined): boolean => {
|
|
62
|
-
const currentUser
|
|
63
|
-
const userPermissions = useSelector((state: IRootState) => state.users.globalPermissions);
|
|
64
|
-
const isSuperAdmin = useSelector((state: IRootState) => state.users.currentUser?.isSuperAdmin);
|
|
67
|
+
const { currentUser, globalPermissions: userPermissions, isSuperAdmin } = useUserState();
|
|
65
68
|
|
|
66
69
|
const isAllowedTo = (permissions: string[]) =>
|
|
67
70
|
userPermissions && permissions.some((permission: string) => userPermissions.includes(permission));
|
|
@@ -87,9 +90,7 @@ const usePermissionsForSite = <T extends Record<string, string | string[]>>(
|
|
|
87
90
|
siteId: number | string,
|
|
88
91
|
permissions: T,
|
|
89
92
|
): Record<keyof T, boolean> => {
|
|
90
|
-
const currentUser
|
|
91
|
-
const roles = useSelector((state: IRootState) => state.users.roles);
|
|
92
|
-
const isSuperAdmin = useSelector((state: IRootState) => state.users.currentUser?.isSuperAdmin);
|
|
93
|
+
const { currentUser, roles, isSuperAdmin } = useUserState();
|
|
93
94
|
|
|
94
95
|
return useMemo(() => {
|
|
95
96
|
if (!currentUser) {
|
|
@@ -145,4 +146,143 @@ const usePermissionsForSite = <T extends Record<string, string | string[]>>(
|
|
|
145
146
|
}, [siteId, permissions, currentUser, roles, isSuperAdmin]);
|
|
146
147
|
};
|
|
147
148
|
|
|
148
|
-
|
|
149
|
+
const usePermissionsForPage = <T extends Record<string, string | string[]>>(
|
|
150
|
+
siteId: number | string,
|
|
151
|
+
pageId: number,
|
|
152
|
+
permissions: T,
|
|
153
|
+
accessGrants?: PageAccessGrantsSummary | null,
|
|
154
|
+
pageLanguages?: (IDataLanguage | IPageLanguage)[],
|
|
155
|
+
): Record<keyof T, boolean> => {
|
|
156
|
+
const { currentUser, roles, isSuperAdmin } = useUserState();
|
|
157
|
+
|
|
158
|
+
return useMemo(() => {
|
|
159
|
+
const createPermissionResult = (value: boolean): Record<keyof T, boolean> =>
|
|
160
|
+
Object.keys(permissions).reduce(
|
|
161
|
+
(acc, key) => {
|
|
162
|
+
acc[key as keyof T] = value;
|
|
163
|
+
return acc;
|
|
164
|
+
},
|
|
165
|
+
{} as Record<keyof T, boolean>,
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
if (!currentUser) {
|
|
169
|
+
return createPermissionResult(false);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (isSuperAdmin) {
|
|
173
|
+
return createPermissionResult(true);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const userRoles = currentUser.roles.find(
|
|
177
|
+
(roleSite) => roleSite.siteId === siteId || roleSite.siteId === "all",
|
|
178
|
+
)?.roles;
|
|
179
|
+
|
|
180
|
+
// Check if pageId is part of a translation group in pageLanguages
|
|
181
|
+
// If yes, user can access it if they have access to ANY translation
|
|
182
|
+
const getLanguagePageId = (lang: IDataLanguage | IPageLanguage): number | undefined => {
|
|
183
|
+
if ("pageId" in lang) {
|
|
184
|
+
return lang.pageId;
|
|
185
|
+
}
|
|
186
|
+
if ("id" in lang) {
|
|
187
|
+
return lang.id;
|
|
188
|
+
}
|
|
189
|
+
return undefined;
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const hasAccessToTranslationGroup = pageLanguages?.length
|
|
193
|
+
? (() => {
|
|
194
|
+
const isPartOfGroup = pageLanguages.some((lang) => getLanguagePageId(lang) === pageId);
|
|
195
|
+
if (!isPartOfGroup) {
|
|
196
|
+
return false; // pageId is not in this translation group
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Get all pageIds in this translation group
|
|
200
|
+
const translationPageIds = pageLanguages
|
|
201
|
+
.map((lang) => getLanguagePageId(lang))
|
|
202
|
+
.filter((id): id is number => id !== undefined);
|
|
203
|
+
|
|
204
|
+
// Check if user has access to ANY of the translation pageIds
|
|
205
|
+
if (currentUser.pagePermissions?.length) {
|
|
206
|
+
const siteEntry = currentUser.pagePermissions.find(
|
|
207
|
+
(entry) => entry.siteId === siteId || entry.siteId === "global",
|
|
208
|
+
);
|
|
209
|
+
if (!siteEntry) {
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
return translationPageIds.some((translationPageId) => siteEntry.pageIds.includes(translationPageId));
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return true; // No pagePermissions restrictions, allow access
|
|
216
|
+
})()
|
|
217
|
+
: undefined;
|
|
218
|
+
|
|
219
|
+
// If pageId is part of a translation group, use the translation group access result
|
|
220
|
+
const isPagePermissionRestricted =
|
|
221
|
+
hasAccessToTranslationGroup !== undefined
|
|
222
|
+
? !hasAccessToTranslationGroup
|
|
223
|
+
: currentUser.pagePermissions?.length
|
|
224
|
+
? (() => {
|
|
225
|
+
const siteEntry = currentUser.pagePermissions.find(
|
|
226
|
+
(entry) => entry.siteId === siteId || entry.siteId === "global",
|
|
227
|
+
);
|
|
228
|
+
// Page is restricted if site is not in pagePermissions or page is not in the site's pageIds
|
|
229
|
+
if (!siteEntry) {
|
|
230
|
+
return true;
|
|
231
|
+
}
|
|
232
|
+
return !siteEntry.pageIds.includes(pageId);
|
|
233
|
+
})()
|
|
234
|
+
: false;
|
|
235
|
+
|
|
236
|
+
// User is restricted by pagePermissions and page is not in their list
|
|
237
|
+
// But they can still access if in accessGrants
|
|
238
|
+
if (isPagePermissionRestricted) {
|
|
239
|
+
const pageHasAccessGrants = accessGrants?.users?.length || accessGrants?.roles?.length;
|
|
240
|
+
|
|
241
|
+
if (pageHasAccessGrants) {
|
|
242
|
+
const hasAccessGrantsAccess =
|
|
243
|
+
accessGrants.users?.includes(currentUser.id || -1) ||
|
|
244
|
+
userRoles?.some((roleId) => accessGrants.roles?.includes(roleId));
|
|
245
|
+
|
|
246
|
+
if (!hasAccessGrantsAccess) {
|
|
247
|
+
return createPermissionResult(false);
|
|
248
|
+
}
|
|
249
|
+
} else {
|
|
250
|
+
return createPermissionResult(false);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (!userRoles?.length) {
|
|
255
|
+
return createPermissionResult(false);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const roleMap = new Map(roles?.map((r) => [r.id, r]) || []);
|
|
259
|
+
const sitePermissionsSet = new Set<string>();
|
|
260
|
+
|
|
261
|
+
userRoles.forEach((roleID: number) => {
|
|
262
|
+
const role = roleMap.get(roleID);
|
|
263
|
+
if (role) {
|
|
264
|
+
const permissions = siteId === "global" ? role.permissions.globalPermissions : role.permissions.sitePermissions;
|
|
265
|
+
permissions.forEach((perm) => {
|
|
266
|
+
sitePermissionsSet.add(perm.key);
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
const result: Record<string, boolean> = {};
|
|
272
|
+
for (const key of Object.keys(permissions)) {
|
|
273
|
+
const permission = permissions[key as keyof T];
|
|
274
|
+
const arrayPermission = Array.isArray(permission) ? permission : [permission];
|
|
275
|
+
result[key] = arrayPermission.some((perm: string) => sitePermissionsSet.has(perm));
|
|
276
|
+
}
|
|
277
|
+
return result as Record<keyof T, boolean>;
|
|
278
|
+
}, [siteId, pageId, permissions, accessGrants, currentUser, roles, isSuperAdmin, pageLanguages]);
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
export {
|
|
282
|
+
useUserState,
|
|
283
|
+
usePermission,
|
|
284
|
+
usePermissions,
|
|
285
|
+
useGlobalPermission,
|
|
286
|
+
usePermissionsForSite,
|
|
287
|
+
usePermissionsForPage,
|
|
288
|
+
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
2
|
|
|
3
|
+
import { users } from "@ax/api";
|
|
3
4
|
import { CheckGroupFilter } from "@ax/components";
|
|
4
5
|
import { isReqOk } from "@ax/helpers";
|
|
5
|
-
import {
|
|
6
|
-
import { IQueryValue, IUser } from "@ax/types";
|
|
6
|
+
import type { IQueryValue, IUser } from "@ax/types";
|
|
7
7
|
|
|
8
8
|
const UserFilter = (props: IUserFilterProps): JSX.Element => {
|
|
9
9
|
const { filterItems, value } = props;
|
|
@@ -39,15 +39,14 @@ const UserFilter = (props: IUserFilterProps): JSX.Element => {
|
|
|
39
39
|
useEffect(() => {
|
|
40
40
|
getUsers()
|
|
41
41
|
.then((items: IUser[]) => {
|
|
42
|
-
items
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
});
|
|
42
|
+
items?.forEach((item) => {
|
|
43
|
+
const newFilter = {
|
|
44
|
+
name: item.id?.toString() || "",
|
|
45
|
+
value: item.id?.toString() || "",
|
|
46
|
+
title: item.name,
|
|
47
|
+
};
|
|
48
|
+
filters.push(newFilter);
|
|
49
|
+
});
|
|
51
50
|
|
|
52
51
|
setOptions(filters);
|
|
53
52
|
})
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
getTemplateDisplayName,
|
|
13
13
|
slugify,
|
|
14
14
|
} from "@ax/helpers";
|
|
15
|
-
import { useModals,
|
|
15
|
+
import { useModals, usePermissionsForPage } from "@ax/hooks";
|
|
16
16
|
import type {
|
|
17
17
|
ICheck,
|
|
18
18
|
IColumn,
|
|
@@ -85,6 +85,7 @@ const PageItem = (props: IPageItemProps): JSX.Element => {
|
|
|
85
85
|
publicationScheduled,
|
|
86
86
|
haveDraftPage,
|
|
87
87
|
title,
|
|
88
|
+
accessGrants,
|
|
88
89
|
} = page;
|
|
89
90
|
|
|
90
91
|
const displayName = getTemplateDisplayName(templateId);
|
|
@@ -95,15 +96,21 @@ const PageItem = (props: IPageItemProps): JSX.Element => {
|
|
|
95
96
|
const [deleteAllVersions, setDeleteAllVersions] = useState(false);
|
|
96
97
|
const { toggleModal, isOpen } = useModals(["duplicate", "remove", "unpublish", "delete", "copy"]);
|
|
97
98
|
|
|
98
|
-
const isAllowedTo =
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
99
|
+
const isAllowedTo = usePermissionsForPage(
|
|
100
|
+
page.site!,
|
|
101
|
+
page.id,
|
|
102
|
+
{
|
|
103
|
+
duplicatePages: "content.duplicatePages",
|
|
104
|
+
publishUnpublishPages: "content.publishUnpublishPages",
|
|
105
|
+
createPages: "content.createPages",
|
|
106
|
+
copyPageInOtherSite: "content.copyPageInOtherSite",
|
|
107
|
+
deletePages: "content.deletePages",
|
|
108
|
+
deletePublishedPages: "content.deletePublishedPages",
|
|
109
|
+
editContentPages: "content.editContentPages",
|
|
110
|
+
},
|
|
111
|
+
accessGrants,
|
|
112
|
+
pageLanguages,
|
|
113
|
+
);
|
|
107
114
|
|
|
108
115
|
const currentTemplateDataPacks = schemas.templates[templateId].dataPacks;
|
|
109
116
|
|
|
@@ -47,7 +47,6 @@ import type {
|
|
|
47
47
|
ISavePageParams,
|
|
48
48
|
ISchemaField,
|
|
49
49
|
ISite,
|
|
50
|
-
ISiteRoles,
|
|
51
50
|
IStructuredData,
|
|
52
51
|
IStructuredDataContent,
|
|
53
52
|
IUser,
|
|
@@ -176,8 +175,7 @@ const Content = (props: IProps): JSX.Element => {
|
|
|
176
175
|
const currentSitePagesTemplatesIds = allSitePages?.map((page) => page.templateId);
|
|
177
176
|
const currentSitesByLang = sitesByLang?.filter(
|
|
178
177
|
(site: ISite) =>
|
|
179
|
-
user?.isSuperAdmin ||
|
|
180
|
-
user?.roles.find((siteRole: ISiteRoles) => siteRole.siteId === site.id || siteRole.siteId === "all"),
|
|
178
|
+
user?.isSuperAdmin || user?.roles.find((siteRole) => siteRole.siteId === site.id || siteRole.siteId === "all"),
|
|
181
179
|
);
|
|
182
180
|
const categoryColumns: ISchemaField[] = currentStructuredData?.schema
|
|
183
181
|
? currentStructuredData.schema.fields.filter((field: ISchemaField) => field.showList)
|
|
@@ -46,6 +46,8 @@ const Editor = (props: IProps) => {
|
|
|
46
46
|
isKeywordsPreviewOpen,
|
|
47
47
|
headingsFilter,
|
|
48
48
|
keywordsFilter,
|
|
49
|
+
isRestrictedPage,
|
|
50
|
+
onRestrictionsClick,
|
|
49
51
|
} = props;
|
|
50
52
|
|
|
51
53
|
const actions = {
|
|
@@ -77,6 +79,8 @@ const Editor = (props: IProps) => {
|
|
|
77
79
|
toggleKeywordsPreview={toggleKeywordsPreview}
|
|
78
80
|
isKeywordsPreviewOpen={isKeywordsPreviewOpen}
|
|
79
81
|
keywordsFilter={keywordsFilter}
|
|
82
|
+
isRestrictedPage={isRestrictedPage}
|
|
83
|
+
onRestrictionsClick={onRestrictionsClick}
|
|
80
84
|
/>
|
|
81
85
|
}
|
|
82
86
|
rightPanel={
|
|
@@ -141,6 +145,8 @@ interface IPageBrowserDispatchProps {
|
|
|
141
145
|
isKeywordsPreviewOpen: boolean;
|
|
142
146
|
headingsFilter: HeadingFilter;
|
|
143
147
|
keywordsFilter: string[];
|
|
148
|
+
isRestrictedPage?: boolean;
|
|
149
|
+
onRestrictionsClick?(): void;
|
|
144
150
|
}
|
|
145
151
|
|
|
146
152
|
type IProps = IEditorStateProps & IPageBrowserDispatchProps;
|
|
@@ -24,6 +24,8 @@ const PageBrowser = (props: IProps) => {
|
|
|
24
24
|
setScrollEditorID,
|
|
25
25
|
toggleHeadingsPreview,
|
|
26
26
|
toggleKeywordsPreview,
|
|
27
|
+
isRestrictedPage,
|
|
28
|
+
onRestrictionsClick,
|
|
27
29
|
} = props;
|
|
28
30
|
|
|
29
31
|
const slugWithSlash = slug ? (slug.startsWith("/") ? slug : `/${slug}`) : "";
|
|
@@ -59,6 +61,8 @@ const PageBrowser = (props: IProps) => {
|
|
|
59
61
|
headingFilter={headingsFilter}
|
|
60
62
|
toggleKeywordsPreview={toggleKeywordsPreview}
|
|
61
63
|
keywordsFilter={keywordsFilter}
|
|
64
|
+
isRestrictedPage={isRestrictedPage}
|
|
65
|
+
onRestrictionsClick={onRestrictionsClick}
|
|
62
66
|
/>
|
|
63
67
|
);
|
|
64
68
|
};
|
|
@@ -89,6 +93,8 @@ interface IPageBrowserDispatchProps {
|
|
|
89
93
|
setScrollEditorID(editorID: number | null): void;
|
|
90
94
|
toggleHeadingsPreview?(): void;
|
|
91
95
|
toggleKeywordsPreview?(): void;
|
|
96
|
+
isRestrictedPage?: boolean;
|
|
97
|
+
onRestrictionsClick?(): void;
|
|
92
98
|
}
|
|
93
99
|
|
|
94
100
|
type IProps = IPageBrowserStateProps & IPageBrowserDispatchProps;
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import PageBrowser from "../PageBrowser";
|
|
2
|
+
|
|
2
3
|
import * as S from "./style";
|
|
3
4
|
|
|
4
5
|
const PreviewBrowser = (props: IProps) => {
|
|
5
|
-
const { theme } = props;
|
|
6
|
+
const { theme, isRestrictedPage } = props;
|
|
6
7
|
return (
|
|
7
8
|
<S.BrowserWrapper>
|
|
8
|
-
<PageBrowser isReadOnly={true} isPreview={true} theme={theme} />
|
|
9
|
+
<PageBrowser isReadOnly={true} isPreview={true} theme={theme} isRestrictedPage={isRestrictedPage} />
|
|
9
10
|
</S.BrowserWrapper>
|
|
10
11
|
);
|
|
11
12
|
};
|
|
12
13
|
|
|
13
14
|
interface IProps {
|
|
14
15
|
theme: string;
|
|
16
|
+
isRestrictedPage: boolean;
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
export default PreviewBrowser;
|