|
|
@@ -8,6 +8,7 @@ import streamToPromise from 'stream-to-promise';
|
|
|
|
|
|
import { SUPPORTED_TARGET_MODEL_TYPE, SUPPORTED_ACTION_TYPE } from '~/interfaces/activity';
|
|
|
import { Ref } from '~/interfaces/common';
|
|
|
+import { V5ConversionErrCode } from '~/interfaces/errors/v5-conversion-error';
|
|
|
import { HasObjectId } from '~/interfaces/has-object-id';
|
|
|
import {
|
|
|
IPage, IPageInfo, IPageInfoForEntity, IPageWithMeta,
|
|
|
@@ -16,7 +17,7 @@ import {
|
|
|
PageDeleteConfigValue, IPageDeleteConfigValueToProcessValidation,
|
|
|
} from '~/interfaces/page-delete-config';
|
|
|
import { IUserHasId } from '~/interfaces/user';
|
|
|
-import { SocketEventName, UpdateDescCountRawData } from '~/interfaces/websocket';
|
|
|
+import { PageMigrationErrorData, SocketEventName, UpdateDescCountRawData } from '~/interfaces/websocket';
|
|
|
import { stringifySnapshot } from '~/models/serializers/in-app-notification-snapshot/page';
|
|
|
import {
|
|
|
CreateMethod, PageCreateOptions, PageModel, PageDocument,
|
|
|
@@ -32,7 +33,6 @@ import { PageRedirectModel } from '../models/page-redirect';
|
|
|
import { serializePageSecurely } from '../models/serializers/page-serializer';
|
|
|
import Subscription from '../models/subscription';
|
|
|
import { V5ConversionError } from '../models/vo/v5-conversion-error';
|
|
|
-import { V5ConversionErrCode } from '~/interfaces/errors/v5-conversion-error';
|
|
|
|
|
|
const debug = require('debug')('growi:services:page');
|
|
|
|
|
|
@@ -2315,24 +2315,37 @@ class PageService {
|
|
|
this.normalizeParentRecursivelyMainOperation(page, user, pageOp._id);
|
|
|
}
|
|
|
|
|
|
- async normalizeParentByPageIds(pageIds: ObjectIdLike[], user, isRecursively: boolean): Promise<void> {
|
|
|
+ async normalizeParentByPageIdsRecursively(pageIds: ObjectIdLike[], user): Promise<void> {
|
|
|
const Page = mongoose.model('Page') as unknown as PageModel;
|
|
|
|
|
|
- if (isRecursively) {
|
|
|
- const pages = await Page.findByIdsAndViewer(pageIds, user, null);
|
|
|
+ const pages = await Page.findByIdsAndViewer(pageIds, user, null);
|
|
|
|
|
|
- // DO NOT await !!
|
|
|
- this.normalizeParentRecursivelyByPages(pages, user);
|
|
|
+ if (pages == null || pages.length === 0) {
|
|
|
+ throw Error('pageIds is null or 0 length.');
|
|
|
+ }
|
|
|
|
|
|
- return;
|
|
|
+ if (pages.length > LIMIT_FOR_MULTIPLE_PAGE_OP) {
|
|
|
+ throw Error(`The maximum number of pageIds allowed is ${LIMIT_FOR_MULTIPLE_PAGE_OP}.`);
|
|
|
}
|
|
|
|
|
|
+ this.normalizeParentRecursivelyByPages(pages, user);
|
|
|
+
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ async normalizeParentByPageIds(pageIds: ObjectIdLike[], user): Promise<void> {
|
|
|
+ const Page = await mongoose.model('Page') as unknown as PageModel;
|
|
|
+
|
|
|
+ const socket = this.crowi.socketIoService.getDefaultSocket();
|
|
|
+
|
|
|
for await (const pageId of pageIds) {
|
|
|
const page = await Page.findById(pageId);
|
|
|
if (page == null) {
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
+ const errorData: PageMigrationErrorData = { paths: [page.path] };
|
|
|
+
|
|
|
try {
|
|
|
const canOperate = await this.crowi.pageOperationService.canOperate(false, page.path, page.path);
|
|
|
if (!canOperate) {
|
|
|
@@ -2342,14 +2355,16 @@ class PageService {
|
|
|
const normalizedPage = await this.normalizeParentByPage(page, user);
|
|
|
|
|
|
if (normalizedPage == null) {
|
|
|
+ socket.emit(SocketEventName.PageMigrationError, errorData);
|
|
|
logger.error(`Failed to update descendantCount of page of id: "${pageId}"`);
|
|
|
}
|
|
|
}
|
|
|
catch (err) {
|
|
|
+ socket.emit(SocketEventName.PageMigrationError, errorData);
|
|
|
logger.error('Something went wrong while normalizing parent.', err);
|
|
|
- // socket.emit('normalizeParentByPageIds', { error: err.message }); TODO: use socket to tell user
|
|
|
}
|
|
|
}
|
|
|
+ socket.emit(SocketEventName.PageMigrationSuccess);
|
|
|
}
|
|
|
|
|
|
private async normalizeParentByPage(page, user) {
|
|
|
@@ -2412,14 +2427,7 @@ class PageService {
|
|
|
/*
|
|
|
* Main Operation
|
|
|
*/
|
|
|
- if (pages == null || pages.length === 0) {
|
|
|
- logger.error('pageIds is null or 0 length.');
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if (pages.length > LIMIT_FOR_MULTIPLE_PAGE_OP) {
|
|
|
- throw Error(`The maximum number of pageIds allowed is ${LIMIT_FOR_MULTIPLE_PAGE_OP}.`);
|
|
|
- }
|
|
|
+ const socket = this.crowi.socketIoService.getDefaultSocket();
|
|
|
|
|
|
const pagesToNormalize = omitDuplicateAreaPageFromPages(pages);
|
|
|
|
|
|
@@ -2429,25 +2437,29 @@ class PageService {
|
|
|
[normalizablePages, nonNormalizablePages] = await this.crowi.pageGrantService.separateNormalizableAndNotNormalizablePages(user, pagesToNormalize);
|
|
|
}
|
|
|
catch (err) {
|
|
|
+ socket.emit(SocketEventName.PageMigrationError);
|
|
|
throw err;
|
|
|
}
|
|
|
|
|
|
if (normalizablePages.length === 0) {
|
|
|
- // socket.emit('normalizeParentRecursivelyByPages', { error: err.message }); TODO: use socket to tell user
|
|
|
+ socket.emit(SocketEventName.PageMigrationError);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
if (nonNormalizablePages.length !== 0) {
|
|
|
- // TODO: iterate nonNormalizablePages and send socket error to client so that the user can know which path failed to migrate
|
|
|
- // socket.emit('normalizeParentRecursivelyByPages', { error: err.message }); TODO: use socket to tell user
|
|
|
+ const nonNormalizablePagePaths: string[] = nonNormalizablePages.map(p => p.path);
|
|
|
+ socket.emit(SocketEventName.PageMigrationError, { paths: nonNormalizablePagePaths });
|
|
|
+ logger.debug('Some pages could not be converted.', nonNormalizablePagePaths);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Main Operation (s)
|
|
|
*/
|
|
|
+ const errorPagePaths: string[] = [];
|
|
|
for await (const page of normalizablePages) {
|
|
|
const canOperate = await this.crowi.pageOperationService.canOperate(true, page.path, page.path);
|
|
|
if (!canOperate) {
|
|
|
+ errorPagePaths.push(page.path);
|
|
|
throw Error(`Cannot operate normalizeParentRecursiively to path "${page.path}" right now.`);
|
|
|
}
|
|
|
|
|
|
@@ -2459,6 +2471,7 @@ class PageService {
|
|
|
const existingPage = await builder.query.exec();
|
|
|
|
|
|
if (existingPage?.parent != null) {
|
|
|
+ errorPagePaths.push(page.path);
|
|
|
throw Error('This page has already converted.');
|
|
|
}
|
|
|
|
|
|
@@ -2474,6 +2487,7 @@ class PageService {
|
|
|
});
|
|
|
}
|
|
|
catch (err) {
|
|
|
+ errorPagePaths.push(page.path);
|
|
|
logger.error('Failed to create PageOperation document.', err);
|
|
|
throw err;
|
|
|
}
|
|
|
@@ -2482,10 +2496,17 @@ class PageService {
|
|
|
await this.normalizeParentRecursivelyMainOperation(page, user, pageOp._id);
|
|
|
}
|
|
|
catch (err) {
|
|
|
+ errorPagePaths.push(page.path);
|
|
|
logger.err('Failed to run normalizeParentRecursivelyMainOperation.', err);
|
|
|
throw err;
|
|
|
}
|
|
|
}
|
|
|
+ if (errorPagePaths.length === 0) {
|
|
|
+ socket.emit(SocketEventName.PageMigrationSuccess);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ socket.emit(SocketEventName.PageMigrationError, { paths: errorPagePaths });
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
async normalizeParentRecursivelyMainOperation(page, user, pageOpId: ObjectIdLike): Promise<void> {
|