Taichi Masuyama 4 лет назад
Родитель
Сommit
d65f8818fb

+ 7 - 6
packages/app/src/components/PageDeleteModal.tsx

@@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next';
 
 
 import { apiPost } from '~/client/util/apiv1-client';
 import { apiPost } from '~/client/util/apiv1-client';
 import { apiv3Post } from '~/client/util/apiv3-client';
 import { apiv3Post } from '~/client/util/apiv3-client';
-import { usePageDeleteModalStatus, usePageDeleteModalOpened } from '~/stores/ui';
+import { usePageDeleteModal, usePageDeleteModalOpened } from '~/stores/ui';
 
 
 import { IPageApiv1Result, IDeleteManyPageApiv3Result } from '~/interfaces/page';
 import { IPageApiv1Result, IDeleteManyPageApiv3Result } from '~/interfaces/page';
 
 
@@ -38,7 +38,7 @@ const PageDeleteModal: FC<Props> = (props: Props) => {
     isDeleteCompletelyModal, isAbleToDeleteCompletely,
     isDeleteCompletelyModal, isAbleToDeleteCompletely,
   } = props;
   } = props;
 
 
-  const { data: deleteModalStatus, close: closeDeleteModal } = usePageDeleteModalStatus();
+  const { data: deleteModalStatus, close: closeDeleteModal } = usePageDeleteModal();
   const { data: pageDeleteModalOpened } = usePageDeleteModalOpened();
   const { data: pageDeleteModalOpened } = usePageDeleteModalOpened();
 
 
   const isOpened = pageDeleteModalOpened?.isOpend != null ? pageDeleteModalOpened.isOpend : false;
   const isOpened = pageDeleteModalOpened?.isOpend != null ? pageDeleteModalOpened.isOpend : false;
@@ -84,7 +84,7 @@ const PageDeleteModal: FC<Props> = (props: Props) => {
         });
         });
 
 
         if (pageDeleteModalOpened != null && pageDeleteModalOpened.onDeleted != null) {
         if (pageDeleteModalOpened != null && pageDeleteModalOpened.onDeleted != null) {
-          pageDeleteModalOpened.onDeleted(data.paths, data.isRecursively);
+          pageDeleteModalOpened.onDeleted(data.paths, data.isRecursively, data.isCompletely);
         }
         }
       }
       }
       catch (err) {
       catch (err) {
@@ -101,7 +101,7 @@ const PageDeleteModal: FC<Props> = (props: Props) => {
 
 
         const page = deleteModalStatus.pages[0];
         const page = deleteModalStatus.pages[0];
 
 
-        const { path, isRecursively } = await apiPost('/pages.remove', {
+        const { path, isRecursively, isCompletely } = await apiPost('/pages.remove', {
           page_id: page.pageId,
           page_id: page.pageId,
           revision_id: page.revisionId,
           revision_id: page.revisionId,
           recursively,
           recursively,
@@ -109,7 +109,7 @@ const PageDeleteModal: FC<Props> = (props: Props) => {
         }) as IPageApiv1Result;
         }) as IPageApiv1Result;
 
 
         if (pageDeleteModalOpened != null && pageDeleteModalOpened.onDeleted != null) {
         if (pageDeleteModalOpened != null && pageDeleteModalOpened.onDeleted != null) {
-          pageDeleteModalOpened.onDeleted(path, isRecursively);
+          pageDeleteModalOpened.onDeleted(path, isRecursively, isCompletely);
         }
         }
       }
       }
       catch (err) {
       catch (err) {
@@ -119,7 +119,8 @@ const PageDeleteModal: FC<Props> = (props: Props) => {
   }
   }
 
 
   async function deleteButtonHandler() {
   async function deleteButtonHandler() {
-    deletePage();
+    await closeDeleteModal();
+    await deletePage();
   }
   }
 
 
   function renderDeleteRecursivelyForm() {
   function renderDeleteRecursivelyForm() {

+ 1 - 1
packages/app/src/components/SearchPage.jsx

@@ -352,7 +352,7 @@ class SearchPage extends React.Component {
           activePage={this.state.activePage}
           activePage={this.state.activePage}
         >
         >
         </SearchPageLayout>
         </SearchPageLayout>
-        {/* TODO: show PageDeleteModal with usePageDeleteModalStatus by 87569  */}
+        {/* TODO: show PageDeleteModal with usePageDeleteModal by 87569  */}
         <PageDeleteModal
         <PageDeleteModal
           isOpen={this.state.isDeleteConfirmModalShown}
           isOpen={this.state.isDeleteConfirmModalShown}
           onClose={this.closeDeleteConfirmModalHandler}
           onClose={this.closeDeleteConfirmModalHandler}

+ 18 - 5
packages/app/src/components/Sidebar/PageTree/ItemsTree.tsx

@@ -8,7 +8,7 @@ import { useSWRxPageAncestorsChildren, useSWRxRootPage } from '../../../stores/p
 import { TargetAndAncestors } from '~/interfaces/page-listing-results';
 import { TargetAndAncestors } from '~/interfaces/page-listing-results';
 import { toastError, toastSuccess } from '~/client/util/apiNotification';
 import { toastError, toastSuccess } from '~/client/util/apiNotification';
 import {
 import {
-  IPageForPageDeleteModal, usePageDuplicateModalStatus, usePageRenameModalStatus, usePageDeleteModalStatus,
+  IPageForPageDeleteModal, usePageDuplicateModalStatus, usePageRenameModalStatus, usePageDeleteModal,
   OnDeletedFunction,
   OnDeletedFunction,
 } from '~/stores/ui';
 } from '~/stores/ui';
 import { smoothScrollIntoView } from '~/client/util/smooth-scroll';
 import { smoothScrollIntoView } from '~/client/util/smooth-scroll';
@@ -99,7 +99,7 @@ const ItemsTree: FC<ItemsTreeProps> = (props: ItemsTreeProps) => {
   const { data: rootPageData, error: error2 } = useSWRxRootPage();
   const { data: rootPageData, error: error2 } = useSWRxRootPage();
   const { open: openDuplicateModal } = usePageDuplicateModalStatus();
   const { open: openDuplicateModal } = usePageDuplicateModalStatus();
   const { open: openRenameModal } = usePageRenameModalStatus();
   const { open: openRenameModal } = usePageRenameModalStatus();
-  const { open: openDeleteModal } = usePageDeleteModalStatus();
+  const { open: openDeleteModal } = usePageDeleteModal();
 
 
   useEffect(() => {
   useEffect(() => {
     const startFrom = document.getElementById('grw-sidebar-contents-scroll-target');
     const startFrom = document.getElementById('grw-sidebar-contents-scroll-target');
@@ -118,13 +118,26 @@ const ItemsTree: FC<ItemsTreeProps> = (props: ItemsTreeProps) => {
     openRenameModal(pageId, revisionId, path);
     openRenameModal(pageId, revisionId, path);
   };
   };
 
 
-  const onDeletedHandler: OnDeletedFunction = (pathOrPathsToDelete, isRecursively) => {
+  const onDeletedHandler: OnDeletedFunction = (pathOrPathsToDelete, isRecursively, isCompletely) => {
     if (typeof pathOrPathsToDelete === 'string') {
     if (typeof pathOrPathsToDelete === 'string') {
+      const path = pathOrPathsToDelete;
+      console.log(pathOrPathsToDelete, isRecursively, isCompletely);
       if (isRecursively) {
       if (isRecursively) {
-        toastSuccess(t('deleted_single_page_recursively', { path: pathOrPathsToDelete }));
+        if (isCompletely) {
+          toastSuccess(t('deleted_single_page_recursively_completely', { path }));
+        }
+        else {
+          toastSuccess(t('deleted_single_page_recursively', { path }));
+        }
       }
       }
       else {
       else {
-        toastSuccess(t('deleted_single_page', { path: pathOrPathsToDelete }));
+        // eslint-disable-next-line no-lonely-if
+        if (isCompletely) {
+          toastSuccess(t('deleted_single_page_completely', { path }));
+        }
+        else {
+          toastSuccess(t('deleted_single_page', { path }));
+        }
       }
       }
     }
     }
   };
   };

+ 2 - 0
packages/app/src/interfaces/common.ts

@@ -7,3 +7,5 @@ import { HasObjectId } from './has-object-id';
 
 
 // Foreign key field
 // Foreign key field
 export type Ref<T> = string | T & HasObjectId;
 export type Ref<T> = string | T & HasObjectId;
+
+export type Nullable<T> = T | null | undefined;

+ 5 - 3
packages/app/src/interfaces/page.ts

@@ -1,4 +1,4 @@
-import { Ref } from './common';
+import { Ref, Nullable } from './common';
 import { IUser } from './user';
 import { IUser } from './user';
 import { IRevision, HasRevisionShortbody } from './revision';
 import { IRevision, HasRevisionShortbody } from './revision';
 import { ITag } from './tag';
 import { ITag } from './tag';
@@ -101,10 +101,12 @@ export type IPageWithMeta<M = IPageInfoAll> = {
 export type IPageApiv1Result = {
 export type IPageApiv1Result = {
   ok: boolean
   ok: boolean
   path: string,
   path: string,
-  isRecursively: true | null | undefined,
+  isRecursively: Nullable<true>,
+  isCompletely: Nullable<true>,
 };
 };
 
 
 export type IDeleteManyPageApiv3Result = {
 export type IDeleteManyPageApiv3Result = {
   paths: string[],
   paths: string[],
-  isRecursively: true | null | undefined,
+  isRecursively: Nullable<true>,
+  isCompletely: Nullable<true>,
 };
 };

+ 2 - 1
packages/app/src/server/routes/apiv3/pages.js

@@ -183,6 +183,7 @@ module.exports = (crowi) => {
     ],
     ],
     deletePages: [
     deletePages: [
       body('pageIdToRevisionIdMap')
       body('pageIdToRevisionIdMap')
+        .exists()
         .withMessage('The body property "pageIdToRevisionIdMap" must be an json map with pageId as key and revisionId as value.'),
         .withMessage('The body property "pageIdToRevisionIdMap" must be an json map with pageId as key and revisionId as value.'),
       body('isCompletely')
       body('isCompletely')
         .custom(v => v === 'true' || v === true || v == null)
         .custom(v => v === 'true' || v === true || v == null)
@@ -761,7 +762,7 @@ module.exports = (crowi) => {
     // run delete
     // run delete
     crowi.pageService.deleteMultiplePages(pagesCanBeDeleted, req.user, isCompletely, isRecursively);
     crowi.pageService.deleteMultiplePages(pagesCanBeDeleted, req.user, isCompletely, isRecursively);
 
 
-    return res.apiv3({ paths: pagesCanBeDeleted.map(p => p.path), isRecursively });
+    return res.apiv3({ paths: pagesCanBeDeleted.map(p => p.path), isRecursively, isCompletely });
   });
   });
 
 
   router.post('/v5-schema-migration', accessTokenParser, loginRequired, adminRequired, csrf, async(req, res) => {
   router.post('/v5-schema-migration', accessTokenParser, loginRequired, adminRequired, csrf, async(req, res) => {

+ 3 - 5
packages/app/src/server/routes/page.js

@@ -1180,10 +1180,7 @@ module.exports = function(crowi, app) {
     const pageId = req.body.page_id;
     const pageId = req.body.page_id;
     const previousRevision = req.body.revision_id || null;
     const previousRevision = req.body.revision_id || null;
 
 
-    // get completely flag
-    const isCompletely = req.body.completely;
-    // get recursively flag
-    const isRecursively = req.body.recursively;
+    const { recursively: isRecursively, completely: isCompletely } = req.body;
 
 
     const options = {};
     const options = {};
 
 
@@ -1223,8 +1220,9 @@ module.exports = function(crowi, app) {
 
 
     debug('Page deleted', page.path);
     debug('Page deleted', page.path);
     const result = {};
     const result = {};
-    result.path = page.path; // TODO consider to use serializePageSecurely method -- 2018.08.06 Yuki Takei
+    result.path = page.path;
     result.isRecursively = isRecursively;
     result.isRecursively = isRecursively;
+    result.isCompletely = isCompletely;
 
 
     res.json(ApiResponse.success(result));
     res.json(ApiResponse.success(result));
 
 

+ 8 - 7
packages/app/src/stores/ui.tsx

@@ -4,6 +4,7 @@ import useSWR, {
 import useSWRImmutable from 'swr/immutable';
 import useSWRImmutable from 'swr/immutable';
 
 
 import { Breakpoint, addBreakpointListener } from '@growi/ui';
 import { Breakpoint, addBreakpointListener } from '@growi/ui';
+import { pagePathUtils } from '@growi/core';
 
 
 import { RefObject } from 'react';
 import { RefObject } from 'react';
 import { SidebarContentsType } from '~/interfaces/ui';
 import { SidebarContentsType } from '~/interfaces/ui';
@@ -15,14 +16,14 @@ import {
   useIsNotCreatable, useIsSharedUser, useNotFoundTargetPathOrId, useIsForbidden, useIsIdenticalPath,
   useIsNotCreatable, useIsSharedUser, useNotFoundTargetPathOrId, useIsForbidden, useIsIdenticalPath,
 } from './context';
 } from './context';
 import { IFocusable } from '~/client/interfaces/focusable';
 import { IFocusable } from '~/client/interfaces/focusable';
-import { isSharedPage } from '^/../core/src/utils/page-path-utils';
+import { Nullable } from '~/interfaces/common';
+
+const { isSharedPage } = pagePathUtils;
 
 
 const logger = loggerFactory('growi:stores:ui');
 const logger = loggerFactory('growi:stores:ui');
 
 
 const isServer = typeof window === 'undefined';
 const isServer = typeof window === 'undefined';
 
 
-type Nullable<T> = T | null;
-
 
 
 /** **********************************************************
 /** **********************************************************
  *                          Unions
  *                          Unions
@@ -299,11 +300,11 @@ export const useCreateModalPath = (): SWRResponse<string | null | undefined, Err
 // PageDeleteModal
 // PageDeleteModal
 export type IPageForPageDeleteModal = {
 export type IPageForPageDeleteModal = {
   pageId: string,
   pageId: string,
-  revisionId: string,
+  revisionId?: string,
   path: string
   path: string
 }
 }
 
 
-export type OnDeletedFunction = (pathOrPaths: string | string[], isRecursively: true | null | undefined) => void;
+export type OnDeletedFunction = (pathOrPaths: string | string[], isRecursively: Nullable<true>, isCompletely: Nullable<true>) => void;
 
 
 type DeleteModalStatus = {
 type DeleteModalStatus = {
   isOpened: boolean,
   isOpened: boolean,
@@ -324,7 +325,7 @@ type DeleteModalStatusUtils = {
   close(): Promise<DeleteModalStatus | undefined>,
   close(): Promise<DeleteModalStatus | undefined>,
 }
 }
 
 
-export const usePageDeleteModalStatus = (status?: DeleteModalStatus): SWRResponse<DeleteModalStatus, Error> & DeleteModalStatusUtils => {
+export const usePageDeleteModal = (status?: DeleteModalStatus): SWRResponse<DeleteModalStatus, Error> & DeleteModalStatusUtils => {
   const initialData: DeleteModalStatus = { isOpened: false };
   const initialData: DeleteModalStatus = { isOpened: false };
   const swrResponse = useStaticSWR<DeleteModalStatus, Error>('deleteModalStatus', status, { fallbackData: initialData });
   const swrResponse = useStaticSWR<DeleteModalStatus, Error>('deleteModalStatus', status, { fallbackData: initialData });
 
 
@@ -336,7 +337,7 @@ export const usePageDeleteModalStatus = (status?: DeleteModalStatus): SWRRespons
 };
 };
 
 
 export const usePageDeleteModalOpened = (): SWRResponse<(DeleteModalOpened | null), Error> => {
 export const usePageDeleteModalOpened = (): SWRResponse<(DeleteModalOpened | null), Error> => {
-  const { data } = usePageDeleteModalStatus();
+  const { data } = usePageDeleteModal();
   return useSWRImmutable(
   return useSWRImmutable(
     data != null ? ['isDeleteModalOpened', data] : null,
     data != null ? ['isDeleteModalOpened', data] : null,
     () => {
     () => {