Просмотр исходного кода

select group inheritance from modal on page create

Futa Arai 2 лет назад
Родитель
Сommit
1cdd3d52d6

+ 20 - 2
apps/app/src/client/services/create-page/use-create-page-and-transit.tsx

@@ -2,8 +2,9 @@ import { useCallback, useState } from 'react';
 
 
 import { useRouter } from 'next/router';
 import { useRouter } from 'next/router';
 
 
-import { exist } from '~/client/services/page-operation';
+import { exist, nonUserRelatedGroupsGranted } from '~/client/services/page-operation';
 import type { IApiv3PageCreateParams } from '~/interfaces/apiv3';
 import type { IApiv3PageCreateParams } from '~/interfaces/apiv3';
+import { useGrantedGroupsInheritanceSelectModal } from '~/stores/modal';
 import { useCurrentPagePath } from '~/stores/page';
 import { useCurrentPagePath } from '~/stores/page';
 import { EditorMode, useEditorMode } from '~/stores/ui';
 import { EditorMode, useEditorMode } from '~/stores/ui';
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
@@ -25,7 +26,7 @@ type OnAborted = () => void;
  */
  */
 type OnTerminated = () => void;
 type OnTerminated = () => void;
 
 
-type CreatePageAndTransitOpts = {
+export type CreatePageAndTransitOpts = {
   shouldCheckPageExists?: boolean,
   shouldCheckPageExists?: boolean,
   onCreationStart?: OnCreated,
   onCreationStart?: OnCreated,
   onCreated?: OnCreated,
   onCreated?: OnCreated,
@@ -49,6 +50,7 @@ export const useCreatePageAndTransit: UseCreatePageAndTransit = () => {
 
 
   const { data: currentPagePath } = useCurrentPagePath();
   const { data: currentPagePath } = useCurrentPagePath();
   const { mutate: mutateEditorMode } = useEditorMode();
   const { mutate: mutateEditorMode } = useEditorMode();
+  const { open: openGrantedGroupsInheritanceSelectModal } = useGrantedGroupsInheritanceSelectModal();
 
 
   const [isCreating, setCreating] = useState(false);
   const [isCreating, setCreating] = useState(false);
 
 
@@ -58,6 +60,22 @@ export const useCreatePageAndTransit: UseCreatePageAndTransit = () => {
       onCreationStart, onCreated, onAborted, onTerminated,
       onCreationStart, onCreated, onAborted, onTerminated,
     } = opts;
     } = opts;
 
 
+    if (params?.parentPath != null && params?.onlyInheritUserRelatedGrantedGroups == null) {
+      try {
+        const { isNonUserRelatedGroupsGranted } = await nonUserRelatedGroupsGranted(params.parentPath);
+        if (isNonUserRelatedGroupsGranted) {
+          openGrantedGroupsInheritanceSelectModal(params, opts);
+          return;
+        }
+      }
+      catch (err) {
+        throw err;
+      }
+      finally {
+        onTerminated?.();
+      }
+    }
+
     // check the page existence
     // check the page existence
     if (shouldCheckPageExists && params.path != null) {
     if (shouldCheckPageExists && params.path != null) {
       const pagePath = params.path;
       const pagePath = params.path;

+ 9 - 0
apps/app/src/client/services/page-operation.ts

@@ -148,6 +148,15 @@ export const exist = async(path: string): Promise<PageExistResponse> => {
   return res.data;
   return res.data;
 };
 };
 
 
+interface NonUserRelatedGroupsGrantedResponse {
+  isNonUserRelatedGroupsGranted: boolean,
+}
+
+export const nonUserRelatedGroupsGranted = async(path: string): Promise<NonUserRelatedGroupsGrantedResponse> => {
+  const res = await apiv3Get<NonUserRelatedGroupsGrantedResponse>('/page/non-user-related-groups-granted', { path });
+  return res.data;
+};
+
 export const publish = async(pageId: string): Promise<IPageHasId> => {
 export const publish = async(pageId: string): Promise<IPageHasId> => {
   const res = await apiv3Put(`/page/${pageId}/publish`);
   const res = await apiv3Put(`/page/${pageId}/publish`);
   return res.data;
   return res.data;

+ 70 - 0
apps/app/src/components/GrantedGroupInheritanceSelectModal.tsx

@@ -0,0 +1,70 @@
+import { useState } from 'react';
+
+import { useTranslation } from 'react-i18next';
+import { Modal, ModalHeader, ModalBody } from 'reactstrap';
+
+import { useCreatePageAndTransit } from '~/client/services/create-page';
+import { useGrantedGroupsInheritanceSelectModal } from '~/stores/modal';
+
+export const GrantedGroupsInheritanceSelectModal = (): JSX.Element => {
+  const { t } = useTranslation();
+  const { data: modalData, close: closeModal } = useGrantedGroupsInheritanceSelectModal();
+  const [onlyInheritUserRelatedGrantedGroups, setOnlyInheritUserRelatedGrantedGroups] = useState(false);
+  const { createAndTransit } = useCreatePageAndTransit();
+
+  const params = modalData?.params;
+  const opts = modalData?.opts;
+  const isOpened = modalData?.isOpened ?? false;
+
+  const onCreateBtnClick = () => {
+    if (params != null) {
+      params.onlyInheritUserRelatedGrantedGroups = onlyInheritUserRelatedGrantedGroups;
+      createAndTransit(params, opts);
+    }
+    closeModal();
+  };
+
+  return (
+    <Modal
+      isOpen={isOpened}
+      toggle={() => closeModal()}
+    >
+      <ModalHeader tag="h4" toggle={() => closeModal()} className="bg-primary text-light">
+        閲覧権限のあるグループを選択
+      </ModalHeader>
+      <ModalBody>
+        <div className="p-3">
+          <div className="form-check radio-primary mb-3">
+            <input
+              type="radio"
+              id="inheritAllGroupsRadio"
+              className="form-check-input"
+              form="formImageType"
+              checked={!onlyInheritUserRelatedGrantedGroups}
+              onChange={() => { setOnlyInheritUserRelatedGrantedGroups(false) }}
+            />
+            <label className="form-check-label" htmlFor="inheritAllGroupsRadio">
+              閲覧権限のあるグループを親ページから全て引き継ぐ
+            </label>
+          </div>
+          <div className="form-check radio-primary mb-4">
+            <input
+              type="radio"
+              id="onlyInheritRelatedGroupsRadio"
+              className="form-check-input"
+              form="formImageType"
+              checked={onlyInheritUserRelatedGrantedGroups}
+              onChange={() => { setOnlyInheritUserRelatedGrantedGroups(true) }}
+            />
+            <label className="form-check-label" htmlFor="onlyInheritRelatedGroupsRadio">
+              自分が所属するグループのみを親ページから引き継ぐ
+            </label>
+          </div>
+          <div className="text-center">
+            <button className="btn btn-primary" type="button" onClick={onCreateBtnClick}>{t('Create')}</button>
+          </div>
+        </div>
+      </ModalBody>
+    </Modal>
+  );
+};

+ 2 - 2
apps/app/src/components/PageAlert/FixPageGrantAlert.tsx

@@ -9,7 +9,7 @@ import {
 import { apiv3Put } from '~/client/util/apiv3-client';
 import { apiv3Put } from '~/client/util/apiv3-client';
 import { toastError, toastSuccess } from '~/client/util/toastr';
 import { toastError, toastSuccess } from '~/client/util/toastr';
 import { UserGroupPageGrantStatus, type IPageGrantData } from '~/interfaces/page';
 import { UserGroupPageGrantStatus, type IPageGrantData } from '~/interfaces/page';
-import type { PopulatedGrantedGroup, IRecordApplicableGrant, IResIsGrantNormalizedGrantData } from '~/interfaces/page-grant';
+import type { PopulatedGrantedGroup, IRecordApplicableGrant, IResGrantData } from '~/interfaces/page-grant';
 import { useCurrentUser } from '~/stores/context';
 import { useCurrentUser } from '~/stores/context';
 import { useSWRxApplicableGrant, useSWRxCurrentGrantData, useSWRxCurrentPage } from '~/stores/page';
 import { useSWRxApplicableGrant, useSWRxCurrentGrantData, useSWRxCurrentPage } from '~/stores/page';
 
 
@@ -17,7 +17,7 @@ type ModalProps = {
   isOpen: boolean
   isOpen: boolean
   pageId: string
   pageId: string
   dataApplicableGrant: IRecordApplicableGrant
   dataApplicableGrant: IRecordApplicableGrant
-  currentAndParentPageGrantData: IResIsGrantNormalizedGrantData
+  currentAndParentPageGrantData: IResGrantData
   close(): void
   close(): void
 }
 }
 
 

+ 7 - 7
apps/app/src/interfaces/page-grant.ts

@@ -1,9 +1,9 @@
-import { PageGrant, GroupType } from '@growi/core';
+import type { PageGrant, GroupType } from '@growi/core';
 
 
-import { ExternalUserGroupDocument } from '~/features/external-user-group/server/models/external-user-group';
-import { UserGroupDocument } from '~/server/models/user-group';
+import type { ExternalUserGroupDocument } from '~/features/external-user-group/server/models/external-user-group';
+import type { UserGroupDocument } from '~/server/models/user-group';
 
 
-import { IPageGrantData } from './page';
+import type { IPageGrantData } from './page';
 
 
 
 
 type UserGroupType = typeof GroupType.userGroup;
 type UserGroupType = typeof GroupType.userGroup;
@@ -18,12 +18,12 @@ export type IRecordApplicableGrant = Partial<Record<PageGrant, IDataApplicableGr
 export type IResApplicableGrant = {
 export type IResApplicableGrant = {
   data?: IRecordApplicableGrant
   data?: IRecordApplicableGrant
 }
 }
-export type IResIsGrantNormalizedGrantData = {
+export type IResGrantData = {
   isForbidden: boolean,
   isForbidden: boolean,
   currentPageGrant: IPageGrantData,
   currentPageGrant: IPageGrantData,
   parentPageGrant?: IPageGrantData
   parentPageGrant?: IPageGrantData
 }
 }
-export type IResIsGrantNormalized = {
+export type IResCurrentGrantData = {
   isGrantNormalized: boolean,
   isGrantNormalized: boolean,
-  grantData: IResIsGrantNormalizedGrantData
+  grantData: IResGrantData
 };
 };

+ 2 - 1
apps/app/src/pages/[[...path]].page.tsx

@@ -21,6 +21,7 @@ import { useRouter } from 'next/router';
 import superjson from 'superjson';
 import superjson from 'superjson';
 
 
 import { useEditorModeClassName } from '~/client/services/layout';
 import { useEditorModeClassName } from '~/client/services/layout';
+import { GrantedGroupsInheritanceSelectModal } from '~/components/GrantedGroupInheritanceSelectModal';
 import { PageView } from '~/components/Page/PageView';
 import { PageView } from '~/components/Page/PageView';
 import { DrawioViewerScript } from '~/components/Script/DrawioViewerScript';
 import { DrawioViewerScript } from '~/components/Script/DrawioViewerScript';
 import { SupportedAction, type SupportedActionType } from '~/interfaces/activity';
 import { SupportedAction, type SupportedActionType } from '~/interfaces/activity';
@@ -47,7 +48,6 @@ import {
 } from '~/stores/page';
 } from '~/stores/page';
 import { useRedirectFrom } from '~/stores/page-redirect';
 import { useRedirectFrom } from '~/stores/page-redirect';
 import { useRemoteRevisionId } from '~/stores/remote-latest-page';
 import { useRemoteRevisionId } from '~/stores/remote-latest-page';
-import { useSelectedGrant } from '~/stores/ui';
 import { useSetupGlobalSocket, useSetupGlobalSocketForPage } from '~/stores/websocket';
 import { useSetupGlobalSocket, useSetupGlobalSocketForPage } from '~/stores/websocket';
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 
 
@@ -371,6 +371,7 @@ Page.getLayout = function getLayout(page: React.ReactElement<Props>) {
       <LinkEditModal />
       <LinkEditModal />
       <TagEditModal />
       <TagEditModal />
       <ConflictDiffModal />
       <ConflictDiffModal />
+      <GrantedGroupsInheritanceSelectModal />
     </>
     </>
   );
   );
 };
 };

+ 31 - 0
apps/app/src/stores/modal.tsx

@@ -8,6 +8,8 @@ import type { SWRResponse } from 'swr';
 
 
 
 
 import MarkdownTable from '~/client/models/MarkdownTable';
 import MarkdownTable from '~/client/models/MarkdownTable';
+import type { CreatePageAndTransitOpts } from '~/client/services/create-page';
+import type { IApiv3PageCreateParams } from '~/interfaces/apiv3';
 import type { BookmarkFolderItems } from '~/interfaces/bookmark-info';
 import type { BookmarkFolderItems } from '~/interfaces/bookmark-info';
 import type {
 import type {
   OnDuplicatedFunction, OnRenamedFunction, OnDeletedFunction, OnPutBackedFunction, onDeletedBookmarkFolderFunction, OnSelectedFunction,
   OnDuplicatedFunction, OnRenamedFunction, OnDeletedFunction, OnPutBackedFunction, onDeletedBookmarkFolderFunction, OnSelectedFunction,
@@ -42,6 +44,35 @@ export const usePageCreateModal = (status?: CreateModalStatus): SWRResponse<Crea
   };
   };
 };
 };
 
 
+/*
+* GrantedGroupsInheritanceSelectModal
+*/
+type GrantedGroupsInheritanceSelectModalStatus = {
+  isOpened: boolean,
+  params?: IApiv3PageCreateParams,
+  opts?: CreatePageAndTransitOpts,
+}
+
+type GrantedGroupsInheritanceSelectModalStatusUtils = {
+  open(params?: IApiv3PageCreateParams, opts?: CreatePageAndTransitOpts): Promise<GrantedGroupsInheritanceSelectModalStatus | undefined>
+  close(): Promise<GrantedGroupsInheritanceSelectModalStatus | undefined>
+}
+
+export const useGrantedGroupsInheritanceSelectModal = (
+    status?: GrantedGroupsInheritanceSelectModalStatus,
+): SWRResponse<GrantedGroupsInheritanceSelectModalStatus, Error> & GrantedGroupsInheritanceSelectModalStatusUtils => {
+  const initialData: GrantedGroupsInheritanceSelectModalStatus = { isOpened: false };
+  const swrResponse = useSWRStatic<GrantedGroupsInheritanceSelectModalStatus, Error>(
+    'grantedGroupsInheritanceSelectModalStatus', status, { fallbackData: initialData },
+  );
+
+  return {
+    ...swrResponse,
+    open: (params?: IApiv3PageCreateParams, opts?: CreatePageAndTransitOpts) => swrResponse.mutate({ isOpened: true, params, opts }),
+    close: () => swrResponse.mutate({ isOpened: false }),
+  };
+};
+
 /*
 /*
 * PageDeleteModal
 * PageDeleteModal
 */
 */

+ 2 - 2
apps/app/src/stores/page.tsx

@@ -18,7 +18,7 @@ import useSWRMutation, { type SWRMutationResponse } from 'swr/mutation';
 
 
 import { apiGet } from '~/client/util/apiv1-client';
 import { apiGet } from '~/client/util/apiv1-client';
 import { apiv3Get } from '~/client/util/apiv3-client';
 import { apiv3Get } from '~/client/util/apiv3-client';
-import type { IRecordApplicableGrant, IResIsGrantNormalized } from '~/interfaces/page-grant';
+import type { IRecordApplicableGrant, IResCurrentGrantData } from '~/interfaces/page-grant';
 import type { AxiosResponse } from '~/utils/axios';
 import type { AxiosResponse } from '~/utils/axios';
 
 
 import type { IPageTagsInfo } from '../interfaces/tag';
 import type { IPageTagsInfo } from '../interfaces/tag';
@@ -269,7 +269,7 @@ export const useSWRxInfinitePageRevisions = (
  */
  */
 export const useSWRxCurrentGrantData = (
 export const useSWRxCurrentGrantData = (
     pageId: string | null | undefined,
     pageId: string | null | undefined,
-): SWRResponse<IResIsGrantNormalized, Error> => {
+): SWRResponse<IResCurrentGrantData, Error> => {
 
 
   const { data: isGuestUser } = useIsGuestUser();
   const { data: isGuestUser } = useIsGuestUser();
   const { data: isReadOnlyUser } = useIsReadOnlyUser();
   const { data: isReadOnlyUser } = useIsReadOnlyUser();