mirror of
				https://github.com/NodeBB/NodeBB.git
				synced 2025-10-31 02:55:58 +01:00 
			
		
		
		
	socketio/posts socketio/topics refactor
This commit is contained in:
		| @@ -321,7 +321,7 @@ define('forum/topic/postTools', ['share', 'navigator', 'components', 'translator | ||||
| 	} | ||||
|  | ||||
| 	function movePost(post, pid, tid) { | ||||
| 		socket.emit('topics.movePost', {pid: pid, tid: tid}, function(err) { | ||||
| 		socket.emit('posts.movePost', {pid: pid, tid: tid}, function(err) { | ||||
| 			$('#move-post-modal').addClass('hide'); | ||||
|  | ||||
| 			if (err) { | ||||
|   | ||||
| @@ -1,27 +1,30 @@ | ||||
| "use strict"; | ||||
|  | ||||
| var	async = require('async'), | ||||
| 	nconf = require('nconf'), | ||||
|  | ||||
| 	winston = require('winston'), | ||||
|  | ||||
| 	db = require('../database'), | ||||
|  | ||||
| 	posts = require('../posts'), | ||||
| 	plugins = require('../plugins'), | ||||
| 	privileges = require('../privileges'), | ||||
| 	meta = require('../meta'), | ||||
| 	topics = require('../topics'), | ||||
| 	favourites = require('../favourites'), | ||||
| 	notifications = require('../notifications'), | ||||
| 	groups = require('../groups'), | ||||
| 	user = require('../user'), | ||||
| 	websockets = require('./index'), | ||||
| 	socketTopics = require('./topics'), | ||||
| 	events = require('../events'), | ||||
| 	utils = require('../../public/src/utils'), | ||||
|  | ||||
| 	SocketPosts = {}; | ||||
|  | ||||
|  | ||||
| require('./posts/edit')(SocketPosts); | ||||
| require('./posts/move')(SocketPosts); | ||||
| require('./posts/favourites')(SocketPosts); | ||||
| require('./posts/tools')(SocketPosts); | ||||
| require('./posts/flag')(SocketPosts); | ||||
|  | ||||
| SocketPosts.reply = function(socket, data, callback) { | ||||
| 	if(!data || !data.tid || !data.content) { | ||||
| 		return callback(new Error('[[error:invalid-data]]')); | ||||
| @@ -85,125 +88,6 @@ SocketPosts.notifyOnlineUsers = function(uid, result) { | ||||
| 	}); | ||||
| }; | ||||
|  | ||||
| SocketPosts.getVoters = function(socket, data, callback) { | ||||
| 	if (!data || !data.pid || !data.cid) { | ||||
| 		return callback(new Error('[[error:invalid-data]]')); | ||||
| 	} | ||||
|  | ||||
| 	async.waterfall([ | ||||
| 		function (next) { | ||||
| 			privileges.categories.isAdminOrMod(data.cid, socket.uid, next); | ||||
| 		}, | ||||
| 		function (isAdminOrMod, next) { | ||||
| 			if (!isAdminOrMod) { | ||||
| 				return next(new Error('[[error:no-privileges]]')); | ||||
| 			} | ||||
|  | ||||
| 			async.parallel({ | ||||
| 				upvoteUids: function(next) { | ||||
| 					db.getSetMembers('pid:' + data.pid + ':upvote', next); | ||||
| 				}, | ||||
| 				downvoteUids: function(next) { | ||||
| 					db.getSetMembers('pid:' + data.pid + ':downvote', next); | ||||
| 				} | ||||
| 			}, next); | ||||
| 		}, | ||||
| 		function (results, next) { | ||||
| 			async.parallel({ | ||||
| 				upvoters: function(next) { | ||||
| 					user.getMultipleUserFields(results.upvoteUids, ['username', 'userslug', 'picture'], next); | ||||
| 				}, | ||||
| 				upvoteCount: function(next) { | ||||
| 					next(null, results.upvoteUids.length); | ||||
| 				}, | ||||
| 				downvoters: function(next) { | ||||
| 					user.getMultipleUserFields(results.downvoteUids, ['username', 'userslug', 'picture'], next); | ||||
| 				}, | ||||
| 				downvoteCount: function(next) { | ||||
| 					next(null, results.downvoteUids.length); | ||||
| 				} | ||||
| 			}, next); | ||||
| 		} | ||||
| 	], callback); | ||||
| }; | ||||
|  | ||||
| SocketPosts.upvote = function(socket, data, callback) { | ||||
| 	favouriteCommand(socket, 'upvote', 'voted', 'notifications:upvoted_your_post_in', data, callback); | ||||
| }; | ||||
|  | ||||
| SocketPosts.downvote = function(socket, data, callback) { | ||||
| 	favouriteCommand(socket, 'downvote', 'voted', '', data, callback); | ||||
| }; | ||||
|  | ||||
| SocketPosts.unvote = function(socket, data, callback) { | ||||
| 	favouriteCommand(socket, 'unvote', 'voted', '', data, callback); | ||||
| }; | ||||
|  | ||||
| SocketPosts.favourite = function(socket, data, callback) { | ||||
| 	favouriteCommand(socket, 'favourite', 'favourited', 'notifications:favourited_your_post_in', data, callback); | ||||
| }; | ||||
|  | ||||
| SocketPosts.unfavourite = function(socket, data, callback) { | ||||
| 	favouriteCommand(socket, 'unfavourite', 'favourited', '', data, callback); | ||||
| }; | ||||
|  | ||||
| function favouriteCommand(socket, command, eventName, notification, data, callback) { | ||||
| 	if(!data || !data.pid || !data.room_id) { | ||||
| 		return callback(new Error('[[error:invalid-data]]')); | ||||
| 	} | ||||
| 	async.parallel({ | ||||
| 		exists: function(next) { | ||||
| 			posts.exists(data.pid, next); | ||||
| 		}, | ||||
| 		deleted: function(next) { | ||||
| 			posts.getPostField(data.pid, 'deleted', next); | ||||
| 		} | ||||
| 	}, function(err, results) { | ||||
| 		if (err || !results.exists) { | ||||
| 			return callback(err || new Error('[[error:invalid-pid]]')); | ||||
| 		} | ||||
|  | ||||
| 		if (parseInt(results.deleted, 10) === 1) { | ||||
| 			return callback(new Error('[[error:post-deleted]]')); | ||||
| 		} | ||||
|  | ||||
| 		/* | ||||
| 		hooks: | ||||
| 			filter.post.upvote | ||||
| 			filter.post.downvote | ||||
| 			filter.post.unvote | ||||
| 			filter.post.favourite | ||||
| 			filter.post.unfavourite | ||||
| 		 */ | ||||
| 		plugins.fireHook('filter:post.' + command, {data: data, uid: socket.uid}, function(err, filteredData) { | ||||
| 			if (err) { | ||||
| 				return callback(err); | ||||
| 			} | ||||
|  | ||||
| 			executeFavouriteCommand(socket, command, eventName, notification, filteredData.data, callback); | ||||
| 		}); | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| function executeFavouriteCommand(socket, command, eventName, notification, data, callback) { | ||||
| 	favourites[command](data.pid, socket.uid, function(err, result) { | ||||
| 		if (err) { | ||||
| 			return callback(err); | ||||
| 		} | ||||
|  | ||||
| 		socket.emit('posts.' + command, result); | ||||
|  | ||||
| 		if (result && eventName) { | ||||
| 			websockets.in(data.room_id).emit('event:' + eventName, result); | ||||
| 		} | ||||
|  | ||||
| 		if (notification) { | ||||
| 			SocketPosts.sendNotificationToPostOwner(data.pid, socket.uid, notification); | ||||
| 		} | ||||
| 		callback(); | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| SocketPosts.sendNotificationToPostOwner = function(pid, fromuid, notification) { | ||||
| 	if(!pid || !fromuid || !notification) { | ||||
| 		return; | ||||
| @@ -261,165 +145,6 @@ SocketPosts.getRawPost = function(socket, pid, callback) { | ||||
| 	], callback); | ||||
| }; | ||||
|  | ||||
| SocketPosts.edit = function(socket, data, callback) { | ||||
| 	if (!socket.uid) { | ||||
| 		return callback(new Error('[[error:not-logged-in]]')); | ||||
| 	} else if (!data || !data.pid || !data.content) { | ||||
| 		return callback(new Error('[[error:invalid-data]]')); | ||||
| 	} else if (data.title && data.title.length < parseInt(meta.config.minimumTitleLength, 10)) { | ||||
| 		return callback(new Error('[[error:title-too-short, ' + meta.config.minimumTitleLength + ']]')); | ||||
| 	} else if (data.title && data.title.length > parseInt(meta.config.maximumTitleLength, 10)) { | ||||
| 		return callback(new Error('[[error:title-too-long, ' + meta.config.maximumTitleLength + ']]')); | ||||
| 	} else if (data.tags && data.tags.length < parseInt(meta.config.minimumTagsPerTopic, 10)) { | ||||
| 		return callback(new Error('[[error:not-enough-tags, ' + meta.config.minimumTagsPerTopic + ']]')); | ||||
| 	} else if (data.tags && data.tags.length > parseInt(meta.config.maximumTagsPerTopic, 10)) { | ||||
| 		return callback(new Error('[[error:too-many-tags, ' + meta.config.maximumTagsPerTopic + ']]')); | ||||
| 	} else if (!data.content || data.content.length < parseInt(meta.config.minimumPostLength, 10)) { | ||||
| 		return callback(new Error('[[error:content-too-short, ' + meta.config.minimumPostLength + ']]')); | ||||
| 	} else if (data.content.length > parseInt(meta.config.maximumPostLength, 10)) { | ||||
| 		return callback(new Error('[[error:content-too-long, ' + meta.config.maximumPostLength + ']]')); | ||||
| 	} | ||||
|  | ||||
| 	posts.edit({ | ||||
| 		uid: socket.uid, | ||||
| 		handle: data.handle, | ||||
| 		pid: data.pid, | ||||
| 		title: data.title, | ||||
| 		content: data.content, | ||||
| 		topic_thumb: data.topic_thumb, | ||||
| 		tags: data.tags | ||||
| 	}, function(err, result) { | ||||
| 		if (err) { | ||||
| 			return callback(err); | ||||
| 		} | ||||
|  | ||||
| 		if (result.topic.renamed) { | ||||
| 			events.log({ | ||||
| 				type: 'topic-rename', | ||||
| 				uid: socket.uid, | ||||
| 				ip: socket.ip, | ||||
| 				oldTitle: result.topic.oldTitle, | ||||
| 				newTitle: result.topic.title | ||||
| 			}); | ||||
| 		} | ||||
|  | ||||
| 		if (parseInt(result.post.deleted) !== 1) { | ||||
| 			websockets.in('topic_' + result.topic.tid).emit('event:post_edited', result); | ||||
| 			return callback(null, result.post); | ||||
| 		} | ||||
|  | ||||
| 		socket.emit('event:post_edited', result); | ||||
| 		callback(null, result.post); | ||||
|  | ||||
| 		async.parallel({ | ||||
| 			admins: async.apply(groups.getMembers, 'administrators', 0, -1), | ||||
| 			moderators: async.apply(groups.getMembers, 'cid:' + result.topic.cid + ':privileges:mods', 0, -1), | ||||
| 			uidsInTopic: async.apply(websockets.getUidsInRoom, 'topic_' + result.topic.tid) | ||||
| 		}, function(err, results) { | ||||
| 			if (err) { | ||||
| 				return winston.error(err); | ||||
| 			} | ||||
|  | ||||
| 			var uids = results.uidsInTopic.filter(function(uid) { | ||||
| 				return results.admins.indexOf(uid) !== -1 || results.moderators.indexOf(uid) !== -1; | ||||
| 			}); | ||||
|  | ||||
| 			uids.forEach(function(uid) { | ||||
| 				websockets.in('uid_' + uid).emit('event:post_edited', result); | ||||
| 			}); | ||||
| 		}); | ||||
| 	}); | ||||
| }; | ||||
|  | ||||
| SocketPosts.delete = function(socket, data, callback) { | ||||
| 	doPostAction('delete', 'event:post_deleted', socket, data, callback); | ||||
| }; | ||||
|  | ||||
| SocketPosts.restore = function(socket, data, callback) { | ||||
| 	doPostAction('restore', 'event:post_restored', socket, data, callback); | ||||
| }; | ||||
|  | ||||
| function doPostAction(command, eventName, socket, data, callback) { | ||||
| 	if (!data) { | ||||
| 		return callback(new Error('[[error:invalid-data]]')); | ||||
| 	} | ||||
|  | ||||
| 	posts.tools[command](socket.uid, data.pid, function(err, postData) { | ||||
| 		if (err) { | ||||
| 			return callback(err); | ||||
| 		} | ||||
|  | ||||
| 		websockets.in('topic_' + data.tid).emit(eventName, postData); | ||||
|  | ||||
| 		events.log({ | ||||
| 			type: 'post-' + command, | ||||
| 			uid: socket.uid, | ||||
| 			pid: data.pid, | ||||
| 			ip: socket.ip | ||||
| 		}); | ||||
|  | ||||
| 		callback(); | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| SocketPosts.purge = function(socket, data, callback) { | ||||
| 	function purgePost() { | ||||
| 		posts.tools.purge(socket.uid, data.pid, function(err) { | ||||
| 			if (err) { | ||||
| 				return callback(err); | ||||
| 			} | ||||
|  | ||||
| 			websockets.in('topic_' + data.tid).emit('event:post_purged', data.pid); | ||||
|  | ||||
| 			events.log({ | ||||
| 				type: 'post-purge', | ||||
| 				uid: socket.uid, | ||||
| 				pid: data.pid, | ||||
| 				ip: socket.ip | ||||
| 			}); | ||||
|  | ||||
| 			callback(); | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	if (!data || !parseInt(data.pid, 10)) { | ||||
| 		return callback(new Error('[[error:invalid-data]]')); | ||||
| 	} | ||||
|  | ||||
| 	isMainAndLastPost(data.pid, function(err, results) { | ||||
| 		if (err) { | ||||
| 			return callback(err); | ||||
| 		} | ||||
|  | ||||
| 		if (!results.isMain) { | ||||
| 			return purgePost(); | ||||
| 		} | ||||
|  | ||||
| 		if (!results.isLast) { | ||||
| 			return callback(new Error('[[error:cant-purge-main-post]]')); | ||||
| 		} | ||||
|  | ||||
| 		posts.getTopicFields(data.pid, ['tid', 'cid'], function(err, topic) { | ||||
| 			if (err) { | ||||
| 				return callback(err); | ||||
| 			} | ||||
| 			socketTopics.doTopicAction('delete', 'event:topic_deleted', socket, {tids: [topic.tid], cid: topic.cid}, callback); | ||||
| 		}); | ||||
| 	}); | ||||
| }; | ||||
|  | ||||
| function isMainAndLastPost(pid, callback) { | ||||
| 	async.parallel({ | ||||
| 		isMain: function(next) { | ||||
| 			posts.isMain(pid, next); | ||||
| 		}, | ||||
| 		isLast: function(next) { | ||||
| 			posts.getTopicFields(pid, ['postcount'], function(err, topic) { | ||||
| 				next(err, topic ? parseInt(topic.postcount, 10) === 1 : false); | ||||
| 			}); | ||||
| 		} | ||||
| 	}, callback); | ||||
| } | ||||
|  | ||||
| SocketPosts.getPrivileges = function(socket, pids, callback) { | ||||
| 	privileges.posts.get(pids, socket.uid, function(err, privileges) { | ||||
| @@ -434,112 +159,6 @@ SocketPosts.getPrivileges = function(socket, pids, callback) { | ||||
| 	}); | ||||
| }; | ||||
|  | ||||
| SocketPosts.getUpvoters = function(socket, pids, callback) { | ||||
| 	if (!Array.isArray(pids)) { | ||||
| 		return callback(new Error('[[error:invalid-data]]')); | ||||
| 	} | ||||
| 	favourites.getUpvotedUidsByPids(pids, function(err, data) { | ||||
| 		if (err || !Array.isArray(data) || !data.length) { | ||||
| 			return callback(err, []); | ||||
| 		} | ||||
|  | ||||
| 		async.map(data, function(uids, next)  { | ||||
| 			var otherCount = 0; | ||||
| 			if (uids.length > 6) { | ||||
| 				otherCount = uids.length - 5; | ||||
| 				uids = uids.slice(0, 5); | ||||
| 			} | ||||
| 			user.getUsernamesByUids(uids, function(err, usernames) { | ||||
| 				next(err, { | ||||
| 					otherCount: otherCount, | ||||
| 					usernames: usernames | ||||
| 				}); | ||||
| 			}); | ||||
| 		}, callback); | ||||
| 	}); | ||||
| }; | ||||
|  | ||||
| SocketPosts.flag = function(socket, pid, callback) { | ||||
| 	if (!socket.uid) { | ||||
| 		return callback(new Error('[[error:not-logged-in]]')); | ||||
| 	} | ||||
|  | ||||
| 	var message = '', | ||||
| 		flaggingUser = {}, | ||||
| 		post; | ||||
|  | ||||
| 	async.waterfall([ | ||||
| 		function(next) { | ||||
| 			posts.getPostFields(pid, ['pid', 'tid', 'uid', 'content', 'deleted'], function(err, postData) { | ||||
| 				if (parseInt(postData.deleted, 10) === 1) { | ||||
| 					return next(new Error('[[error:post-deleted]]')); | ||||
| 				} | ||||
|  | ||||
| 				post = postData; | ||||
| 				next(); | ||||
| 			}); | ||||
| 		}, | ||||
| 		function(next) { | ||||
| 			topics.getTopicFields(post.tid, ['title', 'cid'], function(err, topicData) { | ||||
| 				post.topic = topicData; | ||||
| 				next(); | ||||
| 			}); | ||||
| 		}, | ||||
| 		function(next) { | ||||
| 			async.parallel({ | ||||
| 				isAdminOrMod: function(next) { | ||||
| 					privileges.categories.isAdminOrMod(post.topic.cid, next); | ||||
| 				}, | ||||
| 				userData: function(next) { | ||||
| 					user.getUserFields(socket.uid, ['username', 'reputation'], next); | ||||
| 				} | ||||
| 			}, next); | ||||
| 		}, | ||||
| 		function(user, next) { | ||||
| 			if (!user.isAdminOrMod && parseInt(user.userData.reputation, 10) < parseInt(meta.config['privileges:flag'] || 1, 10)) { | ||||
| 				return next(new Error('[[error:not-enough-reputation-to-flag]]')); | ||||
| 			} | ||||
|  | ||||
| 			flaggingUser = user.userData; | ||||
| 			flaggingUser.uid = socket.uid; | ||||
|  | ||||
| 			next(); | ||||
| 		}, | ||||
| 		function(next) { | ||||
| 			posts.flag(post, socket.uid, next); | ||||
| 		}, | ||||
| 		function(next) { | ||||
| 			message = '[[notifications:user_flagged_post_in, ' + flaggingUser.username + ', ' + post.topic.title + ']]'; | ||||
| 			posts.parsePost(post, next); | ||||
| 		}, | ||||
| 		function(post, next) { | ||||
| 			async.parallel({ | ||||
| 				admins: function(next) { | ||||
| 					groups.getMembers('administrators', 0, -1, next); | ||||
| 				}, | ||||
| 				moderators: function(next) { | ||||
| 					groups.getMembers('cid:' + post.topic.cid + ':privileges:mods', 0, -1, next); | ||||
| 				} | ||||
| 			}, next); | ||||
| 		}, | ||||
| 		function(results, next) { | ||||
| 			notifications.create({ | ||||
| 				bodyShort: message, | ||||
| 				bodyLong: post.content, | ||||
| 				pid: pid, | ||||
| 				nid: 'post_flag:' + pid + ':uid:' + socket.uid, | ||||
| 				from: socket.uid | ||||
| 			}, function(err, notification) { | ||||
| 				if (err || !notification) { | ||||
| 					return next(err); | ||||
| 				} | ||||
|  | ||||
| 				plugins.fireHook('action:post.flag', {post: post, flaggingUser: flaggingUser}); | ||||
| 				notifications.push(notification, results.admins.concat(results.moderators), next); | ||||
| 			}); | ||||
| 		} | ||||
| 	], callback); | ||||
| }; | ||||
|  | ||||
| SocketPosts.loadMoreFavourites = function(socket, data, callback) { | ||||
| 	loadMorePosts('uid:' + data.uid + ':favourites', socket.uid, data, callback); | ||||
| @@ -560,14 +179,6 @@ function loadMorePosts(set, uid, data, callback) { | ||||
| 	posts.getPostSummariesFromSet(set, uid, start, stop, callback); | ||||
| } | ||||
|  | ||||
| SocketPosts.getRecentPosts = function(socket, data, callback) { | ||||
| 	if (!data || !data.count) { | ||||
| 		return callback(new Error('[[error:invalid-data]]')); | ||||
| 	} | ||||
|  | ||||
| 	posts.getRecentPosts(socket.uid, 0, data.count - 1, data.term, callback); | ||||
| }; | ||||
|  | ||||
| SocketPosts.getCategory = function(socket, pid, callback) { | ||||
| 	posts.getCidByPid(pid, callback); | ||||
| }; | ||||
|   | ||||
							
								
								
									
										83
									
								
								src/socket.io/posts/edit.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								src/socket.io/posts/edit.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| var async = require('async'); | ||||
| var winston = require('winston'); | ||||
|  | ||||
| var posts = require('../../posts'); | ||||
| var groups = require('../../groups'); | ||||
| var events = require('../../events'); | ||||
| var meta = require('../../meta'); | ||||
| var websockets = require('../index'); | ||||
|  | ||||
| module.exports = function(SocketPosts) { | ||||
|  | ||||
| 	SocketPosts.edit = function(socket, data, callback) { | ||||
| 		if (!socket.uid) { | ||||
| 			return callback(new Error('[[error:not-logged-in]]')); | ||||
| 		} else if (!data || !data.pid || !data.content) { | ||||
| 			return callback(new Error('[[error:invalid-data]]')); | ||||
| 		} else if (data.title && data.title.length < parseInt(meta.config.minimumTitleLength, 10)) { | ||||
| 			return callback(new Error('[[error:title-too-short, ' + meta.config.minimumTitleLength + ']]')); | ||||
| 		} else if (data.title && data.title.length > parseInt(meta.config.maximumTitleLength, 10)) { | ||||
| 			return callback(new Error('[[error:title-too-long, ' + meta.config.maximumTitleLength + ']]')); | ||||
| 		} else if (data.tags && data.tags.length < parseInt(meta.config.minimumTagsPerTopic, 10)) { | ||||
| 			return callback(new Error('[[error:not-enough-tags, ' + meta.config.minimumTagsPerTopic + ']]')); | ||||
| 		} else if (data.tags && data.tags.length > parseInt(meta.config.maximumTagsPerTopic, 10)) { | ||||
| 			return callback(new Error('[[error:too-many-tags, ' + meta.config.maximumTagsPerTopic + ']]')); | ||||
| 		} else if (!data.content || data.content.length < parseInt(meta.config.minimumPostLength, 10)) { | ||||
| 			return callback(new Error('[[error:content-too-short, ' + meta.config.minimumPostLength + ']]')); | ||||
| 		} else if (data.content.length > parseInt(meta.config.maximumPostLength, 10)) { | ||||
| 			return callback(new Error('[[error:content-too-long, ' + meta.config.maximumPostLength + ']]')); | ||||
| 		} | ||||
|  | ||||
| 		posts.edit({ | ||||
| 			uid: socket.uid, | ||||
| 			handle: data.handle, | ||||
| 			pid: data.pid, | ||||
| 			title: data.title, | ||||
| 			content: data.content, | ||||
| 			topic_thumb: data.topic_thumb, | ||||
| 			tags: data.tags | ||||
| 		}, function(err, result) { | ||||
| 			if (err) { | ||||
| 				return callback(err); | ||||
| 			} | ||||
|  | ||||
| 			if (result.topic.renamed) { | ||||
| 				events.log({ | ||||
| 					type: 'topic-rename', | ||||
| 					uid: socket.uid, | ||||
| 					ip: socket.ip, | ||||
| 					oldTitle: result.topic.oldTitle, | ||||
| 					newTitle: result.topic.title | ||||
| 				}); | ||||
| 			} | ||||
|  | ||||
| 			if (parseInt(result.post.deleted) !== 1) { | ||||
| 				websockets.in('topic_' + result.topic.tid).emit('event:post_edited', result); | ||||
| 				return callback(null, result.post); | ||||
| 			} | ||||
|  | ||||
| 			socket.emit('event:post_edited', result); | ||||
| 			callback(null, result.post); | ||||
|  | ||||
| 			async.parallel({ | ||||
| 				admins: async.apply(groups.getMembers, 'administrators', 0, -1), | ||||
| 				moderators: async.apply(groups.getMembers, 'cid:' + result.topic.cid + ':privileges:mods', 0, -1), | ||||
| 				uidsInTopic: async.apply(websockets.getUidsInRoom, 'topic_' + result.topic.tid) | ||||
| 			}, function(err, results) { | ||||
| 				if (err) { | ||||
| 					return winston.error(err); | ||||
| 				} | ||||
|  | ||||
| 				var uids = results.uidsInTopic.filter(function(uid) { | ||||
| 					return (results.admins.indexOf(uid) !== -1 || results.moderators.indexOf(uid) !== -1) && parseInt(uid, 10) !== socket.uid; | ||||
| 				}); | ||||
|  | ||||
| 				uids.forEach(function(uid) { | ||||
| 					websockets.in('uid_' + uid).emit('event:post_edited', result); | ||||
| 				}); | ||||
| 			}); | ||||
| 		}); | ||||
| 	}; | ||||
| }; | ||||
							
								
								
									
										157
									
								
								src/socket.io/posts/favourites.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								src/socket.io/posts/favourites.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,157 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| var async = require('async'); | ||||
|  | ||||
| var db = require('../../database'); | ||||
| var user = require('../../user'); | ||||
| var posts = require('../../posts'); | ||||
| var favourites = require('../../favourites'); | ||||
| var plugins = require('../../plugins'); | ||||
| var websockets = require('../index'); | ||||
| var privileges = require('../../privileges'); | ||||
|  | ||||
| module.exports = function(SocketPosts) { | ||||
| 	SocketPosts.getVoters = function(socket, data, callback) { | ||||
| 		if (!data || !data.pid || !data.cid) { | ||||
| 			return callback(new Error('[[error:invalid-data]]')); | ||||
| 		} | ||||
|  | ||||
| 		async.waterfall([ | ||||
| 			function (next) { | ||||
| 				privileges.categories.isAdminOrMod(data.cid, socket.uid, next); | ||||
| 			}, | ||||
| 			function (isAdminOrMod, next) { | ||||
| 				if (!isAdminOrMod) { | ||||
| 					return next(new Error('[[error:no-privileges]]')); | ||||
| 				} | ||||
|  | ||||
| 				async.parallel({ | ||||
| 					upvoteUids: function(next) { | ||||
| 						db.getSetMembers('pid:' + data.pid + ':upvote', next); | ||||
| 					}, | ||||
| 					downvoteUids: function(next) { | ||||
| 						db.getSetMembers('pid:' + data.pid + ':downvote', next); | ||||
| 					} | ||||
| 				}, next); | ||||
| 			}, | ||||
| 			function (results, next) { | ||||
| 				async.parallel({ | ||||
| 					upvoters: function(next) { | ||||
| 						user.getMultipleUserFields(results.upvoteUids, ['username', 'userslug', 'picture'], next); | ||||
| 					}, | ||||
| 					upvoteCount: function(next) { | ||||
| 						next(null, results.upvoteUids.length); | ||||
| 					}, | ||||
| 					downvoters: function(next) { | ||||
| 						user.getMultipleUserFields(results.downvoteUids, ['username', 'userslug', 'picture'], next); | ||||
| 					}, | ||||
| 					downvoteCount: function(next) { | ||||
| 						next(null, results.downvoteUids.length); | ||||
| 					} | ||||
| 				}, next); | ||||
| 			} | ||||
| 		], callback); | ||||
| 	}; | ||||
|  | ||||
| 	SocketPosts.getUpvoters = function(socket, pids, callback) { | ||||
| 		if (!Array.isArray(pids)) { | ||||
| 			return callback(new Error('[[error:invalid-data]]')); | ||||
| 		} | ||||
| 		favourites.getUpvotedUidsByPids(pids, function(err, data) { | ||||
| 			if (err || !Array.isArray(data) || !data.length) { | ||||
| 				return callback(err, []); | ||||
| 			} | ||||
|  | ||||
| 			async.map(data, function(uids, next)  { | ||||
| 				var otherCount = 0; | ||||
| 				if (uids.length > 6) { | ||||
| 					otherCount = uids.length - 5; | ||||
| 					uids = uids.slice(0, 5); | ||||
| 				} | ||||
| 				user.getUsernamesByUids(uids, function(err, usernames) { | ||||
| 					next(err, { | ||||
| 						otherCount: otherCount, | ||||
| 						usernames: usernames | ||||
| 					}); | ||||
| 				}); | ||||
| 			}, callback); | ||||
| 		}); | ||||
| 	}; | ||||
|  | ||||
| 	SocketPosts.upvote = function(socket, data, callback) { | ||||
| 		favouriteCommand(socket, 'upvote', 'voted', 'notifications:upvoted_your_post_in', data, callback); | ||||
| 	}; | ||||
|  | ||||
| 	SocketPosts.downvote = function(socket, data, callback) { | ||||
| 		favouriteCommand(socket, 'downvote', 'voted', '', data, callback); | ||||
| 	}; | ||||
|  | ||||
| 	SocketPosts.unvote = function(socket, data, callback) { | ||||
| 		favouriteCommand(socket, 'unvote', 'voted', '', data, callback); | ||||
| 	}; | ||||
|  | ||||
| 	SocketPosts.favourite = function(socket, data, callback) { | ||||
| 		favouriteCommand(socket, 'favourite', 'favourited', 'notifications:favourited_your_post_in', data, callback); | ||||
| 	}; | ||||
|  | ||||
| 	SocketPosts.unfavourite = function(socket, data, callback) { | ||||
| 		favouriteCommand(socket, 'unfavourite', 'favourited', '', data, callback); | ||||
| 	}; | ||||
|  | ||||
| 	function favouriteCommand(socket, command, eventName, notification, data, callback) { | ||||
| 		if(!data || !data.pid || !data.room_id) { | ||||
| 			return callback(new Error('[[error:invalid-data]]')); | ||||
| 		} | ||||
| 		async.parallel({ | ||||
| 			exists: function(next) { | ||||
| 				posts.exists(data.pid, next); | ||||
| 			}, | ||||
| 			deleted: function(next) { | ||||
| 				posts.getPostField(data.pid, 'deleted', next); | ||||
| 			} | ||||
| 		}, function(err, results) { | ||||
| 			if (err || !results.exists) { | ||||
| 				return callback(err || new Error('[[error:invalid-pid]]')); | ||||
| 			} | ||||
|  | ||||
| 			if (parseInt(results.deleted, 10) === 1) { | ||||
| 				return callback(new Error('[[error:post-deleted]]')); | ||||
| 			} | ||||
|  | ||||
| 			/* | ||||
| 			hooks: | ||||
| 				filter.post.upvote | ||||
| 				filter.post.downvote | ||||
| 				filter.post.unvote | ||||
| 				filter.post.favourite | ||||
| 				filter.post.unfavourite | ||||
| 			 */ | ||||
| 			plugins.fireHook('filter:post.' + command, {data: data, uid: socket.uid}, function(err, filteredData) { | ||||
| 				if (err) { | ||||
| 					return callback(err); | ||||
| 				} | ||||
|  | ||||
| 				executeFavouriteCommand(socket, command, eventName, notification, filteredData.data, callback); | ||||
| 			}); | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	function executeFavouriteCommand(socket, command, eventName, notification, data, callback) { | ||||
| 		favourites[command](data.pid, socket.uid, function(err, result) { | ||||
| 			if (err) { | ||||
| 				return callback(err); | ||||
| 			} | ||||
|  | ||||
| 			socket.emit('posts.' + command, result); | ||||
|  | ||||
| 			if (result && eventName) { | ||||
| 				websockets.in(data.room_id).emit('event:' + eventName, result); | ||||
| 			} | ||||
|  | ||||
| 			if (notification) { | ||||
| 				SocketPosts.sendNotificationToPostOwner(data.pid, socket.uid, notification); | ||||
| 			} | ||||
| 			callback(); | ||||
| 		}); | ||||
| 	} | ||||
| }; | ||||
							
								
								
									
										91
									
								
								src/socket.io/posts/flag.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								src/socket.io/posts/flag.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,91 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| var async = require('async'); | ||||
|  | ||||
| var user = require('../../user'); | ||||
| var groups = require('../../groups'); | ||||
| var posts = require('../../posts'); | ||||
| var topics = require('../../topics'); | ||||
| var privileges = require('../../privileges'); | ||||
| var notifications = require('../../notifications'); | ||||
| var plugins = require('../../plugins'); | ||||
| var meta = require('../../meta'); | ||||
|  | ||||
| module.exports = function(SocketPosts) { | ||||
|  | ||||
| 	SocketPosts.flag = function(socket, pid, callback) { | ||||
| 		if (!socket.uid) { | ||||
| 			return callback(new Error('[[error:not-logged-in]]')); | ||||
| 		} | ||||
|  | ||||
| 		var flaggingUser = {}, | ||||
| 			post; | ||||
|  | ||||
| 		async.waterfall([ | ||||
| 			function (next) { | ||||
| 				posts.getPostFields(pid, ['pid', 'tid', 'uid', 'content', 'deleted'], next); | ||||
| 			}, | ||||
| 			function (postData, next) { | ||||
| 				if (parseInt(postData.deleted, 10) === 1) { | ||||
| 					return next(new Error('[[error:post-deleted]]')); | ||||
| 				} | ||||
|  | ||||
| 				post = postData; | ||||
| 				topics.getTopicFields(post.tid, ['title', 'cid'], next); | ||||
| 			}, | ||||
| 			function (topicData, next) { | ||||
| 				post.topic = topicData; | ||||
| 				next(); | ||||
| 			}, | ||||
| 			function (next) { | ||||
| 				async.parallel({ | ||||
| 					isAdminOrMod: function(next) { | ||||
| 						privileges.categories.isAdminOrMod(post.topic.cid, socket.uid, next); | ||||
| 					}, | ||||
| 					userData: function(next) { | ||||
| 						user.getUserFields(socket.uid, ['username', 'reputation'], next); | ||||
| 					} | ||||
| 				}, next); | ||||
| 			}, | ||||
| 			function (user, next) { | ||||
| 				if (!user.isAdminOrMod && parseInt(user.userData.reputation, 10) < parseInt(meta.config['privileges:flag'] || 1, 10)) { | ||||
| 					return next(new Error('[[error:not-enough-reputation-to-flag]]')); | ||||
| 				} | ||||
|  | ||||
| 				flaggingUser = user.userData; | ||||
| 				flaggingUser.uid = socket.uid; | ||||
|  | ||||
| 				posts.flag(post, socket.uid, next); | ||||
| 			}, | ||||
| 			function (next) { | ||||
| 				async.parallel({ | ||||
| 					post: function(next) { | ||||
| 						posts.parsePost(post, next); | ||||
| 					}, | ||||
| 					admins: function(next) { | ||||
| 						groups.getMembers('administrators', 0, -1, next); | ||||
| 					}, | ||||
| 					moderators: function(next) { | ||||
| 						groups.getMembers('cid:' + post.topic.cid + ':privileges:mods', 0, -1, next); | ||||
| 					} | ||||
| 				}, next); | ||||
| 			}, | ||||
| 			function (results, next) { | ||||
| 				notifications.create({ | ||||
| 					bodyShort: '[[notifications:user_flagged_post_in, ' + flaggingUser.username + ', ' + post.topic.title + ']]', | ||||
| 					bodyLong: post.content, | ||||
| 					pid: pid, | ||||
| 					nid: 'post_flag:' + pid + ':uid:' + socket.uid, | ||||
| 					from: socket.uid | ||||
| 				}, function(err, notification) { | ||||
| 					if (err || !notification) { | ||||
| 						return next(err); | ||||
| 					} | ||||
|  | ||||
| 					plugins.fireHook('action:post.flag', {post: post, flaggingUser: flaggingUser}); | ||||
| 					notifications.push(notification, results.admins.concat(results.moderators), next); | ||||
| 				}); | ||||
| 			} | ||||
| 		], callback); | ||||
| 	}; | ||||
| }; | ||||
							
								
								
									
										37
									
								
								src/socket.io/posts/move.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/socket.io/posts/move.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| var async = require('async'); | ||||
| var privileges = require('../../privileges'); | ||||
| var topics = require('../../topics'); | ||||
|  | ||||
|  | ||||
| module.exports = function(SocketPosts) { | ||||
|  | ||||
| 	SocketPosts.movePost = function(socket, data, callback) { | ||||
| 		if (!socket.uid) { | ||||
| 			return callback(new Error('[[error:not-logged-in]]')); | ||||
| 		} | ||||
|  | ||||
| 		if (!data || !data.pid || !data.tid) { | ||||
| 			return callback(new Error('[[error:invalid-data]]')); | ||||
| 		} | ||||
|  | ||||
| 		async.waterfall([ | ||||
| 			function (next) { | ||||
| 				privileges.posts.canMove(data.pid, socket.uid, next); | ||||
| 			}, | ||||
| 			function (canMove, next) { | ||||
| 				if (!canMove){ | ||||
| 					return next(new Error('[[error:no-privileges]]')); | ||||
| 				} | ||||
|  | ||||
| 				topics.movePostToTopic(data.pid, data.tid, next); | ||||
| 			}, | ||||
| 			function (next) { | ||||
| 				SocketPosts.sendNotificationToPostOwner(data.pid, socket.uid, 'notifications:moved_your_post'); | ||||
| 				next(); | ||||
| 			} | ||||
| 		], callback); | ||||
| 	}; | ||||
|  | ||||
| }; | ||||
							
								
								
									
										103
									
								
								src/socket.io/posts/tools.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								src/socket.io/posts/tools.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,103 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| var async = require('async'); | ||||
|  | ||||
| var posts = require('../../posts'); | ||||
| var events = require('../../events'); | ||||
| var websockets = require('../index'); | ||||
| var socketTopics = require('../topics'); | ||||
|  | ||||
|  | ||||
| module.exports = function(SocketPosts) { | ||||
|  | ||||
| 	SocketPosts.delete = function(socket, data, callback) { | ||||
| 		doPostAction('delete', 'event:post_deleted', socket, data, callback); | ||||
| 	}; | ||||
|  | ||||
| 	SocketPosts.restore = function(socket, data, callback) { | ||||
| 		doPostAction('restore', 'event:post_restored', socket, data, callback); | ||||
| 	}; | ||||
|  | ||||
| 	function doPostAction(command, eventName, socket, data, callback) { | ||||
| 		if (!data) { | ||||
| 			return callback(new Error('[[error:invalid-data]]')); | ||||
| 		} | ||||
|  | ||||
| 		posts.tools[command](socket.uid, data.pid, function(err, postData) { | ||||
| 			if (err) { | ||||
| 				return callback(err); | ||||
| 			} | ||||
|  | ||||
| 			websockets.in('topic_' + data.tid).emit(eventName, postData); | ||||
|  | ||||
| 			events.log({ | ||||
| 				type: 'post-' + command, | ||||
| 				uid: socket.uid, | ||||
| 				pid: data.pid, | ||||
| 				ip: socket.ip | ||||
| 			}); | ||||
|  | ||||
| 			callback(); | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	SocketPosts.purge = function(socket, data, callback) { | ||||
| 		function purgePost() { | ||||
| 			posts.tools.purge(socket.uid, data.pid, function(err) { | ||||
| 				if (err) { | ||||
| 					return callback(err); | ||||
| 				} | ||||
|  | ||||
| 				websockets.in('topic_' + data.tid).emit('event:post_purged', data.pid); | ||||
|  | ||||
| 				events.log({ | ||||
| 					type: 'post-purge', | ||||
| 					uid: socket.uid, | ||||
| 					pid: data.pid, | ||||
| 					ip: socket.ip | ||||
| 				}); | ||||
|  | ||||
| 				callback(); | ||||
| 			}); | ||||
| 		} | ||||
|  | ||||
| 		if (!data || !parseInt(data.pid, 10)) { | ||||
| 			return callback(new Error('[[error:invalid-data]]')); | ||||
| 		} | ||||
|  | ||||
| 		isMainAndLastPost(data.pid, function(err, results) { | ||||
| 			if (err) { | ||||
| 				return callback(err); | ||||
| 			} | ||||
|  | ||||
| 			if (!results.isMain) { | ||||
| 				return purgePost(); | ||||
| 			} | ||||
|  | ||||
| 			if (!results.isLast) { | ||||
| 				return callback(new Error('[[error:cant-purge-main-post]]')); | ||||
| 			} | ||||
|  | ||||
| 			posts.getTopicFields(data.pid, ['tid', 'cid'], function(err, topic) { | ||||
| 				if (err) { | ||||
| 					return callback(err); | ||||
| 				} | ||||
| 				socketTopics.doTopicAction('delete', 'event:topic_deleted', socket, {tids: [topic.tid], cid: topic.cid}, callback); | ||||
| 			}); | ||||
| 		}); | ||||
| 	}; | ||||
|  | ||||
| 	function isMainAndLastPost(pid, callback) { | ||||
| 		async.parallel({ | ||||
| 			isMain: function(next) { | ||||
| 				posts.isMain(pid, next); | ||||
| 			}, | ||||
| 			isLast: function(next) { | ||||
| 				posts.getTopicFields(pid, ['postcount'], function(err, topic) { | ||||
| 					next(err, topic ? parseInt(topic.postcount, 10) === 1 : false); | ||||
| 				}); | ||||
| 			} | ||||
| 		}, callback); | ||||
| 	} | ||||
|  | ||||
| }; | ||||
| @@ -6,23 +6,22 @@ var nconf = require('nconf'), | ||||
| 	winston = require('winston'), | ||||
|  | ||||
| 	topics = require('../topics'), | ||||
| 	categories = require('../categories'), | ||||
| 	privileges = require('../privileges'), | ||||
| 	plugins = require('../plugins'), | ||||
| 	notifications = require('../notifications'), | ||||
| 	websockets = require('./index'), | ||||
| 	user = require('../user'), | ||||
| 	db = require('../database'), | ||||
| 	meta = require('../meta'), | ||||
| 	events = require('../events'), | ||||
| 	utils = require('../../public/src/utils'), | ||||
|  | ||||
|  | ||||
| 	SocketTopics = {}; | ||||
|  | ||||
| require('./topics/unread')(SocketTopics); | ||||
| require('./topics/move')(SocketTopics); | ||||
| require('./topics/tools')(SocketTopics); | ||||
| require('./topics/infinitescroll')(SocketTopics); | ||||
| require('./topics/tags')(SocketTopics); | ||||
|  | ||||
| SocketTopics.post = function(socket, data, callback) { | ||||
| 	if(!data) { | ||||
| 	if (!data) { | ||||
| 		return callback(new Error('[[error:invalid-data]]')); | ||||
| 	} | ||||
|  | ||||
| @@ -102,175 +101,11 @@ SocketTopics.bookmark = function(socket, data, callback) { | ||||
| 	topics.setUserBookmark(data.tid, socket.uid, data.index, callback); | ||||
| }; | ||||
|  | ||||
| SocketTopics.markAsRead = function(socket, tids, callback) { | ||||
| 	if (!Array.isArray(tids) || !socket.uid) { | ||||
| 		return callback(new Error('[[error:invalid-data]]')); | ||||
| 	} | ||||
|  | ||||
| 	if (!tids.length) { | ||||
| 		return callback(); | ||||
| 	} | ||||
| 	tids = tids.filter(function(tid) { | ||||
| 		return tid && utils.isNumber(tid); | ||||
| 	}); | ||||
|  | ||||
| 	topics.markAsRead(tids, socket.uid, function(err) { | ||||
| 		if (err) { | ||||
| 			return callback(err); | ||||
| 		} | ||||
|  | ||||
| 		topics.pushUnreadCount(socket.uid); | ||||
|  | ||||
| 		for (var i=0; i<tids.length; ++i) { | ||||
| 			topics.markTopicNotificationsRead(tids[i], socket.uid); | ||||
| 		} | ||||
| 		callback(); | ||||
| 	}); | ||||
| }; | ||||
|  | ||||
| SocketTopics.markTopicNotificationsRead = function(socket, tid, callback) { | ||||
| 	if (!tid || !socket.uid) { | ||||
| 		return callback(new Error('[[error:invalid-data]]')); | ||||
| 	} | ||||
| 	topics.markTopicNotificationsRead(tid, socket.uid); | ||||
| }; | ||||
|  | ||||
| SocketTopics.markAllRead = function(socket, data, callback) { | ||||
| 	db.getSortedSetRevRangeByScore('topics:recent', 0, -1, '+inf', Date.now() - topics.unreadCutoff, function(err, tids) { | ||||
| 		if (err) { | ||||
| 			return callback(err); | ||||
| 		} | ||||
|  | ||||
| 		SocketTopics.markAsRead(socket, tids, callback); | ||||
| 	}); | ||||
| }; | ||||
|  | ||||
| SocketTopics.markCategoryTopicsRead = function(socket, cid, callback) { | ||||
| 	topics.getUnreadTids(cid, socket.uid, 0, -1, function(err, tids) { | ||||
| 		if (err) { | ||||
| 			return callback(err); | ||||
| 		} | ||||
|  | ||||
| 		SocketTopics.markAsRead(socket, tids, callback); | ||||
| 	}); | ||||
| }; | ||||
|  | ||||
| SocketTopics.markAsUnreadForAll = function(socket, tids, callback) { | ||||
| 	if (!Array.isArray(tids)) { | ||||
| 		return callback(new Error('[[error:invalid-tid]]')); | ||||
| 	} | ||||
|  | ||||
| 	if (!socket.uid) { | ||||
| 		return callback(new Error('[[error:no-privileges]]')); | ||||
| 	} | ||||
|  | ||||
| 	user.isAdministrator(socket.uid, function(err, isAdmin) { | ||||
| 		if (err) { | ||||
| 			return callback(err); | ||||
| 		} | ||||
|  | ||||
| 		async.each(tids, function(tid, next) { | ||||
| 			async.waterfall([ | ||||
| 				function(next) { | ||||
| 					topics.exists(tid, next); | ||||
| 				}, | ||||
| 				function(exists, next) { | ||||
| 					if (!exists) { | ||||
| 						return next(new Error('[[error:invalid-tid]]')); | ||||
| 					} | ||||
| 					topics.getTopicField(tid, 'cid', next); | ||||
| 				}, | ||||
| 				function(cid, next) { | ||||
| 					user.isModerator(socket.uid, cid, next); | ||||
| 				}, | ||||
| 				function(isMod, next) { | ||||
| 					if (!isAdmin && !isMod) { | ||||
| 						return next(new Error('[[error:no-privileges]]')); | ||||
| 					} | ||||
| 					topics.markAsUnreadForAll(tid, next); | ||||
| 				}, | ||||
| 				function(next) { | ||||
| 					topics.updateRecent(tid, Date.now(), next); | ||||
| 				} | ||||
| 			], next); | ||||
| 		}, function(err) { | ||||
| 			if (err) { | ||||
| 				return callback(err); | ||||
| 			} | ||||
| 			topics.pushUnreadCount(socket.uid); | ||||
| 			callback(); | ||||
| 		}); | ||||
| 	}); | ||||
| }; | ||||
|  | ||||
| SocketTopics.delete = function(socket, data, callback) { | ||||
| 	SocketTopics.doTopicAction('delete', 'event:topic_deleted', socket, data, callback); | ||||
| }; | ||||
|  | ||||
| SocketTopics.restore = function(socket, data, callback) { | ||||
| 	SocketTopics.doTopicAction('restore', 'event:topic_restored', socket, data, callback); | ||||
| }; | ||||
|  | ||||
| SocketTopics.purge = function(socket, data, callback) { | ||||
| 	SocketTopics.doTopicAction('purge', 'event:topic_purged', socket, data, callback); | ||||
| }; | ||||
|  | ||||
| SocketTopics.lock = function(socket, data, callback) { | ||||
| 	SocketTopics.doTopicAction('lock', 'event:topic_locked', socket, data, callback); | ||||
| }; | ||||
|  | ||||
| SocketTopics.unlock = function(socket, data, callback) { | ||||
| 	SocketTopics.doTopicAction('unlock', 'event:topic_unlocked', socket, data, callback); | ||||
| }; | ||||
|  | ||||
| SocketTopics.pin = function(socket, data, callback) { | ||||
| 	SocketTopics.doTopicAction('pin', 'event:topic_pinned', socket, data, callback); | ||||
| }; | ||||
|  | ||||
| SocketTopics.unpin = function(socket, data, callback) { | ||||
| 	SocketTopics.doTopicAction('unpin', 'event:topic_unpinned', socket, data, callback); | ||||
| }; | ||||
|  | ||||
| SocketTopics.doTopicAction = function(action, event, socket, data, callback) { | ||||
| 	callback = callback || function() {}; | ||||
| 	if (!socket.uid) { | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	if (!data || !Array.isArray(data.tids) || !data.cid) { | ||||
| 		return callback(new Error('[[error:invalid-tid]]')); | ||||
| 	} | ||||
|  | ||||
| 	if (typeof topics.tools[action] !== 'function') { | ||||
| 		return callback(); | ||||
| 	} | ||||
|  | ||||
| 	async.each(data.tids, function(tid, next) { | ||||
| 		topics.tools[action](tid, socket.uid, function(err, data) { | ||||
| 			if (err) { | ||||
| 				return next(err); | ||||
| 			} | ||||
|  | ||||
| 			emitToTopicAndCategory(event, data); | ||||
|  | ||||
| 			if (action === 'delete' || action === 'restore' || action === 'purge') { | ||||
| 				events.log({ | ||||
| 					type: 'topic-' + action, | ||||
| 					uid: socket.uid, | ||||
| 					ip: socket.ip, | ||||
| 					tid: tid | ||||
| 				}); | ||||
| 			} | ||||
|  | ||||
| 			next(); | ||||
| 		}); | ||||
| 	}, callback); | ||||
| }; | ||||
|  | ||||
| function emitToTopicAndCategory(event, data) { | ||||
| SocketTopics.emitToTopicAndCategory = function(event, data) { | ||||
| 	websockets.in('topic_' + data.tid).emit(event, data); | ||||
| 	websockets.in('category_' + data.cid).emit(event, data); | ||||
| } | ||||
| }; | ||||
|  | ||||
| SocketTopics.createTopicFromPosts = function(socket, data, callback) { | ||||
| 	if (!socket.uid) { | ||||
| @@ -284,75 +119,8 @@ SocketTopics.createTopicFromPosts = function(socket, data, callback) { | ||||
| 	topics.createTopicFromPosts(socket.uid, data.title, data.pids, callback); | ||||
| }; | ||||
|  | ||||
| SocketTopics.movePost = function(socket, data, callback) { | ||||
| 	if (!socket.uid) { | ||||
| 		return callback(new Error('[[error:not-logged-in]]')); | ||||
| 	} | ||||
|  | ||||
| 	if (!data || !data.pid || !data.tid) { | ||||
| 		return callback(new Error('[[error:invalid-data]]')); | ||||
| 	} | ||||
|  | ||||
| 	privileges.posts.canMove(data.pid, socket.uid, function(err, canMove) { | ||||
| 		if (err || !canMove) { | ||||
| 			return callback(err || new Error('[[error:no-privileges]]')); | ||||
| 		} | ||||
|  | ||||
| 		topics.movePostToTopic(data.pid, data.tid, function(err) { | ||||
| 			if (err) { | ||||
| 				return callback(err); | ||||
| 			} | ||||
|  | ||||
| 			require('./posts').sendNotificationToPostOwner(data.pid, socket.uid, 'notifications:moved_your_post'); | ||||
| 			callback(); | ||||
| 		}); | ||||
| 	}); | ||||
| }; | ||||
|  | ||||
| SocketTopics.move = function(socket, data, callback) { | ||||
| 	if(!data || !Array.isArray(data.tids) || !data.cid) { | ||||
| 		return callback(new Error('[[error:invalid-data]]')); | ||||
| 	} | ||||
|  | ||||
| 	async.eachLimit(data.tids, 10, function(tid, next) { | ||||
| 		var topicData; | ||||
| 		async.waterfall([ | ||||
| 			function(next) { | ||||
| 				privileges.topics.isAdminOrMod(tid, socket.uid, next); | ||||
| 			}, | ||||
| 			function(canMove, next) { | ||||
| 				if (!canMove) { | ||||
| 					return next(new Error('[[error:no-privileges]]')); | ||||
| 				} | ||||
| 				next(); | ||||
| 			}, | ||||
| 			function(next) { | ||||
| 				topics.getTopicFields(tid, ['cid', 'slug'], next); | ||||
| 			}, | ||||
| 			function(_topicData, next) { | ||||
| 				topicData = _topicData; | ||||
| 				topicData.tid = tid; | ||||
| 				topics.tools.move(tid, data.cid, socket.uid, next); | ||||
| 			} | ||||
| 		], function(err) { | ||||
| 			if(err) { | ||||
| 				return next(err); | ||||
| 			} | ||||
|  | ||||
| 			websockets.in('topic_' + tid).emit('event:topic_moved', topicData); | ||||
|  | ||||
| 			websockets.in('category_' + topicData.cid).emit('event:topic_moved', topicData); | ||||
|  | ||||
| 			SocketTopics.sendNotificationToTopicOwner(tid, socket.uid, 'notifications:moved_your_topic'); | ||||
|  | ||||
| 			next(); | ||||
| 		}); | ||||
| 	}, callback); | ||||
| }; | ||||
|  | ||||
|  | ||||
| SocketTopics.sendNotificationToTopicOwner = function(tid, fromuid, notification) { | ||||
| 	if(!tid || !fromuid) { | ||||
| 	if (!tid || !fromuid) { | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| @@ -378,30 +146,8 @@ SocketTopics.sendNotificationToTopicOwner = function(tid, fromuid, notification) | ||||
| }; | ||||
|  | ||||
|  | ||||
| SocketTopics.moveAll = function(socket, data, callback) { | ||||
| 	if(!data || !data.cid || !data.currentCid) { | ||||
| 		return callback(new Error('[[error:invalid-data]]')); | ||||
| 	} | ||||
|  | ||||
| 	privileges.categories.canMoveAllTopics(data.currentCid, data.cid, data.uid, function(err, canMove) { | ||||
| 		if (err || canMove) { | ||||
| 			return callback(err || new Error('[[error:no-privileges]]')); | ||||
| 		} | ||||
|  | ||||
| 		categories.getTopicIds('cid:' + data.currentCid + ':tids', true, 0, -1, function(err, tids) { | ||||
| 			if (err) { | ||||
| 				return callback(err); | ||||
| 			} | ||||
|  | ||||
| 			async.eachLimit(tids, 10, function(tid, next) { | ||||
| 				topics.tools.move(tid, data.cid, socket.uid, next); | ||||
| 			}, callback); | ||||
| 		}); | ||||
| 	}); | ||||
| }; | ||||
|  | ||||
| SocketTopics.toggleFollow = function(socket, tid, callback) { | ||||
| 	if(!socket.uid) { | ||||
| 	if (!socket.uid) { | ||||
| 		return callback(new Error('[[error:not-logged-in]]')); | ||||
| 	} | ||||
|  | ||||
| @@ -409,154 +155,17 @@ SocketTopics.toggleFollow = function(socket, tid, callback) { | ||||
| }; | ||||
|  | ||||
| SocketTopics.follow = function(socket, tid, callback) { | ||||
| 	if(!socket.uid) { | ||||
| 	if (!socket.uid) { | ||||
| 		return callback(new Error('[[error:not-logged-in]]')); | ||||
| 	} | ||||
|  | ||||
| 	topics.follow(tid, socket.uid, callback); | ||||
| }; | ||||
|  | ||||
| SocketTopics.loadMore = function(socket, data, callback) { | ||||
| 	if (!data || !data.tid || !utils.isNumber(data.after) || parseInt(data.after, 10) < 0)  { | ||||
| 		return callback(new Error('[[error:invalid-data]]')); | ||||
| 	} | ||||
|  | ||||
| 	async.parallel({ | ||||
| 		settings: function(next) { | ||||
| 			user.getSettings(socket.uid, next); | ||||
| 		}, | ||||
| 		privileges: function(next) { | ||||
| 			privileges.topics.get(data.tid, socket.uid, next); | ||||
| 		}, | ||||
| 		topic: function(next) { | ||||
| 			topics.getTopicFields(data.tid, ['postcount', 'deleted'], next); | ||||
| 		} | ||||
| 	}, function(err, results) { | ||||
| 		if (err) { | ||||
| 			return callback(err); | ||||
| 		} | ||||
|  | ||||
| 		if (!results.privileges.read || (parseInt(results.topic.deleted, 10) && !results.privileges.view_deleted)) { | ||||
| 			return callback(new Error('[[error:no-privileges]]')); | ||||
| 		} | ||||
|  | ||||
| 		var set = 'tid:' + data.tid + ':posts'; | ||||
| 		var reverse = results.settings.topicPostSort === 'newest_to_oldest' || results.settings.topicPostSort === 'most_votes'; | ||||
| 		var start = Math.max(0, parseInt(data.after, 10)); | ||||
|  | ||||
| 		var infScrollPostsPerPage = 10; | ||||
|  | ||||
| 		if (data.direction === -1) { | ||||
| 			start = start - (reverse ? -infScrollPostsPerPage : infScrollPostsPerPage); | ||||
| 		} | ||||
|  | ||||
| 		if (reverse) { | ||||
| 			start = results.topic.postcount - 1 - start; | ||||
| 			if (results.settings.topicPostSort === 'most_votes') { | ||||
| 				set = 'tid:' + data.tid + ':posts:votes'; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		var stop = start + (infScrollPostsPerPage - 1); | ||||
|  | ||||
| 		start = Math.max(0, start); | ||||
| 		stop = Math.max(0, stop); | ||||
|  | ||||
| 		async.parallel({ | ||||
| 			mainPost: function(next) { | ||||
| 				if (start > 0) { | ||||
| 					return next(); | ||||
| 				} | ||||
| 				topics.getMainPost(data.tid, socket.uid, next); | ||||
| 			}, | ||||
| 			posts: function(next) { | ||||
| 				topics.getTopicPosts(data.tid, set, start, stop, socket.uid, reverse, next); | ||||
| 			}, | ||||
| 			privileges: function(next) { | ||||
| 				next(null, results.privileges); | ||||
| 			}, | ||||
| 			'reputation:disabled': function(next) { | ||||
| 				next(null, parseInt(meta.config['reputation:disabled'], 10) === 1); | ||||
| 			}, | ||||
| 			'downvote:disabled': function(next) { | ||||
| 				next(null, parseInt(meta.config['downvote:disabled'], 10) === 1); | ||||
| 			} | ||||
| 		}, function(err, results) { | ||||
| 			if (results.mainPost) { | ||||
| 				results.posts = [results.mainPost].concat(results.posts); | ||||
| 			} | ||||
|  | ||||
| 			callback(err, results); | ||||
| 		}); | ||||
| 	}); | ||||
| }; | ||||
|  | ||||
| SocketTopics.loadMoreUnreadTopics = function(socket, data, callback) { | ||||
| 	if (!data || !data.after) { | ||||
| 		return callback(new Error('[[error:invalid-data]]')); | ||||
| 	} | ||||
|  | ||||
| 	var start = parseInt(data.after, 10), | ||||
| 		stop = start + 9; | ||||
|  | ||||
| 	topics.getUnreadTopics(data.cid, socket.uid, start, stop, callback); | ||||
| }; | ||||
|  | ||||
| SocketTopics.loadMoreFromSet = function(socket, data, callback) { | ||||
| 	if (!data || !data.after || !data.set) { | ||||
| 		return callback(new Error('[[error:invalid-data]]')); | ||||
| 	} | ||||
|  | ||||
| 	var start = parseInt(data.after, 10), | ||||
| 		stop = start + 9; | ||||
|  | ||||
| 	topics.getTopicsFromSet(data.set, socket.uid, start, stop, callback); | ||||
| }; | ||||
|  | ||||
| SocketTopics.loadTopics = function(socket, data, callback) { | ||||
| 	if (!data || !data.set || !utils.isNumber(data.start) || !utils.isNumber(data.stop)) { | ||||
| 		return callback(new Error('[[error:invalid-data]]')); | ||||
| 	} | ||||
|  | ||||
| 	topics.getTopicsFromSet(data.set, socket.uid, data.start, data.stop, callback); | ||||
| }; | ||||
|  | ||||
| SocketTopics.getPageCount = function(socket, tid, callback) { | ||||
| 	topics.getPageCount(tid, socket.uid, callback); | ||||
| }; | ||||
|  | ||||
| SocketTopics.searchTags = function(socket, data, callback) { | ||||
| 	topics.searchTags(data, callback); | ||||
| }; | ||||
|  | ||||
| SocketTopics.search = function(socket, data, callback) { | ||||
| 	topics.search(data.tid, data.term, callback); | ||||
| }; | ||||
|  | ||||
| SocketTopics.searchAndLoadTags = function(socket, data, callback) { | ||||
| 	if (!data) { | ||||
| 		return callback(new Error('[[error:invalid-data]]')); | ||||
| 	} | ||||
| 	topics.searchAndLoadTags(data, callback); | ||||
| }; | ||||
|  | ||||
| SocketTopics.loadMoreTags = function(socket, data, callback) { | ||||
| 	if(!data || !utils.isNumber(data.after)) { | ||||
| 		return callback(new Error('[[error:invalid-data]]')); | ||||
| 	} | ||||
|  | ||||
| 	var start = parseInt(data.after, 10), | ||||
| 		stop = start + 99; | ||||
|  | ||||
| 	topics.getTags(start, stop, function(err, tags) { | ||||
| 		if (err) { | ||||
| 			return callback(err); | ||||
| 		} | ||||
|  | ||||
| 		callback(null, {tags: tags, nextStart: stop + 1}); | ||||
| 	}); | ||||
| }; | ||||
|  | ||||
| SocketTopics.isModerator = function(socket, tid, callback) { | ||||
| 	topics.getTopicField(tid, 'cid', function(err, cid) { | ||||
| 		if (err) { | ||||
|   | ||||
							
								
								
									
										109
									
								
								src/socket.io/topics/infinitescroll.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								src/socket.io/topics/infinitescroll.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,109 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| var async = require('async'); | ||||
| var user = require('../../user'); | ||||
| var topics = require('../../topics'); | ||||
| var privileges = require('../../privileges'); | ||||
| var meta = require('../../meta'); | ||||
| var utils = require('../../../public/src/utils'); | ||||
|  | ||||
| module.exports = function(SocketTopics) { | ||||
|  | ||||
| 	SocketTopics.loadMore = function(socket, data, callback) { | ||||
| 		if (!data || !data.tid || !utils.isNumber(data.after) || parseInt(data.after, 10) < 0)  { | ||||
| 			return callback(new Error('[[error:invalid-data]]')); | ||||
| 		} | ||||
|  | ||||
| 		async.parallel({ | ||||
| 			settings: function(next) { | ||||
| 				user.getSettings(socket.uid, next); | ||||
| 			}, | ||||
| 			privileges: function(next) { | ||||
| 				privileges.topics.get(data.tid, socket.uid, next); | ||||
| 			}, | ||||
| 			topic: function(next) { | ||||
| 				topics.getTopicFields(data.tid, ['postcount', 'deleted'], next); | ||||
| 			} | ||||
| 		}, function(err, results) { | ||||
| 			if (err) { | ||||
| 				return callback(err); | ||||
| 			} | ||||
|  | ||||
| 			if (!results.privileges.read || (parseInt(results.topic.deleted, 10) && !results.privileges.view_deleted)) { | ||||
| 				return callback(new Error('[[error:no-privileges]]')); | ||||
| 			} | ||||
|  | ||||
| 			var set = 'tid:' + data.tid + ':posts'; | ||||
| 			var reverse = results.settings.topicPostSort === 'newest_to_oldest' || results.settings.topicPostSort === 'most_votes'; | ||||
| 			var start = Math.max(0, parseInt(data.after, 10)); | ||||
|  | ||||
| 			var infScrollPostsPerPage = 10; | ||||
|  | ||||
| 			if (data.direction === -1) { | ||||
| 				start = start - (reverse ? -infScrollPostsPerPage : infScrollPostsPerPage); | ||||
| 			} | ||||
|  | ||||
| 			if (reverse) { | ||||
| 				start = results.topic.postcount - 1 - start; | ||||
| 				if (results.settings.topicPostSort === 'most_votes') { | ||||
| 					set = 'tid:' + data.tid + ':posts:votes'; | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			var stop = start + (infScrollPostsPerPage - 1); | ||||
|  | ||||
| 			start = Math.max(0, start); | ||||
| 			stop = Math.max(0, stop); | ||||
|  | ||||
| 			async.parallel({ | ||||
| 				mainPost: function(next) { | ||||
| 					if (start > 0) { | ||||
| 						return next(); | ||||
| 					} | ||||
| 					topics.getMainPost(data.tid, socket.uid, next); | ||||
| 				}, | ||||
| 				posts: function(next) { | ||||
| 					topics.getTopicPosts(data.tid, set, start, stop, socket.uid, reverse, next); | ||||
| 				}, | ||||
| 				privileges: function(next) { | ||||
| 					next(null, results.privileges); | ||||
| 				}, | ||||
| 				'reputation:disabled': function(next) { | ||||
| 					next(null, parseInt(meta.config['reputation:disabled'], 10) === 1); | ||||
| 				}, | ||||
| 				'downvote:disabled': function(next) { | ||||
| 					next(null, parseInt(meta.config['downvote:disabled'], 10) === 1); | ||||
| 				} | ||||
| 			}, function(err, results) { | ||||
| 				if (results.mainPost) { | ||||
| 					results.posts = [results.mainPost].concat(results.posts); | ||||
| 				} | ||||
|  | ||||
| 				callback(err, results); | ||||
| 			}); | ||||
| 		}); | ||||
| 	}; | ||||
|  | ||||
| 	SocketTopics.loadMoreUnreadTopics = function(socket, data, callback) { | ||||
| 		if (!data || !data.after) { | ||||
| 			return callback(new Error('[[error:invalid-data]]')); | ||||
| 		} | ||||
|  | ||||
| 		var start = parseInt(data.after, 10), | ||||
| 			stop = start + 9; | ||||
|  | ||||
| 		topics.getUnreadTopics(data.cid, socket.uid, start, stop, callback); | ||||
| 	}; | ||||
|  | ||||
| 	SocketTopics.loadMoreFromSet = function(socket, data, callback) { | ||||
| 		if (!data || !data.after || !data.set) { | ||||
| 			return callback(new Error('[[error:invalid-data]]')); | ||||
| 		} | ||||
|  | ||||
| 		var start = parseInt(data.after, 10), | ||||
| 			stop = start + 9; | ||||
|  | ||||
| 		topics.getTopicsFromSet(data.set, socket.uid, start, stop, callback); | ||||
| 	}; | ||||
|  | ||||
| }; | ||||
							
								
								
									
										73
									
								
								src/socket.io/topics/move.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								src/socket.io/topics/move.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| var async = require('async'); | ||||
| var topics = require('../../topics'); | ||||
| var categories = require('../../categories'); | ||||
| var privileges = require('../../privileges'); | ||||
|  | ||||
| module.exports = function(SocketTopics) { | ||||
|  | ||||
| 	SocketTopics.move = function(socket, data, callback) { | ||||
| 		if (!data || !Array.isArray(data.tids) || !data.cid) { | ||||
| 			return callback(new Error('[[error:invalid-data]]')); | ||||
| 		} | ||||
|  | ||||
| 		async.eachLimit(data.tids, 10, function(tid, next) { | ||||
| 			var topicData; | ||||
| 			async.waterfall([ | ||||
| 				function(next) { | ||||
| 					privileges.topics.isAdminOrMod(tid, socket.uid, next); | ||||
| 				}, | ||||
| 				function(canMove, next) { | ||||
| 					if (!canMove) { | ||||
| 						return next(new Error('[[error:no-privileges]]')); | ||||
| 					} | ||||
| 					next(); | ||||
| 				}, | ||||
| 				function(next) { | ||||
| 					topics.getTopicFields(tid, ['cid', 'slug'], next); | ||||
| 				}, | ||||
| 				function(_topicData, next) { | ||||
| 					topicData = _topicData; | ||||
| 					topicData.tid = tid; | ||||
| 					topics.tools.move(tid, data.cid, socket.uid, next); | ||||
| 				} | ||||
| 			], function(err) { | ||||
| 				if (err) { | ||||
| 					return next(err); | ||||
| 				} | ||||
|  | ||||
| 				SocketTopics.emitToTopicAndCategory('event:topic_moved', topicData); | ||||
|  | ||||
| 				SocketTopics.sendNotificationToTopicOwner(tid, socket.uid, 'notifications:moved_your_topic'); | ||||
|  | ||||
| 				next(); | ||||
| 			}); | ||||
| 		}, callback); | ||||
| 	}; | ||||
|  | ||||
|  | ||||
| 	SocketTopics.moveAll = function(socket, data, callback) { | ||||
| 		if (!data || !data.cid || !data.currentCid) { | ||||
| 			return callback(new Error('[[error:invalid-data]]')); | ||||
| 		} | ||||
|  | ||||
| 		async.waterfall([ | ||||
| 			function (next) { | ||||
| 				privileges.categories.canMoveAllTopics(data.currentCid, data.cid, data.uid, next); | ||||
| 			}, | ||||
| 			function (canMove, next) { | ||||
| 				if (!canMove) { | ||||
| 					return callback(new Error('[[error:no-privileges]]')); | ||||
| 				} | ||||
|  | ||||
| 				categories.getTopicIds('cid:' + data.currentCid + ':tids', true, 0, -1, next); | ||||
| 			}, | ||||
| 			function (tids, next) { | ||||
| 				async.eachLimit(tids, 50, function(tid, next) { | ||||
| 					topics.tools.move(tid, data.cid, socket.uid, next); | ||||
| 				}, next); | ||||
| 			} | ||||
| 		], callback); | ||||
| 	}; | ||||
| }; | ||||
							
								
								
									
										34
									
								
								src/socket.io/topics/tags.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/socket.io/topics/tags.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| var topics = require('../../topics'); | ||||
| var utils = require('../../../public/src/utils'); | ||||
|  | ||||
| module.exports = function(SocketTopics) { | ||||
| 	SocketTopics.searchTags = function(socket, data, callback) { | ||||
| 		topics.searchTags(data, callback); | ||||
| 	}; | ||||
|  | ||||
| 	SocketTopics.searchAndLoadTags = function(socket, data, callback) { | ||||
| 		if (!data) { | ||||
| 			return callback(new Error('[[error:invalid-data]]')); | ||||
| 		} | ||||
| 		topics.searchAndLoadTags(data, callback); | ||||
| 	}; | ||||
|  | ||||
| 	SocketTopics.loadMoreTags = function(socket, data, callback) { | ||||
| 		if (!data || !utils.isNumber(data.after)) { | ||||
| 			return callback(new Error('[[error:invalid-data]]')); | ||||
| 		} | ||||
|  | ||||
| 		var start = parseInt(data.after, 10), | ||||
| 			stop = start + 99; | ||||
|  | ||||
| 		topics.getTags(start, stop, function(err, tags) { | ||||
| 			if (err) { | ||||
| 				return callback(err); | ||||
| 			} | ||||
|  | ||||
| 			callback(null, {tags: tags, nextStart: stop + 1}); | ||||
| 		}); | ||||
| 	}; | ||||
| }; | ||||
							
								
								
									
										74
									
								
								src/socket.io/topics/tools.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								src/socket.io/topics/tools.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,74 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| var async = require('async'); | ||||
| var topics = require('../../topics'); | ||||
| var events = require('../../events'); | ||||
|  | ||||
| module.exports = function(SocketTopics) { | ||||
|  | ||||
|  | ||||
| 	SocketTopics.delete = function(socket, data, callback) { | ||||
| 		SocketTopics.doTopicAction('delete', 'event:topic_deleted', socket, data, callback); | ||||
| 	}; | ||||
|  | ||||
| 	SocketTopics.restore = function(socket, data, callback) { | ||||
| 		SocketTopics.doTopicAction('restore', 'event:topic_restored', socket, data, callback); | ||||
| 	}; | ||||
|  | ||||
| 	SocketTopics.purge = function(socket, data, callback) { | ||||
| 		SocketTopics.doTopicAction('purge', 'event:topic_purged', socket, data, callback); | ||||
| 	}; | ||||
|  | ||||
| 	SocketTopics.lock = function(socket, data, callback) { | ||||
| 		SocketTopics.doTopicAction('lock', 'event:topic_locked', socket, data, callback); | ||||
| 	}; | ||||
|  | ||||
| 	SocketTopics.unlock = function(socket, data, callback) { | ||||
| 		SocketTopics.doTopicAction('unlock', 'event:topic_unlocked', socket, data, callback); | ||||
| 	}; | ||||
|  | ||||
| 	SocketTopics.pin = function(socket, data, callback) { | ||||
| 		SocketTopics.doTopicAction('pin', 'event:topic_pinned', socket, data, callback); | ||||
| 	}; | ||||
|  | ||||
| 	SocketTopics.unpin = function(socket, data, callback) { | ||||
| 		SocketTopics.doTopicAction('unpin', 'event:topic_unpinned', socket, data, callback); | ||||
| 	}; | ||||
|  | ||||
| 	SocketTopics.doTopicAction = function(action, event, socket, data, callback) { | ||||
| 		callback = callback || function() {}; | ||||
| 		if (!socket.uid) { | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| 		if (!data || !Array.isArray(data.tids) || !data.cid) { | ||||
| 			return callback(new Error('[[error:invalid-tid]]')); | ||||
| 		} | ||||
|  | ||||
| 		if (typeof topics.tools[action] !== 'function') { | ||||
| 			return callback(); | ||||
| 		} | ||||
|  | ||||
| 		async.each(data.tids, function(tid, next) { | ||||
| 			topics.tools[action](tid, socket.uid, function(err, data) { | ||||
| 				if (err) { | ||||
| 					return next(err); | ||||
| 				} | ||||
|  | ||||
| 				SocketTopics.emitToTopicAndCategory(event, data); | ||||
|  | ||||
| 				if (action === 'delete' || action === 'restore' || action === 'purge') { | ||||
| 					events.log({ | ||||
| 						type: 'topic-' + action, | ||||
| 						uid: socket.uid, | ||||
| 						ip: socket.ip, | ||||
| 						tid: tid | ||||
| 					}); | ||||
| 				} | ||||
|  | ||||
| 				next(); | ||||
| 			}); | ||||
| 		}, callback); | ||||
| 	}; | ||||
|  | ||||
| }; | ||||
							
								
								
									
										112
									
								
								src/socket.io/topics/unread.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								src/socket.io/topics/unread.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,112 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| var async = require('async'), | ||||
|  | ||||
| 	db = require('../../database'), | ||||
| 	user = require('../../user'), | ||||
| 	topics = require('../../topics'), | ||||
| 	utils = require('../../../public/src/utils'); | ||||
|  | ||||
| module.exports = function(SocketTopics) { | ||||
|  | ||||
| 	SocketTopics.markAsRead = function(socket, tids, callback) { | ||||
| 		if (!Array.isArray(tids) || !socket.uid) { | ||||
| 			return callback(new Error('[[error:invalid-data]]')); | ||||
| 		} | ||||
|  | ||||
| 		if (!tids.length) { | ||||
| 			return callback(); | ||||
| 		} | ||||
| 		tids = tids.filter(function(tid) { | ||||
| 			return tid && utils.isNumber(tid); | ||||
| 		}); | ||||
|  | ||||
| 		topics.markAsRead(tids, socket.uid, function(err) { | ||||
| 			if (err) { | ||||
| 				return callback(err); | ||||
| 			} | ||||
|  | ||||
| 			topics.pushUnreadCount(socket.uid); | ||||
|  | ||||
| 			for (var i=0; i<tids.length; ++i) { | ||||
| 				topics.markTopicNotificationsRead(tids[i], socket.uid); | ||||
| 			} | ||||
| 			callback(); | ||||
| 		}); | ||||
| 	}; | ||||
|  | ||||
| 	SocketTopics.markTopicNotificationsRead = function(socket, tid, callback) { | ||||
| 		if (!tid || !socket.uid) { | ||||
| 			return callback(new Error('[[error:invalid-data]]')); | ||||
| 		} | ||||
| 		topics.markTopicNotificationsRead(tid, socket.uid); | ||||
| 	}; | ||||
|  | ||||
| 	SocketTopics.markAllRead = function(socket, data, callback) { | ||||
| 		db.getSortedSetRevRangeByScore('topics:recent', 0, -1, '+inf', Date.now() - topics.unreadCutoff, function(err, tids) { | ||||
| 			if (err) { | ||||
| 				return callback(err); | ||||
| 			} | ||||
|  | ||||
| 			SocketTopics.markAsRead(socket, tids, callback); | ||||
| 		}); | ||||
| 	}; | ||||
|  | ||||
| 	SocketTopics.markCategoryTopicsRead = function(socket, cid, callback) { | ||||
| 		topics.getUnreadTids(cid, socket.uid, 0, -1, function(err, tids) { | ||||
| 			if (err) { | ||||
| 				return callback(err); | ||||
| 			} | ||||
|  | ||||
| 			SocketTopics.markAsRead(socket, tids, callback); | ||||
| 		}); | ||||
| 	}; | ||||
|  | ||||
| 	SocketTopics.markAsUnreadForAll = function(socket, tids, callback) { | ||||
| 		if (!Array.isArray(tids)) { | ||||
| 			return callback(new Error('[[error:invalid-tid]]')); | ||||
| 		} | ||||
|  | ||||
| 		if (!socket.uid) { | ||||
| 			return callback(new Error('[[error:no-privileges]]')); | ||||
| 		} | ||||
|  | ||||
| 		user.isAdministrator(socket.uid, function(err, isAdmin) { | ||||
| 			if (err) { | ||||
| 				return callback(err); | ||||
| 			} | ||||
|  | ||||
| 			async.each(tids, function(tid, next) { | ||||
| 				async.waterfall([ | ||||
| 					function(next) { | ||||
| 						topics.exists(tid, next); | ||||
| 					}, | ||||
| 					function(exists, next) { | ||||
| 						if (!exists) { | ||||
| 							return next(new Error('[[error:invalid-tid]]')); | ||||
| 						} | ||||
| 						topics.getTopicField(tid, 'cid', next); | ||||
| 					}, | ||||
| 					function(cid, next) { | ||||
| 						user.isModerator(socket.uid, cid, next); | ||||
| 					}, | ||||
| 					function(isMod, next) { | ||||
| 						if (!isAdmin && !isMod) { | ||||
| 							return next(new Error('[[error:no-privileges]]')); | ||||
| 						} | ||||
| 						topics.markAsUnreadForAll(tid, next); | ||||
| 					}, | ||||
| 					function(next) { | ||||
| 						topics.updateRecent(tid, Date.now(), next); | ||||
| 					} | ||||
| 				], next); | ||||
| 			}, function(err) { | ||||
| 				if (err) { | ||||
| 					return callback(err); | ||||
| 				} | ||||
| 				topics.pushUnreadCount(socket.uid); | ||||
| 				callback(); | ||||
| 			}); | ||||
| 		}); | ||||
| 	}; | ||||
| }; | ||||
		Reference in New Issue
	
	Block a user