import React, { useEffect, useState, useCallback } from 'react'; import { PageGrant, GroupType } from '@growi/core'; import { useTranslation } from 'react-i18next'; import { Modal, ModalHeader, ModalBody, ModalFooter, } from 'reactstrap'; import { apiv3Put } from '~/client/util/apiv3-client'; import { toastError, toastSuccess } from '~/client/util/toastr'; import type { IPageGrantData } from '~/interfaces/page'; import type { PopulatedGrantedGroup, IRecordApplicableGrant, IResIsGrantNormalizedGrantData } from '~/interfaces/page-grant'; import { useCurrentUser } from '~/stores/context'; import { useSWRxApplicableGrant, useSWRxIsGrantNormalized, useSWRxCurrentPage } from '~/stores/page'; type ModalProps = { isOpen: boolean pageId: string dataApplicableGrant: IRecordApplicableGrant currentAndParentPageGrantData: IResIsGrantNormalizedGrantData close(): void } const FixPageGrantModal = (props: ModalProps): JSX.Element => { const { t } = useTranslation(); const { isOpen, pageId, dataApplicableGrant, currentAndParentPageGrantData, close, } = props; const [selectedGrant, setSelectedGrant] = useState(PageGrant.GRANT_RESTRICTED); const [isGroupSelectModalShown, setIsGroupSelectModalShown] = useState(false); const [selectedGroups, setSelectedGroups] = useState([]); // Alert message state const [shouldShowModalAlert, setShowModalAlert] = useState(false); const applicableGroups = dataApplicableGrant[PageGrant.GRANT_USER_GROUP]?.applicableGroups; // Reset state when opened useEffect(() => { if (isOpen) { setSelectedGrant(PageGrant.GRANT_RESTRICTED); setSelectedGroups([]); setShowModalAlert(false); } }, [isOpen]); const groupListItemClickHandler = (group: PopulatedGrantedGroup) => { if (selectedGroups.find(g => g.item._id === group.item._id) != null) { setSelectedGroups(selectedGroups.filter(g => g.item._id !== group.item._id)); } else { setSelectedGroups([...selectedGroups, group]); } }; const submit = async() => { // Validate input values if (selectedGrant === PageGrant.GRANT_USER_GROUP && selectedGroups.length === 0) { setShowModalAlert(true); return; } close(); try { await apiv3Put(`/page/${pageId}/grant`, { grant: selectedGrant, userRelatedGrantedGroups: selectedGroups.length !== 0 ? selectedGroups.map((g) => { return { item: g.item._id, type: g.type }; }) : null, }); toastSuccess(t('Successfully updated')); } catch (err) { toastError(t('Failed to update')); } }; const getGrantLabel = useCallback((isForbidden: boolean, grantData?: IPageGrantData): string => { if (isForbidden) { return t('fix_page_grant.modal.grant_label.isForbidden'); } if (grantData == null) { return t('fix_page_grant.modal.grant_label.isForbidden'); } if (grantData.grant === 1) { return t('fix_page_grant.modal.grant_label.public'); } if (grantData.grant === 4) { return t('fix_page_grant.modal.radio_btn.only_me'); } if (grantData.grant === 5) { if (grantData.userRelatedGrantedGroups == null || grantData.userRelatedGrantedGroups.length === 0) { return t('fix_page_grant.modal.grant_label.isForbidden'); } return `${t('fix_page_grant.modal.radio_btn.grant_group')} (${grantData.userRelatedGrantedGroups.map(g => g.name).join(', ')})`; } throw Error('cannot get grant label'); // this error can't be throwed }, [t]); const renderGrantDataLabel = useCallback(() => { const { isForbidden, currentPageGrant, parentPageGrant } = currentAndParentPageGrantData; const currentGrantLabel = getGrantLabel(false, currentPageGrant); const parentGrantLabel = getGrantLabel(isForbidden, parentPageGrant); return ( <>

{ t('fix_page_grant.modal.grant_label.parentPageGrantLabel') + parentGrantLabel }

{ t('fix_page_grant.modal.grant_label.currentPageGrantLabel') + currentGrantLabel }

{/* eslint-disable-next-line react/no-danger */}

); }, [t, currentAndParentPageGrantData, getGrantLabel]); const renderModalBodyAndFooter = () => { const isGrantAvailable = Object.keys(dataApplicableGrant || {}).length > 0; if (!isGrantAvailable) { return (

{ t('fix_page_grant.modal.no_grant_available') }

); } return ( <>
{/* eslint-disable-next-line react/no-danger */}

{/* grant data label */} {renderGrantDataLabel()}

setSelectedGrant(PageGrant.GRANT_RESTRICTED)} />
setSelectedGrant(PageGrant.GRANT_OWNER)} />
setSelectedGrant(PageGrant.GRANT_USER_GROUP)} />
{ shouldShowModalAlert && (

{t('fix_page_grant.modal.alert_message')}

) }
); }; return ( <> { t('fix_page_grant.modal.title') } {renderModalBodyAndFooter()} {applicableGroups != null && ( setIsGroupSelectModalShown(false)} > setIsGroupSelectModalShown(false)} className="bg-purple text-light"> {t('user_group.select_group')} <> { applicableGroups.map((group) => { const groupIsGranted = selectedGroups?.find(g => g.item._id === group.item._id) != null; const activeClass = groupIsGranted ? 'active' : ''; return ( ); }) } )} ); }; export const FixPageGrantAlert = (): JSX.Element => { const { t } = useTranslation(); const { data: currentUser } = useCurrentUser(); const { data: pageData } = useSWRxCurrentPage(); const hasParent = pageData != null ? pageData.parent != null : false; const pageId = pageData?._id; const [isOpen, setOpen] = useState(false); const { data: dataIsGrantNormalized } = useSWRxIsGrantNormalized(currentUser != null ? pageId : null); const { data: dataApplicableGrant } = useSWRxApplicableGrant(currentUser != null ? pageId : null); // Dependencies if (pageData == null) { return <>; } if (!hasParent) { return <>; } if (dataIsGrantNormalized?.isGrantNormalized == null || dataIsGrantNormalized.isGrantNormalized) { return <>; } return ( <>
{t('fix_page_grant.alert.description')}
{ pageId != null && dataApplicableGrant != null && ( setOpen(false)} /> ) } ); };