This commit is contained in:
Sacha Greif 2016-07-11 11:47:55 +09:00
commit 4194595944
8 changed files with 77 additions and 11 deletions

View file

@ -62,7 +62,7 @@ class PostsItem extends Component {
<div className="posts-item-meta"> <div className="posts-item-meta">
{post.user? <div className="posts-item-user"><Telescope.components.UsersAvatar user={post.user} size="small"/><Telescope.components.UsersName user={post.user}/></div> : null} {post.user? <div className="posts-item-user"><Telescope.components.UsersAvatar user={post.user} size="small"/><Telescope.components.UsersName user={post.user}/></div> : null}
<div className="posts-item-date"><FormattedRelative value={post.postedAt}/></div> <div className="posts-item-date">{post.postedAt ? <FormattedRelative value={post.postedAt}/> : <FormattedMessage id="posts.dateNotDefined"/>}</div>
<div className="posts-item-comments"> <div className="posts-item-comments">
<Link to={Posts.getPageUrl(post)}> <Link to={Posts.getPageUrl(post)}>
<FormattedMessage id="comments.count" values={{count: post.commentCount}}/> <FormattedMessage id="comments.count" values={{count: post.commentCount}}/>

View file

@ -1,17 +1,23 @@
import React, { PropTypes, Component } from 'react'; import React, { PropTypes, Component } from 'react';
import DateTimeField from 'react-datetime'; import DateTimePicker from 'react-datetime';
import moment from 'moment'; import moment from 'moment';
class DateTime extends Component { class DateTime extends Component {
// when the datetime picker mounts, NovaForm will catch the date value (no formsy mixin in this component)
componentWillMount() {
this.props.updateCurrentValue(this.props.name, this.props.value || new Date());
}
render() { render() {
return ( return (
<div className="form-group row"> <div className="form-group row">
<label className="control-label col-sm-3">{this.props.label}</label> <label className="control-label col-sm-3">{this.props.label}</label>
<div className="col-sm-9"> <div className="col-sm-9">
<DateTimeField <DateTimePicker
value={this.props.value || new Date()} value={this.props.value || new Date()}
defaultValue={moment().format("x")} // newDate argument is a Moment object given by react-datetime
onChange={newDate => this.props.updateCurrentValue(this.props.name, newDate._d)} onChange={newDate => { this.props.updateCurrentValue(this.props.name, newDate._d)}}
format={"x"} format={"x"}
inputProps={{name: this.props.name}} inputProps={{name: this.props.name}}
/> />

View file

@ -30,6 +30,7 @@ Telescope.strings.en = {
"posts.created_message": "Post created.", "posts.created_message": "Post created.",
"posts.rate_limit_error": "Please wait {details} seconds before posting again.", "posts.rate_limit_error": "Please wait {details} seconds before posting again.",
"posts.postedAt": "Posted at", "posts.postedAt": "Posted at",
"posts.dateNotDefined": "Date not defined",
"comments.comments": "Comments", "comments.comments": "Comments",
"comments.count": "{count, plural, =0 {No comments} one {# comment} other {# comments}}", "comments.count": "{count, plural, =0 {No comments} one {# comment} other {# comments}}",

View file

@ -160,11 +160,11 @@ function PostsNewSubmittedPropertiesCheck (post, user) {
} }
}); });
// note: not needed there anymore, this is already set in the next callback 'posts.new.sync' with other related properties (status, createdAt)
// if no post status has been set, set it now // if no post status has been set, set it now
if (!post.status) { // if (!post.status) {
post.status = Posts.getDefaultStatus(user); // post.status = Posts.getDefaultStatus(user);
} // }
// if no userId has been set, default to current user id // if no userId has been set, default to current user id
if (!post.userId) { if (!post.userId) {
@ -197,7 +197,7 @@ function PostsNewRequiredPropertiesCheck (post, user) {
const defaultProperties = { const defaultProperties = {
createdAt: new Date(), createdAt: new Date(),
author: Users.getDisplayNameById(post.userId), author: Users.getDisplayNameById(post.userId),
status: Posts.getDefaultStatus() status: Posts.getDefaultStatus(user)
}; };
post = _.extend(defaultProperties, post); post = _.extend(defaultProperties, post);
@ -205,6 +205,11 @@ function PostsNewRequiredPropertiesCheck (post, user) {
// generate slug // generate slug
post.slug = Telescope.utils.slugify(post.title); post.slug = Telescope.utils.slugify(post.title);
// post is not pending and has been scheduled to be posted in the future by a moderator/admin
if (post.status !== Posts.config.STATUS_PENDING && post.postedAt && post.postedAt > post.createdAt) {
post.status = Posts.config.STATUS_SCHEDULED;
}
// if post is approved but doesn't have a postedAt date, give it a default date // 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 // note: pending posts get their postedAt date only once theyre approved
if (Posts.isApproved(post) && !post.postedAt) { if (Posts.isApproved(post) && !post.postedAt) {
@ -318,6 +323,12 @@ Telescope.callbacks.add("posts.edit.sync", PostsEditForceStickyToFalse);
* @summary Set postedAt date * @summary Set postedAt date
*/ */
function PostsEditSetPostedAt (post, oldPost) { function PostsEditSetPostedAt (post, oldPost) {
// post is not pending and has been scheduled to be posted in the future by a moderator/admin
if (post.status !== Posts.config.STATUS_PENDING && post.postedAt && post.postedAt > new Date()) {
Posts.update(post._id, {$set: {status: Posts.config.STATUS_SCHEDULED}});
}
// if post is approved but doesn't have a postedAt date, give it a default date // 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 // note: pending posts get their postedAt date only once theyre approved
if (Posts.isApproved(post) && !post.postedAt) { if (Posts.isApproved(post) && !post.postedAt) {

View file

@ -30,6 +30,10 @@ Posts.config.postStatuses = [
{ {
value: 5, value: 5,
label: 'deleted' label: 'deleted'
},
{
value: 6,
label: 'scheduled'
} }
]; ];
@ -38,6 +42,7 @@ Posts.config.STATUS_APPROVED = 2;
Posts.config.STATUS_REJECTED = 3; Posts.config.STATUS_REJECTED = 3;
Posts.config.STATUS_SPAM = 4; Posts.config.STATUS_SPAM = 4;
Posts.config.STATUS_DELETED = 5; Posts.config.STATUS_DELETED = 5;
Posts.config.STATUS_SCHEDULED = 6;
const adminGroup = { const adminGroup = {
name: "admin", name: "admin",

View file

@ -2,5 +2,6 @@ import Posts from './modules.js';
import './server/publications.js'; import './server/publications.js';
import './server/routes.js'; import './server/routes.js';
import './server/cron.js';
export default Posts; export default Posts;

View file

@ -0,0 +1,40 @@
import Posts from '../collection.js';
import moment from 'moment';
SyncedCron.options = {
log: true,
collectionName: 'cronHistory',
utc: false,
collectionTTL: 172800
};
const addJob = function () {
SyncedCron.add({
name: 'checkScheduledPosts',
schedule(parser) {
return parser.text('every 10 minutes');
},
job() {
// fetch all posts tagged as scheduled
const scheduledPosts = Posts.find({status: Posts.config.STATUS_SCHEDULED}, {fields: {_id: 1, status: 1, postedAt: 1, userId: 1, title: 1}}).fetch();
if (scheduledPosts) {
// filter the scheduled posts to retrieve only the one that should update, considering their schedule
const postsToUpdate = scheduledPosts.filter(post => post.postedAt <= new Date());
// update all posts with status approved
const postsIds = _.pluck(postsToUpdate, '_id');
Posts.update({_id: {$in: postsIds}}, {$set: {status: Posts.config.STATUS_APPROVED}}, {multi: true});
// log the action
console.log('// Scheduled posts approved:', postsIds);
}
}
});
};
Meteor.startup(function () {
addJob();
});

View file

@ -83,7 +83,9 @@ Posts.views.add("rejected", function (terms) {
*/ */
Posts.views.add("scheduled", function (terms) { Posts.views.add("scheduled", function (terms) {
return { return {
selector: {postedAt: {$gte: new Date()}}, selector: {
status: Posts.config.STATUS_SCHEDULED
},
options: {sort: {postedAt: -1}}, options: {sort: {postedAt: -1}},
showFuture: true showFuture: true
}; };