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

Revert "apply deletion"

This reverts commit 9bfbfe638d161198292eac308b3da21c11b47120.
ryoji-s 2 лет назад
Родитель
Сommit
f1aa9404cf

+ 3 - 4
apps/app/src/components/SavePageControls.tsx

@@ -2,7 +2,7 @@ import React, { useCallback } from 'react';
 
 import EventEmitter from 'events';
 
-import { isMovablePage } from '@growi/core/dist/utils/page-path-utils';
+import { isTopPage, isUsersProtectedPages } from '@growi/core/dist/utils/page-path-utils';
 import { useTranslation } from 'next-i18next';
 import {
   UncontrolledButtonDropdown, Button,
@@ -11,7 +11,7 @@ import {
 
 import type { IPageGrantData } from '~/interfaces/page';
 import {
-  useIsEditable, useIsAclEnabled, useIsUsersHomepageDeletionEnabled,
+  useIsEditable, useIsAclEnabled,
 } from '~/stores/context';
 import { useWaitingSaveProcessing } from '~/stores/editor';
 import { useSWRxCurrentPage } from '~/stores/page';
@@ -39,7 +39,6 @@ export const SavePageControls = (props: SavePageControlsProps): JSX.Element | nu
   const { data: currentPage } = useSWRxCurrentPage();
   const { data: isEditable } = useIsEditable();
   const { data: isAclEnabled } = useIsAclEnabled();
-  const { data: isUsersHomepageDeletionEnabled } = useIsUsersHomepageDeletionEnabled();
   const { data: grantData, mutate: mutateGrant } = useSelectedGrant();
   const { data: _isWaitingSaveProcessing } = useWaitingSaveProcessing();
 
@@ -70,7 +69,7 @@ export const SavePageControls = (props: SavePageControlsProps): JSX.Element | nu
 
   const { grant, grantedGroup } = grantData;
 
-  const isGrantSelectorDisabledPage = !isMovablePage(currentPage?.path ?? '', currentPage?.creator?.status, isUsersHomepageDeletionEnabled ?? undefined);
+  const isGrantSelectorDisabledPage = isTopPage(currentPage?.path ?? '') || isUsersProtectedPages(currentPage?.path ?? '');
   const labelSubmitButton = (currentPage != null && !currentPage.isEmpty) ? t('Update') : t('Create');
   const labelOverwriteScopes = t('page_edit.overwrite_scopes', { operation: labelSubmitButton });
 

+ 5 - 8
apps/app/src/components/Sidebar/PageTree/Item.tsx

@@ -45,7 +45,6 @@ interface ItemProps {
   isEnableActions: boolean
   isReadOnlyUser: boolean
   itemNode: ItemNode
-  isUsersHomepageDeletionEnabled?: boolean
   targetPathOrId?: Nullable<string>
   isOpen?: boolean
   onRenamed?(fromPath: string | undefined, toPath: string): void
@@ -113,9 +112,8 @@ const NotDraggableForClosableTextInput = (props: NotDraggableProps): JSX.Element
 const Item: FC<ItemProps> = (props: ItemProps) => {
   const { t } = useTranslation();
   const {
-    isEnableActions, isReadOnlyUser, itemNode,
-    isUsersHomepageDeletionEnabled, targetPathOrId, isOpen: _isOpen = false,
-    onRenamed, onClickDuplicateMenuItem, onClickDeleteMenuItem,
+    itemNode, targetPathOrId, isOpen: _isOpen = false,
+    onRenamed, onClickDuplicateMenuItem, onClickDeleteMenuItem, isEnableActions, isReadOnlyUser,
   } = props;
 
   const { page, children } = itemNode;
@@ -157,10 +155,10 @@ const Item: FC<ItemProps> = (props: ItemProps) => {
     type: 'PAGE_TREE',
     item: { page },
     canDrag: () => {
-      if (page.path == null || page.creator?.status == null) {
+      if (page.path == null) {
         return false;
       }
-      return !pagePathUtils.isUsersProtectedPages(page.path, page.creator.status, isUsersHomepageDeletionEnabled);
+      return !pagePathUtils.isUsersProtectedPages(page.path);
     },
     end: (item, monitor) => {
       // in order to set d-none to dropped Item
@@ -544,10 +542,9 @@ const Item: FC<ItemProps> = (props: ItemProps) => {
             <Item
               isEnableActions={isEnableActions}
               isReadOnlyUser={isReadOnlyUser}
-              isUsersHomepageDeletionEnabled={isUsersHomepageDeletionEnabled}
               itemNode={node}
-              targetPathOrId={targetPathOrId}
               isOpen={false}
+              targetPathOrId={targetPathOrId}
               onRenamed={onRenamed}
               onClickDuplicateMenuItem={onClickDuplicateMenuItem}
               onClickDeleteMenuItem={onClickDeleteMenuItem}

+ 3 - 6
apps/app/src/components/Sidebar/PageTree/ItemsTree.tsx

@@ -13,7 +13,6 @@ import { toastError, toastSuccess } from '~/client/util/toastr';
 import { AncestorsChildrenResult, RootPageResult, TargetAndAncestors } from '~/interfaces/page-listing-results';
 import { OnDuplicatedFunction, OnDeletedFunction } from '~/interfaces/ui';
 import { SocketEventName, UpdateDescCountData, UpdateDescCountRawData } from '~/interfaces/websocket';
-import { useIsUsersHomepageDeletionEnabled } from '~/stores/context';
 import {
   IPageForPageDuplicateModal, usePageDuplicateModal, usePageDeleteModal,
 } from '~/stores/modal';
@@ -110,7 +109,6 @@ const ItemsTree = (props: ItemsTreeProps): JSX.Element => {
   const { data: ancestorsChildrenResult, error: error1 } = useSWRxPageAncestorsChildren(targetPath);
   const { data: rootPageResult, error: error2 } = useSWRxRootPage();
   const { data: currentPagePath } = useCurrentPagePath();
-  const { data: isUsersHomepageDeletionEnabled } = useIsUsersHomepageDeletionEnabled();
   const { open: openDuplicateModal } = usePageDuplicateModal();
   const { open: openDeleteModal } = usePageDeleteModal();
   const { data: sidebarScrollerRef } = useSidebarScrollerRef();
@@ -276,12 +274,11 @@ const ItemsTree = (props: ItemsTreeProps): JSX.Element => {
       <ul className={`grw-pagetree ${styles['grw-pagetree']} list-group py-3`} ref={rootElemRef}>
         <Item
           key={initialItemNode.page.path}
-          isEnableActions={isEnableActions}
-          isReadOnlyUser={isReadOnlyUser}
-          itemNode={initialItemNode}
-          isUsersHomepageDeletionEnabled={isUsersHomepageDeletionEnabled ?? undefined}
           targetPathOrId={targetPathOrId}
+          itemNode={initialItemNode}
           isOpen
+          isEnableActions={isEnableActions}
+          isReadOnlyUser={isReadOnlyUser}
           onRenamed={onRenamed}
           onClickDuplicateMenuItem={onClickDuplicateMenuItem}
           onClickDeleteMenuItem={onClickDeleteMenuItem}

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

@@ -32,7 +32,7 @@ import {
   useCurrentUser,
   useIsForbidden, useIsSharedUser,
   useIsEnabledStaleNotification, useIsIdenticalPath,
-  useIsSearchServiceConfigured, useIsSearchServiceReachable, useDisableLinkSharing, useIsUsersHomepageDeletionEnabled,
+  useIsSearchServiceConfigured, useIsSearchServiceReachable, useDisableLinkSharing,
   useHackmdUri, useDefaultIndentSize, useIsIndentSizeForced,
   useIsAclEnabled, useIsSearchPage, useIsEnabledAttachTitleHeader,
   useCsrfToken, useIsSearchScopeChildrenAsDefault, useIsEnabledMarp, useCurrentPathname,
@@ -170,7 +170,6 @@ type Props = CommonProps & {
   adminPreferredIndentSize: number,
   isIndentSizeForced: boolean,
   disableLinkSharing: boolean,
-  isUsersHomepageDeletionEnabled: boolean,
   skipSSR: boolean,
   ssrMaxRevisionBodyLength: number,
 
@@ -219,7 +218,6 @@ const Page: NextPageWithLayout<Props> = (props: Props) => {
   useDefaultIndentSize(props.adminPreferredIndentSize);
   useIsIndentSizeForced(props.isIndentSizeForced);
   useDisableLinkSharing(props.disableLinkSharing);
-  useIsUsersHomepageDeletionEnabled(props.isUsersHomepageDeletionEnabled);
   useRendererConfig(props.rendererConfig);
   useIsEnabledMarp(props.rendererConfig.isEnabledMarp);
   // useRendererSettings(props.rendererSettingsStr != null ? JSON.parse(props.rendererSettingsStr) : undefined);
@@ -581,7 +579,6 @@ function injectServerConfigurations(context: GetServerSidePropsContext, props: P
   props.isContainerFluid = configManager.getConfig('crowi', 'customize:isContainerFluid');
   props.isEnabledStaleNotification = configManager.getConfig('crowi', 'customize:isEnabledStaleNotification');
   props.disableLinkSharing = configManager.getConfig('crowi', 'security:disableLinkSharing');
-  props.isUsersHomepageDeletionEnabled = configManager.getConfig('crowi', 'security:isUsersHomepageDeletionEnabled');
   props.editorConfig = {
     upload: {
       isUploadableFile: crowi.fileUploadService.getFileUploadEnabled(),

+ 5 - 12
apps/app/src/server/models/page.ts

@@ -429,15 +429,8 @@ export class PageQueryBuilder {
     return this;
   }
 
-  async addConditionToMinimizeDataForRendering(): Promise<PageQueryBuilder> {
-    // eslint-disable-next-line rulesdir/no-populate
-    this.query = this.query
-      .select('_id path isEmpty grant revision descendantCount creator')
-      .populate({
-        path: 'creator',
-        model: 'User',
-        select: 'status',
-      });
+  addConditionToMinimizeDataForRendering(): PageQueryBuilder {
+    this.query = this.query.select('_id path isEmpty grant revision descendantCount');
 
     return this;
   }
@@ -665,11 +658,11 @@ schema.statics.findTargetAndAncestorsByPathOrId = async function(pathOrId: strin
   // Do not populate
   const queryBuilder = new PageQueryBuilder(this.find(), true);
   await queryBuilder.addViewerCondition(user, userGroups);
-  queryBuilder.addConditionAsOnTree();
-  queryBuilder.addConditionToListByPathsArray(ancestorPaths);
-  await queryBuilder.addConditionToMinimizeDataForRendering();
 
   const _targetAndAncestors: PageDocument[] = await queryBuilder
+    .addConditionAsOnTree()
+    .addConditionToListByPathsArray(ancestorPaths)
+    .addConditionToMinimizeDataForRendering()
     .addConditionToSortPagesByDescPath()
     .query
     .lean()

+ 4 - 6
apps/app/src/server/routes/apiv3/page-listing.ts

@@ -140,24 +140,22 @@ const routerFactory = (crowi: Crowi): Router => {
       const idToPageInfoMap: Record<string, IPageInfo | IPageInfoForListing> = {};
 
       const isGuestUser = req.user == null;
-      const promiseArray = pages.map(async(page) => {
+      for (const page of pages) {
         // construct isIPageInfoForListing
-        const basicPageInfo = await pageService.constructBasicPageInfo(page, isGuestUser);
+        const basicPageInfo = pageService.constructBasicPageInfo(page, isGuestUser);
 
         const pageInfo = (!isIPageInfoForEntity(basicPageInfo))
           ? basicPageInfo
           // create IPageInfoForListing
           : {
             ...basicPageInfo,
-            isAbleToDeleteCompletely: await pageService.canDeleteCompletely(page.path, (page.creator as IUserHasId)?._id, req.user, false), // use normal delete config
+            isAbleToDeleteCompletely: pageService.canDeleteCompletely(page.path, (page.creator as IUserHasId)?._id, req.user, false), // use normal delete config
             bookmarkCount: bookmarkCountMap != null ? bookmarkCountMap[page._id] : undefined,
             revisionShortBody: shortBodiesMap != null ? shortBodiesMap[page._id] : undefined,
           } as IPageInfoForListing;
 
         idToPageInfoMap[page._id] = pageInfo;
-      });
-
-      await Promise.all(promiseArray);
+      }
 
       return res.apiv3(idToPageInfoMap);
     }

+ 3 - 3
apps/app/src/server/routes/apiv3/pages.js

@@ -641,7 +641,7 @@ module.exports = (crowi) => {
 
     const pagesInTrash = await crowi.pageService.findAllTrashPages(req.user);
 
-    const deletablePages = await crowi.pageService.filterPagesByCanDeleteCompletely(pagesInTrash, req.user, true);
+    const deletablePages = crowi.pageService.filterPagesByCanDeleteCompletely(pagesInTrash, req.user, true);
 
     if (deletablePages.length === 0) {
       const msg = 'No pages can be deleted.';
@@ -910,14 +910,14 @@ module.exports = (crowi) => {
      * Delete Completely
      */
     if (isCompletely) {
-      pagesCanBeDeleted = await crowi.pageService.filterPagesByCanDeleteCompletely(pagesToDelete, req.user, isRecursively);
+      pagesCanBeDeleted = crowi.pageService.filterPagesByCanDeleteCompletely(pagesToDelete, req.user, isRecursively);
     }
     /*
      * Trash
      */
     else {
       pagesCanBeDeleted = pagesToDelete.filter(p => p.isEmpty || p.isUpdatable(pageIdToRevisionIdMap[p._id].toString()));
-      pagesCanBeDeleted = await crowi.pageService.filterPagesByCanDelete(pagesToDelete, req.user, isRecursively);
+      pagesCanBeDeleted = crowi.pageService.filterPagesByCanDelete(pagesToDelete, req.user, isRecursively);
     }
 
     if (pagesCanBeDeleted.length === 0) {

+ 2 - 2
apps/app/src/server/routes/page.js

@@ -762,7 +762,7 @@ module.exports = function(crowi, app) {
 
     try {
       if (isCompletely) {
-        if (!await crowi.pageService.canDeleteCompletely(page.path, creator, req.user, isRecursively)) {
+        if (!crowi.pageService.canDeleteCompletely(page.path, creator, req.user, isRecursively)) {
           return res.json(ApiResponse.error('You can not delete this page completely', 'user_not_admin'));
         }
         await crowi.pageService.deleteCompletely(page, req.user, options, isRecursively, false, activityParameters);
@@ -778,7 +778,7 @@ module.exports = function(crowi, app) {
           return res.json(ApiResponse.error('Someone could update this page, so couldn\'t delete.', 'outdated'));
         }
 
-        if (!await crowi.pageService.canDelete(page.path, creator, req.user, isRecursively)) {
+        if (!crowi.pageService.canDelete(page.path, creator, req.user, isRecursively)) {
           return res.json(ApiResponse.error('You can not delete this page', 'user_not_admin'));
         }
 

+ 18 - 49
apps/app/src/server/service/page.ts

@@ -40,14 +40,12 @@ import ShareLink from '../models/share-link';
 import Subscription from '../models/subscription';
 import { V5ConversionError } from '../models/vo/v5-conversion-error';
 
-import { configManager } from './config-manager';
-
 const debug = require('debug')('growi:services:page');
 
 const logger = loggerFactory('growi:services:page');
 const {
   isTrashPage, isTopPage, omitDuplicateAreaPageFromPages,
-  isMovablePage, canMoveByPath, hasSlash, generateChildrenRegExp,
+  isMovablePage, canMoveByPath, isUsersProtectedPages, hasSlash, generateChildrenRegExp,
 } = pagePathUtils;
 
 const { addTrailingSlash } = pathUtils;
@@ -165,13 +163,8 @@ class PageService {
     this.pageEvent.on('addSeenUsers', this.pageEvent.onAddSeenUsers);
   }
 
-  async canDeleteCompletely(path: string, creatorId: ObjectIdLike, operator: IUserHasId, isRecursively: boolean): Promise<boolean> {
-    const isUsersHomepageDeletionEnabled = configManager.getConfig('crowi', 'security:isUsersHomepageDeletionEnabled');
-    const User = mongoose.model('User');
-    const creator = await User.findById(creatorId);
-    if (operator == null || !isMovablePage(path, creator?.status, isUsersHomepageDeletionEnabled)) {
-      return false;
-    }
+  canDeleteCompletely(path: string, creatorId: ObjectIdLike, operator: any | null, isRecursively: boolean): boolean {
+    if (operator == null || isTopPage(path) || isUsersProtectedPages(path)) return false;
 
     const pageCompleteDeletionAuthority = this.crowi.configManager.getConfig('crowi', 'security:pageCompleteDeletionAuthority');
     const pageRecursiveCompleteDeletionAuthority = this.crowi.configManager.getConfig('crowi', 'security:pageRecursiveCompleteDeletionAuthority');
@@ -181,13 +174,8 @@ class PageService {
     return this.canDeleteLogic(creatorId, operator, isRecursively, singleAuthority, recursiveAuthority);
   }
 
-  async canDelete(path: string, creatorId: ObjectIdLike, operator: IUserHasId, isRecursively: boolean): Promise<boolean> {
-    const isUsersHomepageDeletionEnabled = configManager.getConfig('crowi', 'security:isUsersHomepageDeletionEnabled');
-    const User = mongoose.model('User');
-    const creator = await User.findById(creatorId);
-    if (operator == null || !isMovablePage(path, creator?.status, isUsersHomepageDeletionEnabled)) {
-      return false;
-    }
+  canDelete(path: string, creatorId: ObjectIdLike, operator: any | null, isRecursively: boolean): boolean {
+    if (operator == null || isUsersProtectedPages(path) || isTopPage(path)) return false;
 
     const pageDeletionAuthority = this.crowi.configManager.getConfig('crowi', 'security:pageDeletionAuthority');
     const pageRecursiveDeletionAuthority = this.crowi.configManager.getConfig('crowi', 'security:pageRecursiveDeletionAuthority');
@@ -229,22 +217,12 @@ class PageService {
     return false;
   }
 
-  async filterPagesByCanDeleteCompletely(pages, user, isRecursively: boolean): Promise<boolean[]> {
-    const filteredPages = await Promise.all(pages.map(async(p) => {
-      const canDeleteCompletely = await this.canDeleteCompletely(p.path, p.creator, user, isRecursively);
-      return p.isEmpty || canDeleteCompletely;
-    }));
-
-    return filteredPages;
+  filterPagesByCanDeleteCompletely(pages, user, isRecursively: boolean) {
+    return pages.filter(p => p.isEmpty || this.canDeleteCompletely(p.path, p.creator, user, isRecursively));
   }
 
-  async filterPagesByCanDelete(pages, user, isRecursively: boolean): Promise<boolean[]> {
-    const filteredPages = await Promise.all(pages.map(async(p) => {
-      const canDeleteCompletely = await this.canDelete(p.path, p.creator, user, isRecursively);
-      return p.isEmpty || canDeleteCompletely;
-    }));
-
-    return filteredPages;
+  filterPagesByCanDelete(pages, user, isRecursively: boolean) {
+    return pages.filter(p => p.isEmpty || this.canDelete(p.path, p.creator, user, isRecursively));
   }
 
   // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
@@ -281,7 +259,7 @@ class PageService {
     }
 
     const isGuestUser = user == null;
-    const pageInfo = await this.constructBasicPageInfo(page, isGuestUser);
+    const pageInfo = this.constructBasicPageInfo(page, isGuestUser);
 
     const Bookmark = this.crowi.model('Bookmark');
     const bookmarkCount = await Bookmark.countByPageId(pageId);
@@ -310,8 +288,8 @@ class PageService {
       const notEmptyClosestAncestor = await Page.findNonEmptyClosestAncestor(page.path);
       creatorId = notEmptyClosestAncestor.creator;
     }
-    const isDeletable = await this.canDelete(page.path, creatorId, user, false);
-    const isAbleToDeleteCompletely = await this.canDeleteCompletely(page.path, creatorId, user, false); // use normal delete config
+    const isDeletable = this.canDelete(page.path, creatorId, user, false);
+    const isAbleToDeleteCompletely = this.canDeleteCompletely(page.path, creatorId, user, false); // use normal delete config
 
     return {
       data: page,
@@ -1392,7 +1370,6 @@ class PageService {
      * Common Operation
      */
     const Page = mongoose.model('Page') as PageModel;
-    const isUsersHomepageDeletionEnabled = configManager.getConfig('crowi', 'security:isUsersHomepageDeletionEnabled');
 
     // Separate v4 & v5 process
     const shouldUseV4Process = this.shouldUseV4Process(page);
@@ -1408,8 +1385,7 @@ class PageService {
       throw new Error('This method does NOT support deleting trashed pages.');
     }
 
-    const populatedPage = await page.populate('creator');
-    if (!isMovablePage(page.path, populatedPage.creator?.status, isUsersHomepageDeletionEnabled)) {
+    if (!isMovablePage(page.path)) {
       throw new Error('Page is not deletable.');
     }
 
@@ -1549,7 +1525,6 @@ class PageService {
     const PageTagRelation = mongoose.model('PageTagRelation') as any; // TODO: Typescriptize model
     const Revision = mongoose.model('Revision') as any; // TODO: Typescriptize model
     const PageRedirect = mongoose.model('PageRedirect') as unknown as PageRedirectModel;
-    const isUsersHomepageDeletionEnabled = configManager.getConfig('crowi', 'security:isUsersHomepageDeletionEnabled');
 
     const newPath = Page.getDeletedPageName(page.path);
     const isTrashed = isTrashPage(page.path);
@@ -1558,8 +1533,7 @@ class PageService {
       throw new Error('This method does NOT support deleting trashed pages.');
     }
 
-    const populatedPage = await page.populate('creator');
-    if (!isMovablePage(page.path, populatedPage.creator?.status, isUsersHomepageDeletionEnabled)) {
+    if (!isMovablePage(page.path)) {
       throw new Error('Page is not deletable.');
     }
 
@@ -2409,12 +2383,8 @@ class PageService {
     });
   }
 
-  async constructBasicPageInfo(page: PageDocument, isGuestUser?: boolean): Promise<IPageInfo | IPageInfoForEntity> {
-    const populatedPage = await page.populate('creator');
-    const isUsersHomepageDeletionEnabled = configManager.getConfig('crowi', 'security:isUsersHomepageDeletionEnabled');
-    const isMovable = isGuestUser
-      ? false
-      : isMovablePage(page.path, populatedPage.creator?.status, isUsersHomepageDeletionEnabled);
+  constructBasicPageInfo(page: PageDocument, isGuestUser?: boolean): IPageInfo | IPageInfoForEntity {
+    const isMovable = isGuestUser ? false : isMovablePage(page.path);
 
     if (page.isEmpty) {
       return {
@@ -4198,10 +4168,9 @@ class PageService {
     // get pages at once
     const queryBuilder = new PageQueryBuilder(Page.find({ path: { $in: regexps } }), true);
     await queryBuilder.addViewerCondition(user, userGroups);
-    queryBuilder.addConditionAsOnTree();
-    await queryBuilder.addConditionToMinimizeDataForRendering();
-
     const pages = await queryBuilder
+      .addConditionAsOnTree()
+      .addConditionToMinimizeDataForRendering()
       .addConditionToSortPagesByAscPath()
       .query
       .lean()

+ 0 - 4
apps/app/src/stores/context.tsx

@@ -68,10 +68,6 @@ export const useDisableLinkSharing = (initialData?: Nullable<boolean>): SWRRespo
   return useContextSWR<Nullable<boolean>, Error>('disableLinkSharing', initialData);
 };
 
-export const useIsUsersHomepageDeletionEnabled = (initialData?: Nullable<boolean>): SWRResponse<Nullable<boolean>, Error> => {
-  return useContextSWR<Nullable<boolean>, Error>('isUsersHomepageDeletionEnabled', initialData);
-};
-
 export const useRegistrationWhitelist = (initialData?: Nullable<string[]>): SWRResponse<Nullable<string[]>, Error> => {
   return useContextSWR<Nullable<string[]>, Error>('registrationWhitelist', initialData);
 };