| 
									
										
										
										
											2016-11-23 19:41:35 -05:00
										 |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var async = require('async'); | 
					
						
							|  |  |  | var winston = require('winston'); | 
					
						
							|  |  |  | var db = require('./database'); | 
					
						
							|  |  |  | var user = require('./user'); | 
					
						
							| 
									
										
										
										
											2016-12-06 16:11:56 -05:00
										 |  |  | var groups = require('./groups'); | 
					
						
							|  |  |  | var meta = require('./meta'); | 
					
						
							|  |  |  | var notifications = require('./notifications'); | 
					
						
							| 
									
										
										
										
											2016-11-23 19:41:35 -05:00
										 |  |  | var analytics = require('./analytics'); | 
					
						
							|  |  |  | var topics = require('./topics'); | 
					
						
							|  |  |  | var posts = require('./posts'); | 
					
						
							| 
									
										
										
										
											2016-12-06 16:11:56 -05:00
										 |  |  | var privileges = require('./privileges'); | 
					
						
							|  |  |  | var plugins = require('./plugins'); | 
					
						
							| 
									
										
										
										
											2016-11-23 19:41:35 -05:00
										 |  |  | var utils = require('../public/src/utils'); | 
					
						
							| 
									
										
										
										
											2016-11-30 20:34:06 -05:00
										 |  |  | var _ = require('underscore'); | 
					
						
							| 
									
										
										
										
											2016-12-06 16:11:56 -05:00
										 |  |  | var S = require('string'); | 
					
						
							| 
									
										
										
										
											2016-11-23 19:41:35 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-06 16:11:56 -05:00
										 |  |  | var Flags = {}; | 
					
						
							| 
									
										
										
										
											2016-11-23 19:41:35 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-25 14:17:51 -05:00
										 |  |  | Flags.get = function (flagId, callback) { | 
					
						
							|  |  |  | 	async.waterfall([ | 
					
						
							| 
									
										
										
										
											2016-11-29 22:10:51 -05:00
										 |  |  | 		// First stage
 | 
					
						
							| 
									
										
										
										
											2016-11-25 14:17:51 -05:00
										 |  |  | 		async.apply(async.parallel, { | 
					
						
							|  |  |  | 			base: async.apply(db.getObject.bind(db), 'flag:' + flagId), | 
					
						
							| 
									
										
										
										
											2016-12-01 16:22:10 -05:00
										 |  |  | 			history: async.apply(Flags.getHistory, flagId), | 
					
						
							| 
									
										
										
										
											2016-11-30 20:34:06 -05:00
										 |  |  | 			notes: async.apply(Flags.getNotes, flagId) | 
					
						
							| 
									
										
										
										
											2016-11-25 14:17:51 -05:00
										 |  |  | 		}), | 
					
						
							|  |  |  | 		function (data, next) { | 
					
						
							| 
									
										
										
										
											2016-11-29 22:10:51 -05:00
										 |  |  | 			// Second stage
 | 
					
						
							|  |  |  | 			async.parallel({ | 
					
						
							| 
									
										
										
										
											2016-12-02 11:24:12 -05:00
										 |  |  | 				userObj: async.apply(user.getUserFields, data.base.uid, ['username', 'userslug', 'picture']), | 
					
						
							| 
									
										
										
										
											2016-11-29 22:10:51 -05:00
										 |  |  | 				targetObj: async.apply(Flags.getTarget, data.base.type, data.base.targetId, data.base.uid) | 
					
						
							|  |  |  | 			}, function (err, payload) { | 
					
						
							|  |  |  | 				// Final object return construction
 | 
					
						
							| 
									
										
										
										
											2016-11-25 14:17:51 -05:00
										 |  |  | 				next(err, Object.assign(data.base, { | 
					
						
							| 
									
										
										
										
											2016-12-19 12:13:36 -05:00
										 |  |  | 					datetimeISO: new Date(parseInt(data.base.datetime, 10)).toISOString(), | 
					
						
							| 
									
										
										
										
											2016-11-29 22:10:51 -05:00
										 |  |  | 					target_readable: data.base.type.charAt(0).toUpperCase() + data.base.type.slice(1) + ' ' + data.base.targetId, | 
					
						
							|  |  |  | 					target: payload.targetObj, | 
					
						
							| 
									
										
										
										
											2016-11-25 14:17:51 -05:00
										 |  |  | 					history: data.history, | 
					
						
							|  |  |  | 					notes: data.notes, | 
					
						
							| 
									
										
										
										
											2016-12-02 11:24:12 -05:00
										 |  |  | 					reporter: payload.userObj | 
					
						
							| 
									
										
										
										
											2016-11-25 14:17:51 -05:00
										 |  |  | 				})); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	], callback); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-05 12:40:25 -05:00
										 |  |  | Flags.list = function (filters, uid, callback) { | 
					
						
							| 
									
										
										
										
											2016-12-14 15:00:41 -05:00
										 |  |  | 	if (typeof filters === 'function' && !uid && !callback) { | 
					
						
							| 
									
										
										
										
											2016-11-25 12:43:10 -05:00
										 |  |  | 		callback = filters; | 
					
						
							|  |  |  | 		filters = {}; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-02 15:28:28 -05:00
										 |  |  | 	var sets = []; | 
					
						
							| 
									
										
										
										
											2017-01-03 13:38:06 -05:00
										 |  |  | 	var orSets = []; | 
					
						
							|  |  |  | 	var prepareSets = function (setPrefix, value) { | 
					
						
							|  |  |  | 		if (!Array.isArray(value)) { | 
					
						
							|  |  |  | 			sets.push(setPrefix + value); | 
					
						
							|  |  |  | 		} else if (value.length) { | 
					
						
							|  |  |  | 			value.forEach(function (x) { | 
					
						
							|  |  |  | 				orSets.push(setPrefix + x); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			// Empty array, do nothing
 | 
					
						
							|  |  |  | 			return; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-02 15:28:28 -05:00
										 |  |  | 	if (Object.keys(filters).length > 0) { | 
					
						
							|  |  |  | 		for (var type in filters) { | 
					
						
							|  |  |  | 			switch (type) { | 
					
						
							|  |  |  | 				case 'type': | 
					
						
							| 
									
										
										
										
											2017-01-03 13:38:06 -05:00
										 |  |  | 					prepareSets('flags:byType:', filters[type]); | 
					
						
							| 
									
										
										
										
											2016-12-02 15:28:28 -05:00
										 |  |  | 					break; | 
					
						
							| 
									
										
										
										
											2016-12-05 15:32:58 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 				case 'state': | 
					
						
							| 
									
										
										
										
											2017-01-03 13:38:06 -05:00
										 |  |  | 					prepareSets('flags:byState:', filters[type]); | 
					
						
							| 
									
										
										
										
											2016-12-05 15:32:58 -05:00
										 |  |  | 					break; | 
					
						
							| 
									
										
										
										
											2016-12-02 15:28:28 -05:00
										 |  |  | 				 | 
					
						
							|  |  |  | 				case 'reporterId': | 
					
						
							| 
									
										
										
										
											2017-01-03 13:38:06 -05:00
										 |  |  | 					prepareSets('flags:byReporter:', filters[type]); | 
					
						
							| 
									
										
										
										
											2016-12-02 15:28:28 -05:00
										 |  |  | 					break; | 
					
						
							| 
									
										
										
										
											2016-12-05 12:40:25 -05:00
										 |  |  | 				 | 
					
						
							| 
									
										
										
										
											2016-12-05 15:32:58 -05:00
										 |  |  | 				case 'assignee': | 
					
						
							| 
									
										
										
										
											2017-01-03 13:38:06 -05:00
										 |  |  | 					prepareSets('flags:byAssignee:', filters[type]); | 
					
						
							| 
									
										
										
										
											2016-12-05 15:32:58 -05:00
										 |  |  | 					break; | 
					
						
							| 
									
										
										
										
											2016-12-14 15:53:57 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-06 16:11:56 -05:00
										 |  |  | 				case 'targetUid': | 
					
						
							| 
									
										
										
										
											2017-01-03 13:38:06 -05:00
										 |  |  | 					prepareSets('flags:byTargetUid:', filters[type]); | 
					
						
							| 
									
										
										
										
											2016-12-06 16:11:56 -05:00
										 |  |  | 					break; | 
					
						
							| 
									
										
										
										
											2016-12-05 15:32:58 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-14 15:53:57 -05:00
										 |  |  | 				case 'cid': | 
					
						
							| 
									
										
										
										
											2017-01-03 13:38:06 -05:00
										 |  |  | 					prepareSets('flags:byCid:', filters[type]); | 
					
						
							| 
									
										
										
										
											2016-12-14 15:53:57 -05:00
										 |  |  | 					break; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-05 12:40:25 -05:00
										 |  |  | 				case 'quick': | 
					
						
							|  |  |  | 					switch (filters.quick) { | 
					
						
							|  |  |  | 						case 'mine': | 
					
						
							|  |  |  | 							sets.push('flags:byAssignee:' + uid); | 
					
						
							|  |  |  | 							break; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					break; | 
					
						
							| 
									
										
										
										
											2016-12-02 15:28:28 -05:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-03 13:38:06 -05:00
										 |  |  | 	sets = (sets.length || orSets.length) ? sets : ['flags:datetime'];	// No filter default
 | 
					
						
							| 
									
										
										
										
											2016-12-02 15:28:28 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-25 12:43:10 -05:00
										 |  |  | 	async.waterfall([ | 
					
						
							| 
									
										
										
										
											2016-12-02 15:28:28 -05:00
										 |  |  | 		function (next) { | 
					
						
							|  |  |  | 			if (sets.length === 1) { | 
					
						
							| 
									
										
										
										
											2016-12-05 12:40:25 -05:00
										 |  |  | 				db.getSortedSetRevRange(sets[0], 0, -1, next); | 
					
						
							| 
									
										
										
										
											2017-01-03 13:38:06 -05:00
										 |  |  | 			} else if (sets.length > 1) { | 
					
						
							|  |  |  | 				db.getSortedSetRevIntersect({ sets: sets, start: 0, stop: -1, aggregate: 'MAX' }, next); | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				next(null, []); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		function (flagIds, next) { | 
					
						
							|  |  |  | 			// Find flags according to "or" rules, if any
 | 
					
						
							|  |  |  | 			if (orSets.length) { | 
					
						
							|  |  |  | 				db.getSortedSetRevUnion({ sets: orSets, start: 0, stop: -1, aggregate: 'MAX' }, function (err, _flagIds) { | 
					
						
							|  |  |  | 					if (err) { | 
					
						
							|  |  |  | 						return next(err); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					if (sets.length) { | 
					
						
							|  |  |  | 						// If flag ids are already present, return a subset of flags that are in both sets
 | 
					
						
							|  |  |  | 						next(null, _.intersection(flagIds, _flagIds)); | 
					
						
							|  |  |  | 					} else { | 
					
						
							|  |  |  | 						// Otherwise, return all flags returned via orSets
 | 
					
						
							|  |  |  | 						next(null, _.union(flagIds, _flagIds)); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				}); | 
					
						
							| 
									
										
										
										
											2016-12-02 15:28:28 -05:00
										 |  |  | 			} else { | 
					
						
							| 
									
										
										
										
											2017-01-03 13:38:06 -05:00
										 |  |  | 				setImmediate(next, null, flagIds); | 
					
						
							| 
									
										
										
										
											2016-12-02 15:28:28 -05:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2016-11-25 12:43:10 -05:00
										 |  |  | 		function (flagIds, next) { | 
					
						
							|  |  |  | 			async.map(flagIds, function (flagId, next) { | 
					
						
							|  |  |  | 				async.waterfall([ | 
					
						
							|  |  |  | 					async.apply(db.getObject, 'flag:' + flagId), | 
					
						
							|  |  |  | 					function (flagObj, next) { | 
					
						
							|  |  |  | 						user.getUserFields(flagObj.uid, ['username', 'picture'], function (err, userObj) { | 
					
						
							|  |  |  | 							next(err, Object.assign(flagObj, { | 
					
						
							| 
									
										
										
										
											2016-11-25 14:17:51 -05:00
										 |  |  | 								reporter: { | 
					
						
							| 
									
										
										
										
											2016-11-25 12:43:10 -05:00
										 |  |  | 									username: userObj.username, | 
					
						
							|  |  |  | 									picture: userObj.picture, | 
					
						
							|  |  |  | 									'icon:bgColor': userObj['icon:bgColor'], | 
					
						
							|  |  |  | 									'icon:text': userObj['icon:text'] | 
					
						
							|  |  |  | 								} | 
					
						
							|  |  |  | 							})); | 
					
						
							| 
									
										
										
										
											2016-11-25 15:09:52 -05:00
										 |  |  | 						}); | 
					
						
							| 
									
										
										
										
											2016-11-25 12:43:10 -05:00
										 |  |  | 					} | 
					
						
							|  |  |  | 				], function (err, flagObj) { | 
					
						
							|  |  |  | 					if (err) { | 
					
						
							|  |  |  | 						return next(err); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					switch(flagObj.state) { | 
					
						
							|  |  |  | 						case 'open': | 
					
						
							|  |  |  | 							flagObj.labelClass = 'info'; | 
					
						
							|  |  |  | 							break; | 
					
						
							|  |  |  | 						case 'wip': | 
					
						
							|  |  |  | 							flagObj.labelClass = 'warning'; | 
					
						
							|  |  |  | 							break; | 
					
						
							|  |  |  | 						case 'resolved': | 
					
						
							|  |  |  | 							flagObj.labelClass = 'success'; | 
					
						
							|  |  |  | 							break; | 
					
						
							|  |  |  | 						case 'rejected': | 
					
						
							|  |  |  | 							flagObj.labelClass = 'danger'; | 
					
						
							|  |  |  | 							break; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					next(null, Object.assign(flagObj, { | 
					
						
							| 
									
										
										
										
											2016-11-25 14:17:51 -05:00
										 |  |  | 						target_readable: flagObj.type.charAt(0).toUpperCase() + flagObj.type.slice(1) + ' ' + flagObj.targetId, | 
					
						
							| 
									
										
										
										
											2016-11-25 12:43:10 -05:00
										 |  |  | 						datetimeISO: new Date(parseInt(flagObj.datetime, 10)).toISOString() | 
					
						
							|  |  |  | 					})); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			},  next); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-12-13 09:36:46 -05:00
										 |  |  | 	], callback); | 
					
						
							| 
									
										
										
										
											2016-11-25 12:43:10 -05:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-06 16:11:56 -05:00
										 |  |  | Flags.validate = function (payload, callback) { | 
					
						
							|  |  |  | 	async.parallel({ | 
					
						
							|  |  |  | 		targetExists: async.apply(Flags.targetExists, payload.type, payload.id), | 
					
						
							|  |  |  | 		target: async.apply(Flags.getTarget, payload.type, payload.id, payload.uid), | 
					
						
							|  |  |  | 		reporter: async.apply(user.getUserData, payload.uid) | 
					
						
							|  |  |  | 	}, function (err, data) { | 
					
						
							|  |  |  | 		if (err) { | 
					
						
							|  |  |  | 			return callback(err); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-19 12:52:47 -05:00
										 |  |  | 		if (data.target.deleted) { | 
					
						
							| 
									
										
										
										
											2016-12-06 16:11:56 -05:00
										 |  |  | 			return callback(new Error('[[error:post-deleted]]')); | 
					
						
							| 
									
										
										
										
											2016-12-19 12:40:33 -05:00
										 |  |  | 		} else if (parseInt(data.reporter.banned, 10)) { | 
					
						
							| 
									
										
										
										
											2016-12-06 16:11:56 -05:00
										 |  |  | 			return callback(new Error('[[error:user-banned]]')); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		switch (payload.type) { | 
					
						
							|  |  |  | 			case 'post': | 
					
						
							| 
									
										
										
										
											2016-12-13 09:36:46 -05:00
										 |  |  | 				privileges.posts.canEdit(payload.id, payload.uid, function (err, editable) { | 
					
						
							| 
									
										
										
										
											2016-12-06 16:11:56 -05:00
										 |  |  | 					if (err) { | 
					
						
							|  |  |  | 						return callback(err); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					var minimumReputation = utils.isNumber(meta.config['privileges:flag']) ? parseInt(meta.config['privileges:flag'], 10) : 1; | 
					
						
							| 
									
										
										
										
											2016-12-07 12:07:22 -05:00
										 |  |  | 					// Check if reporter meets rep threshold (or can edit the target post, in which case threshold does not apply)
 | 
					
						
							| 
									
										
										
										
											2016-12-13 09:36:46 -05:00
										 |  |  | 					if (!editable.flag && parseInt(data.reporter.reputation, 10) < minimumReputation) { | 
					
						
							| 
									
										
										
										
											2016-12-06 16:11:56 -05:00
										 |  |  | 						return callback(new Error('[[error:not-enough-reputation-to-flag]]')); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					callback(); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 				break; | 
					
						
							| 
									
										
										
										
											2016-12-07 12:07:22 -05:00
										 |  |  | 			 | 
					
						
							|  |  |  | 			case 'user': | 
					
						
							| 
									
										
										
										
											2016-12-13 09:36:46 -05:00
										 |  |  | 				privileges.users.canEdit(payload.uid, payload.id, function (err, editable) { | 
					
						
							| 
									
										
										
										
											2016-12-07 12:07:22 -05:00
										 |  |  | 					if (err) { | 
					
						
							|  |  |  | 						return callback(err); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					var minimumReputation = utils.isNumber(meta.config['privileges:flag']) ? parseInt(meta.config['privileges:flag'], 10) : 1; | 
					
						
							|  |  |  | 					// Check if reporter meets rep threshold (or can edit the target user, in which case threshold does not apply)
 | 
					
						
							| 
									
										
										
										
											2016-12-13 09:36:46 -05:00
										 |  |  | 					if (!editable && parseInt(data.reporter.reputation, 10) < minimumReputation) { | 
					
						
							| 
									
										
										
										
											2016-12-07 12:07:22 -05:00
										 |  |  | 						return callback(new Error('[[error:not-enough-reputation-to-flag]]')); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					callback(); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			 | 
					
						
							|  |  |  | 			default: | 
					
						
							|  |  |  | 				callback(new Error('[[error:invalid-data]]')); | 
					
						
							|  |  |  | 				break; | 
					
						
							| 
									
										
										
										
											2016-12-06 16:11:56 -05:00
										 |  |  | 		}  | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-30 20:34:06 -05:00
										 |  |  | Flags.getNotes = function (flagId, callback) { | 
					
						
							|  |  |  | 	async.waterfall([ | 
					
						
							|  |  |  | 		async.apply(db.getSortedSetRevRangeWithScores.bind(db), 'flag:' + flagId + ':notes', 0, -1), | 
					
						
							|  |  |  | 		function (notes, next) { | 
					
						
							|  |  |  | 			var uids = []; | 
					
						
							|  |  |  | 			var noteObj; | 
					
						
							|  |  |  | 			notes = notes.map(function (note) { | 
					
						
							|  |  |  | 				try { | 
					
						
							|  |  |  | 					noteObj = JSON.parse(note.value); | 
					
						
							|  |  |  | 					uids.push(noteObj[0]); | 
					
						
							|  |  |  | 					return { | 
					
						
							|  |  |  | 						uid: noteObj[0], | 
					
						
							|  |  |  | 						content: noteObj[1], | 
					
						
							|  |  |  | 						datetime: note.score, | 
					
						
							| 
									
										
										
										
											2016-12-19 12:13:36 -05:00
										 |  |  | 						datetimeISO: new Date(parseInt(note.score, 10)).toISOString() | 
					
						
							| 
									
										
										
										
											2016-11-30 20:34:06 -05:00
										 |  |  | 					}; | 
					
						
							|  |  |  | 				} catch (e) { | 
					
						
							|  |  |  | 					return next(e); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 			next(null, notes, uids); | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		function (notes, uids, next) { | 
					
						
							| 
									
										
										
										
											2016-12-01 16:22:10 -05:00
										 |  |  | 			user.getUsersFields(uids, ['username', 'userslug', 'picture'], function (err, users) { | 
					
						
							| 
									
										
										
										
											2016-11-30 20:34:06 -05:00
										 |  |  | 				if (err) { | 
					
						
							|  |  |  | 					return next(err); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				next(null, notes.map(function (note, idx) { | 
					
						
							|  |  |  | 					note.user = users[idx]; | 
					
						
							|  |  |  | 					return note; | 
					
						
							|  |  |  | 				})); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	], callback); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-09 14:33:59 -05:00
										 |  |  | Flags.create = function (type, id, uid, reason, timestamp, callback) { | 
					
						
							| 
									
										
										
										
											2016-12-06 16:11:56 -05:00
										 |  |  | 	var targetUid; | 
					
						
							| 
									
										
										
										
											2016-12-14 15:53:57 -05:00
										 |  |  | 	var targetCid; | 
					
						
							| 
									
										
										
										
											2016-12-09 14:33:59 -05:00
										 |  |  | 	var doHistoryAppend = false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// timestamp is optional
 | 
					
						
							|  |  |  | 	if (typeof timestamp === 'function' && !callback) { | 
					
						
							|  |  |  | 		callback = timestamp; | 
					
						
							|  |  |  | 		timestamp = Date.now(); | 
					
						
							|  |  |  | 		doHistoryAppend = true; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-12-06 16:11:56 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-23 19:41:35 -05:00
										 |  |  | 	async.waterfall([ | 
					
						
							|  |  |  | 		function (next) { | 
					
						
							|  |  |  | 			async.parallel([ | 
					
						
							| 
									
										
										
										
											2016-12-14 15:53:57 -05:00
										 |  |  | 				// Sanity checks
 | 
					
						
							| 
									
										
										
										
											2016-11-23 19:41:35 -05:00
										 |  |  | 				async.apply(Flags.exists, type, id, uid), | 
					
						
							| 
									
										
										
										
											2016-12-06 16:11:56 -05:00
										 |  |  | 				async.apply(Flags.targetExists, type, id), | 
					
						
							| 
									
										
										
										
											2016-12-14 15:53:57 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 				// Extra data for zset insertion
 | 
					
						
							|  |  |  | 				async.apply(Flags.getTargetUid, type, id), | 
					
						
							|  |  |  | 				async.apply(Flags.getTargetCid, type, id) | 
					
						
							| 
									
										
										
										
											2016-11-23 19:41:35 -05:00
										 |  |  | 			], function (err, checks) { | 
					
						
							| 
									
										
										
										
											2016-11-25 15:09:52 -05:00
										 |  |  | 				if (err) { | 
					
						
							|  |  |  | 					return next(err); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-06 16:11:56 -05:00
										 |  |  | 				targetUid = checks[2] || null; | 
					
						
							| 
									
										
										
										
											2016-12-14 15:53:57 -05:00
										 |  |  | 				targetCid = checks[3] || null; | 
					
						
							| 
									
										
										
										
											2016-12-06 16:11:56 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-23 19:41:35 -05:00
										 |  |  | 				if (checks[0]) { | 
					
						
							|  |  |  | 					return next(new Error('[[error:already-flagged]]')); | 
					
						
							|  |  |  | 				} else if (!checks[1]) { | 
					
						
							|  |  |  | 					return next(new Error('[[error:invalid-data]]')); | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					next(); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2016-11-25 14:17:51 -05:00
										 |  |  | 		async.apply(db.incrObjectField, 'global', 'nextFlagId'), | 
					
						
							|  |  |  | 		function (flagId, next) { | 
					
						
							| 
									
										
										
										
											2016-12-06 16:11:56 -05:00
										 |  |  | 			var tasks = [ | 
					
						
							|  |  |  | 				async.apply(db.setObject.bind(db), 'flag:' + flagId, { | 
					
						
							| 
									
										
										
										
											2016-11-25 14:17:51 -05:00
										 |  |  | 					flagId: flagId, | 
					
						
							| 
									
										
										
										
											2016-11-25 12:43:10 -05:00
										 |  |  | 					type: type, | 
					
						
							| 
									
										
										
										
											2016-11-25 14:17:51 -05:00
										 |  |  | 					targetId: id, | 
					
						
							| 
									
										
										
										
											2016-11-25 12:43:10 -05:00
										 |  |  | 					description: reason, | 
					
						
							|  |  |  | 					uid: uid, | 
					
						
							| 
									
										
										
										
											2016-12-09 14:33:59 -05:00
										 |  |  | 					datetime: timestamp | 
					
						
							| 
									
										
										
										
											2016-12-06 16:11:56 -05:00
										 |  |  | 				}), | 
					
						
							| 
									
										
										
										
											2016-12-09 14:33:59 -05:00
										 |  |  | 				async.apply(db.sortedSetAdd.bind(db), 'flags:datetime', timestamp, flagId),	// by time, the default
 | 
					
						
							|  |  |  | 				async.apply(db.sortedSetAdd.bind(db), 'flags:byReporter:' + uid, timestamp, flagId),	// by reporter
 | 
					
						
							|  |  |  | 				async.apply(db.sortedSetAdd.bind(db), 'flags:byType:' + type, timestamp, flagId),	// by flag type
 | 
					
						
							| 
									
										
										
										
											2016-12-13 09:36:46 -05:00
										 |  |  | 				async.apply(db.sortedSetAdd.bind(db), 'flags:hash', flagId, [type, id, uid].join(':')),	// save zset for duplicate checking
 | 
					
						
							| 
									
										
										
										
											2016-12-09 14:33:59 -05:00
										 |  |  | 				async.apply(analytics.increment, 'flags')	// some fancy analytics
 | 
					
						
							| 
									
										
										
										
											2016-12-06 16:11:56 -05:00
										 |  |  | 			]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (targetUid) { | 
					
						
							| 
									
										
										
										
											2016-12-09 14:33:59 -05:00
										 |  |  | 				tasks.push(async.apply(db.sortedSetAdd.bind(db), 'flags:byTargetUid:' + targetUid, timestamp, flagId));	// by target uid
 | 
					
						
							| 
									
										
										
										
											2016-12-06 16:11:56 -05:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-12-14 15:53:57 -05:00
										 |  |  | 			if (targetCid) { | 
					
						
							|  |  |  | 				tasks.push(async.apply(db.sortedSetAdd.bind(db), 'flags:byCid:' + targetCid, timestamp, flagId));	// by target uid
 | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-12-06 16:11:56 -05:00
										 |  |  | 		 | 
					
						
							|  |  |  | 			async.parallel(tasks, function (err, data) { | 
					
						
							| 
									
										
										
										
											2016-11-25 15:09:52 -05:00
										 |  |  | 				if (err) { | 
					
						
							|  |  |  | 					return next(err); | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2016-11-23 19:41:35 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-09 14:33:59 -05:00
										 |  |  | 				if (doHistoryAppend) { | 
					
						
							|  |  |  | 					Flags.update(flagId, uid, { "state": "open" }); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-25 15:09:52 -05:00
										 |  |  | 				next(null, flagId); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		async.apply(Flags.get) | 
					
						
							|  |  |  | 	], callback); | 
					
						
							| 
									
										
										
										
											2016-11-23 19:41:35 -05:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Flags.exists = function (type, id, uid, callback) { | 
					
						
							| 
									
										
										
										
											2016-12-13 09:36:46 -05:00
										 |  |  | 	db.isSortedSetMember('flags:hash', [type, id, uid].join(':'), callback); | 
					
						
							| 
									
										
										
										
											2016-11-23 19:41:35 -05:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-19 11:16:03 -05:00
										 |  |  | Flags.getTarget = function (type, id, uid, callback) { | 
					
						
							|  |  |  | 	async.waterfall([ | 
					
						
							|  |  |  | 		async.apply(Flags.targetExists, type, id), | 
					
						
							|  |  |  | 		function (exists, next) { | 
					
						
							|  |  |  | 			if (exists) { | 
					
						
							|  |  |  | 				switch (type) { | 
					
						
							|  |  |  | 					case 'post': | 
					
						
							|  |  |  | 						async.waterfall([ | 
					
						
							|  |  |  | 							async.apply(posts.getPostsByPids, [id], uid), | 
					
						
							|  |  |  | 							function (posts, next) { | 
					
						
							|  |  |  | 								topics.addPostData(posts, uid, next); | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 						], function (err, posts) { | 
					
						
							|  |  |  | 							next(err, posts[0]); | 
					
						
							|  |  |  | 						}); | 
					
						
							|  |  |  | 						break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					case 'user': | 
					
						
							|  |  |  | 						user.getUsersData([id], function (err, users) { | 
					
						
							|  |  |  | 							next(err, users ? users[0] : undefined); | 
					
						
							|  |  |  | 						}); | 
					
						
							|  |  |  | 						break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					default: | 
					
						
							|  |  |  | 						next(new Error('[[error:invalid-data]]')); | 
					
						
							|  |  |  | 						break; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				// Target used to exist (otherwise flag creation'd fail), but no longer
 | 
					
						
							|  |  |  | 				next(null, {}); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	], callback); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-23 19:41:35 -05:00
										 |  |  | Flags.targetExists = function (type, id, callback) { | 
					
						
							|  |  |  | 	switch (type) { | 
					
						
							|  |  |  | 		case 'post': | 
					
						
							|  |  |  | 			posts.exists(id, callback); | 
					
						
							|  |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2016-12-07 12:07:22 -05:00
										 |  |  | 		 | 
					
						
							|  |  |  | 		case 'user': | 
					
						
							|  |  |  | 			user.exists(id, callback); | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			callback(new Error('[[error:invalid-data]]')); | 
					
						
							|  |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2016-11-23 19:41:35 -05:00
										 |  |  | 	} | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-06 16:11:56 -05:00
										 |  |  | Flags.getTargetUid = function (type, id, callback) { | 
					
						
							|  |  |  | 	switch (type) { | 
					
						
							|  |  |  | 		case 'post': | 
					
						
							|  |  |  | 			posts.getPostField(id, 'uid', callback); | 
					
						
							|  |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2016-12-07 12:07:22 -05:00
										 |  |  | 		 | 
					
						
							| 
									
										
										
										
											2016-12-14 15:53:57 -05:00
										 |  |  | 		default: | 
					
						
							| 
									
										
										
										
											2016-12-07 12:07:22 -05:00
										 |  |  | 			setImmediate(callback, null, id); | 
					
						
							|  |  |  | 			break;  | 
					
						
							| 
									
										
										
										
											2016-12-06 16:11:56 -05:00
										 |  |  | 	} | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-14 15:53:57 -05:00
										 |  |  | Flags.getTargetCid = function (type, id, callback) { | 
					
						
							|  |  |  | 	switch (type) { | 
					
						
							|  |  |  | 		case 'post': | 
					
						
							|  |  |  | 			posts.getCidByPid(id, callback); | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			setImmediate(callback, null, id); | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-01 11:42:06 -05:00
										 |  |  | Flags.update = function (flagId, uid, changeset, callback) { | 
					
						
							| 
									
										
										
										
											2016-11-23 19:41:35 -05:00
										 |  |  | 	// Retrieve existing flag data to compare for history-saving purposes
 | 
					
						
							| 
									
										
										
										
											2016-12-01 11:42:06 -05:00
										 |  |  | 	var fields = ['state', 'assignee']; | 
					
						
							| 
									
										
										
										
											2016-12-05 12:40:25 -05:00
										 |  |  | 	var tasks = []; | 
					
						
							| 
									
										
										
										
											2016-12-09 14:33:59 -05:00
										 |  |  | 	var now = changeset.datetime || Date.now(); | 
					
						
							| 
									
										
										
										
											2016-11-23 19:41:35 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-01 11:42:06 -05:00
										 |  |  | 	async.waterfall([ | 
					
						
							|  |  |  | 		async.apply(db.getObjectFields.bind(db), 'flag:' + flagId, fields), | 
					
						
							|  |  |  | 		function (current, next) { | 
					
						
							| 
									
										
										
										
											2016-12-02 12:34:58 -05:00
										 |  |  | 			for (var prop in changeset) { | 
					
						
							| 
									
										
										
										
											2016-12-01 11:42:06 -05:00
										 |  |  | 				if (changeset.hasOwnProperty(prop)) { | 
					
						
							|  |  |  | 					if (current[prop] === changeset[prop]) { | 
					
						
							|  |  |  | 						delete changeset[prop]; | 
					
						
							| 
									
										
										
										
											2016-12-02 12:34:58 -05:00
										 |  |  | 					} else { | 
					
						
							| 
									
										
										
										
											2016-12-05 12:40:25 -05:00
										 |  |  | 						// Add tasks as necessary
 | 
					
						
							|  |  |  | 						switch (prop) { | 
					
						
							|  |  |  | 							case 'state': | 
					
						
							|  |  |  | 								tasks.push(async.apply(db.sortedSetAdd.bind(db), 'flags:byState:' + changeset[prop], now, flagId)); | 
					
						
							|  |  |  | 								tasks.push(async.apply(db.sortedSetRemove.bind(db), 'flags:byState:' + current[prop], flagId)); | 
					
						
							|  |  |  | 								break; | 
					
						
							|  |  |  | 							 | 
					
						
							|  |  |  | 							case 'assignee': | 
					
						
							|  |  |  | 								tasks.push(async.apply(db.sortedSetAdd.bind(db), 'flags:byAssignee:' + changeset[prop], now, flagId)); | 
					
						
							|  |  |  | 								break; | 
					
						
							|  |  |  | 						} | 
					
						
							| 
									
										
										
										
											2016-11-23 19:41:35 -05:00
										 |  |  | 					} | 
					
						
							| 
									
										
										
										
											2016-12-01 11:42:06 -05:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-11-23 19:41:35 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-01 11:42:06 -05:00
										 |  |  | 			if (!Object.keys(changeset).length) { | 
					
						
							|  |  |  | 				// No changes
 | 
					
						
							|  |  |  | 				return next(); | 
					
						
							| 
									
										
										
										
											2016-11-23 19:41:35 -05:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-05 12:40:25 -05:00
										 |  |  | 			// Save new object to db (upsert)
 | 
					
						
							|  |  |  | 			tasks.push(async.apply(db.setObject, 'flag:' + flagId, changeset)); | 
					
						
							|  |  |  | 			// Append history
 | 
					
						
							| 
									
										
										
										
											2016-12-07 15:13:40 -05:00
										 |  |  | 			tasks.push(async.apply(Flags.appendHistory, flagId, uid, changeset)); | 
					
						
							| 
									
										
										
										
											2016-12-05 12:40:25 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			async.parallel(tasks, function (err, data) { | 
					
						
							| 
									
										
										
										
											2016-12-01 16:22:10 -05:00
										 |  |  | 				return next(err); | 
					
						
							|  |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2016-11-23 19:41:35 -05:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-12-01 11:42:06 -05:00
										 |  |  | 	], callback); | 
					
						
							| 
									
										
										
										
											2016-11-23 19:41:35 -05:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-01 16:22:10 -05:00
										 |  |  | Flags.getHistory = function (flagId, callback) { | 
					
						
							|  |  |  | 	var history; | 
					
						
							|  |  |  | 	var uids = []; | 
					
						
							|  |  |  | 	async.waterfall([ | 
					
						
							|  |  |  | 		async.apply(db.getSortedSetRevRangeWithScores.bind(db), 'flag:' + flagId + ':history', 0, -1), | 
					
						
							|  |  |  | 		function (_history, next) { | 
					
						
							|  |  |  | 			history = _history.map(function (entry) { | 
					
						
							|  |  |  | 				try { | 
					
						
							|  |  |  | 					entry.value = JSON.parse(entry.value); | 
					
						
							|  |  |  | 				} catch (e) { | 
					
						
							|  |  |  | 					return callback(e); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				uids.push(entry.value[0]); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-07 15:13:40 -05:00
										 |  |  | 				// Deserialise changeset
 | 
					
						
							|  |  |  | 				var changeset = entry.value[1]; | 
					
						
							|  |  |  | 				if (changeset.hasOwnProperty('state')) { | 
					
						
							|  |  |  | 					changeset.state = changeset.state === undefined ? '' : '[[flags:state-' + changeset.state + ']]'; | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2016-12-02 12:34:58 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-01 16:22:10 -05:00
										 |  |  | 				return { | 
					
						
							|  |  |  | 					uid: entry.value[0], | 
					
						
							| 
									
										
										
										
											2016-12-07 15:13:40 -05:00
										 |  |  | 					fields: changeset, | 
					
						
							| 
									
										
										
										
											2016-12-01 16:22:10 -05:00
										 |  |  | 					datetime: entry.score, | 
					
						
							| 
									
										
										
										
											2016-12-19 12:13:36 -05:00
										 |  |  | 					datetimeISO: new Date(parseInt(entry.score, 10)).toISOString() | 
					
						
							| 
									
										
										
										
											2016-12-01 16:22:10 -05:00
										 |  |  | 				}; | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			user.getUsersFields(uids, ['username', 'userslug', 'picture'], next); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	], function (err, users) { | 
					
						
							|  |  |  | 		if (err) { | 
					
						
							|  |  |  | 			return callback(err); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-02 12:34:58 -05:00
										 |  |  | 		// Append user data to each history event
 | 
					
						
							| 
									
										
										
										
											2016-12-01 16:22:10 -05:00
										 |  |  | 		history = history.map(function (event, idx) { | 
					
						
							|  |  |  | 			event.user = users[idx]; | 
					
						
							|  |  |  | 			return event; | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		callback(null, history); | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-01 11:42:06 -05:00
										 |  |  | Flags.appendHistory = function (flagId, uid, changeset, callback) { | 
					
						
							| 
									
										
										
										
											2016-12-01 16:22:10 -05:00
										 |  |  | 	var payload; | 
					
						
							| 
									
										
										
										
											2016-12-09 14:33:59 -05:00
										 |  |  | 	var datetime = changeset.datetime || Date.now(); | 
					
						
							|  |  |  | 	delete changeset.datetime; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-01 16:22:10 -05:00
										 |  |  | 	try { | 
					
						
							| 
									
										
										
										
											2016-12-09 14:33:59 -05:00
										 |  |  | 		payload = JSON.stringify([uid, changeset, datetime]); | 
					
						
							| 
									
										
										
										
											2016-12-01 16:22:10 -05:00
										 |  |  | 	} catch (e) { | 
					
						
							|  |  |  | 		return callback(e); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-09 14:33:59 -05:00
										 |  |  | 	db.sortedSetAdd('flag:' + flagId + ':history', datetime, payload, callback); | 
					
						
							| 
									
										
										
										
											2016-12-01 11:42:06 -05:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2016-11-23 19:41:35 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-09 14:33:59 -05:00
										 |  |  | Flags.appendNote = function (flagId, uid, note, datetime, callback) { | 
					
						
							|  |  |  | 	if (typeof datetime === 'function' && !callback) { | 
					
						
							|  |  |  | 		callback = datetime; | 
					
						
							|  |  |  | 		datetime = Date.now(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-01 11:42:06 -05:00
										 |  |  | 	var payload; | 
					
						
							|  |  |  | 	try { | 
					
						
							|  |  |  | 		payload = JSON.stringify([uid, note]); | 
					
						
							|  |  |  | 	} catch (e) { | 
					
						
							|  |  |  | 		return callback(e); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-11-23 19:41:35 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-01 11:42:06 -05:00
										 |  |  | 	async.waterfall([ | 
					
						
							| 
									
										
										
										
											2016-12-09 14:33:59 -05:00
										 |  |  | 		async.apply(db.sortedSetAdd, 'flag:' + flagId + ':notes', datetime, payload), | 
					
						
							| 
									
										
										
										
											2016-12-07 15:13:40 -05:00
										 |  |  | 		async.apply(Flags.appendHistory, flagId, uid, { | 
					
						
							| 
									
										
										
										
											2016-12-09 14:33:59 -05:00
										 |  |  | 			notes: null, | 
					
						
							|  |  |  | 			datetime: datetime | 
					
						
							| 
									
										
										
										
											2016-12-07 15:13:40 -05:00
										 |  |  | 		}) | 
					
						
							| 
									
										
										
										
											2016-12-01 11:42:06 -05:00
										 |  |  | 	], callback); | 
					
						
							| 
									
										
										
										
											2016-11-23 19:41:35 -05:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-06 16:11:56 -05:00
										 |  |  | Flags.notify = function (flagObj, uid, callback) { | 
					
						
							|  |  |  | 	// Notify administrators, mods, and other associated people
 | 
					
						
							| 
									
										
										
										
											2016-12-13 12:11:51 -05:00
										 |  |  | 	if (!callback) { | 
					
						
							|  |  |  | 		callback = function () {}; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-06 16:11:56 -05:00
										 |  |  | 	switch (flagObj.type) { | 
					
						
							|  |  |  | 		case 'post': | 
					
						
							|  |  |  | 			async.parallel({ | 
					
						
							|  |  |  | 				post: function (next) { | 
					
						
							|  |  |  | 					async.waterfall([ | 
					
						
							|  |  |  | 						async.apply(posts.getPostData, flagObj.targetId), | 
					
						
							|  |  |  | 						async.apply(posts.parsePost) | 
					
						
							|  |  |  | 					], next); | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 				title: async.apply(topics.getTitleByPid, flagObj.targetId), | 
					
						
							|  |  |  | 				admins: async.apply(groups.getMembers, 'administrators', 0, -1), | 
					
						
							|  |  |  | 				globalMods: async.apply(groups.getMembers, 'Global Moderators', 0, -1), | 
					
						
							|  |  |  | 				moderators: function (next) { | 
					
						
							|  |  |  | 					async.waterfall([ | 
					
						
							|  |  |  | 						async.apply(posts.getCidByPid, flagObj.targetId), | 
					
						
							|  |  |  | 						function (cid, next) { | 
					
						
							|  |  |  | 							groups.getMembers('cid:' + cid + ':privileges:mods', 0, -1, next); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					], next); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			}, function (err, results) { | 
					
						
							|  |  |  | 				if (err) { | 
					
						
							|  |  |  | 					return callback(err); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				var title = S(results.title).decodeHTMLEntities().s; | 
					
						
							|  |  |  | 				var titleEscaped = title.replace(/%/g, '%').replace(/,/g, ','); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				notifications.create({ | 
					
						
							|  |  |  | 					bodyShort: '[[notifications:user_flagged_post_in, ' + flagObj.reporter.username + ', ' + titleEscaped + ']]', | 
					
						
							| 
									
										
										
										
											2016-12-07 12:07:22 -05:00
										 |  |  | 					bodyLong: flagObj.description, | 
					
						
							| 
									
										
										
										
											2016-12-06 16:11:56 -05:00
										 |  |  | 					pid: flagObj.targetId, | 
					
						
							|  |  |  | 					path: '/post/' + flagObj.targetId, | 
					
						
							|  |  |  | 					nid: 'flag:post:' + flagObj.targetId + ':uid:' + uid, | 
					
						
							|  |  |  | 					from: uid, | 
					
						
							|  |  |  | 					mergeId: 'notifications:user_flagged_post_in|' + flagObj.targetId, | 
					
						
							|  |  |  | 					topicTitle: results.title | 
					
						
							|  |  |  | 				}, function (err, notification) { | 
					
						
							|  |  |  | 					if (err || !notification) { | 
					
						
							|  |  |  | 						return callback(err); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-07 13:06:55 -05:00
										 |  |  | 					plugins.fireHook('action:flag.create', { | 
					
						
							|  |  |  | 						flag: flagObj | 
					
						
							|  |  |  | 					}); | 
					
						
							| 
									
										
										
										
											2016-12-06 16:11:56 -05:00
										 |  |  | 					notifications.push(notification, results.admins.concat(results.moderators).concat(results.globalMods), callback); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2016-12-07 12:07:22 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		case 'user': | 
					
						
							|  |  |  | 			async.parallel({ | 
					
						
							|  |  |  | 				admins: async.apply(groups.getMembers, 'administrators', 0, -1), | 
					
						
							|  |  |  | 				globalMods: async.apply(groups.getMembers, 'Global Moderators', 0, -1), | 
					
						
							|  |  |  | 			}, function (err, results) { | 
					
						
							|  |  |  | 				if (err) { | 
					
						
							|  |  |  | 					return callback(err); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				notifications.create({ | 
					
						
							|  |  |  | 					bodyShort: '[[notifications:user_flagged_user, ' + flagObj.reporter.username + ', ' + flagObj.target.username + ']]', | 
					
						
							|  |  |  | 					bodyLong: flagObj.description, | 
					
						
							|  |  |  | 					path: '/uid/' + flagObj.targetId, | 
					
						
							|  |  |  | 					nid: 'flag:user:' + flagObj.targetId + ':uid:' + uid, | 
					
						
							|  |  |  | 					from: uid, | 
					
						
							|  |  |  | 					mergeId: 'notifications:user_flagged_user|' + flagObj.targetId | 
					
						
							|  |  |  | 				}, function (err, notification) { | 
					
						
							|  |  |  | 					if (err || !notification) { | 
					
						
							|  |  |  | 						return callback(err); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-07 13:06:55 -05:00
										 |  |  | 					plugins.fireHook('action:flag.create', { | 
					
						
							|  |  |  | 						flag: flagObj | 
					
						
							|  |  |  | 					}); | 
					
						
							| 
									
										
										
										
											2016-12-07 12:07:22 -05:00
										 |  |  | 					notifications.push(notification, results.admins.concat(results.globalMods), callback); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		 | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			callback(new Error('[[error:invalid-data]]')); | 
					
						
							|  |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2016-12-06 16:11:56 -05:00
										 |  |  | 	} | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-23 19:41:35 -05:00
										 |  |  | module.exports = Flags; |