2015-04-22 07:50:11 +09:00
|
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* Post Methods
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
2015-04-29 12:16:14 +09:00
|
|
|
|
/**
|
|
|
|
|
* Insert a post in the database (note: optional post properties not listed here)
|
2015-05-10 13:37:42 +09:00
|
|
|
|
* @param {Object} post - the post being inserted
|
|
|
|
|
* @param {string} post.userId - the id of the user the post belongs to
|
2015-04-29 12:16:14 +09:00
|
|
|
|
* @param {string} post.title - the post's title
|
|
|
|
|
*/
|
2015-04-22 07:50:11 +09:00
|
|
|
|
Posts.submit = function (post) {
|
|
|
|
|
|
|
|
|
|
var userId = post.userId, // at this stage, a userId is expected
|
2015-05-03 13:09:57 -04:00
|
|
|
|
user = Users.findOne(userId);
|
2015-04-22 07:50:11 +09:00
|
|
|
|
|
|
|
|
|
// ------------------------------ Checks ------------------------------ //
|
|
|
|
|
|
|
|
|
|
// check that a title was provided
|
|
|
|
|
if(!post.title)
|
|
|
|
|
throw new Meteor.Error(602, i18n.t('please_fill_in_a_title'));
|
|
|
|
|
|
|
|
|
|
// check that there are no posts with the same URL
|
|
|
|
|
if(!!post.url)
|
|
|
|
|
Posts.checkForSameUrl(post.url, user);
|
|
|
|
|
|
|
|
|
|
// ------------------------------ Properties ------------------------------ //
|
|
|
|
|
|
|
|
|
|
var defaultProperties = {
|
|
|
|
|
createdAt: new Date(),
|
|
|
|
|
author: Users.getDisplayNameById(userId),
|
|
|
|
|
upvotes: 0,
|
|
|
|
|
downvotes: 0,
|
|
|
|
|
commentCount: 0,
|
|
|
|
|
clickCount: 0,
|
|
|
|
|
viewCount: 0,
|
|
|
|
|
baseScore: 0,
|
|
|
|
|
score: 0,
|
|
|
|
|
inactive: false,
|
|
|
|
|
sticky: false,
|
|
|
|
|
status: Posts.getDefaultStatus()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
post = _.extend(defaultProperties, post);
|
|
|
|
|
|
|
|
|
|
// if post is approved but doesn't have a postedAt date, give it a default date
|
|
|
|
|
// note: pending posts get their postedAt date only once theyre approved
|
2015-05-01 18:22:00 +02:00
|
|
|
|
if (post.status === Posts.config.STATUS_APPROVED && !post.postedAt)
|
2015-04-22 07:50:11 +09:00
|
|
|
|
post.postedAt = new Date();
|
|
|
|
|
|
|
|
|
|
// clean up post title
|
|
|
|
|
post.title = Telescope.utils.cleanUp(post.title);
|
|
|
|
|
|
2015-06-18 13:04:38 +09:00
|
|
|
|
// generate slug
|
|
|
|
|
post.slug = Telescope.utils.slugify(post.title);
|
|
|
|
|
|
2015-04-22 07:50:11 +09:00
|
|
|
|
// ------------------------------ Callbacks ------------------------------ //
|
|
|
|
|
|
|
|
|
|
// run all post submit server callbacks on post object successively
|
2015-04-24 09:48:36 +09:00
|
|
|
|
post = Telescope.callbacks.run("postSubmit", post);
|
2015-04-22 07:50:11 +09:00
|
|
|
|
|
|
|
|
|
// -------------------------------- Insert ------------------------------- //
|
|
|
|
|
|
|
|
|
|
post._id = Posts.insert(post);
|
|
|
|
|
|
|
|
|
|
// --------------------- Server-Side Async Callbacks --------------------- //
|
|
|
|
|
|
2015-06-24 15:38:14 +09:00
|
|
|
|
// note: query for post to get fresh document with collection-hooks effects applied
|
|
|
|
|
Telescope.callbacks.runAsync("postSubmitAsync", Posts.findOne(post._id));
|
2015-04-22 07:50:11 +09:00
|
|
|
|
|
|
|
|
|
return post;
|
2015-05-01 18:22:00 +02:00
|
|
|
|
};
|
2015-04-22 07:50:11 +09:00
|
|
|
|
|
2015-05-10 13:37:42 +09:00
|
|
|
|
/**
|
|
|
|
|
* Edit a post in the database
|
|
|
|
|
* @param {string} postId – the ID of the post being edited
|
|
|
|
|
* @param {Object} modifier – the modifier object
|
|
|
|
|
* @param {Object} post - the current post object
|
|
|
|
|
*/
|
2015-05-04 10:19:50 +09:00
|
|
|
|
Posts.edit = function (postId, modifier, post) {
|
|
|
|
|
|
|
|
|
|
if (typeof post === "undefined") {
|
|
|
|
|
post = Posts.findOne(postId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ------------------------------ Callbacks ------------------------------ //
|
|
|
|
|
|
|
|
|
|
modifier = Telescope.callbacks.run("postEdit", modifier, post);
|
|
|
|
|
|
|
|
|
|
// ------------------------------ Update ------------------------------ //
|
|
|
|
|
|
|
|
|
|
Posts.update(postId, modifier);
|
|
|
|
|
|
|
|
|
|
// ------------------------------ Callbacks ------------------------------ //
|
|
|
|
|
|
2015-08-22 18:13:37 +09:00
|
|
|
|
Telescope.callbacks.runAsync("postEditAsync", Posts.findOne(postId), post);
|
2015-05-04 10:19:50 +09:00
|
|
|
|
|
|
|
|
|
// ------------------------------ After Update ------------------------------ //
|
|
|
|
|
return Posts.findOne(postId);
|
|
|
|
|
};
|
|
|
|
|
|
2015-04-22 07:50:11 +09:00
|
|
|
|
// ------------------------------------------------------------------------------------------- //
|
|
|
|
|
// ----------------------------------------- Methods ----------------------------------------- //
|
|
|
|
|
// ------------------------------------------------------------------------------------------- //
|
|
|
|
|
|
|
|
|
|
var postViews = [];
|
|
|
|
|
|
|
|
|
|
Meteor.methods({
|
|
|
|
|
|
2015-05-10 13:37:42 +09:00
|
|
|
|
/**
|
|
|
|
|
* Meteor method for submitting a post from the client
|
|
|
|
|
* @memberof Posts
|
|
|
|
|
* @param {Object} post - the post being inserted
|
|
|
|
|
*/
|
2015-04-22 07:50:11 +09:00
|
|
|
|
submitPost: function(post){
|
|
|
|
|
|
2015-07-10 11:05:13 +09:00
|
|
|
|
check(post, Posts.simpleSchema());
|
|
|
|
|
|
2015-04-22 07:50:11 +09:00
|
|
|
|
// required properties:
|
|
|
|
|
// title
|
|
|
|
|
|
|
|
|
|
// optional properties
|
|
|
|
|
// URL
|
|
|
|
|
// body
|
|
|
|
|
// categories
|
|
|
|
|
// thumbnailUrl
|
|
|
|
|
|
|
|
|
|
// NOTE: the current user and the post author user might be two different users!
|
|
|
|
|
var user = Meteor.user(),
|
2015-04-28 10:45:00 +09:00
|
|
|
|
hasAdminRights = Users.is.admin(user),
|
|
|
|
|
schema = Posts.simpleSchema()._schema;
|
2015-04-22 07:50:11 +09:00
|
|
|
|
|
|
|
|
|
// ------------------------------ Checks ------------------------------ //
|
|
|
|
|
|
|
|
|
|
// check that user can post
|
|
|
|
|
if (!user || !Users.can.post(user))
|
|
|
|
|
throw new Meteor.Error(601, i18n.t('you_need_to_login_or_be_invited_to_post_new_stories'));
|
|
|
|
|
|
|
|
|
|
// --------------------------- Rate Limiting -------------------------- //
|
|
|
|
|
|
|
|
|
|
if(!hasAdminRights){
|
|
|
|
|
|
2015-04-28 15:54:19 +09:00
|
|
|
|
var timeSinceLastPost = Users.timeSinceLast(user, Posts),
|
|
|
|
|
numberOfPostsInPast24Hours = Users.numberOfItemsInPast24Hours(user, Posts),
|
2015-04-22 07:50:11 +09:00
|
|
|
|
postInterval = Math.abs(parseInt(Settings.get('postInterval', 30))),
|
|
|
|
|
maxPostsPer24Hours = Math.abs(parseInt(Settings.get('maxPostsPerDay', 30)));
|
|
|
|
|
|
|
|
|
|
// check that user waits more than X seconds between posts
|
|
|
|
|
if(timeSinceLastPost < postInterval)
|
|
|
|
|
throw new Meteor.Error(604, i18n.t('please_wait')+(postInterval-timeSinceLastPost)+i18n.t('seconds_before_posting_again'));
|
|
|
|
|
|
|
|
|
|
// check that the user doesn't post more than Y posts per day
|
|
|
|
|
if(numberOfPostsInPast24Hours > maxPostsPer24Hours)
|
|
|
|
|
throw new Meteor.Error(605, i18n.t('sorry_you_cannot_submit_more_than')+maxPostsPer24Hours+i18n.t('posts_per_day'));
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ------------------------------ Properties ------------------------------ //
|
|
|
|
|
|
|
|
|
|
// admin-only properties
|
|
|
|
|
// status
|
|
|
|
|
// postedAt
|
|
|
|
|
// userId
|
|
|
|
|
// sticky (default to false)
|
|
|
|
|
|
2015-04-28 10:45:00 +09:00
|
|
|
|
// go over each schema field and throw an error if it's not editable
|
|
|
|
|
_.keys(post).forEach(function (fieldName) {
|
|
|
|
|
|
|
|
|
|
var field = schema[fieldName];
|
2015-04-28 15:54:19 +09:00
|
|
|
|
if (!Users.can.submitField(user, field)) {
|
2015-04-28 10:45:00 +09:00
|
|
|
|
throw new Meteor.Error("disallowed_property", i18n.t('disallowed_property_detected') + ": " + fieldName);
|
|
|
|
|
}
|
2015-04-28 11:32:53 +09:00
|
|
|
|
|
2015-04-28 10:45:00 +09:00
|
|
|
|
});
|
2015-04-22 07:50:11 +09:00
|
|
|
|
|
|
|
|
|
// if no post status has been set, set it now
|
|
|
|
|
if (!post.status) {
|
|
|
|
|
post.status = Posts.getDefaultStatus(user);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// if no userId has been set, default to current user id
|
|
|
|
|
if (!post.userId) {
|
2015-05-01 18:22:00 +02:00
|
|
|
|
post.userId = user._id;
|
2015-04-22 07:50:11 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Posts.submit(post);
|
|
|
|
|
},
|
|
|
|
|
|
2015-05-10 13:37:42 +09:00
|
|
|
|
/**
|
|
|
|
|
* Meteor method for editing a post from the client
|
|
|
|
|
* @memberof Posts
|
|
|
|
|
* @param {Object} modifier - the update modifier
|
|
|
|
|
* @param {Object} postId - the id of the post being updated
|
|
|
|
|
*/
|
2015-04-22 07:50:11 +09:00
|
|
|
|
editPost: function (modifier, postId) {
|
|
|
|
|
|
2015-07-10 11:05:13 +09:00
|
|
|
|
// checking might be redundant because SimpleSchema already enforces the schema, but you never know
|
2015-07-14 11:40:58 +09:00
|
|
|
|
check(modifier, Match.OneOf({$set: Posts.simpleSchema()}, {$unset: Object}, {$set: Posts.simpleSchema(), $unset: Object}));
|
2015-07-10 11:05:13 +09:00
|
|
|
|
check(postId, String);
|
|
|
|
|
|
2015-04-22 07:50:11 +09:00
|
|
|
|
var user = Meteor.user(),
|
2015-04-28 10:45:00 +09:00
|
|
|
|
post = Posts.findOne(postId),
|
|
|
|
|
schema = Posts.simpleSchema()._schema;
|
2015-04-22 07:50:11 +09:00
|
|
|
|
|
|
|
|
|
// ------------------------------ Checks ------------------------------ //
|
|
|
|
|
|
2015-04-28 10:45:00 +09:00
|
|
|
|
// check that user can edit document
|
|
|
|
|
if (!user || !Users.can.edit(user, post)) {
|
2015-04-22 07:50:11 +09:00
|
|
|
|
throw new Meteor.Error(601, i18n.t('sorry_you_cannot_edit_this_post'));
|
2015-04-28 10:45:00 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// go over each field and throw an error if it's not editable
|
|
|
|
|
// loop over each operation ($set, $unset, etc.)
|
|
|
|
|
_.each(modifier, function (operation) {
|
|
|
|
|
// loop over each property being operated on
|
|
|
|
|
_.keys(operation).forEach(function (fieldName) {
|
|
|
|
|
|
|
|
|
|
var field = schema[fieldName];
|
2015-04-28 15:54:19 +09:00
|
|
|
|
if (!Users.can.editField(user, field, post)) {
|
2015-04-28 10:45:00 +09:00
|
|
|
|
throw new Meteor.Error("disallowed_property", i18n.t('disallowed_property_detected') + ": " + fieldName);
|
|
|
|
|
}
|
2015-04-22 07:50:11 +09:00
|
|
|
|
|
|
|
|
|
});
|
2015-04-28 10:45:00 +09:00
|
|
|
|
});
|
2015-04-22 07:50:11 +09:00
|
|
|
|
|
2015-05-04 10:19:50 +09:00
|
|
|
|
return Posts.edit(postId, modifier, post);
|
2015-04-22 07:50:11 +09:00
|
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
setPostedAt: function(post, customPostedAt){
|
|
|
|
|
|
2015-07-10 11:40:11 +09:00
|
|
|
|
// this method is not actually used?
|
|
|
|
|
|
|
|
|
|
check(post, Posts.simpleSchema());
|
|
|
|
|
check(customPostedAt, Date);
|
|
|
|
|
|
2015-04-22 07:50:11 +09:00
|
|
|
|
var postedAt = new Date(); // default to current date and time
|
|
|
|
|
|
2015-04-27 17:14:07 +09:00
|
|
|
|
if(Users.is.admin(Meteor.user()) && typeof customPostedAt !== 'undefined') // if user is admin and a custom datetime has been set
|
2015-04-22 07:50:11 +09:00
|
|
|
|
postedAt = customPostedAt;
|
|
|
|
|
|
|
|
|
|
Posts.update(post._id, {$set: {postedAt: postedAt}});
|
|
|
|
|
},
|
|
|
|
|
|
2015-07-28 10:59:04 +09:00
|
|
|
|
approvePost: function(postId){
|
2015-07-10 11:40:11 +09:00
|
|
|
|
|
2015-07-28 10:59:04 +09:00
|
|
|
|
check(postId, String);
|
2015-10-03 13:03:39 +09:00
|
|
|
|
|
2015-07-28 10:59:04 +09:00
|
|
|
|
var post = Posts.findOne(postId);
|
2015-10-03 13:03:39 +09:00
|
|
|
|
var now = new Date();
|
2015-07-10 11:40:11 +09:00
|
|
|
|
|
2015-04-27 17:14:07 +09:00
|
|
|
|
if(Users.is.admin(Meteor.user())){
|
2015-04-22 07:50:11 +09:00
|
|
|
|
|
2015-10-03 13:03:39 +09:00
|
|
|
|
var set = {status: Posts.config.STATUS_APPROVED};
|
|
|
|
|
|
|
|
|
|
if (!post.postedAt) {
|
|
|
|
|
set.postedAt = now;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Posts.update(post._id, {$set: set});
|
2015-04-22 07:50:11 +09:00
|
|
|
|
|
2015-09-06 11:37:48 +09:00
|
|
|
|
Telescope.callbacks.runAsync("postApproveAsync", post);
|
2015-04-22 07:50:11 +09:00
|
|
|
|
|
|
|
|
|
}else{
|
|
|
|
|
Messages.flash('You need to be an admin to do that.', "error");
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
2015-09-06 11:37:48 +09:00
|
|
|
|
rejectPost: function(postId){
|
2015-07-10 11:40:11 +09:00
|
|
|
|
|
2015-07-28 10:59:04 +09:00
|
|
|
|
check(postId, String);
|
|
|
|
|
var post = Posts.findOne(postId);
|
|
|
|
|
|
2015-04-27 17:14:07 +09:00
|
|
|
|
if(Users.is.admin(Meteor.user())){
|
2015-09-06 11:37:48 +09:00
|
|
|
|
|
|
|
|
|
Posts.update(post._id, {$set: {status: Posts.config.STATUS_REJECTED}});
|
|
|
|
|
|
|
|
|
|
Telescope.callbacks.runAsync("postRejectAsync", post);
|
|
|
|
|
|
2015-04-22 07:50:11 +09:00
|
|
|
|
}else{
|
|
|
|
|
Messages.flash('You need to be an admin to do that.', "error");
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
increasePostViews: function(postId, sessionId){
|
2015-07-10 11:05:13 +09:00
|
|
|
|
|
|
|
|
|
check(postId, String);
|
2015-08-13 15:46:33 +09:00
|
|
|
|
check(sessionId, Match.Any);
|
2015-07-10 11:05:13 +09:00
|
|
|
|
|
2015-04-22 07:50:11 +09:00
|
|
|
|
this.unblock();
|
|
|
|
|
|
|
|
|
|
// only let users increment a post's view counter once per session
|
|
|
|
|
var view = {_id: postId, userId: this.userId, sessionId: sessionId};
|
|
|
|
|
|
2015-05-01 18:22:00 +02:00
|
|
|
|
if(_.where(postViews, view).length === 0){
|
2015-04-22 07:50:11 +09:00
|
|
|
|
postViews.push(view);
|
|
|
|
|
Posts.update(postId, { $inc: { viewCount: 1 }});
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
deletePostById: function(postId) {
|
2015-07-10 11:40:11 +09:00
|
|
|
|
|
|
|
|
|
check(postId, String);
|
|
|
|
|
|
2015-04-22 07:50:11 +09:00
|
|
|
|
// remove post comments
|
|
|
|
|
// if(!this.isSimulation) {
|
|
|
|
|
// Comments.remove({post: postId});
|
|
|
|
|
// }
|
|
|
|
|
// NOTE: actually, keep comments after all
|
|
|
|
|
|
|
|
|
|
var post = Posts.findOne({_id: postId});
|
|
|
|
|
|
|
|
|
|
if(!Meteor.userId() || !Users.can.editById(Meteor.userId(), post)) throw new Meteor.Error(606, 'You need permission to edit or delete a post');
|
|
|
|
|
|
|
|
|
|
// decrement post count
|
2015-05-10 13:37:42 +09:00
|
|
|
|
Users.update({_id: post.userId}, {$inc: {"telescope.postCount": -1}});
|
2015-04-22 07:50:11 +09:00
|
|
|
|
|
|
|
|
|
// delete post
|
|
|
|
|
Posts.remove(postId);
|
2015-08-24 09:56:21 +09:00
|
|
|
|
|
|
|
|
|
Telescope.callbacks.runAsync("postDeleteAsync", post);
|
|
|
|
|
|
2015-09-19 10:33:37 +09:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
checkForDuplicates: function (url) {
|
|
|
|
|
Posts.checkForSameUrl(url);
|
2015-04-22 07:50:11 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
});
|