Vulcan/packages/nova-posts/lib/methods.js

342 lines
10 KiB
JavaScript
Raw Normal View History

/**
*
* Post Methods
*
*/
Posts.methods = {};
/**
* 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
* @param {string} post.title - the post's title
*/
Posts.methods.new = 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);
// ------------------------------ Checks ------------------------------ //
// check that a title was provided
if(!post.title)
throw new Meteor.Error(602, __('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
if (post.status === Posts.config.STATUS_APPROVED && !post.postedAt)
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);
// ------------------------------ 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);
// -------------------------------- 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));
return post;
};
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
*/
Posts.methods.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);
// ------------------------------ After Update ------------------------------ //
return Posts.findOne(postId);
};
// ------------------------------------------------------------------------------------------- //
// ----------------------------------------- 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
*/
'posts.new': function(post){
check(post, Posts.simpleSchema());
// 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(),
hasAdminRights = Users.is.admin(user),
schema = Posts.simpleSchema()._schema;
// ------------------------------ Checks ------------------------------ //
// check that user can post
if (!user || !Users.can.post(user))
throw new Meteor.Error(601, __('you_need_to_login_or_be_invited_to_post_new_stories'));
// --------------------------- Rate Limiting -------------------------- //
if(!hasAdminRights){
var timeSinceLastPost = Users.timeSinceLast(user, Posts),
numberOfPostsInPast24Hours = Users.numberOfItemsInPast24Hours(user, Posts),
2016-02-16 15:08:30 +09:00
postInterval = Math.abs(parseInt(Telescope.settings.get('postInterval', 30))),
maxPostsPer24Hours = Math.abs(parseInt(Telescope.settings.get('maxPostsPerDay', 30)));
// check that user waits more than X seconds between posts
if(timeSinceLastPost < postInterval)
throw new Meteor.Error(604, __('please_wait')+(postInterval-timeSinceLastPost)+__('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, __('sorry_you_cannot_submit_more_than')+maxPostsPer24Hours+__('posts_per_day'));
}
// ------------------------------ Properties ------------------------------ //
// admin-only properties
// status
// postedAt
// userId
// sticky (default to false)
// go over each schema field and throw an error if it's not editable
_.keys(post).forEach(function (fieldName) {
var field = schema[fieldName];
if (!Users.can.submitField(user, field)) {
throw new Meteor.Error("disallowed_property", __('disallowed_property_detected') + ": " + fieldName);
}
});
// 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) {
post.userId = user._id;
}
2016-02-21 13:57:02 +09:00
if (Meteor.isServer) {
post.userIP = this.connection.clientAddress;
post.userAgent = this.connection.httpHeaders["user-agent"];
}
return Posts.methods.new(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
*/
'posts.edit': function (modifier, postId) {
// 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}));
check(postId, String);
var user = Meteor.user(),
post = Posts.findOne(postId),
schema = Posts.simpleSchema()._schema;
// ------------------------------ Checks ------------------------------ //
// check that user can edit document
if (!user || !Users.can.edit(user, post)) {
throw new Meteor.Error(601, __('sorry_you_cannot_edit_this_post'));
}
// 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];
if (!Users.can.editField(user, field, post)) {
throw new Meteor.Error("disallowed_property", __('disallowed_property_detected') + ": " + fieldName);
}
});
});
return Posts.methods.edit(postId, modifier, post);
},
'posts.approve': function(postId){
2015-07-10 11:40:11 +09:00
check(postId, String);
2015-10-03 13:03:39 +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-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});
Telescope.callbacks.runAsync("postApproveAsync", post);
}else{
Messages.flash('You need to be an admin to do that.', "error");
}
},
'posts.reject': function(postId){
2015-07-10 11:40:11 +09:00
check(postId, String);
var post = Posts.findOne(postId);
2015-04-27 17:14:07 +09:00
if(Users.is.admin(Meteor.user())){
Posts.update(post._id, {$set: {status: Posts.config.STATUS_REJECTED}});
Telescope.callbacks.runAsync("postRejectAsync", post);
}else{
Messages.flash('You need to be an admin to do that.', "error");
}
},
'posts.increaseViews': function(postId, sessionId){
check(postId, String);
2015-08-13 15:46:33 +09:00
check(sessionId, Match.Any);
2016-02-17 17:46:34 +09:00
// only let users increment a post's view counter once per session
var view = {_id: postId, userId: this.userId, sessionId: sessionId};
if(_.where(postViews, view).length === 0){
postViews.push(view);
Posts.update(postId, { $inc: { viewCount: 1 }});
}
},
'posts.deleteById': function(postId) {
2015-07-10 11:40:11 +09:00
check(postId, String);
// 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}});
// delete post
Posts.remove(postId);
Telescope.callbacks.runAsync("postDeleteAsync", post);
},
'posts.checkForDuplicates': function (url) {
Posts.checkForSameUrl(url);
2016-02-18 12:16:32 +09:00
},
'posts.upvote': function (postId) {
check(postId, String);
return Telescope.operateOnItem.call(this, Posts, postId, Meteor.user(), "upvote");
},
'posts.downvote': function (postId) {
check(postId, String);
return Telescope.operateOnItem.call(this, Posts, postId, Meteor.user(), "downvote");
},
'posts.cancelUpvote': function (postId) {
check(postId, String);
return Telescope.operateOnItem.call(this, Posts, postId, Meteor.user(), "cancelUpvote");
},
'posts.cancelDownvote': function (postId) {
check(postId, String);
return Telescope.operateOnItem.call(this, Posts, postId, Meteor.user(), "cancelDownvote");
}
});