diff --git a/docs/client/api.html b/docs/client/api.html
index 3accf566b9..958378749d 100644
--- a/docs/client/api.html
+++ b/docs/client/api.html
@@ -120,6 +120,11 @@ 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
@@ -201,6 +206,8 @@ 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.
+* `setUserId`: a function that associates the current client with a user.
Calling `methods` on the client defines *stub* functions associated with
server methods of the same name. You don't have to define a stub for
@@ -215,6 +222,29 @@ intended to *simulate* the result of what the server's method will do,
but without waiting for the round trip delay. If a stub throws an
exception it will be logged to the console.
+
+{{> api_box method_invocation_userId}}
+
+The user id is an arbitrary string — 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.
+
+{{> api_box method_invocation_setUserId}}
+
+Call this function to change the currently logged in user on the
+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` collection.
+
+`setUserId` is not retroactive. It affects the current method call and
+any future method calls on the connection. Any previous method calls on
+this connection will still see the value of `userId` that was in effect
+when they started.
+
{{> api_box method_invocation_isSimulation}}
{{> api_box method_invocation_unblock}}
@@ -623,6 +653,153 @@ Example:
Logs.remove({});
+{{> api_box allow}}
+
+When a client calls `insert`, `update`, or `remove` on a collection, the
+collection's `allow` and `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.
+
+These checks are run only when a client tries to write to the database
+directly, for example by calling `update` from inside an event
+handler. Server code is trusted and isn't subject to `allow` and `deny`
+restrictions. That includes methods that are called with `Meteor.call`
+— they are expected to do their own access checking rather than
+relying on `allow` and `deny`.
+
+You can call `allow` as many times as you like, and each call can
+include any combination of `insert`, `update`, and `remove`
+functions. The functions should return `true` if they think the
+operation should be allowed. Otherwise they should return `false`, or
+nothing at all (`undefined`). In that case Meteor will continue
+searching through any other `allow` rules on the collection.
+
+The available callbacks are:
+
+
+{{#dtdd "insert(userId, doc)"}}
+The user `userId` wants to insert the document `doc` into the
+collection. Return `true` if this should be allowed.
+{{/dtdd}}
+
+{{#dtdd "update(userId, docs, fields, modifier)"}}
+The user `userId` wants to update some documents. Meteor has fetched the
+documents from the database and they are available in `docs` as an
+array. Return `true` if the user should be allowed to change these
+documents.
+
+Additional details about the proposed modification are in `fields` and
+`modifier`. `fields` is the top-level fields in the document that the
+client wishes to modify, for example `['name', 'score']`. `modifier` is
+the raw Mongo modifier that the client wants to execute, for example
+`{$set: {'name.first': "Alice"}, $inc: {score: 1}}`.
+
+Only Mongo modifiers are supported (operations like `$set` and `$push`.)
+If the user tries to replace the entire document rather than use
+$-modifiers, the request will be denied without checking the `allow`
+functions.
+
+{{/dtdd}}
+
+{{#dtdd "remove(userId, docs)"}}
+The user `userId` wants to remove some documents. Meteor has fetched the
+documents from the database and they are available in `docs` as an
+array. Return `true` if the user should be allowed to remove these
+documents.
+{{/dtdd}}
+
+
+
+By default, when Meteor fetches the documents from the database for the
+`docs` array, it will retrieve all of the fields in the documents. For
+efficiency you may instead want to retrieve just the fields that are
+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!
+
+ // Create a collection where users can only modify documents that
+ // they own. Ownership is tracked by an 'owner' field on each
+ // document. All documents must be owned by the user that created
+ // them and ownership can't be changed. Only a document's owner
+ // is allowed to delete it, and the 'locked' attribute can be
+ // set on a document to prevent its accidental deletion.
+
+ Posts = new Meteor.Collection("posts");
+
+ Posts.allow({
+ insert: function (userId, doc) {
+ // the user must be logged in, and the document must be owned by the user
+ return (userId && doc.owner === userId);
+ },
+ update: function (userId, docs, fields, modifier) {
+ // can only change your own documents
+ return _.all(docs, function(doc) {
+ return doc.owner === userId;
+ });
+ },
+ remove: function (userId, docs) {
+ // can only remove your own documents
+ return _.all(docs, function(doc) {
+ return doc.owner === userId;
+ });
+ },
+ fetch: ['owner']
+ });
+
+ Posts.deny({
+ update: function (userId, docs, fields, modifier) {
+ // can't change owners
+ return _.contains(fields, 'owner');
+ },
+ remove: function (userId, docs) {
+ // can't remove locked documents
+ return _.any(docs, function (doc) {
+ return doc.locked;
+ });
+ },
+ fetch: ['locked'] // no need to fetch 'owner'
+ });
+
+If you never set up any `allow` rules on a collection then all client
+writes to the collection will be denied, and it will only be possible to
+write to the collection from server-side code. In this case you will
+have to create a method for each possible write that clients are allowed
+to do. You'll then call these methods with `Meteor.call` rather than
+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`.
+
+{{#note}}
+For `update` and `remove`, documents will be affected only if they match
+the selector both at the time the documents are fetched to run the
+`allow` and `deny` rules, __and__ at the time that the operation is
+actually executed. This is accomplished by rewriting the selector to
+`{$and: [(original selector), {$in: {_id: [(ids of documents fetched
+and checked by allow and deny)]}}]}`.
+{{/note}}
+
+{{> api_box deny}}
+
+This works just like `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.
+
+When a client tries to write to a collection, the Meteor server first
+checks the collection's `deny` rules. If none of them return true then
+it checks the collection's `allow` rules. Meteor allows the write only
+if no `deny` rules return `true` and at least one `allow` rule returns
+`true`.
+
Cursors
To create a cursor, use [`find`](#find). To access the documents in a
@@ -1460,6 +1637,84 @@ sub-template.
+Accounts
+
+XXX intro text
+
+{{> api_box user}}
+
+- {_id: foo} if not userLoaded.
+- schema / common fields
+
+
+{{> api_box userId}}
+
+{{> api_box users}}
+
+- on the client, current user. on the server all users.
+- examples of usage?
+- schema
+- default publish and allow for profile
+
+{{> api_box userLoaded}}
+
+- more text
+
+{{#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}}
+
+- example scopes
+
+{{> api_box accounts_createUser}}
+
+- logs you in on the client
+- diff between client and server
+- username and/or email. which are optional.
+- default user hook adds profile, override w/ onCreateUser
+- not for oauth
+
+{{> api_box accounts_changePassword}}
+
+{{> api_box accounts_forgotPassword}}
+
+- triggers sendResetPasswordEmail
+- document where the token goes in Accounts._whatever?
+- youre responsibility to get it into resetPassword
+
+{{> api_box accounts_resetPassword}}
+
+- don't need to call if you have accounts-ui
+
+{{> api_box accounts_setPassword}}
+
+{{> api_box accounts_verifyEmail}}
+
+- pass token from sendVerificationEmail
+- what changes in the schema
+
+{{> api_box accounts_sendResetPasswordEmail}}
+{{> api_box accounts_sendEnrollmentEmail}}
+{{> api_box accounts_sendVerificationEmail}}
+{{> api_box accounts_emailTemplates}}
+
+{{> api_box accounts_config}}
+{{> api_box accounts_ui_config}}
+{{> api_box accounts_validateNewUser}}
+{{> api_box accounts_onCreateUser}}
+
+- takes `options`, `user`
+
+
+
Timers
Meteor uses global environment variables
diff --git a/docs/client/api.js b/docs/client/api.js
index 283ff58082..80f04a4631 100644
--- a/docs/client/api.js
+++ b/docs/client/api.js
@@ -145,6 +145,14 @@ Template.api.subscription_onStop = {
]
};
+Template.api.subscription_userId = {
+ id: "publish_userId",
+ name: "this.userId",
+ locus: "Server",
+ descr: ["The id of logged-in user, or `null` if no user is logged in."]
+};
+
+
Template.api.subscribe = {
id: "meteor_subscribe",
name: "Meteor.subscribe(name [, arg1, arg2, ... ] [, onComplete])",
@@ -187,6 +195,25 @@ Template.api.methods = {
]
};
+Template.api.method_invocation_userId = {
+ id: "method_userId",
+ name: "this.userId",
+ locus: "Anywhere",
+ descr: ["The id of the user that made this method call, or `null` if no user was logged in."]
+};
+
+Template.api.method_invocation_setUserId = {
+ id: "method_setUserId",
+ name: "this.setUserId(userId)",
+ locus: "Server",
+ descr: ["Set the logged in user."],
+ args: [
+ {name: "userId",
+ type: "String or null",
+ descr: "The value that should be returned by `userId` on this connection."}
+ ]
+};
+
Template.api.method_invocation_unblock = {
id: "method_unblock",
name: "this.unblock()",
@@ -371,6 +398,93 @@ Template.api.findone = {
]
};
+Template.api.insert = {
+ id: "insert",
+ name: "collection.insert(doc, [callback])",
+ locus: "Anywhere",
+ descr: ["Insert a document in the collection. Returns its unique _id."],
+ args: [
+ {name: "doc",
+ type: "Object",
+ descr: "The document to insert. Should not yet have an _id attribute."},
+ {name: "callback",
+ type: "Function",
+ descr: "Optional. If present, called with an error object as the first argument and, if no error, the _id as the second."}
+ ]
+};
+
+Template.api.update = {
+ id: "update",
+ name: "collection.update(selector, modifier, [options], [callback])",
+ locus: "Anywhere",
+ descr: ["Modify one or more documents in the collection"],
+ args: [
+ {name: "selector",
+ type: "Object: Mongo selector, or String",
+ type_link: "selectors",
+ descr: "Specifies which documents to modify"},
+ {name: "modifier",
+ type: "Object: Mongo modifier",
+ type_link: "modifiers",
+ descr: "Specifies how to modify the documents"},
+ {name: "callback",
+ type: "Function",
+ descr: "Optional. If present, called with an error object as its argument."}
+ ],
+ options: [
+ {name: "multi",
+ type: "Boolean",
+ descr: "True to modify all matching documents; false to only modify one of the matching documents (the default)."}
+ ]
+};
+
+Template.api.remove = {
+ id: "remove",
+ name: "collection.remove(selector, [callback])",
+ locus: "Anywhere",
+ descr: ["Remove documents from the collection"],
+ args: [
+ {name: "selector",
+ type: "Object: Mongo selector, or String",
+ type_link: "selectors",
+ descr: "Specifies which documents to remove"},
+ {name: "callback",
+ type: "Function",
+ descr: "Optional. If present, called with an error object as its argument."}
+ ]
+};
+
+Template.api.allow = {
+ id: "allow",
+ name: "collection.allow(options)",
+ locus: "Server",
+ descr: ["Allow users to write directly to this collection from client code, subject to limitations you define."],
+ options: [
+ {name: "insert, update, remove",
+ type: "Function",
+ descr: "Functions that look at a proposed modification to the database and return true if it should be allowed."},
+ {name: "fetch",
+ type: "Array of String",
+ descr: "Optional performance enhancement. Limits the fields that will be fetched from the database for inspection by your `update` and `remove` functions."}
+ ]
+};
+
+Template.api.deny = {
+ id: "deny",
+ name: "collection.deny(options)",
+ locus: "Server",
+ descr: ["Override `allow` rules."],
+ options: [
+ {name: "insert, update, remove",
+ type: "Function",
+ descr: "Functions that look at a proposed modification to the database and return true if it should be denied, even if an `allow` rule says otherwise."},
+ {name: "fetch",
+ type: "Array of Strings",
+ descr: "Optional performance enhancement. Limits the fields that will be fetched from the database for inspection by your `update` and `remove` functions."}
+ ]
+};
+
+
Template.api.cursor_count = {
id: "count",
name: "cursor.count()",
@@ -429,62 +543,6 @@ Template.api.cursor_observe = {
]
};
-Template.api.insert = {
- id: "insert",
- name: "collection.insert(doc, [callback])",
- locus: "Anywhere",
- descr: ["Insert a document in the collection. Returns its unique _id."],
- args: [
- {name: "doc",
- type: "Object",
- descr: "The document to insert. Should not yet have an _id attribute."},
- {name: "callback",
- type: "Function",
- descr: "Optional. If present, called with an error object as the first argument and, if no error, the _id as the second."}
- ]
-};
-
-Template.api.update = {
- id: "update",
- name: "collection.update(selector, modifier, [options], [callback])",
- locus: "Anywhere",
- descr: ["Modify one or more documents in the collection"],
- args: [
- {name: "selector",
- type: "Object: Mongo selector, or String",
- type_link: "selectors",
- descr: "Specifies which documents to modify"},
- {name: "modifier",
- type: "Object: Mongo modifier",
- type_link: "modifiers",
- descr: "Specifies how to modify the documents"},
- {name: "callback",
- type: "Function",
- descr: "Optional. If present, called with an error object as its argument."}
- ],
- options: [
- {name: "multi",
- type: "Boolean",
- descr: "True to modify all matching documents; false to only modify one of the matching documents (the default)."}
- ]
-};
-
-Template.api.remove = {
- id: "remove",
- name: "collection.remove(selector, [callback])",
- locus: "Anywhere",
- descr: ["Remove documents from the collection"],
- args: [
- {name: "selector",
- type: "Object: Mongo selector, or String",
- type_link: "selectors",
- descr: "Specifies which documents to remove"},
- {name: "callback",
- type: "Function",
- descr: "Optional. If present, called with an error object as its argument."}
- ]
-};
-
Template.api.selectors = {
id: "selectors",
name: "Mongo-style Selectors"
@@ -610,6 +668,380 @@ Template.api.isolate = {
+Template.api.user = {
+ id: "meteor_user",
+ name: "Meteor.user()",
+ locus: "Anywhere but publish functions",
+ descr: ["Get the current user record, or `null` if no user is logged in. A reactive data source."]
+};
+
+
+Template.api.userId = {
+ id: "meteor_userid",
+ name: "Meteor.userId()",
+ locus: "Anywhere but publish functions",
+ descr: ["Get the current user id, or `null` if no user is logged in. A reactive data source."]
+};
+
+
+Template.api.users = {
+ id: "meteor_users",
+ name: "Meteor.users",
+ locus: "Anywhere",
+ descr: ["A Meteor.Collection containing user documents."]
+};
+
+Template.api.userLoaded = {
+ id: "meteor_userloaded",
+ name: "Meteor.userLoaded()",
+ locus: "Client",
+ descr: ["Determine if the current user document is fully loaded in Meteor.users. A reactive data source."]
+};
+
+
+
+Template.api.logout = {
+ id: "meteor_logout",
+ name: "Meteor.logout([callback])",
+ locus: "Client",
+ descr: ["Log the user out."],
+ args: [
+ {
+ name: "callback",
+ type: "Function",
+ descr: "Optional callback. Called with no arguments on success, or with a single `Error` argument on failure."
+ }
+ ]
+};
+
+
+Template.api.loginWithPassword = {
+ id: "meteor_loginwithpassword",
+ name: "Meteor.loginWithPassword(user, password, [callback])",
+ locus: "Client",
+ descr: ["Log the user in with a password."],
+ args: [
+ {
+ name: "user",
+ type: "Object or String",
+ descr: "Either a string interpreted as a username or an email; or an object with a single key: `email`, `username` or `id`."
+ },
+ {
+ name: "password",
+ type: "String",
+ descr: "The user's password. This is __not__ sent in plain text over the wire — it is secured with SRP."
+ },
+ {
+ name: "callback",
+ type: "Function",
+ descr: "Optional callback. Called with no arguments on success, or with a single `Error` argument on failure."
+ }
+ ]
+};
+
+
+Template.api.loginWithOAuth = {
+ id: "meteor_loginwithoauth",
+ name: "Meteor.loginWithOAuthProvider([options], [callback])",
+ locus: "Client",
+ descr: ["Log the user in using an external OAuth service."],
+ args: [
+ {
+ name: "callback",
+ type: "Function",
+ descr: "Optional callback. Called with no arguments on success, or with a single `Error` argument on failure."
+ }
+ ],
+ options: [
+ {
+ name: "requestPermissions",
+ type: "Array of Strings",
+ descr: "A list of permissions to request from the user."
+ }
+ ]
+};
+
+Template.api.accounts_createUser = {
+ id: "accounts_createuser",
+ name: "Accounts.createUser(options, [callback])",
+ locus: "Anywhere",
+ descr: ["Create a new user."],
+ args: [
+ {
+ name: "callback",
+ type: "Function",
+ descr: "Client only, optional callback. Called with no arguments on success, or with a single `Error` argument on failure."
+ }
+ ],
+ options: [
+ {
+ name: "username",
+ type: "String",
+ descr: "A unique name for this user."
+ },
+ {
+ name: "email",
+ type: "String",
+ descr: "The user's email address."
+ },
+ {
+ name: "password",
+ type: "String",
+ descr: "The user's password. This is __not__ sent in plain text over the wire."
+ },
+ {
+ name: "profile",
+ type: "Object",
+ descr: "The user's profile, typically including the `name` field."
+ }
+ ]
+};
+
+Template.api.accounts_changePassword = {
+ id: "accounts_changepassword",
+ name: "Accounts.changePassword(oldPassword, newPassword, [callback])",
+ locus: "Client",
+ descr: ["Change the current user's password. Must be logged in."],
+ args: [
+ {
+ name: "oldPassword",
+ type: "String",
+ descr: "The user's current password. This is __not__ sent in plain text over the wire."
+ },
+ {
+ name: "newPassword",
+ type: "String",
+ descr: "A new password for the user. This is __not__ sent in plain text over the wire."
+ },
+ {
+ name: "callback",
+ type: "Function",
+ descr: "Optional callback. Called with no arguments on success, or with a single `Error` argument on failure."
+ }
+ ]
+};
+
+Template.api.accounts_forgotPassword = {
+ id: "accounts_forgotpassword",
+ name: "Accounts.forgotPassword(options, [callback])",
+ locus: "Client",
+ descr: ["Request a forgot password email."],
+ args: [
+ {
+ name: "callback",
+ type: "Function",
+ descr: "Optional callback. Called with no arguments on success, or with a single `Error` argument on failure."
+ }
+ ],
+ options: [
+ {
+ name: "email",
+ type: "String",
+ descr: "The email address to send a password reset link."
+ }
+ ]
+};
+
+Template.api.accounts_resetPassword = {
+ id: "accounts_resetpassword",
+ name: "Accounts.resetPassword(token, newPassword, [callback])",
+ locus: "Client",
+ descr: ["Reset the password for a user using a token received in email. Logs the user in afterwards."],
+ args: [
+ {
+ name: "token",
+ type: "String",
+ descr: "The token retrieved from the reset password URL."
+ },
+ {
+ name: "newPassword",
+ type: "String",
+ descr: "A new password for the user. This is __not__ sent in plain text over the wire."
+ },
+ {
+ name: "callback",
+ type: "Function",
+ descr: "Optional callback. Called with no arguments on success, or with a single `Error` argument on failure."
+ }
+ ],
+};
+
+Template.api.accounts_setPassword = {
+ id: "accounts_setpassword",
+ name: "Accounts.setPassword(userId, newPassword)",
+ locus: "Server",
+ descr: ["Forcibly change the password for a user."],
+ args: [
+ {
+ name: "userId",
+ type: "String",
+ descr: "The id of the user to update."
+ },
+ {
+ name: "newPassword",
+ type: "String",
+ descr: "A new password for the user."
+ }
+ ]
+};
+
+Template.api.accounts_verifyEmail = {
+ id: "accounts_verifyemail",
+ name: "Accounts.verifyEmail(token, [callback])",
+ locus: "Client",
+ descr: ["Marks the user's email address as verified. Logs the user in afterwards."],
+ args: [
+ {
+ name: "token",
+ type: "String",
+ descr: "The token retrieved from the verification URL."
+ },
+ {
+ name: "callback",
+ type: "Function",
+ descr: "Optional callback. Called with no arguments on success, or with a single `Error` argument on failure."
+ }
+ ]
+};
+
+
+Template.api.accounts_sendResetPasswordEmail = {
+ id: "accounts_sendresetpasswordemail",
+ name: "Accounts.sendResetPasswordEmail(userId, [email])",
+ locus: "Server",
+ descr: ["Send an email with a link the user can use to reset their password."],
+ args: [
+ {
+ name: "userId",
+ type: "String",
+ descr: "The id of the user to send email to."
+ },
+ {
+ name: "email",
+ type: "String",
+ descr: "Optional. Which address of the user's to send the email to. This address must be in the user's `emails` list. Defaults to the first email in the list."
+ }
+ ]
+};
+
+Template.api.accounts_sendEnrollmentEmail = {
+ id: "accounts_sendenrollmentemail",
+ name: "Accounts.sendEnrollmentEmail(userId, [email])",
+ locus: "Server",
+ descr: ["Send an email with a link the user can use to set their initial password."],
+ args: [
+ {
+ name: "userId",
+ type: "String",
+ descr: "The id of the user to send email to."
+ },
+ {
+ name: "email",
+ type: "String",
+ descr: "Optional. Which address of the user's to send the email to. This address must be in the user's `emails` list. Defaults to the first email in the list."
+ }
+ ]
+};
+
+Template.api.accounts_sendVerificationEmail = {
+ id: "accounts_sendverificationemail",
+ name: "Accounts.sendVerificationEmail(userId, [email])",
+ locus: "Server",
+ descr: ["Send an email with a link the user can use verify their email address."],
+ args: [
+ {
+ name: "userId",
+ type: "String",
+ descr: "The id of the user to send email to."
+ },
+ {
+ name: "email",
+ type: "String",
+ descr: "Optional. Which address of the user's to send the email to. This address must be in the user's `emails` list. Defaults to the first unverified email in the list."
+ }
+ ]
+};
+
+
+
+Template.api.accounts_emailTemplates = {
+ id: "accounts_emailtemplates",
+ name: "Accounts.emailTemplates",
+ locus: "Anywhere",
+ descr: ["XXX"]
+};
+
+
+
+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` 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 ojbject, or throw an `Error` to abort the creation."
+ }
+ ]
+};
+
+
+
+
Template.api.setTimeout = {
id: "meteor_settimeout",
name: "Meteor.setTimeout",
diff --git a/docs/client/concepts.html b/docs/client/concepts.html
index 964289b85f..ca1a4a4640 100644
--- a/docs/client/concepts.html
+++ b/docs/client/concepts.html
@@ -13,6 +13,7 @@ when writing those apps.
{{> livehtml }}
{{> templates }}
{{> packages_concept }}
+{{> accounts }}
{{> deploying }}
@@ -207,6 +208,9 @@ And the reactive data sources that can trigger changes are:
* Session variables
* Database queries on Collections
* `Meteor.status`
+* `Meteor.user`
+* `Meteor.userId`
+* `Meteor.userLoaded`
Meteor's implementation
of reactivity is short and sweet, about 50 lines of code. You can
@@ -489,6 +493,30 @@ make your own packages just yet. Coming soon.
{{/better_markdown}}
+
+
+{{#better_markdown}}
+
+Accounts
+
+Meteor has an Accounts system.
+
+- simplest integration
+ - add accounts-ui accounts-password
+ - and XXX loginButtons}} to app
+
+- which packages
+
+- can build own accounts-ui
+
+- link to api section
+
+
+{{/better_markdown}}
+
+
+
+
{{#better_markdown}}
diff --git a/docs/client/docs.css b/docs/client/docs.css
index 3905086782..b4661d1730 100644
--- a/docs/client/docs.css
+++ b/docs/client/docs.css
@@ -265,6 +265,10 @@ dl.callbacks dt .name, dl.methods dt .name {
font-size: 1.1em;
}
+dl.callbacks {
+ margin-left: 1.5em;
+}
+
#main dd p {
margin-top: 0.5em;
}
diff --git a/docs/client/docs.js b/docs/client/docs.js
index bd90697b0c..f7b096b7e4 100644
--- a/docs/client/docs.js
+++ b/docs/client/docs.js
@@ -75,6 +75,7 @@ var toc = [
"Live HTML",
"Templates",
"Smart Packages",
+ "Accounts",
"Deploying"
],
@@ -93,7 +94,8 @@ var toc = [
{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: "stop", id: "publish_stop"},
+ {instance: "this", name: "userId", id: "publish_userId"}
],
"Meteor.subscribe",
"Meteor.autosubscribe"
@@ -101,6 +103,8 @@ var toc = [
{name: "Methods", id: "methods_header"}, [
"Meteor.methods", [
+ {instance: "this", name: "userId", id: "method_userId"},
+ {instance: "this", name: "setUserId", id: "method_setUserId"},
{instance: "this", name: "isSimulation", id: "method_issimulation"},
{instance: "this", name: "unblock", id: "method_unblock"}
],
@@ -121,7 +125,9 @@ var toc = [
{instance: "collection", name: "findOne"},
{instance: "collection", name: "insert"},
{instance: "collection", name: "update"},
- {instance: "collection", name: "remove"}
+ {instance: "collection", name: "remove"},
+ {instance: "collection", name: "allow"},
+ {instance: "collection", name: "deny"}
],
"Meteor.Collection.Cursor", [
@@ -168,6 +174,41 @@ 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",
@@ -206,6 +247,7 @@ var toc = [
],
"Packages", [ [
+ "accounts-ui",
"amplify",
"backbone",
"bootstrap",
diff --git a/docs/client/packages.html b/docs/client/packages.html
index 1c12157077..035dedc822 100644
--- a/docs/client/packages.html
+++ b/docs/client/packages.html
@@ -16,6 +16,7 @@ and removed with:
$ meteor remove
+{{> pkg_accounts_ui}}
{{> pkg_amplify}}
{{> pkg_backbone}}
{{> pkg_bootstrap}}
diff --git a/docs/client/packages/accounts-ui.html b/docs/client/packages/accounts-ui.html
new file mode 100644
index 0000000000..ff497c73e4
--- /dev/null
+++ b/docs/client/packages/accounts-ui.html
@@ -0,0 +1,11 @@
+
+{{#better_markdown}}
+## `accounts-ui`
+
+A full featured login interface.
+
+`XXX loginButtons` and `XXX loginButtonsRight`.
+
+
+{{/better_markdown}}
+