Using accounts
Know who's visiting, give people access to things, gate content and add pages to their account, from any plugin.
On this page
Visitor accounts let visitors sign up and sign in to your site. Your plugin can use them to find out who's there, give someone access to a course or a download, and add pages to their account.
Accounts are built in, so your plugin needs nothing extra in require. cms()->accounts() also answers sensibly when a site has visitor accounts off: nobody signs up, and access you grant waits until someone can sign in.
#Give someone access
A typical case: someone bought a course, so they may read its lessons.
$accounts = cms()->accounts();
// A buyer who may not have an account yet: made with no password, and nothing is sent.
$user = $accounts->ensure('buyer@example.com', 'Bea', 'de');
$accounts->grant($user, 'course-php', source: 'my-shop', until: now()->addYear());Later, anywhere on the site:
$accounts->holds($user, 'course-php'); // true, until the date passesThe buyer claims the account by signing up or resetting their password, which proves the mailbox.
#What access means
An access name means whatever you decide. Nothing here knows about shops, courses or subscriptions.
- A role counts as access, and so does a grant.
- The source says who gave it. Granting from two sources keeps both, and each source revokes only its own. Without a source,
revoke()takes away every grant of that name. - Grants with a date stop counting when it passes, with no task to run.
#The API
$accounts = cms()->accounts();
$accounts->enabled(); // the site has visitor accounts
$accounts->signUpOpen(); // a stranger can sign up right now (it needs working email)
$user = $accounts->current(); // ?Cms\Users\User: this request's person. A suspended or stale session gets null
$accounts->find('bea@example.com'); // ?User, by email
$accounts->isVisitor($user); // no admin access
$accounts->signInUrl('/next', 'de'); // where to send someone; null with accounts off
$accounts->pathOf('account', 'de'); // the address of an account page
// What they hold. A role counts, and so does a grant.
$accounts->holds($user, 'course-php');
$accounts->holdsAny($user, ['gold', 'silver']);
$accounts->grants($user); // every grant, with its source and end date
$accounts->grant($user, 'course-php', source: 'my-shop', until: now()->addYear());
$accounts->revoke($user, 'course-php', source: 'my-shop');
// A buyer who has no account yet: made with no password, nothing sent. They claim it by
// signing up or resetting their password, which proves the mailbox.
$user = $accounts->ensure('buyer@example.com', 'Bea', 'de');#Gate content
Editors set who may read an entry; see Members-only content. In code:
$gate = cms()->entries()->gate();
$gate->decide($entry); // full, teaser, sign-in, denied or missing, for this request
$gate->view($entry); // the entry as this request may read it
$gate->isGated($entry); // has a rule at allYour plugin is safe by default:
->live()queries already leave out what this request may not read, and return teasers instead of full entries.- Admin code that has checked its own permission reads past that with
->ungated(). - If you load an entry by id or address, pass it through
view()before showing it. - A reader's copy can't be saved:
save()throws.
To decide access yourself, for example from an external system:
cms()->hooks()->addFilter('gate.allows', function (bool $allowed, User $user, Entry $entry, array $rule) {
return $allowed || MyMemberships::has($user, $rule['access']);
});#Add a page to the account
An account page is a block document that wears the site's design, so your plugin ships no template:
$hooks->addFilter('account.screens', fn (array $screens) => [...$screens, 'orders' => [
'label' => 'Orders',
'path' => '/account/orders',
'auth' => 'user', // user, guest, token or any
'nav' => true, // in the Account Navigation block
'order' => 30, // its place there. Overview is 0, Profile 60, Email and password 70, Your data 80
'seed' => fn () => [['id' => 'orders', 'type' => 'my-shop/orders', 'props' => []]],
]]);The page gets the sign-in redirect, every language prefix and a row in Pages, under Built-in Pages › Account, where a designer can Customize it. seed is its starting content: register your block as usual, and post its forms to a route of your own in the web middleware group, which gives you the session and CSRF protection.
To show something per person in a block, bind to account.* on pages rendered per person; see Visitor accounts.
#Hooks
| Hook | Kind | Arguments | When |
|---|---|---|---|
account.created | action | $user | An account exists: signed up, or made with ensure(). |
account.verified | action | $user | The mailbox is proven. Link what you hold for this address here. |
account.signed-in, account.signed-out | action | $user | |
account.updated, account.password-changed | action | $user | |
account.email-changed | action | $user, $old | |
account.granted, account.revoked | action | $user, $access, $source | |
account.deleting | action | $user | Inside the deletion's transaction. Delete or anonymize your data; throwing keeps the account. |
account.deleted | action | $id | Afterward. |
account.delete.blockers | filter | $reasons, $user | Reasons the account can't be deleted now, shown to the person. |
account.export | filter | $sections, $user | Add your section to their data download. |
account.screens | filter | $screens | Add or change account pages. |
account.overview | filter | $html, $user | What the account's first page shows under who is signed in. |
account.gate-notice | filter | $html, $user, $path | What a teaser or a denied page says, for example a link to buy access. |
gate.allows | filter | $allowed, $user, $entry, $rule | Whether someone may read a gated entry. |