|
|
@@ -2,6 +2,7 @@ import { DeleteWriteOpResultObject } from 'mongodb';
|
|
|
import {
|
|
|
Types, Document, Model, Schema,
|
|
|
} from 'mongoose';
|
|
|
+import Crowi from '../crowi';
|
|
|
|
|
|
import { getOrCreateModel, getModelSafely } from '../util/mongoose-utils';
|
|
|
import loggerFactory from '../../utils/logger';
|
|
|
@@ -9,10 +10,11 @@ import ActivityDefine from '../util/activityDefine';
|
|
|
|
|
|
import Watcher from './watcher';
|
|
|
// import { InAppNotification } from './in-app-notification';
|
|
|
-// import activityEvent from '../events/activity';
|
|
|
|
|
|
const logger = loggerFactory('growi:models:activity');
|
|
|
|
|
|
+const mongoose = require('mongoose');
|
|
|
+
|
|
|
export interface ActivityDocument extends Document {
|
|
|
_id: Types.ObjectId
|
|
|
user: Types.ObjectId | any
|
|
|
@@ -37,195 +39,180 @@ export interface ActivityModel extends Model<ActivityDocument> {
|
|
|
getActionUsersFromActivities(activities: ActivityDocument[]): any[]
|
|
|
}
|
|
|
|
|
|
-// const activityEvent = crowi.event('Activity');
|
|
|
-
|
|
|
-// TODO: add revision id
|
|
|
-const activitySchema = new Schema<ActivityDocument, ActivityModel>({
|
|
|
- user: {
|
|
|
- type: Schema.Types.ObjectId,
|
|
|
- ref: 'User',
|
|
|
- index: true,
|
|
|
- require: true,
|
|
|
- },
|
|
|
- targetModel: {
|
|
|
- type: String,
|
|
|
- require: true,
|
|
|
- enum: ActivityDefine.getSupportTargetModelNames(),
|
|
|
- },
|
|
|
- target: {
|
|
|
- type: Schema.Types.ObjectId,
|
|
|
- refPath: 'targetModel',
|
|
|
- require: true,
|
|
|
- },
|
|
|
- action: {
|
|
|
- type: String,
|
|
|
- require: true,
|
|
|
- enum: ActivityDefine.getSupportActionNames(),
|
|
|
- },
|
|
|
- event: {
|
|
|
- type: Schema.Types.ObjectId,
|
|
|
- refPath: 'eventModel',
|
|
|
- },
|
|
|
- eventModel: {
|
|
|
- type: String,
|
|
|
- enum: ActivityDefine.getSupportEventModelNames(),
|
|
|
- },
|
|
|
- createdAt: {
|
|
|
- type: Date,
|
|
|
- default: Date.now,
|
|
|
- },
|
|
|
-});
|
|
|
-activitySchema.index({ target: 1, action: 1 });
|
|
|
-activitySchema.index({
|
|
|
- user: 1, target: 1, action: 1, createdAt: 1,
|
|
|
-}, { unique: true });
|
|
|
-
|
|
|
-/**
|
|
|
- * @param {object} parameters
|
|
|
- * @return {Promise}
|
|
|
- */
|
|
|
-activitySchema.statics.createByParameters = function(parameters) {
|
|
|
- return this.create(parameters);
|
|
|
-};
|
|
|
+module.exports = function(crowi: Crowi) {
|
|
|
+ const activityEvent = crowi.event('activity');
|
|
|
+
|
|
|
+ // TODO: add revision id
|
|
|
+ const activitySchema = new Schema<ActivityDocument, ActivityModel>({
|
|
|
+ user: {
|
|
|
+ type: Schema.Types.ObjectId,
|
|
|
+ ref: 'User',
|
|
|
+ index: true,
|
|
|
+ require: true,
|
|
|
+ },
|
|
|
+ targetModel: {
|
|
|
+ type: String,
|
|
|
+ require: true,
|
|
|
+ enum: ActivityDefine.getSupportTargetModelNames(),
|
|
|
+ },
|
|
|
+ target: {
|
|
|
+ type: Schema.Types.ObjectId,
|
|
|
+ refPath: 'targetModel',
|
|
|
+ require: true,
|
|
|
+ },
|
|
|
+ action: {
|
|
|
+ type: String,
|
|
|
+ require: true,
|
|
|
+ enum: ActivityDefine.getSupportActionNames(),
|
|
|
+ },
|
|
|
+ event: {
|
|
|
+ type: Schema.Types.ObjectId,
|
|
|
+ refPath: 'eventModel',
|
|
|
+ },
|
|
|
+ eventModel: {
|
|
|
+ type: String,
|
|
|
+ enum: ActivityDefine.getSupportEventModelNames(),
|
|
|
+ },
|
|
|
+ createdAt: {
|
|
|
+ type: Date,
|
|
|
+ default: Date.now,
|
|
|
+ },
|
|
|
+ });
|
|
|
+ activitySchema.index({ target: 1, action: 1 });
|
|
|
+ activitySchema.index({
|
|
|
+ user: 1, target: 1, action: 1, createdAt: 1,
|
|
|
+ }, { unique: true });
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @param {object} parameters
|
|
|
+ * @return {Promise}
|
|
|
+ */
|
|
|
+ activitySchema.statics.createByParameters = function(parameters) {
|
|
|
+ return this.create(parameters);
|
|
|
+ };
|
|
|
|
|
|
-/**
|
|
|
- * @param {object} parameters
|
|
|
- */
|
|
|
-activitySchema.statics.removeByParameters = async function(parameters) {
|
|
|
- const activity = await this.findOne(parameters);
|
|
|
- // activityEvent.emit('remove', activity);
|
|
|
+ /**
|
|
|
+ * @param {object} parameters
|
|
|
+ */
|
|
|
+ activitySchema.statics.removeByParameters = async function(parameters) {
|
|
|
+ const activity = await this.findOne(parameters);
|
|
|
+ activityEvent.emit('remove', activity);
|
|
|
|
|
|
- return this.deleteMany(parameters).exec();
|
|
|
-};
|
|
|
+ return this.deleteMany(parameters).exec();
|
|
|
+ };
|
|
|
|
|
|
-/**
|
|
|
- * @param {Comment} comment
|
|
|
- * @return {Promise}
|
|
|
- */
|
|
|
-activitySchema.statics.createByPageComment = function(comment) {
|
|
|
- const parameters = {
|
|
|
- user: comment.creator,
|
|
|
- targetModel: ActivityDefine.MODEL_PAGE,
|
|
|
- target: comment.page,
|
|
|
- eventModel: ActivityDefine.MODEL_COMMENT,
|
|
|
- event: comment._id,
|
|
|
- action: ActivityDefine.ACTION_COMMENT,
|
|
|
+ /**
|
|
|
+ * @param {Comment} comment
|
|
|
+ * @return {Promise}
|
|
|
+ */
|
|
|
+ activitySchema.statics.createByPageComment = function(comment) {
|
|
|
+ const parameters = {
|
|
|
+ user: comment.creator,
|
|
|
+ targetModel: ActivityDefine.MODEL_PAGE,
|
|
|
+ target: comment.page,
|
|
|
+ eventModel: ActivityDefine.MODEL_COMMENT,
|
|
|
+ event: comment._id,
|
|
|
+ action: ActivityDefine.ACTION_COMMENT,
|
|
|
+ };
|
|
|
+
|
|
|
+ return this.createByParameters(parameters);
|
|
|
};
|
|
|
|
|
|
- return this.createByParameters(parameters);
|
|
|
-};
|
|
|
+ /**
|
|
|
+ * @param {Page} page
|
|
|
+ * @param {User} user
|
|
|
+ * @return {Promise}
|
|
|
+ */
|
|
|
+ activitySchema.statics.createByPageLike = function(page, user) {
|
|
|
+ const parameters = {
|
|
|
+ user: user._id,
|
|
|
+ targetModel: ActivityDefine.MODEL_PAGE,
|
|
|
+ target: page,
|
|
|
+ action: ActivityDefine.ACTION_LIKE,
|
|
|
+ };
|
|
|
+
|
|
|
+ return this.createByParameters(parameters);
|
|
|
+ };
|
|
|
|
|
|
-/**
|
|
|
- * @param {Page} page
|
|
|
- * @param {User} user
|
|
|
- * @return {Promise}
|
|
|
- */
|
|
|
-activitySchema.statics.createByPageLike = function(page, user) {
|
|
|
- const parameters = {
|
|
|
- user: user._id,
|
|
|
- targetModel: ActivityDefine.MODEL_PAGE,
|
|
|
- target: page,
|
|
|
- action: ActivityDefine.ACTION_LIKE,
|
|
|
+ /**
|
|
|
+ * @param {Page} page
|
|
|
+ * @param {User} user
|
|
|
+ * @return {Promise}
|
|
|
+ */
|
|
|
+ activitySchema.statics.removeByPageUnlike = function(page, user) {
|
|
|
+ const parameters = {
|
|
|
+ user,
|
|
|
+ targetModel: ActivityDefine.MODEL_PAGE,
|
|
|
+ target: page,
|
|
|
+ action: ActivityDefine.ACTION_LIKE,
|
|
|
+ };
|
|
|
+
|
|
|
+ return this.removeByParameters(parameters);
|
|
|
};
|
|
|
|
|
|
- return this.createByParameters(parameters);
|
|
|
-};
|
|
|
+ /**
|
|
|
+ * @param {Page} page
|
|
|
+ *
|
|
|
+ * @return {Promise}
|
|
|
+ */
|
|
|
+ activitySchema.statics.removeByPage = async function(page) {
|
|
|
+ // const activityEvent = new ActivityEvent();
|
|
|
+ const activities = await this.find({ target: page });
|
|
|
+ for (const activity of activities) {
|
|
|
+ // TODO: implement removeActivity when page deleted by GW-7481
|
|
|
+ // activityEvent.emit('remove', activity);
|
|
|
+ }
|
|
|
+ return this.deleteMany({ target: page }).exec();
|
|
|
+ };
|
|
|
|
|
|
-/**
|
|
|
- * @param {Page} page
|
|
|
- * @param {User} user
|
|
|
- * @return {Promise}
|
|
|
- */
|
|
|
-activitySchema.statics.removeByPageUnlike = function(page, user) {
|
|
|
- const parameters = {
|
|
|
- user,
|
|
|
- targetModel: ActivityDefine.MODEL_PAGE,
|
|
|
- target: page,
|
|
|
- action: ActivityDefine.ACTION_LIKE,
|
|
|
+ /**
|
|
|
+ * @param {User} user
|
|
|
+ * @return {Promise}
|
|
|
+ */
|
|
|
+ activitySchema.statics.findByUser = function(user) {
|
|
|
+ return this.find({ user }).sort({ createdAt: -1 }).exec();
|
|
|
};
|
|
|
|
|
|
- return this.removeByParameters(parameters);
|
|
|
-};
|
|
|
+ activitySchema.statics.getActionUsersFromActivities = function(activities) {
|
|
|
+ return activities.map(({ user }) => user).filter((user, i, self) => self.indexOf(user) === i);
|
|
|
+ };
|
|
|
|
|
|
-/**
|
|
|
- * @param {Page} page
|
|
|
- * @return {Promise}
|
|
|
- */
|
|
|
-activitySchema.statics.removeByPage = async function(page) {
|
|
|
- const activities = await this.find({ target: page });
|
|
|
- for (const activity of activities) {
|
|
|
- // activityEvent.emit('remove', activity);
|
|
|
- }
|
|
|
- return this.deleteMany({ target: page }).exec();
|
|
|
-};
|
|
|
+ activitySchema.methods.getNotificationTargetUsers = async function() {
|
|
|
+ const User = getModelSafely('User') || require('~/server/models/user')();
|
|
|
+ const { user: actionUser, targetModel, target } = this;
|
|
|
+
|
|
|
+ const model: any = await this.model(targetModel).findById(target);
|
|
|
+ const [targetUsers, watchUsers, ignoreUsers] = await Promise.all([
|
|
|
+ model.getNotificationTargetUsers(),
|
|
|
+ Watcher.getWatchers((target as any) as Types.ObjectId),
|
|
|
+ Watcher.getIgnorers((target as any) as Types.ObjectId),
|
|
|
+ ]);
|
|
|
+
|
|
|
+ const unique = array => Object.values(array.reduce((objects, object) => ({ ...objects, [object.toString()]: object }), {}));
|
|
|
+ const filter = (array, pull) => {
|
|
|
+ const ids = pull.map(object => object.toString());
|
|
|
+ return array.filter(object => !ids.includes(object.toString()));
|
|
|
+ };
|
|
|
+ const notificationUsers = filter(unique([...targetUsers, ...watchUsers]), [...ignoreUsers, actionUser]);
|
|
|
+ const activeNotificationUsers = await User.find({
|
|
|
+ _id: { $in: notificationUsers },
|
|
|
+ status: User.STATUS_ACTIVE,
|
|
|
+ }).distinct('_id');
|
|
|
+ return activeNotificationUsers;
|
|
|
+ };
|
|
|
|
|
|
-/**
|
|
|
- * @param {User} user
|
|
|
- * @return {Promise}
|
|
|
- */
|
|
|
-activitySchema.statics.findByUser = function(user) {
|
|
|
- return this.find({ user }).sort({ createdAt: -1 }).exec();
|
|
|
-};
|
|
|
+ activitySchema.post('save', async(savedActivity: ActivityDocument) => {
|
|
|
+ let targetUsers: Types.ObjectId[] = [];
|
|
|
+ try {
|
|
|
+ targetUsers = await savedActivity.getNotificationTargetUsers();
|
|
|
+ }
|
|
|
+ catch (err) {
|
|
|
+ logger.error(err);
|
|
|
+ }
|
|
|
|
|
|
-activitySchema.statics.getActionUsersFromActivities = function(activities) {
|
|
|
- return activities.map(({ user }) => user).filter((user, i, self) => self.indexOf(user) === i);
|
|
|
-};
|
|
|
+ activityEvent.emit('create', targetUsers, savedActivity);
|
|
|
+ });
|
|
|
|
|
|
-activitySchema.methods.getNotificationTargetUsers = async function() {
|
|
|
- const User = getModelSafely('User') || require('~/server/models/user')();
|
|
|
- const { user: actionUser, targetModel, target } = this;
|
|
|
-
|
|
|
- const model: any = await this.model(targetModel).findById(target);
|
|
|
- const [targetUsers, watchUsers, ignoreUsers] = await Promise.all([
|
|
|
- model.getNotificationTargetUsers(),
|
|
|
- Watcher.getWatchers((target as any) as Types.ObjectId),
|
|
|
- Watcher.getIgnorers((target as any) as Types.ObjectId),
|
|
|
- ]);
|
|
|
-
|
|
|
- const unique = array => Object.values(array.reduce((objects, object) => ({ ...objects, [object.toString()]: object }), {}));
|
|
|
- const filter = (array, pull) => {
|
|
|
- const ids = pull.map(object => object.toString());
|
|
|
- return array.filter(object => !ids.includes(object.toString()));
|
|
|
- };
|
|
|
- const notificationUsers = filter(unique([...targetUsers, ...watchUsers]), [...ignoreUsers, actionUser]);
|
|
|
- const activeNotificationUsers = await User.find({
|
|
|
- _id: { $in: notificationUsers },
|
|
|
- status: User.STATUS_ACTIVE,
|
|
|
- }).distinct('_id');
|
|
|
- return activeNotificationUsers;
|
|
|
-};
|
|
|
+ return getOrCreateModel<ActivityDocument, ActivityModel>('Activity', activitySchema);
|
|
|
|
|
|
-/**
|
|
|
- * saved hook TODO: getNotificationTargetUsers by GW-7346
|
|
|
- */
|
|
|
-activitySchema.post('save', async(savedActivity: ActivityDocument) => {
|
|
|
- try {
|
|
|
- // const notificationUsers = await savedActivity.getNotificationTargetUsers();
|
|
|
-
|
|
|
- // await Promise.all(notificationUsers.map(user => InAppNotification.upsertByActivity(user, savedActivity)));
|
|
|
- return;
|
|
|
- }
|
|
|
- catch (err) {
|
|
|
- logger.error(err);
|
|
|
- }
|
|
|
-});
|
|
|
-
|
|
|
-
|
|
|
-/**
|
|
|
- * TODO: improve removeActivity that decleard in InAppNotificationService by GW-7481
|
|
|
- */
|
|
|
-
|
|
|
-// because mongoose's 'remove' hook fired only when remove by a method of Document (not by a Model method)
|
|
|
-// move 'save' hook from mongoose's events to activityEvent if I have a time.
|
|
|
-// activityEvent.on('remove', async(activity: ActivityDocument) => {
|
|
|
-
|
|
|
-// try {
|
|
|
-// await InAppNotification.removeActivity(activity);
|
|
|
-// }
|
|
|
-// catch (err) {
|
|
|
-// logger.error(err);
|
|
|
-// }
|
|
|
-// });
|
|
|
-
|
|
|
-const Activity = getOrCreateModel<ActivityDocument, ActivityModel>('Activity', activitySchema);
|
|
|
-export { Activity };
|
|
|
+};
|