eslint & clean up code, also fixed some bugs (#1515)

* [eslint] update eslint rules & add .eslintignore to ignore non-ready nova packages

* [clean-up] nova-voting

* [clean-up] [bug] nova-users: missing user parameter

* [clean-up] nova-users

* [clean-up] nova-subscribe

* [clean-up] nova-settings

* [clean-up] nova-rss

* [clean-up] [bug] nova-posts: correct UsersRemoveDeletePosts

* [clean-up] nova-posts

* [clean-up] nova-notifications

* [clean-up] [bug] nova-newsletter: no error.message on throw error

* [clean-up] nova-newsletter

* [clean-up] nova-lib

* [clean-up] nova-kadira

* [clean-up] nova-inject-data

* [clean-up] nova-getting-started

* [clean-up] nova-forms

* [clean-up] nova-events

* [clean-up] [bug] nova-embedly: no FlowRouter

* [clean-up] nova-embedly

* [clean-up] nova-email-templates

* [clean-up] nova-email

* [clean-up] nova-debug

* [clean-up] nova-core

* [clean-up] [bug] nova-comments: correct UsersRemoveDeleteComments

* [clean-up] nova-comments

* [clean-up] [bug] nova-cloudinary: use Telescope.settings.collection instand

* [clean-up] nova-cloudinary

* [clean-up] nova-categories

* [clean-up] nova-base-components

* [clean-up] nova-api

* [eslint] extends react recommended

* [clean-up] for jsx files

* [eslint] extends meteor recommended

* i forgot this one little change
This commit is contained in:
Comus Leong 2016-11-26 02:46:55 +08:00 committed by Xavier Cazalot
parent 99c8d6ef34
commit 464e20a96c
102 changed files with 548 additions and 457 deletions

1
.eslintignore Normal file
View file

@ -0,0 +1 @@
packages/_*

View file

@ -1,6 +1,8 @@
{ {
"extends": [ "extends": [
"eslint:recommended" "eslint:recommended",
"plugin:meteor/recommended",
"plugin:react/recommended"
], ],
"parser": "babel-eslint", "parser": "babel-eslint",
"parserOptions": { "parserOptions": {
@ -10,7 +12,17 @@
}, },
"rules": { "rules": {
"babel/generator-star-spacing": 0, "babel/generator-star-spacing": 0,
"babel/new-cap": 1, "babel/new-cap": [1, {
"capIsNewExceptions": [
"Optional",
"OneOf",
"Maybe",
"MailChimpAPI",
"Juice",
"Run",
"AppComposer"
]
}],
"babel/array-bracket-spacing": 0, "babel/array-bracket-spacing": 0,
"babel/object-curly-spacing": 0, "babel/object-curly-spacing": 0,
"babel/object-shorthand": 0, "babel/object-shorthand": 0,
@ -20,8 +32,14 @@
"key-spacing": 0, "key-spacing": 0,
"no-extra-boolean-cast": 0, "no-extra-boolean-cast": 0,
"no-undef": 1, "no-undef": 1,
"no-unused-vars": 1, "no-unused-vars": [1, {
"no-console": 1 "vars": "all",
"args": "none",
"varsIgnorePattern": "React|PropTypes|Component"
}],
"no-console": 1,
"react/prop-types": 0,
"meteor/audit-argument-checks": 0
}, },
"env": { "env": {
"browser": true, "browser": true,
@ -32,7 +50,8 @@
}, },
"plugins": [ "plugins": [
"babel", "babel",
"meteor" "meteor",
"react"
], ],
"settings": { "settings": {
"import/resolver": "meteor" "import/resolver": "meteor"

View file

@ -1,7 +1,7 @@
/* /*
The original Newsletter components is defined using an The original Newsletter components is defined using an
ES6 class, so we use the "class foo extends bar" syntax ES6 class, so we use the "class foo extends bar" syntax
to extend it. This way, we can simply redefine the to extend it. This way, we can simply redefine the
render method to change it, while preserving render method to change it, while preserving
all of the class's other methods (other render all of the class's other methods (other render
functions, event handlers, etc.). functions, event handlers, etc.).
@ -9,7 +9,7 @@ functions, event handlers, etc.).
import Telescope from 'meteor/nova:lib'; import Telescope from 'meteor/nova:lib';
import React, { PropTypes, Component } from 'react'; import React, { PropTypes, Component } from 'react';
import { FormattedMessage, intlShape } from 'react-intl'; import { FormattedMessage /*, intlShape */ } from 'react-intl';
class CustomNewsletter extends Telescope.components.Newsletter { class CustomNewsletter extends Telescope.components.Newsletter {
@ -27,4 +27,4 @@ class CustomNewsletter extends Telescope.components.Newsletter {
} }
export default CustomNewsletter; export default CustomNewsletter;

View file

@ -1,12 +1,12 @@
import Telescope from 'meteor/nova:lib'; import Telescope from 'meteor/nova:lib';
import Posts from "meteor/nova:posts";
import React, { PropTypes, Component } from 'react'; import React, { PropTypes, Component } from 'react';
import { FormattedMessage, FormattedRelative } from 'react-intl'; import { FormattedMessage, FormattedRelative } from 'react-intl';
import { Button } from 'react-bootstrap';
import moment from 'moment';
import { ModalTrigger } from "meteor/nova:core";
import { Link } from 'react-router'; import { Link } from 'react-router';
import Posts from "meteor/nova:posts"; // import { Button } from 'react-bootstrap';
import Categories from "meteor/nova:categories"; // import moment from 'moment';
// import { ModalTrigger } from "meteor/nova:core";
// import Categories from "meteor/nova:categories";
class CustomPostsItem extends Telescope.components.PostsItem { class CustomPostsItem extends Telescope.components.PostsItem {
@ -14,7 +14,7 @@ class CustomPostsItem extends Telescope.components.PostsItem {
const post = this.props.post; const post = this.props.post;
let postClass = "posts-item"; let postClass = "posts-item";
if (post.sticky) postClass += " posts-sticky"; if (post.sticky) postClass += " posts-sticky";
// custom code starts here // custom code starts here
@ -25,22 +25,22 @@ class CustomPostsItem extends Telescope.components.PostsItem {
return ( return (
<div className={postClass}> <div className={postClass}>
<div className="posts-item-vote"> <div className="posts-item-vote">
<Telescope.components.Vote post={post} /> <Telescope.components.Vote post={post} />
</div> </div>
{post.thumbnailUrl ? <Telescope.components.PostsThumbnail post={post}/> : null} {post.thumbnailUrl ? <Telescope.components.PostsThumbnail post={post}/> : null}
<div className="posts-item-content"> <div className="posts-item-content">
<h3 className="posts-item-title"> <h3 className="posts-item-title">
<Link to={Posts.getLink(post)} className="posts-item-title-link" target={Posts.getLinkTarget(post)}> <Link to={Posts.getLink(post)} className="posts-item-title-link" target={Posts.getLinkTarget(post)}>
{post.title} {post.title}
</Link> </Link>
{this.renderCategories()} {this.renderCategories()}
</h3> </h3>
<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"><FormattedRelative value={post.postedAt}/></div>
@ -56,13 +56,13 @@ class CustomPostsItem extends Telescope.components.PostsItem {
</div> </div>
{this.renderCommenters()} {this.renderCommenters()}
</div> </div>
) )
} }
}; }
CustomPostsItem.propTypes = { CustomPostsItem.propTypes = {
post: React.PropTypes.object.isRequired post: React.PropTypes.object.isRequired
} }
@ -71,4 +71,4 @@ CustomPostsItem.contextTypes = {
currentUser: React.PropTypes.object currentUser: React.PropTypes.object
}; };
export default CustomPostsItem; export default CustomPostsItem;

View file

@ -1,3 +1,4 @@
import { Picker } from 'meteor/meteorhacks:picker';
import { servePostsApi } from './api.js'; import { servePostsApi } from './api.js';
// for backwards compatibility's sake, accept a "limit" segment // for backwards compatibility's sake, accept a "limit" segment
@ -6,4 +7,4 @@ Picker.route('/api/:limit?', function(params, req, res, next) {
params.query.limit = params.limit; params.query.limit = params.limit;
} }
res.end(servePostsApi(params.query)); res.end(servePostsApi(params.query));
}); });

View file

@ -1,9 +1,9 @@
import Telescope from 'meteor/nova:lib'; import Telescope from 'meteor/nova:lib';
import NovaForm from "meteor/nova:forms";
import Categories from "meteor/nova:categories";
import React, { PropTypes, Component } from 'react'; import React, { PropTypes, Component } from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import NovaForm from "meteor/nova:forms"; // import { DocumentContainer } from "meteor/utilities:react-list-container";
import { DocumentContainer } from "meteor/utilities:react-list-container";
import Categories from "meteor/nova:categories";
class CategoriesEditForm extends Component{ class CategoriesEditForm extends Component{
@ -14,7 +14,7 @@ class CategoriesEditForm extends Component{
deleteCategory() { deleteCategory() {
const category = this.props.category; const category = this.props.category;
if (window.confirm(`Delete category “${category.name}”?`)) { if (window.confirm(`Delete category “${category.name}”?`)) {
this.context.actions.call("categories.deleteById", category._id, (error, result) => { this.context.actions.call("categories.deleteById", category._id, (error, result) => {
if (error) { if (error) {
this.context.messages.flash(error.message, "error"); this.context.messages.flash(error.message, "error");
@ -30,7 +30,7 @@ class CategoriesEditForm extends Component{
return ( return (
<div className="categories-edit-form"> <div className="categories-edit-form">
<NovaForm <NovaForm
document={this.props.category} document={this.props.category}
collection={Categories} collection={Categories}
methodName="categories.edit" methodName="categories.edit"
@ -57,4 +57,4 @@ CategoriesEditForm.contextTypes = {
}; };
module.exports = CategoriesEditForm; module.exports = CategoriesEditForm;
export default CategoriesEditForm; export default CategoriesEditForm;

View file

@ -1,11 +1,11 @@
import Telescope from 'meteor/nova:lib'; import Telescope from 'meteor/nova:lib';
import { /* ModalTrigger, */ ContextPasser } from "meteor/nova:core";
import React, { PropTypes, Component } from 'react'; import React, { PropTypes, Component } from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { Button, DropdownButton, MenuItem, Modal } from 'react-bootstrap'; import { Button, DropdownButton, MenuItem, Modal } from 'react-bootstrap';
import { /* ModalTrigger, */ ContextPasser } from "meteor/nova:core";
import { withRouter } from 'react-router' import { withRouter } from 'react-router'
import { LinkContainer } from 'react-router-bootstrap'; import { LinkContainer } from 'react-router-bootstrap';
import Users from 'meteor/nova:users'; // import Users from 'meteor/nova:users';
// note: cannot use ModalTrigger component because of https://github.com/react-bootstrap/react-bootstrap/issues/1808 // note: cannot use ModalTrigger component because of https://github.com/react-bootstrap/react-bootstrap/issues/1808
@ -36,12 +36,12 @@ class CategoriesList extends Component {
} }
renderCategoryEditModal(category, index) { renderCategoryEditModal(category, index) {
return ( return (
<Modal key={index} show={this.state.openModal === index+1} onHide={this.closeModal}> <Modal key={index} show={this.state.openModal === index+1} onHide={this.closeModal}>
<Modal.Header closeButton> <Modal.Header closeButton>
<Modal.Title><FormattedMessage id="categories.edit"/></Modal.Title> <Modal.Title><FormattedMessage id="categories.edit"/></Modal.Title>
</Modal.Header> </Modal.Header>
<Modal.Body> <Modal.Body>
<ContextPasser currentUser={this.context.currentUser} messages={this.context.messages} actions={this.context.actions} closeCallback={this.closeModal}> <ContextPasser currentUser={this.context.currentUser} messages={this.context.messages} actions={this.context.actions} closeCallback={this.closeModal}>
<Telescope.components.CategoriesEditForm category={category}/> <Telescope.components.CategoriesEditForm category={category}/>
@ -52,12 +52,12 @@ class CategoriesList extends Component {
} }
renderCategoryNewModal() { renderCategoryNewModal() {
return ( return (
<Modal show={this.state.openModal === 0} onHide={this.closeModal}> <Modal show={this.state.openModal === 0} onHide={this.closeModal}>
<Modal.Header closeButton> <Modal.Header closeButton>
<Modal.Title><FormattedMessage id="categories.new"/></Modal.Title> <Modal.Title><FormattedMessage id="categories.new"/></Modal.Title>
</Modal.Header> </Modal.Header>
<Modal.Body> <Modal.Body>
<ContextPasser currentUser={this.context.currentUser} messages={this.context.messages} closeCallback={this.closeModal}> <ContextPasser currentUser={this.context.currentUser} messages={this.context.messages} closeCallback={this.closeModal}>
<Telescope.components.CategoriesNewForm/> <Telescope.components.CategoriesNewForm/>
@ -82,18 +82,18 @@ class CategoriesList extends Component {
} }
render() { render() {
const categories = this.props.categories; const categories = this.props.categories;
const context = this.context; // const context = this.context;
const currentQuery = _.clone(this.props.router.location.query); const currentQuery = _.clone(this.props.router.location.query);
delete currentQuery.cat; delete currentQuery.cat;
return ( return (
<div> <div>
<DropdownButton <DropdownButton
bsStyle="default" bsStyle="default"
className="categories-list btn-secondary" className="categories-list btn-secondary"
title={<FormattedMessage id="categories"/>} title={<FormattedMessage id="categories"/>}
id="categories-dropdown" id="categories-dropdown"
> >
<div className="category-menu-item dropdown-item"> <div className="category-menu-item dropdown-item">
@ -115,7 +115,7 @@ class CategoriesList extends Component {
) )
} }
}; }
CategoriesList.propTypes = { CategoriesList.propTypes = {
categories: React.PropTypes.array categories: React.PropTypes.array
@ -128,4 +128,4 @@ CategoriesList.contextTypes = {
}; };
module.exports = withRouter(CategoriesList); module.exports = withRouter(CategoriesList);
export default withRouter(CategoriesList); export default withRouter(CategoriesList);

View file

@ -1,11 +1,11 @@
import Telescope from 'meteor/nova:lib'; import Telescope from 'meteor/nova:lib';
import Users from 'meteor/nova:users';
import React, { PropTypes, Component } from 'react'; import React, { PropTypes, Component } from 'react';
import { Button, DropdownButton, MenuItem } from 'react-bootstrap';
import classNames from "classnames";
//import { Messages, ModalTrigger } from 'meteor/nova:core';
import { LinkContainer } from 'react-router-bootstrap'; import { LinkContainer } from 'react-router-bootstrap';
import { withRouter } from 'react-router' import { withRouter } from 'react-router'
import Users from 'meteor/nova:users'; import { /* Button, DropdownButton, */ MenuItem } from 'react-bootstrap';
// import classNames from "classnames";
// import { Messages, ModalTrigger } from 'meteor/nova:core';
class Category extends Component { class Category extends Component {
@ -26,7 +26,7 @@ class Category extends Component {
const {category, index, router} = this.props; const {category, index, router} = this.props;
const currentQuery = router.location.query; // const currentQuery = router.location.query;
const currentCategorySlug = router.location.query.cat; const currentCategorySlug = router.location.query.cat;
const newQuery = _.clone(router.location.query); const newQuery = _.clone(router.location.query);
newQuery.cat = category.slug; newQuery.cat = category.slug;
@ -34,9 +34,9 @@ class Category extends Component {
return ( return (
<div className="category-menu-item dropdown-item"> <div className="category-menu-item dropdown-item">
<LinkContainer to={{pathname:"/", query: newQuery}}> <LinkContainer to={{pathname:"/", query: newQuery}}>
<MenuItem <MenuItem
eventKey={index+1} eventKey={index+1}
key={category._id} key={category._id}
> >
{currentCategorySlug === category.slug ? <Telescope.components.Icon name="voted"/> : null} {currentCategorySlug === category.slug ? <Telescope.components.Icon name="voted"/> : null}
{category.name} {category.name}
@ -60,4 +60,4 @@ Category.contextTypes = {
}; };
module.exports = withRouter(Category); module.exports = withRouter(Category);
export default withRouter(Category); export default withRouter(Category);

View file

@ -1,8 +1,8 @@
import Telescope from 'meteor/nova:lib'; import Telescope from 'meteor/nova:lib';
import React, { PropTypes, Component } from 'react'; import React, { PropTypes, Component } from 'react';
import moment from 'moment';
import { intlShape, FormattedMessage, FormattedRelative } from 'react-intl'; import { intlShape, FormattedMessage, FormattedRelative } from 'react-intl';
import Users from 'meteor/nova:users'; // import moment from 'moment';
// import Users from 'meteor/nova:users';
class CommentsItem extends Component{ class CommentsItem extends Component{
@ -33,7 +33,7 @@ class CommentsItem extends Component{
event.preventDefault(); event.preventDefault();
this.setState({showEdit: true}); this.setState({showEdit: true});
} }
editCancelCallback(event) { editCancelCallback(event) {
event.preventDefault(); event.preventDefault();
this.setState({showEdit: false}); this.setState({showEdit: false});
@ -44,11 +44,11 @@ class CommentsItem extends Component{
} }
deleteComment() { deleteComment() {
const comment = this.props.comment; const comment = this.props.comment;
const deleteConfirmMessage = this.context.intl.formatMessage({id: "comments.delete_confirm"}, {body: Telescope.utils.trimWords(comment.body, 20)}); const deleteConfirmMessage = this.context.intl.formatMessage({id: "comments.delete_confirm"}, {body: Telescope.utils.trimWords(comment.body, 20)});
const deleteSuccessMessage = this.context.intl.formatMessage({id: "comments.delete_success"}, {body: Telescope.utils.trimWords(comment.body, 20)}); const deleteSuccessMessage = this.context.intl.formatMessage({id: "comments.delete_success"}, {body: Telescope.utils.trimWords(comment.body, 20)});
if (window.confirm(deleteConfirmMessage)) { if (window.confirm(deleteConfirmMessage)) {
this.context.actions.call('comments.deleteById', comment._id, (error, result) => { this.context.actions.call('comments.deleteById', comment._id, (error, result) => {
this.context.messages.flash(deleteSuccessMessage, "success"); this.context.messages.flash(deleteSuccessMessage, "success");
@ -70,7 +70,7 @@ class CommentsItem extends Component{
<a className="comments-item-reply-link" onClick={this.showReply}> <a className="comments-item-reply-link" onClick={this.showReply}>
<Telescope.components.Icon name="reply"/> <FormattedMessage id="comments.reply"/> <Telescope.components.Icon name="reply"/> <FormattedMessage id="comments.reply"/>
</a> : null} </a> : null}
</div> </div>
) )
} }
@ -78,12 +78,12 @@ class CommentsItem extends Component{
return ( return (
<div className="comments-item-reply"> <div className="comments-item-reply">
<Telescope.components.CommentsNew <Telescope.components.CommentsNew
postId={this.props.comment.postId} postId={this.props.comment.postId}
parentComment={this.props.comment} parentComment={this.props.comment}
successCallback={this.replySuccessCallback} successCallback={this.replySuccessCallback}
cancelCallback={this.replyCancelCallback} cancelCallback={this.replyCancelCallback}
type="reply" type="reply"
/> />
</div> </div>
) )
@ -92,9 +92,9 @@ class CommentsItem extends Component{
renderEdit() { renderEdit() {
return ( return (
<Telescope.components.CommentsEdit <Telescope.components.CommentsEdit
comment={this.props.comment} comment={this.props.comment}
successCallback={this.editSuccessCallback} successCallback={this.editSuccessCallback}
cancelCallback={this.editCancelCallback} cancelCallback={this.editCancelCallback}
/> />
) )

View file

@ -1,6 +1,6 @@
import Telescope from 'meteor/nova:lib'; import Telescope from 'meteor/nova:lib';
import React from 'react'; import React from 'react';
import {injectIntl, FormattedMessage} from 'react-intl'; import {/* injectIntl, */ FormattedMessage} from 'react-intl';
const CommentsList = ({results, hasMore, ready, count, totalCount, loadMore}) => { const CommentsList = ({results, hasMore, ready, count, totalCount, loadMore}) => {
@ -24,11 +24,11 @@ const CommentsList = ({results, hasMore, ready, count, totalCount, loadMore}) =>
<FormattedMessage id="comments.no_comments"/> <FormattedMessage id="comments.no_comments"/>
</p> </p>
</div> </div>
) )
} }
}; };
CommentsList.displayName = "CommentsList"; CommentsList.displayName = "CommentsList";
module.exports = CommentsList; module.exports = CommentsList;

View file

@ -18,8 +18,8 @@ class CommentsNew extends Component {
return ( return (
<div className="comments-new-form"> <div className="comments-new-form">
<NovaForm <NovaForm
collection={Comments} collection={Comments}
methodName="comments.new" methodName="comments.new"
prefilledProps={prefilledProps} prefilledProps={prefilledProps}
successCallback={this.props.successCallback} successCallback={this.props.successCallback}
@ -30,7 +30,7 @@ class CommentsNew extends Component {
) )
} }
}; }
CommentsNew.propTypes = { CommentsNew.propTypes = {
postId: React.PropTypes.string.isRequired, postId: React.PropTypes.string.isRequired,
@ -46,4 +46,4 @@ CommentsNew.contextTypes = {
currentUser: React.PropTypes.object currentUser: React.PropTypes.object
} }
module.exports = CommentsNew; module.exports = CommentsNew;

View file

@ -4,7 +4,7 @@ import React, { PropTypes, Component } from 'react';
class CommentsNode extends Component { class CommentsNode extends Component {
renderComment(comment) { renderComment(comment) {
return ( return (
<Telescope.components.CommentsItem comment={comment} key={comment._id} /> <Telescope.components.CommentsItem comment={comment} key={comment._id} />
) )
@ -22,7 +22,7 @@ class CommentsNode extends Component {
const comment = this.props.comment; const comment = this.props.comment;
const children = this.props.comment.childrenResults; const children = this.props.comment.childrenResults;
return ( return (
<div className="comments-node"> <div className="comments-node">
{this.renderComment(comment)} {this.renderComment(comment)}
@ -31,7 +31,7 @@ class CommentsNode extends Component {
) )
} }
}; }
CommentsNode.propTypes = { CommentsNode.propTypes = {
comment: React.PropTypes.object.isRequired, // the current comment comment: React.PropTypes.object.isRequired, // the current comment
@ -41,4 +41,4 @@ CommentsNode.contextTypes = {
currentUser: React.PropTypes.object, // the current user currentUser: React.PropTypes.object, // the current user
}; };
module.exports = CommentsNode; module.exports = CommentsNode;

View file

@ -10,10 +10,10 @@ class App extends Component {
} }
getChildContext() { getChildContext() {
const messages = Telescope.strings[this.getLocale()] || {}; const messages = Telescope.strings[this.getLocale()] || {};
const intlProvider = new IntlProvider({locale: this.getLocale()}, messages); const intlProvider = new IntlProvider({locale: this.getLocale()}, messages);
const {intl} = intlProvider.getChildContext(); const {intl} = intlProvider.getChildContext();
return { return {
@ -29,8 +29,8 @@ class App extends Component {
return ( return (
<IntlProvider locale={this.getLocale()} messages={Telescope.strings[this.getLocale()]}> <IntlProvider locale={this.getLocale()} messages={Telescope.strings[this.getLocale()]}>
{ {
this.props.ready ? this.props.ready ?
<Telescope.components.Layout>{this.props.children}</Telescope.components.Layout> <Telescope.components.Layout>{this.props.children}</Telescope.components.Layout>
: <Telescope.components.AppLoading /> : <Telescope.components.AppLoading />
} }
</IntlProvider> </IntlProvider>
@ -56,4 +56,4 @@ App.childContextTypes = {
} }
module.exports = AppComposer(App); module.exports = AppComposer(App);
export default AppComposer(App); export default AppComposer(App);

View file

@ -29,7 +29,7 @@ class Newsletter extends Component {
subscribeEmail(data) { subscribeEmail(data) {
this.context.actions.call("newsletter.addEmail", data.email, (error, result) => { this.context.actions.call("newsletter.addEmail", data.email, (error, result) => {
if (error) { if (error) {
console.log(error); console.log(error); // eslint-disable-line
this.context.messages.flash(error.message, "error"); this.context.messages.flash(error.message, "error");
} else { } else {
this.successCallbackSubscription(result); this.successCallbackSubscription(result);

View file

@ -1,7 +1,7 @@
import React, { PropTypes, Component } from 'react'; import React, { PropTypes, Component } from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { Button } from 'react-bootstrap'; import { Button } from 'react-bootstrap';
import { Messages } from 'meteor/nova:core'; // import { Messages } from 'meteor/nova:core';
import Users from 'meteor/nova:users'; import Users from 'meteor/nova:users';
class NewsletterButton extends Component { class NewsletterButton extends Component {
@ -15,7 +15,7 @@ class NewsletterButton extends Component {
'newsletter.removeUser' : 'newsletter.addUser'; 'newsletter.removeUser' : 'newsletter.addUser';
this.context.actions.call(action, this.props.user, (error, result) => { this.context.actions.call(action, this.props.user, (error, result) => {
if (error) { if (error) {
console.log(error); console.log(error); // eslint-disable-line
this.context.messages.flash(error.message, "error"); this.context.messages.flash(error.message, "error");
} else { } else {
this.props.successCallback(result); this.props.successCallback(result);

View file

@ -1,5 +1,6 @@
import Telescope from 'meteor/nova:lib'; import Telescope from 'meteor/nova:lib';
import Users from 'meteor/nova:users'; import Users from 'meteor/nova:users';
import { T9n } from 'meteor/softwarerero:accounts-t9n';
// import { checkNpmVersions } from 'meteor/tmeasday:check-npm-versions'; // import { checkNpmVersions } from 'meteor/tmeasday:check-npm-versions';
// checkNpmVersions({ // checkNpmVersions({
@ -16,4 +17,4 @@ Users.avatar.setOptions({
}); });
// https://github.com/softwarerero/meteor-accounts-t9n // https://github.com/softwarerero/meteor-accounts-t9n
T9n.setLanguage(Telescope.settings.get("locale", "en")); T9n.setLanguage(Telescope.settings.get("locale", "en"));

View file

@ -1,6 +1,6 @@
import Telescope from "meteor/nova:lib"; import Telescope from "meteor/nova:lib";
import React, { PropTypes, Component } from "react"; import React, { PropTypes, Component } from "react";
import { Button } from "react-bootstrap"; // import { Button } from "react-bootstrap";
import moment from "moment"; import moment from "moment";
import { FormattedMessage } from "react-intl"; import { FormattedMessage } from "react-intl";

View file

@ -1,8 +1,8 @@
import Telescope from 'meteor/nova:lib'; import Telescope from 'meteor/nova:lib';
import React, { PropTypes, Component } from 'react';
import { ListContainer } from "meteor/utilities:react-list-container";
import moment from 'moment';
import Posts from "meteor/nova:posts"; import Posts from "meteor/nova:posts";
import { ListContainer } from "meteor/utilities:react-list-container";
import React, { PropTypes, Component } from 'react';
import moment from 'moment';
class PostsDay extends Component { class PostsDay extends Component {
@ -19,19 +19,19 @@ class PostsDay extends Component {
listId: `posts.list.${moment(date).format("YYYY-MM-DD")}` listId: `posts.list.${moment(date).format("YYYY-MM-DD")}`
}; };
({selector, options} = Posts.parameters.get(terms)); const {selector, options} = Posts.parameters.get(terms);
const postsPerPage = Telescope.settings.get("postsPerPage", 10); const postsPerPage = Telescope.settings.get("postsPerPage", 10);
return ( return (
<div className="posts-day"> <div className="posts-day">
<h4 className="posts-day-heading">{moment(date).format("dddd, MMMM Do YYYY")}</h4> <h4 className="posts-day-heading">{moment(date).format("dddd, MMMM Do YYYY")}</h4>
<ListContainer <ListContainer
collection={Posts} collection={Posts}
publication="posts.list" publication="posts.list"
selector={selector} selector={selector}
options={options} options={options}
terms={terms} terms={terms}
joins={Posts.getJoins()} joins={Posts.getJoins()}
component={Telescope.components.PostsList} component={Telescope.components.PostsList}
componentProps={{showHeader: false}} componentProps={{showHeader: false}}
@ -51,4 +51,4 @@ PostsDay.propTypes = {
} }
module.exports = PostsDay; module.exports = PostsDay;
export default PostsDay; export default PostsDay;

View file

@ -1,12 +1,12 @@
import Telescope from 'meteor/nova:lib'; import Telescope from 'meteor/nova:lib';
import React, { PropTypes, Component } from 'react';
import { FormattedMessage, intlShape } from 'react-intl';
import NovaForm from "meteor/nova:forms"; import NovaForm from "meteor/nova:forms";
import { DocumentContainer } from "meteor/utilities:react-list-container"; import { DocumentContainer } from "meteor/utilities:react-list-container";
//import { Messages } from "meteor/nova:core";
//import Actions from "../actions.js";
import Posts from "meteor/nova:posts"; import Posts from "meteor/nova:posts";
import Users from 'meteor/nova:users'; import React, { PropTypes, Component } from 'react';
import { FormattedMessage, intlShape } from 'react-intl';
// import { Messages } from "meteor/nova:core";
// import Actions from "../actions.js";
// import Users from 'meteor/nova:users';
class PostsEditForm extends Component{ class PostsEditForm extends Component{
@ -20,7 +20,7 @@ class PostsEditForm extends Component{
const deletePostConfirm = this.context.intl.formatMessage({id: "posts.delete_confirm"}, {title: post.title}); const deletePostConfirm = this.context.intl.formatMessage({id: "posts.delete_confirm"}, {title: post.title});
const deletePostSuccess = this.context.intl.formatMessage({id: "posts.delete_success"}, {title: post.title}); const deletePostSuccess = this.context.intl.formatMessage({id: "posts.delete_success"}, {title: post.title});
if (window.confirm(deletePostConfirm)) { if (window.confirm(deletePostConfirm)) {
this.context.actions.call('posts.remove', post._id, (error, result) => { this.context.actions.call('posts.remove', post._id, (error, result) => {
this.context.messages.flash(deletePostSuccess, "success"); this.context.messages.flash(deletePostSuccess, "success");
this.context.events.track("post deleted", {'_id': post._id}); this.context.events.track("post deleted", {'_id': post._id});
@ -41,13 +41,13 @@ class PostsEditForm extends Component{
render() { render() {
return ( return (
<div className="posts-edit-form"> <div className="posts-edit-form">
{this.renderAdminArea()} {this.renderAdminArea()}
<DocumentContainer <DocumentContainer
collection={Posts} collection={Posts}
publication="posts.single" publication="posts.single"
selector={{_id: this.props.post._id}} selector={{_id: this.props.post._id}}
terms={{_id: this.props.post._id}} terms={{_id: this.props.post._id}}
joins={Posts.getJoins()} joins={Posts.getJoins()}
@ -57,7 +57,7 @@ class PostsEditForm extends Component{
collection: Posts, collection: Posts,
currentUser: this.context.currentUser, currentUser: this.context.currentUser,
methodName: "posts.edit", methodName: "posts.edit",
successCallback: (post) => { successCallback: (post) => {
this.context.messages.flash(this.context.intl.formatMessage({id: "posts.edit_success"}, {title: post.title}), 'success') this.context.messages.flash(this.context.intl.formatMessage({id: "posts.edit_success"}, {title: post.title}), 'success')
} }
}} }}
@ -82,4 +82,4 @@ PostsEditForm.contextTypes = {
} }
module.exports = PostsEditForm; module.exports = PostsEditForm;
export default PostsEditForm; export default PostsEditForm;

View file

@ -1,6 +1,6 @@
import Telescope from 'meteor/nova:lib'; import Telescope from 'meteor/nova:lib';
import React, { PropTypes, Component } from 'react'; import React, { PropTypes, Component } from 'react';
import { ListContainer, DocumentContainer } from "meteor/utilities:react-list-container"; import { ListContainer /* , DocumentContainer */ } from "meteor/utilities:react-list-container";
import Posts from "meteor/nova:posts"; import Posts from "meteor/nova:posts";
class PostsHome extends Component { class PostsHome extends Component {
@ -8,19 +8,19 @@ class PostsHome extends Component {
getDefaultView() { getDefaultView() {
return {view: 'top'} return {view: 'top'}
} }
render() { render() {
const params = {...this.getDefaultView(), ...this.props.location.query, listId: "posts.list.main"}; const params = {...this.getDefaultView(), ...this.props.location.query, listId: "posts.list.main"};
const {selector, options} = Posts.parameters.get(params); const {selector, options} = Posts.parameters.get(params);
return ( return (
<ListContainer <ListContainer
collection={Posts} collection={Posts}
publication="posts.list" publication="posts.list"
selector={selector} selector={selector}
options={options} options={options}
terms={params} terms={params}
joins={Posts.getJoins()} joins={Posts.getJoins()}
component={Telescope.components.PostsList} component={Telescope.components.PostsList}
cacheSubscription={true} cacheSubscription={true}
@ -29,6 +29,6 @@ class PostsHome extends Component {
/> />
) )
} }
}; }
module.exports = PostsHome; module.exports = PostsHome;

View file

@ -1,12 +1,12 @@
import Telescope from 'meteor/nova:lib'; import Telescope from 'meteor/nova:lib';
import { ModalTrigger } from "meteor/nova:core";
import Posts from "meteor/nova:posts";
import React, { PropTypes, Component } from 'react'; import React, { PropTypes, Component } from 'react';
import { FormattedMessage, FormattedRelative } from 'react-intl'; import { FormattedMessage, FormattedRelative } from 'react-intl';
import { Button } from 'react-bootstrap';
import moment from 'moment';
import { ModalTrigger } from "meteor/nova:core";
import { Link } from 'react-router'; import { Link } from 'react-router';
import Posts from "meteor/nova:posts"; // import { Button } from 'react-bootstrap';
import Users from 'meteor/nova:users'; // import moment from 'moment';
// import Users from 'meteor/nova:users';
class PostsItem extends Component { class PostsItem extends Component {
@ -32,12 +32,12 @@ class PostsItem extends Component {
</div> </div>
) )
} }
render() { render() {
const post = this.props.post; const post = this.props.post;
let postClass = "posts-item"; let postClass = "posts-item";
if (post.sticky) postClass += " posts-sticky"; if (post.sticky) postClass += " posts-sticky";
// console.log(post) // console.log(post)
@ -45,22 +45,22 @@ class PostsItem extends Component {
return ( return (
<div className={postClass}> <div className={postClass}>
<div className="posts-item-vote"> <div className="posts-item-vote">
<Telescope.components.Vote post={post} /> <Telescope.components.Vote post={post} />
</div> </div>
{post.thumbnailUrl ? <Telescope.components.PostsThumbnail post={post}/> : null} {post.thumbnailUrl ? <Telescope.components.PostsThumbnail post={post}/> : null}
<div className="posts-item-content"> <div className="posts-item-content">
<h3 className="posts-item-title"> <h3 className="posts-item-title">
<Link to={Posts.getLink(post)} className="posts-item-title-link" target={Posts.getLinkTarget(post)}> <Link to={Posts.getLink(post)} className="posts-item-title-link" target={Posts.getLinkTarget(post)}>
{post.title} {post.title}
</Link> </Link>
{this.renderCategories()} {this.renderCategories()}
</h3> </h3>
<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">{post.postedAt ? <FormattedRelative value={post.postedAt}/> : <FormattedMessage id="posts.dateNotDefined"/>}</div> <div className="posts-item-date">{post.postedAt ? <FormattedRelative value={post.postedAt}/> : <FormattedMessage id="posts.dateNotDefined"/>}</div>
@ -76,13 +76,13 @@ class PostsItem extends Component {
</div> </div>
{this.renderCommenters()} {this.renderCommenters()}
</div> </div>
) )
} }
}; }
PostsItem.propTypes = { PostsItem.propTypes = {
post: React.PropTypes.object.isRequired post: React.PropTypes.object.isRequired
} }
@ -92,4 +92,4 @@ PostsItem.contextTypes = {
}; };
module.exports = PostsItem; module.exports = PostsItem;
export default PostsItem; export default PostsItem;

View file

@ -1,6 +1,6 @@
import React, { PropTypes, Component } from 'react'; import React, { PropTypes, Component } from 'react';
import { FormattedMessage, intlShape } from 'react-intl'; import { FormattedMessage, intlShape } from 'react-intl';
import { Button, ButtonGroup, DropdownButton, MenuItem } from 'react-bootstrap'; import { /* Button, ButtonGroup, */ DropdownButton, MenuItem } from 'react-bootstrap';
import { LinkContainer } from 'react-router-bootstrap'; import { LinkContainer } from 'react-router-bootstrap';
import { withRouter } from 'react-router' import { withRouter } from 'react-router'
import Users from 'meteor/nova:users'; import Users from 'meteor/nova:users';
@ -9,7 +9,7 @@ const PostsViews = (props, context) => {
let views = ["top", "new", "best"]; let views = ["top", "new", "best"];
const adminViews = ["pending", "rejected", "scheduled"]; const adminViews = ["pending", "rejected", "scheduled"];
if (Users.canDo(context.currentUser, "posts.edit.all")) { if (Users.canDo(context.currentUser, "posts.edit.all")) {
views = views.concat(adminViews); views = views.concat(adminViews);
} }
@ -18,13 +18,13 @@ const PostsViews = (props, context) => {
return ( return (
<div className="posts-views"> <div className="posts-views">
<DropdownButton <DropdownButton
bsStyle="default" bsStyle="default"
className="views btn-secondary" className="views btn-secondary"
title={context.intl.formatMessage({id: "posts.view"})} title={context.intl.formatMessage({id: "posts.view"})}
id="views-dropdown" id="views-dropdown"
> >
{views.map(view => {views.map(view =>
<LinkContainer key={view} to={{pathname: "/", query: {...query, view: view}}} /*to={}*/ className="dropdown-item"> <LinkContainer key={view} to={{pathname: "/", query: {...query, view: view}}} /*to={}*/ className="dropdown-item">
<MenuItem> <MenuItem>
<FormattedMessage id={"posts."+view}/> <FormattedMessage id={"posts."+view}/>

View file

@ -5,7 +5,7 @@ import { Accounts } from 'meteor/std:accounts-ui';
const UsersAccountForm = () => { const UsersAccountForm = () => {
return ( return (
<Accounts.ui.LoginForm /> <Accounts.ui.LoginForm />
) )
}; };
module.exports = UsersAccountForm; module.exports = UsersAccountForm;
@ -23,12 +23,12 @@ class AccountsButton extends Accounts.ui.Button {
render () { render () {
const {label, href, type, disabled, className, onClick} = this.props; const {label, href, type, disabled, className, onClick} = this.props;
if (type === 'link') { if (type === 'link') {
return <a href={ href } className={ className } onClick={ onClick }>{ label }</a>; return <a href={ href } className={ className } onClick={ onClick }>{ label }</a>;
} }
return <Button return <Button
bsStyle="primary" bsStyle="primary"
className={ className } className={ className }
type={ type }  type={ type }
disabled={ disabled } disabled={ disabled }
onClick={ onClick }>{ label } onClick={ onClick }>{ label }
</Button>; </Button>;
@ -44,13 +44,13 @@ class AccountsField extends Accounts.ui.Field {
onChange({ target: { value: this.input.value } }) onChange({ target: { value: this.input.value } })
} }
} }
render() { render() {
const { id, hint, label, type = 'text', onChange, className = "field", defaultValue = "" } = this.props; const { id, hint, /* label, */ type = 'text', onChange, className = "field", defaultValue = "" } = this.props;
const { mount = true } = this.state; const { mount = true } = this.state;
return mount ? ( return mount ? (
<div className={ className }> <div className={ className }>
<FormControl id={ id } type={ type } onChange={ onChange } placeholder={ hint } defaultValue={ defaultValue } /> <FormControl id={ id } type={ type } onChange={ onChange } placeholder={ hint } defaultValue={ defaultValue } />
</div> </div>
) : null; ) : null;
} }
@ -99,4 +99,4 @@ class AccountsField extends Accounts.ui.Field {
Accounts.ui.Button = AccountsButton; Accounts.ui.Button = AccountsButton;
Accounts.ui.Field = AccountsField; Accounts.ui.Field = AccountsField;
// Accounts.ui.SocialButtons = AccountsSocialButtons; // Accounts.ui.SocialButtons = AccountsSocialButtons;
// Accounts.ui.PasswordOrService = AccountsPasswordOrService; // Accounts.ui.PasswordOrService = AccountsPasswordOrService;

View file

@ -1,7 +1,7 @@
import Telescope from 'meteor/nova:lib'; import Telescope from 'meteor/nova:lib';
import React, { PropTypes, Component } from 'react'; import React, { PropTypes, Component } from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { Dropdown, Button } from 'react-bootstrap'; import { Dropdown /* , Button */ } from 'react-bootstrap';
const UsersAccountMenu = () => { const UsersAccountMenu = () => {
@ -14,10 +14,10 @@ const UsersAccountMenu = () => {
<Telescope.components.UsersAccountForm /> <Telescope.components.UsersAccountForm />
</Dropdown.Menu> </Dropdown.Menu>
</Dropdown> </Dropdown>
) )
}; };
UsersAccountMenu.displayName = "UsersAccountMenu"; UsersAccountMenu.displayName = "UsersAccountMenu";
module.exports = UsersAccountMenu; module.exports = UsersAccountMenu;
export default UsersAccountMenu; export default UsersAccountMenu;

View file

@ -1,7 +1,7 @@
import Telescope from 'meteor/nova:lib'; import Telescope from 'meteor/nova:lib';
import React, { PropTypes, Component } from 'react'; import React, { PropTypes, Component } from 'react';
import { FormattedMessage, intlShape } from 'react-intl'; import { FormattedMessage, intlShape } from 'react-intl';
import { Row, Col } from 'react-bootstrap'; // import { Row, Col } from 'react-bootstrap';
import NovaForm from "meteor/nova:forms"; import NovaForm from "meteor/nova:forms";
//import { Messages } from "meteor/nova:core"; //import { Messages } from "meteor/nova:core";
import Users from 'meteor/nova:users'; import Users from 'meteor/nova:users';
@ -9,21 +9,21 @@ import Users from 'meteor/nova:users';
const UsersEdit = (props, context) => { const UsersEdit = (props, context) => {
const user = props.user; const user = props.user;
const currentUser = props.currentUser; // const currentUser = props.currentUser;
//const label = `Edit profile for ${Users.getDisplayName(user)}`; //const label = `Edit profile for ${Users.getDisplayName(user)}`;
return ( return (
<Telescope.components.CanDo <Telescope.components.CanDo
action="users.edit" action="users.edit"
document={user} document={user}
displayNoPermissionMessage={true} displayNoPermissionMessage={true}
> >
<div className="page users-edit-form"> <div className="page users-edit-form">
<h2 className="page-title users-edit-form-title"><FormattedMessage id="users.edit_account"/></h2> <h2 className="page-title users-edit-form-title"><FormattedMessage id="users.edit_account"/></h2>
<NovaForm <NovaForm
collection={Users} collection={Users}
document={user} document={user}
methodName="users.edit" methodName="users.edit"
successCallback={(user)=>{ successCallback={(user)=>{
context.messages.flash(context.intl.formatMessage({id: "users.edit_success"}, {name: Users.getDisplayName(user)}), 'success') context.messages.flash(context.intl.formatMessage({id: "users.edit_success"}, {name: Users.getDisplayName(user)}), 'success')
@ -34,7 +34,7 @@ const UsersEdit = (props, context) => {
) )
}; };
UsersEdit.propTypes = { UsersEdit.propTypes = {
user: React.PropTypes.object.isRequired, user: React.PropTypes.object.isRequired,
}; };
@ -48,4 +48,4 @@ UsersEdit.contextTypes = {
UsersEdit.displayName = "UsersEdit"; UsersEdit.displayName = "UsersEdit";
module.exports = UsersEdit; module.exports = UsersEdit;
export default UsersEdit; export default UsersEdit;

View file

@ -1,5 +1,6 @@
import React, { Component } from 'react';
import { Accounts, STATES } from 'meteor/std:accounts-ui'; import { Accounts, STATES } from 'meteor/std:accounts-ui';
import { T9n } from 'meteor/softwarerero:accounts-t9n';
import React, { Component } from 'react';
import { Link } from 'react-router'; import { Link } from 'react-router';
class UsersResetPassword extends Component { class UsersResetPassword extends Component {

View file

@ -1,16 +1,16 @@
import Telescope from 'meteor/nova:lib'; import Telescope from 'meteor/nova:lib';
import React from 'react';
import {mount} from 'react-mounter';
import { Messages } from 'meteor/nova:core'; import { Messages } from 'meteor/nova:core';
import { IndexRoute, Route, useRouterHistory, browserHistory, createMemoryHistory } from 'react-router';
import { ReactRouterSSR } from 'meteor/reactrouter:react-router-ssr';
import { ListContainer, DocumentContainer } from "meteor/utilities:react-list-container";
// import useNamedRoutes from 'use-named-routes';
import createBrowserHistory from 'history/lib/createBrowserHistory';
import Events from "meteor/nova:events"; import Events from "meteor/nova:events";
import { ReactRouterSSR } from 'meteor/reactrouter:react-router-ssr';
import React from 'react';
import Helmet from 'react-helmet'; import Helmet from 'react-helmet';
import Cookie from 'react-cookie'; import Cookie from 'react-cookie';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
// import {mount} from 'react-mounter';
// import { IndexRoute, Route, useRouterHistory, browserHistory, createMemoryHistory } from 'react-router';
// import { ListContainer, DocumentContainer } from "meteor/utilities:react-list-container";
// import useNamedRoutes from 'use-named-routes';
// import createBrowserHistory from 'history/lib/createBrowserHistory';
Telescope.routes.indexRoute = { name: "posts.list", component: Telescope.components.PostsHome }; Telescope.routes.indexRoute = { name: "posts.list", component: Telescope.components.PostsHome };
@ -33,13 +33,13 @@ Meteor.startup(() => {
childRoutes: Telescope.routes.routes childRoutes: Telescope.routes.routes
} }
let history; // let history;
const clientOptions = { const clientOptions = {
renderHook: ReactDOM.render, renderHook: ReactDOM.render,
props: { props: {
onUpdate: () => { onUpdate: () => {
Events.analyticsRequest(); Events.analyticsRequest();
Messages.clearSeen(); Messages.clearSeen();
} }
} }
@ -48,15 +48,15 @@ Meteor.startup(() => {
const serverOptions = { const serverOptions = {
htmlHook: (html) => { htmlHook: (html) => {
const head = Helmet.rewind(); const head = Helmet.rewind();
return html.replace('<head>', '<head>'+ head.title + head.meta + head.link); return html.replace('<head>', '<head>'+ head.title + head.meta + head.link);
}, },
preRender: (req, res) => { preRender: (req, res) => {
Cookie.plugToRequest(req, res); Cookie.plugToRequest(req, res);
}, },
}; };
ReactRouterSSR.Run(AppRoutes, clientOptions, serverOptions); ReactRouterSSR.Run(AppRoutes, clientOptions, serverOptions);
// note: we did like this at first // note: we did like this at first
// if (Meteor.isClient) { // if (Meteor.isClient) {
// history = useNamedRoutes(useRouterHistory(createBrowserHistory))({ routes: AppRoutes }); // history = useNamedRoutes(useRouterHistory(createBrowserHistory))({ routes: AppRoutes });
@ -66,4 +66,4 @@ Meteor.startup(() => {
// } // }
// ReactRouterSSR.Run(AppRoutes, {historyHook: () => history}, {historyHook: () => history}); // ReactRouterSSR.Run(AppRoutes, {historyHook: () => history}, {historyHook: () => history});
}); });

View file

@ -18,7 +18,8 @@ Categories.getParents = function (category) {
categoriesArray.push(parent); categoriesArray.push(parent);
recurse(parent); recurse(parent);
} }
}(category); };
getParents(category);
return categoriesArray; return categoriesArray;
}; };
@ -37,7 +38,8 @@ Categories.getChildren = function (category) {
categoriesArray = categoriesArray.concat(children); categoriesArray = categoriesArray.concat(children);
recurse(children); recurse(children);
} }
}([category]); };
getChildren([category]);
return categoriesArray; return categoriesArray;
}; };

View file

@ -1,6 +1,7 @@
import Telescope from 'meteor/nova:lib'; import Telescope from 'meteor/nova:lib';
import Categories from "./collection.js"; import Categories from "./collection.js";
import Users from 'meteor/nova:users'; import Users from 'meteor/nova:users';
import { SimpleSchema } from 'meteor/aldeed:simple-schema';
const canInsert = user => Users.canDo(user, "categories.new"); const canInsert = user => Users.canDo(user, "categories.new");
const canEdit = user => Users.canDo(user, "categories.edit.all"); const canEdit = user => Users.canDo(user, "categories.edit.all");
@ -79,7 +80,7 @@ Telescope.settings.collection.addField([
optional: true, optional: true,
form: { form: {
group: 'categories', group: 'categories',
instructions: 'Let users filter by one or multiple categories at a time.', instructions: 'Let users filter by one or multiple categories at a time.',
options: function () { options: function () {
return [ return [
{value: "single", label: "categories_behavior_one_at_a_time"}, {value: "single", label: "categories_behavior_one_at_a_time"},
@ -100,4 +101,4 @@ Telescope.settings.collection.addField([
} }
} }
} }
]); ]);

View file

@ -19,7 +19,7 @@ if (Meteor.settings && Meteor.settings.categories) {
} else { } else {
// if not, create it // if not, create it
Categories.insert(category); Categories.insert(category);
console.log(`// Creating category “${category.name}`); console.log(`// Creating category “${category.name}`); // eslint-disable-line
} }
}); });
} }

View file

@ -1,14 +1,16 @@
import Posts from "meteor/nova:posts"; // import Posts from "meteor/nova:posts";
import Users from 'meteor/nova:users'; import Users from 'meteor/nova:users';
import Categories from "../collection.js"; import Categories from "../collection.js";
Meteor.publish('categories', function() { Meteor.publish('categories', function() {
const currentUser = this.userId && Users.findOne(this.userId); const currentUser = this.userId && Users.findOne(this.userId);
if(Users.canDo(currentUser, "posts.view.approved.all")){ if(Users.canDo(currentUser, "posts.view.approved.all")){
var categories = Categories.find({}, {fields: Categories.publishedFields.list}); var categories = Categories.find({}, {fields: Categories.publishedFields.list});
/*
var publication = this; var publication = this;
categories.forEach(function (category) { categories.forEach(function (category) {
@ -17,8 +19,9 @@ Meteor.publish('categories', function() {
var cursor = Posts.find({$and: [{categories: {$in: categoryIds}}, {status: Posts.config.STATUS_APPROVED}]}); var cursor = Posts.find({$and: [{categories: {$in: categoryIds}}, {status: Posts.config.STATUS_APPROVED}]});
// Counts.publish(publication, category.getCounterName(), cursor, { noReady: true }); // Counts.publish(publication, category.getCounterName(), cursor, { noReady: true });
}); });
*/
return categories; return categories;
} }
return []; return [];
}); });

View file

@ -1,3 +1,4 @@
import Telescope from 'meteor/nova:lib';
import Posts from "meteor/nova:posts"; import Posts from "meteor/nova:posts";
import PublicationUtils from 'meteor/utilities:smart-publications'; import PublicationUtils from 'meteor/utilities:smart-publications';
@ -20,7 +21,7 @@ Posts.addField([
]); ]);
if (typeof Settings !== "undefined") { if (typeof Settings !== "undefined") {
Settings.addField([ Telescope.settings.collection.addField([
{ {
fieldName: 'cloudinaryCloudName', fieldName: 'cloudinaryCloudName',
fieldSchema: { fieldSchema: {

View file

@ -14,7 +14,7 @@ Cloudinary.config({
}); });
const CloudinaryUtils = { const CloudinaryUtils = {
// send an image URL to Cloudinary and get a cloudinary result object in return // send an image URL to Cloudinary and get a cloudinary result object in return
uploadImage(imageUrl) { uploadImage(imageUrl) {
try { try {
@ -26,8 +26,8 @@ const CloudinaryUtils = {
}; };
return data; return data;
} catch (error) { } catch (error) {
console.log("// Cloudinary upload failed for URL: "+imageUrl); console.log("// Cloudinary upload failed for URL: "+imageUrl); // eslint-disable-line
console.log(error.stack); console.log(error.stack); // eslint-disable-line
} }
}, },
@ -35,8 +35,8 @@ const CloudinaryUtils = {
getUrls(cloudinaryId) { getUrls(cloudinaryId) {
return Telescope.settings.get("cloudinaryFormats").map(format => { return Telescope.settings.get("cloudinaryFormats").map(format => {
const url = Cloudinary.url(cloudinaryId, { const url = Cloudinary.url(cloudinaryId, {
width: format.width, width: format.width,
height: format.height, height: format.height,
crop: 'fill', crop: 'fill',
sign_url: true, sign_url: true,
fetch_format: "auto", fetch_format: "auto",
@ -56,7 +56,7 @@ Meteor.methods({
if (Users.isAdmin(Meteor.user())) { if (Users.isAdmin(Meteor.user())) {
thumbnailUrl = typeof thumbnailUrl === "undefined" ? "http://www.telescopeapp.org/images/logo.png" : thumbnailUrl; thumbnailUrl = typeof thumbnailUrl === "undefined" ? "http://www.telescopeapp.org/images/logo.png" : thumbnailUrl;
const data = CloudinaryUtils.uploadImage(thumbnailUrl); const data = CloudinaryUtils.uploadImage(thumbnailUrl);
console.log(data); console.log(data); // eslint-disable-line
} }
}, },
cachePostThumbnails: function (limit = 20) { cachePostThumbnails: function (limit = 20) {
@ -71,16 +71,16 @@ Meteor.methods({
postsWithUncachedThumbnails.forEach(Meteor.bindEnvironment((post, index) => { postsWithUncachedThumbnails.forEach(Meteor.bindEnvironment((post, index) => {
Meteor.setTimeout(function () { Meteor.setTimeout(function () {
console.log(`// ${index}. Caching thumbnail for post “${post.title}” (_id: ${post._id})`); console.log(`// ${index}. Caching thumbnail for post “${post.title}” (_id: ${post._id})`); // eslint-disable-line
const data = CloudinaryUtils.uploadImage(post.thumbnailUrl); const data = CloudinaryUtils.uploadImage(post.thumbnailUrl);
Posts.update(post._id, {$set:{ Posts.update(post._id, {$set:{
cloudinaryId: data.cloudinaryId, cloudinaryId: data.cloudinaryId,
cloudinaryUrls: data.urls cloudinaryUrls: data.urls
}}); }});
}, index * 1000); }, index * 1000);
})); }));
} }
} }
@ -106,7 +106,7 @@ Telescope.callbacks.add("posts.new.async", cachePostThumbnailOnSubmit);
function cachePostThumbnailOnEdit (newPost, oldPost) { function cachePostThumbnailOnEdit (newPost, oldPost) {
if (Telescope.settings.get("cloudinaryAPIKey")) { if (Telescope.settings.get("cloudinaryAPIKey")) {
if (newPost.thumbnailUrl && newPost.thumbnailUrl !== oldPost.thumbnailUrl) { if (newPost.thumbnailUrl && newPost.thumbnailUrl !== oldPost.thumbnailUrl) {
const data = CloudinaryUtils.uploadImage(newPost.thumbnailUrl); const data = CloudinaryUtils.uploadImage(newPost.thumbnailUrl);
Posts.update(newPost._id, {$set:{ Posts.update(newPost._id, {$set:{
cloudinaryId: data.cloudinaryId, cloudinaryId: data.cloudinaryId,
@ -118,4 +118,4 @@ function cachePostThumbnailOnEdit (newPost, oldPost) {
} }
Telescope.callbacks.add("posts.edit.async", cachePostThumbnailOnEdit); Telescope.callbacks.add("posts.edit.async", cachePostThumbnailOnEdit);
export default CloudinaryUtils; export default CloudinaryUtils;

View file

@ -232,7 +232,7 @@ function CommentsNewNotifications (comment) {
} }
} }
} }
} }
} }
@ -278,7 +278,7 @@ Telescope.callbacks.add("comments.edit.method", CommentsEditSubmittedPropertiesC
function UsersRemoveDeleteComments (user, options) { function UsersRemoveDeleteComments (user, options) {
if (options && options.deleteComments) { if (options && options.deleteComments) {
Comments.remove({userId: userId}); Comments.remove({userId: user._id});
} else { } else {
// not sure if anything should be done in that scenario yet // not sure if anything should be done in that scenario yet
// Comments.update({userId: userId}, {$set: {author: "\[deleted\]"}}, {multi: true}); // Comments.update({userId: userId}, {$set: {author: "\[deleted\]"}}, {multi: true});

View file

@ -2,6 +2,6 @@
* @summary The global namespace for Comments. * @summary The global namespace for Comments.
* @namespace Comments * @namespace Comments
*/ */
Comments = new Mongo.Collection("comments"); const Comments = new Mongo.Collection("comments");
export default Comments; export default Comments;

View file

@ -1,6 +1,6 @@
import PublicationUtils from 'meteor/utilities:smart-publications'; import PublicationUtils from 'meteor/utilities:smart-publications';
import Posts from "meteor/nova:posts"; import Posts from 'meteor/nova:posts';
import Users from "meteor/nova:users"; import Users from 'meteor/nova:users';
Posts.addField([ Posts.addField([
/** /**

View file

@ -1,4 +1,4 @@
import Telescope from 'meteor/nova:lib'; // import Telescope from 'meteor/nova:lib';
import Comments from './collection.js'; import Comments from './collection.js';
import Posts from 'meteor/nova:posts'; import Posts from 'meteor/nova:posts';
import Users from 'meteor/nova:users'; import Users from 'meteor/nova:users';

View file

@ -1,6 +1,6 @@
import Comments from './collection.js'; import Comments from './collection.js';
import PublicationsUtils from 'meteor/utilities:smart-publications'; import PublicationsUtils from 'meteor/utilities:smart-publications';
import Posts from "meteor/nova:posts"; // import Posts from "meteor/nova:posts";
Comments.publishedFields = {}; Comments.publishedFields = {};
@ -26,4 +26,4 @@ Comments.publishedFields.list = PublicationsUtils.arrayToFields([
* @summary Specify which fields should be published by the posts.single publication * @summary Specify which fields should be published by the posts.single publication
* @array Posts.publishedFields.single * @array Posts.publishedFields.single
*/ */
Comments.publishedFields.single = PublicationsUtils.arrayToFields(Comments.getPublishedFields()); Comments.publishedFields.single = PublicationsUtils.arrayToFields(Comments.getPublishedFields());

View file

@ -1,7 +1,7 @@
import Telescope from 'meteor/nova:lib'; import Telescope from 'meteor/nova:lib';
import Comments from './collection.js';
import Users from 'meteor/nova:users'; import Users from 'meteor/nova:users';
import { SimpleSchema } from 'meteor/aldeed:simple-schema';
import Comments from './collection.js';
// check if user can create a new comment // check if user can create a new comment
const canInsert = user => Users.canDo(user, "comments.new"); const canInsert = user => Users.canDo(user, "comments.new");
@ -10,7 +10,7 @@ const canInsert = user => Users.canDo(user, "comments.new");
const canEdit = Users.canEdit; const canEdit = Users.canEdit;
// check if user can edit *all* comments // check if user can edit *all* comments
const canEditAll = user => Users.canDo(user, "comments.edit.all"); // const canEditAll = user => Users.canDo(user, "comments.edit.all");
/** /**
* @summary Comments schema * @summary Comments schema
@ -126,7 +126,7 @@ Comments.schema = new SimpleSchema({
} }
}, },
/** /**
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,

View file

@ -1,5 +1,6 @@
import Posts from "meteor/nova:posts"; import Posts from "meteor/nova:posts";
import Users from 'meteor/nova:users'; import Users from 'meteor/nova:users';
import Comments from '../collection.js';
Comments._ensureIndex({postId: 1}); Comments._ensureIndex({postId: 1});
Comments._ensureIndex({parentCommentId: 1}); Comments._ensureIndex({parentCommentId: 1});
@ -9,11 +10,11 @@ Comments._ensureIndex({parentCommentId: 1});
* @param {Object} terms * @param {Object} terms
*/ */
Meteor.publish('comments.list', function (terms) { Meteor.publish('comments.list', function (terms) {
const currentUser = this.userId && Users.findOne(this.userId); const currentUser = this.userId && Users.findOne(this.userId);
terms.currentUserId = this.userId; // add currentUserId to terms terms.currentUserId = this.userId; // add currentUserId to terms
({selector, options} = Comments.parameters.get(terms)); const {selector, options} = Comments.parameters.get(terms);
// commenting this because of FR-SSR issue // commenting this because of FR-SSR issue
// Counts.publish(this, 'comments.list', Comments.find(selector, options)); // Counts.publish(this, 'comments.list', Comments.find(selector, options));
@ -41,12 +42,12 @@ Meteor.publish('comments.list', function (terms) {
// check(terms, {_id: String}); // check(terms, {_id: String});
// //
// let commentIds = [terms._id]; // let commentIds = [terms._id];
// const childCommentIds = _.pluck(Comments.find({parentCommentId: terms._id}, {fields: {_id: 1}}).fetch(), '_id'); // const childCommentIds = _.pluck(Comments.find({parentCommentId: terms._id}, {fields: {_id: 1}}).fetch(), '_id');
// commentIds = commentIds.concat(childCommentIds); // commentIds = commentIds.concat(childCommentIds);
// return Users.canView(currentUser) ? Comments.find({_id: {$in: commentIds}}, {sort: {score: -1, postedAt: -1}}) : []; // return Users.canView(currentUser) ? Comments.find({_id: {$in: commentIds}}, {sort: {score: -1, postedAt: -1}}) : [];
// }); // });
@ -60,7 +61,7 @@ Meteor.publish('comments.list', function (terms) {
// check(commentId, String); // check(commentId, String);
// //
// if(Users.canViewById(this.userId)){ // if(Users.canViewById(this.userId)){
// var comment = Comments.findOne(commentId); // var comment = Comments.findOne(commentId);
@ -75,8 +76,8 @@ Meteor.publish('comments.list', function (terms) {
// check(commentId, String); // check(commentId, String);
// //
// var userIds = []; // var userIds = [];
// if(Users.canViewById(this.userId)){ // if(Users.canViewById(this.userId)){
@ -92,11 +93,11 @@ Meteor.publish('comments.list', function (terms) {
// } // }
// return Users.find({_id: {$in: userIds}}, {fields: Users.pubsub.publicProperties}); // return Users.find({_id: {$in: userIds}}, {fields: Users.pubsub.publicProperties});
// } // }
// } // }
// return []; // return [];
// }); // });

View file

@ -1,4 +1,4 @@
import Telescope from 'meteor/nova:lib'; // import Telescope from 'meteor/nova:lib';
/** /**
* @summary Update an item's (post or comment) score * @summary Update an item's (post or comment) score
@ -7,6 +7,8 @@ import Telescope from 'meteor/nova:lib';
* @param {object} collection - The collection the item belongs to * @param {object} collection - The collection the item belongs to
* @param {string} operation - The operation being performed * @param {string} operation - The operation being performed
*/ */
/*
function updateScore (item, user, collection, operation) { function updateScore (item, user, collection, operation) {
Telescope.updateScore({collection: collection, item: item, forceUpdate: true}); Telescope.updateScore({collection: collection, item: item, forceUpdate: true});
} }
@ -14,6 +16,7 @@ Telescope.callbacks.add("upvote.async", updateScore);
Telescope.callbacks.add("downvote.async", updateScore); Telescope.callbacks.add("downvote.async", updateScore);
Telescope.callbacks.add("cancelUpvote.async", updateScore); Telescope.callbacks.add("cancelUpvote.async", updateScore);
Telescope.callbacks.add("cancelDownvote.async", updateScore); Telescope.callbacks.add("cancelDownvote.async", updateScore);
*/
/** /**
* @summary Update the profile of the user doing the operation * @summary Update the profile of the user doing the operation
@ -22,6 +25,8 @@ Telescope.callbacks.add("cancelDownvote.async", updateScore);
* @param {object} collection - The collection the item belongs to * @param {object} collection - The collection the item belongs to
* @param {string} operation - The operation being performed * @param {string} operation - The operation being performed
*/ */
/*
function updateUser (item, user, collection, operation) { function updateUser (item, user, collection, operation) {
var update = {}; var update = {};
@ -39,10 +44,10 @@ function updateUser (item, user, collection, operation) {
case "downvote": case "downvote":
update.$addToSet = {'telescope.downvotedPosts': vote}; update.$addToSet = {'telescope.downvotedPosts': vote};
break; break;
case "cancelUpvote": case "cancelUpvote":
update.$pull = {'telescope.upvotedPosts': {itemId: item._id}}; update.$pull = {'telescope.upvotedPosts': {itemId: item._id}};
break; break;
case "cancelDownvote": case "cancelDownvote":
update.$pull = {'telescope.downvotedPosts': {itemId: item._id}}; update.$pull = {'telescope.downvotedPosts': {itemId: item._id}};
break; break;
} }
@ -54,6 +59,7 @@ Telescope.callbacks.add("upvote.async", updateUser);
Telescope.callbacks.add("downvote.async", updateUser); Telescope.callbacks.add("downvote.async", updateUser);
Telescope.callbacks.add("cancelUpvote.async", updateUser); Telescope.callbacks.add("cancelUpvote.async", updateUser);
Telescope.callbacks.add("cancelDownvote.async", updateUser); Telescope.callbacks.add("cancelDownvote.async", updateUser);
*/
/** /**
* @summary Update the karma of the item's owner * @summary Update the karma of the item's owner
@ -62,11 +68,13 @@ Telescope.callbacks.add("cancelDownvote.async", updateUser);
* @param {object} collection - The collection the item belongs to * @param {object} collection - The collection the item belongs to
* @param {string} operation - The operation being performed * @param {string} operation - The operation being performed
*/ */
/*
function updateKarma (item, user, collection, operation) { function updateKarma (item, user, collection, operation) {
var votePower = Telescope.getVotePower(user); var votePower = Telescope.getVotePower(user);
var karmaAmount = (operation === "upvote" || operation === "cancelDownvote") ? votePower : -votePower; var karmaAmount = (operation === "upvote" || operation === "cancelDownvote") ? votePower : -votePower;
// only update karma is the operation isn't done by the item's author // only update karma is the operation isn't done by the item's author
if (item.userId !== user._id) { if (item.userId !== user._id) {
Users.update({_id: item.userId}, {$inc: {"telescope.karma": karmaAmount}}); Users.update({_id: item.userId}, {$inc: {"telescope.karma": karmaAmount}});
@ -76,4 +84,5 @@ function updateKarma (item, user, collection, operation) {
Telescope.callbacks.add("upvote.async", updateKarma); Telescope.callbacks.add("upvote.async", updateKarma);
Telescope.callbacks.add("downvote.async", updateKarma); Telescope.callbacks.add("downvote.async", updateKarma);
Telescope.callbacks.add("cancelUpvote.async", updateKarma); Telescope.callbacks.add("cancelUpvote.async", updateKarma);
Telescope.callbacks.add("cancelDownvote.async", updateKarma); Telescope.callbacks.add("cancelDownvote.async", updateKarma);
*/

View file

@ -48,7 +48,7 @@ class ModalTrigger extends Component {
<Modal bsSize={this.props.size} show={this.state.modalIsOpen} onHide={this.closeModal}> <Modal bsSize={this.props.size} show={this.state.modalIsOpen} onHide={this.closeModal}>
{this.props.title ? this.renderHeader() : null} {this.props.title ? this.renderHeader() : null}
<Modal.Body> <Modal.Body>
<ContextPasser <ContextPasser
currentUser={this.context.currentUser} currentUser={this.context.currentUser}
actions={this.context.actions} actions={this.context.actions}
events={this.context.events} events={this.context.events}
@ -62,7 +62,7 @@ class ModalTrigger extends Component {
</div> </div>
) )
} }
}; }
ModalTrigger.propTypes = { ModalTrigger.propTypes = {
component: React.PropTypes.object.isRequired, component: React.PropTypes.object.isRequired,
@ -86,4 +86,4 @@ ModalTrigger.contextTypes = {
// } // }
module.exports = ModalTrigger; module.exports = ModalTrigger;
export default ModalTrigger; export default ModalTrigger;

View file

@ -1,6 +1,7 @@
import Telescope from 'meteor/nova:lib'; import Telescope from 'meteor/nova:lib';
import {Inject} from 'meteor/meteorhacks:inject-initial';
import Events from "meteor/nova:events"; import Events from "meteor/nova:events";
import { Inject } from 'meteor/meteorhacks:inject-initial';
import { SyncedCron } from 'meteor/percolatestudio:synced-cron';
Meteor.startup(function () { Meteor.startup(function () {
Events.log({ Events.log({
@ -20,4 +21,4 @@ Meteor.startup(function() {
} }
}); });
Inject.obj('serverTimezoneOffset', {offset: new Date().getTimezoneOffset()}); Inject.obj('serverTimezoneOffset', {offset: new Date().getTimezoneOffset()});

View file

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import Posts from "meteor/nova:posts"; // import Posts from "meteor/nova:posts";
import Comments from "meteor/nova:comments"; // import Comments from "meteor/nova:comments";
import Users from 'meteor/nova:users'; import Users from 'meteor/nova:users';
const Group = ({name, actions}) => { const Group = ({name, actions}) => {
@ -32,11 +32,11 @@ const Groups = props => {
</table> </table>
</div> </div>
</div> </div>
) )
} }
module.exports = Groups module.exports = Groups
export default Groups export default Groups

View file

@ -1,3 +1,5 @@
/* eslint-disable no-undef */
import TelescopeImport from 'meteor/nova:lib'; import TelescopeImport from 'meteor/nova:lib';
import PostsImport from "meteor/nova:posts"; import PostsImport from "meteor/nova:posts";
import CommentsImport from "meteor/nova:comments"; import CommentsImport from "meteor/nova:comments";

View file

@ -4,14 +4,14 @@ import Users from 'meteor/nova:users';
Meteor.methods({ Meteor.methods({
"email.test": function (emailName) { "email.test": function (emailName) {
const email = NovaEmail.emails[emailName]; const email = NovaEmail.emails[emailName];
if(Users.isAdminById(this.userId)){ if(Users.isAdminById(this.userId)){
console.log("// testing email ["+emailName+"]"); console.log("// testing email ["+emailName+"]"); // eslint-disable-line
let html, properties; let html, properties;
// if email has a custom way of generating its HTML, use it // if email has a custom way of generating its HTML, use it
if (typeof email.getTestHTML !== "undefined") { if (typeof email.getTestHTML !== "undefined") {
@ -33,11 +33,11 @@ Meteor.methods({
const subject = "[Test] " + email.subject.bind(email)(properties); const subject = "[Test] " + email.subject.bind(email)(properties);
NovaEmail.send (Telescope.settings.get('defaultEmail'), subject, html) NovaEmail.send (Telescope.settings.get('defaultEmail'), subject, html)
return subject; return subject;
} else { } else {
throw new Meteor.Error("must_be_admin", "You must be an admin to send test emails"); throw new Meteor.Error("must_be_admin", "You must be an admin to send test emails");
} }
} }
}); });

View file

@ -1,10 +1,13 @@
/* global Movies:true */
import Telescope from 'meteor/nova:lib'; import Telescope from 'meteor/nova:lib';
import React, { PropTypes, Component } from 'react';
import {mount} from 'react-mounter';
import MoviesWrapper from './demo-components.jsx';
import Core from 'meteor/nova:core';
import { Route } from 'react-router';
import Users from 'meteor/nova:users'; import Users from 'meteor/nova:users';
import { SimpleSchema } from 'meteor/aldeed:simple-schema';
import React, { PropTypes, Component } from 'react';
import MoviesWrapper from './demo-components.jsx';
// import {mount} from 'react-mounter';
// import Core from 'meteor/nova:core';
// import { Route } from 'react-router';
////////////////////////////////////////////////////// //////////////////////////////////////////////////////
// Collection & Schema // // Collection & Schema //

View file

@ -1,10 +1,12 @@
/* global Movies */
import Telescope from 'meteor/nova:lib'; import Telescope from 'meteor/nova:lib';
import React, { PropTypes, Component } from 'react'; import React, { PropTypes, Component } from 'react';
import { ListContainer } from "meteor/utilities:react-list-container"; import { ListContainer } from "meteor/utilities:react-list-container";
import NovaForm from "meteor/nova:forms"; import NovaForm from "meteor/nova:forms";
import { Button } from 'react-bootstrap'; import { Button } from 'react-bootstrap';
import { Accounts } from 'meteor/std:accounts-ui'; import { Accounts } from 'meteor/std:accounts-ui';
import { ModalTrigger, Messages, FlashContainer } from "meteor/nova:core"; import { ModalTrigger, /* Messages, */ FlashContainer } from "meteor/nova:core";
const FlashMessages = Telescope.components.FlashMessages; const FlashMessages = Telescope.components.FlashMessages;
@ -24,8 +26,8 @@ class MoviesWrapper extends Component {
<FlashContainer component={FlashMessages}/> <FlashContainer component={FlashMessages}/>
<div className="main"> <div className="main">
<ListContainer <ListContainer
collection={Movies} collection={Movies}
publication="movies.list" publication="movies.list"
terms={{options: {sort: {createdAt: -1}}}} terms={{options: {sort: {createdAt: -1}}}}
options={{sort: {createdAt: -1}}} options={{sort: {createdAt: -1}}}
@ -48,23 +50,23 @@ class MoviesWrapper extends Component {
class MoviesList extends Component { class MoviesList extends Component {
renderNew() { renderNew() {
const component = ( const component = (
<div className="add-movie"> <div className="add-movie">
<ModalTrigger <ModalTrigger
title="Add Movie" title="Add Movie"
component={<Button bsStyle="primary">Add Movie</Button>} component={<Button bsStyle="primary">Add Movie</Button>}
> >
<NovaForm <NovaForm
collection={Movies} collection={Movies}
methodName="movies.create" methodName="movies.create"
currentUser={this.props.currentUser} currentUser={this.props.currentUser}
/> />
</ModalTrigger> </ModalTrigger>
<hr/> <hr/>
</div> </div>
) )
return !!this.props.currentUser ? component : ""; return !!this.props.currentUser ? component : "";
} }
@ -78,7 +80,7 @@ class MoviesList extends Component {
</div> </div>
) )
} }
}; }
////////////////////////////////////////////////////// //////////////////////////////////////////////////////
// Movie // // Movie //
@ -91,14 +93,14 @@ class Movie extends Component {
const movie = this.props; const movie = this.props;
const component = ( const component = (
<ModalTrigger <ModalTrigger
label="Edit Movie" label="Edit Movie"
component={<Button bsStyle="primary">Edit Movie</Button>} component={<Button bsStyle="primary">Edit Movie</Button>}
> >
<NovaForm <NovaForm
collection={Movies} collection={Movies}
currentUser={this.props.currentUser} currentUser={this.props.currentUser}
document={movie} document={movie}
methodName="movies.edit" methodName="movies.edit"
/> />
</ModalTrigger> </ModalTrigger>
@ -112,7 +114,7 @@ class Movie extends Component {
} }
render() { render() {
const movie = this.props; const movie = this.props;
return ( return (
@ -124,8 +126,8 @@ class Movie extends Component {
) )
} }
}; }
const LoadMore = props => <a href="#" className="load-more button button--primary" onClick={props.loadMore}>Load More ({props.count}/{props.totalCount})</a> const LoadMore = props => <a href="#" className="load-more button button--primary" onClick={props.loadMore}>Load More ({props.count}/{props.totalCount})</a>
export default MoviesWrapper export default MoviesWrapper

View file

@ -1,3 +1,5 @@
import { Picker } from 'meteor/meteorhacks:picker';
import NovaEmail from 'meteor/nova:email'; import NovaEmail from 'meteor/nova:email';
Meteor.startup(function () { Meteor.startup(function () {
@ -29,7 +31,7 @@ Meteor.startup(function () {
// return html // return html
res.end(html); res.end(html);
}); });
// raw template // raw template
@ -38,5 +40,5 @@ Meteor.startup(function () {
}); });
}); });
}); });

View file

@ -77,10 +77,10 @@ NovaEmail.send = function(to, subject, html, text){
}); });
} }
console.log('//////// sending email…'); console.log('//////// sending email…'); // eslint-disable-line
console.log('from: '+from); console.log('from: '+from); // eslint-disable-line
console.log('to: '+to); console.log('to: '+to); // eslint-disable-line
console.log('subject: '+subject); console.log('subject: '+subject); // eslint-disable-line
// console.log('html: '+html); // console.log('html: '+html);
// console.log('text: '+text); // console.log('text: '+text);
@ -95,8 +95,8 @@ NovaEmail.send = function(to, subject, html, text){
try { try {
Email.send(email); Email.send(email);
} catch (error) { } catch (error) {
console.log("// error while sending email:") console.log("// error while sending email:"); // eslint-disable-line
console.log(error) console.log(error); // eslint-disable-line
} }
return email; return email;

View file

@ -24,16 +24,16 @@ class EmbedlyURL extends Component {
// the URL has changed, get a new thumbnail // the URL has changed, get a new thumbnail
this.context.actions.call("getEmbedlyData", url, (error, result) => { this.context.actions.call("getEmbedlyData", url, (error, result) => {
console.log("querying Embedly…"); console.log("querying Embedly…"); // eslint-disable-line
this.setState({loading: false}); this.setState({loading: false});
if (error) { if (error) {
console.log(error) console.log(error); // eslint-disable-line
this.context.throwError({content: error.message, type: "error"}); this.context.throwError({content: error.message, type: "error"});
} else { } else {
console.log(result) console.log(result); // eslint-disable-line
this.context.addToAutofilledValues({ this.context.addToAutofilledValues({
title: result.title, title: result.title,
body: result.description, body: result.description,
@ -46,7 +46,7 @@ class EmbedlyURL extends Component {
} }
render() { render() {
const Loading = Telescope.components.Loading; const Loading = Telescope.components.Loading;
const wrapperStyle = { const wrapperStyle = {
@ -61,16 +61,16 @@ class EmbedlyURL extends Component {
}; };
loadingStyle.display = this.state.loading ? "block" : "none"; loadingStyle.display = this.state.loading ? "block" : "none";
// see https://facebook.github.io/react/warnings/unknown-prop.html // see https://facebook.github.io/react/warnings/unknown-prop.html
const {document, updateCurrentValue, control, ...rest} = this.props; const {document, updateCurrentValue, control, ...rest} = this.props; // eslint-disable-line
return ( return (
<div className="embedly-url-field" style={wrapperStyle}> <div className="embedly-url-field" style={wrapperStyle}>
<Input <Input
{...rest} {...rest}
onBlur={this.handleBlur} onBlur={this.handleBlur}
type="text" type="text"
ref={ref => this.input = ref} ref={ref => this.input = ref}
/> />
<div className="embedly-url-field-loading" style={loadingStyle}> <div className="embedly-url-field-loading" style={loadingStyle}>
@ -93,4 +93,4 @@ EmbedlyURL.contextTypes = {
actions: React.PropTypes.object, actions: React.PropTypes.object,
} }
export default EmbedlyURL; export default EmbedlyURL;

View file

@ -1,7 +1,7 @@
import Telescope from 'meteor/nova:lib'; import Telescope from 'meteor/nova:lib';
import React, { PropTypes, Component } from 'react'; import React, { PropTypes, Component } from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import Formsy from 'formsy-react'; // import Formsy from 'formsy-react';
import FRC from 'formsy-react-components'; import FRC from 'formsy-react-components';
const Input = FRC.Input; const Input = FRC.Input;
@ -29,9 +29,9 @@ class ThumbnailURL extends Component {
renderThumbnail() { renderThumbnail() {
return ( return (
<div> <div>
<img <img
className="embedly-thumbnail" className="embedly-thumbnail"
src={this.props.value} src={this.props.value}
style={{ style={{
"width": 150, "width": 150,
"height": Telescope.settings.get('thumbnailHeight', 150) * 150 / Telescope.settings.get('thumbnailWidth', 150) "height": Telescope.settings.get('thumbnailHeight', 150) * 150 / Telescope.settings.get('thumbnailWidth', 150)
@ -44,7 +44,7 @@ class ThumbnailURL extends Component {
render() { render() {
const {name, value, label} = this.props; const {name, /* value, */ label} = this.props;
const inputType = this.state.showInput ? "text" : "hidden"; const inputType = this.state.showInput ? "text" : "hidden";
@ -73,4 +73,4 @@ ThumbnailURL.contextTypes = {
deleteValue: React.PropTypes.func deleteValue: React.PropTypes.func
} }
export default ThumbnailURL; export default ThumbnailURL;

View file

@ -10,7 +10,7 @@ Telescope.callbacks.add("postClass", addThumbnailClass);
function checkIfPreviouslyPosted (data) { function checkIfPreviouslyPosted (data) {
Meteor.call("checkForDuplicates", data.url, function (error, result) { Meteor.call("checkForDuplicates", data.url, function (error, result) {
if (error) { if (error) {
Messages.flash(error.reason + '. <a href="'+FlowRouter.path("postPage", {_id: error.details})+'">'+"go_to_post"+'</a>'); // Messages.flash(error.reason + '. <a href="'+FlowRouter.path("postPage", {_id: error.details})+'">'+"go_to_post"+'</a>');
} }
}); });
return data; return data;

View file

@ -2,8 +2,8 @@ import Telescope from 'meteor/nova:lib';
import Posts from "meteor/nova:posts"; import Posts from "meteor/nova:posts";
import Users from 'meteor/nova:users'; import Users from 'meteor/nova:users';
getEmbedlyData = function (url) { var getEmbedlyData = function (url) {
var data = {}; // var data = {};
var extractBase = 'http://api.embed.ly/1/extract'; var extractBase = 'http://api.embed.ly/1/extract';
var embedlyKey = Telescope.settings.get('embedlyKey'); var embedlyKey = Telescope.settings.get('embedlyKey');
var thumbnailWidth = Telescope.settings.get('thumbnailWidth', 200); var thumbnailWidth = Telescope.settings.get('thumbnailWidth', 200);
@ -11,7 +11,7 @@ getEmbedlyData = function (url) {
if(!embedlyKey) { if(!embedlyKey) {
// fail silently to still let the post be submitted as usual // fail silently to still let the post be submitted as usual
console.log("Couldn't find an Embedly API key! Please add it to your Telescope settings or remove the Embedly module."); console.log("Couldn't find an Embedly API key! Please add it to your Telescope settings or remove the Embedly module."); // eslint-disable-line
return null; return null;
} }
@ -42,7 +42,7 @@ getEmbedlyData = function (url) {
return embedlyData; return embedlyData;
} catch (error) { } catch (error) {
console.log(error) console.log(error); // eslint-disable-line
// the first 13 characters of the Embedly errors are "failed [400] ", so remove them and parse the rest // the first 13 characters of the Embedly errors are "failed [400] ", so remove them and parse the rest
var errorObject = JSON.parse(error.message.substring(13)); var errorObject = JSON.parse(error.message.substring(13));
throw new Meteor.Error(errorObject.error_code, errorObject.error_message); throw new Meteor.Error(errorObject.error_code, errorObject.error_message);
@ -111,7 +111,7 @@ var regenerateThumbnail = function (post) {
Meteor.methods({ Meteor.methods({
testGetEmbedlyData: function (url) { testGetEmbedlyData: function (url) {
check(url, String); check(url, String);
console.log(getEmbedlyData(url)); console.log(getEmbedlyData(url)); // eslint-disable-line
}, },
getEmbedlyData: function (url) { getEmbedlyData: function (url) {
check(url, String); check(url, String);
@ -129,11 +129,11 @@ Meteor.methods({
generateThumbnails: function (limit = 20, mode = "generate") { generateThumbnails: function (limit = 20, mode = "generate") {
// mode = "generate" : generate thumbnails only for all posts that don't have one // mode = "generate" : generate thumbnails only for all posts that don't have one
// mode = "all" : regenerate thumbnais for all posts // mode = "all" : regenerate thumbnais for all posts
if (Users.isAdmin(Meteor.user())) { if (Users.isAdmin(Meteor.user())) {
console.log("// Generating thumbnails…") console.log("// Generating thumbnails…"); // eslint-disable-line
const selector = {url: {$exists: true}}; const selector = {url: {$exists: true}};
if (mode === "generate") { if (mode === "generate") {
selector.thumbnailUrl = {$exists: false}; selector.thumbnailUrl = {$exists: false};
@ -143,11 +143,11 @@ Meteor.methods({
posts.forEach((post, index) => { posts.forEach((post, index) => {
Meteor.setTimeout(function () { Meteor.setTimeout(function () {
console.log(`// ${index}. fetching thumbnail for “${post.title}” (_id: ${post._id})`); console.log(`// ${index}. fetching thumbnail for “${post.title}” (_id: ${post._id})`); // eslint-disable-line
try { try {
regenerateThumbnail(post); regenerateThumbnail(post);
} catch (error) { } catch (error) {
console.log(error); console.log(error); // eslint-disable-line
} }
}, index * 1000); }, index * 1000);
}); });

View file

@ -1,3 +1,5 @@
import { SimpleSchema } from 'meteor/aldeed:simple-schema';
const Events = new Mongo.Collection('events'); const Events = new Mongo.Collection('events');
Events.schema = new SimpleSchema({ Events.schema = new SimpleSchema({
@ -63,4 +65,4 @@ Events.track = function(event, properties){
// } // }
}; };
export default Events; export default Events;

View file

@ -31,7 +31,7 @@ class Tags extends Component {
const tags = this.state.tags; const tags = this.state.tags;
tags.splice(i, 1); tags.splice(i, 1);
const value = this.state.value; const value = this.state.value;
value.splice(i,1); value.splice(i,1);
@ -42,7 +42,7 @@ class Tags extends Component {
} }
handleAddition(tag) { handleAddition(tag) {
// first, check if added tag is part of the possible options // first, check if added tag is part of the possible options
const option = _.findWhere(this.props.options, {label: tag}); const option = _.findWhere(this.props.options, {label: tag});
@ -69,14 +69,14 @@ class Tags extends Component {
render() { render() {
const {name, value, label} = this.props; const {name, /* value, */ label} = this.props;
return ( return (
<div className="form-group row"> <div className="form-group row">
<label className="control-label col-sm-3">{label}</label> <label className="control-label col-sm-3">{label}</label>
<div className="col-sm-9"> <div className="col-sm-9">
<div className="tags-field"> <div className="tags-field">
<ReactTags <ReactTags
tags={this.state.tags} tags={this.state.tags}
suggestions={this.state.suggestions} suggestions={this.state.suggestions}
handleDelete={this.handleDelete} handleDelete={this.handleDelete}
@ -105,4 +105,4 @@ Tags.propTypes = {
label: React.PropTypes.string label: React.PropTypes.string
} }
export default Tags; export default Tags;

View file

@ -1,6 +1,6 @@
import React, { PropTypes, Component } from 'react'; import React, { PropTypes, Component } from 'react';
import DateTimePicker 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) // when the datetime picker mounts, NovaForm will catch the date value (no formsy mixin in this component)
@ -13,11 +13,11 @@ class DateTime extends Component {
<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">
<DateTimePicker <DateTimePicker
value={this.props.value || new Date()} value={this.props.value || new Date()}
// newDate argument is a Moment object given by react-datetime // newDate argument is a Moment object given by react-datetime
onChange={newDate => { this.context.addToAutofilledValues({[this.props.name]: newDate._d}) }} onChange={newDate => { this.context.addToAutofilledValues({[this.props.name]: newDate._d}) }}
format={"x"} format={"x"}
inputProps={{name: this.props.name}} inputProps={{name: this.props.name}}
/> />
</div> </div>

View file

@ -4,7 +4,7 @@ import FRC from 'formsy-react-components';
import DateTime from './DateTime.jsx'; import DateTime from './DateTime.jsx';
import Utils from './utils.js'; // import Utils from './utils.js';
const Checkbox = FRC.Checkbox; const Checkbox = FRC.Checkbox;
// const CheckboxGroup = FRC.CheckboxGroup; // const CheckboxGroup = FRC.CheckboxGroup;
@ -30,7 +30,7 @@ class FormComponent extends Component {
renderComponent() { renderComponent() {
// see https://facebook.github.io/react/warnings/unknown-prop.html // see https://facebook.github.io/react/warnings/unknown-prop.html
const { control, group, updateCurrentValue, document, ...rest } = this.props; const { control, group, updateCurrentValue, document, ...rest } = this.props; // eslint-disable-line
const base = this.props.control === "function" ? this.props : rest; const base = this.props.control === "function" ? this.props : rest;
@ -53,7 +53,7 @@ class FormComponent extends Component {
case "textarea": case "textarea":
return <Textarea {...properties} />; return <Textarea {...properties} />;
case "checkbox": case "checkbox":
return <Checkbox {...properties} />; return <Checkbox {...properties} />;
// note: checkboxgroup cause React refs error // note: checkboxgroup cause React refs error
case "checkboxgroup": case "checkboxgroup":
return <CheckboxGroup {...properties} />; return <CheckboxGroup {...properties} />;
@ -63,7 +63,7 @@ class FormComponent extends Component {
return <Select {...properties} />; return <Select {...properties} />;
case "datetime": case "datetime":
return <DateTime {...properties} />; return <DateTime {...properties} />;
default: default:
return <Input {...properties} type="text" />; return <Input {...properties} type="text" />;
} }
@ -88,7 +88,7 @@ FormComponent.propTypes = {
label: React.PropTypes.string, label: React.PropTypes.string,
value: React.PropTypes.any, value: React.PropTypes.any,
placeholder: React.PropTypes.string, placeholder: React.PropTypes.string,
prefilledValue: React.PropTypes.any, prefilledValue: React.PropTypes.any,
options: React.PropTypes.any, options: React.PropTypes.any,
control: React.PropTypes.any, control: React.PropTypes.any,
datatype: React.PropTypes.any, datatype: React.PropTypes.any,

View file

@ -85,7 +85,7 @@ class NovaForm extends Component{
// backward compatibility from 'autoform' to 'form' // backward compatibility from 'autoform' to 'form'
if (fieldSchema.autoform) { if (fieldSchema.autoform) {
fieldSchema.form = fieldSchema.autoform; fieldSchema.form = fieldSchema.autoform;
console.warn(`🔭 Telescope Nova Warning: The 'autoform' field is deprecated. You should rename it to 'form' instead. It was defined on your '${fieldName}' field on the '${this.props.collection._name}' collection`); console.warn(`🔭 Telescope Nova Warning: The 'autoform' field is deprecated. You should rename it to 'form' instead. It was defined on your '${fieldName}' field on the '${this.props.collection._name}' collection`); // eslint-disable-line
} }
// replace value by prefilled value if value is empty // replace value by prefilled value if value is empty
@ -96,7 +96,7 @@ class NovaForm extends Component{
field.value = prefilledValue; field.value = prefilledValue;
} }
} }
// replace empty value, which has not been prefilled, by the default value from the schema // replace empty value, which has not been prefilled, by the default value from the schema
if (fieldSchema.defaultValue && field.value === "") { if (fieldSchema.defaultValue && field.value === "") {
field.value = fieldSchema.defaultValue; field.value = fieldSchema.defaultValue;
@ -291,7 +291,7 @@ class NovaForm extends Component{
this.setState({disabled: false}); this.setState({disabled: false});
console.log(error); console.log(error); // eslint-disable-line
const errorContent = this.context.intl.formatMessage({id: error.reason}, {details: error.details}) const errorContent = this.context.intl.formatMessage({id: error.reason}, {details: error.details})
// add error to state // add error to state
@ -326,7 +326,7 @@ class NovaForm extends Component{
// complete the data with values from custom components which are not being catched by Formsy mixin // complete the data with values from custom components which are not being catched by Formsy mixin
// note: it follows the same logic as NovaForm's getDocument method // note: it follows the same logic as NovaForm's getDocument method
data = { data = {
...this.state.autofilledValues, // ex: can be values from EmbedlyURL or NewsletterSubscribe component ...this.state.autofilledValues, // ex: can be values from EmbedlyURL or NewsletterSubscribe component
...data, // original data generated thanks to Formsy ...data, // original data generated thanks to Formsy
...this.state.currentValues, // ex: can be values from DateTime component ...this.state.currentValues, // ex: can be values from DateTime component

View file

@ -1,3 +1,5 @@
/* eslint-disable react/display-name */
'use strict'; 'use strict';
var React = require('react'); var React = require('react');
@ -156,4 +158,4 @@ module.exports = {
} }
return (this.isValid() === false); return (this.isValid() === false);
} }
}; };

View file

@ -1,3 +1,5 @@
import { SimpleSchema } from 'meteor/aldeed:simple-schema';
if (typeof SimpleSchema !== "undefined") { if (typeof SimpleSchema !== "undefined") {
SimpleSchema.extendOptions({ SimpleSchema.extendOptions({
control: Match.Optional(Match.Any), // NovaForm control (String or React component) control: Match.Optional(Match.Any), // NovaForm control (String or React component)

View file

@ -92,7 +92,7 @@ var createDummyComments = function () {
}; };
deleteDummyContent = function () { var deleteDummyContent = function () {
Users.remove({'profile.isDummy': true}); Users.remove({'profile.isDummy': true});
Posts.remove({isDummy: true}); Posts.remove({isDummy: true});
Comments.remove({isDummy: true}); Comments.remove({isDummy: true});

View file

@ -1,3 +1,5 @@
/* global InjectData */
Meteor.startup(function() { Meteor.startup(function() {
var dom = $('script[type="text/inject-data"]', document); var dom = $('script[type="text/inject-data"]', document);
var injectedDataString = $.trim(dom.text()); var injectedDataString = $.trim(dom.text());
@ -8,4 +10,4 @@ InjectData.getData = function(key, callback) {
Meteor.startup(function() { Meteor.startup(function() {
callback(InjectData._data[key]); callback(InjectData._data[key]);
}); });
}; };

View file

@ -1 +1,3 @@
InjectData = {}; /* eslint-disable */
InjectData = {};

View file

@ -1,4 +1,6 @@
var http = Npm.require('http'); /* global InjectData */
// var http = Npm.require('http');
var templateText = Assets.getText('lib/inject.html'); var templateText = Assets.getText('lib/inject.html');
var injectDataTemplate = _.template(templateText); var injectDataTemplate = _.template(templateText);
@ -43,7 +45,7 @@ InjectData._hijackWriteIfNeeded = function(res) {
'warn: injecting data turned off due to CORS headers. ' + 'warn: injecting data turned off due to CORS headers. ' +
'read more: http://goo.gl/eGwb4e'; 'read more: http://goo.gl/eGwb4e';
console.warn(warnMessage); console.warn(warnMessage); // eslint-disable-line
originalWrite.call(res, chunk, encoding); originalWrite.call(res, chunk, encoding);
return; return;
} }

View file

@ -1,3 +1,5 @@
/* global InjectData */
InjectData._encode = function(ejson) { InjectData._encode = function(ejson) {
var ejsonString = EJSON.stringify(ejson); var ejsonString = EJSON.stringify(ejson);
return encodeURIComponent(ejsonString); return encodeURIComponent(ejsonString);

View file

@ -1,9 +1,9 @@
var fs = Npm.require('fs'); // var fs = Npm.require('fs');
var path = Npm.require('path'); // var path = Npm.require('path');
// We use this patch to avoid data injection failure during server-side rendering on Meteor 1.4 // We use this patch to avoid data injection failure during server-side rendering on Meteor 1.4
// All the credits for this package goes to Arunoda, Kadira's team & @rigconfig // All the credits for this package goes to Arunoda, Kadira's team & @rigconfig
// see https://github.com/meteor/meteor/issues/7992 // see https://github.com/meteor/meteor/issues/7992
Package.describe({ Package.describe({
"summary": "A way to inject data to the client with initial HTML", "summary": "A way to inject data to the client with initial HTML",

View file

@ -1,4 +1,5 @@
import Telescope from 'meteor/nova:lib'; import Telescope from 'meteor/nova:lib';
import { Kadira } from 'meteor/meteorhacks:kadira';
Meteor.startup(function() { Meteor.startup(function() {
if(process.env.NODE_ENV === "production" && !!Telescope.settings.get('kadiraAppId') && !!Telescope.settings.get('kadiraAppSecret')){ if(process.env.NODE_ENV === "production" && !!Telescope.settings.get('kadiraAppId') && !!Telescope.settings.get('kadiraAppSecret')){

View file

@ -1,8 +1,8 @@
import Telescope from './config.js'; import Telescope from './config.js';
import moment from 'moment'; // import moment from 'moment';
/** /**
* @summary Callback hooks provide an easy way to add extra steps to common operations. * @summary Callback hooks provide an easy way to add extra steps to common operations.
* @namespace Telescope.callbacks * @namespace Telescope.callbacks
*/ */
Telescope.callbacks = {}; Telescope.callbacks = {};
@ -95,7 +95,7 @@ Telescope.callbacks.runAsync = function () {
callback.apply(this, args); callback.apply(this, args);
}); });
}); });
} }
}; };

View file

@ -1,3 +1,4 @@
import { SimpleSchema } from 'meteor/aldeed:simple-schema';
import Telescope from './config.js'; import Telescope from './config.js';
/** /**

View file

@ -1,9 +1,11 @@
import { SimpleSchema } from 'meteor/aldeed:simple-schema';
/** /**
* @summary Kick off the global namespace for Telescope. * @summary Kick off the global namespace for Telescope.
* @namespace Telescope * @namespace Telescope
*/ */
Telescope = {}; const Telescope = {};
Telescope.VERSION = '0.27.4-nova'; Telescope.VERSION = '0.27.4-nova';
@ -106,4 +108,4 @@ Telescope.statuses = [
} }
]; ];
export default Telescope; export default Telescope;

View file

@ -1,3 +1,4 @@
/* eslint-disable */
// see https://gist.github.com/furf/3208381 // see https://gist.github.com/furf/3208381
_.mixin({ _.mixin({

View file

@ -60,7 +60,7 @@ Telescope.utils.camelCaseify = function(str) {
* @param {Number} numWords - Number of words to trim sentence to. * @param {Number} numWords - Number of words to trim sentence to.
*/ */
Telescope.utils.trimWords = function(s, numWords) { Telescope.utils.trimWords = function(s, numWords) {
if (!s) if (!s)
return s; return s;
@ -89,7 +89,7 @@ Telescope.utils.capitalise = function(str) {
Telescope.utils.t = function(message) { Telescope.utils.t = function(message) {
var d = new Date(); var d = new Date();
console.log("### "+message+" rendered at "+d.getHours()+":"+d.getMinutes()+":"+d.getSeconds()); console.log("### "+message+" rendered at "+d.getHours()+":"+d.getMinutes()+":"+d.getSeconds()); // eslint-disable-line
}; };
Telescope.utils.nl2br = function(str) { Telescope.utils.nl2br = function(str) {
@ -147,7 +147,7 @@ Telescope.utils.slugify = function (s) {
Telescope.utils.getUnusedSlug = function (collection, slug) { Telescope.utils.getUnusedSlug = function (collection, slug) {
let suffix = ""; let suffix = "";
let index = 0; let index = 0;
// handle edge case for Users collection // handle edge case for Users collection
const field = collection._name === 'users' ? 'telescope.slug' : 'slug'; const field = collection._name === 'users' ? 'telescope.slug' : 'slug';
@ -239,7 +239,7 @@ Telescope.utils.checkNested = function(obj /*, level1, level2, ... levelN*/) {
Telescope.log = function (s) { Telescope.log = function (s) {
if(Telescope.settings.get('debug', false) || process.env.NODE_ENV === "development") { if(Telescope.settings.get('debug', false) || process.env.NODE_ENV === "development") {
console.log(s); console.log(s); // eslint-disable-line
} }
}; };
@ -313,4 +313,4 @@ Telescope.utils.getLogoUrl = () => {
// the logo may be hosted on another website // the logo may be hosted on another website
return logoUrl.indexOf('://') > -1 ? logoUrl : prefix + logoUrl; return logoUrl.indexOf('://') > -1 ? logoUrl : prefix + logoUrl;
} }
}; };

View file

@ -5,10 +5,10 @@ import Users from 'meteor/nova:users';
function subscribeUserOnProfileCompletion (user) { function subscribeUserOnProfileCompletion (user) {
if (!!Telescope.settings.get('autoSubscribe') && !!Users.getEmail(user)) { if (!!Telescope.settings.get('autoSubscribe') && !!Users.getEmail(user)) {
MailChimpList.add(user, false, function (error, result) { MailChimpList.add(user, false, function (error, result) {
console.log(error); console.log(error); // eslint-disable-line
console.log(result); console.log(result); // eslint-disable-line
}); });
} }
return user; return user;
} }
Telescope.callbacks.add("users.profileCompleted.async", subscribeUserOnProfileCompletion); Telescope.callbacks.add("users.profileCompleted.async", subscribeUserOnProfileCompletion);

View file

@ -1,6 +1,7 @@
import Telescope from 'meteor/nova:lib'; import Telescope from 'meteor/nova:lib';
import Newsletter from '../namespace.js'; import { SyncedCron } from 'meteor/percolatestudio:synced-cron';
import moment from 'moment'; import moment from 'moment';
import Newsletter from '../namespace.js';
const defaultFrequency = [1]; // every monday const defaultFrequency = [1]; // every monday
const defaultTime = '00:00'; // GMT const defaultTime = '00:00'; // GMT
@ -45,7 +46,7 @@ var getSchedule = function (parser) {
Meteor.methods({ Meteor.methods({
getNextJob: function () { getNextJob: function () {
var nextJob = SyncedCron.nextScheduledAtDate('scheduleNewsletter'); var nextJob = SyncedCron.nextScheduledAtDate('scheduleNewsletter');
console.log(nextJob); console.log(nextJob); // eslint-disable-line
return nextJob; return nextJob;
} }
}); });
@ -60,8 +61,8 @@ var addJob = function () {
job: function() { job: function() {
// only schedule newsletter campaigns in production // only schedule newsletter campaigns in production
if (process.env.NODE_ENV === "production" || Telescope.settings.get("enableNewsletterInDev", false)) { if (process.env.NODE_ENV === "production" || Telescope.settings.get("enableNewsletterInDev", false)) {
console.log("// Scheduling newsletter…") console.log("// Scheduling newsletter…"); // eslint-disable-line
console.log(new Date()); console.log(new Date()); // eslint-disable-line
Newsletter.scheduleNextWithMailChimp(); Newsletter.scheduleNextWithMailChimp();
} }
} }

View file

@ -23,7 +23,7 @@ const MailChimp = function ( apiKey, options ) {
if ( !mailChimpOptions.apiKey || mailChimpOptions.apiKey === '' ) { if ( !mailChimpOptions.apiKey || mailChimpOptions.apiKey === '' ) {
console.error( '[MailChimp] Error: No API Key defined!' ); console.error( '[MailChimp] Error: No API Key defined!' ); // eslint-disable-line
throw new Meteor.Error( throw new Meteor.Error(
'No API Key', 'No API Key',
@ -89,4 +89,4 @@ MailChimp.prototype.call = function ( section, method, options, callback ) {
// } // }
// }); // });
export default MailChimp; export default MailChimp;

View file

@ -30,7 +30,7 @@ MailChimpList.add = function(userOrEmail, confirm, done){
try { try {
console.log('// Adding "'+email+'" to MailChimp list…'); console.log('// Adding "'+email+'" to MailChimp list…'); // eslint-disable-line
var api = new MailChimp(apiKey); var api = new MailChimp(apiKey);
var subscribeOptions = { var subscribeOptions = {
@ -47,7 +47,7 @@ MailChimpList.add = function(userOrEmail, confirm, done){
Users.methods.setSetting(user._id, 'newsletter.subscribed', true); Users.methods.setSetting(user._id, 'newsletter.subscribed', true);
} }
console.log("// User subscribed"); console.log("// User subscribed"); // eslint-disable-line
return subscribe; return subscribe;
@ -55,7 +55,7 @@ MailChimpList.add = function(userOrEmail, confirm, done){
throw new Meteor.Error("subscription-failed", error.message); throw new Meteor.Error("subscription-failed", error.message);
} }
} else { } else {
throw new Meteor.Error("Please provide your MailChimp API key and list ID", error.message); throw new Meteor.Error("Please provide your MailChimp API key and list ID");
} }
}; };
@ -74,7 +74,7 @@ MailChimpList.remove = (user) => {
try { try {
console.log('// Removing "'+email+'" from MailChimp list…'); console.log('// Removing "'+email+'" from MailChimp list…'); // eslint-disable-line
var api = new MailChimp(apiKey); var api = new MailChimp(apiKey);
var subscribeOptions = { var subscribeOptions = {
@ -89,7 +89,7 @@ MailChimpList.remove = (user) => {
// mark user as unsubscribed // mark user as unsubscribed
Users.methods.setSetting(user._id, 'newsletter.subscribed', false); Users.methods.setSetting(user._id, 'newsletter.subscribed', false);
console.log("// User unsubscribed"); console.log("// User unsubscribed"); // eslint-disable-line
return subscribe; return subscribe;
@ -97,7 +97,7 @@ MailChimpList.remove = (user) => {
throw new Meteor.Error("unsubscription-failed", error.message); throw new Meteor.Error("unsubscription-failed", error.message);
} }
} else { } else {
throw new Meteor.Error("Please provide your MailChimp API key and list ID", error.message); throw new Meteor.Error("Please provide your MailChimp API key and list ID");
} }
}; };

View file

@ -1,11 +1,14 @@
/* eslint-disable no-console */
// newsletter scheduling with MailChimp // newsletter scheduling with MailChimp
import Telescope from 'meteor/nova:lib'; import Telescope from 'meteor/nova:lib';
import Newsletter from '../../namespace.js'; import Posts from 'meteor/nova:posts';
import MailChimp from './mailchimp_api.js';
import NovaEmail from 'meteor/nova:email'; import NovaEmail from 'meteor/nova:email';
import htmlToText from 'html-to-text'; import htmlToText from 'html-to-text';
import moment from 'moment'; import moment from 'moment';
import Newsletter from '../../namespace.js';
import MailChimp from './mailchimp_api.js';
const defaultPosts = 5; const defaultPosts = 5;
@ -68,14 +71,14 @@ Newsletter.scheduleWithMailChimp = function (campaign, isTest = false) {
}; };
// schedule campaign // schedule campaign
var schedule = api.call('campaigns', 'schedule', scheduleOptions); var schedule = api.call('campaigns', 'schedule', scheduleOptions); // eslint-disable-line
console.log('// Newsletter scheduled for '+scheduledTime); console.log('// Newsletter scheduled for '+scheduledTime);
// console.log(schedule) // console.log(schedule)
// if this is not a test, mark posts as sent // if this is not a test, mark posts as sent
if (!isTest) if (!isTest)
var updated = Posts.update({_id: {$in: campaign.postIds}}, {$set: {scheduledAt: new Date()}}, {multi: true}) var updated = Posts.update({_id: {$in: campaign.postIds}}, {$set: {scheduledAt: new Date()}}, {multi: true}) // eslint-disable-line
// send confirmation email // send confirmation email
var confirmationHtml = NovaEmail.getTemplate('newsletterConfirmation')({ var confirmationHtml = NovaEmail.getTemplate('newsletterConfirmation')({
@ -90,4 +93,4 @@ Newsletter.scheduleWithMailChimp = function (campaign, isTest = false) {
} }
return subject; return subject;
} }
}; };

View file

@ -1,10 +1,11 @@
import Telescope from 'meteor/nova:lib'; import Telescope from 'meteor/nova:lib';
import moment from 'moment';
import Posts from "meteor/nova:posts"; import Posts from "meteor/nova:posts";
import Comments from "meteor/nova:comments"; import Comments from "meteor/nova:comments";
import Users from 'meteor/nova:users'; import Users from 'meteor/nova:users';
import Categories from "meteor/nova:categories"; import Categories from "meteor/nova:categories";
import NovaEmail from 'meteor/nova:email'; import NovaEmail from 'meteor/nova:email';
import { SyncedCron } from 'meteor/percolatestudio:synced-cron';
import moment from 'moment';
import Newsletter from '../namespace.js'; import Newsletter from '../namespace.js';
// create new "newsletter" view for all posts from the past X days that haven't been scheduled yet // create new "newsletter" view for all posts from the past X days that haven't been scheduled yet
@ -14,7 +15,7 @@ Posts.views.add("newsletter", function (terms) {
scheduledAt: {$exists: false} scheduledAt: {$exists: false}
}, },
options: { options: {
sort: {baseScore: -1}, sort: {baseScore: -1},
limit: terms.limit limit: terms.limit
} }
}; };
@ -32,14 +33,14 @@ Newsletter.getPosts = function (postsCount) {
// if there is a last newsletter and it was sent less than 7 days ago use its date, else default to posts from the last 7 days // if there is a last newsletter and it was sent less than 7 days ago use its date, else default to posts from the last 7 days
var lastWeek = moment().subtract(7, 'days'); var lastWeek = moment().subtract(7, 'days');
var after = (lastNewsletter && moment(lastNewsletter.finishedAt).isAfter(lastWeek)) ? lastNewsletter.finishedAt : lastWeek.format("YYYY-MM-DD"); var after = (lastNewsletter && moment(lastNewsletter.finishedAt).isAfter(lastWeek)) ? lastNewsletter.finishedAt : lastWeek.format("YYYY-MM-DD");
// get parameters using "newsletter" view // get parameters using "newsletter" view
var params = Posts.parameters.get({ var params = Posts.parameters.get({
view: "newsletter", view: "newsletter",
after: after, after: after,
limit: postsCount limit: postsCount
}); });
return Posts.find(params.selector, params.options).fetch(); return Posts.find(params.selector, params.options).fetch();
}; };
@ -91,7 +92,7 @@ Newsletter.build = function (postsArray) {
// get the two highest-scoring comments // get the two highest-scoring comments
properties.popularComments = Comments.find({postId: post._id}, {sort: {score: -1}, limit: 2, transform: function (comment) { properties.popularComments = Comments.find({postId: post._id}, {sort: {score: -1}, limit: 2, transform: function (comment) {
// get comment author // get comment author
var user = Users.findOne(comment.userId); var user = Users.findOne(comment.userId);
@ -105,7 +106,7 @@ Newsletter.build = function (postsArray) {
} catch (error) { } catch (error) {
comment.authorAvatarUrl = false; comment.authorAvatarUrl = false;
} }
return comment; return comment;
}}).fetch(); }}).fetch();
@ -151,13 +152,13 @@ Newsletter.build = function (postsArray) {
var emailHTML = NovaEmail.buildTemplate(newsletterHTML, { var emailHTML = NovaEmail.buildTemplate(newsletterHTML, {
date: moment().format("dddd, MMMM D YYYY") date: moment().format("dddd, MMMM D YYYY")
}); });
// 4. build campaign object and return it // 4. build campaign object and return it
var campaign = { var campaign = {
postIds: _.pluck(postsArray, '_id'), postIds: _.pluck(postsArray, '_id'),
subject: Telescope.utils.trimWords(subject, 15), subject: Telescope.utils.trimWords(subject, 15),
html: emailHTML html: emailHTML
}; };
return campaign; return campaign;
}; };

View file

@ -21,7 +21,7 @@ Telescope.notifications.create = (userIds, notificationName, data) => {
if (!!userEmail) { if (!!userEmail) {
NovaEmail.buildAndSendHTML(Users.getEmail(user), subject, html); NovaEmail.buildAndSendHTML(Users.getEmail(user), subject, html);
} else { } else {
console.log(`// Couldn't send notification: admin user ${user._id} doesn't have an email`); console.log(`// Couldn't send notification: admin user ${user._id} doesn't have an email`); // eslint-disable-line
} }
}); });

View file

@ -360,7 +360,7 @@ Telescope.callbacks.add("posts.edit.sync", PostsEditSetPostedAt);
// ------------------------------------- posts.edit.async -------------------------------- // // ------------------------------------- posts.edit.async -------------------------------- //
function PostsEditRunPostApprovedCallbacks (post, oldPost) { function PostsEditRunPostApprovedCallbacks (post, oldPost) {
var now = new Date(); // var now = new Date();
if (Posts.isApproved(post) && !Posts.isApproved(oldPost)) { if (Posts.isApproved(post) && !Posts.isApproved(oldPost)) {
Telescope.callbacks.runAsync("posts.approve.async", post); Telescope.callbacks.runAsync("posts.approve.async", post);
@ -388,7 +388,7 @@ Telescope.callbacks.add("posts.approve.async", PostsApprovedNotification);
function UsersRemoveDeletePosts (user, options) { function UsersRemoveDeletePosts (user, options) {
if (options && options.deletePosts) { if (options && options.deletePosts) {
Posts.remove({userId: userId}); Posts.remove({userId: user._id});
} else { } else {
// not sure if anything should be done in that scenario yet // not sure if anything should be done in that scenario yet
// Posts.update({userId: userId}, {$set: {author: "\[deleted\]"}}, {multi: true}); // Posts.update({userId: userId}, {$set: {author: "\[deleted\]"}}, {multi: true});

View file

@ -4,16 +4,16 @@ const returnEmptyObject = function () {
return {}; return {};
} }
console.log(Mongo) console.log(Mongo); // eslint-disable-line
const Mongo = typeof Mongo !== "undefined" ? Mongo : { const Mongo = typeof Mongo !== "undefined" ? Mongo : {
Collection: function () { Collection: function () {
return {attachSchema: returnEmptyObject} return {attachSchema: returnEmptyObject}
} }
}; };
console.log("// Mongo") console.log("// Mongo"); // eslint-disable-line
console.log(Mongo) console.log(Mongo); // eslint-disable-line
const Meteor = typeof Meteor !== "undefined" ? Meteor : { const Meteor = typeof Meteor !== "undefined" ? Meteor : {
methods: returnEmptyObject methods: returnEmptyObject
@ -22,4 +22,4 @@ const Meteor = typeof Meteor !== "undefined" ? Meteor : {
const SimpleSchema = typeof SimpleSchema !== "undefined" ? SimpleSchema : returnEmptyObject; const SimpleSchema = typeof SimpleSchema !== "undefined" ? SimpleSchema : returnEmptyObject;
const Foo = "bar" const Foo = "bar"
export { Mongo, Foo } export { Mongo, Foo }

View file

@ -1,7 +1,8 @@
import Telescope from 'meteor/nova:lib'; import Telescope from 'meteor/nova:lib';
import Posts from './collection.js'
import Users from 'meteor/nova:users'; import Users from 'meteor/nova:users';
import Events from "meteor/nova:events"; import Events from 'meteor/nova:events';
import { Messages } from 'meteor/nova:core';
import Posts from './collection.js'
/** /**
* *
@ -113,7 +114,7 @@ Meteor.methods({
* @param {Object} modifier - the update modifier * @param {Object} modifier - the update modifier
*/ */
'posts.edit': function (postId, modifier) { 'posts.edit': function (postId, modifier) {
Posts.simpleSchema().namedContext("posts.edit").validate(modifier, {modifier: true}); Posts.simpleSchema().namedContext("posts.edit").validate(modifier, {modifier: true});
check(postId, String); check(postId, String);
@ -134,7 +135,7 @@ Meteor.methods({
'posts.approve': function(postId){ 'posts.approve': function(postId){
check(postId, String); check(postId, String);
const post = Posts.findOne(postId); const post = Posts.findOne(postId);
const now = new Date(); const now = new Date();
@ -145,7 +146,7 @@ Meteor.methods({
if (!post.postedAt) { if (!post.postedAt) {
set.postedAt = now; set.postedAt = now;
} }
Posts.update(post._id, {$set: set}); Posts.update(post._id, {$set: set});
Telescope.callbacks.runAsync("posts.approve.async", post); Telescope.callbacks.runAsync("posts.approve.async", post);
@ -164,15 +165,15 @@ Meteor.methods({
'posts.reject': function(postId){ 'posts.reject': function(postId){
check(postId, String); check(postId, String);
const post = Posts.findOne(postId); const post = Posts.findOne(postId);
if(Users.isAdmin(Meteor.user())){ if(Users.isAdmin(Meteor.user())){
Posts.update(post._id, {$set: {status: Posts.config.STATUS_REJECTED}}); Posts.update(post._id, {$set: {status: Posts.config.STATUS_REJECTED}});
Telescope.callbacks.runAsync("postRejectAsync", post); Telescope.callbacks.runAsync("postRejectAsync", post);
}else{ }else{
Messages.flash('You need to be an admin to do that.', "error"); Messages.flash('You need to be an admin to do that.', "error");
} }
@ -188,7 +189,7 @@ Meteor.methods({
check(postId, String); check(postId, String);
check(sessionId, Match.Any); check(sessionId, Match.Any);
// only let users increment a post's view counter once per session // only let users increment a post's view counter once per session
var view = {_id: postId, userId: this.userId, sessionId: sessionId}; var view = {_id: postId, userId: this.userId, sessionId: sessionId};
@ -237,7 +238,7 @@ Meteor.methods({
* @param {String} url - the URL to check * @param {String} url - the URL to check
*/ */
'posts.checkForDuplicates': function (url) { 'posts.checkForDuplicates': function (url) {
Posts.checkForSameUrl(url); Posts.checkForSameUrl(url);
}, },
/** /**

View file

@ -1,9 +1,10 @@
import Telescope from 'meteor/nova:lib'; import Telescope from 'meteor/nova:lib';
import Posts from './collection.js' import { Injected } from 'meteor/meteorhacks:inject-initial';
import moment from 'moment'; import moment from 'moment';
import Posts from './collection.js'
/** /**
* @summary Parameter callbacks let you add parameters to subscriptions * @summary Parameter callbacks let you add parameters to subscriptions
* @namespace Posts.parameters * @namespace Posts.parameters
*/ */
Posts.parameters = {}; Posts.parameters = {};
@ -32,25 +33,25 @@ Posts.parameters.get = function (terms) {
// iterate over posts.parameters callbacks // iterate over posts.parameters callbacks
parameters = Telescope.callbacks.run("posts.parameters", parameters, _.clone(terms)); parameters = Telescope.callbacks.run("posts.parameters", parameters, _.clone(terms));
// if sort options are not provided, default to "createdAt" sort // if sort options are not provided, default to "createdAt" sort
if (_.isEmpty(parameters.options.sort)) { if (_.isEmpty(parameters.options.sort)) {
parameters.options.sort = {sticky: -1, createdAt: -1}; parameters.options.sort = {sticky: -1, createdAt: -1};
} }
// extend sort to sort posts by _id to break ties // extend sort to sort posts by _id to break ties
// NOTE: always do this last to avoid _id sort overriding another sort // NOTE: always do this last to avoid _id sort overriding another sort
parameters = Telescope.utils.deepExtend(true, parameters, {options: {sort: {_id: -1}}}); parameters = Telescope.utils.deepExtend(true, parameters, {options: {sort: {_id: -1}}});
// console.log(parameters); // console.log(parameters);
return parameters; return parameters;
}; };
// Parameter callbacks // Parameter callbacks
// View Parameter // View Parameter
// Add a "view" property to terms which can be used to filter posts. // Add a "view" property to terms which can be used to filter posts.
function addViewParameter (parameters, terms) { function addViewParameter (parameters, terms) {
// if view is not defined, default to "new" // if view is not defined, default to "new"
@ -65,25 +66,25 @@ function addViewParameter (parameters, terms) {
Telescope.callbacks.add("posts.parameters", addViewParameter); Telescope.callbacks.add("posts.parameters", addViewParameter);
// View Parameter // View Parameter
// Add "after" and "before" properties to terms which can be used to limit posts in time. // Add "after" and "before" properties to terms which can be used to limit posts in time.
function addTimeParameter (parameters, terms) { function addTimeParameter (parameters, terms) {
// console.log("// addTimeParameter") // console.log("// addTimeParameter")
if (typeof parameters.selector.postedAt === "undefined") { if (typeof parameters.selector.postedAt === "undefined") {
let postedAt = {}, mAfter, mBefore, startOfDay, endOfDay, clientTimezoneOffset, serverTimezoneOffset, timeDifference; let postedAt = {}, mAfter, mBefore, startOfDay, endOfDay, clientTimezoneOffset, serverTimezoneOffset, timeDifference;
/* /*
If we're on the client, add the time difference between client and server If we're on the client, add the time difference between client and server
Example: client is on Japanese time (+9 hours), Example: client is on Japanese time (+9 hours),
server on UCT (Greenwich) time (+0 hours), for a total difference of +9 hours. server on UCT (Greenwich) time (+0 hours), for a total difference of +9 hours.
So the time "00:00, UCT" is equivalent to "09:00, JST". So the time "00:00, UCT" is equivalent to "09:00, JST".
So if we want to express the timestamp "00:00, UCT" on the client, So if we want to express the timestamp "00:00, UCT" on the client,
we *add* 9 hours to "00:00, JST" on the client to get "09:00, JST" and we *add* 9 hours to "00:00, JST" on the client to get "09:00, JST" and
sync up both times. sync up both times.
@ -93,7 +94,7 @@ function addTimeParameter (parameters, terms) {
clientTimezoneOffset = -1 * new Date().getTimezoneOffset(); clientTimezoneOffset = -1 * new Date().getTimezoneOffset();
serverTimezoneOffset = -1 * Injected.obj('serverTimezoneOffset').offset; serverTimezoneOffset = -1 * Injected.obj('serverTimezoneOffset').offset;
timeDifference = clientTimezoneOffset - serverTimezoneOffset; timeDifference = clientTimezoneOffset - serverTimezoneOffset;
// console.log("client time:"+clientTimezoneOffset); // console.log("client time:"+clientTimezoneOffset);
// console.log("server time:"+serverTimezoneOffset); // console.log("server time:"+serverTimezoneOffset);
// console.log("difference: "+timeDifference); // console.log("difference: "+timeDifference);
@ -127,7 +128,7 @@ function addTimeParameter (parameters, terms) {
} }
postedAt.$lt = endOfDay.toDate(); postedAt.$lt = endOfDay.toDate();
} }
if (!_.isEmpty(postedAt)) { if (!_.isEmpty(postedAt)) {

View file

@ -1,6 +1,7 @@
import Telescope from 'meteor/nova:lib'; import Telescope from 'meteor/nova:lib';
import Posts from './collection.js';
import Users from 'meteor/nova:users'; import Users from 'meteor/nova:users';
import { SimpleSchema } from 'meteor/aldeed:simple-schema';
import Posts from './collection.js';
/** /**
* @summary Posts config namespace * @summary Posts config namespace
@ -235,7 +236,7 @@ Posts.schemaJSON = {
publish: true, publish: true,
}, },
/** /**
The post author's `_id`. The post author's `_id`.
*/ */
userId: { userId: {
type: String, type: String,

View file

@ -1,5 +1,6 @@
import { SyncedCron } from 'meteor/percolatestudio:synced-cron';
// import moment from 'moment';
import Posts from '../collection.js'; import Posts from '../collection.js';
import moment from 'moment';
SyncedCron.options = { SyncedCron.options = {
log: true, log: true,
@ -18,17 +19,17 @@ const addJob = function () {
job() { job() {
// fetch all posts tagged as future // fetch all posts tagged as future
const scheduledPosts = Posts.find({isFuture: true}, {fields: {_id: 1, status: 1, postedAt: 1, userId: 1, title: 1}}).fetch(); const scheduledPosts = Posts.find({isFuture: true}, {fields: {_id: 1, status: 1, postedAt: 1, userId: 1, title: 1}}).fetch();
// filter the scheduled posts to retrieve only the one that should update, considering their schedule // filter the scheduled posts to retrieve only the one that should update, considering their schedule
const postsToUpdate = scheduledPosts.filter(post => post.postedAt <= new Date()); const postsToUpdate = scheduledPosts.filter(post => post.postedAt <= new Date());
// update posts found // update posts found
if (!_.isEmpty(postsToUpdate)) { if (!_.isEmpty(postsToUpdate)) {
const postsIds = _.pluck(postsToUpdate, '_id'); const postsIds = _.pluck(postsToUpdate, '_id');
Posts.update({_id: {$in: postsIds}}, {$set: {isFuture: false}}, {multi: true}); Posts.update({_id: {$in: postsIds}}, {$set: {isFuture: false}}, {multi: true});
// log the action // log the action
console.log('// Scheduled posts approved:', postsIds); console.log('// Scheduled posts approved:', postsIds); // eslint-disable-line
} }
} }
}); });

View file

@ -1,7 +1,8 @@
import Telescope from 'meteor/nova:lib'; import Telescope from 'meteor/nova:lib';
import Posts from '../collection.js';
// import Comments from "meteor/nova:comments"; // import Comments from "meteor/nova:comments";
import Users from 'meteor/nova:users'; import Users from 'meteor/nova:users';
import { Counts } from 'meteor/tmeasday:publish-counts';
import Posts from '../collection.js';
Posts._ensureIndex({"status": 1, "postedAt": 1}); Posts._ensureIndex({"status": 1, "postedAt": 1});
@ -25,7 +26,7 @@ const getPostsListUsers = posts => {
userIds = _.unique(userIds); userIds = _.unique(userIds);
return Users.find({_id: {$in: userIds}}, {fields: Users.publishedFields.list}); return Users.find({_id: {$in: userIds}}, {fields: Users.publishedFields.list});
}; };
/** /**
@ -37,12 +38,12 @@ const getSinglePostUsers = post => {
let users = [post.userId]; // publish post author's ID let users = [post.userId]; // publish post author's ID
/* /*
NOTE: to avoid circular dependencies between nova:posts and nova:comments, NOTE: to avoid circular dependencies between nova:posts and nova:comments,
use callback hook to get comment authors use callback hook to get comment authors
*/ */
users = Telescope.callbacks.run("posts.single.getUsers", users, post); users = Telescope.callbacks.run("posts.single.getUsers", users, post);
// add upvoters // add upvoters
if (post.upvoters && post.upvoters.length) { if (post.upvoters && post.upvoters.length) {
users = users.concat(post.upvoters); users = users.concat(post.upvoters);
@ -67,15 +68,15 @@ const getSinglePostUsers = post => {
*/ */
Meteor.publish('posts.list', function (terms) { Meteor.publish('posts.list', function (terms) {
// this.unblock(); // causes bug where publication returns 0 results // this.unblock(); // causes bug where publication returns 0 results
this.autorun(function () { this.autorun(function () {
const currentUser = this.userId && Users.findOne(this.userId); const currentUser = this.userId && Users.findOne(this.userId);
terms.currentUserId = this.userId; // add currentUserId to terms terms.currentUserId = this.userId; // add currentUserId to terms
const {selector, options} = Posts.parameters.get(terms); const {selector, options} = Posts.parameters.get(terms);
Counts.publish(this, terms.listId, Posts.find(selector, options), {noReady: true}); Counts.publish(this, terms.listId, Posts.find(selector, options), {noReady: true});
options.fields = Posts.publishedFields.list; options.fields = Posts.publishedFields.list;
@ -90,7 +91,7 @@ Meteor.publish('posts.list', function (terms) {
}); });
return Users.canDo(currentUser, "posts.view.approved.all") ? [posts, users] : []; return Users.canDo(currentUser, "posts.view.approved.all") ? [posts, users] : [];
}); });
}); });
@ -112,8 +113,8 @@ Meteor.publish('posts.single', function (terms) {
const users = getSinglePostUsers(post); const users = getSinglePostUsers(post);
return Users.canView(currentUser, post) ? [posts, users] : []; return Users.canView(currentUser, post) ? [posts, users] : [];
} else { } else {
console.log(`// posts.single: no post found for _id “${terms._id}`) console.log(`// posts.single: no post found for _id “${terms._id}`); // eslint-disable-line
return []; return [];
} }
}); });

View file

@ -1,11 +1,12 @@
import Posts from '../collection.js'; import { Picker } from 'meteor/meteorhacks:picker';
import escapeStringRegexp from 'escape-string-regexp'; import escapeStringRegexp from 'escape-string-regexp';
import Posts from '../collection.js';
Picker.route('/out', function(params, req, res, next) { Picker.route('/out', function(params, req, res, next) {
var query = params.query; var query = params.query;
if(query.url){ // for some reason, query.url doesn't need to be decoded if(query.url){ // for some reason, query.url doesn't need to be decoded
/* /*
If the URL passed to ?url= is in plain text, any hash fragment If the URL passed to ?url= is in plain text, any hash fragment
will get stripped out. will get stripped out.
So we search for any post whose URL contains the current URL to get a match So we search for any post whose URL contains the current URL to get a match
@ -25,4 +26,4 @@ Picker.route('/out', function(params, req, res, next) {
} else { } else {
res.end("Please provide a URL"); res.end("Please provide a URL");
} }
}); });

View file

@ -1,3 +1,4 @@
import Users from 'meteor/nova:users';
import Posts from './collection.js' import Posts from './collection.js'
/** /**
@ -103,7 +104,7 @@ Posts.views.add("userPosts", function (terms) {
isFuture: {$ne: true} isFuture: {$ne: true}
}, },
options: { options: {
limit: 5, limit: 5,
sort: { sort: {
postedAt: -1 postedAt: -1
} }

View file

@ -1,3 +1,4 @@
import { Picker } from 'meteor/meteorhacks:picker';
import { servePostRSS, serveCommentRSS } from './rss.js'; import { servePostRSS, serveCommentRSS } from './rss.js';
Picker.route('/feed.xml', function(params, req, res, next) { Picker.route('/feed.xml', function(params, req, res, next) {

View file

@ -52,9 +52,9 @@ const serveCommentRSS = function (terms, url) {
var feed = new RSS(getMeta(url)); var feed = new RSS(getMeta(url));
Comments.find({isDeleted: {$ne: true}}, {sort: {postedAt: -1}, limit: 20}).forEach(function(comment) { Comments.find({isDeleted: {$ne: true}}, {sort: {postedAt: -1}, limit: 20}).forEach(function(comment) {
post = Posts.findOne(comment.postId); var post = Posts.findOne(comment.postId);
feed.item({ feed.item({
title: 'Comment on '+post.title, title: 'Comment on ' + post.title,
description: `${comment.body}</br></br><a href="${comment.getPageUrl(true)}">Discuss</a>`, description: `${comment.body}</br></br><a href="${comment.getPageUrl(true)}">Discuss</a>`,
author: comment.author, author: comment.author,
date: comment.postedAt, date: comment.postedAt,

View file

@ -1,5 +1,6 @@
import Telescope from 'meteor/nova:lib'; import Telescope from 'meteor/nova:lib';
import Users from 'meteor/nova:users'; import Users from 'meteor/nova:users';
import { SimpleSchema } from 'meteor/aldeed:simple-schema';
const isInSettingsJSON = function () { const isInSettingsJSON = function () {
// settings can either be in settings json's public, or in the special object we publish only for admins for private settings // settings can either be in settings json's public, or in the special object we publish only for admins for private settings
@ -371,4 +372,4 @@ Telescope.settings.schema = new SimpleSchema({
Telescope.settings.collection.attachSchema(Telescope.settings.schema); Telescope.settings.collection.attachSchema(Telescope.settings.schema);
Telescope.subscriptions.preload("settings"); Telescope.subscriptions.preload("settings");

View file

@ -16,7 +16,7 @@ Meteor.methods({
"settings.exportToJSON": function () { "settings.exportToJSON": function () {
if (Users.isAdminById(this.userId)) { if (Users.isAdminById(this.userId)) {
let settings = Telescope.settings.collection.findOne(); let settings = Telescope.settings.collection.findOne();
const schema = Telescope.settings.collection.simpleSchema()._schema; // const schema = Telescope.settings.collection.simpleSchema()._schema;
const publicFields = Telescope.settings.collection.getPublicFields(); const publicFields = Telescope.settings.collection.getPublicFields();
delete settings._id; delete settings._id;
settings.public = {}; settings.public = {};
@ -26,7 +26,7 @@ Meteor.methods({
delete settings[key]; delete settings[key];
} }
}); });
console.log(JSON.stringify(settings, null, 2)); console.log(JSON.stringify(settings, null, 2)); // eslint-disable-line
return settings; return settings;
} }
}, },
@ -36,4 +36,4 @@ Meteor.methods({
Telescope.settings.collection.update(settings._id, {}, {validate: false}); Telescope.settings.collection.update(settings._id, {}, {validate: false});
} }
} }
}) })

View file

@ -1,5 +1,5 @@
import PublicationUtils from 'meteor/utilities:smart-publications'; import PublicationUtils from 'meteor/utilities:smart-publications';
import Users from "meteor/nova:users"; import Users from 'meteor/nova:users';
Users.addField([ Users.addField([
{ {

View file

@ -6,13 +6,13 @@ import Users from 'meteor/nova:users';
* @param {Collection} collection * @param {Collection} collection
* @param {String} itemId * @param {String} itemId
* @param {Object} user * @param {Object} user
* @returns {Object} collectionName, fields: object, item, hasSubscribedItem: boolean * @returns {Object} collectionName, fields: object, item, hasSubscribedItem: boolean
*/ */
const prepareSubscription = (action, collection, itemId, user) => { const prepareSubscription = (action, collection, itemId, user) => {
// get item's collection name // get item's collection name
const collectionName = collection._name.slice(0,1).toUpperCase() + collection._name.slice(1); const collectionName = collection._name.slice(0,1).toUpperCase() + collection._name.slice(1);
// get item data // get item data
const item = collection.findOne(itemId); const item = collection.findOne(itemId);
@ -20,7 +20,7 @@ const prepareSubscription = (action, collection, itemId, user) => {
if (!user || !item) { if (!user || !item) {
return false; return false;
} }
// edge case: Users collection // edge case: Users collection
if (collectionName === 'Users') { if (collectionName === 'Users') {
// someone can't subscribe to themself, abort process // someone can't subscribe to themself, abort process
@ -35,12 +35,12 @@ const prepareSubscription = (action, collection, itemId, user) => {
} }
// assign the right fields depending on the collection // assign the right fields depending on the collection
const fields = { const fields = {
subscribers: collectionName === 'Users' ? 'telescope.subscribers' : 'subscribers', subscribers: collectionName === 'Users' ? 'telescope.subscribers' : 'subscribers',
subscriberCount: collectionName === 'Users' ? 'telescope.subscriberCount' : 'subscriberCount', subscriberCount: collectionName === 'Users' ? 'telescope.subscriberCount' : 'subscriberCount',
}; };
// return true if the item has the subscriber's id in its fields // return true if the item has the subscriber's id in its fields
const hasSubscribedItem = !!_.deep(item, fields.subscribers) && _.deep(item, fields.subscribers) && _.deep(item, fields.subscribers).indexOf(user._id) !== -1; const hasSubscribedItem = !!_.deep(item, fields.subscribers) && _.deep(item, fields.subscribers) && _.deep(item, fields.subscribers).indexOf(user._id) !== -1;
// assign the right update operator and count depending on the action type // assign the right update operator and count depending on the action type
@ -107,7 +107,7 @@ const performSubscriptionAction = (action, collection, itemId, user) => {
itemId: item._id, itemId: item._id,
}; };
// in case of subscription, log also the date // in case of subscription, log also the date
if (action === 'subscribe') { if (action === 'subscribe') {
loggedItem = { loggedItem = {
...loggedItem, ...loggedItem,
@ -129,11 +129,12 @@ const performSubscriptionAction = (action, collection, itemId, user) => {
}; };
/** /**
* @summary Generate methods 'collection.subscribe' & 'collection.unsubscribe' automatically * @summary Generate methods 'collection.subscribe' & 'collection.unsubscribe' automatically
* @params {Array[Collections]} collections * @params {Array[Collections]} collections
*/ */
let subscribeMethodsGenerator;
export default subscribeMethodsGenerator = (collection) => { export default subscribeMethodsGenerator = (collection) => {
// generic method function calling the performSubscriptionAction // generic method function calling the performSubscriptionAction
const genericMethodFunction = (col, action) => { const genericMethodFunction = (col, action) => {
// return the method code // return the method code
@ -151,7 +152,7 @@ const performSubscriptionAction = (action, collection, itemId, user) => {
return performSubscriptionAction(action, col, docId, user); return performSubscriptionAction(action, col, docId, user);
}; };
}; };
const collectionName = collection._name; const collectionName = collection._name;
// return an object of the shape expected by Meteor.methods // return an object of the shape expected by Meteor.methods
return { return {
@ -162,7 +163,7 @@ const performSubscriptionAction = (action, collection, itemId, user) => {
// Finally. Add the methods to the Meteor namespace 🖖 // Finally. Add the methods to the Meteor namespace 🖖
// nova:users is a dependency of this package, it is alreay imported // nova:users is a dependency of this package, it is alreay imported
Meteor.methods(subscribeMethodsGenerator(Users)); Meteor.methods(subscribeMethodsGenerator(Users));
// check if nova:posts exists, if yes, add the methods to Posts // check if nova:posts exists, if yes, add the methods to Posts

View file

@ -1,3 +1,5 @@
import Users from 'meteor/nova:users';
if (typeof Package['nova:posts'] !== "undefined") { if (typeof Package['nova:posts'] !== "undefined") {
import Posts from "meteor/nova:posts"; import Posts from "meteor/nova:posts";

View file

@ -1,3 +1,4 @@
import { Gravatar } from 'meteor/jparker:gravatar';
import Users from './collection.js'; import Users from './collection.js';
// var _ = require('underscore'); // var _ = require('underscore');
@ -191,7 +192,7 @@ Users.avatar = {
return service[0]; return service[0];
}, },
computeUrl: function(prop) { computeUrl: function(prop, user) {
if (typeof prop === 'function') { if (typeof prop === 'function') {
prop = prop.call(user); prop = prop.call(user);
} }
@ -210,9 +211,9 @@ Users.avatar = {
var customProp = user && this.options.customImageProperty; var customProp = user && this.options.customImageProperty;
if (typeof customProp === 'function') { if (typeof customProp === 'function') {
return this.computeUrl(customProp); return this.computeUrl(customProp, user);
} else if (customProp) { } else if (customProp) {
return this.computeUrl(this.getDescendantProp(user, customProp)); return this.computeUrl(this.getDescendantProp(user, customProp), user);
} }
}, },
@ -230,7 +231,7 @@ Users.avatar = {
} }
var emailOrHash = this.getUserEmail(user) || Users.getEmailHash(user); var emailOrHash = this.getUserEmail(user) || Users.getEmailHash(user);
var secure = true; // var secure = true;
var options = { var options = {
// NOTE: Gravatar's default option requires a publicly accessible URL, // NOTE: Gravatar's default option requires a publicly accessible URL,
// so it won't work when your app is running on localhost and you're // so it won't work when your app is running on localhost and you're

View file

@ -1,8 +1,9 @@
import Telescope from 'meteor/nova:lib'; import Telescope from 'meteor/nova:lib';
import Users from './collection.js';
import marked from 'marked';
import Events from "meteor/nova:events"; import Events from "meteor/nova:events";
import NovaEmail from 'meteor/nova:email'; import NovaEmail from 'meteor/nova:email';
import { Gravatar } from 'meteor/jparker:gravatar';
import marked from 'marked';
import Users from './collection.js';
////////////////////////////////////////////////////// //////////////////////////////////////////////////////
// Collection Hooks // // Collection Hooks //
@ -130,7 +131,7 @@ function setupUser (user, options) {
user.telescope.displayName = user.services.linkedin.firstName + " " + user.services.linkedin.lastName; user.telescope.displayName = user.services.linkedin.firstName + " " + user.services.linkedin.lastName;
} else { } else {
user.telescope.displayName = user.username; user.telescope.displayName = user.username;
} }
// create a basic slug from display name and then modify it if this slugs already exists; // create a basic slug from display name and then modify it if this slugs already exists;
const basicSlug = Telescope.utils.slugify(user.telescope.displayName); const basicSlug = Telescope.utils.slugify(user.telescope.displayName);

View file

@ -39,7 +39,7 @@ Users.getUserName = function (user) {
return user.services.twitter.screenName; return user.services.twitter.screenName;
} }
catch (error){ catch (error){
console.log(error); console.log(error); // eslint-disable-line
return null; return null;
} }
}; };

View file

@ -1,6 +1,7 @@
import Telescope from 'meteor/nova:lib'; import Telescope from 'meteor/nova:lib';
import Users from './collection.js'; import Users from './collection.js';
/*
var completeUserProfile = function (userId, modifier, user) { var completeUserProfile = function (userId, modifier, user) {
Users.update(userId, modifier); Users.update(userId, modifier);
@ -10,6 +11,7 @@ var completeUserProfile = function (userId, modifier, user) {
return Users.findOne(userId); return Users.findOne(userId);
}; };
*/
Users.methods = {}; Users.methods = {};
@ -97,7 +99,7 @@ Meteor.methods({
}, },
'users.remove'(userId, options) { 'users.remove'(userId, options) {
// do the user which to delete his account or another user? // do the user which to delete his account or another user?
const actionType = this.userId === userId ? "own" : "all"; const actionType = this.userId === userId ? "own" : "all";

Some files were not shown because too many files have changed in this diff Show more