|
@@ -1,12 +1,15 @@
|
|
|
import { pagePathUtils } from '@growi/core';
|
|
import { pagePathUtils } from '@growi/core';
|
|
|
import loggerFactory from '~/utils/logger';
|
|
import loggerFactory from '~/utils/logger';
|
|
|
|
|
+import ActivityDefine from '../util/activityDefine';
|
|
|
|
|
+
|
|
|
|
|
+import { stringifySnapshot } from '~/models/serializers/in-app-notification-snapshot/page';
|
|
|
|
|
|
|
|
const mongoose = require('mongoose');
|
|
const mongoose = require('mongoose');
|
|
|
const escapeStringRegexp = require('escape-string-regexp');
|
|
const escapeStringRegexp = require('escape-string-regexp');
|
|
|
const streamToPromise = require('stream-to-promise');
|
|
const streamToPromise = require('stream-to-promise');
|
|
|
|
|
|
|
|
-const logger = loggerFactory('growi:models:page');
|
|
|
|
|
-const debug = require('debug')('growi:models:page');
|
|
|
|
|
|
|
+const logger = loggerFactory('growi:service:page');
|
|
|
|
|
+const debug = require('debug')('growi:service:page');
|
|
|
const { Writable } = require('stream');
|
|
const { Writable } = require('stream');
|
|
|
const { createBatchStream } = require('~/server/util/batch-stream');
|
|
const { createBatchStream } = require('~/server/util/batch-stream');
|
|
|
|
|
|
|
@@ -22,9 +25,78 @@ class PageService {
|
|
|
this.pageEvent = crowi.event('page');
|
|
this.pageEvent = crowi.event('page');
|
|
|
|
|
|
|
|
// init
|
|
// init
|
|
|
|
|
+ this.initPageEvent();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ initPageEvent() {
|
|
|
|
|
+ // create
|
|
|
this.pageEvent.on('create', this.pageEvent.onCreate);
|
|
this.pageEvent.on('create', this.pageEvent.onCreate);
|
|
|
- this.pageEvent.on('update', this.pageEvent.onUpdate);
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // createMany
|
|
|
this.pageEvent.on('createMany', this.pageEvent.onCreateMany);
|
|
this.pageEvent.on('createMany', this.pageEvent.onCreateMany);
|
|
|
|
|
+
|
|
|
|
|
+ // update
|
|
|
|
|
+ this.pageEvent.on('update', async(page, user) => {
|
|
|
|
|
+
|
|
|
|
|
+ this.pageEvent.onUpdate();
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ await this.createAndSendNotifications(page, user, ActivityDefine.ACTION_PAGE_UPDATE);
|
|
|
|
|
+ }
|
|
|
|
|
+ catch (err) {
|
|
|
|
|
+ logger.error(err);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // rename
|
|
|
|
|
+ this.pageEvent.on('rename', async(page, user) => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ await this.createAndSendNotifications(page, user, ActivityDefine.ACTION_PAGE_RENAME);
|
|
|
|
|
+ }
|
|
|
|
|
+ catch (err) {
|
|
|
|
|
+ logger.error(err);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // delete
|
|
|
|
|
+ this.pageEvent.on('delete', async(page, user) => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ await this.createAndSendNotifications(page, user, ActivityDefine.ACTION_PAGE_DELETE);
|
|
|
|
|
+ }
|
|
|
|
|
+ catch (err) {
|
|
|
|
|
+ logger.error(err);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // delete completely
|
|
|
|
|
+ this.pageEvent.on('deleteCompletely', async(page, user) => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ await this.createAndSendNotifications(page, user, ActivityDefine.ACTION_PAGE_DELETE_COMPLETELY);
|
|
|
|
|
+ }
|
|
|
|
|
+ catch (err) {
|
|
|
|
|
+ logger.error(err);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // likes
|
|
|
|
|
+ this.pageEvent.on('like', async(page, user) => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ await this.createAndSendNotifications(page, user, ActivityDefine.ACTION_PAGE_LIKE);
|
|
|
|
|
+ }
|
|
|
|
|
+ catch (err) {
|
|
|
|
|
+ logger.error(err);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // bookmark
|
|
|
|
|
+ this.pageEvent.on('bookmark', async(page, user) => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ await this.createAndSendNotifications(page, user, ActivityDefine.ACTION_PAGE_BOOKMARK);
|
|
|
|
|
+ }
|
|
|
|
|
+ catch (err) {
|
|
|
|
|
+ logger.error(err);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
async findPageAndMetaDataByViewer({ pageId, path, user }) {
|
|
async findPageAndMetaDataByViewer({ pageId, path, user }) {
|
|
@@ -141,8 +213,7 @@ class PageService {
|
|
|
await Page.create(path, body, user, { redirectTo: newPagePath });
|
|
await Page.create(path, body, user, { redirectTo: newPagePath });
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- this.pageEvent.emit('delete', page, user);
|
|
|
|
|
- this.pageEvent.emit('create', renamedPage, user);
|
|
|
|
|
|
|
+ this.pageEvent.emit('rename', page, user);
|
|
|
|
|
|
|
|
return renamedPage;
|
|
return renamedPage;
|
|
|
}
|
|
}
|
|
@@ -232,7 +303,7 @@ class PageService {
|
|
|
logger.debug(`Reverting pages has completed: (totalCount=${count})`);
|
|
logger.debug(`Reverting pages has completed: (totalCount=${count})`);
|
|
|
// update path
|
|
// update path
|
|
|
targetPage.path = newPagePath;
|
|
targetPage.path = newPagePath;
|
|
|
- pageEvent.emit('syncDescendants', targetPage, user);
|
|
|
|
|
|
|
+ pageEvent.emit('syncDescendantsUpdate', targetPage, user);
|
|
|
callback();
|
|
callback();
|
|
|
},
|
|
},
|
|
|
});
|
|
});
|
|
@@ -428,7 +499,7 @@ class PageService {
|
|
|
logger.debug(`Adding pages has completed: (totalCount=${count})`);
|
|
logger.debug(`Adding pages has completed: (totalCount=${count})`);
|
|
|
// update path
|
|
// update path
|
|
|
page.path = newPagePath;
|
|
page.path = newPagePath;
|
|
|
- pageEvent.emit('syncDescendants', page, user);
|
|
|
|
|
|
|
+ pageEvent.emit('syncDescendantsUpdate', page, user);
|
|
|
callback();
|
|
callback();
|
|
|
},
|
|
},
|
|
|
});
|
|
});
|
|
@@ -524,6 +595,9 @@ class PageService {
|
|
|
throw new Error('Failed to revert pages: ', err);
|
|
throw new Error('Failed to revert pages: ', err);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+ finally {
|
|
|
|
|
+ this.pageEvent.emit('syncDescendantsDelete', pages, user);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -570,12 +644,12 @@ class PageService {
|
|
|
|
|
|
|
|
await this.deleteCompletelyOperation(ids, paths);
|
|
await this.deleteCompletelyOperation(ids, paths);
|
|
|
|
|
|
|
|
- this.pageEvent.emit('deleteCompletely', pages, user); // update as renamed page
|
|
|
|
|
|
|
+ this.pageEvent.emit('syncDescendantsDelete', pages, user); // update as renamed page
|
|
|
|
|
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- async deleteCompletely(page, user, options = {}, isRecursively = false) {
|
|
|
|
|
|
|
+ async deleteCompletely(page, user, options = {}, isRecursively = false, preventEmitting = false) {
|
|
|
const ids = [page._id];
|
|
const ids = [page._id];
|
|
|
const paths = [page.path];
|
|
const paths = [page.path];
|
|
|
|
|
|
|
@@ -587,7 +661,9 @@ class PageService {
|
|
|
this.deleteCompletelyDescendantsWithStream(page, user, options);
|
|
this.deleteCompletelyDescendantsWithStream(page, user, options);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- this.pageEvent.emit('delete', page, user); // update as renamed page
|
|
|
|
|
|
|
+ if (!preventEmitting) {
|
|
|
|
|
+ this.pageEvent.emit('deleteCompletely', page, user);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
@@ -690,7 +766,9 @@ class PageService {
|
|
|
if (originPage.redirectTo !== page.path) {
|
|
if (originPage.redirectTo !== page.path) {
|
|
|
throw new Error('The new page of to revert is exists and the redirect path of the page is not the deleted page.');
|
|
throw new Error('The new page of to revert is exists and the redirect path of the page is not the deleted page.');
|
|
|
}
|
|
}
|
|
|
- await this.deleteCompletely(originPage, options);
|
|
|
|
|
|
|
+
|
|
|
|
|
+ await this.deleteCompletely(originPage, user, options, false, true);
|
|
|
|
|
+ this.pageEvent.emit('revert', page, user);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (isRecursively) {
|
|
if (isRecursively) {
|
|
@@ -774,6 +852,28 @@ class PageService {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ createAndSendNotifications = async function(page, user, action) {
|
|
|
|
|
+ const { activityService, inAppNotificationService } = this.crowi;
|
|
|
|
|
+
|
|
|
|
|
+ const snapshot = stringifySnapshot(page);
|
|
|
|
|
+
|
|
|
|
|
+ // Create activity
|
|
|
|
|
+ const parameters = {
|
|
|
|
|
+ user: user._id,
|
|
|
|
|
+ targetModel: ActivityDefine.MODEL_PAGE,
|
|
|
|
|
+ target: page,
|
|
|
|
|
+ action,
|
|
|
|
|
+ };
|
|
|
|
|
+ const activity = await activityService.createByParameters(parameters);
|
|
|
|
|
+
|
|
|
|
|
+ // Get user to be notified
|
|
|
|
|
+ const targetUsers = await activity.getNotificationTargetUsers();
|
|
|
|
|
+
|
|
|
|
|
+ // Create and send notifications
|
|
|
|
|
+ await inAppNotificationService.upsertByActivity(targetUsers, activity, snapshot);
|
|
|
|
|
+ await inAppNotificationService.emitSocketIo(targetUsers);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
module.exports = PageService;
|
|
module.exports = PageService;
|