reworking publications

This commit is contained in:
Sacha Greif 2016-02-17 11:28:00 +09:00
parent 38af000a4f
commit 9f8a094ab8
23 changed files with 372 additions and 290 deletions

View file

@ -41,7 +41,9 @@ telescope:comments
# telescope:spiderable # telescope:spiderable
# telescope:subscribe-to-posts # notifications # telescope:subscribe-to-posts # notifications
# telescope:tagline-banner # telescope:tagline-banner
# telescope:tags
telescope:tags
# telescope:theme-base # telescope:theme-base
# telescope:theme-hubble # telescope:theme-hubble
# telescope:update-prompt # telescope:update-prompt

View file

@ -112,6 +112,7 @@ telescope:lib@0.25.7
telescope:posts@0.25.7 telescope:posts@0.25.7
telescope:search@0.25.7 telescope:search@0.25.7
telescope:settings@0.25.7 telescope:settings@0.25.7
telescope:tags@0.25.7
telescope:users@0.25.7 telescope:users@0.25.7
templating@1.1.6-modules.8 templating@1.1.6-modules.8
templating-tools@1.0.1-modules.8 templating-tools@1.0.1-modules.8

View file

@ -1,21 +1,4 @@
Telescope is an open-source, real-time social news site built with [Meteor](http://meteor.com) # Telescope: Nova
**Note:** Telescope is beta software. Most of it should work but it's still a little unpolished and you'll probably find some bugs. Use at your own risk :) **Nova** is a top-secret, highly unstable experimental branch of Telescope with a really cool name.
Note that Telescope is distributed under the [MIT License](http://opensource.org/licenses/MIT)
### Getting Started
Note that while simply cloning this repository will work, it is recommended you clone the [sample project](https://github.com/TelescopeJS/sample-project/) repository instead for a simpler workflow.
Please refer to [the documentation](http://telescope.readme.io/v0.20/docs/installing-telescope) for more instructions on installing Telescope.
### Learn More
- [Homepage](http://telescopeapp.org)
- [Demo](http://demo2.telescopeapp.org)
- [Sample Project](https://github.com/TelescopeJS/sample-project/)
- [Documentation](http://telescope.readme.io)
- [Roadmap](https://trello.com/b/oLMMqjVL/telescope-roadmap)
- [Slack](http://slack.telescopeapp.org/)
- [Meta](http://meta.telescopeapp.org/) Discussions about Telescope

View file

@ -0,0 +1,11 @@
const CategoriesList = props => {
return (
<ul>
{props.results.map(category => <li key={category._id}>{category.name}</li>)}
</ul>
)
};
module.exports = CategoriesList;

View file

@ -1,6 +1,6 @@
const Header = props => { const Header = props => {
const Logo = Telescope.getComponent("Logo"); ({Logo, ListContainer, CategoriesList} = Telescope.components);
const logoUrl = Telescope.settings.get("logoUrl"); const logoUrl = Telescope.settings.get("logoUrl");
const siteTitle = Telescope.settings.get("title", "Telescope"); const siteTitle = Telescope.settings.get("title", "Telescope");
@ -13,9 +13,7 @@ const Header = props => {
{tagline ? <h2 className="tagline">{tagline}</h2> : "" } {tagline ? <h2 className="tagline">{tagline}</h2> : "" }
</div> </div>
<div className="nav"> <div className="nav">
<ul> <ListContainer collection={Categories} publication="categories" component={CategoriesList} limit={0}/>
<li>Nav link</li>
</ul>
</div> </div>
</header> </header>
) )

View file

@ -19,4 +19,8 @@ Telescope.registerComponent("PostEdit", require('./posts/PostEdit.jsx'));
// comments // comments
Telescope.registerComponent("CommentItem", require('./comments/list/CommentItem.jsx')); Telescope.registerComponent("CommentItem", require('./comments/list/CommentItem.jsx'));
Telescope.registerComponent("CommentList", require('./comments/list/CommentList.jsx')); Telescope.registerComponent("CommentList", require('./comments/list/CommentList.jsx'));
// categories
Telescope.registerComponent("CategoriesList", require('./categories/list/CategoriesList.jsx'));

View file

@ -14,7 +14,8 @@ Comments.schema = new SimpleSchema({
*/ */
_id: { _id: {
type: String, type: String,
optional: true optional: true,
public: true,
}, },
/** /**
The `_id` of the parent comment, if there is one The `_id` of the parent comment, if there is one
@ -25,6 +26,7 @@ Comments.schema = new SimpleSchema({
max: 500, max: 500,
editableBy: ["member", "admin"], editableBy: ["member", "admin"],
optional: true, optional: true,
public: true,
autoform: { autoform: {
omit: true // never show this omit: true // never show this
} }
@ -38,6 +40,7 @@ Comments.schema = new SimpleSchema({
max: 500, max: 500,
editableBy: ["member", "admin"], editableBy: ["member", "admin"],
optional: true, optional: true,
public: true,
autoform: { autoform: {
omit: true // never show this omit: true // never show this
} }
@ -47,14 +50,16 @@ Comments.schema = new SimpleSchema({
*/ */
createdAt: { createdAt: {
type: Date, type: Date,
optional: true optional: true,
public: true,
}, },
/** /**
The timestamp of the comment being posted. For now, comments are always created and posted at the same time The timestamp of the comment being posted. For now, comments are always created and posted at the same time
*/ */
postedAt: { postedAt: {
type: Date, type: Date,
optional: true optional: true,
public: true,
}, },
/** /**
The comment body (Markdown) The comment body (Markdown)
@ -63,6 +68,7 @@ Comments.schema = new SimpleSchema({
type: String, type: String,
max: 3000, max: 3000,
editableBy: ["member", "admin"], editableBy: ["member", "admin"],
public: true,
autoform: { autoform: {
rows: 5, rows: 5,
afFormGroup: { afFormGroup: {
@ -75,7 +81,8 @@ Comments.schema = new SimpleSchema({
*/ */
htmlBody: { htmlBody: {
type: String, type: String,
optional: true optional: true,
public: true,
}, },
/** /**
The comment's base score (doesn't factor in comment age) The comment's base score (doesn't factor in comment age)
@ -83,7 +90,8 @@ Comments.schema = new SimpleSchema({
baseScore: { baseScore: {
type: Number, type: Number,
decimal: true, decimal: true,
optional: true optional: true,
public: true,
}, },
/** /**
The comment's current score (factors in comment age) The comment's current score (factors in comment age)
@ -91,49 +99,56 @@ Comments.schema = new SimpleSchema({
score: { score: {
type: Number, type: Number,
decimal: true, decimal: true,
optional: true optional: true,
public: true,
}, },
/** /**
The number of upvotes the comment has received The number of upvotes the comment has received
*/ */
upvotes: { upvotes: {
type: Number, type: Number,
optional: true optional: true,
public: true,
}, },
/** /**
An array containing the `_id`s of upvoters An array containing the `_id`s of upvoters
*/ */
upvoters: { upvoters: {
type: [String], type: [String],
optional: true optional: true,
public: true,
}, },
/** /**
The number of downvotes the comment has received The number of downvotes the comment has received
*/ */
downvotes: { downvotes: {
type: Number, type: Number,
optional: true optional: true,
public: true,
}, },
/** /**
An array containing the `_id`s of downvoters An array containing the `_id`s of downvoters
*/ */
downvoters: { downvoters: {
type: [String], type: [String],
optional: true optional: true,
public: true,
}, },
/** /**
The comment author's name The comment author's name
*/ */
author: { author: {
type: String, type: String,
optional: true optional: true,
public: true,
}, },
/** /**
Whether the comment is inactive. Inactive comments' scores gets recalculated less often Whether the comment is inactive. Inactive comments' scores gets recalculated less often
*/ */
inactive: { inactive: {
type: Boolean, type: Boolean,
optional: true optional: true,
public: true,
}, },
/** /**
The post's `_id` The post's `_id`
@ -141,6 +156,7 @@ Comments.schema = new SimpleSchema({
postId: { postId: {
type: String, type: String,
optional: true, optional: true,
public: true,
// regEx: SimpleSchema.RegEx.Id, // regEx: SimpleSchema.RegEx.Id,
max: 500, max: 500,
// editableBy: ["member", "admin"], // TODO: should users be able to set postId, but not modify it? // editableBy: ["member", "admin"], // TODO: should users be able to set postId, but not modify it?
@ -153,14 +169,16 @@ Comments.schema = new SimpleSchema({
*/ */
userId: { userId: {
type: String, type: String,
optional: true optional: true,
public: true,
}, },
/** /**
Whether the comment is deleted. Delete comments' content doesn't appear on the site. Whether the comment is deleted. Delete comments' content doesn't appear on the site.
*/ */
isDeleted: { isDeleted: {
type: Boolean, type: Boolean,
optional: true optional: true,
public: true,
} }
}); });

View file

@ -0,0 +1,27 @@
Posts.addField([
/**
Count of the post's comments
*/
{
fieldName: "commentCount",
fieldSchema: {
type: Number,
optional: true,
public: true
}
},
/**
An array containing the `_id`s of commenters
*/
{
fieldName: "commenters",
fieldSchema: {
type: [String],
optional: true,
public: true
}
}
]);
Posts.publicationFields.list.push("commentCount", "commenters");
Posts.publicationFields.single.push("commentCount", "commenters");

View file

@ -13,8 +13,6 @@ Meteor.publish('comments.list', function(terms) {
if(Users.can.viewById(this.userId)){ if(Users.can.viewById(this.userId)){
var parameters = Comments.parameters.get(terms); var parameters = Comments.parameters.get(terms);
console.log(terms)
console.log(parameters)
var comments = Comments.find(parameters.find, parameters.options); var comments = Comments.find(parameters.find, parameters.options);
// if there are comments, find out which posts were commented on // if there are comments, find out which posts were commented on

View file

@ -13,6 +13,7 @@ Package.onUse(function (api) {
'telescope:lib@0.25.7', 'telescope:lib@0.25.7',
// 'telescope:i18n@0.25.7', // 'telescope:i18n@0.25.7',
'telescope:settings@0.25.7', 'telescope:settings@0.25.7',
'telescope:posts@0.25.7',
'telescope:users@0.25.7' 'telescope:users@0.25.7'
]); ]);
@ -23,6 +24,7 @@ Package.onUse(function (api) {
'lib/views.js', 'lib/views.js',
'lib/parameters.js', 'lib/parameters.js',
'lib/helpers.js', 'lib/helpers.js',
'lib/custom_fields.js',
// 'lib/routes.js' // 'lib/routes.js'
], ['client', 'server']); ], ['client', 'server']);

View file

@ -23,12 +23,17 @@ const ListContainer = React.createClass({
mixins: [ReactMeteorData], mixins: [ReactMeteorData],
getMeteorData() { getMeteorData() {
const terms = {...this.props.terms, limit: this.state.limit};
const parameters = this.props.collection.parameters.get(terms);
const find = parameters.find;
const options = parameters.options;
options.limit = this.state.limit;
let terms = {...this.props.terms, limit: this.state.limit};
let find = {};
let options = {limit: this.state.limit};
if (this.props.collection.parameters) {
const parameters = this.props.collection.parameters.get(terms);
find = parameters.find;
options = parameters.options;
}
const subscription = Meteor.subscribe(this.props.publication, terms); const subscription = Meteor.subscribe(this.props.publication, terms);
const totalCount = Counts.get(this.props.publication); const totalCount = Counts.get(this.props.publication);

View file

@ -48,4 +48,4 @@ Telescope.subscriptions = [];
*/ */
Telescope.subscriptions.preload = function (subscription, args) { Telescope.subscriptions.preload = function (subscription, args) {
Telescope.subscriptions.push({name: subscription, arguments: args}); Telescope.subscriptions.push({name: subscription, arguments: args});
}; };

View file

@ -252,3 +252,11 @@ Telescope.getNestedProperty = function (obj, desc) {
while(arr.length && (obj = obj[arr.shift()])); while(arr.length && (obj = obj[arr.shift()]));
return obj; return obj;
}; };
/**
* Convert an array of fields to publish into a Mongo fields specifier
* @param {Array} fieldsArray
*/
Telescope.utils.arrayToFields = function (fieldsArray) {
return _.object(fieldsArray, _.map(fieldsArray, function () {return true}));
};

View file

@ -0,0 +1,29 @@
Posts.publicationFields = {};
/**
* Specify which fields should be published by the posts.list publication
* @array Posts.publicationFields.list
*/
Posts.publicationFields.list = [
"_id",
"postedAt",
"url",
"title",
"slug",
"htmlBody",
"viewCount",
"lastCommentedAt",
"clickCount",
"baseScore",
"score",
"status",
"sticky",
"author",
"userId"
];
/**
* Specify which fields should be published by the posts.single publication
* @array Posts.publicationFields.single
*/
Posts.publicationFields.single = Posts.simpleSchema().getPublicFields();

View file

@ -8,14 +8,16 @@ Posts.schema = new SimpleSchema({
*/ */
_id: { _id: {
type: String, type: String,
optional: true optional: true,
public: true
}, },
/** /**
Timetstamp of post creation Timetstamp of post creation
*/ */
createdAt: { createdAt: {
type: Date, type: Date,
optional: true optional: true,
public: true
}, },
/** /**
Timestamp of post first appearing on the site (i.e. being approved) Timestamp of post first appearing on the site (i.e. being approved)
@ -24,10 +26,11 @@ Posts.schema = new SimpleSchema({
type: Date, type: Date,
optional: true, optional: true,
editableBy: ["admin"], editableBy: ["admin"],
// autoform: { public: true,
// group: 'admin', autoform: {
// type: "bootstrap-datetimepicker" group: 'admin',
// } type: "bootstrap-datetimepicker"
}
}, },
/** /**
URL URL
@ -37,10 +40,11 @@ Posts.schema = new SimpleSchema({
optional: true, optional: true,
max: 500, max: 500,
editableBy: ["member", "admin"], editableBy: ["member", "admin"],
// autoform: { public: true,
// type: "bootstrap-url", autoform: {
// order: 10 type: "bootstrap-url",
// } order: 10
}
}, },
/** /**
Title Title
@ -50,16 +54,18 @@ Posts.schema = new SimpleSchema({
optional: false, optional: false,
max: 500, max: 500,
editableBy: ["member", "admin"], editableBy: ["member", "admin"],
// autoform: { public: true,
// order: 20 autoform: {
// } order: 20
}
}, },
/** /**
Slug Slug
*/ */
slug: { slug: {
type: String, type: String,
optional: true optional: true,
public: true,
}, },
/** /**
Post body (markdown) Post body (markdown)
@ -69,52 +75,43 @@ Posts.schema = new SimpleSchema({
optional: true, optional: true,
max: 3000, max: 3000,
editableBy: ["member", "admin"], editableBy: ["member", "admin"],
// autoform: { public: true,
// rows: 5, autoform: {
// order: 30 rows: 5,
// } order: 30
}
}, },
/** /**
HTML version of the post body HTML version of the post body
*/ */
htmlBody: { htmlBody: {
type: String, type: String,
optional: true optional: true,
public: true,
}, },
/** /**
Count of how many times the post's page was viewed Count of how many times the post's page was viewed
*/ */
viewCount: { viewCount: {
type: Number, type: Number,
optional: true optional: true,
}, public: true,
/**
Count of the post's comments
*/
commentCount: {
type: Number,
optional: true
},
/**
An array containing the `_id`s of commenters
*/
commenters: {
type: [String],
optional: true
}, },
/** /**
Timestamp of the last comment Timestamp of the last comment
*/ */
lastCommentedAt: { lastCommentedAt: {
type: Date, type: Date,
optional: true optional: true,
public: true,
}, },
/** /**
Count of how many times the post's link was clicked Count of how many times the post's link was clicked
*/ */
clickCount: { clickCount: {
type: Number, type: Number,
optional: true optional: true,
public: true,
}, },
/** /**
The post's base score (not factoring in the post's age) The post's base score (not factoring in the post's age)
@ -122,35 +119,8 @@ Posts.schema = new SimpleSchema({
baseScore: { baseScore: {
type: Number, type: Number,
decimal: true, decimal: true,
optional: true optional: true,
}, public: true,
/**
How many upvotes the post has received
*/
upvotes: {
type: Number,
optional: true
},
/**
An array containing the `_id`s of the post's upvoters
*/
upvoters: {
type: [String],
optional: true
},
/**
How many downvotes the post has received
*/
downvotes: {
type: Number,
optional: true
},
/**
An array containing the `_id`s of the post's downvoters
*/
downvoters: {
type: [String],
optional: true
}, },
/** /**
The post's current score (factoring in age) The post's current score (factoring in age)
@ -158,7 +128,8 @@ Posts.schema = new SimpleSchema({
score: { score: {
type: Number, type: Number,
decimal: true, decimal: true,
optional: true optional: true,
public: true,
}, },
/** /**
The post's status. One of pending (`1`), approved (`2`), or deleted (`3`) The post's status. One of pending (`1`), approved (`2`), or deleted (`3`)
@ -167,6 +138,7 @@ Posts.schema = new SimpleSchema({
type: Number, type: Number,
optional: true, optional: true,
editableBy: ["admin"], editableBy: ["admin"],
public: true,
autoValue: function () { autoValue: function () {
// only provide a default value // only provide a default value
// 1) this is an insert operation // 1) this is an insert operation
@ -175,11 +147,11 @@ Posts.schema = new SimpleSchema({
if (this.isInsert && !this.isSet) if (this.isInsert && !this.isSet)
return Posts.getDefaultStatus(user); return Posts.getDefaultStatus(user);
}, },
// autoform: { autoform: {
// noselect: true, noselect: true,
// options: Posts.config.postStatuses, options: Posts.config.postStatuses,
// group: 'admin' group: 'admin'
// } }
}, },
/** /**
Whether the post is sticky (pinned to the top of posts lists) Whether the post is sticky (pinned to the top of posts lists)
@ -189,39 +161,45 @@ Posts.schema = new SimpleSchema({
optional: true, optional: true,
defaultValue: false, defaultValue: false,
editableBy: ["admin"], editableBy: ["admin"],
// autoform: { public: true,
// group: 'admin', autoform: {
// leftLabel: "Sticky" group: 'admin',
// } leftLabel: "Sticky"
}
}, },
/** /**
Whether the post is inactive. Inactive posts see their score recalculated less often Whether the post is inactive. Inactive posts see their score recalculated less often
*/ */
inactive: { inactive: {
type: Boolean, type: Boolean,
optional: true optional: true,
public: false,
}, },
/** /**
Save info for later spam checking on a post. We will use this for the akismet package Save info for later spam checking on a post. We will use this for the akismet package
*/ */
userIP: { userIP: {
type: String, type: String,
optional: true optional: true,
public: false,
}, },
userAgent: { userAgent: {
type: String, type: String,
optional: true optional: true,
public: false,
}, },
referrer: { referrer: {
type: String, type: String,
optional: true optional: true,
public: false,
}, },
/** /**
The post author's name The post author's name
*/ */
author: { author: {
type: String, type: String,
optional: true optional: true,
public: true,
}, },
/** /**
The post author's `_id`. The post author's `_id`.
@ -231,17 +209,18 @@ Posts.schema = new SimpleSchema({
optional: true, optional: true,
// regEx: SimpleSchema.RegEx.Id, // regEx: SimpleSchema.RegEx.Id,
editableBy: ["admin"], editableBy: ["admin"],
// autoform: { public: true,
// group: 'admin', autoform: {
// options: function () { group: 'admin',
// return Meteor.users.find().map(function (user) { options: function () {
// return { return Meteor.users.find().map(function (user) {
// value: user._id, return {
// label: Users.getDisplayName(user) value: user._id,
// }; label: Users.getDisplayName(user)
// }); };
// } });
// } }
}
} }
}); });

View file

@ -1,124 +1,99 @@
Posts._ensureIndex({"status": 1, "postedAt": 1}); Posts._ensureIndex({"status": 1, "postedAt": 1});
// ------------------------------------- Helpers -------------------------------- //
/**
* Get all users relevant to a list of posts
* (authors of the listed posts, and first four commenters of each post)
* @param {Object} posts
*/
const getPostsListUsers = posts => {
// add the userIds of each post authors
let userIds = _.pluck(posts.fetch(), 'userId');
// for each post, also add first four commenter's userIds to userIds array
posts.forEach(function (post) {
userIds = userIds.concat(_.first(post.commenters,4));
});
userIds = _.unique(userIds);
return Meteor.users.find({_id: {$in: userIds}}, {fields: Users.pubsub.avatarProperties});
};
/**
* Get all users relevant to a single post
* (author of the current post, authors of its comments, and upvoters & downvoters of the post)
* @param {Object} post
*/
const getSinglePostUsers = post => {
let users = [post.userId]; // publish post author's ID
// get IDs from all commenters on the post
const comments = Comments.find({postId: post._id}).fetch();
if (comments.length) {
users = users.concat(_.pluck(comments, "userId"));
}
// add upvoters
if (post.upvoters && post.upvoters.length) {
users = users.concat(post.upvoters);
}
// add downvoters
if (post.downvoters && post.downvoters.length) {
users = users.concat(post.downvoters);
}
// remove any duplicate IDs
users = _.unique(users);
return Meteor.users.find({_id: {$in: users}}, {fields: Users.pubsub.publicProperties});
};
// ------------------------------------- Publications -------------------------------- //
/**
* posts.list publication
* @param {Object} terms
*/
Meteor.publish('posts.list', function(terms) { Meteor.publish('posts.list', function(terms) {
var parameters = Posts.parameters.get(terms), this.unblock();
posts = Posts.find(parameters.find, parameters.options);
Counts.publish(this, 'posts.list', Posts.find(parameters.find, parameters.options)); const currentUser = Meteor.users.findOne(this.userId);
return posts; terms.currentUserId = this.userId; // add currentUserId to terms
({find, options} = Posts.parameters.get(terms));
Counts.publish(this, 'posts.list', Posts.find(find, options));
options.fields = Telescope.utils.arrayToFields(Posts.publicationFields.list);
const posts = Posts.find(find, options);
const users = getPostsListUsers(posts);
return Users.can.view(currentUser) ? [posts, users] : [];
}); });
/**
* posts.item publication
* @param {Object} terms
*/
Meteor.publish('posts.single', function(terms) { Meteor.publish('posts.single', function(terms) {
return Posts.find(terms); check(terms, {_id: String});
}); this.unblock();
// Publish a list of posts const currentUser = Meteor.users.findOne(this.userId);
const options = {fields: Telescope.utils.arrayToFields(Posts.publicationFields.single)};
const post = Posts.find(terms, options);
const users = getSinglePostUsers(post);
// Meteor.publish('postsList', function(terms) { return Users.can.viewPost(currentUser, post) ? [post, users] : [];
// this.unblock(); });
// if (this.userId) { // add currentUserId to terms if a user is logged in
// terms.currentUserId = this.userId;
// }
// if(Users.can.viewById(this.userId)){
// var parameters = Posts.parameters.get(terms),
// posts = Posts.find(parameters.find, parameters.options);
// return posts;
// }
// return [];
// });
// // Publish all the users that have posted the currently displayed list of posts
// // plus the commenters for each post
// Meteor.publish('postsListUsers', function(terms) {
// this.unblock();
// if (this.userId) {
// terms.currentUserId = this.userId; // add userId to terms
// }
// if(Users.can.viewById(this.userId)){
// var parameters = Posts.parameters.get(terms),
// posts = Posts.find(parameters.find, parameters.options),
// userIds = _.pluck(posts.fetch(), 'userId');
// // for each post, add first four commenter's userIds to userIds array
// posts.forEach(function (post) {
// userIds = userIds.concat(_.first(post.commenters,4));
// });
// userIds = _.unique(userIds);
// return Meteor.users.find({_id: {$in: userIds}}, {fields: Users.pubsub.avatarProperties, multi: true});
// }
// return [];
// });
// // Publish a single post
// Meteor.publish('singlePost', function(postId) {
// check(postId, String);
// this.unblock();
// var user = Meteor.users.findOne(this.userId);
// var post = Posts.findOne(postId);
// if (Users.can.viewPost(user, post)){
// return Posts.find(postId);
// } else {
// return [];
// }
// });
// // Publish author of the current post, authors of its comments, and upvoters of the post
// Meteor.publish('postUsers', function(postId) {
// check(postId, String);
// this.unblock();
// if (Users.can.viewById(this.userId)){
// // publish post author and post commenters
// var post = Posts.findOne(postId);
// var users = [];
// if (post) {
// users.push(post.userId); // publish post author's ID
// // get IDs from all commenters on the post
// var comments = Comments.find({postId: post._id}).fetch();
// if (comments.length) {
// users = users.concat(_.pluck(comments, "userId"));
// }
// // publish upvoters
// if (post.upvoters && post.upvoters.length) {
// users = users.concat(post.upvoters);
// }
// // publish downvoters
// if (post.downvoters && post.downvoters.length) {
// users = users.concat(post.downvoters);
// }
// }
// // remove any duplicate IDs
// users = _.unique(users);
// return Meteor.users.find({_id: {$in: users}}, {fields: Users.pubsub.publicProperties});
// }
// return [];
// });

View file

@ -29,6 +29,7 @@ Package.onUse(function (api) {
'lib/parameters.js', 'lib/parameters.js',
'lib/views.js', 'lib/views.js',
'lib/helpers.js', 'lib/helpers.js',
'lib/fields.js',
// 'lib/modules.js', // 'lib/modules.js',
// 'lib/callbacks.js', // 'lib/callbacks.js',
// 'lib/methods.js', // 'lib/methods.js',

View file

@ -47,22 +47,14 @@ Categories.schema = new SimpleSchema({
} }
}); });
Meteor.startup(function(){ // Meteor.startup(function(){
Categories.internationalize(); // Categories.internationalize();
}); // });
Categories.attachSchema(Categories.schema); Categories.attachSchema(Categories.schema);
Meteor.startup(function () {
Categories.allow({
insert: Users.is.adminById,
update: Users.is.adminById,
remove: Users.is.adminById
});
});
Telescope.settings.collection.addField([
Settings.addField([
{ {
fieldName: 'categoriesBehavior', fieldName: 'categoriesBehavior',
fieldSchema: { fieldSchema: {

View file

@ -22,3 +22,6 @@ Posts.addField(
} }
} }
); );
Posts.publicationFields.list.push("categories");
Posts.publicationFields.single.push("categories");

View file

@ -14,43 +14,43 @@ Package.onUse(function (api) {
api.addFiles([ api.addFiles([
'lib/categories.js', 'lib/categories.js',
'lib/helpers.js', 'lib/helpers.js',
'lib/callbacks.js', // 'lib/callbacks.js',
'lib/parameters.js', // 'lib/parameters.js',
'lib/custom_fields.js', // 'lib/custom_fields.js',
'lib/methods.js', // 'lib/methods.js',
'lib/modules.js', // 'lib/modules.js',
'lib/routes.js', // 'lib/routes.js',
'package-tap.i18n' // 'package-tap.i18n'
], ['client', 'server']); ], ['client', 'server']);
api.addFiles([ api.addFiles([
'lib/client/scss/categories.scss', // 'lib/client/scss/categories.scss',
'lib/client/templates/categories_admin.html', // 'lib/client/templates/categories_admin.html',
'lib/client/templates/categories_admin.js', // 'lib/client/templates/categories_admin.js',
'lib/client/templates/category_item.html', // 'lib/client/templates/category_item.html',
'lib/client/templates/category_item.js', // 'lib/client/templates/category_item.js',
'lib/client/templates/categories_menu.html', // 'lib/client/templates/categories_menu.html',
'lib/client/templates/categories_menu.js', // 'lib/client/templates/categories_menu.js',
'lib/client/templates/categories_menu_item.html', // 'lib/client/templates/categories_menu_item.html',
'lib/client/templates/categories_menu_item.js', // 'lib/client/templates/categories_menu_item.js',
'lib/client/templates/category_title.html', // 'lib/client/templates/category_title.html',
'lib/client/templates/category_title.js', // 'lib/client/templates/category_title.js',
'lib/client/templates/posts_category.html', // 'lib/client/templates/posts_category.html',
'lib/client/templates/post_categories.html', // 'lib/client/templates/post_categories.html',
'lib/client/templates/post_categories.js', // 'lib/client/templates/post_categories.js',
'lib/client/templates/autoform_category.html', // 'lib/client/templates/autoform_category.html',
'lib/client/templates/autoform_category.js' // 'lib/client/templates/autoform_category.js'
], ['client']); ], ['client']);
api.addFiles([ api.addFiles([
'lib/server/publications.js' 'lib/server/publications.js'
], ['server']); ], ['server']);
var languages = ["ar", "bg", "cs", "da", "de", "el", "en", "es", "et", "fr", "hu", "id", "it", "ja", "kk", "ko", "nl", "pl", "pt-BR", "ro", "ru", "sl", "sv", "th", "tr", "vi", "zh-CN"]; // var languages = ["ar", "bg", "cs", "da", "de", "el", "en", "es", "et", "fr", "hu", "id", "it", "ja", "kk", "ko", "nl", "pl", "pt-BR", "ro", "ru", "sl", "sv", "th", "tr", "vi", "zh-CN"];
var languagesPaths = languages.map(function (language) { // var languagesPaths = languages.map(function (language) {
return "i18n/"+language+".i18n.json"; // return "i18n/"+language+".i18n.json";
}); // });
api.addFiles(languagesPaths, ["client", "server"]); // api.addFiles(languagesPaths, ["client", "server"]);
api.export([ api.export([
'Categories' 'Categories'

View file

@ -27,7 +27,7 @@ Package.onUse(function (api) {
// 'lib/modules.js', // 'lib/modules.js',
'lib/helpers.js', 'lib/helpers.js',
// 'lib/menus.js', // 'lib/menus.js',
// 'lib/pubsub.js', 'lib/pubsub.js',
// 'lib/methods.js', // 'lib/methods.js',
// 'lib/routes.js' // 'lib/routes.js'
], ['client', 'server']); ], ['client', 'server']);

View file

@ -0,0 +1,45 @@
Telescope.settings.addField([
/**
How many upvotes the post has received
*/
{
fieldName: "upvotes",
fieldSchema: {
type: Number,
optional: true
}
},
/**
An array containing the `_id`s of the post's upvoters
*/
{
fieldName: "upvoters",
fieldSchema: {
type: [String],
optional: true
}
},
/**
How many downvotes the post has received
*/
{
fieldName: "downvotes",
fieldSchema: {
type: Number,
optional: true
}
},
/**
An array containing the `_id`s of the post's downvoters
*/
{
fieldName: "downvoters",
fieldSchema: {
type: [String],
optional: true
}
},
]);
Posts.publicationFields.list.push("upvotes", "downvotes");
Posts.publicationFields.single.push("upvotes", "upvoters", "downvotes", "downvoters");

View file

@ -15,7 +15,8 @@ Package.onUse(function (api) {
api.addFiles([ api.addFiles([
// 'package-tap.i18n', // 'package-tap.i18n',
'lib/vote.js' 'lib/vote.js',
'lib/custom_fields.js'
], ['client', 'server']); ], ['client', 'server']);
api.addFiles([ api.addFiles([