2016-08-10 10:40:17 +09:00
|
|
|
import Telescope from 'meteor/nova:lib';
|
2016-06-23 15:00:58 +09:00
|
|
|
import Posts from './collection.js';
|
|
|
|
import Users from 'meteor/nova:users';
|
2016-11-14 17:17:44 +09:00
|
|
|
import marked from 'marked';
|
2016-06-23 15:00:58 +09:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @summary Posts config namespace
|
|
|
|
* @type {Object}
|
|
|
|
*/
|
|
|
|
Posts.config = {};
|
|
|
|
|
|
|
|
Posts.config.STATUS_PENDING = 1;
|
|
|
|
Posts.config.STATUS_APPROVED = 2;
|
|
|
|
Posts.config.STATUS_REJECTED = 3;
|
|
|
|
Posts.config.STATUS_SPAM = 4;
|
|
|
|
Posts.config.STATUS_DELETED = 5;
|
|
|
|
|
2016-07-19 15:13:16 +09:00
|
|
|
Posts.formGroups = {
|
2016-07-13 11:46:45 +09:00
|
|
|
admin: {
|
|
|
|
name: "admin",
|
|
|
|
order: 2
|
|
|
|
}
|
|
|
|
};
|
2016-06-23 15:00:58 +09:00
|
|
|
|
2016-07-21 09:40:05 +09:00
|
|
|
// check if user can create a new post
|
|
|
|
const canInsert = user => Users.canDo(user, "posts.new");
|
|
|
|
|
|
|
|
// check if user can edit a post
|
|
|
|
const canEdit = Users.canEdit;
|
|
|
|
|
|
|
|
// check if user can edit *all* posts
|
|
|
|
const canEditAll = user => Users.canDo(user, "posts.edit.all");
|
|
|
|
|
2016-10-29 14:17:57 +09:00
|
|
|
const alwaysPublic = user => true;
|
|
|
|
|
2016-06-23 15:00:58 +09:00
|
|
|
/**
|
|
|
|
* @summary Posts schema
|
|
|
|
* @type {SimpleSchema}
|
|
|
|
*/
|
|
|
|
Posts.schemaJSON = {
|
|
|
|
/**
|
|
|
|
ID
|
|
|
|
*/
|
|
|
|
_id: {
|
|
|
|
type: String,
|
|
|
|
optional: true,
|
2016-11-08 15:16:58 +09:00
|
|
|
publish: true,
|
|
|
|
viewableIf: alwaysPublic,
|
2016-06-23 15:00:58 +09:00
|
|
|
},
|
|
|
|
/**
|
|
|
|
Timetstamp of post creation
|
|
|
|
*/
|
|
|
|
createdAt: {
|
|
|
|
type: Date,
|
|
|
|
optional: true,
|
2016-10-29 14:17:57 +09:00
|
|
|
viewableIf: canEditAll,
|
2016-11-14 17:17:44 +09:00
|
|
|
publish: true, // publish so that admins can sort pending posts by createdAt
|
|
|
|
autoValue: (documentOrModifier) => {
|
|
|
|
if (documentOrModifier && !documentOrModifier.$set) return new Date() // if this is an insert, set createdAt to current timestamp
|
|
|
|
}
|
2016-06-23 15:00:58 +09:00
|
|
|
},
|
|
|
|
/**
|
|
|
|
Timestamp of post first appearing on the site (i.e. being approved)
|
|
|
|
*/
|
|
|
|
postedAt: {
|
|
|
|
type: Date,
|
|
|
|
optional: true,
|
2016-10-29 14:17:57 +09:00
|
|
|
viewableIf: alwaysPublic,
|
2016-07-21 09:40:05 +09:00
|
|
|
insertableIf: canEditAll,
|
|
|
|
editableIf: canEditAll,
|
2016-06-23 15:00:58 +09:00
|
|
|
publish: true,
|
|
|
|
control: "datetime",
|
2016-07-19 15:13:16 +09:00
|
|
|
group: Posts.formGroups.admin
|
2016-06-23 15:00:58 +09:00
|
|
|
},
|
|
|
|
/**
|
|
|
|
URL
|
|
|
|
*/
|
|
|
|
url: {
|
|
|
|
type: String,
|
|
|
|
optional: true,
|
|
|
|
max: 500,
|
2016-10-29 14:17:57 +09:00
|
|
|
viewableIf: alwaysPublic,
|
2016-07-21 09:40:05 +09:00
|
|
|
insertableIf: canInsert,
|
|
|
|
editableIf: canEdit,
|
2016-06-23 15:00:58 +09:00
|
|
|
control: "text",
|
|
|
|
publish: true,
|
|
|
|
order: 10
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
Title
|
|
|
|
*/
|
|
|
|
title: {
|
|
|
|
type: String,
|
|
|
|
optional: false,
|
|
|
|
max: 500,
|
2016-10-29 14:17:57 +09:00
|
|
|
viewableIf: alwaysPublic,
|
2016-07-21 09:40:05 +09:00
|
|
|
insertableIf: canInsert,
|
|
|
|
editableIf: canEdit,
|
2016-06-23 15:00:58 +09:00
|
|
|
control: "text",
|
|
|
|
publish: true,
|
|
|
|
order: 20
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
Slug
|
|
|
|
*/
|
|
|
|
slug: {
|
|
|
|
type: String,
|
|
|
|
optional: true,
|
2016-10-29 14:17:57 +09:00
|
|
|
viewableIf: alwaysPublic,
|
2016-06-23 15:00:58 +09:00
|
|
|
publish: true,
|
2016-11-14 17:17:44 +09:00
|
|
|
autoValue: (documentOrModifier) => {
|
|
|
|
// if title is changing, return new slug
|
|
|
|
const newTitle = documentOrModifier.title || documentOrModifier.$set && documentOrModifier.$set.title
|
|
|
|
if (newTitle) {
|
|
|
|
return Telescope.utils.slugify(newTitle)
|
|
|
|
}
|
|
|
|
}
|
2016-06-23 15:00:58 +09:00
|
|
|
},
|
|
|
|
/**
|
|
|
|
Post body (markdown)
|
|
|
|
*/
|
|
|
|
body: {
|
|
|
|
type: String,
|
|
|
|
optional: true,
|
|
|
|
max: 3000,
|
2016-10-29 14:17:57 +09:00
|
|
|
viewableIf: alwaysPublic,
|
2016-07-21 09:40:05 +09:00
|
|
|
insertableIf: canInsert,
|
|
|
|
editableIf: canEdit,
|
2016-06-23 15:00:58 +09:00
|
|
|
control: "textarea",
|
|
|
|
publish: true,
|
|
|
|
order: 30
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
HTML version of the post body
|
|
|
|
*/
|
|
|
|
htmlBody: {
|
|
|
|
type: String,
|
|
|
|
optional: true,
|
|
|
|
publish: true,
|
2016-10-29 14:17:57 +09:00
|
|
|
viewableIf: alwaysPublic,
|
2016-11-14 17:17:44 +09:00
|
|
|
autoValue(documentOrModifier) {
|
|
|
|
const body = documentOrModifier.body || documentOrModifier.$set && documentOrModifier.$set.body;
|
|
|
|
if (body) {
|
|
|
|
return Telescope.utils.sanitize(marked(body))
|
|
|
|
} else if (documentOrModifier.$unset && documentOrModifier.$unset.body) {
|
|
|
|
return ''
|
|
|
|
}
|
|
|
|
}
|
2016-06-23 15:00:58 +09:00
|
|
|
},
|
|
|
|
/**
|
|
|
|
Post Excerpt
|
|
|
|
*/
|
|
|
|
excerpt: {
|
|
|
|
type: String,
|
|
|
|
optional: true,
|
|
|
|
max: 255, //should not be changed the 255 is max we should load for each post/item
|
|
|
|
publish: true,
|
2016-10-29 14:17:57 +09:00
|
|
|
viewableIf: alwaysPublic,
|
2016-11-14 17:17:44 +09:00
|
|
|
autoValue(documentOrModifier) {
|
|
|
|
const body = documentOrModifier.body || documentOrModifier.$set && documentOrModifier.$set.body;
|
|
|
|
if (body) {
|
|
|
|
return Telescope.utils.trimHTML(Telescope.utils.sanitize(marked(body)), 30);
|
|
|
|
} else if (documentOrModifier.$unset && documentOrModifier.$unset.body) {
|
|
|
|
return ''
|
|
|
|
}
|
|
|
|
}
|
2016-06-23 15:00:58 +09:00
|
|
|
},
|
|
|
|
/**
|
|
|
|
Count of how many times the post's page was viewed
|
|
|
|
*/
|
|
|
|
viewCount: {
|
|
|
|
type: Number,
|
|
|
|
optional: true,
|
|
|
|
publish: true,
|
2016-10-29 14:17:57 +09:00
|
|
|
viewableIf: alwaysPublic,
|
2016-06-23 15:00:58 +09:00
|
|
|
defaultValue: 0
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
Timestamp of the last comment
|
|
|
|
*/
|
|
|
|
lastCommentedAt: {
|
|
|
|
type: Date,
|
|
|
|
optional: true,
|
|
|
|
publish: true,
|
2016-10-29 14:17:57 +09:00
|
|
|
viewableIf: alwaysPublic,
|
2016-06-23 15:00:58 +09:00
|
|
|
},
|
|
|
|
/**
|
|
|
|
Count of how many times the post's link was clicked
|
|
|
|
*/
|
|
|
|
clickCount: {
|
|
|
|
type: Number,
|
|
|
|
optional: true,
|
|
|
|
publish: true,
|
2016-10-29 14:17:57 +09:00
|
|
|
viewableIf: canEditAll,
|
2016-06-23 15:00:58 +09:00
|
|
|
defaultValue: 0
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
The post's status. One of pending (`1`), approved (`2`), or deleted (`3`)
|
|
|
|
*/
|
|
|
|
status: {
|
|
|
|
type: Number,
|
|
|
|
optional: true,
|
2016-10-29 14:17:57 +09:00
|
|
|
viewableIf: alwaysPublic,
|
2016-07-21 09:40:05 +09:00
|
|
|
insertableIf: canEditAll,
|
|
|
|
editableIf: canEditAll,
|
2016-06-23 15:00:58 +09:00
|
|
|
control: "select",
|
|
|
|
publish: true,
|
2016-11-14 17:17:44 +09:00
|
|
|
autoValue(documentOrModifier) {
|
|
|
|
// provide a default value if this is an insert operation and status field is not set in the document
|
|
|
|
if (documentOrModifier && !documentOrModifier.$set && documentOrModifier.userId) {
|
|
|
|
const user = Users.findOne(documentOrModifier.userId);
|
2016-06-23 15:00:58 +09:00
|
|
|
return Posts.getDefaultStatus(user);
|
2016-11-14 17:17:44 +09:00
|
|
|
}
|
2016-06-23 15:00:58 +09:00
|
|
|
},
|
2016-10-05 08:43:13 +02:00
|
|
|
form: {
|
2016-06-23 15:00:58 +09:00
|
|
|
noselect: true,
|
2016-08-10 10:40:17 +09:00
|
|
|
options: Telescope.statuses,
|
2016-06-23 15:00:58 +09:00
|
|
|
group: 'admin'
|
|
|
|
},
|
2016-07-19 15:13:16 +09:00
|
|
|
group: Posts.formGroups.admin
|
2016-06-23 15:00:58 +09:00
|
|
|
},
|
2016-07-14 15:54:59 +09:00
|
|
|
/**
|
|
|
|
Whether a post is scheduled in the future or not
|
|
|
|
*/
|
|
|
|
isFuture: {
|
|
|
|
type: Boolean,
|
|
|
|
optional: true,
|
2016-10-29 14:17:57 +09:00
|
|
|
viewableIf: alwaysPublic,
|
2016-07-14 15:54:59 +09:00
|
|
|
publish: true
|
|
|
|
},
|
2016-06-23 15:00:58 +09:00
|
|
|
/**
|
|
|
|
Whether the post is sticky (pinned to the top of posts lists)
|
|
|
|
*/
|
|
|
|
sticky: {
|
|
|
|
type: Boolean,
|
|
|
|
optional: true,
|
|
|
|
defaultValue: false,
|
2016-10-29 14:17:57 +09:00
|
|
|
viewableIf: alwaysPublic,
|
2016-07-21 09:40:05 +09:00
|
|
|
insertableIf: canEditAll,
|
|
|
|
editableIf: canEditAll,
|
2016-06-23 15:00:58 +09:00
|
|
|
control: "checkbox",
|
|
|
|
publish: true,
|
2016-07-19 15:13:16 +09:00
|
|
|
group: Posts.formGroups.admin
|
2016-06-23 15:00:58 +09:00
|
|
|
},
|
|
|
|
/**
|
|
|
|
Whether the post is inactive. Inactive posts see their score recalculated less often
|
|
|
|
*/
|
|
|
|
inactive: {
|
|
|
|
type: Boolean,
|
|
|
|
optional: true,
|
|
|
|
publish: false,
|
|
|
|
defaultValue: false
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
Save info for later spam checking on a post. We will use this for the akismet package
|
|
|
|
*/
|
|
|
|
userIP: {
|
|
|
|
type: String,
|
|
|
|
optional: true,
|
2016-10-29 14:17:57 +09:00
|
|
|
viewableIf: canEditAll,
|
2016-06-23 15:00:58 +09:00
|
|
|
publish: false
|
|
|
|
},
|
|
|
|
userAgent: {
|
|
|
|
type: String,
|
|
|
|
optional: true,
|
2016-10-29 14:17:57 +09:00
|
|
|
viewableIf: canEditAll,
|
2016-06-23 15:00:58 +09:00
|
|
|
publish: false
|
|
|
|
},
|
|
|
|
referrer: {
|
|
|
|
type: String,
|
|
|
|
optional: true,
|
2016-10-29 14:17:57 +09:00
|
|
|
viewableIf: canEditAll,
|
2016-06-23 15:00:58 +09:00
|
|
|
publish: false
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
The post author's name
|
|
|
|
*/
|
|
|
|
author: {
|
|
|
|
type: String,
|
|
|
|
optional: true,
|
2016-10-29 14:17:57 +09:00
|
|
|
viewableIf: alwaysPublic,
|
2016-06-23 15:00:58 +09:00
|
|
|
publish: true,
|
2016-11-14 17:17:44 +09:00
|
|
|
autoValue: (documentOrModifier) => {
|
|
|
|
// if userId is changing, change the author name too
|
|
|
|
const userId = documentOrModifier.userId || documentOrModifier.$set && documentOrModifier.$set.userId
|
|
|
|
if (userId) return Users.getDisplayNameById(userId)
|
|
|
|
}
|
2016-06-23 15:00:58 +09:00
|
|
|
},
|
|
|
|
/**
|
|
|
|
The post author's `_id`.
|
|
|
|
*/
|
|
|
|
userId: {
|
|
|
|
type: String,
|
|
|
|
optional: true,
|
2016-11-18 09:28:32 +01:00
|
|
|
control: "select",
|
|
|
|
viewableIf: alwaysPublic,
|
|
|
|
insertableIf: canInsert,
|
|
|
|
control: "none",
|
|
|
|
resolveAs: 'user: User',
|
|
|
|
// publish: true,
|
2016-06-23 15:00:58 +09:00
|
|
|
// regEx: SimpleSchema.RegEx.Id,
|
2016-07-21 09:40:05 +09:00
|
|
|
// insertableIf: canEditAll,
|
|
|
|
// editableIf: canEditAll,
|
2016-11-08 14:56:39 +09:00
|
|
|
// form: {
|
|
|
|
// group: 'admin',
|
|
|
|
// options: function () {
|
|
|
|
// return Users.find().map(function (user) {
|
|
|
|
// return {
|
|
|
|
// value: user._id,
|
|
|
|
// label: Users.getDisplayName(user)
|
|
|
|
// };
|
|
|
|
// });
|
|
|
|
// }
|
|
|
|
// },
|
|
|
|
// join: {
|
|
|
|
// joinAs: "user",
|
|
|
|
// collection: () => Users
|
|
|
|
// }
|
2016-06-23 15:00:58 +09:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-11-08 14:56:39 +09:00
|
|
|
if (typeof SimpleSchema !== "undefined") {
|
|
|
|
Posts.schema = new SimpleSchema(Posts.schemaJSON);
|
|
|
|
Posts.attachSchema(Posts.schema);
|
|
|
|
}
|
2016-10-29 16:37:33 +09:00
|
|
|
|
2016-11-04 15:49:42 +09:00
|
|
|
|
2016-11-08 14:56:39 +09:00
|
|
|
// Posts.graphQLSchema = `
|
|
|
|
// type Post {
|
|
|
|
// _id: String
|
|
|
|
// createdAt: String
|
|
|
|
// postedAt: String
|
|
|
|
// url: String
|
|
|
|
// title: String
|
|
|
|
// slug: String
|
|
|
|
// body: String
|
|
|
|
// htmlBody: String
|
|
|
|
// excerpt: String
|
|
|
|
// sticky: Boolean
|
|
|
|
// viewCount: Int
|
|
|
|
// lastCommentedAt: String
|
|
|
|
// clickCount: Int
|
|
|
|
// status: Int
|
|
|
|
// isFuture: Boolean
|
|
|
|
// user: User
|
|
|
|
// commentCount: Int
|
|
|
|
// commenters: [User]
|
|
|
|
// # comments: [Comment]
|
|
|
|
// categories: [Category]
|
|
|
|
// scheduledAt: String
|
|
|
|
// dummySlug: String
|
|
|
|
// isDummy: String
|
|
|
|
// upvotes: Int
|
|
|
|
// upvoters: [User]
|
|
|
|
// downvotes: Int
|
|
|
|
// downvoters: [User]
|
|
|
|
// baseScore: Int
|
|
|
|
// score: Float
|
|
|
|
// clickCount: Int
|
|
|
|
// viewCount: Int
|
|
|
|
// thumbnailUrl: String
|
|
|
|
// userIP: String
|
|
|
|
// userAgent: String
|
|
|
|
// referrer: String
|
|
|
|
// }
|
2016-11-04 14:38:31 +09:00
|
|
|
|
2016-11-08 14:56:39 +09:00
|
|
|
// input postsInput {
|
|
|
|
// postedAt: String
|
|
|
|
// url: String
|
|
|
|
// title: String
|
|
|
|
// slug: String
|
|
|
|
// body: String
|
|
|
|
// sticky: Boolean
|
|
|
|
// status: Int
|
|
|
|
// categories: [String]
|
|
|
|
// scheduledAt: String
|
|
|
|
// thumbnailUrl: String
|
|
|
|
// }
|
|
|
|
|
|
|
|
// input postsUnset {
|
|
|
|
// postedAt: Boolean
|
|
|
|
// url: Boolean
|
|
|
|
// title: Boolean
|
|
|
|
// slug: Boolean
|
|
|
|
// body: Boolean
|
|
|
|
// sticky: Boolean
|
|
|
|
// status: Boolean
|
|
|
|
// categories: Boolean
|
|
|
|
// scheduledAt: Boolean
|
|
|
|
// thumbnailUrl: Boolean
|
|
|
|
// }
|
|
|
|
|
|
|
|
// input Terms {
|
|
|
|
// view: String
|
|
|
|
// userId: String
|
|
|
|
// cat: String
|
|
|
|
// date: String
|
|
|
|
// after: String
|
|
|
|
// before: String
|
|
|
|
// enableCache: Boolean
|
|
|
|
// listId: String
|
|
|
|
// query: String # search query
|
|
|
|
// }
|
|
|
|
// `;
|
2016-10-29 16:37:33 +09:00
|
|
|
|
2016-11-07 17:45:17 +09:00
|
|
|
|
2016-11-08 15:12:23 +09:00
|
|
|
Telescope.graphQL.addCollection(Posts);
|
2016-11-08 14:56:39 +09:00
|
|
|
|
|
|
|
const termsSchema = `
|
|
|
|
input Terms {
|
|
|
|
view: String
|
|
|
|
userId: String
|
|
|
|
cat: String
|
|
|
|
date: String
|
|
|
|
after: String
|
|
|
|
before: String
|
|
|
|
enableCache: Boolean
|
|
|
|
listId: String
|
|
|
|
query: String # search query
|
|
|
|
}
|
|
|
|
`;
|
|
|
|
|
|
|
|
Telescope.graphQL.addSchema(termsSchema);
|
2016-10-29 16:37:33 +09:00
|
|
|
|
2016-11-03 21:39:09 +09:00
|
|
|
Telescope.graphQL.addToContext({ Posts });
|