Memberz for Craft CMS

Forms

A form is an ordered list of rows plus what happens when it is submitted. Forms live in project config, so they deploy alongside the fields they render.

The row list is the allow-list

This is the load-bearing idea of the whole plugin, and it is worth being explicit about.

When a form is submitted, Memberz walks the form's rows and reads the one body parameter each row names. It never iterates the POST body, and it never calls Craft's setFieldValuesFromRequest(), which would take every posted field on the user layout.

So a submission carrying admin=1, groups[]=1, suspended=1 or a custom field the site deliberately left off the form has no effect: those are not rows, so nothing reads them. There is no list of forbidden parameters to keep in step with Craft, because nothing is read by default.

The practical consequence for you: adding an input to a copy of a Memberz template does nothing on its own. Add the row in the control panel instead.

The three types

Registration

Creates an account. Only ever runs when Craft's own public-registration setting is on.

A registration form needs an Email row, and a Username row unless Craft's useEmailAsUsername is on. Memberz refuses to save one that cannot produce an account Craft would accept.

Group membership comes from the form's own configuration — the Add new members to setting — plus whatever default user group Craft is configured with. A submission cannot ask for a group.

Profile

Edits the signed-in member's own fields. Never touches credentials.

This is the form a member's privacy controls hang off: each row can offer a visible to dropdown beside it.

Account settings

Changes email or password, and demands the current password first — the same bar Craft's own account screen sets. The demand comes from the form, not from what was posted, so leaving a box blank does not dodge it.

A username row does not trigger the demand. That is the same line Craft draws: email and password are the two that let somebody take an account, and gating a username change as well would make an ordinary profile save ask for a password, which teaches members to type it into whatever asks.

Rows

Each row is one of:

  • A profile field — any custom field on the user field layout, held by UID, so renaming the field in the control panel leaves every form still pointing at it.
  • An account attribute — Username, Email, Password, Full Name, First Name, Last Name, Photo.
  • A heading or a block of HTML, for structure.

Each input row carries:

SettingWhat it does
Label, Instructions, PlaceholderOverride what the field calls itself. Blank keeps the field's own.
RequiredAdds a requirement. A field the layout already marks required stays required whatever this says — the form is a view onto the field layout, not a way around its validation.
Width100%, 50%, 33% or 25%. Presentational, and snapped server-side to exactly those four.
Visible toThe ceiling for who may see this field's value. See Privacy.
Members may change thisWhether the owner gets a visible to control for it.

Fields Memberz can draw

Plain Text, Email, URL, Number, Range, Money, Dropdown, Radio Buttons, Checkboxes, Multi-select, Lightswitch, Date, Time, Color and Country.

Anything else — Matrix, Assets, Entries, Table, Link, Addresses — is reported rather than approximated. Craft's own getInputHtml() is deliberately not used: control-panel inputs pull in Craft's asset bundles and JavaScript, assume the control panel's stylesheet, and several do not work outside it. A finite, visible list of supported types is a better bargain than a fallback that silently renders a text box for a Matrix field and loses what the member typed.

Spam checks

Registration forms get two, both free and both involving no third party.

A honeypot — a hidden input a person never fills in and a naive bot always does. It is positioned off-screen rather than display: none, because a bot that reads the stylesheet skips a hidden field and fills in one it can see, and it is marked aria-hidden so nothing using a screen reader is asked to complete it.

A minimum time on screen — the form carries a timestamp of when it was drawn, signed with your site's security key, so it cannot be back-dated. A submission faster than the configured number of seconds is refused.

Neither check tells the submitter which one it failed. A bot that is told is a bot that stops tripping it.

Rendering

{{ craft.memberz.form('register') }}

To draw the markup yourself, get the form and loop its rows:

{% set form = craft.memberz.getForm('register') %}
<form method="post" accept-charset="UTF-8">
    {{ csrfInput() }}
    {{ actionInput('memberz/account/register') }}
    <input type="hidden" name="formHandle" value="{{ form.handle }}">

    {% for row in form.usableRows %}
        <label for="{{ row.inputId }}">{{ row.displayLabel }}</label>
        {{ craft.memberz.input(row) }}
    {% endfor %}

    <button type="submit">{{ form.displaySubmitLabel }}</button>
</form>

A registration form written by hand also needs the honeypot input and the signed timestamp, or those checks will refuse every submission. Copying the shipped template is easier than reconstructing it — set the form's Template setting to your copy.