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

+ 10 - 0
packages/app/src/server/models/page.ts

@@ -567,6 +567,16 @@ schema.statics.removeLeafEmptyPagesById = async function(pageId: ObjectIdLike):
   await this.deleteMany({ _id: { $in: pageIdsToRemove } });
 };
 
+schema.statics.findByPageIdsToEdit = async function(ids, user, shouldIncludeEmpty = false) {
+  const builder = new PageQueryBuilder(this.find({ _id: { $in: ids } }), shouldIncludeEmpty);
+
+  await this.addConditionToFilteringByViewerToEdit(builder, user);
+
+  const pages = await builder.query.lean().exec();
+
+  return pages;
+};
+
 export type PageCreateOptions = {
   format?: string
   grantUserGroupId?: ObjectIdLike

+ 15 - 32
packages/app/src/server/routes/apiv3/pages.js

@@ -182,9 +182,8 @@ module.exports = (crowi) => {
       body('isRecursively').if(value => value != null).isBoolean().withMessage('isRecursively must be boolean'),
     ],
     deletePages: [
-      body('pageIds')
-        .custom(v => (v == null ? true : Array.isArray(v)))
-        .withMessage('The body property "pageId" must be an array or null. (null is the same as [])'),
+      body('pageIdToRevisionIdMap')
+        .withMessage('The body property "pageIdToRevisionIdMap" must be an json map with pageId as key and revisionId as value.'),
       body('isCompletely')
         .custom(v => v === 'true' || v === true || v == null)
         .withMessage('The body property "isCompletely" must be "true" or true. (Omit param for false)'),
@@ -721,16 +720,19 @@ module.exports = (crowi) => {
   });
 
   router.delete('/delete', accessTokenParser, loginRequiredStrictly, csrf, validator.deletePages, apiV3FormValidator, async(req, res) => {
-    const { pageIds: _pageIds, isCompletely, isRecursively } = req.body;
-    const pageIds = _pageIds == null ? [] : _pageIds;
+    const { pageIdToRevisionIdMap, isCompletely, isRecursively } = req.body;
+    const pageIds = Object.keys(pageIdToRevisionIdMap);
 
+    if (pageIds.length === 0) {
+      return res.apiv3Err(new ErrorV3('Select pages to delete.', 'no_page_selected'), 400);
+    }
     if (pageIds.length > LIMIT_FOR_MULTIPLE_PAGE_OP) {
       return res.apiv3Err(new ErrorV3(`The maximum number of pages you can select is ${LIMIT_FOR_MULTIPLE_PAGE_OP}.`, 'exceeded_maximum_number'), 400);
     }
 
     let pagesToDelete;
     try {
-      pagesToDelete = await Page.findListByPageIds(pageIds, null, false);
+      pagesToDelete = await Page.findByPageIdsToEdit(pageIds, req.user, true);
     }
     catch (err) {
       logger.error('Failed to find pages to delete.', err);
@@ -738,41 +740,22 @@ module.exports = (crowi) => {
     }
 
     let pagesCanBeDeleted;
-
     /*
      * Delete Completely
      */
     if (isCompletely) {
-      try {
-        pagesCanBeDeleted = crowi.pageService.filterPagesByCanDeleteCompletely(pagesToDelete, req.user);
-      }
-      catch (err) {
-        const msg = 'Failed to process completely delete pages.';
-        logger.error(msg, err);
-        return res.apiv3Err(new ErrorV3(msg), 500);
-      }
+      pagesCanBeDeleted = crowi.pageService.filterPagesByCanDeleteCompletely(pagesToDelete, req.user);
     }
-
     /*
      * Trash
      */
     else {
-      try {
-        // recursive
-        if (isRecursively) {
-
-        }
-
-        // non-recursive
-        else {
+      pagesCanBeDeleted = pagesToDelete.filter(p => p.isEmpty || p.isUpdatable(pageIdToRevisionIdMap[p._id].toString()));
+    }
 
-        }
-      }
-      catch (err) {
-        const msg = 'Failed to process completely delete pages.';
-        logger.error(msg, err);
-        return res.apiv3Err(new ErrorV3(msg), 500);
-      }
+    if (pagesCanBeDeleted.length === 0) {
+      const msg = 'No pages can be deleted.';
+      return res.apiv3Err(new ErrorV3(msg), 500);
     }
 
     // run delete
@@ -808,7 +791,7 @@ module.exports = (crowi) => {
 
     if (isRecursively) {
       // this method innerly uses socket to send message
-      crowi.pageService.normalizeParentRecursivelyByPageIds(pageIds);
+      crowi.pageService.normalizeParentRecursivelyByPageIds(pageIds, req.user);
     }
     else {
       try {

+ 5 - 5
packages/app/src/server/service/page.ts

@@ -1436,10 +1436,10 @@ class PageService {
       throw Error(`The maximum number of pages is ${LIMIT_FOR_MULTIPLE_PAGE_OP}.`);
     }
 
-    // omit duplicate paths if isRecursively true
-    const pages = isRecursively ? omitDuplicateAreaPageFromPages(pagesToDelete) : pagesToDelete;
+    // omit duplicate paths if isRecursively true, omit empty pages if isRecursively false
+    const pages = isRecursively ? omitDuplicateAreaPageFromPages(pagesToDelete) : pagesToDelete.filter(p => !p.isEmpty);
 
-    // TODO: insertMany PageOperationBlock
+    // TODO: insertMany PageOperationBlock if isRecursively true
 
     if (isCompletely) {
       for await (const page of pages) {
@@ -1854,7 +1854,7 @@ class PageService {
     return Page.updateOne({ _id: pageId }, { parent: parent._id });
   }
 
-  async normalizeParentRecursivelyByPageIds(pageIds) {
+  async normalizeParentRecursivelyByPageIds(pageIds, user) {
     if (pageIds == null || pageIds.length === 0) {
       logger.error('pageIds is null or 0 length.');
       return;
@@ -1879,7 +1879,7 @@ class PageService {
 
     let result;
     try {
-      result = await Page.findListByPageIds(pageIds, null, false);
+      result = await Page.findByPageIdsToEdit(pageIds, user, false);
     }
     catch (err) {
       logger.error('Failed to find pages by ids', err);

+ 1 - 1
packages/app/test/integration/service/v5-migration.test.js

@@ -59,7 +59,7 @@ describe('V5 page migration', () => {
 
       const pageIds = pages.map(page => page._id);
       // migrate
-      await crowi.pageService.normalizeParentRecursivelyByPageIds(pageIds);
+      await crowi.pageService.normalizeParentRecursivelyByPageIds(pageIds, testUser1);
 
       const migratedPages = await Page.find({
         path: {