| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228 |
- import { DeleteWriteOpResultObject } from 'mongodb';
- import {
- Types, Document, Model, Schema, model,
- } from 'mongoose';
- import loggerFactory from '~/utils/logger';
- import ActivityDefine from '../util/activityDefine';
- import Crowi from '../crowi';
- const logger = loggerFactory('growi:models:activity');
- export interface ActivityDocument extends Document {
- _id: Types.ObjectId
- user: Types.ObjectId | any
- targetModel: string
- target: string
- action: string
- event: Types.ObjectId
- eventModel: string
- createdAt: Date
- getNotificationTargetUsers(): Promise<any[]>
- }
- export interface ActivityModel extends Model<ActivityDocument> {
- createByParameters(parameters: any): Promise<ActivityDocument>
- removeByParameters(parameters: any): any
- createByPageComment(comment: any): Promise<ActivityDocument>
- createByPageLike(page: any, user: any): Promise<ActivityDocument>
- removeByPageUnlike(page: any, user: any): Promise<DeleteWriteOpResultObject['result']>
- removeByPage(page: any): Promise<DeleteWriteOpResultObject['result']>
- findByUser(user: any): Promise<ActivityDocument[]>
- getActionUsersFromActivities(activities: ActivityDocument[]): any[]
- }
- export default (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 Activity.create(parameters);
- };
- /**
- * @param {object} parameters
- */
- activitySchema.statics.removeByParameters = async function(parameters) {
- const activity = await Activity.findOne(parameters);
- activityEvent.emit('remove', activity);
- return Activity.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,
- };
- 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.removeByPageUnlike = function(page, user) {
- const parameters = {
- user,
- targetModel: ActivityDefine.MODEL_PAGE,
- target: page,
- action: ActivityDefine.ACTION_LIKE,
- };
- return this.removeByParameters(parameters);
- };
- /**
- * @param {Page} page
- * @return {Promise}
- */
- activitySchema.statics.removeByPage = async function(page) {
- const activities = await Activity.find({ target: page });
- for (const activity of activities) {
- activityEvent.emit('remove', activity);
- }
- return Activity.deleteMany({ target: page }).exec();
- };
- /**
- * @param {User} user
- * @return {Promise}
- */
- activitySchema.statics.findByUser = function(user) {
- return Activity.find({ user }).sort({ createdAt: -1 }).exec();
- };
- activitySchema.statics.getActionUsersFromActivities = function(activities) {
- return activities.map(({ user }) => user).filter((user, i, self) => self.indexOf(user) === i);
- };
- activitySchema.methods.getNotificationTargetUsers = async function() {
- const User = crowi.model('User');
- const Watcher = crowi.model('Watcher');
- 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;
- };
- /**
- * saved hook
- */
- activitySchema.post('save', async(savedActivity: ActivityDocument) => {
- const Notification = crowi.model('Notification');
- try {
- const notificationUsers = await savedActivity.getNotificationTargetUsers();
- await Promise.all(notificationUsers.map(user => Notification.upsertByActivity(user, savedActivity)));
- return;
- }
- catch (err) {
- logger.error(err);
- }
- });
- // 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) => {
- const Notification = crowi.model('Notification');
- try {
- await Notification.removeActivity(activity);
- }
- catch (err) {
- logger.error(err);
- }
- });
- const Activity = model<ActivityDocument, ActivityModel>('Activity', activitySchema);
- return Activity;
- };
|