Merge pull request #1475 from TelescopeJS/devel

v0.27.3
This commit is contained in:
Xavier Cazalot 2016-10-19 09:50:20 +02:00 committed by GitHub
commit 84a090f01a
87 changed files with 420 additions and 444 deletions

1
Dockerfile Normal file
View file

@ -0,0 +1 @@
FROM abernix/meteord:onbuild

View file

@ -1,8 +1,35 @@
## vNEXT
Apollo integration, Webpack build (work in progress on `apollo` & `webpack` branches).
## v0.27.3
- Explain with more details how to deploy with Meteor Up (PR [#1456](https://github.com/TelescopeJS/Telescope/pull/1456), thanks [@asmita005](https://github.com/asmita005)!).
- Add slug to `newPendingPost` notifications, fixes [#1254](https://github.com/TelescopeJS/Telescope/issues/1254).
- Ensure slug unicity on user's slug as done as category's slug (use of `Telescope.utils.getUnusedSlug`), fixes [#1213](https://github.com/TelescopeJS/Telescope/issues/1213).
- Remove some dead code from Telescope Legacy.
- Use of Comment's `getPageUrl` helper in `nova:rss`.
- Prefer `Users` namespace to `Meteor.users` in active packages.
- If you used the property `autoform` on your custom fields, it's now entitled `form`. This was an old reference to [AutoForm](https://github.com/aldeed/meteor-autoform) used by Telescope Legacy. We will give you a console warning if you still use it to advice you to change it.
- Fix errors on `nova:forms`: callbacks from components wrapping a `NovaForm` (ex: `ModalTrigger`) are not fired anymore when it has already been unmounted.
- Fix errors when logging out from the "profile check modal" (`UsersProfileCheck`).
- Prevent errors when creating/editing a category with custom fields (load order of smart methods with extended schema).
- The callback on `nova:subscribe` related to categories has been updated to prevent a user from receiving multiple emails if he/she is subscribed to multiple categories (PR [#1466](https://github.com/TelescopeJS/Telescope/pull/1466), thanks [@chptung](https://github.com/chptung)).
- You can now submit a post/comments (or any `NovaForm` comp) with CMD + Enter / Ctrl + Enter shortcuts (PR [#1472](https://github.com/TelescopeJS/Telescope/pull/1472), thanks [@aszx87410](https://github.com/aszx87410)).
- You can run Telescope Nova inside Docker without deploying (see [this awesome guide](http://spartatek.se/meteor_blog/docker/2016/01/12/running-telescope.html)), fixes [#1477](https://github.com/TelescopeJS/Telescope/issues/1477)
- Add `flex-wrap: wrap;` to posts-categories class for better styling if a user creates a post with too many categories (PR [#1469](https://github.com/TelescopeJS/Telescope/pull/1469), thanks [@chptung](https://github.com/chptung)).
**Changes that may break some parts of your app:**
- Some callbacks have been renamed for consistency purposes: `postsParameters` becomes `posts.parameters`, `profileCompletedAsync` becomes `users.profileCompleted.async`, `profileCompletedChecks` becomes `users.profileCompleted.sync`, `onCreateUserAsync` becomes `users.new.async`, `onCreateUser` becomes `users.new.sync`, `UsersEdit` becomes `users.edit.sync`, `UsersEditAsync` becomes `users.edit.async`.
- The use of `react-bootstrap@0.30.3` is now forced in `package.json`: the latest versions break the dropdown at the moment (see [#1463](https://github.com/TelescopeJS/Telescope/issues/1463)). You should re-run `npm install` if you update from a previous version.
- The `currentUser` props has been removed, the current user is explicitly passed through the context as a matter of consistency across the app. If one of your custom components extending one of `nova:base-components` used `currentUser` as a props, you should update it to use it via the context and add the corresponding contextTypes. See commit [b04cb52](https://github.com/TelescopeJS/Telescope/commit/b04cb5247027fc431f7aa1704ef823ac8ce5fdd1).
## v0.27.2
- Move `updateCurrentValue` function from `propTypes` to `contextTypes` in the datetime picker`DateTime` (`nova:forms`) (#1449).
- Check duplicate links on post's edit (#247).
- Cloudinary images from `nova:cloudinary` are now served over HTTPS (#1224).
- Add year and name to licence (#1117).
- Move `updateCurrentValue` function from `propTypes` to `contextTypes` in the datetime picker`DateTime` (`nova:forms`) ([#1449](https://github.com/TelescopeJS/Telescope/issues/1449)).
- Check duplicate links on post's edit [(#247](https://github.com/TelescopeJS/Telescope/issues/247)).
- Cloudinary images from `nova:cloudinary` are now served over HTTPS ([#1224](https://github.com/TelescopeJS/Telescope/issues/1224)).
- Add year and name to licence ([#1117](https://github.com/TelescopeJS/Telescope/issues/1117)).
- Clean Legacy's issues & PRs. Be ready for the [Hacktoberfest](https://hacktoberfest.digitalocean.com/)!! 🍻
## v0.27.1
@ -13,16 +40,16 @@
- Added support for a custom CSS class for `SubscribeTo` component.
- No more global variable in `nova:api` (the last one? \o/).
- Fix a version problem with `fourseven:scss`, now running on 3.9.0.
- Remove unnecessary NPM dependency on `load-script` (thanks @MHerszak!).
- You can now run Nova in Brazilian Portuguese by [adding this package](https://github.com/lukasag/nova-i18n-pt-br) (thanks @lukasag!).
- Added support for a `defaultValue` property in `nova:forms`. You can define it in your custom fields, it will be added if no value nor prefilled value is defined (thanks @beeva-franciscocalle!).
- Fixed edge bug when users don't have an `username`, use `displayName` instead (thanks @jeffreywyman!).
- Remove unnecessary NPM dependency on `load-script` (thanks [@MHerszak](https://github.com/mherszak)!).
- You can now run Nova in Brazilian Portuguese by [adding this package](https://github.com/lukasag/nova-i18n-pt-br) (thanks [@lukasag](https://github.com/lukasag)!).
- Added support for a `defaultValue` property in `nova:forms`. You can define it in your custom fields, it will be added if no value nor prefilled value is defined (thanks [@beeva-franciscocalle](https://github.com/beeva-franciscocalle)!).
- Fixed edge bug when users don't have an `username`, use `displayName` instead (thanks [@jeffreywyman](https://github.com/jeffreywyman)!).
## v0.27.0
- Remove Telescope global variable.
- Update to Meteor 1.4.
- A user can now subscribe to any collection with `nova:subscribe` package ([docs](https://github.com/TelescopeJS/Telescope/tree/master/packages/nova-subscribe)) and a reusable `SubscribeTo` component (thanks @schabluk!).
- A user can now subscribe to any collection with `nova:subscribe` package ([docs](https://github.com/TelescopeJS/Telescope/tree/master/packages/nova-subscribe)) and a reusable `SubscribeTo` component (thanks [@schabluk](https://github.com/schabluk)!).
*The rest of the modifications are not yet documented, you can [browse the commits history from there](https://github.com/TelescopeJS/Telescope/commits/2b34713c0b6dbf094668f8a87d007443a1e2c580).*
@ -57,7 +84,7 @@
- You can now see a post's ID and stats in the post edit form if you're an admin.
- Fixed bug (I hope?) where daily view would become messed up when client and server were on different timezones.
- Now showing a user's posts on their profile page.
- Added soft delete for comments (thanks @justintime4tea!).
- Added soft delete for comments (thanks [@justintime4tea](https://github.com/justintime4tea)!).
- Fixed posts notifications bugs.
- Got rid of a lot of Meteor packages in favor of NPM equivalents.

107
README.md
View file

@ -15,8 +15,8 @@ Note that both versions use the same data format, so you can go back and forth b
- [Getting Started](#getting-started)
- [Updating](#updating)
- [Resources](#resources)
- [Deployment](#deployment)
- [Settings](#settings)
- [Deployment](#deployment)
- [Categories](#categories)
- [Social Login](#social-login)
- [Packages](#packages)
@ -36,11 +36,14 @@ Note that both versions use the same data format, so you can go back and forth b
- [Groups & Permissions](#groups--permissions)
- [Internationalization](#internationalization)
- [Cheatsheet](#cheatsheet)
- [Third-Party packages](#third-party-packages)
## Getting Started
### First Steps
Install the latest version of Node and NPM. We recommend the usage of [NVM](http://nvm.sh).
[Install Meteor](https://www.meteor.com/install):
```sh
@ -111,10 +114,6 @@ For local development, an easy way to do that is to simply copy the `.meteor/loc
The best ways to get support are [Telescope Meta](http://meta.telescopeapp.org) and the [Telescope Slack Chatroom](http://slack.telescopeapp.org).
## Deployment
The recommended way to deploy Nova is by using [Mup](https://github.com/kadirahq/meteor-up/), at least v1.0.3.
## Settings
Settings can be configured in your `settings.json` file. For legacy compatibility reasons, settings can also be specified in your database, but note that settings specified in `settings.json` take priority over those stored in the database.
@ -124,7 +123,78 @@ Settings can be public (meaning they will be published to the client) or private
To use your `settings.json` file:
- Development: `meteor --settings settings.json`
- Production: specify the path to `settings.json` in `mup.json`
- Production: specify the path to `settings.json` in the tool you use to deploy (i.e. `mup deploy --settings settings.json`, see below)
## Deployment
The recommended way to deploy Nova is by using [Mup](https://github.com/kadirahq/meteor-up/), at least v1.0.3.
#### Configuration
You should have a Linux server online, for instance [a Digital Ocean droplet running with Ubuntu](https://www.digitalocean.com).
Install globally the latest `kadirahq/meteor-up`.
```
npm install -g mup
```
Create Meteor Up configuration files in your project directory with `mup init`. In the example below, the configuration files are created in a `.deploy` directory at the root of your app.
```
cd my-app-folder
mkdir .deploy
cd .deploy
mup init
```
This will create two files :
```
mup.js - Meteor Up configuration file
settings.json - Settings for Meteor's settings API
```
Then, replace the content of the newly created `settings.json` with your own settings (you can use the content of `sample_settings.json` as a starter).
Fill `mup.js` with your credentials and optional settings (check the [Mup repo](https://github.com/kadirahq/meteor-up) for additional docs).
**Note:** the `ROOT_URL` field should be the absolute url of your deploy ; and you need to explicitly point out to use `abernix/meteord:base` docker image with a `docker` field within the `meteor` object.
```
...
meteor: {
...
path: '../' // relative path of the app considering your mup config files
env: {
ROOT_URL: 'http://nova-app.com', // absolute url of your deploy
...
},
...
docker: {
image:'abernix/meteord:base' // docker image working with meteor 1.4 & node 4
},
...
},
...
```
You can take inspiration (or copy/paste) on this [`mup.js` example](https://gist.github.com/xavcz/6ddc2bb6f67fe0936c8328ab3314641d).
#### Setup your server
From this folder, you can now setup Docker & Mongo your server with:
```
mup setup
```
#### Deploy your app to your server
Still in the same folder, to deploy your app with your settings file:
```
mup --settings settings.json
```
## Categories
@ -304,7 +374,26 @@ If a component deals with a collection (`Posts`, `Comments`, etc.) its name shou
For example: `PostsShare`.
The outermost HTML element within the component will have a class of the same name, but with a dash instead: `posts-share`. If possible, classes for all other elements within the component will start with the component's class: `posts-share-button`, `posts-share-divider`, etc.
The outermost HTML element within the component will have a class of the same name, but with a dash instead: `posts-share`. If possible, classes for all other elements within the component will start with the component's class: `posts-share-button`, `posts-share-divider`, etc.
### Get current user
The current user is given to the components via the React context. You can access it via `this.context.currentUser` (class) or `context.currentUser` (stateless-component).
The component needs to define `currentUser` in its `contextTypes`. If `contextTypes` is not defined, then `context` will be an empty object and you won't be able to access to the current user.
Example :
```js
const CustomHeader = (props, context) => {
// if a user is connected, show its username; else say hello
return context.currentUser ? <div>Hey ${context.currentUser.username}!</div> : <div>Hello!</div>
};
// if you don't define `contextTypes` for `CustomHeader`, then the `context` argument will be an empty object
CustomHeader.contextTypes = {
currentUser: React.PropTypes.object
};
```
## Customizing Emails
@ -458,7 +547,7 @@ function addSearchQueryParameter (parameters, terms) {
}
return parameters;
}
Telescope.callbacks.add("postsParameters", addSearchQueryParameter);
Telescope.callbacks.add("posts.parameters", addSearchQueryParameter);
```
The callback takes two arguments: the current MongoDB `parameters` (an object with a `selector` and `options` properties), and the `terms` extracted from the URL.
@ -647,7 +736,7 @@ If you create a new internationalization package, let us know so we can add it h
You can access a dynamically generated cheatsheet of Nova's main functions at [http://localhost:3000/cheatsheet](/cheatsheet) (replace with your own development URL).
## Third-Party Plugins
## Third-Party Packages
- [Post By Feed](https://github.com/xavcz/nova-post-by-feed): register RSS feeds that will be fetched every 30 minutes to create new posts automatically.
- [Post To Slack](https://github.com/xavcz/nova-slack): A package that automatically sends your posts as messages to any connected Slack Team.

View file

@ -23,7 +23,7 @@
"moment": "^2.13.0",
"react": "^15.3.2",
"react-addons-pure-render-mixin": "^15.3.2",
"react-bootstrap": "^0.30.3",
"react-bootstrap": "0.30.3",
"react-bootstrap-datetimepicker": "0.0.22",
"react-cookie": "^0.4.6",
"react-datetime": "^2.3.2",

View file

@ -27,7 +27,7 @@ class CustomPostsItem extends Telescope.components.PostsItem {
<div className={postClass}>
<div className="posts-item-vote">
<Telescope.components.Vote post={post} currentUser={this.context.currentUser}/>
<Telescope.components.Vote post={post} />
</div>
{post.thumbnailUrl ? <Telescope.components.PostsThumbnail post={post}/> : null}
@ -49,7 +49,7 @@ class CustomPostsItem extends Telescope.components.PostsItem {
<FormattedMessage id="comments.count" values={{count: post.commentCount}}/>
</Link>
</div>
{(this.context.currentUser && this.context.currentUser.isAdmin) ?<Telescope.components.PostsStats post={post} />:null}
{this.context.currentUser && this.context.currentUser.isAdmin ? <Telescope.components.PostsStats post={post} /> : null}
{this.renderActions()}
</div>

View file

@ -21,7 +21,7 @@ Posts.addField(
optional: true, // this field is not required
insertableIf: canInsert,
editableIf: canEdit,
autoform: {
form: {
options: function () { // options for the select form control
return [
{value: "white", label: "White"},

View file

@ -3,8 +3,6 @@ import React, { PropTypes, Component } from 'react';
import { FormattedMessage } from 'react-intl';
import NovaForm from "meteor/nova:forms";
import { DocumentContainer } from "meteor/utilities:react-list-container";
//import { Messages } from "meteor/nova:core";
//import Actions from "../actions.js";
import Categories from "meteor/nova:categories";
class CategoriesEditForm extends Component{
@ -35,7 +33,6 @@ class CategoriesEditForm extends Component{
<NovaForm
document={this.props.category}
collection={Categories}
currentUser={this.context.currentUser}
methodName="categories.edit"
successCallback={(category)=>{
this.context.messages.flash("Category edited.", "success");

View file

@ -9,7 +9,6 @@ const CategoriesNewForm = (props, context) => {
<div className="categories-new-form">
<NovaForm
collection={Categories}
currentUser={context.currentUser}
methodName="categories.new"
successCallback={(category)=>{
context.messages.flash("Category created.", "success");

View file

@ -10,7 +10,6 @@ class CommentsEdit extends Component {
<NovaForm
collection={Comments}
document={this.props.comment}
currentUser={this.context.currentUser}
methodName="comments.edit"
successCallback={this.props.successCallback}
layout="elementOnly"

View file

@ -123,8 +123,7 @@ class CommentsItem extends Component{
CommentsItem.propTypes = {
comment: React.PropTypes.object.isRequired, // the current comment
//currentUser: React.PropTypes.object, // the current user
}
};
CommentsItem.contextTypes = {
currentUser: React.PropTypes.object,
@ -132,6 +131,6 @@ CommentsItem.contextTypes = {
messages: React.PropTypes.object,
events: React.PropTypes.object,
intl: intlShape
}
};
module.exports = CommentsItem;

View file

@ -2,12 +2,12 @@ import Telescope from 'meteor/nova:lib';
import React from 'react';
import {injectIntl, FormattedMessage} from 'react-intl';
const CommentsList = ({results, currentUser, hasMore, ready, count, totalCount, loadMore}) => {
const CommentsList = ({results, hasMore, ready, count, totalCount, loadMore}) => {
if (!!results.length) {
return (
<div className="comments-list">
{results.map(comment => <Telescope.components.CommentsNode comment={comment} key={comment._id} currentUser={currentUser}/>)}
{results.map(comment => <Telescope.components.CommentsNode comment={comment} key={comment._id} />)}
{hasMore ? (ready ? <Telescope.components.CommentsLoadMore loadMore={loadMore} count={count} totalCount={totalCount} /> : <Telescope.components.Loading/>) : null}
</div>
)

View file

@ -20,7 +20,6 @@ class CommentsNew extends Component {
<div className="comments-new-form">
<NovaForm
collection={Comments}
currentUser={this.context.currentUser}
methodName="comments.new"
prefilledProps={prefilledProps}
successCallback={this.props.successCallback}

View file

@ -6,14 +6,14 @@ class CommentsNode extends Component {
renderComment(comment) {
return (
<Telescope.components.CommentsItem comment={comment} key={comment._id} currentUser={this.props.currentUser}/>
<Telescope.components.CommentsItem comment={comment} key={comment._id} />
)
}
renderChildren(children) {
return (
<div className="comments-children">
{children.map(comment => <CommentsNode comment={comment} key={comment._id} currentUser={this.props.currentUser}/>)}
{children.map(comment => <CommentsNode comment={comment} key={comment._id} />)}
</div>
)
}
@ -35,7 +35,10 @@ class CommentsNode extends Component {
CommentsNode.propTypes = {
comment: React.PropTypes.object.isRequired, // the current comment
};
CommentsNode.contextTypes = {
currentUser: React.PropTypes.object, // the current user
}
};
module.exports = CommentsNode;

View file

@ -30,7 +30,7 @@ class App extends Component {
<IntlProvider locale={this.getLocale()} messages={Telescope.strings[this.getLocale()]}>
{
this.props.ready ?
<Telescope.components.Layout currentUser={this.props.currentUser}>{this.props.children}</Telescope.components.Layout>
<Telescope.components.Layout>{this.props.children}</Telescope.components.Layout>
: <Telescope.components.AppLoading />
}
</IntlProvider>

View file

@ -2,7 +2,7 @@ import Telescope from 'meteor/nova:lib';
import React from 'react';
//import { Messages } from "meteor/nova:core";
const Header = ({currentUser}) => {
const Header = (props, {currentUser}) => {
const logoUrl = Telescope.settings.get("logoUrl");
const siteTitle = Telescope.settings.get("title", "Nova");
@ -21,7 +21,7 @@ const Header = ({currentUser}) => {
<div className="nav">
<div className="nav-user">
{currentUser ? <Telescope.components.UsersMenu user={currentUser}/> : <Telescope.components.UsersAccountMenu/>}
{currentUser ? <Telescope.components.UsersMenu/> : <Telescope.components.UsersAccountMenu/>}
</div>
<div className="nav-new-post">
@ -37,4 +37,8 @@ const Header = ({currentUser}) => {
Header.displayName = "Header";
Header.contextTypes = {
currentUser: React.PropTypes.object,
};
module.exports = Header;

View file

@ -3,10 +3,8 @@ import React, { PropTypes, Component } from 'react';
import { FormattedMessage, intlShape } from 'react-intl';
import Formsy from 'formsy-react';
import { Input } from 'formsy-react-components';
//import Actions from "../actions.js";
import { Button } from 'react-bootstrap';
import Cookie from 'react-cookie';
//import { Messages } from "meteor/nova:core";
import Users from 'meteor/nova:users';
class Newsletter extends Component {

View file

@ -1,7 +1,5 @@
import Telescope from 'meteor/nova:lib';
import React, { PropTypes, Component } from 'react';
//import Actions from "../actions.js";
//import { Messages } from "meteor/nova:core";
import classNames from 'classnames';
import Users from 'meteor/nova:users';
@ -61,8 +59,7 @@ class Vote extends Component {
Vote.propTypes = {
post: React.PropTypes.object.isRequired, // the current post
// currentUser: React.PropTypes.object, // the current user
}
};
Vote.contextTypes = {
currentUser: React.PropTypes.object,

View file

@ -5,7 +5,7 @@ import { ListContainer } from "meteor/utilities:react-list-container";
import { ModalTrigger } from "meteor/nova:core";
import Comments from "meteor/nova:comments";
const PostsCommentsThread = ({document, currentUser}) => {
const PostsCommentsThread = ({document}, {currentUser}) => {
const post = document;
@ -39,5 +39,9 @@ const PostsCommentsThread = ({document, currentUser}) => {
PostsCommentsThread.displayName = "PostsCommentsThread";
PostsCommentsThread.contextTypes = {
currentUser: React.PropTypes.object
};
module.exports = PostsCommentsThread;
export default PostsCommentsThread;

View file

@ -47,7 +47,7 @@ class PostsItem extends Component {
<div className={postClass}>
<div className="posts-item-vote">
<Telescope.components.Vote post={post} currentUser={this.context.currentUser}/>
<Telescope.components.Vote post={post} />
</div>
{post.thumbnailUrl ? <Telescope.components.PostsThumbnail post={post}/> : null}
@ -69,7 +69,7 @@ class PostsItem extends Component {
<FormattedMessage id="comments.count" values={{count: post.commentCount}}/>
</Link>
</div>
{(this.context.currentUser && this.context.currentUser.isAdmin) ?<Telescope.components.PostsStats post={post} />:null}
{this.context.currentUser && this.context.currentUser.isAdmin ? <Telescope.components.PostsStats post={post} /> : null}
{this.renderActions()}
</div>

View file

@ -3,18 +3,12 @@ import React from 'react';
const PostsList = ({results, currentUser, hasMore, ready, count, totalCount, loadMore, showHeader = true}) => {
// console.log(results);
// console.log(ready);
// console.log(hasMore);
// console.log(totalCount);
// console.log(count);
if (!!results.length) {
return (
<div className="posts-list">
{showHeader ? <Telescope.components.PostsListHeader /> : null}
<div className="posts-list-content">
{results.map(post => <Telescope.components.PostsItem post={post} currentUser={currentUser} key={post._id}/>)}
{results.map(post => <Telescope.components.PostsItem post={post} key={post._id}/>)}
</div>
{hasMore ? (ready ? <Telescope.components.PostsLoadMore loadMore={loadMore} count={count} totalCount={totalCount} /> : <Telescope.components.PostsLoading/>) : <Telescope.components.PostsNoMore/>}
</div>

View file

@ -18,7 +18,6 @@ const PostsNewForm = (props, context) => {
<div className="posts-new-form">
<NovaForm
collection={Posts}
currentUser={context.currentUser}
methodName="posts.new"
successCallback={(post)=>{
context.messages.flash(context.intl.formatMessage({id: "posts.created_message"}), "success");

View file

@ -18,7 +18,7 @@ const PostsPage = ({document, currentUser}) => {
{/*<SocialShare url={ Posts.getLink(post) } title={ post.title }/>*/}
<Telescope.components.PostsCommentsThread document={post} currentUser={currentUser}/>
<Telescope.components.PostsCommentsThread document={post} />
</div>
)

View file

@ -7,14 +7,18 @@ const UsersAccount = (props, context) => {
const terms = props.params.slug ? {"telescope.slug": props.params.slug} : context.currentUser ? {_id: context.currentUser._id } : undefined;
return (
<Telescope.components.CanDo action="users.edit.own" displayNoPermissionMessage={true}>
<DocumentContainer
collection={Users}
publication="users.single"
selector={terms}
terms={terms}
documentPropName="user"
component={Telescope.components.UsersEdit}
/>
{
// do not try to render DocumentContainer without required prop 'terms'
typeof terms === "undefined" ? null
: <DocumentContainer
collection={Users}
publication="users.single"
selector={terms}
terms={terms}
documentPropName="user"
component={Telescope.components.UsersEdit}
/>
}
</Telescope.components.CanDo>
)
};

View file

@ -22,8 +22,7 @@ const UsersEdit = (props, context) => {
<div className="page users-edit-form">
<h2 className="page-title users-edit-form-title"><FormattedMessage id="users.edit_account"/></h2>
<NovaForm
currentUser={currentUser}
collection={Meteor.users}
collection={Users}
document={user}
methodName="users.edit"
successCallback={(user)=>{
@ -41,6 +40,7 @@ UsersEdit.propTypes = {
};
UsersEdit.contextTypes = {
currentUser: React.PropTypes.object,
messages: React.PropTypes.object,
intl: intlShape
};

View file

@ -3,8 +3,7 @@ import React, { PropTypes, Component } from 'react';
import { FormattedMessage } from 'react-intl';
import { Meteor } from 'meteor/meteor';
import { Accounts } from 'meteor/std:accounts-ui';
import { Modal, Dropdown, MenuItem } from 'react-bootstrap';
import { ContextPasser } from "meteor/nova:core";
import { Dropdown, MenuItem } from 'react-bootstrap';
import { LinkContainer } from 'react-router-bootstrap';
import Users from 'meteor/nova:users';
@ -12,20 +11,20 @@ class UsersMenu extends Component {
render() {
const user = this.props.user;
const {currentUser} = this.context;
return (
<div className="users-menu">
<Dropdown id="user-dropdown">
<Dropdown.Toggle>
<Telescope.components.UsersAvatar size="small" user={user} link={false} />
<div>{Users.getDisplayName(user)}</div>
<Telescope.components.UsersAvatar size="small" user={currentUser} link={false} />
<div>{Users.getDisplayName(currentUser)}</div>
</Dropdown.Toggle>
<Dropdown.Menu>
<LinkContainer to={`/users/${user.telescope.slug}`} /*to={{name: "users.single", params: {slug: user.telescope.slug}}}*/>
<LinkContainer to={`/users/${currentUser.telescope.slug}`}>
<MenuItem className="dropdown-item" eventKey="1"><FormattedMessage id="users.profile"/></MenuItem>
</LinkContainer>
<LinkContainer to={`/account`} /*to={{name: "account"}}*/>
<LinkContainer to={`/account`}>
<MenuItem className="dropdown-item" eventKey="2"><FormattedMessage id="users.edit_account"/></MenuItem>
</LinkContainer>
<MenuItem className="dropdown-item" eventKey="4" onClick={() => Meteor.logout(Accounts.ui._options.onSignedOutHook())}><FormattedMessage id="users.log_out"/></MenuItem>
@ -37,11 +36,8 @@ class UsersMenu extends Component {
}
UsersMenu.propTypes = {
user: React.PropTypes.object
}
UsersMenu.contextTypes = {
currentUser: React.PropTypes.object,
messages: React.PropTypes.object
}

View file

@ -5,14 +5,15 @@ import { Modal } from 'react-bootstrap';
import NovaForm from "meteor/nova:forms";
import { withRouter } from 'react-router'
import Users from 'meteor/nova:users';
import { Accounts } from 'meteor/std:accounts-ui';
const UsersProfileCheckModal = ({currentUser, show, router}) => {
const UsersProfileCheckModal = ({show, router}, {currentUser}) => {
// return fields that are required by the schema but haven't been filled out yet
const schema = Users.simpleSchema()._schema;
const requiredFields = _.filter(_.keys(schema), function (fieldName) {
const requiredFields = _.filter(_.keys(schema), (fieldName) => {
var field = schema[fieldName];
return !!field.required && !Telescope.getNestedProperty(Meteor.user(), fieldName);
return !!field.required && !Telescope.getNestedProperty(currentUser, fieldName);
});
return (
@ -22,31 +23,31 @@ const UsersProfileCheckModal = ({currentUser, show, router}) => {
</Modal.Header>
<Modal.Body>
<NovaForm
currentUser={ currentUser }
collection={ Meteor.users }
collection={ Users }
document={ currentUser }
methodName="users.edit"
successCallback={ (user) => Telescope.callbacks.runAsync("profileCompletedAsync", user) }
successCallback={ (user) => Telescope.callbacks.runAsync("users.profileCompleted.async", user) }
fields={ requiredFields }
/>
</Modal.Body>
<Modal.Footer>
<FormattedMessage id="app.or"/> <a className="complete-profile-logout" onClick={ () => Meteor.logout(() => router.push({pathname: '/'})) }><FormattedMessage id="users.log_out"/></a>
<FormattedMessage id="app.or"/> <a className="complete-profile-logout" onClick={ () => Meteor.logout(Accounts.ui._options.onSignedOutHook()) }><FormattedMessage id="users.log_out"/></a>
</Modal.Footer>
</Modal>
)
};
class UsersProfileCheck extends Component {
render() {
const currentUser = this.context.currentUser;
return currentUser ? <UsersProfileCheckModal currentUser={currentUser} show={!Users.hasCompletedProfile(currentUser)}/> : null
}
}
const UsersProfileCheck = (props, {currentUser}) => {
return currentUser ? <UsersProfileCheckModal show={!Users.hasCompletedProfile(currentUser)}/> : null;
};
UsersProfileCheck.contextTypes = {
currentUser: React.PropTypes.object
}
};
UsersProfileCheckModal.contextTypes = {
currentUser: React.PropTypes.object
};
UsersProfileCheck.displayName = "UsersProfileCheck";

View file

@ -156,6 +156,7 @@
}
.posts-categories{
flex-wrap: wrap;
@include medium-large{
margin-left: 5px;
}
@ -294,4 +295,4 @@
@include flex-center;
justify-content: space-between;
margin-bottom: $vmargin * 2;
}
}

View file

@ -17,7 +17,7 @@ Posts.addField(
optional: true,
insertableIf: canInsert,
editableIf: canEdit,
autoform: {
form: {
noselect: true,
type: "bootstrap-category",
order: 50,

View file

@ -1,3 +1,4 @@
import { Meteor } from 'meteor/meteor';
import Posts from "meteor/nova:posts";
import Users from 'meteor/nova:users';
import Categories from "./collection.js";
@ -28,7 +29,10 @@ Meteor.methods({
}
});
Categories.smartMethods({
createName: "categories.new",
editName: "categories.edit"
// assign smart methods on startup so the method code generated takes care of categories' custom fields (extended schema) -> prevent bug on create/edit categories with custom fields
Meteor.startup(() => {
Categories.smartMethods({
createName: "categories.new",
editName: "categories.edit"
});
});

View file

@ -1,7 +1,7 @@
import Telescope from 'meteor/nova:lib';
import Categories from "./collection.js";
// Category Parameter
// Category Posts Parameters
// Add a "categories" property to terms which can be used to filter *all* existing Posts views.
function addCategoryParameter (parameters, terms) {
@ -32,4 +32,4 @@ function addCategoryParameter (parameters, terms) {
}
return parameters;
}
Telescope.callbacks.add("postsParameters", addCategoryParameter);
Telescope.callbacks.add("posts.parameters", addCategoryParameter);

View file

@ -19,7 +19,7 @@ Categories.schema = new SimpleSchema({
insertableIf: canInsert,
editableIf: canEdit,
publish: true,
autoform: {
form: {
rows: 3
}
},
@ -50,7 +50,7 @@ Categories.schema = new SimpleSchema({
insertableIf: canInsert,
editableIf: canEdit,
publish: true,
autoform: {
form: {
options: function () {
var categories = Categories.find().map(function (category) {
return {
@ -77,7 +77,7 @@ Telescope.settings.collection.addField([
fieldSchema: {
type: String,
optional: true,
autoform: {
form: {
group: 'categories',
instructions: 'Let users filter by one or multiple categories at a time.',
options: function () {
@ -94,7 +94,7 @@ Telescope.settings.collection.addField([
fieldSchema: {
type: Boolean,
optional: true,
autoform: {
form: {
group: 'categories',
instructions: 'Hide empty categories in navigation'
}

View file

@ -1,5 +0,0 @@
{
"translation_function_name": "__",
"helper_name": "_",
"namespace": "project"
}

View file

@ -26,7 +26,7 @@ if (typeof Settings !== "undefined") {
fieldSchema: {
type: String,
optional: true,
autoform: {
form: {
group: 'cloudinary'
}
}
@ -37,7 +37,7 @@ if (typeof Settings !== "undefined") {
type: String,
optional: true,
private: true,
autoform: {
form: {
group: 'cloudinary',
class: 'private-field'
}
@ -49,7 +49,7 @@ if (typeof Settings !== "undefined") {
type: String,
optional: true,
private: true,
autoform: {
form: {
group: 'cloudinary',
class: 'private-field'
}

View file

@ -1,5 +0,0 @@
{
"translation_function_name": "__",
"helper_name": "_",
"namespace": "project"
}

View file

@ -80,8 +80,8 @@ Telescope.callbacks.add("comments.new.method", CommentsNewUserCheck);
function CommentsNewRateLimit (comment, user) {
if (!Users.isAdmin(user)) {
var timeSinceLastComment = Users.timeSinceLast(user, Comments),
commentInterval = Math.abs(parseInt(Telescope.settings.get('commentInterval',15)));
const timeSinceLastComment = Users.timeSinceLast(user, Comments);
const commentInterval = Math.abs(parseInt(Telescope.settings.get('commentInterval',15)));
// check that user waits more than 15 seconds between comments
if((timeSinceLastComment < commentInterval)) {
throw new Meteor.Error("CommentsNewRateLimit", "comments.rate_limit_error", commentInterval-timeSinceLastComment);
@ -156,7 +156,7 @@ function CommentsNewOperations (comment) {
var userId = comment.userId;
// increment comment count
Meteor.users.update({_id: userId}, {
Users.update({_id: userId}, {
$inc: {'telescope.commentCount': 1}
});
@ -175,7 +175,7 @@ function CommentsNewUpvoteOwnComment (comment) {
if (typeof Telescope.operateOnItem !== "undefined") {
var commentAuthor = Meteor.users.findOne(comment.userId);
var commentAuthor = Users.findOne(comment.userId);
// upvote comment
Telescope.operateOnItem(Comments, comment._id, commentAuthor, "upvote");

View file

@ -1,5 +1,7 @@
import Telescope from 'meteor/nova:lib';
import Comments from './collection.js';
import Posts from 'meteor/nova:posts';
import Users from 'meteor/nova:users';
Comments.helpers({getCollection: () => Comments});
Comments.helpers({getCollectionName: () => "comments"});
@ -16,7 +18,7 @@ Comments.getPageUrl = function(comment, isAbsolute = false){
const post = Posts.findOne(comment.postId);
return `${Posts.getPageUrl(post, isAbsolute)}/#${comment._id}`;
};
Comments.helpers({getPageUrl: function () {return Comments.getPageUrl(this);}});
Comments.helpers({getPageUrl: function (isAbsolute) {return Comments.getPageUrl(this, isAbsolute);}});
///////////////////
// Other Helpers //
@ -27,7 +29,7 @@ Comments.helpers({getPageUrl: function () {return Comments.getPageUrl(this);}});
* @param {Object} comment
*/
Comments.getAuthorName = function (comment) {
var user = Meteor.users.findOne(comment.userId);
var user = Users.findOne(comment.userId);
return user ? user.getDisplayName() : comment.author;
};
Comments.helpers({getAuthorName: function () {return Comments.getAuthorName(this);}});

View file

@ -2,6 +2,7 @@ import Telescope from 'meteor/nova:lib';
import Comments from './collection.js';
import Posts from "meteor/nova:posts";
import Users from 'meteor/nova:users';
import { Messages } from 'meteor/nova:core';
Comments.methods = {};
// ------------------------------------------------------------------------------------------- //
@ -10,7 +11,7 @@ Comments.methods = {};
Comments.methods.new = function (comment) {
const currentUser = Meteor.users.findOne(comment.userId);
const currentUser = Users.findOne(comment.userId);
comment = Telescope.callbacks.run("comments.new.sync", comment, currentUser);
@ -97,7 +98,6 @@ Meteor.methods({
var user = Meteor.user();
if(Users.canEdit(user, comment)){
// decrement post comment count and remove user ID from post
Posts.update(comment.postId, {
$inc: {commentCount: -1},
@ -105,7 +105,7 @@ Meteor.methods({
});
// decrement user comment count and remove comment ID from user
Meteor.users.update({_id: comment.userId}, {
Users.update({_id: comment.userId}, {
$inc: {'telescope.commentCount': -1}
});
@ -117,11 +117,8 @@ Meteor.methods({
htmlBody: 'Deleted',
isDeleted: true
}});
}else{
} else {
Messages.flash("You don't have permission to delete this comment.", "error");
}
},

View file

@ -1,9 +1,10 @@
import Comments from './collection.js';
import Posts from "meteor/nova:posts";
import Users from 'meteor/nova:users';
Comments.getNotificationProperties = function (data) {
const comment = data.comment;
const commentAuthor = Meteor.users.findOne(comment.userId);
const commentAuthor = Users.findOne(comment.userId);
const post = Posts.findOne(comment.postId);
const properties = {
profileUrl: commentAuthor && commentAuthor.getProfileUrl(true),

View file

@ -109,7 +109,7 @@ Comments.schema = new SimpleSchema({
publish: true,
// regEx: SimpleSchema.RegEx.Id,
max: 500,
autoform: {
form: {
omit: true // never show this
}
},
@ -122,7 +122,7 @@ Comments.schema = new SimpleSchema({
publish: true,
join: {
joinAs: "user",
collection: () => Meteor.users
collection: () => Users
}
},
/**
@ -158,7 +158,7 @@ if (typeof Telescope.notifications !== "undefined") {
fieldSchema: {
type: Boolean,
optional: true,
autoform: {
form: {
omit: true
}
}

View file

@ -10,7 +10,7 @@ Comments._ensureIndex({parentCommentId: 1});
*/
Meteor.publish('comments.list', function (terms) {
const currentUser = this.userId && Meteor.users.findOne(this.userId);
const currentUser = this.userId && Users.findOne(this.userId);
terms.currentUserId = this.userId; // add currentUserId to terms
({selector, options} = Comments.parameters.get(terms));
@ -22,7 +22,7 @@ Meteor.publish('comments.list', function (terms) {
const comments = Comments.find(selector, options);
const posts = Posts.find({_id: {$in: _.pluck(comments.fetch(), 'postId')}}, {fields: Posts.publishedFields.list});
const users = Meteor.users.find({_id: {$in: _.pluck(comments.fetch(), 'userId')}}, {fields: Users.publishedFields.list});
const users = Users.find({_id: {$in: _.pluck(comments.fetch(), 'userId')}}, {fields: Users.publishedFields.list});
return Users.canDo(currentUser, "comments.view.all") ? [comments, posts, users] : [];
@ -91,7 +91,7 @@ Meteor.publish('comments.list', function (terms) {
// userIds.push(post.userId);
// }
// return Meteor.users.find({_id: {$in: userIds}}, {fields: Users.pubsub.publicProperties});
// return Users.find({_id: {$in: userIds}}, {fields: Users.pubsub.publicProperties});
// }

View file

@ -1,9 +1,5 @@
import Telescope from 'meteor/nova:lib';
var modifyKarma = function (userId, karma) {
Meteor.users.update({_id: userId}, {$inc: {"telescope.karma": karma}});
};
/**
* @summary Update an item's (post or comment) score
* @param {object} item - The item being operated on
@ -51,7 +47,7 @@ function updateUser (item, user, collection, operation) {
break;
}
Meteor.users.update({_id: user._id}, update);
Users.update({_id: user._id}, update);
}
Telescope.callbacks.add("upvote.async", updateUser);
@ -73,7 +69,7 @@ function updateKarma (item, user, collection, operation) {
// only update karma is the operation isn't done by the item's author
if (item.userId !== user._id) {
Meteor.users.update({_id: item.userId}, {$inc: {"telescope.karma": karmaAmount}});
Users.update({_id: item.userId}, {$inc: {"telescope.karma": karmaAmount}});
}
}

View file

@ -8,7 +8,7 @@ const renderSetting = (field, key) => {
<td>{field.type && field.type.name}</td>
<td>{field.private ? <span className="private">private</span> : Telescope.settings.get(key)}</td>
<td>{field.defaultValue && field.defaultValue.toString()}</td>
<td>{field.autoform && field.autoform.instructions}</td>
<td>{field.form && field.form.instructions}</td>
</tr>
)
}

View file

@ -4,6 +4,7 @@ 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';
//////////////////////////////////////////////////////
// Collection & Schema //
@ -45,7 +46,7 @@ const schema = new SimpleSchema({
type: String,
publish: true,
join: {
collection: () => Meteor.users,
collection: () => Users,
joinAs: "user",
fields: ["_id", "username"]
}

View file

@ -1,4 +0,0 @@
{
"translation_function_name": "__",
"helper_name": "_"
}

View file

@ -71,7 +71,7 @@ if (typeof Telescope.settings.collection !== "undefined") {
type: String,
optional: true,
private: true,
autoform: {
form: {
group: 'embedly',
class: 'private-field'
}
@ -82,7 +82,7 @@ if (typeof Telescope.settings.collection !== "undefined") {
fieldSchema: {
type: Number,
optional: true,
autoform: {
form: {
group: 'embedly'
}
}
@ -92,7 +92,7 @@ if (typeof Telescope.settings.collection !== "undefined") {
fieldSchema: {
type: Number,
optional: true,
autoform: {
form: {
group: 'embedly'
}
}

View file

@ -1,5 +0,0 @@
{
"translation_function_name": "__",
"helper_name": "_",
"namespace": "project"
}

View file

@ -81,7 +81,6 @@ New document form:
```jsx
<NovaForm
collection={Posts}
currentUser={currentUser},
methodName="posts.new"
/>
```
@ -91,9 +90,8 @@ Edit document form:
```jsx
<NovaForm
collection={Posts}
currentUser={currentUser},
methodName="posts.edit",
document={post},
methodName="posts.edit"
document={post}
labelFunction={capitalize}
successCallback={closeModal}
/>

View file

@ -21,7 +21,10 @@ class FormComponent extends Component {
}
handleBlur() {
this.props.updateCurrentValue(this.props.name, this.formControl.getValue());
// see https://facebook.github.io/react/docs/more-about-refs.html
if (this.formControl !== null) {
this.props.updateCurrentValue(this.props.name, this.formControl.getValue());
}
}
renderComponent() {

View file

@ -32,6 +32,7 @@ class NovaForm extends Component{
this.throwError = this.throwError.bind(this);
this.clearErrors = this.clearErrors.bind(this);
this.updateCurrentValue = this.updateCurrentValue.bind(this);
this.formKeyDown = this.formKeyDown.bind(this);
// a debounced version of seState that only updates state every 500 ms (not used)
this.debouncedSetState = _.debounce(this.setState, 500);
@ -81,9 +82,15 @@ class NovaForm extends Component{
// add value
field.value = this.getDocument() && deepValue(this.getDocument(), fieldName) ? deepValue(this.getDocument(), fieldName) : "";
// backward compatibility from 'autoform' to 'form'
if (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`);
}
// replace value by prefilled value if value is empty
if (fieldSchema.autoform && fieldSchema.autoform.prefill) {
const prefilledValue = typeof fieldSchema.autoform.prefill === "function" ? fieldSchema.autoform.prefill.call(fieldSchema) : fieldSchema.autoform.prefill;
if (fieldSchema.form && fieldSchema.form.prefill) {
const prefilledValue = typeof fieldSchema.form.prefill === "function" ? fieldSchema.form.prefill.call(fieldSchema) : fieldSchema.form.prefill;
if (!!prefilledValue && !field.value) {
field.prefilledValue = prefilledValue;
field.value = prefilledValue;
@ -96,21 +103,21 @@ class NovaForm extends Component{
}
// add options if they exist
if (fieldSchema.autoform && fieldSchema.autoform.options) {
field.options = typeof fieldSchema.autoform.options === "function" ? fieldSchema.autoform.options.call(fieldSchema) : fieldSchema.autoform.options;
if (fieldSchema.form && fieldSchema.form.options) {
field.options = typeof fieldSchema.form.options === "function" ? fieldSchema.form.options.call(fieldSchema) : fieldSchema.form.options;
}
if (fieldSchema.autoform && fieldSchema.autoform.disabled) {
field.disabled = typeof fieldSchema.autoform.disabled === "function" ? fieldSchema.autoform.disabled.call(fieldSchema) : fieldSchema.autoform.disabled;
if (fieldSchema.form && fieldSchema.form.disabled) {
field.disabled = typeof fieldSchema.form.disabled === "function" ? fieldSchema.form.disabled.call(fieldSchema) : fieldSchema.form.disabled;
}
if (fieldSchema.autoform && fieldSchema.autoform.help) {
field.help = typeof fieldSchema.autoform.help === "function" ? fieldSchema.autoform.help.call(fieldSchema) : fieldSchema.autoform.help;
if (fieldSchema.form && fieldSchema.form.help) {
field.help = typeof fieldSchema.form.help === "function" ? fieldSchema.form.help.call(fieldSchema) : fieldSchema.form.help;
}
// add placeholder
if (fieldSchema.autoform && fieldSchema.autoform.placeholder) {
field.placeholder = fieldSchema.autoform.placeholder;
if (fieldSchema.form && fieldSchema.form.placeholder) {
field.placeholder = fieldSchema.form.placeholder;
}
if (fieldSchema.beforeComponent) field.beforeComponent = fieldSchema.beforeComponent;
@ -172,7 +179,7 @@ class NovaForm extends Component{
const fields = this.props.fields;
// get all editable/insertable fields (depending on current form type)
let relevantFields = this.getFormType() === "edit" ? getEditableFields(this.getSchema(), this.props.currentUser, this.getDocument()) : getInsertableFields(this.getSchema(), this.props.currentUser);
let relevantFields = this.getFormType() === "edit" ? getEditableFields(this.getSchema(), this.context.currentUser, this.getDocument()) : getInsertableFields(this.getSchema(), this.context.currentUser);
// if "fields" prop is specified, restrict list of fields to it
if (typeof fields !== "undefined" && fields.length > 0) {
@ -223,10 +230,11 @@ class NovaForm extends Component{
// ------------------------------- Errors ------------------------------ //
// --------------------------------------------------------------------- //
// clear all errors
// clear all errors and re-enable the form
clearErrors() {
this.setState({
errors: []
errors: [],
disabled: false,
});
}
@ -276,11 +284,11 @@ class NovaForm extends Component{
// common callback for both new and edit forms
methodCallback(error, document) {
this.setState({disabled: false});
if (error) { // error
console.log(error)
this.setState({disabled: false});
console.log(error);
const errorContent = this.context.intl.formatMessage({id: error.reason}, {details: error.details})
// add error to state
@ -294,8 +302,6 @@ class NovaForm extends Component{
} else { // success
this.clearErrors();
// reset form if this is a new document form
if (this.getFormType() === "new") this.refs.form.reset();
@ -304,6 +310,9 @@ class NovaForm extends Component{
// run close callback if it exists in context (i.e. we're inside a modal)
if (this.context.closeCallback) this.context.closeCallback();
// else there is no close callback (i.e. we're not inside a modal), call the clear errors method
// note: we don't want to update the state of an unmounted component
else this.clearErrors();
}
}
@ -361,6 +370,23 @@ class NovaForm extends Component{
}
componentWillUnmount() {
// note: patch to cancel closeCallback given by parent
// we clean the event by hand
// example : the closeCallback is a function that closes a modal by calling setState, this modal being the parent of this NovaForm component
// if this componentWillUnmount hook is triggered, that means that the modal doesn't exist anymore!
// let's not call setState on an unmounted component (avoid no-op / memory leak)
this.context.closeCallback = f => f;
}
// key down handler
formKeyDown(event) {
if( (event.ctrlKey || event.metaKey) && event.keyCode === 13) {
this.submitForm(this.refs.form.getModel());
}
}
// --------------------------------------------------------------------- //
// ------------------------------- Render ------------------------------ //
// --------------------------------------------------------------------- //
@ -373,6 +399,7 @@ class NovaForm extends Component{
<div className={"document-"+this.getFormType()}>
<Formsy.Form
onSubmit={this.submitForm}
onKeyDown={this.formKeyDown}
disabled={this.state.disabled}
ref="form"
>
@ -391,7 +418,6 @@ NovaForm.propTypes = {
collection: React.PropTypes.object,
schema: React.PropTypes.object,
document: React.PropTypes.object, // if a document is passed, this will be an edit form
currentUser: React.PropTypes.object,
submitCallback: React.PropTypes.func,
successCallback: React.PropTypes.func,
errorCallback: React.PropTypes.func,
@ -409,6 +435,7 @@ NovaForm.defaultProps = {
NovaForm.contextTypes = {
closeCallback: React.PropTypes.func,
currentUser: React.PropTypes.object,
intl: intlShape
}

View file

@ -8,7 +8,7 @@ Users.addField({
fieldSchema: {
type: Boolean,
optional: true,
autoform: {
form: {
omit: true
}
}
@ -19,7 +19,7 @@ Posts.addField({
fieldSchema: {
type: String,
optional: true,
autoform: {
form: {
omit: true
}
}
@ -30,7 +30,7 @@ Posts.addField({
fieldSchema: {
type: Boolean,
optional: true,
autoform: {
form: {
omit: true
}
}
@ -41,7 +41,7 @@ fieldName: 'isDummy',
fieldSchema: {
type: Boolean,
optional: true,
autoform: {
form: {
omit: true
}
}

View file

@ -15,7 +15,7 @@ var createPost = function (slug, postedAt, username, thumbnail) {
title: toTitleCase(slug.replace(/_/g, ' ')),
dummySlug: slug,
isDummy: true,
userId: Meteor.users.findOne({username: username})._id
userId: Users.findOne({username: username})._id
};
if (typeof thumbnail !== "undefined")
@ -28,7 +28,7 @@ var createComment = function (slug, username, body, parentBody) {
var comment = {
postId: Posts.findOne({dummySlug: slug})._id,
userId: Meteor.users.findOne({username: username})._id,
userId: Users.findOne({username: username})._id,
body: body,
isDummy: true,
disableNotifications: true
@ -93,7 +93,7 @@ var createDummyComments = function () {
};
deleteDummyContent = function () {
Meteor.users.remove({'profile.isDummy': true});
Users.remove({'profile.isDummy': true});
Posts.remove({isDummy: true});
Comments.remove({isDummy: true});
};

View file

@ -1,5 +0,0 @@
{
"translation_function_name": "__",
"helper_name": "_",
"namespace": "project"
}

View file

@ -6,7 +6,7 @@ var kadiraAppIdProperty = {
fieldSchema: {
type: String,
optional: true,
autoform: {
form: {
group: "kadira"
}
}
@ -20,7 +20,7 @@ var kadiraAppSecretProperty = {
type: String,
optional: true,
private: true,
autoform: {
form: {
group: "kadira",
class: "private-field"
}

View file

@ -1,5 +0,0 @@
{
"translation_function_name": "__",
"helper_name": "_",
"namespace": "project"
}

View file

@ -25,7 +25,8 @@ SimpleSchema.extendOptions({
required: Match.Optional(Boolean), // required: true means the field is required to have a complete profile
profile: Match.Optional(Boolean), // profile: true means the field is shown on user profiles
template: Match.Optional(String), // template used to display the field
autoform: Match.Optional(Object), // autoform placeholder
form: Match.Optional(Object), // form placeholder
autoform: Match.Optional(Object), // legacy form placeholder; backward compatibility
control: Match.Optional(Match.Any), // NovaForm control (String or React component)
order: Match.Optional(Number), // position in the form
group: Match.Optional(Object) // form fieldset group

View file

@ -131,35 +131,6 @@ Telescope.utils.getOutgoingUrl = function (url) {
return Telescope.utils.getSiteUrl() + "out?url=" + encodeURIComponent(url);
};
// This function should only ever really be necessary server side
// Client side using .path() is a better option since it's relative
// and shouldn't care about the siteUrl.
Telescope.utils.getRouteUrl = function (routeName, params, options) {
options = options || {};
var route = FlowRouter.path(
routeName,
params || {},
options
);
return route;
};
Telescope.utils.getSignupUrl = function() {
return this.getRouteUrl('signUp');
};
Telescope.utils.getSigninUrl = function() {
return this.getRouteUrl('signIn');
};
//TODO: fix this
Telescope.utils.getPostCommentUrl = function(postId, commentId) {
// get link to a comment on a post page
return this.getRouteUrl('post_page_comment', {
_id: postId,
commentId: commentId
});
};
Telescope.utils.slugify = function (s) {
var slug = getSlug(s, {
truncate: 60
@ -174,8 +145,8 @@ Telescope.utils.slugify = function (s) {
};
Telescope.utils.getUnusedSlug = function (collection, slug) {
var suffix = "";
var index = 0;
let suffix = "";
let index = 0;
// test if slug is already in use
while (!!collection.findOne({slug: slug+suffix})) {

View file

@ -46,7 +46,7 @@ if (typeof Telescope.settings.collection !== "undefined") {
fieldSchema: {
type: Boolean,
optional: true,
autoform: {
form: {
group: 'newsletter',
instructions: 'Enable newsletter (requires restart).'
}
@ -57,7 +57,7 @@ if (typeof Telescope.settings.collection !== "undefined") {
fieldSchema: {
type: Boolean,
optional: true,
autoform: {
form: {
group: 'newsletter',
instructions: 'Enable newsletter in development too (requires restart).'
}
@ -69,7 +69,7 @@ if (typeof Telescope.settings.collection !== "undefined") {
type: String,
optional: true,
private: true,
autoform: {
form: {
group: "newsletter",
class: "private-field"
}
@ -81,7 +81,7 @@ if (typeof Telescope.settings.collection !== "undefined") {
type: String,
optional: true,
private: true,
autoform: {
form: {
group: 'newsletter',
instructions: 'The ID of the list you want to send to.',
class: "private-field"
@ -93,7 +93,7 @@ if (typeof Telescope.settings.collection !== "undefined") {
fieldSchema: {
type: Number,
optional: true,
autoform: {
form: {
group: 'newsletter'
}
}
@ -104,7 +104,7 @@ if (typeof Telescope.settings.collection !== "undefined") {
type: [Number],
optional: true,
defaultValue: [2,4,6],
autoform: {
form: {
group: 'newsletter',
instructions: 'Defaults to once a week on Monday. Changes require restarting your app to take effect.',
noselect: true,
@ -147,7 +147,7 @@ if (typeof Telescope.settings.collection !== "undefined") {
type: String,
optional: true,
defaultValue: '00:00',
autoform: {
form: {
group: 'newsletter',
instructions: 'Defaults to 00:00/12:00 AM. Time to send out newsletter if enabled (GMT).',
type: 'time'
@ -159,7 +159,7 @@ if (typeof Telescope.settings.collection !== "undefined") {
fieldSchema: {
type: Boolean,
optional: true,
autoform: {
form: {
group: 'newsletter',
instructions: 'Automatically subscribe new users on sign-up.'
}

View file

@ -11,4 +11,4 @@ function subscribeUserOnProfileCompletion (user) {
}
return user;
}
Telescope.callbacks.add("profileCompletedAsync", subscribeUserOnProfileCompletion);
Telescope.callbacks.add("users.profileCompleted.async", subscribeUserOnProfileCompletion);

View file

@ -59,7 +59,7 @@ Newsletter.build = function (postsArray) {
subject += post.title;
// get author of the current post
var postUser = Meteor.users.findOne(post.userId);
var postUser = Users.findOne(post.userId);
// the naked post object as stored in the database is missing a few properties, so let's add them
var properties = _.extend(post, {
@ -93,7 +93,7 @@ Newsletter.build = function (postsArray) {
properties.popularComments = Comments.find({postId: post._id}, {sort: {score: -1}, limit: 2, transform: function (comment) {
// get comment author
var user = Meteor.users.findOne(comment.userId);
var user = Users.findOne(comment.userId);
// add properties to comment
comment.body = Telescope.utils.trimHTML(comment.htmlBody, 20);

View file

@ -1,5 +0,0 @@
{
"translation_function_name": "__",
"helper_name": "_",
"namespace": "project"
}

View file

@ -34,7 +34,7 @@ if (typeof Telescope.settings.collection !== "undefined") {
type: Boolean,
optional: true,
defaultValue: true,
autoform: {
form: {
group: 'notifications',
instructions: 'Enable email notifications for new posts and new comments (requires restart).'
}

View file

@ -1,27 +0,0 @@
import Telescope from 'meteor/nova:lib';
getUnsubscribeLink = function(user){
return Telescope.utils.getRouteUrl('unsubscribe', {hash: user.telescope.emailHash});
};
Meteor.methods({
unsubscribeUser : function(hash){
check(hash, String);
// TO-DO: currently, if you have somebody's email you can unsubscribe them
// A user-specific salt should be added to the hashing method to prevent this
var user = Meteor.users.findOne({"telescope.emailHash": hash});
if(user){
Meteor.users.update(user._id, {
$set: {
'profile.notifications.users' : 0,
'profile.notifications.posts' : 0,
'profile.notifications.comments' : 0,
'profile.notifications.replies' : 0
}
});
return true;
}
return false;
}
});

View file

@ -1,5 +0,0 @@
{
"translation_function_name": "__",
"helper_name": "_",
"namespace": "project"
}

View file

@ -20,8 +20,4 @@ Package.onUse(function (api) {
'lib/custom_fields.js'
], ['client', 'server']);
api.addFiles([
'lib/server/notifications-server.js'
], ['server']);
});

View file

@ -207,11 +207,6 @@ function PostsNewRequiredPropertiesCheck (post, user) {
// generate slug
post.slug = Telescope.utils.slugify(post.title);
// post is not pending and has been scheduled to be posted in the future by a moderator/admin
if (post.status !== Posts.config.STATUS_PENDING && post.postedAt && post.postedAt > post.createdAt) {
post.status = Posts.config.STATUS_SCHEDULED;
}
// if post is approved but doesn't have a postedAt date, give it a default date
// note: pending posts get their postedAt date only once theyre approved
if (Posts.isApproved(post) && !post.postedAt) {
@ -238,7 +233,7 @@ Telescope.callbacks.add("posts.new.sync", PostsNewSetFuture);
*/
function PostsNewIncrementPostCount (post) {
var userId = post.userId;
Meteor.users.update({_id: userId}, {$inc: {"telescope.postCount": 1}});
Users.update({_id: userId}, {$inc: {"telescope.postCount": 1}});
}
Telescope.callbacks.add("posts.new.async", PostsNewIncrementPostCount);
@ -247,7 +242,7 @@ Telescope.callbacks.add("posts.new.async", PostsNewIncrementPostCount);
*/
function PostsNewUpvoteOwnPost (post) {
if (typeof Telescope.operateOnItem !== "undefined") {
var postAuthor = Meteor.users.findOne(post.userId);
var postAuthor = Users.findOne(post.userId);
Telescope.operateOnItem(Posts, post._id, postAuthor, "upvote");
}
}
@ -263,7 +258,7 @@ function PostsNewNotifications (post) {
var adminIds = _.pluck(Users.adminUsers({fields: {_id:1}}), '_id');
var notifiedUserIds = _.pluck(Users.find({'telescope.notifications_posts': true}, {fields: {_id:1}}).fetch(), '_id');
var notificationData = {
post: _.pick(post, '_id', 'userId', 'title', 'url')
post: _.pick(post, '_id', 'userId', 'title', 'url', 'slug')
};
// remove post author ID from arrays

View file

@ -57,7 +57,7 @@ Posts.helpers({getPageUrl: function (isAbsolute) {return Posts.getPageUrl(this,
* @param {Object} post
*/
Posts.getAuthorName = function (post) {
var user = Meteor.users.findOne(post.userId);
var user = Users.findOne(post.userId);
if (user) {
return user.getDisplayName();
} else {

View file

@ -18,7 +18,7 @@ Posts.methods = {};
*/
Posts.methods.new = function (post) {
const currentUser = Meteor.users.findOne(post.userId);
const currentUser = Users.findOne(post.userId);
post = Telescope.callbacks.run("posts.new.sync", post, currentUser);

View file

@ -4,7 +4,7 @@ import Users from 'meteor/nova:users';
Posts.getNotificationProperties = function (data) {
const post = data.post;
const postAuthor = Meteor.users.findOne(post.userId);
const postAuthor = Users.findOne(post.userId);
const properties = {
postAuthorName : Posts.getAuthorName(post),
postTitle : Telescope.utils.cleanUp(post.title),

View file

@ -30,8 +30,8 @@ Posts.parameters.get = function (terms) {
options: {}
};
// iterate over postsParameters callbacks
parameters = Telescope.callbacks.run("postsParameters", parameters, _.clone(terms));
// iterate over posts.parameters callbacks
parameters = Telescope.callbacks.run("posts.parameters", parameters, _.clone(terms));
// if sort options are not provided, default to "createdAt" sort
if (_.isEmpty(parameters.options.sort)) {
@ -62,7 +62,7 @@ function addViewParameter (parameters, terms) {
return parameters;
}
Telescope.callbacks.add("postsParameters", addViewParameter);
Telescope.callbacks.add("posts.parameters", addViewParameter);
// View Parameter
// Add "after" and "before" properties to terms which can be used to limit posts in time.
@ -138,7 +138,7 @@ function addTimeParameter (parameters, terms) {
return parameters;
}
Telescope.callbacks.add("postsParameters", addTimeParameter);
Telescope.callbacks.add("posts.parameters", addTimeParameter);
// limit the number of items that can be requested at once
function limitPosts (parameters, terms) {
@ -172,4 +172,4 @@ function limitPosts (parameters, terms) {
return parameters;
}
Telescope.callbacks.add("postsParameters", limitPosts);
Telescope.callbacks.add("posts.parameters", limitPosts);

View file

@ -167,11 +167,11 @@ Posts.schemaJSON = {
// only provide a default value
// 1) this is an insert operation
// 2) status field is not set in the document being inserted
var user = Meteor.users.findOne(this.userId);
var user = Users.findOne(this.userId);
if (this.isInsert && !this.isSet)
return Posts.getDefaultStatus(user);
},
autoform: {
form: {
noselect: true,
options: Telescope.statuses,
group: 'admin'
@ -245,10 +245,10 @@ Posts.schemaJSON = {
// editableIf: canEditAll,
control: "select",
publish: true,
autoform: {
form: {
group: 'admin',
options: function () {
return Meteor.users.find().map(function (user) {
return Users.find().map(function (user) {
return {
value: user._id,
label: Users.getDisplayName(user)
@ -258,7 +258,7 @@ Posts.schemaJSON = {
},
join: {
joinAs: "user",
collection: () => Meteor.users
collection: () => Users
}
}
};

View file

@ -24,7 +24,7 @@ const getPostsListUsers = posts => {
userIds = _.unique(userIds);
return Meteor.users.find({_id: {$in: userIds}}, {fields: Users.publishedFields.list});
return Users.find({_id: {$in: userIds}}, {fields: Users.publishedFields.list});
};
@ -56,7 +56,7 @@ const getSinglePostUsers = post => {
// remove any duplicate IDs
users = _.unique(users);
return Meteor.users.find({_id: {$in: users}}, {fields: Users.publishedFields.list});
return Users.find({_id: {$in: users}}, {fields: Users.publishedFields.list});
};
// ------------------------------------- Publications -------------------------------- //
@ -71,7 +71,7 @@ Meteor.publish('posts.list', function (terms) {
this.autorun(function () {
const currentUser = this.userId && Meteor.users.findOne(this.userId);
const currentUser = this.userId && Users.findOne(this.userId);
terms.currentUserId = this.userId; // add currentUserId to terms
const {selector, options} = Posts.parameters.get(terms);
@ -103,7 +103,7 @@ Meteor.publish('posts.single', function (terms) {
check(terms, Match.OneOf({_id: String}, {_id: String, slug: Match.Any}));
const currentUser = this.userId && Meteor.users.findOne(this.userId);
const currentUser = this.userId && Users.findOne(this.userId);
const options = {fields: Posts.publishedFields.single};
const posts = Posts.find(terms._id, options);
const post = posts.fetch()[0];

View file

@ -115,7 +115,7 @@ Posts.views.add("userPosts", function (terms) {
* @summary User upvoted posts view
*/
Posts.views.add("userUpvotedPosts", function (terms) {
var user = Meteor.users.findOne(terms.userId);
var user = Users.findOne(terms.userId);
var postsIds = _.pluck(user.telescope.upvotedPosts, "itemId");
return {
selector: {_id: {$in: postsIds}, userId: {$ne: terms.userId}}, // exclude own posts
@ -127,7 +127,7 @@ Posts.views.add("userUpvotedPosts", function (terms) {
* @summary User downvoted posts view
*/
Posts.views.add("userDownvotedPosts", function (terms) {
var user = Meteor.users.findOne(terms.userId);
var user = Users.findOne(terms.userId);
var postsIds = _.pluck(user.telescope.downvotedPosts, "itemId");
// TODO: sort based on votedAt timestamp and not postedAt, if possible
return {

View file

@ -55,7 +55,7 @@ const serveCommentRSS = function (terms, url) {
post = Posts.findOne(comment.postId);
feed.item({
title: 'Comment on '+post.title,
description: comment.body+'</br></br>'+'<a href="'+Telescope.utils.getPostCommentUrl(post._id, comment._id)+'">Discuss</a>',
description: `${comment.body}</br></br><a href="${comment.getPageUrl(true)}">Discuss</a>`,
author: comment.author,
date: comment.postedAt,
url: comment.getPageUrl(true),

View file

@ -21,4 +21,4 @@ function addSearchQueryParameter (parameters, terms) {
}
return parameters;
}
Telescope.callbacks.add("postsParameters", addSearchQueryParameter);
Telescope.callbacks.add("posts.parameters", addSearchQueryParameter);

View file

@ -1,5 +0,0 @@
{
"translation_function_name": "__",
"helper_name": "_",
"namespace": "project"
}

View file

@ -21,7 +21,7 @@ Telescope.settings.schema = new SimpleSchema({
insertableIf: canEdit,
editableIf: canEdit,
publish: true,
autoform: {
form: {
disabled: isInSettingsJSON,
prefill: getFromJSON,
instructions: "Your site's title.",
@ -35,7 +35,7 @@ Telescope.settings.schema = new SimpleSchema({
// regEx: SimpleSchema.RegEx.Url,
insertableIf: canEdit,
editableIf: canEdit,
autoform: {
form: {
disabled: isInSettingsJSON,
prefill: getFromJSON,
group: "01_general",
@ -49,7 +49,7 @@ Telescope.settings.schema = new SimpleSchema({
publish: true,
insertableIf: canEdit,
editableIf: canEdit,
autoform: {
form: {
disabled: isInSettingsJSON,
prefill: getFromJSON,
group: "01_general"
@ -61,7 +61,7 @@ Telescope.settings.schema = new SimpleSchema({
publish: true,
insertableIf: canEdit,
editableIf: canEdit,
autoform: {
form: {
disabled: isInSettingsJSON,
prefill: getFromJSON,
group: "01_general",
@ -76,7 +76,7 @@ Telescope.settings.schema = new SimpleSchema({
insertableIf: canEdit,
editableIf: canEdit,
regEx: SimpleSchema.RegEx.Url,
autoform: {
form: {
disabled: isInSettingsJSON,
prefill: getFromJSON,
group: "01_general",
@ -90,7 +90,7 @@ Telescope.settings.schema = new SimpleSchema({
insertableIf: canEdit,
editableIf: canEdit,
control: "checkbox",
autoform: {
form: {
disabled: isInSettingsJSON,
prefill: getFromJSON,
group: 'invites',
@ -104,7 +104,7 @@ Telescope.settings.schema = new SimpleSchema({
insertableIf: canEdit,
editableIf: canEdit,
control: "checkbox",
autoform: {
form: {
disabled: isInSettingsJSON,
prefill: getFromJSON,
group: 'invites',
@ -118,7 +118,7 @@ Telescope.settings.schema = new SimpleSchema({
insertableIf: canEdit,
editableIf: canEdit,
control: "checkbox",
autoform: {
form: {
disabled: isInSettingsJSON,
prefill: getFromJSON,
group: "01_general",
@ -131,7 +131,7 @@ Telescope.settings.schema = new SimpleSchema({
optional: true,
insertableIf: canEdit,
editableIf: canEdit,
autoform: {
form: {
disabled: isInSettingsJSON,
prefill: getFromJSON,
group: "06_email",
@ -144,7 +144,7 @@ Telescope.settings.schema = new SimpleSchema({
optional: true,
insertableIf: canEdit,
editableIf: canEdit,
autoform: {
form: {
disabled: isInSettingsJSON,
prefill: getFromJSON,
group: "06_email",
@ -158,7 +158,7 @@ Telescope.settings.schema = new SimpleSchema({
defaultValue: 30,
insertableIf: canEdit,
editableIf: canEdit,
autoform: {
form: {
disabled: isInSettingsJSON,
prefill: getFromJSON,
group: '01_general',
@ -173,7 +173,7 @@ Telescope.settings.schema = new SimpleSchema({
defaultValue: 30,
insertableIf: canEdit,
editableIf: canEdit,
autoform: {
form: {
disabled: isInSettingsJSON,
prefill: getFromJSON,
group: "02_posts",
@ -187,7 +187,7 @@ Telescope.settings.schema = new SimpleSchema({
insertableIf: canEdit,
editableIf: canEdit,
control: "radiogroup",
autoform: {
form: {
disabled: isInSettingsJSON,
prefill: getFromJSON,
group: "02_posts",
@ -204,7 +204,7 @@ Telescope.settings.schema = new SimpleSchema({
defaultValue: 15,
insertableIf: canEdit,
editableIf: canEdit,
autoform: {
form: {
disabled: isInSettingsJSON,
prefill: getFromJSON,
group: "03_comments",
@ -218,7 +218,7 @@ Telescope.settings.schema = new SimpleSchema({
defaultValue: 30,
insertableIf: canEdit,
editableIf: canEdit,
autoform: {
form: {
disabled: isInSettingsJSON,
prefill: getFromJSON,
group: "02_posts",
@ -232,7 +232,7 @@ Telescope.settings.schema = new SimpleSchema({
publish: true,
insertableIf: canEdit,
editableIf: canEdit,
autoform: {
form: {
disabled: isInSettingsJSON,
prefill: getFromJSON,
group: 'invites'
@ -245,7 +245,7 @@ Telescope.settings.schema = new SimpleSchema({
publish: true,
insertableIf: canEdit,
editableIf: canEdit,
autoform: {
form: {
disabled: isInSettingsJSON,
prefill: getFromJSON,
group: "02_posts"
@ -257,7 +257,7 @@ Telescope.settings.schema = new SimpleSchema({
publish: true,
insertableIf: canEdit,
editableIf: canEdit,
autoform: {
form: {
disabled: isInSettingsJSON,
prefill: getFromJSON,
group: "04_logo"
@ -269,7 +269,7 @@ Telescope.settings.schema = new SimpleSchema({
publish: true,
insertableIf: canEdit,
editableIf: canEdit,
autoform: {
form: {
disabled: isInSettingsJSON,
prefill: getFromJSON,
group: "04_logo"
@ -281,7 +281,7 @@ Telescope.settings.schema = new SimpleSchema({
publish: true,
insertableIf: canEdit,
editableIf: canEdit,
autoform: {
form: {
disabled: isInSettingsJSON,
prefill: getFromJSON,
group: "04_logo"
@ -293,7 +293,7 @@ Telescope.settings.schema = new SimpleSchema({
publish: true,
insertableIf: canEdit,
editableIf: canEdit,
autoform: {
form: {
disabled: isInSettingsJSON,
prefill: getFromJSON,
group: "04_logo"
@ -305,7 +305,7 @@ Telescope.settings.schema = new SimpleSchema({
// optional: true,
// insertableIf: canEdit,
// editableIf: canEdit,
// autoform: {
// form: {
// group: "01_general",
// instructions: 'The app\'s language. Defaults to English.',
// options: function () {
@ -325,7 +325,7 @@ Telescope.settings.schema = new SimpleSchema({
publish: true,
insertableIf: canEdit,
editableIf: canEdit,
autoform: {
form: {
disabled: isInSettingsJSON,
prefill: getFromJSON,
group: "07_integrations"
@ -337,7 +337,7 @@ Telescope.settings.schema = new SimpleSchema({
publish: true,
insertableIf: canEdit,
editableIf: canEdit,
autoform: {
form: {
disabled: isInSettingsJSON,
prefill: getFromJSON,
group: "07_integrations"
@ -349,7 +349,7 @@ Telescope.settings.schema = new SimpleSchema({
publish: true,
insertableIf: canEdit,
editableIf: canEdit,
autoform: {
form: {
disabled: isInSettingsJSON,
prefill: getFromJSON,
group: "07_integrations"
@ -361,7 +361,7 @@ Telescope.settings.schema = new SimpleSchema({
publish: true,
insertableIf: canEdit,
editableIf: canEdit,
autoform: {
form: {
disabled: isInSettingsJSON,
prefill: getFromJSON,
group: "01_general"

View file

@ -1,5 +0,0 @@
{
"translation_function_name": "__",
"helper_name": "_",
"namespace": "project"
}

View file

@ -90,7 +90,7 @@ if (typeof Package['nova:posts'] !== "undefined") {
if (!!subscribers && !!subscribers.length) {
// remove userIds of users that have already been notified and of post's author
let subscriberIdsToNotify = _.difference(subscribers, userIdsNotified, [post.userId]);
let subscriberIdsToNotify = _.uniq(_.difference(subscribers, userIdsNotified, [post.userId]));
Telescope.notifications.create(subscriberIdsToNotify, 'newPost', notificationData);

View file

@ -8,7 +8,7 @@ Users.addField([
type: Object,
optional: true,
blackbox: true,
autoform: {
form: {
omit: true
}
}
@ -18,7 +18,7 @@ Users.addField([
fieldSchema: {
type: [String],
optional: true,
autoform: {
form: {
omit: true
},
publish: true,
@ -33,7 +33,7 @@ Users.addField([
fieldSchema: {
type: Number,
optional: true,
autoform: {
form: {
omit: true
}
}
@ -50,7 +50,7 @@ if (typeof Package['nova:posts'] !== "undefined") {
fieldSchema: {
type: [String],
optional: true,
autoform: {
form: {
omit: true
},
publish: true,
@ -65,7 +65,7 @@ if (typeof Package['nova:posts'] !== "undefined") {
fieldSchema: {
type: Number,
optional: true,
autoform: {
form: {
omit: true
}
}
@ -84,7 +84,7 @@ if (typeof Package['nova:categories'] !== "undefined") {
fieldSchema: {
type: [String],
optional: true,
autoform: {
form: {
omit: true
},
publish: true,
@ -99,7 +99,7 @@ if (typeof Package['nova:categories'] !== "undefined") {
fieldSchema: {
type: Number,
optional: true,
autoform: {
form: {
omit: true
}
}

View file

@ -2,7 +2,7 @@ if (typeof Package['nova:posts'] !== "undefined") {
import Posts from "meteor/nova:posts";
Posts.views.add("userSubscribedPosts", function (terms) {
var user = Meteor.users.findOne(terms.userId),
var user = Users.findOne(terms.userId),
postsIds = [];
if (user && user.telescope.subscribedItems && user.telescope.subscribedItems.Posts) {

View file

@ -14,11 +14,11 @@ import NovaEmail from 'meteor/nova:email';
Users.after.insert(function (userId, user) {
// run create user async callbacks
Telescope.callbacks.runAsync("onCreateUserAsync", user);
Telescope.callbacks.runAsync("users.new.async", user);
// check if all required fields have been filled in. If so, run profile completion callbacks
if (Users.hasCompletedProfile(user)) {
Telescope.callbacks.runAsync("profileCompletedAsync", user);
Telescope.callbacks.runAsync("users.profileCompleted.async", user);
}
});
@ -132,23 +132,24 @@ function setupUser (user, options) {
user.telescope.displayName = user.username;
}
// create slug from display name
user.telescope.slug = Telescope.utils.slugify(user.telescope.displayName);
// create a basic slug from display name and then modify it if this slugs already exists;
const basicSlug = Telescope.utils.slugify(user.telescope.displayName);
user.telescope.slug = Telescope.utils.getUnusedSlug(Users, basicSlug);
// if this is not a dummy account, and is the first user ever, make them an admin
user.isAdmin = (!user.profile.isDummy && Meteor.users.find({'profile.isDummy': {$ne: true}}).count() === 0) ? true : false;
user.isAdmin = (!user.profile.isDummy && Users.find({'profile.isDummy': {$ne: true}}).count() === 0) ? true : false;
Events.track('new user', {username: user.telescope.displayName, email: user.telescope.email});
return user;
}
Telescope.callbacks.add("onCreateUser", setupUser);
Telescope.callbacks.add("users.new.sync", setupUser);
function hasCompletedProfile (user) {
return Users.hasCompletedProfile(user);
}
Telescope.callbacks.add("profileCompletedChecks", hasCompletedProfile);
Telescope.callbacks.add("users.profileCompleted.sync", hasCompletedProfile);
function adminUserCreationNotification (user) {
// send notifications to admins
@ -162,4 +163,4 @@ function adminUserCreationNotification (user) {
});
return user;
}
Telescope.callbacks.add("onCreateUser", adminUserCreationNotification);
Telescope.callbacks.add("users.new.sync", adminUserCreationNotification);

View file

@ -21,7 +21,7 @@ Users.getUser = function (userOrUserId) {
return Meteor.user();
}
} else if (typeof userOrUserId === "string") {
return Meteor.users.findOne(userOrUserId);
return Users.findOne(userOrUserId);
} else {
return userOrUserId;
}
@ -44,7 +44,7 @@ Users.getUserName = function (user) {
}
};
Users.helpers({getUserName: function () {return Users.getUserName(this);}});
Users.getUserNameById = function (userId) {return Users.getUserName(Meteor.users.findOne(userId))};
Users.getUserNameById = function (userId) {return Users.getUserName(Users.findOne(userId))};
/**
* @summary Get a user's display name (not unique, can take special characters and spaces)
@ -58,7 +58,7 @@ Users.getDisplayName = function (user) {
}
};
Users.helpers({getDisplayName: function () {return Users.getDisplayName(this);}});
Users.getDisplayNameById = function (userId) {return Users.getDisplayName(Meteor.users.findOne(userId));};
Users.getDisplayNameById = function (userId) {return Users.getDisplayName(Users.findOne(userId));};
/**
* @summary Get a user's profile URL
@ -105,7 +105,7 @@ Users.getTwitterName = function (user) {
return null;
};
Users.helpers({getTwitterName: function () {return Users.getTwitterName(this);}});
Users.getTwitterNameById = function (userId) {return Users.getTwitterName(Meteor.users.findOne(userId));};
Users.getTwitterNameById = function (userId) {return Users.getTwitterName(Users.findOne(userId));};
/**
* @summary Get a user's GitHub name
@ -121,7 +121,7 @@ Users.getGitHubName = function (user) {
return null;
};
Users.helpers({getGitHubName: function () {return Users.getGitHubName(this);}});
Users.getGitHubNameById = function (userId) {return Users.getGitHubName(Meteor.users.findOne(userId));};
Users.getGitHubNameById = function (userId) {return Users.getGitHubName(Users.findOne(userId));};
/**
* @summary Get a user's email
@ -135,7 +135,7 @@ Users.getEmail = function (user) {
}
};
Users.helpers({getEmail: function () {return Users.getEmail(this);}});
Users.getEmailById = function (userId) {return Users.getEmail(Meteor.users.findOne(userId));};
Users.getEmailById = function (userId) {return Users.getEmail(Users.findOne(userId));};
/**
* @summary Get a user's email hash
@ -145,7 +145,7 @@ Users.getEmailHash = function (user) {
return user.telescope.emailHash;
};
Users.helpers({getEmailHash: function () {return Users.getEmailHash(this);}});
Users.getEmailHashById = function (userId) {return Users.getEmailHash(Meteor.users.findOne(userId));};
Users.getEmailHashById = function (userId) {return Users.getEmailHash(Users.findOne(userId));};
/**
* @summary Get a user setting
@ -182,7 +182,7 @@ Users.hasCompletedProfile = function (user) {
});
};
Users.helpers({hasCompletedProfile: function () {return Users.hasCompletedProfile(this);}});
Users.hasCompletedProfileById = function (userId) {return Users.hasCompletedProfile(Meteor.users.findOne(userId));};
Users.hasCompletedProfileById = function (userId) {return Users.hasCompletedProfile(Users.findOne(userId));};
/**
* @summary Check if a user has upvoted a document
@ -272,6 +272,6 @@ Users.getCurrentUserEmail = function () {
};
Users.findByEmail = function (email) {
return Meteor.users.findOne({"telescope.email": email});
return Users.findOne({"telescope.email": email});
};

View file

@ -5,7 +5,7 @@ var completeUserProfile = function (userId, modifier, user) {
Users.update(userId, modifier);
Telescope.callbacks.runAsync("profileCompletedAsync", Users.findOne(userId));
Telescope.callbacks.runAsync("users.profileCompleted.async", Users.findOne(userId));
return Users.findOne(userId);
@ -27,7 +27,7 @@ Users.methods.edit = (userId, modifier, user) => {
// ------------------------------ Callbacks ------------------------------ //
modifier = Telescope.callbacks.run("UsersEdit", modifier, user);
modifier = Telescope.callbacks.run("users.edit.sync", modifier, user);
// ------------------------------ Update ------------------------------ //
@ -35,7 +35,7 @@ Users.methods.edit = (userId, modifier, user) => {
// ------------------------------ Callbacks ------------------------------ //
Telescope.callbacks.runAsync("UsersEditAsync", Users.findOne(userId), user);
Telescope.callbacks.runAsync("users.edit.async", Users.findOne(userId), user);
// ------------------------------ After Update ------------------------------ //
return Users.findOne(userId);
@ -61,53 +61,6 @@ Users.methods.removeGroup = (userId, groupName) => {
};
Meteor.methods({
'users.compleProfile'(modifier, userId) {
check(modifier, Match.OneOf({$set: Object}, {$unset: Object}, {$set: Object, $unset: Object}));
check(userId, String);
var currentUser = Meteor.user(),
user = Users.findOne(userId),
schema = Users.simpleSchema()._schema;
// ------------------------------ Checks ------------------------------ //
// check that user can edit document
if (!user || !Users.canEdit(currentUser, user)) {
throw new Meteor.Error(601, 'sorry_you_cannot_edit_this_user');
}
// if an $unset modifier is present, it means one or more of the fields is missing
if (modifier.$unset) {
throw new Meteor.Error(601, 'all_fields_are_required');
}
// check for existing emails and throw error if necessary
// NOTE: redundant with collection hook, but better to throw the error here to avoid wiping out the form
if (modifier.$set && modifier.$set["telescope.email"]) {
var email = modifier.$set["telescope.email"];
if (Users.findByEmail(email)) {
throw new Meteor.Error("email_taken1", "this_email_is_already_taken" + " (" + email + ")");
}
}
// go over each field and throw an error if it's not editable
// loop over each operation ($set, $unset, etc.)
_.each(modifier, function (operation) {
// loop over each property being operated on
_.keys(operation).forEach(function (fieldName) {
var field = schema[fieldName];
if (!Users.canEditField(user, field, user)) {
throw new Meteor.Error("disallowed_property", 'disallowed_property_detected' + ": " + fieldName);
}
});
});
completeUserProfile(userId, modifier, user);
},
'users.edit'(userId, modifier) {
// checking might be redundant because SimpleSchema already enforces the schema, but you never know
@ -149,7 +102,7 @@ Meteor.methods({
const user = Users.findOne(userId);
Meteor.users.remove(userId);
Users.remove(userId);
Telescope.callbacks.runAsync("users.remove.async", user, options);

View file

@ -47,7 +47,7 @@ Telescope.schemas.userData = new SimpleSchema({
control: "textarea",
insertableIf: canInsert,
editableIf: canEdit,
// autoform: {
// form: {
// rows: 5
// }
},
@ -116,7 +116,7 @@ Telescope.schemas.userData = new SimpleSchema({
publish: true,
profile: true,
optional: true,
// autoform: {
// form: {
// omit: true
// },
template: "user_profile_bio"
@ -146,7 +146,7 @@ Telescope.schemas.userData = new SimpleSchema({
// optional: true,
// editableIf: canEdit,
// blackbox: true,
// autoform: {
// form: {
// omit: true
// }
// },
@ -209,7 +209,7 @@ Telescope.schemas.userData = new SimpleSchema({
control: "checkboxgroup",
insertableIf: canEditAll,
editableIf: canEditAll,
autoform: {
form: {
options: function () {
const groups = _.without(_.keys(Users.groups), "anonymous", "default", "admins");
return groups.map(group => {return {value: group, label: group};});
@ -260,7 +260,7 @@ Users.schema = new SimpleSchema({
insertableIf: canEditAll,
editableIf: canEditAll,
group: adminGroup
// autoform: {
// form: {
// omit: true
// }
},
@ -285,6 +285,6 @@ Users.schema = new SimpleSchema({
// });
/**
* @summary Attach schema to Meteor.users collection
* @summary Attach schema to Users (Meteor.users at the moment) collection
*/
Users.attachSchema(Users.schema);

View file

@ -1,7 +1,7 @@
import Telescope from 'meteor/nova:lib';
function telescopeCreateUserCallback (options, user) {
user = Telescope.callbacks.run("onCreateUser", user, options);
user = Telescope.callbacks.run("users.new.sync", user, options);
return user;
};

View file

@ -7,11 +7,11 @@ import Users from '../modules.js';
Meteor.publish('users.single', function (terms) {
var idOrSlug = terms._id || terms['telescope.slug'];
var findById = Meteor.users.findOne(idOrSlug);
var findBySlug = Meteor.users.findOne({"telescope.slug": idOrSlug});
var findById = Users.findOne(idOrSlug);
var findBySlug = Users.findOne({"telescope.slug": idOrSlug});
var user = typeof findById !== 'undefined' ? findById : findBySlug;
var options = Users.isAdmin(this.userId) ? {} : {fields: Users.publishedFields.public};
return user ? Meteor.users.find({_id: user._id}, options) : [];
return user ? Users.find({_id: user._id}, options) : [];
});
@ -19,7 +19,7 @@ Meteor.publish('users.single', function (terms) {
* @summary Publish the current user
*/
Meteor.publish('users.current', function () {
const user = Meteor.users.find({_id: this.userId}, {fields: {'services.password.bcrypt': false}});
const user = Users.find({_id: this.userId}, {fields: {'services.password.bcrypt': false}});
return user || [];
});
@ -32,7 +32,7 @@ Meteor.publish('users.current', function () {
// var selector = Settings.get('requirePostInvite') ? {isInvited: true} : {}; // only users that can post
// if (Users.isAdminById(this.userId)) {
// return Meteor.users.find(selector, {fields: Users.pubsub.avatarProperties});
// return Users.find(selector, {fields: Users.pubsub.avatarProperties});
// }
// return [];
// });

View file

@ -1,5 +0,0 @@
{
"translation_function_name": "__",
"helper_name": "_",
"namespace": "project"
}