Merge branch 'release-0.5.0' into parties-example

Conflicts:
	docs/client/packages/d3.html
This commit is contained in:
Nick Martin
2012-10-15 22:15:02 -07:00
32 changed files with 850 additions and 601 deletions

View File

@@ -40,6 +40,9 @@
* Meteor now provides a compatible replacement for the DOM `localStorage`
facility that works in IE7, in the `localstorage-polyfill` smart package.
* Meteor now packages the D3 library for manipulating documents based on data in
a smart package called `d3`.
* `Meteor.Collection` now takes its optional `manager` argument (used to
associate a collection with a server you've connected to with
`Meteor.connect`) as a named option. (The old call syntax continues to work

View File

@@ -356,6 +356,11 @@ Copyright (c) 2011:
Tim Koschützki (tim@debuggable.com)
Felix Geisendörfer (felix@debuggable.com)
----------
node-form-data: https://github.com/felixge/node-form-data
----------
Copyright (c) 2012 Felix Geisendörfer (felix@debuggable.com) and contributors
==============
@@ -622,6 +627,11 @@ npmlog: https://github.com/isaacs/npmlog
once: https://github.com/isaacs/once
osenv: https://github.com/isaacs/osenv
mute-stream: https://github.com/isaacs/mute-stream
couch-login: https://github.com/isaacs/couch-login
npmconf: https://github.com/isaacs/npmconf
read-installed: https://github.com/isaacs/read-installed
read-package-json: https://github.com/isaacs/read-package-json
promzard: https://github.com/isaacs/promzard
----------
Copyright (c) Isaac Z. Schlueter ("Author")
@@ -752,6 +762,38 @@ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISE
OF THE POSSIBILITY OF SUCH DAMAGE.
----------
D3: http://d3js.org/
----------
Copyright (c) 2012, Michael Bostock
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* The name Michael Bostock may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL MICHAEL BOSTOCK BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
=============
Public Domain
@@ -841,6 +883,36 @@ By Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/)
0. You just DO WHAT THE FUCK YOU WANT TO.
----------
node-stream-buffer: https://github.com/samcday/node-stream-buffer
----------
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>
----------
mongodb: http://www.mongodb.org/
----------

View File

@@ -3,10 +3,10 @@
<h1 id="api">The Meteor API</h1>
Your Javascript code can run in two environments: the *client*
(browser), and the *server* (a Node.js container on a server). For
each function in this API reference, we'll indicate if the function is
available just on the client, just on the server, or *Anywhere*.
Your Javascript code can run in two environments: the *client* (browser), and
the *server* (a [Node.js](http://nodejs.org/) container on a server). For each
function in this API reference, we'll indicate if the function is available just
on the client, just on the server, or *Anywhere*.
<h2 id="core"><span>Meteor Core</span></h2>
@@ -47,7 +47,14 @@ will publish that cursor's documents.
// server: publish the rooms collection, minus secret info.
Meteor.publish("rooms", function () {
return Rooms.find({}, {fields: {secretInfo: false}});
return Rooms.find({}, {fields: {secretInfo: 0}});
});
// ... and publish secret info for rooms where the logged-in user
// is an admin. If the client subscribes to both streams, the records
// are merged together into the same documents in the Rooms collection.
Meteor.publish("adminSecretInfo", function () {
return Rooms.find({admin: this.userId}, {fields: {secretInfo: 1}});
});
Otherwise, the publish function can [`set`](#publish_set) and
@@ -108,6 +115,11 @@ project that includes the `autopublish` package. Your publish function
will still work.
{{/warning}}
{{> api_box subscription_userId}}
This is constant. However, if the logged-in user changes, the publish
function is rerun with the new value.
{{> api_box subscription_set}}
{{> api_box subscription_unset}}
{{> api_box subscription_complete}}
@@ -120,11 +132,6 @@ is the place to stop the observes.
{{> api_box subscription_stop}}
{{> api_box subscription_userId}}
This is constant. However, if the logged-in user changes, the publish
function is rerun with the new value.
{{> api_box subscribe}}
When you subscribe to a record set, it tells the server to send records
@@ -150,9 +157,9 @@ attribute.)
If all of the attributes in a document are removed, Meteor
will remove the (now empty) document. If you want to publish empty
documents, just use a placeholder attribute.
documents, just use a placeholder attribute:
// Clicks.insert({exists: true});
Clicks.insert({exists: true});
{{> api_box autosubscribe}}
@@ -206,7 +213,7 @@ object, which provides the following:
* `isSimulation`: a boolean value, true if this invocation is a stub.
* `unblock`: when called, allows the next method from this client to
begin running.
* `userId`: a function that returns the id of the current user.
* `userId`: the id of the current user.
* `setUserId`: a function that associates the current client with a user.
Calling `methods` on the client defines *stub* functions associated with
@@ -225,9 +232,9 @@ exception it will be logged to the console.
{{> api_box method_invocation_userId}}
The user id is an arbitrary string &mdash; typically the id of the user
record in the database. You can set it with the `setUserId` function. If
you're using the Meteor accounts system then this is handled for you.
The user id is an arbitrary string &mdash; typically the id of the user record
in the database. You can set it with the `setUserId` function. If you're using
the [Meteor accounts system](#accounts_api) then this is handled for you.
{{> api_box method_invocation_setUserId}}
@@ -236,9 +243,9 @@ connection that made this method call. This simply sets the value of
`userId` for future method calls received on this connection. Pass
`null` to log out the connection.
If you are using the built-in Meteor accounts system then this should correspond
to the `_id` field of a document in the [`Meteor.users`](#meteor_users)
collection.
If you are using the [built-in Meteor accounts system](#accounts_api) then this
should correspond to the `_id` field of a document in the
[`Meteor.users`](#meteor_users) collection.
`setUserId` is not retroactive. It affects the current method call and
any future method calls on the connection. Any previous method calls on
@@ -268,7 +275,7 @@ This is how to invoke a method. It will run the method on the server.
If a stub is available, it will also run the stub on the client.
If you include a callback function as the last argument (which can't be
an argument to the method, since functions aren't serializeable), the
an argument to the method, since functions aren't serializable), the
method will run asynchronously: it will return nothing in particular and
will not throw an exception. When the method is complete (which may or
may not happen before `Meteor.call` returns), the callback will be
@@ -312,8 +319,9 @@ only to the server.)
{{> api_box meteor_apply}}
`Meteor.apply` is just like `Meteor.call`, but it allows the
arguments to be passed as an array.
`Meteor.apply` is just like `Meteor.call`, except that the method arguments are
passed as an array rather than directly as arguments, and you can specify
options about how the client executes the method.
<h2 id="connections"><span>Server connections</span></h2>
@@ -382,7 +390,7 @@ sets, call `Meteor.connect` with the URL of the application.
Get the current connection status. See
[Meteor.status](#meteor_status).
* `reconnect` -
See <a href="meteor_reconnect">Meteor.reconnect</a>.
See [Meteor.reconnect](#meteor_reconnect).
* `onReconnect` - Set this to a function to be called as the first step of
reconnecting. This function can call methods which will be executed before
any other outstanding methods. For example, this can be used to re-establish
@@ -444,7 +452,8 @@ Specifically, when you pass a `name`, here's what happens:
* On the server, a collection with that name is created on a backend
Mongo server. When you call methods on that collection on the server,
they translate directly into normal Mongo operations.
they translate directly into normal Mongo operations (after checking that
they match your [access control rules](#allow)).
* On the client, a Minimongo instance is
created. Minimongo is essentially an in-memory, non-persistent
@@ -471,13 +480,8 @@ the package:
$ meteor remove autopublish
{{#warning}}
Currently the client is given full write access to the collection. They
can execute arbitrary Mongo update commands. Once we build
authentication, you will be able to limit the client's direct access to
insert, update, and remove. We are also considering validators and
other ORM-like functionality.
{{/warning}}
and instead call [`Meteor.publish`](#meteor_publish) to specify which parts of
your collection should be published to which users.
// Create a collection called Posts and put a document in it. The
// document will be immediately visible in the local copy of the
@@ -546,8 +550,8 @@ those changes may or may not appear in the result set.
Cursors are a reactive data source. The first time you retrieve a
cursor's documents with `fetch`, `map`, or `forEach` inside a
reactive context (eg, [`Meteor.render`](#meteor_render),
[`Meteor.autosubscribe`](#meteor_autosubscribe), Meteor will register a
reactive context (eg, [`Meteor.render`](#meteor_render) or
[`Meteor.autosubscribe`](#meteor_autosubscribe)), Meteor will register a
dependency on the underlying data. Any change to the collection that
changes the documents in a cursor will trigger a recomputation. To
disable this behavior, pass `{reactive: false}` as an option to
@@ -589,10 +593,9 @@ Example:
{{> api_box update}}
Modify documents that match `selector` as
given by `modifier` (see <a href="#modifiers">modifier
documentation</a>). By default, modify only one matching document.
If `multi` is true, modify all matching documents.
Modify documents that match `selector` as given by `modifier` (see [modifier
documentation](#modifiers)). By default, modify only one matching document. If
`multi` is true, modify all matching documents.
Instead of a selector, you can pass a string, which will be
interpreted as an `_id`.
@@ -656,7 +659,7 @@ Example:
{{> api_box allow}}
When a client calls `insert`, `update`, or `remove` on a collection, the
collection's `allow` and <a href='#deny'>`deny`</a> callbacks are called
collection's `allow` and [`deny`](#deny) callbacks are called
on the server to determine if the write should be allowed. If at least
one `allow` callback allows the write, and no `deny` callbacks deny the
write, then the write is allowed to proceed.
@@ -718,7 +721,7 @@ actually needed by your functions. This is enabled by the `fetch`
option. Set `fetch` to an array of the field names that should be
retrieved.
Example: XXX test me!
Example:
// Create a collection where users can only modify documents that
// they own. Ownership is tracked by an 'owner' field on each
@@ -772,12 +775,12 @@ having the clients call `insert`, `update`, and `remove` directly on the
collection.
Meteor also has a special "insecure mode" for quickly prototyping new
applications. In insecure mode, if you haven't set up any `allow` or
`deny` rules on a collection, then all users have full write access to
the collection. This is the only effect of insecure mode. If you call
`allow` or `deny` at all, even `allow({})`, then access is checked just
like normal. __New Meteor projects start in insecure mode by default.__ To
turn it off just type `meteor remove insecure`.
applications. In insecure mode, if you haven't set up any `allow` or `deny`
rules on a collection, then all users have full write access to the
collection. This is the only effect of insecure mode. If you call `allow` or
`deny` at all on a collection, even `Posts.allow({})`, then access is checked
just like normal on that collection. __New Meteor projects start in insecure
mode by default.__ To turn it off just type `meteor remove insecure`.
{{#note}}
For `update` and `remove`, documents will be affected only if they match
@@ -790,7 +793,7 @@ and checked by allow and deny)]}}]}`.
{{> api_box deny}}
This works just like <a href='#allow'>`allow`</a>, except it lets you
This works just like [`allow`](#allow), except it lets you
make sure that certain writes are definitely denied, even if there is an
`allow` rule that says that they should be permitted.
@@ -943,7 +946,8 @@ But they can also contain more complicated tests:
// Matches documents where fruit is one of three possibilities
{fruit: {$in: ["peach", "plum", "pear"]}}
See the <a href="http://www.mongodb.org/display/DOCS/Advanced+Queries" target="_blank">complete documentation.</a>
See the [complete
documentation](http://www.mongodb.org/display/DOCS/Advanced+Queries).
{{/api_box_inline}}
@@ -959,14 +963,17 @@ place by changing some of its fields. Some examples:
// 'supporters' array
{$inc: {votes: 2}, $push: {supporters: "Traz"}}
But if a modifier doesn't contain any $-operators, then it is
instead interpreted as a literal document, and completely replaces
whatever was previously in the database.
But if a modifier doesn't contain any $-operators, then it is instead
interpreted as a literal document, and completely replaces whatever was
previously in the database. (Literal document modifiers are not currently
supported by [validated updates](#allow).)
// Find the document with id "123", and completely replace it.
Users.update({_id: "123"}, {name: "Alice", friends: ["Bob"]});
See the <a href="http://www.mongodb.org/display/DOCS/Updating#Updating-ModifierOperations" target="_blank">full list of modifiers.</a>
See the [full list of
modifiers](http://www.mongodb.org/display/DOCS/Updating#Updating-ModifierOperations)
full list of modifiers.
{{/api_box_inline}}
@@ -1101,6 +1108,359 @@ For object and array session values, you cannot use `Session.equals`; instead,
you need to use the `underscore` package and write
`_.isEqual(Session.get(key), value)`.
<h2 id="accounts_api"><span>Accounts</span></h2>
XXX intro text
{{> api_box user}}
Retreives the user record for the current user from
the [`Meteor.users`](#meteor_users) collection.
On the client this will be a subset of the fields in the document, only
those that are published from the server are available on the client. By
default the server publishes `username`, `emails`, and
`profile`. See [`Meteor.users`](#meteor_users) for more on
the fields used in user documents.
If the user is logged in but the user's database record is not fully
loaded yet, this returns an object with only the `_id` field set. During
this period [`userLoaded`](#meteor_userloaded) will return
`false`.
{{> api_box userId}}
{{> api_box users}}
This collection contains one document per registered user. Here's an example
user document:
{
_id: "bbca5d6a-2156-41c4-89da-0329e8c99a4f", // Meteor.userId()
username: "cool_kid_13", // unique name
emails: [
// each email address can only belong to one user.
{ address: "cool@example.com", verified: true },
{ address: "another@different.com", verified: false }
],
profile: {
// The profile is writable by the user by default.
name: "Joe Schmoe"
},
services: {
facebook: {
id: "709050", // facebook id
accessToken: "AAACCgdX7G2...AbV9AZDZD"
},
resume: {
loginTokens: [
{ token: "97e8c205-c7e4-47c9-9bea-8e2ccc0694cd",
when: 1349761684048 }
]
}
}
}
A user document can contain any data you want to store about a user. Meteor
treats the following fields specially:
- `username`: a unique String identifying the user.
- `emails`: an Array of Objects with keys `address` and `verified`;
an email address may belong to at most one user. `verified` is
a Boolean which is true if the user has [verified the
address](#accounts_verifyemail) with a token sent over email.
- `profile`: an Object which (by default) the user can create
and update with any data.
- `services`: an Object containing data used by particular
login services. For example, its `reset` field contains
tokens used by [forgot password](#accounts_forgotpassword) links,
and its `resume` field contains tokens used to keep you
logged in between sessions.
Like all [Meteor.Collection](#collections)s, you can access all
documents on the server, but only those specifically published by the server are
available on the client.
By default, the current user's `username`, `emails` and `profile` are
published to the client. You can publish additional fields for the
current user with:
Meteor.publish("userData", function () {
return Meteor.users.find({_id: this.userId},
{fields: {'other': 1, 'things': 1}});
});
If the `autopublish` package is installed, the `username` and `profile` fields
for all users are published to all clients. To publish specific fields from all
users:
Meteor.publish("allUserData", function () {
return Meteor.users.find({}, {fields: {'nested.things': 1}});
});
Users are by default allowed to specify their own `profile` field with
[`Accounts.createUser`](#accounts_createuser) and modify it with
`Meteor.users.update`. To allow users to edit additional fields, use
[`Meteor.users.allow`](#allow). To forbid users from making any modifications to
their user document:
Meteor.users.deny({update: function () { return true; }});
{{> api_box userLoaded}}
There are some cases when the client knows the id of the logged in user
but has not yet received the user data from the server. For example, if
the user is logged in and reloads the page the user data will be
unavailable during initial page load.
During these periods, `userLoaded` will return false
and [`user`](#meteor_user) will return an object with only
the `_id` key.
{{#note}}
We realize this is inconvenient. It is a temporary solution. In the
future we will either make it unnecessary or fold it into a more
general mechanism.
{{/note}}
{{> api_box logout}}
{{> api_box loginWithPassword}}
XXX needs link to passwords section and mention the package
{{> api_box loginWithExternalService}}
These functions initiate the login process with an external
service (eg: Facebook, Google, etc), using OAuth. When called they open a new pop-up
window that loads the provider's login page. Once the user has logged in
with the provider, the pop-up window is closed and the Meteor client
logs in to the Meteor server with the information provided by the external
service.
<a id="requestpermissions" name="requestpermissions" />
If the user has not already granted all the permissions requested they will be
prompted to grant access to their account in the pop-up dialog. Values for the
`requestPermissions` parameter differ for each login service:
- Facebook: <http://developers.facebook.com/docs/authentication/permissions/>
- GitHub: <http://developer.github.com/v3/oauth/#scopes>
- Google: <https://developers.google.com/accounts/docs/OAuth2Login#scopeparameter>
- Twitter, Weibo: `requestPermissions` currently not supported
XXX mention provider packages
{{> api_box accounts_config}}
{{> api_box accounts_ui_config}}
Example:
Accounts.ui.config({
requestPermissions: {
facebook: ['user_likes'],
github: ['user', 'repo']
},
passwordSignupFields: 'USERNAME_AND_OPTIONAL_EMAIL'
});
{{> api_box accounts_validateNewUser}}
This can be called multiple times. If any of the functions return `false` or
throw an error, the new user creation is aborted. To set a specific error
message (which will be displayed by [`accounts-ui`](#accountsui)), throw a new
[`Meteor.Error`](#meteor_error).
Example:
// Validate username, sending a specific error message on failure.
Accounts.validateNewUser(function (user) {
if (user.username && user.username.length >= 3)
return true;
throw new Meteor.Error(403, "Username must have at least 3 characters");
});
// Validate username, without a specific error message.
Accounts.validateNewUser(function (user) {
return user.username !== "root";
});
{{> api_box accounts_onCreateUser}}
Use this when you need to do more than simply accept or reject new user
creation. With this function you can programatically control the
contents of new user documents.
The function you pass will be called with two arguments: `options` and
`user`. The `options` argument comes
from [`Accounts.createUser`](#accounts_createuser) for
password-based users or from an external service login flow. `options` may come
from an untrusted client so make sure to validate any values you read from
it. The `user` argument is created on the server and contains a
proposed user object with all the automatically generated fields
required for the user to log in.
The function should return the user document (either the one passed in or a
newly-created object) with whatever modifications are desired. The returned
document is inserted directly into the [`Meteor.users`](#meteor_users) collection.
The default create user function simply copies `options.profile` into
the new user document. Calling `onCreateUser` overrides the default
hook. This can only be called once.
Example:
<!-- XXX replace d6 with _.random once we have underscore 1.4.2 -->
// Support for playing D&D: Roll 3d6 for dexterity
Accounts.onCreateUser(function(options, user) {
var d6 = function () { return Math.floor(Math.random() * 6) + 1; };
user.dexterity = d6() + d6() + d6();
// We still want the default hook's 'profile' behavior.
if (options.profile)
user.profile = options.profile;
return user;
});
<h2 id="accounts_passwords"><span>Passwords</span></h2>
The `accounts-password` package implements a complete system for
password based authentication. In addition to the basic username and
password based sign-in process it also supports email based sign-in
including address verification and password recovery emails.
Unlike most web applications, the Meteor client does not send the user's
password directly to the server. It uses the [Secure Remote Password
protocol](http://en.wikipedia.org/wiki/Secure_Remote_Password_protocol)
to ensure the server never sees the user's plain-text password. This
helps protect against embarrassing password leaks if the server's
database is compromised.
To add password support to your application, run `$ meteor add
accounts-password`. You can construct your own user interface using the
functions below, or use the [`accounts-ui` package](#accountsui) to
include a turn-key user interface for password-based sign-in.
{{> api_box accounts_createUser}}
On the client this function logs in as the newly created user on
successful completion. On the server, it returns the newly created user
id.
On the client, you must pass `password` and one of `username` or `email`
&mdash; enough information for the user to be able to log in again
later. On the server, you can pass any subset of these options, but the
user will not be able to log in until it has an identifier and a
password.
To create an account without a password on the server and still let the
user pick their own password, call `createUser` with the `email` option
and then
call [`Accounts.sendEnrollmentEmail`](#accounts_sendenrollmentemail). This
will send the user an email with a link to set their initial password.
By default the `profile` option is added directly to the new user document. To
override this behavior, use [`Accounts.onCreateUser`](#accounts_createuser).
This function is only used for creating users with passwords. The external
service login flows do not use this function.
{{> api_box accounts_changePassword}}
{{> api_box accounts_forgotPassword}}
This triggers a call
to [`Accounts.sendResetPasswordEmail`](#accounts_sendresetpasswordemail)
on the server. Pass the token the user receives in this email
to [`Accounts.resetPassword`](#accounts_resetpassword) to
complete the password reset process.
If you are using the [`accounts-ui` package](#pkg_accounts_ui), this is handled
automatically. Otherwise, it is your responsiblity to prompt the user for the
new password and call `resetPassword`.
{{> api_box accounts_resetPassword}}
This function accepts tokens generated
by [`Accounts.sendResetPasswordEmail`](#accounts_sendresetpasswordemail)
and
[`Accounts.sendEnrollmentEmail`](#accounts_sendenrollmentemail).
{{> api_box accounts_setPassword}}
{{> api_box accounts_verifyEmail}}
This function accepts tokens generated
by [`Accounts.sendVerificationEmail`](#accounts_sendverificationemail). It
sets the `emails.verified` field in the user record.
{{> api_box accounts_sendResetPasswordEmail}}
The token in this email should be passed
to [`Accounts.resetPassword`](#accounts_resetpassword).
To customize the contents of the email, see
[`Accounts.emailTemplates`](#accounts_emailtemplates).
{{> api_box accounts_sendEnrollmentEmail}}
The token in this email should be passed
to [`Accounts.resetPassword`](#accounts_resetpassword).
To customize the contents of the email, see
[`Accounts.emailTemplates`](#accounts_emailtemplates).
{{> api_box accounts_sendVerificationEmail}}
The token in this email should be passed
to [`Accounts.verifyEmail`](#accounts_verifyemail).
To customize the contents of the email, see
[`Accounts.emailTemplates`](#accounts_emailtemplates).
{{> api_box accounts_emailTemplates}}
This is an `Object` with several fields that are used to generate text
for the emails by `sendResetPasswordEmail`, `sendEnrollmentEmail`, and
`sendVerificationEmail`.
Override fields of the object by assigning to them:
- `from`: A `String` with an [RFC5322](http://tools.ietf.org/html/rfc5322) From
address. By default email is from `no-reply@meteor.com`. If you wish to
receive email from users asking for help with their account, be sure to set
this to an email address that you can receive email at.
- `siteName`: The public name of your application. Defaults to the DNS name of
the application (eg: `awesome.meteor.com`).
- `resetPassword`: An `Object` with two fields:
- `resetPassword.subject`: A `Function` that takes a user object and returns
a `String` for the subject line of a reset password email.
- `resetPassword.text`: A `Function` that takes a user object and a url, and
returns the body text for a reset password email.
- `enrollAccount`: Same as `resetPassword`, but for initial password setup for
new accounts.
- `verifyEmail`: Same as `resetPassword`, but for verifying the users email
address.
Example:
Accounts.emailTemplates.siteName = "AwesomeSite";
Accounts.emailTemplates.from = "AwesomeSite Admin <accounts@example.com>";
Accounts.emailTemplates.enrollAccount.subject = function (user) {
return "Welcome to Awesome Town, " + user.profile.name;
};
Accounts.emailTemplates.enrollAccount.text = function (user, url) {
return "You have been selected to participate in building a better future!"
+ " To activate your account, simply click the link below:\n\n"
+ url;
};
<h2 id="templates_api"><span>Templates</span></h2>
A template that you declare as `<{{! }}template name="foo"> ... </{{!
@@ -1639,306 +1999,6 @@ sub-template.
{{/api_box_inline}}
<h2 id="accounts_api"><span>Accounts</span></h2>
XXX intro text
{{> api_box user}}
Retreives the user record for the current user from
the [`Meteor.users`](#meteor_users) collection.
On the client this will be a subset of the fields in the document, only
those that are published from the server are available on the client. By
default the server publishes `username`, `emails`, and
`profile`. See [`Meteor.users`](#meteor_users) for more on
the fields used user documents.
If the user is logged in but the user's database record is not fully
loaded yet, this returns an object with only the `_id` field set. During
this period [`userLoaded`](#meteor_userloaded) will return
`false`.
{{> api_box userId}}
{{> api_box users}}
This collection contains one document per user. Example user document:
{
_id: "bbca5d6a-2156-41c4-89da-0329e8c99a4f" // userId
username: "cool_kid_13", // unique name
emails: [
// each email address can only belong to one user.
{ address: "cool@example.com", verified: true },
{ address: "another@different.com", verified: false }
],
profile: {
// The profile is writable by the user by default.
name: "Joe Schmoe"
},
services: {
facebook: {
id: "709050", // facebook id
accessToken: "AAACCgdX7G2...AbV9AZDZD"
},
resume: {
loginTokens: [
{ token: "97e8c205-c7e4-47c9-9bea-8e2ccc0694cd",
when: 1349761684048 }
]
}
}
}
Like all <a href='#collections'>Meteor.Collection</a>s, you can access all
documents on the server, but only those specifically published by the server are
available on the client.
By default, the current user's `username`, `emails` and `profile` are
published to the client. You can publish additional fields for the
current user with:
Meteor.publish("userData", function () {
return Meteor.users.find({_id: this.userId},
{fields: {'other': 1, 'things': 1}});
});
If the `autopublish` package is installed, the `username` and `profile` fields
for all users are published to all clients. To publish specific fields from all
users:
Meteor.publish("allUserData", function () {
return Meteor.users.find({}, {fields: {'nested.things': 1}});
});
Users are by default allowed to specify their own `profile` field with
[`Accounts.createUser`](#accounts_createuser) and modify it with
`Meteor.users.update`. To allow users to edit additional fields, use
[`Meteor.users.allow`](#allow). To forbid users from making any modifications to
their user document:
Meteor.users.deny({update: function () { return true; }});
{{> api_box userLoaded}}
There are some cases when the client knows the id of the logged in user
but has not yet received the user data from the server. For example, if
the user is logged in and reloads the page the user data will be
unavailable during initial page load.
During these periods, `userLoaded` will return false
and [`user`](#meteor_user) will return an object with only
the `_id` key.
{{#note}}
We realize this is inconvenient. It is a temporary solution. In the
future we will either make it unnecessary or fold it into a more
general mechanism.
{{/note}}
{{> api_box logout}}
{{> api_box loginWithPassword}}
{{> api_box loginWithOAuth}}
These functions initiate the login process with a third party OAuth
provider (eg: Facebook, Google, etc). When called they open a new pop-up
window that loads the provider's login page. Once the user has logged in
with the provider, the pop-up window is closed and the Meteor client
logs in to the Meteor server with the information provided by the OAuth
provider.
If the user has not already granted all the permissions requested they will be
prompted to grant access to their account in the pop-up dialog. Values for the
`requestPermissions` parameter differ for each login service:
- Facebook: <a href="http://developers.facebook.com/docs/authentication/permissions/">http://developers.facebook.com/docs/authentication/permissions/</a>
- GitHub: <a href="http://developer.github.com/v3/oauth/#scopes">http://developer.github.com/v3/oauth/#scopes</a>
- Google: <a href="https://developers.google.com/accounts/docs/OAuth2Login#scopeparameter">https://developers.google.com/accounts/docs/OAuth2Login#scopeparameter</a>
Currently, `loginWithTwitter` and `loginWithWeibo` do not support
`requestPermissions`.
{{> api_box accounts_createUser}}
On the client this function logs in as the newly created user on
successful completion. On the server, it returns the newly created user
id.
On the client, you must pass `password` and one of `username` or `email`
&mdash; enough information for the user to be able to log in again
later. On the server, you can pass any subset of these options, but the
user will not be able to log in until it has an identifier and a
password.
To create an account without a password on the server and still let the
user pick their own password, call `createUser` with the `email` option
and then
call [`Accounts.sendEnrollmentEmail`](#accounts_sendenrollmentemail). This
will send the user an email with a link to set their initial password.
By default the `profile` option is added directly to the new user document. To
override this behavior, use [`Accounts.onCreateUser`](#accounts_createuser).
This function is only used for creating users with passwords. The OAuth
login flows do not use this function.
{{> api_box accounts_changePassword}}
{{> api_box accounts_forgotPassword}}
This triggers a call
to [`Accounts.sendResetPasswordEmail`](#accounts_sendresetpasswordemail)
on the server. Pass the token the user receives in this email
to [`Accounts.resetPassword`](#accounts_resetpassword) to
complete the password reset process.
If you are using the <a href="#pkg_accounts_ui">`accounts-ui`
package</a>, this is handled automatically. Otherwise, it is your
responsiblity to prompt the user for the new password and call `resetPassword`.
- XXX token goes to `Accounts._resetPasswordToken`.
{{> api_box accounts_resetPassword}}
This function accepts tokens generated
by [`Accounts.sendResetPasswordEmail`](#accounts_sendresetpasswordemail)
and
[`Accounts.sendEnrollmentEmail`](#accounts_sendenrollmentemail)
{{> api_box accounts_setPassword}}
{{> api_box accounts_verifyEmail}}
This function accepts tokens generated
by [`Accounts.sendVerificationEmail`](#accounts_sendverificationemail). It
sets the `emails.verified` field in the user record.
{{> api_box accounts_sendResetPasswordEmail}}
The token in this email should be passed
to [`Accounts.resetPassword`](#accounts_resetpassword).
To customize the contents of the email, see
[`Accounts.emailTemplates`](#accounts_emailtemplates).
{{> api_box accounts_sendEnrollmentEmail}}
The token in this email should be passed
to [`Accounts.resetPassword`](#accounts_resetpassword).
To customize the contents of the email, see
[`Accounts.emailTemplates`](#accounts_emailtemplates).
{{> api_box accounts_sendVerificationEmail}}
The token in this email should be passed
to [`Accounts.verifyEmail`](#accounts_verifyemail).
To customize the contents of the email, see
[`Accounts.emailTemplates`](#accounts_emailtemplates).
{{> api_box accounts_emailTemplates}}
This is an `Object` with several fields that are used to generate text
for the emails by `sendResetPasswordEmail`, `sendEnrollmentEmail`, and
`sendVerificationEmail`.
Override fields of the object by assigning to them:
- `from`: A `String` with an <a href="http://tools.ietf.org/html/rfc5322"
target="_blank">RFC5322</a> From address. By default email is from
`no-reply@meteor.com`. If you wish to receive email from users asking for help
with their account, be sure to set this to an email address that you can receive
email at.
- `siteName`: The public name of your application. Defaults to the DNS name of
the application (eg: `awesome.meteor.com`).
- `resetPassword`: An `Object` with two fields:
- `resetPassword.subject`: A `Function` that takes a user object and returns
a `String` for the subject line of a reset password email.
- `resetPassword.text`: A `Function` that takes a user object and a url, and
returns the body text for a reset password email.
- `enrollAccount`: Same as `resetPassword`, but for initial password setup for
new accounts.
- `verifyEmail`: Same as `resetPassword`, but for verifying the users email
address.
Example:
Accounts.emailTemplates.siteName = "AwesomeSite";
Accounts.emailTemplates.from = "AwesomeSite Admin <accounts@example.com>";
Accounts.emailTemplates.enrollAccount.subject = function (user) {
return "Welcome to Awesome Town, " + user.profile.name;
};
Accounts.emailTemplates.enrollAccount.text = function (user, url) {
return "You have been selected to participate in building better future!"
+ " To activate your account, simply click the link below:\n\n"
+ url;
};
{{> api_box accounts_config}}
{{> api_box accounts_ui_config}}
{{> api_box accounts_validateNewUser}}
This can be called multiple times. If any of the functions return
`false` the new user creation is aborted.
Example:
// All users must have a username longer than 3 characters.
Accounts.validateNewUser(function (user) {
return user.username && user.username.length >= 3;
});
{{> api_box accounts_onCreateUser}}
Use this when you need to do more than simply accept or reject new user
creation. With this function you can programatically control the
contents of new user documents.
The function you pass will be called with two arguments: `options` and
`user`. The `options` argument comes
from [`Accounts.createUser`](#accounts_createuser) for
password-based users or from the OAuth login flow. `options` may come
from an untrusted client so make to validate any values you read from
it. The `user` argument is created on the server and contains a
proposed user object with all the automatically generated fields
required for the user to log in.
The function should return a new user object with whatever modifications
are desired. The returned object is inserted directly into
the [`Meteor.users`](#meteor_users) collection.
The default create user function simply copies `options.profile` into
the new user document. Calling `onCreateUser` overrides the default
hook. This can only be called once.
Example:
// Make members of the GitHub 'meteor' group into admins.
Accounts.onCreateUser(function(options, user) {
// http://developer.github.com/v3/orgs/members/#get-member
if (!Meteor.http.get(
"https://api.github.com/orgs/meteor/members/" +
user.services.github.username +
"?access_token=" + user.services.github.accessToken
).error) {
user.admin = true;
}
return user;
});
<h2 id="timers"><span>Timers</span></h2>
Meteor uses global environment variables
@@ -2297,9 +2357,9 @@ send mail. Currently, Meteor supports sending mail over SMTP; the `MAIL_URL`
environment variable should be of the form
`smtp://USERNAME:PASSWORD@HOST:PORT/`. For apps deployed with `meteor deploy`,
`MAIL_URL` defaults to an account (provided by
<a href="http://www.mailgun.com/" target="_blank">Mailgun</a>) which allows
apps to send up to 200 emails per day; you may override this default by
assigning to `process.env.MAIL_URL` before your first call to `Email.send`.
[Mailgun](http://www.mailgun.com/)) which allows apps to send up to 200 emails
per day; you may override this default by assigning to `process.env.MAIL_URL`
before your first call to `Email.send`.
If `MAIL_URL` is not set (eg, when running your application locally),
`Email.send` outputs the message to standard output instead.

View File

@@ -741,7 +741,7 @@ Template.api.loginWithPassword = {
{
name: "password",
type: "String",
descr: "The user's password. This is __not__ sent in plain text over the wire &mdash; it is secured with <a href='http://en.wikipedia.org/wiki/Secure_Remote_Password_protocol' target='_blank'>SRP</a>."
descr: "The user's password. This is __not__ sent in plain text over the wire &mdash; it is secured with [SRP](http://en.wikipedia.org/wiki/Secure_Remote_Password_protocol)."
},
{
name: "callback",
@@ -752,11 +752,11 @@ Template.api.loginWithPassword = {
};
Template.api.loginWithOAuth = {
id: "meteor_loginwithoauth",
name: "Meteor.loginWith<i>OAuthProvider</i>([options], [callback])",
Template.api.loginWithExternalService = {
id: "meteor_loginwithexternalservice",
name: "Meteor.loginWith<i>ExternalService</i>([options], [callback])",
locus: "Client",
descr: ["Log the user in using an external OAuth service."],
descr: ["Log the user in using an external service."],
args: [
{
name: "callback",
@@ -773,6 +773,76 @@ Template.api.loginWithOAuth = {
]
};
Template.api.accounts_config = {
id: "accounts_config",
name: "Accounts.config(options)",
locus: "Anywhere",
descr: ["Set global accounts options."],
options: [
{
name: "sendVerificationEmail",
type: "Boolean",
descr: "New users with an email address will receive an address verification email."
},
{
name: "forbidClientAccountCreation",
type: "Boolean",
descr: "[`createUser`](#accounts_createuser) requests from the client will be rejected."
}
]
};
Template.api.accounts_ui_config = {
id: "accounts_ui_config",
name: "Accounts.ui.config(options)",
locus: "Client",
descr: ["Configure the behavior of [`{{loginButtons}}`](#accountsui)."],
options: [
{
name: "requestPermissions",
type: "Object",
descr: "Which [permissions](#requestpermissions) to request from the user for each external service."
},
{
name: "passwordSignupFields",
type: "String",
descr: "Which fields to display in the user creation form. One of '`USERNAME_AND_EMAIL`', '`USERNAME_AND_OPTIONAL_EMAIL`', '`USERNAME_ONLY`', or '`EMAIL_ONLY`' (default)."
}
]
};
Template.api.accounts_validateNewUser = {
id: "accounts_validatenewuser",
name: "Accounts.validateNewUser(func)",
locus: "Server",
descr: ["Set restrictions on new user creation."],
args: [
{
name: "func",
type: "Function",
descr: "Called whenever a new user is created. Takes the new user object, and returns true to allow the creation or false to abort."
}
]
};
Template.api.accounts_onCreateUser = {
id: "accounts_oncreateuser",
name: "Accounts.onCreateUser(func)",
locus: "Server",
descr: ["Customize new user creation."],
args: [
{
name: "func",
type: "Function",
descr: "Called whenever a new user is created. Return the new user object, or throw an `Error` to abort the creation."
}
]
};
Template.api.accounts_createUser = {
id: "accounts_createuser",
name: "Accounts.createUser(options, [callback])",
@@ -985,75 +1055,6 @@ Template.api.accounts_emailTemplates = {
Template.api.accounts_config = {
id: "accounts_config",
name: "Accounts.config(options)",
locus: "Anywhere",
descr: ["Set global accounts options."],
options: [
{
name: "sendVerificationEmail",
type: "Boolean",
descr: "New users with an email address will receive an address verification email."
},
{
name: "forbidClientAccountCreation",
type: "Boolean",
descr: "[`createUser`](#accounts_createuser) requests from the client will be rejected."
}
]
};
Template.api.accounts_ui_config = {
id: "accounts_ui_config",
name: "Accounts.ui.config(options)",
locus: "Client",
descr: ["Set Accounts UI options for the `loginButtons` template."],
options: [
{
name: "requestPermissions",
type: "Object",
descr: "Which permissions to request from the user for each OAuth service. For example: `{facebook: ['user_likes'], github: ['user', 'repo']}`"
},
{
name: "passwordSignupFields",
type: "String",
descr: "Which fields to display in the user creation form. One of '`USERNAME_AND_EMAIL`', '`USERNAME_AND_OPTIONAL_EMAIL`', '`USERNAME_ONLY`', or '`EMAIL_ONLY`' (default)."
}
]
};
Template.api.accounts_validateNewUser = {
id: "accounts_validatenewuser",
name: "Accounts.validateNewUser(func)",
locus: "Server",
descr: ["Set restrictions on new user creation."],
args: [
{
name: "func",
type: "Function",
descr: "Called whenever a new user is created. Takes the new user object, and returns true to allow the creation or false to abort."
}
]
};
Template.api.accounts_onCreateUser = {
id: "accounts_oncreateuser",
name: "Accounts.onCreateUser(func)",
locus: "Server",
descr: ["Customize new user creation."],
args: [
{
name: "func",
type: "Function",
descr: "Called whenever a new user is created. Return the new user object, or throw an `Error` to abort the creation."
}
]
};
Template.api.setTimeout = {
id: "meteor_settimeout",
name: "Meteor.setTimeout",
@@ -1402,8 +1403,7 @@ Template.api.template_data = {
};
var rfc = function (descr) {
return ('<a href="http://tools.ietf.org/html/rfc5322" target="_blank">RFC5322'
+ '</a> ' + descr);
return '[RFC5322](http://tools.ietf.org/html/rfc5322) ' + descr;
};
Template.api.email_send = {

View File

@@ -22,12 +22,12 @@ when writing those apps.
<h2 id="structuringyourapp">Structuring your application</h2>
A Meteor application is a mix of JavaScript that runs inside a
client web browser, JavaScript that runs on the Meteor server inside
a Node.js container, and all the supporting HTML fragments, CSS rules,
and static assets. Meteor automates the packaging and transmission
of these different components. And, it is quite flexible about how
you choose to structure those components in your file tree.
A Meteor application is a mix of JavaScript that runs inside a client web
browser, JavaScript that runs on the Meteor server inside a
[Node.js](http://nodejs.org/) container, and all the supporting HTML fragments,
CSS rules, and static assets. Meteor automates the packaging and transmission
of these different components. And, it is quite flexible about how you choose
to structure those components in your file tree.
The only server asset is JavaScript. Meteor gathers all your JavaScript
files, excluding anything under the `client`
@@ -108,8 +108,7 @@ your client model code.
Meteor's protocol for distributing document updates is database
agnostic. By default, Meteor applications use the
familiar <a target="_blank"
href="http://www.mongodb.org/display/DOCS/Manual">MongoDB API</a>:
familiar [MongoDB API](http://www.mongodb.org/display/DOCS/Manual):
servers store documents in MongoDB collections, and clients cache those
documents in a client-side cache that implements the same Mongo API for
queries and updates.
@@ -149,14 +148,8 @@ server-side database driver and/or a client-side cache that implements
an alternative API. The `mongo-livedata` is a good starting point for
such a project.
{{#note}}
A pre-release version of Meteor includes a user login system and a set of tools
for securing read and write access to data based on the logged-in user. For more
information, see the
<a target="_blank"
href="https://github.com/meteor/meteor/wiki/Getting-Started-with-Auth">Getting
Started with Auth</a> wiki page.
{{/note}}
XXX should we mention security/auth at all here, or just expect folks to keep
reading until the accounts section?
{{/better_markdown}}
</template>
@@ -166,11 +159,10 @@ Started with Auth</a> wiki page.
<h2 id="reactivity">Reactivity</h2>
Meteor embraces the concept of
<a target="_blank" href="http://en.wikipedia.org/wiki/Reactive_programming">
reactive programming</a>. This means that you can write your code in a
simple imperative style, and the result will be automatically
recalculated whenever data changes that your code depends on.
Meteor embraces the concept of [reactive
programming](http://en.wikipedia.org/wiki/Reactive_programming). This means that
you can write your code in a simple imperative style, and the result will be
automatically recalculated whenever data changes that your code depends on.
Meteor.autosubscribe(function () {
Meteor.subscribe("messages", Session.get("currentRoomId"));
@@ -214,10 +206,11 @@ And the reactive data sources that can trigger changes are:
* [`Meteor.userId`](#meteor_userid)
* [`Meteor.userLoaded`](#meteor_userloaded)
Meteor's <a href="https://github.com/meteor/meteor/blob/master/packages/deps/deps.js" target="_blank">implementation</a>
of reactivity is short and sweet, about 50 lines of code. You can
hook into it yourself to add new reactive contexts or data sources,
using the [`Meteor.deps`](#meteor_deps) module.
Meteor's
[implementation](https://github.com/meteor/meteor/blob/master/packages/deps/deps.js)
of reactivity is short and sweet, about 50 lines of code. You can hook into it
yourself to add new reactive contexts or data sources, using the
[`Meteor.deps`](#meteor_deps) module.
{{/better_markdown}}
</template>
@@ -267,15 +260,14 @@ the new elements using a library like jQuery. In that case, call
[`Meteor.flush`](#meteor_flush) to bring the DOM up to date
immediately.
When live-updating DOM elements are taken off the screen, they are
automatically cleaned up &mdash; their callbacks are torn down, any
associated database queries are stopped, and they stop updating. For
this reason, you never have to worry about
the <a href="http://lostechies.com/derickbailey/2011/09/15/zombies-run-managing-page-transitions-in-backbone-apps/"
target="_blank">zombie templates</a> that plague hand-written update
logic. To protect your elements from cleanup, just make sure that they
on-screen before your code returns to the event loop, or before any
call you make to [`Meteor.flush`](#meteor_flush).
When live-updating DOM elements are taken off the screen, they are automatically
cleaned up &mdash; their callbacks are torn down, any associated database
queries are stopped, and they stop updating. For this reason, you never have to
worry about the [zombie
templates](http://lostechies.com/derickbailey/2011/09/15/zombies-run-managing-page-transitions-in-backbone-apps/)
that plague hand-written update logic. To protect your elements from cleanup,
just make sure that they on-screen before your code returns to the event loop,
or before any call you make to [`Meteor.flush`](#meteor_flush).
Another thorny problem in hand-written applications is element
preservation. Suppose the user is typing text into an `<input>`
@@ -313,10 +305,9 @@ available as a function on the global `Template` object.
{{#note}}
Today, the only templating system that has been packaged for Meteor is
Handlebars. Let us know what templating systems you'd like to use with
Meteor. Meanwhile, see
the <a href="http://www.handlebarsjs.com/" target="_blank">Handlebars documentation</a>
and <a href="https://github.com/meteor/meteor/wiki/Handlebars" target="_blank">Meteor
Handlebars extensions</a>.
Meteor. Meanwhile, see the [Handlebars
documentation](http://www.handlebarsjs.com/) and [Meteor Handlebars
extensions](https://github.com/meteor/meteor/wiki/Handlebars).
{{/note}}
A template with a `name` of `hello` is rendered by calling the

View File

@@ -436,11 +436,11 @@ pre {
@media (min-width: 1024px) {
/* ipad landscape and desktop */
#main {
width: 600px;
margin-left: 310px; /* nav width + padding */
width: 610px;
margin-left: 330px; /* nav width + padding */
}
#nav {
width: 250px;
width: 270px;
}
.github-ribbon {
display: block;

View File

@@ -48,18 +48,29 @@ Meteor.startup(function () {
}
});
window.onhashchange = function () {
scrollToSection(location.hash);
};
var scrollToSection = function (section) {
ignore_waypoints = true;
Session.set("section", section.substr(1));
scroller().animate({
scrollTop: $(section).offset().top
}, 500, 'swing', function () {
window.location.hash = section;
ignore_waypoints = false;
});
};
$('#main, #nav').delegate("a[href^='#']", 'click', function (evt) {
evt.preventDefault();
var sel = $(this).attr('href');
ignore_waypoints = true;
Session.set("section", sel.substr(1));
scroller().animate({
scrollTop: $(sel).offset().top
}, 500, 'swing', function () {
window.location.hash = sel;
ignore_waypoints = false;
});
scrollToSection(sel);
});
// Make external links open in a new tab.
$('a:not([href^="#"])').attr('target', '_blank');
});
var toc = [
@@ -89,13 +100,13 @@ var toc = [
"Publish and subscribe", [
"Meteor.publish", [
{instance: "this", name: "userId", id: "publish_userId"},
{instance: "this", name: "set", id: "publish_set"},
{instance: "this", name: "unset", id: "publish_unset"},
{instance: "this", name: "complete", id: "publish_complete"},
{instance: "this", name: "flush", id: "publish_flush"},
{instance: "this", name: "onStop", id: "publish_onstop"},
{instance: "this", name: "stop", id: "publish_stop"},
{instance: "this", name: "userId", id: "publish_userId"}
{instance: "this", name: "stop", id: "publish_stop"}
],
"Meteor.subscribe",
"Meteor.autosubscribe"
@@ -141,7 +152,8 @@ var toc = [
{type: "spacer"},
{name: "Selectors", style: "noncode"},
{name: "Modifiers", style: "noncode"},
{name: "Sort specifiers", style: "noncode"}
{name: "Sort specifiers", style: "noncode"},
{name: "Field specifiers", style: "noncode"}
],
"Session", [
@@ -150,6 +162,41 @@ var toc = [
"Session.equals"
],
{name: "Accounts", id: "accounts_api"}, [
"Meteor.user",
"Meteor.userId",
"Meteor.users",
"Meteor.userLoaded",
"Meteor.logout",
"Meteor.loginWithPassword",
{name: "Meteor.loginWithFacebook", id: "meteor_loginwithexternalservice"},
{name: "Meteor.loginWithGithub", id: "meteor_loginwithexternalservice"},
{name: "Meteor.loginWithGoogle", id: "meteor_loginwithexternalservice"},
{name: "Meteor.loginWithTwitter", id: "meteor_loginwithexternalservice"},
{name: "Meteor.loginWithWeibo", id: "meteor_loginwithexternalservice"},
{type: "spacer"},
"Accounts.config",
"Accounts.ui.config",
"Accounts.validateNewUser",
"Accounts.onCreateUser"
],
{name: "Passwords", id: "accounts_passwords"}, [
"Accounts.createUser",
"Accounts.changePassword",
"Accounts.forgotPassword",
"Accounts.resetPassword",
"Accounts.setPassword",
"Accounts.verifyEmail",
{type: "spacer"},
"Accounts.sendResetPasswordEmail",
"Accounts.sendEnrollmentEmail",
"Accounts.sendVerificationEmail",
"Accounts.emailTemplates"
],
{name: "Templates", id: "templates_api"}, [
{prefix: "Template", instance: "myTemplate", id: "template_call"}, [
{name: "rendered", id: "template_rendered"},
@@ -174,41 +221,6 @@ var toc = [
{name: "Reactivity isolation", style: "noncode", id: "isolate"}
],
{name: "Accounts", id: "accounts_api"}, [
"Meteor.user",
"Meteor.userId",
"Meteor.users",
"Meteor.userLoaded",
"Meteor.logout",
"Meteor.loginWithPassword",
{name: "Meteor.loginWithFacebook", id: "meteor_loginwithoauth"},
{name: "Meteor.loginWithGithub", id: "meteor_loginwithoauth"},
{name: "Meteor.loginWithGoogle", id: "meteor_loginwithoauth"},
{name: "Meteor.loginWithTwitter", id: "meteor_loginwithoauth"},
{name: "Meteor.loginWithWeibo", id: "meteor_loginwithoauth"},
{type: "spacer"},
"Accounts.createUser",
"Accounts.changePassword",
"Accounts.forgotPassword",
"Accounts.resetPassword",
"Accounts.setPassword",
"Accounts.verifyEmail",
{type: "spacer"},
"Accounts.sendResetPasswordEmail",
"Accounts.sendEnrollmentEmail",
"Accounts.sendVerificationEmail",
"Accounts.emailTemplates",
{type: "spacer"},
"Accounts.config",
"Accounts.ui.config",
"Accounts.validateNewUser",
"Accounts.onCreateUser"
],
"Timers", [
"Meteor.setTimeout",
"Meteor.setInterval",

View File

@@ -39,7 +39,8 @@ our thinking. We'd love to hear your feedback.
<!-- change colors on these. $ and command output in grey, rest in
white -->
The following works on all <a target="_blank" href="https://github.com/meteor/meteor/wiki/Supported-Platforms">supported platforms</a>.
The following works on all [supported
platforms](https://github.com/meteor/meteor/wiki/Supported-Platforms).
Install Meteor:
@@ -106,25 +107,24 @@ clean, classically beautiful APIs.
<h2 id="resources">Developer Resources</h2>
<!-- https://github.com/blog/273-github-ribbons -->
<a href="http://github.com/meteor/meteor" target="_blank"><img class="github-ribbon visible-desktop" style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub"></a>
<a href="http://github.com/meteor/meteor"><img class="github-ribbon visible-desktop" style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub"></a>
If anything in Meteor catches your interest, we hope you'll get involved
with the project!
<dl class="involved">
<dt><span>Stack Overflow</span></dt>
<dd>The best place to ask (and answer!) technical questions is
on <a href="http://stackoverflow.com/questions/tagged/meteor" target="_blank">Stack
Overflow</a>. Be sure to add the <code>meteor</code> tag to your
question.
<dd>The best place to ask (and answer!) technical questions is on [Stack
Overflow](http://stackoverflow.com/questions/tagged/meteor). Be sure to add
the <code>meteor</code> tag to your question.
</dd>
<dt><span>Mailing lists</span></dt>
<dd>
We have two mailing lists for Meteor. <nobr><a href="http://groups.google.com/group/meteor-talk" target="_blank"><code>meteor-talk@googlegroups.com</code></a></nobr>
We have two mailing lists for Meteor. <nobr><a href="http://groups.google.com/group/meteor-talk"><code>meteor-talk@googlegroups.com</code></a></nobr>
is for general questions, requests for help, and new project
announcements.
<nobr><a href="http://groups.google.com/group/meteor-core" target="_blank"><code>meteor-core@googlegroups.com</code></a></nobr>
<nobr><a href="http://groups.google.com/group/meteor-core"><code>meteor-core@googlegroups.com</code></a></nobr>
is for discussing Meteor internals and proposed changes.
</dd>
@@ -134,7 +134,7 @@ developers hang out here and will answer your questions whenever they
can.</dd>
<dt><span>GitHub</span></dt>
<dd>The code is on <a target="_blank" href="http://github.com/meteor/meteor">GitHub</a>. The best way to send a patch is with a GitHub pull request, and the best way to file a bug is in the GitHub bug tracker.</dd>
<dd>The code is on <a href="http://github.com/meteor/meteor">GitHub</a>. The best way to send a patch is with a GitHub pull request, and the best way to file a bug is in the GitHub bug tracker.</dd>
</dl>
{{/markdown}}

View File

@@ -5,21 +5,25 @@
A turn-key user interface for Meteor Accounts.
To add Accounts and a set of login controls to an application add the
`accounts-ui` package and one or more login provider package:
`accounts-ui` package and at least one login provider package:
`accounts-password`, `accounts-facebook`, `accounts-github`,
`accounts-google`, `accounts-twitter`, and `accounts-weibo`.
`accounts-google`, `accounts-twitter`, or `accounts-weibo`.
Then simply add the `{{dstache}}loginButtons}}` helper to an HTML
file. This will place a login widget on the page. If there is only one
OAuth provider configured, this will add one login/logout button. If you
use `accounts-password` or have more than one OAuth provider, this will
add a 'Sign in' link which opens a dropdown menu with login options. To
make the login dropdown right aligned, use `{{dstache}}loginButtons align=right}}`.
Then simply add the `{{dstache}}loginButtons}}` helper to an HTML file. This
will place a login widget on the page. If there is only one provider configured
and it is an external service, this will add a login/logout button. If you use
`accounts-password` or use multiple external login services, this will add
a "Sign in" link which opens a dropdown menu with login options. To make the
login dropdown right aligned (useful if you position the login buttons
at the right edge of the screen), use `{{dstache}}loginButtons align=right}}`.
To configure the behavior of `{{dstache}}loginButtons}}`, use
[`Accounts.ui.config`](#accounts_ui_config).
`accounts-ui` also includes modal popup dialogs to handle links from
[`sendResetPasswordEmail`](#accounts_sendresetpasswordemail), [`sendVerificationEmail`](#accounts_sendverificationemail),
and [`sendEnrollmentEmail`](#accounts_sendenrollmentemail). These
do not have be manually placed in HTML, they are automatically activated
do not have be manually placed in HTML: they are automatically activated
when the URLs are loaded.

View File

@@ -10,6 +10,7 @@ components, and several useful utility functions.
Amplify defines a global namespace `amplify` on the client only. It does
not run on the server.
For more information about Amplify, see <a href="http://amplifyjs.com/" target="_blank">http://amplifyjs.com/</a>.
For more information about Amplify, see <http://amplifyjs.com/>.
{{/better_markdown}}
</template>

View File

@@ -8,7 +8,7 @@ functionality, it also provides an API for HTML5 pushState and
client-side URL routing.
For more information about Backbone, see
<a href="http://documentcloud.github.com/backbone/" target="_blank">http://documentcloud.github.com/backbone/</a>.
<http://documentcloud.github.com/backbone/>.
{{/better_markdown}}
</template>

View File

@@ -9,7 +9,7 @@ interactions including typography, forms, buttons, tables, grids, and
navigation.
For more information about Bootstrap, see
<a href="http://twitter.github.com/bootstrap/" target="_blank">http://twitter.github.com/bootstrap/</a>.
<http://twitter.github.com/bootstrap/>.
{{/better_markdown}}
</template>

View File

@@ -10,8 +10,7 @@ interpretation at runtime.
CoffeeScript is supported on both the client and the server. Files
ending with `.coffee` are automatically compiled to JavaScript.
See <a href="http://jashkenas.github.com/coffee-script/" target="_blank">http://jashkenas.github.com/coffee-script/</a>
for more information.
See <http://jashkenas.github.com/coffee-script/> for more information.
{{/better_markdown}}
</template>

View File

@@ -3,7 +3,7 @@
## `d3`
[D3.js](http://d3js.org/D3.js) is a JavaScript library for manipulating
[D3.js](http://d3js.org/) is a JavaScript library for manipulating
documents based on data. D3 helps you bring data to life using HTML, SVG
and CSS. D3's emphasis on web standards gives you the full capabilities
of modern browsers without tying yourself to a proprietary framework,

View File

@@ -3,7 +3,7 @@
## `jquery`
<a href="http://jquery.com/" target="_blank">jQuery</a> is a fast and concise JavaScript
[jQuery](http://jquery.com/) is a fast and concise JavaScript
Library that simplifies HTML document traversing, event handling,
animating, and Ajax interactions for rapid web development.

View File

@@ -10,8 +10,7 @@ With the `less` package installed, `.less` files in your application are
automatically compiled to CSS and the results are included in the client
CSS bundle.
See <a href="http://lesscss.org/" target="_blank">http://lesscss.org/</a> for
documentation of the LESS language.
See <http://lesscss.org/> for documentation of the LESS language.
{{/better_markdown}}
</template>

View File

@@ -7,10 +7,15 @@ expresions. With the `sass` package installed, `.sass` files in your
application are automatically compiled to CSS and the results are
included in the client CSS bundle.
See <a href="https://github.com/visionmedia/sass.js" target="_blank">https://github.com/visionmedia/sass.js</a>
for the JavaScript implementation of the Sass language
and <a href="http://sass-lang.com/" target="_blank">http://sass-lang.com/</a> for the
original project.
See <https://github.com/visionmedia/sass.js> for the JavaScript implementation
of the Sass language and <http://sass-lang.com/> for the original project.
{{#warning}}
The Sass JavaScript implementation used by Node is unmaintained and doesn't
implement the newest language syntax documented at <http://sass-lang.com/>. It
may be removed from a future version of Meteor; consider using [Less](#less) or
[Stylus](#stylus) instead.
{{/warning}}
{{/better_markdown}}
</template>

View File

@@ -3,37 +3,33 @@
## `spiderable`
The `spiderable` package is a temporary solution to allow web search
engines to index a Meteor application. It uses the <a target="_blank"
href="https://developers.google.com/webmasters/ajax-crawling/">AJAX
Crawling specification</a> published by Google to serve HTML to
compatible spiders (Google, Bing, Yandex, and more).
The `spiderable` package is a temporary solution to allow web search engines to
index a Meteor application. It uses the [AJAX Crawling
specification](https://developers.google.com/webmasters/ajax-crawling/)
published by Google to serve HTML to compatible spiders (Google, Bing, Yandex,
and more).
When a spider requests an HTML snapshot of a page the Meteor server runs
the client half of the application inside <a target="_blank"
href="http://phantomjs.org/">phantomjs</a>, a headless browser, and
returns the full HTML generated by the client code.
When a spider requests an HTML snapshot of a page the Meteor server runs the
client half of the application inside [phantomjs](http://phantomjs.org/), a
headless browser, and returns the full HTML generated by the client code.
{{#warning}}
This is a temporary approach to allow Meteor applications to be
searchable. Expect significant changes to this package.
{{/warning}}
In order to have links between multiple pages on a site visible to
spiders, apps must use real links (eg `<a href="/about">`) rather than
simply re-rendering portions of the page when an element is
clicked. Apps should render their content based on the URL of the page
and can use
<a target="_blank" href="https://developer.mozilla.org/en-US/docs/DOM/Manipulating_the_browser_history">HTML5 pushState</a>
to alter the URL on the client without triggering a page reload. See the
<a target="_blank" href="http://meteor.com/examples/todos">Todos example</a>
for a demonstration.
In order to have links between multiple pages on a site visible to spiders, apps
must use real links (eg `<a href="/about">`) rather than simply re-rendering
portions of the page when an element is clicked. Apps should render their
content based on the URL of the page and can use [HTML5
pushState](https://developer.mozilla.org/en-US/docs/DOM/Manipulating_the_browser_history)
to alter the URL on the client without triggering a page reload. See the [Todos
example](http://meteor.com/examples/todos) for a demonstration.
{{#warning}}
If you deploy your application with `meteor bundle`, you must install
`phantomjs` (<a target="_blank"
href="http://phantomjs.org/">http://phantomjs.org</a>) somewhere in your
`phantomjs` ([http://phantomjs.org](http://phantomjs.org/)) somewhere in your
`$PATH`. If you use `meteor deploy` this is already taken care of.
{{/warning}}

View File

@@ -14,10 +14,9 @@ The `stylus` package also includes `nib` support. Add `@import 'nib'` to
your `.styl` files to enable cross-browser mixins such as
`linear-gradient` and `border-radius`.
See <a href="http://learnboost.github.com/stylus/" target="_blank">http://learnboost.github.com/stylus</a>
for documentation of the Stylus language,
and <a href="http://visionmedia.github.com/nib/" target="_blank">http://visionmedia.github.com/nib</a>
for documentation of the nib extensions.
See <http://learnboost.github.com/stylus> for documentation of the Stylus
language, and <http://visionmedia.github.com/nib> for documentation of the nib
extensions.
{{/better_markdown}}
</template>

View File

@@ -9,8 +9,8 @@ concise JavaScript in a functional style.
The `underscore` package defines the `_` namespace on both the client
and the server.
See <a href="http://documentcloud.github.com/underscore/" target="_blank">http://documentcloud.github.com/underscore/</a>
for underscore API documentation.
See <http://documentcloud.github.com/underscore/> for underscore API
documentation.
{{#warning}}
Currently, underscore is included in all projects, as the Meteor

View File

@@ -12,12 +12,22 @@ if (!Accounts._options) {
// - forbidClientAccountCreation {Boolean}
// Do not allow clients to create accounts directly.
Accounts.config = function(options) {
_.each(["sendVerificationEmail", "forbidClientAccountCreation"], function(key) {
// validate option keys
var VALID_KEYS = ["sendVerificationEmail", "forbidClientAccountCreation"];
_.each(_.keys(options), function (key) {
if (!_.contains(VALID_KEYS, key)) {
throw new Error("Accounts.config: Invalid key: " + key);
}
});
// set values in Accounts._options
_.each(VALID_KEYS, function (key) {
if (key in options) {
if (key in Accounts._options)
if (key in Accounts._options) {
throw new Error("Can't set `" + key + "` more than once");
else
} else {
Accounts._options[key] = options[key];
}
}
});
};

View File

@@ -316,7 +316,7 @@
return true;
},
fields: ['_id'] // we only look at _id.
fetch: ['_id'] // we only look at _id.
});
/// DEFAULT INDEXES ON USERS

View File

@@ -1,3 +1,12 @@
// XXX it'd be cool to also test that the right thing happens if options
// *are* validated, but Accounts._options is global state which makes this hard
// (impossible?)
Tinytest.add('accounts - config validates keys', function (test) {
test.throws(function () {
Accounts.config({foo: "bar"});
});
});
Tinytest.add('accounts - updateOrCreateUserFromExternalService', function (test) {
var facebookId = Meteor.uuid();
var weiboId1 = Meteor.uuid();

View File

@@ -13,14 +13,13 @@
}
var state = Meteor.uuid();
var required_scope = ['user'];
var scope = _.union((options && options.requestPermissions) || [], required_scope);
var flat_scope = _.map(scope, encodeURIComponent).join('+');
var scope = (options && options.requestPermissions) || [];
var flatScope = _.map(scope, encodeURIComponent).join('+');
var loginUrl =
'https://github.com/login/oauth/authorize' +
'?client_id=' + config.clientId +
'&scope=' + flat_scope +
'&scope=' + flatScope +
'&redirect_uri=' + Meteor.absoluteUrl('_oauth/github?close') +
'&state=' + state;

View File

@@ -107,8 +107,8 @@
test.equal(Meteor.user().emails.length, 1);
test.equal(Meteor.user().emails[0].address, email2);
test.isFalse(Meteor.user().emails[0].verified);
// We should NOT be publishing verification tokens!
test.isFalse(_.has(Meteor.user(), 'emailVerificationTokens'));
// We should NOT be publishing things like verification tokens!
test.isFalse(_.has(Meteor.user(), 'services'));
},
function (test, expect) {
getVerifyEmailToken(email2, test, expect);

View File

@@ -138,13 +138,15 @@
if (!token)
throw new Meteor.Error(400, "Need to pass token");
var user = Meteor.users.findOne({'emailVerificationTokens.token': token});
var user = Meteor.users.findOne(
{'services.email.verificationTokens.token': token});
if (!user)
throw new Meteor.Error(403, "Verify email link expired");
var tokenRecord = _.find(user.emailVerificationTokens, function (t) {
return t.token == token;
});
var tokenRecord = _.find(user.services.email.verificationTokens,
function (t) {
return t.token == token;
});
if (!tokenRecord)
throw new Meteor.Error(403, "Verify email link expired");
@@ -166,7 +168,7 @@
{_id: user._id,
'emails.address': tokenRecord.address},
{$set: {'emails.$.verified': true},
$pull: {emailVerificationTokens: {token: token}},
$pull: {'services.email.verificationTokens': {token: token}},
$push: {'services.resume.loginTokens': stampedLoginToken}});
this.setUserId(user._id);
@@ -234,8 +236,9 @@
token: Meteor.uuid(),
address: address,
when: +(new Date)};
Meteor.users.update({_id: userId},
{$push: {emailVerificationTokens: tokenRecord}});
Meteor.users.update(
{_id: userId},
{$push: {'services.email.verificationTokens': tokenRecord}});
var verifyEmailUrl = Accounts.urls.verifyEmail(tokenRecord.token);
Email.send({
@@ -442,7 +445,7 @@
// XXX allow an optional callback?
if (callback) {
throw new Error("Meteor.createUser with callback not supported on the server yet.");
throw new Error("Accounts.createUser with callback not supported on the server yet.");
}
var userId = createUser(options).id;

View File

@@ -9,6 +9,14 @@ if (!Accounts.ui._options) {
Accounts.ui.config = function(options) {
// validate options keys
var VALID_KEYS = ['passwordSignupFields', 'requestPermissions'];
_.each(_.keys(options), function (key) {
if (!_.contains(VALID_KEYS, key))
throw new Error("Accounts.ui.config: Invalid key: " + key);
});
// deal with `passwordSignupFields`
if (options.passwordSignupFields) {
if (_.contains([
"USERNAME_AND_EMAIL",
@@ -17,20 +25,24 @@ Accounts.ui.config = function(options) {
"EMAIL_ONLY"
], options.passwordSignupFields)) {
if (Accounts.ui._options.passwordSignupFields)
throw new Error("Can't set `passwordSignupFields` more than once");
throw new Error("Accounts.ui.config: Can't set `passwordSignupFields` more than once");
else
Accounts.ui._options.passwordSignupFields = options.passwordSignupFields;
} else {
throw new Error("Invalid option for `passwordSignupFields`: " + options.passwordSignupFields);
throw new Error("Accounts.ui.config: Invalid option for `passwordSignupFields`: " + options.passwordSignupFields);
}
}
// deal with `requestPermissions`
if (options.requestPermissions) {
_.each(options.requestPermissions, function (scope, service) {
if (Accounts.ui._options.requestPermissions[service])
throw new Error("Can't set `requestPermissions` more than once for " + service);
else
if (Accounts.ui._options.requestPermissions[service]) {
throw new Error("Accounts.ui.config: Can't set `requestPermissions` more than once for " + service);
} else if (!(scope instanceof Array)) {
throw new Error("Accounts.ui.config: Value for `requestPermissions` must be an array");
} else {
Accounts.ui._options.requestPermissions[service] = scope;
}
});
}
};

View File

@@ -0,0 +1,17 @@
// XXX it'd be cool to also test that the right thing happens if options
// *are* validated, but Accouns.ui._options is global state which makes this hard
// (impossible?)
Tinytest.add('accounts-ui - config validates keys', function (test) {
test.throws(function () {
Accounts.ui.config({foo: "bar"});
});
test.throws(function () {
Accounts.ui.config({passwordSignupFields: "not a valid option"});
});
test.throws(function () {
Accounts.ui.config({requestPermissions: {facebook: "not an array"}});
});
});

View File

@@ -385,7 +385,7 @@
var signup = function () {
loginButtonsSession.resetMessages();
var options = {}; // to be passed to Meteor.createUser
var options = {}; // to be passed to Accounts.createUser
var username = trimmedElementValueById('login-username');
if (username !== null) {

View File

@@ -21,3 +21,9 @@ Package.on_use(function (api) {
'login_buttons_dropdown.js',
'login_buttons_dialogs.js'], 'client');
});
Package.on_test(function (api) {
api.use('accounts-ui-unstyled');
api.use('tinytest');
api.add_files('accounts_ui_tests.js', 'client');
});

View File

@@ -186,6 +186,43 @@
//
if (Meteor.isServer) {
Tinytest.add("collection - allow and deny validate options", function (test) {
var collection = new Meteor.Collection(null);
test.throws(function () {
collection.allow({invalidOption: true});
});
test.throws(function () {
collection.deny({invalidOption: true});
});
_.each(['insert', 'update', 'remove', 'fetch'], function (key) {
var options = {};
options[key] = true;
test.throws(function () {
collection.allow(options);
});
test.throws(function () {
collection.deny(options);
});
});
_.each(['insert', 'update', 'remove'], function (key) {
var options = {};
options[key] = ['an array']; // this should be a function, not an array
test.throws(function () {
collection.allow(options);
});
test.throws(function () {
collection.deny(options);
});
});
test.throws(function () {
collection.allow({fetch: function () {}}); // this should be an array
});
});
Tinytest.add("collection - calling allow restricts", function (test) {
var collection = new Meteor.Collection(null);
test.equal(collection._restricted, false);

View File

@@ -184,7 +184,7 @@ _.each(["insert", "update", "remove"], function (name) {
// down.
callback = function (err) {
if (err)
Meteor._debug(name + " failed: " + err.error + " -- " + err.reason);
Meteor._debug(name + " failed: " + (err.reason || err.stack));
};
}
@@ -281,19 +281,34 @@ Meteor.Collection.prototype._ensureIndex = function (index, options) {
(function () {
var addValidator = function(allowOrDeny, options) {
// validate keys
var VALID_KEYS = ['insert', 'update', 'remove', 'fetch'];
_.each(_.keys(options), function (key) {
if (!_.contains(VALID_KEYS, key))
throw new Error(allowOrDeny + ": Invalid key: " + key);
});
var self = this;
self._restricted = true;
_.each(['insert', 'update', 'remove'], function (name) {
if (options[name])
if (options[name]) {
if (!(options[name] instanceof Function)) {
throw new Error(allowOrDeny + ": Value for `" + name + "` must be a function");
}
self._validators[name][allowOrDeny].push(options[name]);
}
});
// Only update the fetch fields if we're passed things that affect
// fetching. This way allow({}) and allow({insert: f}) don't result in
// setting fetchAllFields
if (options.update || options.remove || options.fetch)
if (options.update || options.remove || options.fetch) {
if (options.fetch && !(options.fetch instanceof Array)) {
throw new Error(allowOrDeny + ": Value for `fetch` must be an array");
}
self._updateFetch(options.fetch);
}
};
Meteor.Collection.prototype.allow = function(options) {