mirror of
				https://github.com/NodeBB/NodeBB.git
				synced 2025-10-31 19:15:58 +01:00 
			
		
		
		
	Topics methods to call the "editable" method first instead of just checking rep thresholds. Also changed the limit on thread length from 10 to infinity (for now, until infinite scrolling makes it in)
		
			
				
	
	
		
			498 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			498 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| var	RDB = require('./redis.js'),
 | |
| 	utils = require('./utils.js'),
 | |
| 	marked = require('marked'),
 | |
| 	user = require('./user.js'),
 | |
| 	topics = require('./topics.js'),
 | |
| 	config = require('../config.js'),
 | |
| 	async = require('async');
 | |
| 
 | |
| marked.setOptions({
 | |
| 	breaks: true
 | |
| });
 | |
| 
 | |
| (function(Posts) {
 | |
| 
 | |
| 	Posts.get = function(callback, tid, current_user, start, end) {
 | |
| 
 | |
| 		if (start == null) start = 0;
 | |
| 		if (end == null) end = -1;//start + 10;
 | |
| 
 | |
| 		var post_data, user_data, thread_data, vote_data, viewer_data;
 | |
| 
 | |
| 		getTopicPosts();
 | |
| 
 | |
| 		getViewerData();
 | |
| 		
 | |
| 
 | |
| 		//compile thread after all data is asynchronously called
 | |
| 		function generateThread() {
 | |
| 			if (!post_data || !user_data || !thread_data || !vote_data || !viewer_data) return;
 | |
| 
 | |
| 			var	posts = [],
 | |
| 				main_posts = [],
 | |
| 				manage_content = (
 | |
| 					viewer_data.reputation >= config.privilege_thresholds.manage_content ||
 | |
| 					viewer_data.isModerator ||
 | |
| 					viewer_data.isAdministrator
 | |
| 				);
 | |
| 
 | |
| 			for (var i=0, ii= post_data.pid.length; i<ii; i++) {
 | |
| 				var uid = post_data.uid[i],
 | |
| 					pid = post_data.pid[i];
 | |
| 					
 | |
| 				if (post_data.deleted[i] === null || (post_data.deleted[i] === '1' && manage_content) || current_user === uid) {
 | |
| 					var post_obj = {
 | |
| 						'pid' : pid,
 | |
| 						'uid' : uid,
 | |
| 						'content' : marked(post_data.content[i] || ''),
 | |
| 						'post_rep' : post_data.reputation[i] || 0,
 | |
| 						'timestamp' : post_data.timestamp[i],
 | |
| 						'relativeTime': utils.relativeTime(post_data.timestamp[i]),
 | |
| 						'username' : user_data[uid].username || 'anonymous',
 | |
| 						'user_rep' : user_data[uid].reputation || 0,
 | |
| 						'gravatar' : user_data[uid].picture || 'http://www.gravatar.com/avatar/d41d8cd98f00b204e9800998ecf8427e',
 | |
| 						'signature' : user_data[uid].signature,
 | |
| 						'fav_star_class' : vote_data[pid] ? 'icon-star' : 'icon-star-empty',
 | |
| 						'display_moderator_tools': (uid == current_user || manage_content || viewer_data.isModerator) ? 'show' : 'none',
 | |
| 						'edited-class': post_data.editor[i] !== null ? '' : 'none',
 | |
| 						'editor': post_data.editor[i] !== null ? user_data[post_data.editor[i]].username : '',
 | |
| 						'relativeEditTime': post_data.editTime !== null ? utils.relativeTime(post_data.editTime[i]) : '',
 | |
| 						'deleted': post_data.deleted[i] || '0'
 | |
| 					};
 | |
| 
 | |
| 					if (i == 0) main_posts.push(post_obj);
 | |
| 					else posts.push(post_obj);
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			callback({
 | |
| 				'topic_name':thread_data.topic_name,
 | |
| 				'category_name':thread_data.category_name,
 | |
| 				'category_slug':thread_data.category_slug,
 | |
| 				'locked': parseInt(thread_data.locked) || 0,
 | |
| 				'deleted': parseInt(thread_data.deleted) || 0,
 | |
| 				'pinned': parseInt(thread_data.pinned) || 0,
 | |
| 				'topic_id': tid,
 | |
| 				'expose_tools': manage_content ? 1 : 0,
 | |
| 				'posts': posts,
 | |
| 				'main_posts': main_posts
 | |
| 			});
 | |
| 		}
 | |
| 
 | |
| 		function getTopicPosts() {
 | |
| 			// get all data for thread in asynchronous fashion
 | |
| 			RDB.lrange('tid:' + tid + ':posts', start, end, function(err, pids) {
 | |
| 				RDB.handle(err);
 | |
| 				
 | |
| 				if(pids.length === 0 ){
 | |
| 					callback(false);
 | |
| 					return;
 | |
| 				}
 | |
| 				
 | |
| 				topics.markAsRead(tid, current_user);
 | |
| 				
 | |
| 				var content = [], uid = [], timestamp = [], pid = [], post_rep = [], editor = [], editTime = [], deleted = [];
 | |
| 
 | |
| 				for (var i=0, ii=pids.length; i<ii; i++) {
 | |
| 					content.push('pid:' + pids[i] + ':content');
 | |
| 					uid.push('pid:' + pids[i] + ':uid');
 | |
| 					timestamp.push('pid:' + pids[i] + ':timestamp');
 | |
| 					post_rep.push('pid:' + pids[i] + ':rep');
 | |
| 					editor.push('pid:' + pids[i] + ':editor');
 | |
| 					editTime.push('pid:' + pids[i] + ':edited');
 | |
| 					deleted.push('pid:' + pids[i] + ':deleted');
 | |
| 					pid.push(pids[i]);
 | |
| 				}
 | |
| 
 | |
| 				Posts.getFavouritesByPostIDs(pids, current_user, function(fav_data) {
 | |
| 					vote_data = fav_data;
 | |
| 					generateThread();
 | |
| 				});
 | |
| 
 | |
| 				RDB.multi()
 | |
| 					.mget(content)
 | |
| 					.mget(uid)
 | |
| 					.mget(timestamp)
 | |
| 					.mget(post_rep)
 | |
| 					.get('tid:' + tid + ':title')
 | |
| 					.get('tid:' + tid + ':locked')
 | |
| 					.get('tid:' + tid + ':category_name')
 | |
| 					.get('tid:' + tid + ':category_slug')
 | |
| 					.get('tid:' + tid + ':deleted')
 | |
| 					.get('tid:' + tid + ':pinned')
 | |
| 					.mget(editor)
 | |
| 					.mget(editTime)
 | |
| 					.mget(deleted)
 | |
| 					.exec(function(err, replies) {
 | |
| 						post_data = {
 | |
| 							pid: pids,
 | |
| 							content: replies[0],
 | |
| 							uid: replies[1],
 | |
| 							timestamp: replies[2],
 | |
| 							reputation: replies[3],
 | |
| 							editor: replies[10],
 | |
| 							editTime: replies[11],
 | |
| 							deleted: replies[12]
 | |
| 						};
 | |
| 
 | |
| 						thread_data = {
 | |
| 							topic_name: replies[4],
 | |
| 							locked: replies[5] || 0,
 | |
| 							category_name: replies[6],
 | |
| 							category_slug: replies[7],
 | |
| 							deleted: replies[8] || 0,
 | |
| 							pinned: replies[9] || 0
 | |
| 						};
 | |
| 
 | |
| 						// Add any editors to the user_data object
 | |
| 						for(var x=0,numPosts=replies[10].length;x<numPosts;x++) {
 | |
| 							if (replies[10][x] !== null && post_data.uid.indexOf(replies[10][x]) === -1) {
 | |
| 								post_data.uid.push(replies[10][x]);
 | |
| 							}
 | |
| 						}
 | |
| 
 | |
| 						user.getMultipleUserFields(post_data.uid, ['username','reputation','picture', 'signature'], function(user_details){
 | |
| 							user_data = user_details;
 | |
| 							generateThread();
 | |
| 						});
 | |
| 					});
 | |
| 			});
 | |
| 		}
 | |
| 
 | |
| 		function getViewerData() {
 | |
| 			async.parallel([
 | |
| 				function(callback) {
 | |
| 					user.getUserField(current_user, 'reputation', function(reputation){
 | |
| 						viewer_data = viewer_data || {};
 | |
| 						viewer_data.reputation = reputation;
 | |
| 
 | |
| 						callback(null);
 | |
| 					});
 | |
| 				},
 | |
| 				function(callback) {
 | |
| 					RDB.get('tid:' + tid + ':cid', function(err, cid) {
 | |
| 						user.isModerator(current_user, cid, function(isMod) {
 | |
| 							viewer_data = viewer_data || {};
 | |
| 							viewer_data.isModerator = isMod;
 | |
| 							callback(null);
 | |
| 						});
 | |
| 					})
 | |
| 				},
 | |
| 				function(callback) {
 | |
| 					user.isAdministrator(current_user, function(isAdmin) {
 | |
| 						viewer_data = viewer_data || {};
 | |
| 						viewer_data.isAdministrator = isAdmin;
 | |
| 						callback(null);
 | |
| 					});
 | |
| 				}
 | |
| 			], function(err) {
 | |
| 					generateThread();
 | |
| 			});
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	Posts.editable = function(uid, pid, callback) {
 | |
| 		async.parallel([
 | |
| 			function(next) {
 | |
| 				RDB.get('pid:' + pid + ':uid', function(err, author) {
 | |
| 					if (author && parseInt(author) > 0) next(null, author === uid);
 | |
| 				});
 | |
| 			},
 | |
| 			function(next) {
 | |
| 				user.getUserField(uid, 'reputation', function(reputation) {
 | |
| 					next(null, reputation >= config.privilege_thresholds.manage_content);
 | |
| 				});
 | |
| 			},
 | |
| 			function(next) {
 | |
| 				Posts.get_tid_by_pid(pid, function(tid) {
 | |
| 					RDB.get('tid:' + tid + ':cid', function(err, cid) {
 | |
| 						user.isModerator(uid, cid, function(isMod) {
 | |
| 							next(null, isMod);
 | |
| 						});
 | |
| 					});
 | |
| 				});
 | |
| 			}, function(next) {
 | |
| 				user.isAdministrator(uid, function(err, isAdmin) {
 | |
| 					next(null, isAdmin);
 | |
| 				});
 | |
| 			}
 | |
| 		], function(err, results) {
 | |
| 			// If any return true, allow the edit
 | |
| 			if (results.indexOf(true) !== -1) callback(true);
 | |
| 		});
 | |
| 	}
 | |
| 
 | |
| 	Posts.get_tid_by_pid = function(pid, callback) {
 | |
| 		RDB.get('pid:' + pid + ':tid', function(err, tid) {
 | |
| 			if (tid && parseInt(tid) > 0) callback(tid);
 | |
| 			else callback(false);
 | |
| 		});
 | |
| 	}
 | |
| 
 | |
| 	Posts.get_cid_by_pid = function(pid, callback) {
 | |
| 		Posts.get_tid(pid, function(tid) {
 | |
| 			if (tid) topics.get_cid_by_tid(tid, function(cid) {
 | |
| 				if (cid) callback(cid);
 | |
| 				else callback(false);
 | |
| 			});
 | |
| 		})
 | |
| 	}
 | |
| 
 | |
| 	Posts.reply = function(socket, tid, uid, content) {
 | |
| 		if (uid < 1) {
 | |
| 			socket.emit('event:alert', {
 | |
| 				title: 'Reply Unsuccessful',
 | |
| 				message: 'You don't seem to be logged in, so you cannot reply.',
 | |
| 				type: 'error',
 | |
| 				timeout: 2000
 | |
| 			});
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		Posts.create(uid, tid, content, function(pid) {
 | |
| 			if (pid > 0) {
 | |
| 				RDB.rpush('tid:' + tid + ':posts', pid);
 | |
| 
 | |
| 				RDB.del('tid:' + tid + ':read_by_uid'); // let everybody know there is an unread post
 | |
| 
 | |
| 				// Re-add the poster, so he/she does not get an "unread" flag on this topic
 | |
| 				topics.markAsRead(tid, uid);
 | |
| 
 | |
| 				socket.emit('event:alert', {
 | |
| 					title: 'Reply Successful',
 | |
| 					message: 'You have successfully replied. Click here to view your reply.',
 | |
| 					type: 'notify',
 | |
| 					timeout: 2000
 | |
| 				});
 | |
| 
 | |
| 			
 | |
| 				user.getUserFields(uid, ['username','reputation','picture'], function(data){
 | |
| 					
 | |
| 					var timestamp = new Date().getTime();
 | |
| 					
 | |
| 					io.sockets.in('topic_' + tid).emit('event:new_post', {
 | |
| 						'posts' : [
 | |
| 							{
 | |
| 								'pid' : pid,
 | |
| 								'content' : marked(content || ''),
 | |
| 								'uid' : uid,
 | |
| 								'username' : data.username || 'anonymous',
 | |
| 								'user_rep' : data.reputation || 0,
 | |
| 								'post_rep' : 0,
 | |
| 								'gravatar' : data.picture,
 | |
| 								'timestamp' : timestamp,
 | |
| 								'relativeTime': utils.relativeTime(timestamp),
 | |
| 								'fav_star_class' :'icon-star-empty',
 | |
| 								'edited-class': 'none',
 | |
| 								'editor': '',
 | |
| 							}
 | |
| 						]
 | |
| 					});
 | |
| 				});
 | |
| 			} else {
 | |
| 				socket.emit('event:alert', {
 | |
| 					title: 'Reply Unsuccessful',
 | |
| 					message: 'Your reply could not be posted at this time. Please try again later.',
 | |
| 					type: 'notify',
 | |
| 					timeout: 2000
 | |
| 				});
 | |
| 			}
 | |
| 		});
 | |
| 	};
 | |
| 
 | |
| 	Posts.create = function(uid, tid, content, callback) {
 | |
| 		if (uid === null) return;
 | |
| 		
 | |
| 		RDB.get('tid:' + tid + ':locked', function(err, locked) {
 | |
| 			RDB.handle(err);
 | |
| 
 | |
| 			if (!locked || locked === '0') {
 | |
| 				RDB.incr('global:next_post_id', function(err, pid) {
 | |
| 					RDB.handle(err);
 | |
| 			
 | |
| 					// Posts Info
 | |
| 					RDB.set('pid:' + pid + ':content', content);
 | |
| 					RDB.set('pid:' + pid + ':uid', uid);
 | |
| 					RDB.set('pid:' + pid + ':timestamp', new Date().getTime());
 | |
| 					RDB.set('pid:' + pid + ':rep', 0);
 | |
| 					RDB.set('pid:' + pid + ':tid', tid);
 | |
| 					
 | |
| 					RDB.incr('tid:' + tid + ':postcount');
 | |
| 
 | |
| 
 | |
| 					user.getUserFields(uid, ['username'], function(data){
 | |
| 						//add active users to this category
 | |
| 						RDB.get('tid:' + tid + ':cid', function(err, cid) {
 | |
| 							RDB.handle(err);
 | |
| 
 | |
| 							// this is a bit of a naive implementation, defn something to look at post-MVP
 | |
| 							RDB.scard('cid:' + cid + ':active_users', function(amount) {
 | |
| 								if (amount > 10) {
 | |
| 									RDB.spop('cid:' + cid + ':active_users');
 | |
| 								}
 | |
| 
 | |
| 								RDB.sadd('cid:' + cid + ':active_users', data.username);
 | |
| 							});
 | |
| 						});
 | |
| 					});
 | |
| 					
 | |
| 					
 | |
| 					// User Details - move this out later
 | |
| 					RDB.lpush('uid:' + uid + ':posts', pid);
 | |
| 					
 | |
| 					user.incrementUserFieldBy(uid, 'postcount', 1);
 | |
| 
 | |
| 					if (callback) 
 | |
| 						callback(pid);
 | |
| 				});
 | |
| 			} else {
 | |
| 				callback(-1);
 | |
| 			}
 | |
| 		});
 | |
| 	}
 | |
| 
 | |
| 
 | |
| 	Posts.favourite = function(pid, room_id, uid, socket) {
 | |
| 		if (uid === 0) {
 | |
| 			socket.emit('event:alert', {
 | |
| 				alert_id: 'post_favourite',
 | |
| 				title: 'Not Logged In',
 | |
| 				message: 'Please log in in order to favourite this post',
 | |
| 				type: 'error',
 | |
| 				timeout: 5000
 | |
| 			});
 | |
| 
 | |
| 			socket.emit('api:posts.favourite', {
 | |
| 				status: 'error',
 | |
| 				pid: pid
 | |
| 			});
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		RDB.get('pid:' + pid + ':uid', function(err, uid_of_poster) {
 | |
| 			RDB.handle(err);
 | |
| 
 | |
| 			Posts.hasFavourited(pid, uid, function(hasFavourited) {
 | |
| 				if (hasFavourited == false) {
 | |
| 					RDB.sadd('pid:' + pid + ':users_favourited', uid);
 | |
| 					RDB.incr('pid:' + pid + ':rep');
 | |
| 
 | |
| 					if (uid !== uid_of_poster) user.incrementUserFieldBy(uid_of_poster, 'reputation', 1);
 | |
| 
 | |
| 					if (room_id) {
 | |
| 						io.sockets.in(room_id).emit('event:rep_up', {uid: uid !== uid_of_poster ? uid_of_poster : 0, pid: pid});
 | |
| 					}
 | |
| 
 | |
| 					socket.emit('api:posts.favourite', {
 | |
| 						status: 'ok'
 | |
| 					});
 | |
| 				}
 | |
| 			});
 | |
| 		});
 | |
| 	}
 | |
| 
 | |
| 	Posts.unfavourite = function(pid, room_id, uid, socket) {
 | |
| 		if (uid === 0) {
 | |
| 			socket.emit('event:alert', {
 | |
| 				alert_id: 'post_favourite',
 | |
| 				title: 'Not Logged In',
 | |
| 				message: 'Please log in in order to favourite this post',
 | |
| 				type: 'error',
 | |
| 				timeout: 5000
 | |
| 			});
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		RDB.get('pid:' + pid + ':uid', function(err, uid_of_poster) {
 | |
| 			RDB.handle(err);
 | |
| 
 | |
| 			Posts.hasFavourited(pid, uid, function(hasFavourited) {
 | |
| 				if (hasFavourited == true) {
 | |
| 					
 | |
| 					RDB.srem('pid:' + pid + ':users_favourited', uid);
 | |
| 					RDB.decr('pid:' + pid + ':rep');
 | |
| 					
 | |
| 					if (uid !== uid_of_poster) user.incrementUserFieldBy(uid_of_poster, 'reputation', -1);
 | |
| 
 | |
| 					if (room_id) {
 | |
| 						io.sockets.in(room_id).emit('event:rep_down', {uid: uid !== uid_of_poster ? uid_of_poster : 0, pid: pid});
 | |
| 					}
 | |
| 				}
 | |
| 			});
 | |
| 		});
 | |
| 	}
 | |
| 
 | |
| 	Posts.hasFavourited = function(pid, uid, callback) {
 | |
| 		RDB.sismember('pid:' + pid + ':users_favourited', uid, function(err, hasFavourited) {
 | |
| 			RDB.handle(err);
 | |
| 			
 | |
| 			callback(hasFavourited);
 | |
| 		});
 | |
| 	}
 | |
| 
 | |
| 	Posts.getFavouritesByPostIDs = function(pids, uid, callback) {
 | |
| 		var loaded = 0;
 | |
| 		var data = {};
 | |
| 
 | |
| 		for (var i=0, ii=pids.length; i<ii; i++) {
 | |
| 			(function(post_id) {
 | |
| 				Posts.hasFavourited(post_id, uid, function(hasFavourited) {
 | |
| 			
 | |
| 					data[post_id] = hasFavourited;
 | |
| 					loaded ++;
 | |
| 					if (loaded == pids.length) callback(data);
 | |
| 				});
 | |
| 			}(pids[i]))
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	Posts.getRawContent = function(pid, socket) {
 | |
| 		RDB.get('pid:' + pid + ':content', function(err, raw) {
 | |
| 			socket.emit('api:posts.getRawPost', { post: raw });
 | |
| 		});
 | |
| 	}
 | |
| 
 | |
| 	Posts.edit = function(uid, pid, content) {
 | |
| 		var	success = function() {
 | |
| 				RDB.set('pid:' + pid + ':content', content);
 | |
| 				RDB.set('pid:' + pid + ':edited', new Date().getTime());
 | |
| 				RDB.set('pid:' + pid + ':editor', uid);
 | |
| 
 | |
| 				Posts.get_tid_by_pid(pid, function(tid) {
 | |
| 					io.sockets.in('topic_' + tid).emit('event:post_edited', { pid: pid, content: marked(content || '') });
 | |
| 				});
 | |
| 			};
 | |
| 
 | |
| 		Posts.editable(uid, pid, function(editable) {
 | |
| 			if (editable) success();
 | |
| 		});
 | |
| 	}
 | |
| 
 | |
| 	Posts.delete = function(uid, pid) {
 | |
| 		var	success = function() {
 | |
| 				RDB.set('pid:' + pid + ':deleted', 1);
 | |
| 
 | |
| 				Posts.get_tid_by_pid(pid, function(tid) {
 | |
| 					io.sockets.in('topic_' + tid).emit('event:post_deleted', { pid: pid });
 | |
| 				});
 | |
| 			};
 | |
| 
 | |
| 		Posts.editable(uid, pid, function(editable) {
 | |
| 			if (editable) success();
 | |
| 		});
 | |
| 	}
 | |
| 
 | |
| 	Posts.restore = function(uid, pid) {
 | |
| 		var	success = function() {
 | |
| 				RDB.del('pid:' + pid + ':deleted');
 | |
| 
 | |
| 				Posts.get_tid_by_pid(pid, function(tid) {
 | |
| 					io.sockets.in('topic_' + tid).emit('event:post_restored', { pid: pid });
 | |
| 				});
 | |
| 			};
 | |
| 
 | |
| 		Posts.editable(uid, pid, function(editable) {
 | |
| 			if (editable) success();
 | |
| 		});
 | |
| 	}
 | |
| }(exports)); |