mirror of
https://github.com/strapi/strapi.git
synced 2025-12-18 18:53:46 +00:00
WIP add redux support to license limit hook, various minor changes
Co-authored-by: Gustav Hansen <gu@stav.dev>
This commit is contained in:
parent
a0144c1565
commit
ebdf9c06c2
@ -1,14 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
// This wrapper is needed for ee_else_ce component substitution to add ee specific license context in ee mode
|
|
||||||
const LicenseContextWrapper = ({ children }) => {
|
|
||||||
// eslint-disable-next-line react/jsx-no-useless-fragment
|
|
||||||
return <>{children}</>;
|
|
||||||
};
|
|
||||||
|
|
||||||
LicenseContextWrapper.propTypes = {
|
|
||||||
children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default LicenseContextWrapper;
|
|
||||||
@ -9,7 +9,7 @@ import {
|
|||||||
} from '@strapi/helper-plugin';
|
} from '@strapi/helper-plugin';
|
||||||
import { useQueries } from 'react-query';
|
import { useQueries } from 'react-query';
|
||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
import LicenseContextWrapper from 'ee_else_ce/components/AuthenticatedApp/LicenseContextWrapper';
|
import useLicenseLimitInfos from 'ee_else_ce/hooks/useLicenseLimitInfos';
|
||||||
import packageJSON from '../../../../package.json';
|
import packageJSON from '../../../../package.json';
|
||||||
import { useConfigurations } from '../../hooks';
|
import { useConfigurations } from '../../hooks';
|
||||||
import PluginsInitializer from '../PluginsInitializer';
|
import PluginsInitializer from '../PluginsInitializer';
|
||||||
@ -73,6 +73,10 @@ const AuthenticatedApp = () => {
|
|||||||
}
|
}
|
||||||
}, [userRoles, appInfos]);
|
}, [userRoles, appInfos]);
|
||||||
|
|
||||||
|
// This hook fetches license allowances in 'ee' mode and stores data in redux for global access.
|
||||||
|
// It does nothing in 'ce'.
|
||||||
|
useLicenseLimitInfos();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const getUserId = async () => {
|
const getUserId = async () => {
|
||||||
const userId = await hashAdminUserEmail(userInfo);
|
const userId = await hashAdminUserEmail(userInfo);
|
||||||
@ -110,13 +114,11 @@ const AuthenticatedApp = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LicenseContextWrapper>
|
<AppInfosContext.Provider value={appInfosValue}>
|
||||||
<AppInfosContext.Provider value={appInfosValue}>
|
<RBACProvider permissions={permissions} refetchPermissions={refetch}>
|
||||||
<RBACProvider permissions={permissions} refetchPermissions={refetch}>
|
<PluginsInitializer />
|
||||||
<PluginsInitializer />
|
</RBACProvider>
|
||||||
</RBACProvider>
|
</AppInfosContext.Provider>
|
||||||
</AppInfosContext.Provider>
|
|
||||||
</LicenseContextWrapper>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
// Shallow component that is overridden in EE
|
|
||||||
const EENotification = () => {
|
|
||||||
// eslint-disable-next-line react/jsx-no-useless-fragment
|
|
||||||
return <></>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default EENotification;
|
|
||||||
@ -67,7 +67,14 @@ const Notification = ({ dispatch, notification }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (title) {
|
if (title) {
|
||||||
alertTitle = typeof title === 'string' ? title : formatMessage(title);
|
alertTitle =
|
||||||
|
typeof title === 'string'
|
||||||
|
? title
|
||||||
|
: formattedMessage({
|
||||||
|
id: title?.id || title,
|
||||||
|
defaultMessage: title?.defaultMessage || title?.id || title,
|
||||||
|
values: title?.values,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -10,3 +10,4 @@ export { default as usePermissionsDataManager } from './usePermissionsDataManage
|
|||||||
export { default as useReleaseNotification } from './useReleaseNotification';
|
export { default as useReleaseNotification } from './useReleaseNotification';
|
||||||
export { default as useThemeToggle } from './useThemeToggle';
|
export { default as useThemeToggle } from './useThemeToggle';
|
||||||
export { default as useRegenerate } from './useRegenerate';
|
export { default as useRegenerate } from './useRegenerate';
|
||||||
|
export { default as useLicenseLimitInfos } from './useLicenseLimitInfos';
|
||||||
|
|||||||
@ -0,0 +1,5 @@
|
|||||||
|
const useLicenseLimitInfos = () => {
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useLicenseLimitInfos;
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
const useLicenseLimitNotification = () => {
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useLicenseLimitNotification;
|
||||||
@ -13,7 +13,7 @@ import { Layout } from '@strapi/design-system/Layout';
|
|||||||
import { Main } from '@strapi/design-system/Main';
|
import { Main } from '@strapi/design-system/Main';
|
||||||
import { Box } from '@strapi/design-system/Box';
|
import { Box } from '@strapi/design-system/Box';
|
||||||
import { Grid, GridItem } from '@strapi/design-system/Grid';
|
import { Grid, GridItem } from '@strapi/design-system/Grid';
|
||||||
import EENotification from 'ee_else_ce/components/EENotification';
|
import useLicenseLimitNotification from 'ee_else_ce/hooks/useLicenseLimitNotification';
|
||||||
import cornerOrnamentPath from './assets/corner-ornament.svg';
|
import cornerOrnamentPath from './assets/corner-ornament.svg';
|
||||||
import { useModels } from '../../hooks';
|
import { useModels } from '../../hooks';
|
||||||
import isGuidedTourCompleted from '../../components/GuidedTour/utils/isGuidedTourCompleted';
|
import isGuidedTourCompleted from '../../components/GuidedTour/utils/isGuidedTourCompleted';
|
||||||
@ -36,6 +36,7 @@ const HomePage = () => {
|
|||||||
// Temporary until we develop the menu API
|
// Temporary until we develop the menu API
|
||||||
const { collectionTypes, singleTypes, isLoading: isLoadingForModels } = useModels();
|
const { collectionTypes, singleTypes, isLoading: isLoadingForModels } = useModels();
|
||||||
const { guidedTourState, isGuidedTourVisible, isSkipped } = useGuidedTour();
|
const { guidedTourState, isGuidedTourVisible, isSkipped } = useGuidedTour();
|
||||||
|
useLicenseLimitNotification();
|
||||||
|
|
||||||
const showGuidedTour =
|
const showGuidedTour =
|
||||||
!isGuidedTourCompleted(guidedTourState) && isGuidedTourVisible && !isSkipped;
|
!isGuidedTourCompleted(guidedTourState) && isGuidedTourVisible && !isSkipped;
|
||||||
@ -61,7 +62,6 @@ const HomePage = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
<EENotification />
|
|
||||||
<FormattedMessage id="HomePage.helmet.title" defaultMessage="Homepage">
|
<FormattedMessage id="HomePage.helmet.title" defaultMessage="Homepage">
|
||||||
{(title) => <Helmet title={title[0]} />}
|
{(title) => <Helmet title={title[0]} />}
|
||||||
</FormattedMessage>
|
</FormattedMessage>
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
const AdminSeatInfo = () => {};
|
const AdminSeatInfo = () => {
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
export default AdminSeatInfo;
|
export default AdminSeatInfo;
|
||||||
|
|||||||
@ -18,7 +18,6 @@ import { Button } from '@strapi/design-system/Button';
|
|||||||
import { Link } from '@strapi/design-system/v2/Link';
|
import { Link } from '@strapi/design-system/v2/Link';
|
||||||
import ExternalLink from '@strapi/icons/ExternalLink';
|
import ExternalLink from '@strapi/icons/ExternalLink';
|
||||||
import Check from '@strapi/icons/Check';
|
import Check from '@strapi/icons/Check';
|
||||||
import EENotification from 'ee_else_ce/components/EENotification';
|
|
||||||
import AdminSeatInfo from 'ee_else_ce/pages/SettingsPage/pages/ApplicationInfosPage/components/AdminSeatInfo';
|
import AdminSeatInfo from 'ee_else_ce/pages/SettingsPage/pages/ApplicationInfosPage/components/AdminSeatInfo';
|
||||||
import adminPermissions from '../../../../permissions';
|
import adminPermissions from '../../../../permissions';
|
||||||
import { useConfigurations } from '../../../../hooks';
|
import { useConfigurations } from '../../../../hooks';
|
||||||
@ -95,7 +94,6 @@ const ApplicationInfosPage = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
<EENotification />
|
|
||||||
<SettingsPageTitle name="Application" />
|
<SettingsPageTitle name="Application" />
|
||||||
<Main>
|
<Main>
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
|
|||||||
@ -14,8 +14,8 @@ import { useNotifyAT } from '@strapi/design-system/LiveRegions';
|
|||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import { useMutation, useQuery, useQueryClient } from 'react-query';
|
import { useMutation, useQuery, useQueryClient } from 'react-query';
|
||||||
import EENotification from 'ee_else_ce/components/EENotification';
|
|
||||||
import CreateAction from 'ee_else_ce/pages/SettingsPage/pages/Users/ListPage/CreateAction';
|
import CreateAction from 'ee_else_ce/pages/SettingsPage/pages/Users/ListPage/CreateAction';
|
||||||
|
import useLicenseLimitNotification from 'ee_else_ce/hooks/useLicenseLimitNotification';
|
||||||
import adminPermissions from '../../../../../permissions';
|
import adminPermissions from '../../../../../permissions';
|
||||||
import TableRows from './DynamicTable/TableRows';
|
import TableRows from './DynamicTable/TableRows';
|
||||||
import Filters from './Filters';
|
import Filters from './Filters';
|
||||||
@ -35,6 +35,7 @@ const ListPage = () => {
|
|||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
const { search } = useLocation();
|
const { search } = useLocation();
|
||||||
useFocusWhenNavigate();
|
useFocusWhenNavigate();
|
||||||
|
useLicenseLimitNotification();
|
||||||
const { notifyStatus } = useNotifyAT();
|
const { notifyStatus } = useNotifyAT();
|
||||||
const queryName = ['users', search];
|
const queryName = ['users', search];
|
||||||
|
|
||||||
@ -104,7 +105,6 @@ const ListPage = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Main aria-busy={isLoading}>
|
<Main aria-busy={isLoading}>
|
||||||
<EENotification />
|
|
||||||
<SettingsPageTitle name="Users" />
|
<SettingsPageTitle name="Users" />
|
||||||
<HeaderLayout
|
<HeaderLayout
|
||||||
primaryAction={createAction}
|
primaryAction={createAction}
|
||||||
|
|||||||
@ -844,7 +844,9 @@
|
|||||||
"notification.warning.title": "Warning:",
|
"notification.warning.title": "Warning:",
|
||||||
"notification.warning.404": "404 - Not found",
|
"notification.warning.404": "404 - Not found",
|
||||||
"notification.ee.warning.over-seat-limit": "Add seats to re-enable Users. If you already did it but it's not reflected in Strapi yet, make sure to restart your app.",
|
"notification.ee.warning.over-seat-limit": "Add seats to re-enable Users. If you already did it but it's not reflected in Strapi yet, make sure to restart your app.",
|
||||||
|
"notification.ee.warning.over-seat-limit.title": "Over seat limit ({currentUserCount}/{permittedSeats})",
|
||||||
"notification.ee.warning.at-seat-limit": "Add seats to invite more Users. If you already did it but it's not reflected in Strapi yet, make sure to restart your app.",
|
"notification.ee.warning.at-seat-limit": "Add seats to invite more Users. If you already did it but it's not reflected in Strapi yet, make sure to restart your app.",
|
||||||
|
"notification.ee.warning.at-seat-limit.title": "At seat limit ({currentUserCount}/{permittedSeats})",
|
||||||
"or": "OR",
|
"or": "OR",
|
||||||
"request.error.model.unknown": "This model doesn't exist",
|
"request.error.model.unknown": "This model doesn't exist",
|
||||||
"skipToContent": "Skip to content",
|
"skipToContent": "Skip to content",
|
||||||
|
|||||||
@ -1,23 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { useQuery } from 'react-query';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { fetchLicenseLimitInfo } from './utils/api';
|
|
||||||
import { LicenseLimitInfosContext } from '../../contexts';
|
|
||||||
|
|
||||||
const LicenseContextWrapper = ({ children }) => {
|
|
||||||
const { data: licenseLimitInfo } = useQuery('license-limit-info', fetchLicenseLimitInfo, {
|
|
||||||
initialData: {},
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<LicenseLimitInfosContext.Provider value={licenseLimitInfo}>
|
|
||||||
{children}
|
|
||||||
</LicenseLimitInfosContext.Provider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
LicenseContextWrapper.propTypes = {
|
|
||||||
children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default LicenseContextWrapper;
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
import { axiosInstance } from '../../../../../admin/src/core/utils';
|
|
||||||
|
|
||||||
const fetchLicenseLimitInfo = async () => {
|
|
||||||
try {
|
|
||||||
const { data, headers } = await axiosInstance.get('/admin/license-limit-information');
|
|
||||||
|
|
||||||
if (!headers['content-type'].includes('application/json')) {
|
|
||||||
throw new Error('Not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
return data.data;
|
|
||||||
} catch (error) {
|
|
||||||
throw new Error(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export { fetchLicenseLimitInfo };
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
import { useEffect } from 'react';
|
|
||||||
import { useLicenseLimitNotification, useLicenseLimitInfos } from '../../hooks';
|
|
||||||
|
|
||||||
// Shallow component that is overridden in EE
|
|
||||||
const EENotification = () => {
|
|
||||||
const licenseLimitNotification = useLicenseLimitNotification();
|
|
||||||
const licenseLimitInfos = useLicenseLimitInfos();
|
|
||||||
const { shouldNotify } = licenseLimitInfos;
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (shouldNotify) {
|
|
||||||
licenseLimitNotification(() => {
|
|
||||||
window.sessionStorage.setItem('licenseNotificationShownOnHome', true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default EENotification;
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
import { createContext } from 'react';
|
|
||||||
|
|
||||||
const LicenseLimitInfosContext = createContext({});
|
|
||||||
|
|
||||||
export default LicenseLimitInfosContext;
|
|
||||||
@ -1 +0,0 @@
|
|||||||
export { default as LicenseLimitInfosContext } from './LicenseLimitInfosContext';
|
|
||||||
@ -1,10 +1,68 @@
|
|||||||
import { useContext } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { LicenseLimitInfosContext } from '../../contexts';
|
import { useSelector, useDispatch } from 'react-redux';
|
||||||
|
import { useFetchClient } from '@strapi/helper-plugin';
|
||||||
|
import { useQuery } from 'react-query';
|
||||||
|
import { produce } from 'immer';
|
||||||
|
|
||||||
|
import { useInjectReducer } from '../../../../admin/src/hooks/useInjectReducer';
|
||||||
|
|
||||||
|
const NS = 'StrapiAdmin/ee_license-info';
|
||||||
|
const ACTION_EE_LICENSE_INFO_SET_DATA = 'StrapiAdmin/EE_LICENSE_INFO_GET_DATA';
|
||||||
|
|
||||||
|
const initalState = {
|
||||||
|
serverState: {
|
||||||
|
currentUserCount: null,
|
||||||
|
permittedSeats: null,
|
||||||
|
shouldNotify: false,
|
||||||
|
licenseLimitStatus: null,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const reducer = (state = initalState, action) =>
|
||||||
|
/* eslint-disable-next-line consistent-return */
|
||||||
|
produce(state, (draft) => {
|
||||||
|
switch (action.type) {
|
||||||
|
case ACTION_EE_LICENSE_INFO_SET_DATA: {
|
||||||
|
draft.serverState = action.payload;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return draft;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const actionSetData = (payload) => {
|
||||||
|
return {
|
||||||
|
type: ACTION_EE_LICENSE_INFO_SET_DATA,
|
||||||
|
payload,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const useLicenseLimitInfos = () => {
|
const useLicenseLimitInfos = () => {
|
||||||
const context = useContext(LicenseLimitInfosContext);
|
const instance = useFetchClient();
|
||||||
|
const fetchLicenseLimitInfo = async () => {
|
||||||
|
const {
|
||||||
|
data: { data },
|
||||||
|
} = await instance.get('/admin/license-limit-information');
|
||||||
|
|
||||||
return context;
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
const { data, status } = useQuery('license-limit-info', fetchLicenseLimitInfo);
|
||||||
|
|
||||||
|
const state = useSelector((state) => state?.[NS] ?? initalState);
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
useInjectReducer(NS, reducer);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (status === 'success') {
|
||||||
|
dispatch(actionSetData(data));
|
||||||
|
}
|
||||||
|
}, [data, status, dispatch]);
|
||||||
|
|
||||||
|
return state.serverState;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default useLicenseLimitInfos;
|
export default useLicenseLimitInfos;
|
||||||
|
|||||||
@ -3,7 +3,8 @@
|
|||||||
* useLicenseLimitNotification
|
* useLicenseLimitNotification
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
// import { useRef } from 'react';
|
import { useEffect } from 'react';
|
||||||
|
import { useLocation } from 'react-router';
|
||||||
import { useNotification } from '@strapi/helper-plugin';
|
import { useNotification } from '@strapi/helper-plugin';
|
||||||
import useLicenseLimitInfos from '../useLicenseLimitInfos';
|
import useLicenseLimitInfos from '../useLicenseLimitInfos';
|
||||||
|
|
||||||
@ -18,7 +19,12 @@ const notificationBody = (currentUserCount, permittedSeats, licenseLimitStatus)
|
|||||||
defaultMessage:
|
defaultMessage:
|
||||||
"Add seats to re-enable users. If you already did it but it's not reflected in Strapi yet, make sure to restart your app.",
|
"Add seats to re-enable users. If you already did it but it's not reflected in Strapi yet, make sure to restart your app.",
|
||||||
},
|
},
|
||||||
title: `Over seat limit (${currentUserCount}/${permittedSeats})`,
|
// Title is translated in the Notification component
|
||||||
|
title: {
|
||||||
|
id: 'notification.ee.warning.over-seat-limit.title',
|
||||||
|
defaultMessage: 'Over seat limit ({currentUserCount}/{permittedSeats})',
|
||||||
|
values: { currentUserCount, permittedSeats },
|
||||||
|
},
|
||||||
link: {
|
link: {
|
||||||
url: 'test url',
|
url: 'test url',
|
||||||
label: 'ADD SEATS',
|
label: 'ADD SEATS',
|
||||||
@ -37,7 +43,11 @@ const notificationBody = (currentUserCount, permittedSeats, licenseLimitStatus)
|
|||||||
defaultMessage:
|
defaultMessage:
|
||||||
"Add seats to re-enable users. If you already did it but it's not reflected in Strapi yet, make sure to restart your app.",
|
"Add seats to re-enable users. If you already did it but it's not reflected in Strapi yet, make sure to restart your app.",
|
||||||
},
|
},
|
||||||
title: `At seat limit (${currentUserCount}/${permittedSeats})`,
|
title: {
|
||||||
|
id: 'notification.ee.warning.at-seat-limit.title',
|
||||||
|
defaultMessage: 'At seat limit ({currentUserCount}/{permittedSeats})',
|
||||||
|
values: { currentUserCount, permittedSeats },
|
||||||
|
},
|
||||||
link: {
|
link: {
|
||||||
url: 'test url',
|
url: 'test url',
|
||||||
label: 'ADD SEATS',
|
label: 'ADD SEATS',
|
||||||
@ -51,21 +61,38 @@ const notificationBody = (currentUserCount, permittedSeats, licenseLimitStatus)
|
|||||||
return notification;
|
return notification;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const shouldDisplayNotification = (pathname) => {
|
||||||
|
const isLocation = (string) => pathname.includes(string);
|
||||||
|
const shownInSession = window.sessionStorage.getItem(`notification-${pathname}`);
|
||||||
|
|
||||||
|
if (isLocation('/') && shownInSession) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLocation('users') && shownInSession) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
const useLicenseLimitNotification = () => {
|
const useLicenseLimitNotification = () => {
|
||||||
const licenseLimitInfos = useLicenseLimitInfos();
|
let licenseLimitInfos = useLicenseLimitInfos();
|
||||||
const toggleNotification = useNotification();
|
const toggleNotification = useNotification();
|
||||||
const { currentUserCount, permittedSeats, licenseLimitStatus } = licenseLimitInfos;
|
const location = useLocation();
|
||||||
|
|
||||||
if (!licenseLimitInfos) return;
|
|
||||||
|
|
||||||
// Won't notify if license user and seat info is not present
|
|
||||||
|
|
||||||
const notification = notificationBody(currentUserCount, permittedSeats, licenseLimitStatus);
|
|
||||||
|
|
||||||
// eslint-disable-next-line consistent-return
|
// eslint-disable-next-line consistent-return
|
||||||
return (onClose) => {
|
useEffect(() => {
|
||||||
|
if (!licenseLimitInfos) return;
|
||||||
|
|
||||||
|
if (!shouldDisplayNotification(location.pathname)) return;
|
||||||
|
|
||||||
|
const { currentUserCount, permittedSeats, licenseLimitStatus } = licenseLimitInfos;
|
||||||
|
const notification = notificationBody(currentUserCount, permittedSeats, licenseLimitStatus);
|
||||||
|
const onClose = () => window.sessionStorage.setItem(`notification-${location.pathname}`, true);
|
||||||
toggleNotification({ ...notification, onClose });
|
toggleNotification({ ...notification, onClose });
|
||||||
};
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default useLicenseLimitNotification;
|
export default useLicenseLimitNotification;
|
||||||
|
|||||||
@ -2,7 +2,11 @@ import React from 'react';
|
|||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Button } from '@strapi/design-system/Button';
|
import { Button } from '@strapi/design-system/Button';
|
||||||
|
import { Tooltip } from '@strapi/design-system/Tooltip';
|
||||||
|
import { Icon } from '@strapi/design-system/Icon';
|
||||||
|
import { Stack } from '@strapi/design-system';
|
||||||
import Envelop from '@strapi/icons/Envelop';
|
import Envelop from '@strapi/icons/Envelop';
|
||||||
|
import ExclamationMarkCircle from '@strapi/icons/ExclamationMarkCircle';
|
||||||
import { useLicenseLimitInfos } from '../../../../../../hooks';
|
import { useLicenseLimitInfos } from '../../../../../../hooks';
|
||||||
|
|
||||||
const CreateAction = ({ onClick }) => {
|
const CreateAction = ({ onClick }) => {
|
||||||
@ -11,18 +15,34 @@ const CreateAction = ({ onClick }) => {
|
|||||||
const { licenseLimitStatus } = licenseLimitInfos;
|
const { licenseLimitStatus } = licenseLimitInfos;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Stack spacing={2} horizontal>
|
||||||
data-testid="create-user-button"
|
<Tooltip
|
||||||
onClick={onClick}
|
description={formatMessage({
|
||||||
startIcon={<Envelop />}
|
id: 'Settings.application.admin-seats.at-limit-tooltip',
|
||||||
size="S"
|
defaultMessage: 'At limit: add seats to invite more users',
|
||||||
disabled={!!licenseLimitStatus}
|
})}
|
||||||
>
|
position="left"
|
||||||
{formatMessage({
|
>
|
||||||
id: 'Settings.permissions.users.create',
|
<Icon
|
||||||
defaultMessage: 'Invite new user',
|
width={`${14 / 16}rem`}
|
||||||
})}
|
height={`${14 / 16}rem`}
|
||||||
</Button>
|
color="danger500"
|
||||||
|
as={ExclamationMarkCircle}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
<Button
|
||||||
|
data-testid="create-user-button"
|
||||||
|
onClick={onClick}
|
||||||
|
startIcon={<Envelop />}
|
||||||
|
size="S"
|
||||||
|
disabled={!!licenseLimitStatus}
|
||||||
|
>
|
||||||
|
{formatMessage({
|
||||||
|
id: 'Settings.permissions.users.create',
|
||||||
|
defaultMessage: 'Invite new user',
|
||||||
|
})}
|
||||||
|
</Button>
|
||||||
|
</Stack>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -2,14 +2,25 @@
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
async licenseLimitInformation() {
|
async licenseLimitInformation() {
|
||||||
const currentUserCount = await strapi.db
|
let shouldNotify = false;
|
||||||
|
let licenseLimitStatus = null;
|
||||||
|
let currentUserCount;
|
||||||
|
const permittedSeats = 5;
|
||||||
|
|
||||||
|
const currentActiveUserCount = await strapi.db
|
||||||
.query('admin::user')
|
.query('admin::user')
|
||||||
.count({ where: { isActive: true } });
|
.count({ where: { isActive: true } });
|
||||||
|
|
||||||
const permittedSeats = 5;
|
const data = await strapi.db.query('strapi::ee-store').findOne({
|
||||||
|
where: { key: 'ee_disabled_users' },
|
||||||
|
});
|
||||||
|
|
||||||
let shouldNotify = false;
|
if (data.value) {
|
||||||
let licenseLimitStatus = null;
|
const eeDisabledUsers = JSON.parse(data.value);
|
||||||
|
currentUserCount = currentActiveUserCount + eeDisabledUsers.length;
|
||||||
|
} else {
|
||||||
|
currentUserCount = currentActiveUserCount;
|
||||||
|
}
|
||||||
|
|
||||||
if (currentUserCount > permittedSeats) {
|
if (currentUserCount > permittedSeats) {
|
||||||
shouldNotify = true;
|
shouldNotify = true;
|
||||||
|
|||||||
@ -4,7 +4,10 @@ const { _, get } = require('lodash');
|
|||||||
const { pick } = require('lodash/fp');
|
const { pick } = require('lodash/fp');
|
||||||
const { ApplicationError } = require('@strapi/utils').errors;
|
const { ApplicationError } = require('@strapi/utils').errors;
|
||||||
const { validateUserCreationInput } = require('../validation/user');
|
const { validateUserCreationInput } = require('../validation/user');
|
||||||
const { validateUserUpdateInput } = require('../../../server/validation/user');
|
const {
|
||||||
|
validateUserUpdateInput,
|
||||||
|
validateUsersDeleteInput,
|
||||||
|
} = require('../../../server/validation/user');
|
||||||
const { getService } = require('../../../server/utils');
|
const { getService } = require('../../../server/utils');
|
||||||
|
|
||||||
const pickUserCreationAttributes = pick(['firstname', 'lastname', 'email', 'roles']);
|
const pickUserCreationAttributes = pick(['firstname', 'lastname', 'email', 'roles']);
|
||||||
@ -68,4 +71,39 @@ module.exports = {
|
|||||||
data: getService('user').sanitizeUser(updatedUser),
|
data: getService('user').sanitizeUser(updatedUser),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async deleteOne(ctx) {
|
||||||
|
const { id } = ctx.params;
|
||||||
|
|
||||||
|
const deletedUser = await getService('user').deleteById(id);
|
||||||
|
|
||||||
|
if (!deletedUser) {
|
||||||
|
return ctx.notFound('User not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
await getService('user').shouldRemoveFromEEDisabledUsersList(id);
|
||||||
|
|
||||||
|
return ctx.deleted({
|
||||||
|
data: getService('user').sanitizeUser(deletedUser),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete several users
|
||||||
|
* @param {KoaContext} ctx - koa context
|
||||||
|
*/
|
||||||
|
async deleteMany(ctx) {
|
||||||
|
const { body } = ctx.request;
|
||||||
|
await validateUsersDeleteInput(body);
|
||||||
|
|
||||||
|
const users = await getService('user').deleteByIds(body.ids);
|
||||||
|
|
||||||
|
await getService('user').shouldRemoveFromEEDisabledUsersList(body.ids);
|
||||||
|
|
||||||
|
const sanitizedUsers = users.map(getService('user').sanitizeUser);
|
||||||
|
|
||||||
|
return ctx.deleted({
|
||||||
|
data: sanitizedUsers,
|
||||||
|
});
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -7,7 +7,6 @@ const _ = require('lodash');
|
|||||||
* @param {object} input
|
* @param {object} input
|
||||||
*/
|
*/
|
||||||
const shouldUpdateEEDisabledUsersList = async (id, input) => {
|
const shouldUpdateEEDisabledUsersList = async (id, input) => {
|
||||||
console.log('update service');
|
|
||||||
const data = await strapi.db.query('strapi::ee-store').findOne({
|
const data = await strapi.db.query('strapi::ee-store').findOne({
|
||||||
where: { key: 'ee_disabled_users' },
|
where: { key: 'ee_disabled_users' },
|
||||||
});
|
});
|
||||||
@ -19,7 +18,6 @@ const shouldUpdateEEDisabledUsersList = async (id, input) => {
|
|||||||
|
|
||||||
if (user.isActive !== input.isActive) {
|
if (user.isActive !== input.isActive) {
|
||||||
const newDisabledUsersList = _.filter(disabledUsers, (user) => user.id !== Number(id));
|
const newDisabledUsersList = _.filter(disabledUsers, (user) => user.id !== Number(id));
|
||||||
console.log(newDisabledUsersList);
|
|
||||||
await strapi.db.query('strapi::ee-store').update({
|
await strapi.db.query('strapi::ee-store').update({
|
||||||
where: { id: data.id },
|
where: { id: data.id },
|
||||||
data: { value: JSON.stringify(newDisabledUsersList) },
|
data: { value: JSON.stringify(newDisabledUsersList) },
|
||||||
@ -27,6 +25,29 @@ const shouldUpdateEEDisabledUsersList = async (id, input) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const shouldRemoveFromEEDisabledUsersList = async (ids) => {
|
||||||
|
let idsToCheck;
|
||||||
|
if (typeof ids === 'object') {
|
||||||
|
idsToCheck = [...ids];
|
||||||
|
} else {
|
||||||
|
idsToCheck = [Number(ids)];
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await strapi.db.query('strapi::ee-store').findOne({
|
||||||
|
where: { key: 'ee_disabled_users' },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!data || !data.value || data.value.length === 0) return;
|
||||||
|
const disabledUsers = JSON.parse(data.value);
|
||||||
|
|
||||||
|
const newDisabledUsersList = _.filter(disabledUsers, (user) => !idsToCheck.includes(user.id));
|
||||||
|
await strapi.db.query('strapi::ee-store').update({
|
||||||
|
where: { id: data.id },
|
||||||
|
data: { value: JSON.stringify(newDisabledUsersList) },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
shouldUpdateEEDisabledUsersList,
|
shouldUpdateEEDisabledUsersList,
|
||||||
|
shouldRemoveFromEEDisabledUsersList,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -6,12 +6,12 @@ const { ApplicationError } = require('@strapi/utils/lib/errors');
|
|||||||
const { PolicyError } = utils.errors;
|
const { PolicyError } = utils.errors;
|
||||||
|
|
||||||
module.exports = async (policyCtx, config = {}) => {
|
module.exports = async (policyCtx, config = {}) => {
|
||||||
if (!strapi.EE) return true;
|
if (!strapi.isEE) return true;
|
||||||
|
if (userCount < permittedSeats) return true;
|
||||||
|
|
||||||
const userCount = await strapi.db.query('admin::user').count({
|
const userCount = await strapi.db.query('admin::user').count({
|
||||||
where: { isActive: true },
|
where: { isActive: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
const permittedSeats = 5;
|
const permittedSeats = 5;
|
||||||
|
|
||||||
if (userCount >= permittedSeats && config.isCreating) {
|
if (userCount >= permittedSeats && config.isCreating) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user