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

Revert "Deleted unnecessary method"

This reverts commit 6700c542085ec3e4ed79e4a26a932ad418d2e670.
Taichi Masuyama 4 лет назад
Родитель
Сommit
3de82bebb0
1 измененных файлов с 96 добавлено и 27 удалено
  1. 96 27
      packages/app/src/server/service/page.ts

+ 96 - 27
packages/app/src/server/service/page.ts

@@ -313,20 +313,6 @@ class PageService {
     return page.grant !== Page.GRANT_RESTRICTED && page.grant !== Page.GRANT_SPECIFIED;
   }
 
-  /**
-   * Remove all empty pages at leaf position by page whose parent will change or which will be deleted.
-   * @param page Page whose parent will change or which will be deleted
-   */
-  async removeLeafEmptyPages(page): Promise<void> {
-    const Page = mongoose.model('Page') as unknown as PageModel;
-
-    // delete leaf empty pages
-    const isSiblingsOrChildrenExist = await Page.exists({ parent: { $in: [page.parent, page._id] }, _id: { $ne: page._id } });
-    if (!isSiblingsOrChildrenExist) {
-      await Page.removeLeafEmptyPagesRecursively(page.parent);
-    }
-  }
-
   /**
    * Generate read stream to operate descendants of the specified page path
    * @param {string} targetPagePath
@@ -370,6 +356,15 @@ class PageService {
       return this.renamePageV4(page, newPagePath, user, options);
     }
 
+    if (await Page.exists({ path: newPagePath })) {
+      throw Error(`Page already exists at ${newPagePath}`);
+    }
+
+    const canOperate = await this.crowi.pageOperationService.canOperate(true, page.path, newPagePath);
+    if (!canOperate) {
+      throw Error(`Cannot operate rename to path "${newPagePath}" right now.`);
+    }
+
     /*
      * Resumable Operation
      */
@@ -449,6 +444,10 @@ class PageService {
       update.updatedAt = new Date();
     }
     const renamedPage = await Page.findByIdAndUpdate(page._id, { $set: update }, { new: true });
+
+    // remove empty pages at leaf position
+    await Page.removeLeafEmptyPagesRecursively(page.parent);
+
     this.pageEvent.emit('rename', page, user);
 
     // Set to Sub
@@ -726,7 +725,7 @@ class PageService {
       .pipe(createBatchStream(BULK_REINDEX_SIZE))
       .pipe(writeStream);
 
-    await streamToPromise(readStream);
+    await streamToPromise(writeStream);
   }
 
   /*
@@ -756,6 +755,11 @@ class PageService {
       return this.duplicateV4(page, newPagePath, user, isRecursively);
     }
 
+    const canOperate = await this.crowi.pageOperationService.canOperate(isRecursively, page.path, newPagePath);
+    if (!canOperate) {
+      throw Error(`Cannot operate duplicate to path "${newPagePath}" right now.`);
+    }
+
     // 2. UserGroup & Owner validation
     // use the parent's grant when target page is an empty page
     let grant;
@@ -1176,6 +1180,13 @@ class PageService {
       throw new Error('Page is not deletable.');
     }
 
+    const newPath = Page.getDeletedPageName(page.path);
+
+    const canOperate = await this.crowi.pageOperationService.canOperate(isRecursively, page.path, newPath);
+    if (!canOperate) {
+      throw Error(`Cannot operate delete to path "${newPath}" right now.`);
+    }
+
     // Replace with an empty page
     const isChildrenExist = await Page.exists({ parent: page._id });
     const shouldReplace = !isRecursively && isChildrenExist;
@@ -1203,11 +1214,9 @@ class PageService {
       await this.updateDescendantCountOfAncestors(page.parent, -1, true);
     }
     // 2. Delete leaf empty pages
-    const parent = await Page.findById(page.parent);
-    await this.removeLeafEmptyPages(parent);
+    await Page.removeLeafEmptyPagesRecursively(page.parent);
 
     if (isRecursively) {
-      const newPath = Page.getDeletedPageName(page.path);
       let pageOp;
       try {
         pageOp = await PageOperation.create({
@@ -1459,7 +1468,7 @@ class PageService {
       ShareLink.deleteMany({ relatedPage: { $in: pageIds } }),
       Revision.deleteMany({ pageId: { $in: pageIds } }),
       Page.deleteMany({ $or: [{ path: { $in: pagePaths } }, { _id: { $in: pageIds } }] }),
-      PageRedirect.deleteMany({ $or: [{ toPath: { $in: pagePaths } }] }),
+      PageRedirect.deleteMany({ $or: [{ fromPath: { $in: pagePaths }, toPath: { $in: pagePaths } }] }),
       attachmentService.removeAllAttachments(attachments),
     ]);
   }
@@ -1498,6 +1507,11 @@ class PageService {
       return this.deleteCompletelyV4(page, user, options, isRecursively, preventEmitting);
     }
 
+    const canOperate = await this.crowi.pageOperationService.canOperate(isRecursively, page.path, null);
+    if (!canOperate) {
+      throw Error(`Cannot operate deleteCompletely from path "${page.path}" right now.`);
+    }
+
     const ids = [page._id];
     const paths = [page.path];
 
@@ -1523,8 +1537,7 @@ class PageService {
     await this.deleteCompletelyOperation(ids, paths);
 
     // delete leaf empty pages
-    const parent = await Page.findById(page.parent);
-    await this.removeLeafEmptyPages(parent);
+    await Page.removeLeafEmptyPagesRecursively(page.parent);
 
     if (!page.isEmpty && !preventEmitting) {
       this.pageEvent.emit('deleteCompletely', page, user);
@@ -1631,7 +1644,7 @@ class PageService {
       .pipe(createBatchStream(BULK_REINDEX_SIZE))
       .pipe(writeStream);
 
-    await streamToPromise(readStream);
+    await streamToPromise(writeStream);
 
     return nDeletedNonEmptyPages;
   }
@@ -1709,6 +1722,12 @@ class PageService {
     }
 
     const newPath = Page.getRevertDeletedPageName(page.path);
+
+    const canOperate = await this.crowi.pageOperationService.canOperate(isRecursively, page.path, newPath);
+    if (!canOperate) {
+      throw Error(`Cannot operate revert from path "${page.path}" right now.`);
+    }
+
     const includeEmpty = true;
     const originPage = await Page.findByPath(newPath, includeEmpty);
 
@@ -1801,6 +1820,18 @@ class PageService {
     await PageOperation.findByIdAndDelete(pageOpId);
   }
 
+  async resumableRevertDeletedDescendants(page, user, options, shouldUseV4Process) {
+    const revertedDescendantCount = await this.revertDeletedDescendantsWithStream(page, user, options, shouldUseV4Process);
+
+    // update descendantCount of ancestors'
+    if (page.parent != null) {
+      await this.updateDescendantCountOfAncestors(page.parent, revertedDescendantCount + 1, true);
+
+      // delete leaf empty pages
+      await this.removeLeafEmptyPages(page);
+    }
+  }
+
   private async revertDeletedPageV4(page, user, options = {}, isRecursively = false) {
     const Page = this.crowi.model('Page');
     const PageTagRelation = this.crowi.model('PageTagRelation');
@@ -2049,8 +2080,9 @@ class PageService {
   }
 
   async normalizeParentByPageIds(pageIds: ObjectIdLike[], user, isRecursively: boolean): Promise<void> {
+    const Page = mongoose.model('Page') as unknown as PageModel;
+
     if (isRecursively) {
-      const Page = mongoose.model('Page') as unknown as PageModel;
       const pages = await Page.findByPageIdsToEdit(pageIds, user, false);
 
       // DO NOT await !!
@@ -2060,7 +2092,17 @@ class PageService {
     }
 
     for await (const pageId of pageIds) {
+      const page = await Page.findById(pageId);
+      if (page == null) {
+        continue;
+      }
+
       try {
+        const canOperate = await this.crowi.pageOperationService.canOperate(false, page.path, page.path);
+        if (!canOperate) {
+          throw Error(`Cannot operate normalizeParent to path "${page.path}" right now.`);
+        }
+
         const normalizedPage = await this.normalizeParentByPageId(pageId, user);
 
         if (normalizedPage == null) {
@@ -2166,11 +2208,30 @@ class PageService {
      * Main Operation (s)
      */
     for await (const page of normalizablePages) {
-      await this.normalizeParentRecursivelyMainOperation(page, user);
+      const canOperate = await this.crowi.pageOperationService.canOperate(true, page.path, page.path);
+      if (!canOperate) {
+        throw Error(`Cannot operate normalizeParentRecursiively to path "${page.path}" right now.`);
+      }
+
+      let pageOp;
+      try {
+        pageOp = await PageOperation.create({
+          actionType: PageActionType.NormalizeParent,
+          actionStage: PageActionStage.Main,
+          page,
+          user,
+          toPath: page.path,
+        });
+      }
+      catch (err) {
+        logger.error('Failed to create PageOperation document.', err);
+        throw err;
+      }
+      await this.normalizeParentRecursivelyMainOperation(page, user, pageOp._id);
     }
   }
 
-  async normalizeParentRecursivelyMainOperation(page, user): Promise<void> {
+  async normalizeParentRecursivelyMainOperation(page, user, pageOpId: ObjectIdLike): Promise<void> {
     // TODO: insertOne PageOperationBlock
 
     try {
@@ -2183,10 +2244,16 @@ class PageService {
       throw err;
     }
 
-    await this.normalizeParentRecursivelySubOperation(page, user);
+    // Set to Sub
+    const pageOp = await PageOperation.findByIdAndUpdatePageActionStage(pageOpId, PageActionStage.Sub);
+    if (pageOp == null) {
+      throw Error('PageOperation document not found');
+    }
+
+    await this.normalizeParentRecursivelySubOperation(page, user, pageOp._id);
   }
 
-  async normalizeParentRecursivelySubOperation(page, user): Promise<void> {
+  async normalizeParentRecursivelySubOperation(page, user, pageOpId: ObjectIdLike): Promise<void> {
     const Page = mongoose.model('Page') as unknown as PageModel;
 
     try {
@@ -2206,6 +2273,8 @@ class PageService {
       logger.error('Failed to update descendantCount after normalizing parent:', err);
       throw Error(`Failed to update descendantCount after normalizing parent: ${err}`);
     }
+
+    await PageOperation.findByIdAndDelete(pageOpId);
   }
 
   async _isPagePathIndexUnique() {