2014-09-17 21:11:59 +02:00
|
|
|
postSchemaObject = {
|
2014-06-22 12:58:41 +09:00
|
|
|
_id: {
|
|
|
|
type: String,
|
|
|
|
optional: true
|
|
|
|
},
|
|
|
|
createdAt: {
|
|
|
|
type: Date,
|
|
|
|
optional: true
|
|
|
|
},
|
2014-07-03 13:15:23 +09:00
|
|
|
postedAt: {
|
2014-06-22 12:58:41 +09:00
|
|
|
type: Date,
|
|
|
|
optional: true
|
|
|
|
},
|
|
|
|
title: {
|
|
|
|
type: String,
|
2014-09-16 15:18:27 -04:00
|
|
|
label: "Title"
|
2014-06-22 12:58:41 +09:00
|
|
|
},
|
|
|
|
url: {
|
|
|
|
type: String,
|
|
|
|
label: "URL",
|
|
|
|
optional: true
|
|
|
|
},
|
|
|
|
body: {
|
|
|
|
type: String,
|
|
|
|
optional: true
|
|
|
|
},
|
2014-08-31 16:11:48 +09:00
|
|
|
htmlBody: {
|
|
|
|
type: String,
|
|
|
|
optional: true
|
|
|
|
},
|
2014-08-22 10:31:05 +09:00
|
|
|
commentsCount: {
|
2014-06-22 12:58:41 +09:00
|
|
|
type: Number,
|
|
|
|
optional: true
|
|
|
|
},
|
2014-08-22 10:31:05 +09:00
|
|
|
commenters: {
|
|
|
|
type: [String],
|
|
|
|
optional: true
|
|
|
|
},
|
2014-07-22 10:29:37 +09:00
|
|
|
lastCommentedAt: {
|
2014-07-19 15:08:28 +09:00
|
|
|
type: Date,
|
|
|
|
optional: true
|
|
|
|
},
|
2014-07-03 17:13:22 +09:00
|
|
|
clicks: {
|
|
|
|
type: Number,
|
|
|
|
optional: true
|
|
|
|
},
|
2014-06-22 12:58:41 +09:00
|
|
|
baseScore: {
|
|
|
|
type: Number,
|
|
|
|
decimal: true,
|
|
|
|
optional: true
|
|
|
|
},
|
|
|
|
upvotes: {
|
|
|
|
type: Number,
|
|
|
|
optional: true
|
|
|
|
},
|
|
|
|
upvoters: {
|
|
|
|
type: [String], // XXX
|
|
|
|
optional: true
|
|
|
|
},
|
|
|
|
downvotes: {
|
|
|
|
type: Number,
|
|
|
|
optional: true
|
|
|
|
},
|
|
|
|
downvoters: {
|
|
|
|
type: [String], // XXX
|
|
|
|
optional: true
|
|
|
|
},
|
|
|
|
score: {
|
|
|
|
type: Number,
|
|
|
|
decimal: true,
|
|
|
|
optional: true
|
|
|
|
},
|
|
|
|
status: {
|
|
|
|
type: Number,
|
|
|
|
optional: true
|
|
|
|
},
|
|
|
|
sticky: {
|
|
|
|
type: Boolean,
|
|
|
|
optional: true
|
|
|
|
},
|
|
|
|
inactive: {
|
|
|
|
type: Boolean,
|
|
|
|
optional: true
|
|
|
|
},
|
|
|
|
userId: {
|
|
|
|
type: String, // XXX
|
|
|
|
optional: true
|
|
|
|
}
|
2014-09-17 21:11:59 +02:00
|
|
|
};
|
2014-06-22 12:58:41 +09:00
|
|
|
|
|
|
|
// add any extra properties to postSchemaObject (provided by packages for example)
|
|
|
|
_.each(addToPostSchema, function(item){
|
|
|
|
postSchemaObject[item.propertyName] = item.propertySchema;
|
|
|
|
});
|
|
|
|
|
2014-09-17 20:10:43 +02:00
|
|
|
Posts = new Meteor.Collection("posts");
|
2014-09-17 21:11:59 +02:00
|
|
|
|
|
|
|
PostSchema = new SimpleSchema(postSchemaObject);
|
2014-09-17 20:20:23 +02:00
|
|
|
Posts.attachSchema(PostSchema);
|
2014-05-10 16:57:17 +09:00
|
|
|
|
2012-10-24 11:04:42 +09:00
|
|
|
STATUS_PENDING=1;
|
|
|
|
STATUS_APPROVED=2;
|
|
|
|
STATUS_REJECTED=3;
|
|
|
|
|
2013-07-04 12:51:26 +09:00
|
|
|
Posts.deny({
|
|
|
|
update: function(userId, post, fieldNames) {
|
|
|
|
if(isAdminById(userId))
|
2013-07-05 07:09:15 +09:00
|
|
|
return false;
|
2013-11-05 09:32:21 +09:00
|
|
|
// deny the update if it contains something other than the following fields
|
2014-05-16 09:19:35 +09:00
|
|
|
return (_.without(fieldNames, 'title', 'url', 'body', 'shortUrl', 'shortTitle', 'categories').length > 0);
|
2013-07-04 12:51:26 +09:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2013-11-05 09:32:21 +09:00
|
|
|
Posts.allow({
|
2014-09-01 18:19:36 +09:00
|
|
|
update: canEditById,
|
|
|
|
remove: canEditById
|
2013-11-05 09:32:21 +09:00
|
|
|
});
|
|
|
|
|
2013-11-06 10:11:35 +09:00
|
|
|
clickedPosts = [];
|
|
|
|
|
2014-08-04 09:47:10 +09:00
|
|
|
getPostProperties = function(post) {
|
|
|
|
|
2014-09-16 15:18:27 -04:00
|
|
|
var postAuthor = Meteor.users.findOne(post.userId);
|
2014-08-04 09:47:10 +09:00
|
|
|
var p = {
|
|
|
|
postAuthorName : getDisplayName(postAuthor),
|
|
|
|
postTitle : cleanUp(post.title),
|
|
|
|
profileUrl: getProfileUrlById(post.userId),
|
2014-08-12 16:16:44 +09:00
|
|
|
postUrl: getPostPageUrl(post),
|
2014-08-04 11:22:43 +09:00
|
|
|
thumbnailUrl: post.thumbnailUrl,
|
|
|
|
linkUrl: !!post.url ? getOutgoingUrl(post.url) : getPostPageUrl(post._id)
|
2014-08-04 09:47:10 +09:00
|
|
|
};
|
|
|
|
|
|
|
|
if(post.url)
|
|
|
|
p.url = post.url;
|
|
|
|
|
2014-08-31 16:11:48 +09:00
|
|
|
if(post.htmlBody)
|
|
|
|
p.htmlBody = post.htmlBody;
|
2014-08-04 09:47:10 +09:00
|
|
|
|
|
|
|
return p;
|
2014-09-16 15:18:27 -04:00
|
|
|
};
|
2014-08-04 09:47:10 +09:00
|
|
|
|
2014-08-04 11:22:43 +09:00
|
|
|
getPostPageUrl = function(post){
|
|
|
|
return getSiteUrl()+'posts/'+post._id;
|
|
|
|
};
|
|
|
|
|
|
|
|
getPostEditUrl = function(id){
|
|
|
|
return getSiteUrl()+'posts/'+id+'/edit';
|
|
|
|
};
|
|
|
|
|
|
|
|
// for a given post, return its link if it has one, or else its post page URL
|
|
|
|
getPostLink = function (post) {
|
|
|
|
return !!post.url ? getOutgoingUrl(post.url) : getPostPageUrl(post);
|
2014-09-16 15:18:27 -04:00
|
|
|
};
|
2014-08-04 11:22:43 +09:00
|
|
|
|
2014-08-31 16:11:48 +09:00
|
|
|
Posts.before.insert(function (userId, doc) {
|
2014-09-11 14:10:57 +09:00
|
|
|
if(Meteor.isServer && !!doc.body)
|
2014-09-02 14:54:04 +09:00
|
|
|
doc.htmlBody = sanitize(marked(doc.body));
|
2014-08-31 16:11:48 +09:00
|
|
|
});
|
|
|
|
|
|
|
|
Posts.before.update(function (userId, doc, fieldNames, modifier, options) {
|
|
|
|
// if body is being modified, update htmlBody too
|
2014-09-11 14:10:57 +09:00
|
|
|
if (Meteor.isServer && modifier.$set && modifier.$set.body) {
|
2014-08-31 16:11:48 +09:00
|
|
|
modifier.$set = modifier.$set || {};
|
|
|
|
modifier.$set.htmlBody = sanitize(marked(modifier.$set.body));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2012-10-04 11:45:12 +10:00
|
|
|
Meteor.methods({
|
|
|
|
post: function(post){
|
2014-05-16 09:19:35 +09:00
|
|
|
var title = cleanUp(post.title),
|
2014-08-31 16:11:48 +09:00
|
|
|
body = post.body,
|
2014-08-06 12:02:17 +09:00
|
|
|
userId = this.userId,
|
|
|
|
user = Meteor.users.findOne(userId),
|
2013-04-26 17:28:09 +09:00
|
|
|
timeSinceLastPost=timeSinceLast(user, Posts),
|
|
|
|
numberOfPostsInPast24Hours=numberOfItemsInPast24Hours(user, Posts),
|
|
|
|
postInterval = Math.abs(parseInt(getSetting('postInterval', 30))),
|
|
|
|
maxPostsPer24Hours = Math.abs(parseInt(getSetting('maxPostsPerDay', 30))),
|
|
|
|
postId = '';
|
2014-07-03 13:15:23 +09:00
|
|
|
|
|
|
|
|
|
|
|
// ------------------------------ Checks ------------------------------ //
|
2013-11-06 09:33:56 +09:00
|
|
|
|
2012-10-30 12:01:11 +09:00
|
|
|
// check that user can post
|
2012-10-05 13:59:40 +09:00
|
|
|
if (!user || !canPost(user))
|
2013-11-11 22:07:19 +01:00
|
|
|
throw new Meteor.Error(601, i18n.t('You need to login or be invited to post new stories.'));
|
2012-10-10 08:32:49 +09:00
|
|
|
|
2014-05-16 09:19:35 +09:00
|
|
|
// check that user provided a title
|
|
|
|
if(!post.title)
|
|
|
|
throw new Meteor.Error(602, i18n.t('Please fill in a title'));
|
2012-10-05 13:59:40 +09:00
|
|
|
|
2014-07-03 14:20:24 +09:00
|
|
|
|
|
|
|
if(!!post.url){
|
|
|
|
// check that there are no previous posts with the same link in the past 6 months
|
2014-08-31 16:11:48 +09:00
|
|
|
var sixMonthsAgo = moment().subtract(6, 'months').toDate();
|
2014-07-03 14:20:24 +09:00
|
|
|
var postWithSameLink = Posts.findOne({url: post.url, postedAt: {$gte: sixMonthsAgo}});
|
|
|
|
|
|
|
|
if(typeof postWithSameLink !== 'undefined'){
|
|
|
|
Meteor.call('upvotePost', postWithSameLink);
|
|
|
|
throw new Meteor.Error(603, i18n.t('This link has already been posted'), postWithSameLink._id);
|
|
|
|
}
|
2012-10-30 12:01:11 +09:00
|
|
|
}
|
2012-10-06 13:15:55 +09:00
|
|
|
|
2013-01-13 19:18:01 +09:00
|
|
|
if(!isAdmin(Meteor.user())){
|
|
|
|
// check that user waits more than X seconds between posts
|
|
|
|
if(!this.isSimulation && timeSinceLastPost < postInterval)
|
2013-11-11 22:07:19 +01:00
|
|
|
throw new Meteor.Error(604, i18n.t('Please wait ')+(postInterval-timeSinceLastPost)+i18n.t(' seconds before posting again'));
|
2012-10-24 11:04:42 +09:00
|
|
|
|
2013-01-13 19:18:01 +09:00
|
|
|
// check that the user doesn't post more than Y posts per day
|
|
|
|
if(!this.isSimulation && numberOfPostsInPast24Hours > maxPostsPer24Hours)
|
2013-11-11 22:07:19 +01:00
|
|
|
throw new Meteor.Error(605, i18n.t('Sorry, you cannot submit more than ')+maxPostsPer24Hours+i18n.t(' posts per day'));
|
2013-01-13 19:18:01 +09:00
|
|
|
}
|
2012-10-23 12:24:38 +09:00
|
|
|
|
2014-07-03 13:15:23 +09:00
|
|
|
// ------------------------------ Properties ------------------------------ //
|
|
|
|
|
|
|
|
// Basic Properties
|
|
|
|
properties = {
|
2014-05-16 09:19:35 +09:00
|
|
|
title: title,
|
2012-12-13 10:58:17 +09:00
|
|
|
body: body,
|
2012-10-23 12:24:38 +09:00
|
|
|
userId: userId,
|
|
|
|
author: getDisplayNameById(userId),
|
2014-06-22 09:55:34 +09:00
|
|
|
upvotes: 0,
|
|
|
|
downvotes: 0,
|
2014-08-27 09:25:05 +09:00
|
|
|
commentsCount: 0,
|
2012-10-04 11:45:12 +10:00
|
|
|
baseScore: 0,
|
2012-10-24 11:04:42 +09:00
|
|
|
score: 0,
|
2014-09-16 15:18:27 -04:00
|
|
|
inactive: false
|
|
|
|
};
|
2014-07-03 13:15:23 +09:00
|
|
|
|
|
|
|
// UserId
|
|
|
|
if(isAdmin(Meteor.user()) && !!post.userId){ // only let admins post as other users
|
|
|
|
properties.userId = post.userId;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Status
|
|
|
|
var defaultPostStatus = getSetting('requirePostsApproval') ? STATUS_PENDING : STATUS_APPROVED;
|
|
|
|
if(isAdmin(Meteor.user()) && !!post.status){ // if user is admin and a custom status has been set
|
|
|
|
properties.status = post.status;
|
|
|
|
}else{ // else use default status
|
|
|
|
properties.status = defaultPostStatus;
|
|
|
|
}
|
|
|
|
|
|
|
|
// CreatedAt
|
|
|
|
properties.createdAt = new Date();
|
2013-07-19 14:30:39 +03:00
|
|
|
|
2014-07-03 13:15:23 +09:00
|
|
|
// PostedAt
|
|
|
|
if(properties.status == 2){ // only set postedAt if post is approved
|
|
|
|
if(isAdmin(Meteor.user()) && !!post.postedAt){ // if user is admin and a custom postDate has been set
|
|
|
|
properties.postedAt = post.postedAt;
|
|
|
|
}else{ // else use current time
|
|
|
|
properties.postedAt = new Date();
|
|
|
|
}
|
2013-01-13 08:52:35 +09:00
|
|
|
}
|
2012-10-04 15:26:59 +09:00
|
|
|
|
2014-07-03 13:15:23 +09:00
|
|
|
post = _.extend(post, properties);
|
|
|
|
|
2014-07-05 18:06:28 +09:00
|
|
|
// ------------------------------ Callbacks ------------------------------ //
|
|
|
|
|
|
|
|
// run all post submit server callbacks on post object successively
|
2014-09-20 09:57:09 +09:00
|
|
|
post = postSubmitMethodCallbacks.reduce(function(result, currentFunction) {
|
2014-07-05 18:06:28 +09:00
|
|
|
return currentFunction(result);
|
|
|
|
}, post);
|
|
|
|
|
2014-07-03 13:15:23 +09:00
|
|
|
// ------------------------------ Insert ------------------------------ //
|
2014-06-22 09:55:34 +09:00
|
|
|
|
2014-07-03 14:13:47 +09:00
|
|
|
// console.log(post)
|
2014-07-03 13:15:23 +09:00
|
|
|
post._id = Posts.insert(post);
|
|
|
|
|
2014-09-20 10:42:42 +09:00
|
|
|
// ------------------------------ Callbacks ------------------------------ //
|
|
|
|
|
|
|
|
// run all post submit server callbacks on post object successively
|
|
|
|
post = postAfterSubmitMethodCallbacks.reduce(function(result, currentFunction) {
|
|
|
|
return currentFunction(result);
|
|
|
|
}, post);
|
|
|
|
|
2014-07-03 13:15:23 +09:00
|
|
|
// ------------------------------ Post-Insert ------------------------------ //
|
2013-01-15 08:46:00 +09:00
|
|
|
|
2013-11-08 11:10:23 +09:00
|
|
|
// increment posts count
|
|
|
|
Meteor.users.update({_id: userId}, {$inc: {postCount: 1}});
|
|
|
|
|
2013-01-19 22:42:59 +09:00
|
|
|
var postAuthor = Meteor.users.findOne(post.userId);
|
2013-10-29 17:54:45 +09:00
|
|
|
|
|
|
|
Meteor.call('upvotePost', post, postAuthor);
|
2013-01-19 22:42:59 +09:00
|
|
|
|
2012-10-24 11:04:42 +09:00
|
|
|
return post;
|
2013-01-13 08:52:35 +09:00
|
|
|
},
|
2014-07-03 13:15:23 +09:00
|
|
|
setPostedAt: function(post, customPostedAt){
|
2014-07-03 16:17:36 +09:00
|
|
|
|
2014-07-03 13:15:23 +09:00
|
|
|
var postedAt = new Date(); // default to current date and time
|
2014-07-03 16:17:36 +09:00
|
|
|
|
2014-07-03 13:15:23 +09:00
|
|
|
if(isAdmin(Meteor.user()) && typeof customPostedAt !== 'undefined') // if user is admin and a custom datetime has been set
|
2014-09-16 15:18:27 -04:00
|
|
|
postedAt = customPostedAt;
|
2014-07-03 16:17:36 +09:00
|
|
|
|
2014-07-03 13:15:23 +09:00
|
|
|
Posts.update(post._id, {$set: {postedAt: postedAt}});
|
|
|
|
},
|
2013-01-13 10:21:09 +09:00
|
|
|
post_edit: function(post){
|
2013-11-06 09:29:10 +09:00
|
|
|
// TODO: make post_edit server-side?
|
2013-07-04 12:51:26 +09:00
|
|
|
},
|
2014-05-23 13:08:52 +09:00
|
|
|
approvePost: function(post){
|
|
|
|
if(isAdmin(Meteor.user())){
|
2014-06-22 13:20:10 +09:00
|
|
|
var now = new Date();
|
2014-07-03 14:13:47 +09:00
|
|
|
Posts.update(post._id, {$set: {status: 2, postedAt: now}});
|
2014-05-23 13:08:52 +09:00
|
|
|
}else{
|
|
|
|
throwError('You need to be an admin to do that.');
|
|
|
|
}
|
|
|
|
},
|
|
|
|
unapprovePost: function(post){
|
|
|
|
if(isAdmin(Meteor.user())){
|
|
|
|
Posts.update(post._id, {$set: {status: 1}});
|
|
|
|
}else{
|
|
|
|
throwError('You need to be an admin to do that.');
|
|
|
|
}
|
|
|
|
},
|
2013-11-06 10:11:35 +09:00
|
|
|
clickedPost: function(post, sessionId){
|
|
|
|
// only let clients increment a post's click counter once per session
|
|
|
|
var click = {_id: post._id, sessionId: sessionId};
|
|
|
|
if(_.where(clickedPosts, click).length == 0){
|
|
|
|
clickedPosts.push(click);
|
|
|
|
Posts.update(post._id, { $inc: { clicks: 1 }});
|
|
|
|
}
|
2013-07-19 14:30:39 +03:00
|
|
|
},
|
|
|
|
deletePostById: function(postId) {
|
|
|
|
// remove post comments
|
2013-11-08 11:10:23 +09:00
|
|
|
// if(!this.isSimulation) {
|
|
|
|
// Comments.remove({post: postId});
|
|
|
|
// }
|
2014-09-16 15:18:27 -04:00
|
|
|
// NOTE: actually, keep comments after all
|
2013-11-08 11:10:23 +09:00
|
|
|
|
|
|
|
// decrement post count
|
2013-11-11 20:53:02 +02:00
|
|
|
var post = Posts.findOne({_id: postId});
|
2013-11-11 21:15:04 +02:00
|
|
|
if(!Meteor.userId() || !canEditById(Meteor.userId(), post)) throw new Meteor.Error(606, 'You need permission to edit or delete a post');
|
2013-11-11 20:53:02 +02:00
|
|
|
|
2013-11-11 21:15:04 +02:00
|
|
|
Meteor.users.update({_id: post.userId}, {$inc: {postCount: -1}});
|
2013-07-19 14:30:39 +03:00
|
|
|
Posts.remove(postId);
|
2012-10-04 11:45:12 +10:00
|
|
|
}
|
2013-07-19 14:30:39 +03:00
|
|
|
});
|