Install and show the forms
This page is for the markup. Everything below is done in a MODX template and in .tpl files; PHP starts only on the next page, and then only two lines of it.
Installation
- Install PageBlocks 3.x — without it the component switches itself off and writes a log entry.
- Extras → Installer → Download Extras, find and install pbAuth.
- System → System Settings, filter by
pageblocks. Thepageblocks_routingsetting must be set to Route Only or Full API. - While you are there, enable
pageblocks_load_scripts— forms will start submitting without a reload, and errors will light up right in the fields. - Clear the site cache.
Check: open /login. A page with a working login form should come up. You do not need to create a resource for it — the router serves that address.
The migration is a separate step
The table is only needed for social networks, and the package creates it during installation on its own. If exec() is disabled on the hosting, the resolver says so plainly in the log — then apply the migration by hand:
php core/components/pageblocks/vendor/bin/phinx migrate \
--configuration=core/components/pbauth/src/phinx.php --environment=productionWithout the table everything works except social login.
What the installer put where
| Where | What | Whose |
|---|---|---|
core/components/pbauth/ | code, routes, shipped settings, lexicons | the component's — will be overwritten on update |
core/App/elements/auth/ | wrapper templates and form chunks | yours |
core/App/lang/{ru,en,uk,de}/auth.php | all the labels | yours |
core/App/config/pbauth.php | a settings stub, entirely commented out | yours |
"Yours" here is not politeness. Neither installation nor an update ever overwrites a file that already exists in core/App/. Exactly what the installer put there is recorded with hashes in core/App/.pbauth-installed.json, and uninstalling the component removes only the files you never touched.
"Login" and "Register" buttons in the header
The ready-made chunks are already in place, you only have to call them from the site template. There are two options.
Plain links — they lead to the separate pages /login and /register:
{include 'file:auth/chunks/auth.tpl'}Modal windows — the forms open on top of the page:
{include 'file:auth/chunks/auth_modal.tpl'}Both chunks work out for themselves whether the person is logged in: a guest sees the buttons, a logged-in user sees an avatar with their name and a link to the profile.
file: is the site's folder
file:auth/chunks/auth.tpl means core/App/elements/auth/chunks/auth.tpl. The default source is set by the pageblocks_elements_path setting.
Changing how they look
Open core/App/elements/auth/chunks/auth.tpl and edit it like ordinary markup. There are only two meaningful constructs inside:
{auth} ...what a logged-in user sees... {/auth}
{guest} ...what a guest sees... {/guest}Do not write the links by hand — take them by name, then they will not break if the page address changes:
<a href="{route 'pageLogin'}">Login</a>
<a href="{route 'pageRegister'}">Register</a>
<a href="{route 'pageProfile'}">My profile</a>
<a href="{route 'logout'}">Log out</a>The full list of names is in the reference.
The login and registration pages
A page is assembled from two files:
| File | What it is | |
|---|---|---|
| wrapper | core/App/elements/auth/templates/auth.tpl | <html>, <head>, styles, the common frame |
| form | core/App/elements/auth/chunks/form.login.tpl | the form itself, inserted inside the wrapper |
There are two wrappers: auth.tpl (login, registration, recovery) and profile.tpl (profile and password change — that one has a section menu on the side).
The forms sit next to each other, one per action:
core/App/elements/auth/chunks/
form.login.tpl login
form.register.tpl registration
form.forgotPassword.tpl "forgot password"
form.resetPassword.tpl entering a new password via the link from the e-mail
form.changePassword.tpl changing the password in the profile
form.confirmPassword.tpl password confirmation
form.resendVerification.tpl re-sending the confirmation link
form.twoFactorChallenge.tpl entering the code at login
form.twoFactor.tpl enabling and disabling the code in the profile
form.socialEmail.tpl asking for an e-mail when the social network gave none
form.profile.tpl profile editing
social.buttons.tpl social login buttons
social.accounts.tpl linked networks in the profile
modals/ the same forms for modal windows
email.verifyEmail.tpl the e-mail with the confirmation link
email.resetPassword.tpl the password reset e-mailTwo variables reach the wrapper template: $title — the page heading, and $form — the name of the chunk with the form (for example form.login). The form is included like this:
{set $chunkPath = 'file:auth/chunks/' ~ $form}
{include $chunkPath}What a form field must have
When editing a form, keep three things on every field — otherwise it stays functional but stops behaving decently:
<div class="form-group mb-3">
<label class="mb-2" for="email">E-mail</label>
<input type="email" name="email" id="email"
class="form-control{if $errors.email} is-invalid{/if}"
value="{$old_input.email}" required>
<span class="invalid-feedback" data-error="email">{$errors.email}</span>
</div>| What | Why |
|---|---|
name="email" | the field arrives at the server under this name |
{$errors.email} and data-error="email" | the error text lands here; data-error is what the script needs to insert it without a reload |
{$old_input.email} | so that what was typed is not wiped on an error |
Errors are not shown
Almost always one of two things: the field has no {$errors.name} and data-error="name", or the pageblocks_load_scripts setting is off.
Replace a single form
Your own registration form, with the rest left as they are: put your chunk next to them and name it in core/App/config/pbauth.php.
<?php
return [
'forms' => [
'register' => 'my.register', // core/App/elements/auth/chunks/my.register.tpl
],
];And if your template draws the form itself and does not need the $form variable:
'forms' => ['profile' => null],Your own template instead of the shipped one
If you already have your own page template — do not edit the shipped file, point to yours:
'views' => [
'auth' => 'file:templates/my-auth', // core/App/elements/templates/my-auth.tpl
'profile' => 'file:templates/my-profile',
],There are only two wrappers, but an individual page can be given its own — a key named after the action overrides the general one:
'views' => [
'profile' => 'file:templates/my-profile', // all profile pages
'two_factor' => 'file:auth/templates/profile', // and this one its own way
],Handy when the site's profile template is self-sufficient and does not substitute $form, yet some page does need a form after all.
Write only the differences
The settings file is merged with the shipped one: dictionaries are extended by key, lists are replaced whole. There is no need to list all the forms for the sake of one replacement. Details — "The settings file".
Texts and translations
All the labels are in core/App/lang/ru/auth.php, an ordinary PHP array:
<?php
return [
'login_title' => 'Login',
'register_title' => 'Register',
'register_success' => 'Check your e-mail — we have sent a confirmation link',
];In a template a label is called as {lang 'auth.login_title'}, in PHP as lang('auth.login_title').
There are four languages: ru, en, uk, de. Add a new key to all of them — otherwise another language will show the key itself instead of the text.
Do not confuse this with the lexicon
core/components/pbauth/lexicon/ holds the captions for settings and buttons in the MODX manager. They belong to the component and are overwritten on update.
If the confirmation e-mail never arrived
After registering, a person receives an e-mail with a link and cannot log in until they follow it. E-mails get lost all the time: spam, delays, a typo in the address.
That is what the /resend-verification page is for — it is already wired up, and a link to it sits at the bottom of the login form.
The form's response is always the same, regardless of whether the address was found or not and whether it is confirmed. Otherwise this form could be used to brute-force which addresses are registered on the site. "Forgot password" behaves the same way.
The two forms are not interchangeable
Password recovery works only for confirmed accounts, and re-sending only for unconfirmed ones. MODX keeps both one-time keys in the same remote_key field, and if the forms overlapped, an issued link would kill the previous one.
The edit is not visible
Clear the MODX cache and reload the page with Ctrl+F5. If the edit has vanished entirely — most likely it was in a file inside core/components/pbauth/: everything there gets overwritten.