Download the PHP package visol/neos-papertiger-zebraadaptor without Composer

On this page you can find all versions of the php package visol/neos-papertiger-zebraadaptor. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package neos-papertiger-zebraadaptor

Visol.PaperTiger.ZebraAdaptor

Render Sitegeist.PaperTiger forms through a headless Neos setup — Zebra / Next.js — instead of Fusion.

PaperTiger models forms as content: a Form node with a collection of field nodes and a collection of action nodes, rendered and submitted by Fusion. In a headless setup Fusion never renders the form, so this package does three things instead:

  1. Serialises the form into the content API payload — field/action nodes plus a client-side validation and trigger schema, and an HMAC-signed form identifier.
  2. Accepts the submission over JSON — FormApiController validates the HMAC and honeypots, runs server-side field validators, and executes the form actions (message, e-mail, redirect, database storage, plus anything a domain package registers).
  3. Handles file uploads out of band, so a file is stored before the form is submitted.

Rendering the actual form markup is the frontend's job; this package is backend-only.


Requirements

Installation

Sitegeist.PaperTiger's own Fusion is switched off (Neos.Neos.fusion.autoInclude), because this package replaces the rendering path.


What editors get

On top of PaperTiger's own node types:

Node type Purpose
Visol.PaperTiger.ZebraAdaptor:Action.DatabaseStorage Persists the submission through wegmeister/databasestorage so editors can review and export entries in the backend module.
Visol.PaperTiger.ZebraAdaptor:Mixin.HelpText Adds a helpText property to every field. Not rendered here — it is exposed through the content API for the frontend.

Field types that make no sense headless (Field.FriendlyCaptcha, Field.Slider, Field.Date, Field.Number, Field.TelephoneNumber) are removed from the element list in NodeTypes/Override/DisabledFieldTypes.yaml. Re-enable one by setting its Sitegeist.PaperTiger:Field.Constraint supertype back to true.

Sitegeist.PaperTiger:Field.Button is the submit button — it already renders as <button type="submit">. This package only re-icons it and sets editor-facing defaults.

No starter node template is shipped: a new Form node gets PaperTiger's empty fields and actions collections. What a form should start with is a project decision (the field labels are stored content, not translatable UI labels), so define it in your own override:

Keep the whole template in one place — YAML deep-merge appends keys added by a second override, so a template split across packages cannot control where the added fields land.


Content API payload

Resources/Private/Fusion/Api/ extends Networkteam.Neos.ContentApi:BaseNode:


HTTP API

POST /api/form/submit

Body: a JSON array of { "name": …, "value": … } pairs — the flat form data, plus:

Field Meaning
__form formIdentifierWithHmac from the payload. Tampering aborts the submission.
__language Content dimension to resolve the form node in. Required.
<uuid>[…][one\|two\|three] Honeypot values. one must be empty; two/three are the signed timestamp.
<field>[_uploadedFileIdentifier] HMAC'd identifier returned by the upload endpoint.

Fields whose name does not start with the form identifier are discarded, so a tampered payload cannot inject values from another form.

200 — {"data": { … }} where the keys depend on which actions ran:

Key Set by
message Action.Message
redirectUri Action.Redirect (node URIs are resolved to real URIs)
errors [{"action": "Email"}, …] — actions that threw. The submission itself succeeded.
revalidateDocument true when the form contains a field registered in documentRevalidatingFieldTypes
custom The successKey of a registered action handler

422 — {"data": {"status": "invalid", "fieldErrors": {"<uuid>[email]": ["…"]}}} when a registered field validator rejected the input. No actions ran.

POST /api/form/upload

Multipart, honeypot-gated (_hp_one, _hp_two, _hp_three). Stores the file in the form resource collection and returns {"identifier": "<uuid>::<hmac>", "name": "…"}. Submit that identifier as <field>[_uploadedFileIdentifier]. Hard limit 128 MB (413 above it).

POST /api/form/upload-cancel

Honeypot-gated. Takes identifier and deletes the resource. Idempotent. Scoped to the form collection, and safe under Flow's SHA1 deduplication — two users uploading the same file cannot delete each other's row.

All three routes are granted to Neos.Flow:Everybody in Configuration/Policy.yaml. Submissions are logged to Data/Logs/FormApi.log, each with a random id that ties the log lines of one request together.


Extension points

Everything customer-specific is registered through settings, so this package never needs to know about your node types.

Custom form actions

The service implements Visol\PaperTiger\ZebraAdaptor\Contract\FormActionHandlerInterface. A handler that throws is logged and reported in data.errors without failing the whole submission.

Server-side field validation

The service implements Visol\PaperTiger\ZebraAdaptor\Contract\FormFieldValidatorInterface. Any non-empty return short-circuits the submission with 422 before any action runs.

Cache revalidation

Only forms containing such a field make the response set revalidateDocument. This is server-authoritative on purpose: a generic contact form cannot be used to bust the frontend cache.

Field type behaviour

fieldTypes.* assigns your field types to the behaviours the generated schemas care about. Every entry is a map of arbitrary key → NodeType name, merged across packages. Comparison is by exact NodeType name, so a subtype of a PaperTiger field must be registered explicitly.

Key Effect
email Adds the email validator
choice Required checkable field (radio/checkbox group, consent checkbox): gets the group-aware choice (min: 1) validator instead of notEmpty
multiValue Field name gets the [] suffix
omitted No validator/trigger config; also kept out of {allFormValues}
notEmptyMessage.{selectOption,selectAtLeastOneOption,acceptRequired,selectFile} Which "required" message the field gets
triggerEvents.{keyup,change} Client-side validation trigger (single DOM event; unlisted types fall back to blur)

E-mail

Action.Email ships no default sender address, and the field is editable. If your project may only send from one authorised address, fix it in your own NodeTypes override:

Mail transport is not configured here — configure sitegeist/neos-symfonymailer in your project:

Other settings:

Setting Default Meaning
form.overrideRecipientAddress ~ Redirect all mail to this address and prefix the subject with TEST <hostname>. Use in non-production contexts.
form.allFormValues.excludeFieldNames [] Field names never listed in {allFormValues}.

The {allFormValues} placeholder in the e-mail body renders a definition list of every submitted field. Individual fields are available as {fieldName}.


Translations

Ships en (source) and de. Node type labels resolve through Visol.PaperTiger.ZebraAdaptor:NodeTypes.PaperTiger.*; validation messages live in ValidationErrors and are looked up in the current content dimension's language.


Rendering the form in the frontend

This package ships no frontend components — it gives you a JSON payload and three endpoints. What follows is the contract you have to honour, and a working reference implementation.

1. Field names are the contract

Everything hinges on one convention: every input's name attribute is <formIdentifier>[<fieldName>], with [] appended for multi-value fields. formIdentifier is the form node's identifier; fieldName is the name property the editor configured on the field node.

The three schemas are keyed by exactly these strings, and FormApiController discards any field whose name does not start with the form identifier. Get this wrong and validation silently does nothing while the submission drops your data.

fieldTypes.multiValue decides which types get the [] suffix, but that setting is server-side and not exposed in the payload — so the frontend either knows it per component (a checkbox-group component always passes isMultiValue), or derives it from the schema keys, which already carry the suffix. Keep the prefixing itself in one place:

Each field component then renders whatever markup it likes, as long as the input carries resolvedName and the field node's own properties (label, helpText, placeholder, isRequired, minimumLength, maximumLength, options, …) are respected.

2. The form element

Render the fields and actions content collections inside a <form noValidate> — noValidate because the client-side validator replaces native browser validation — plus two hidden inputs:

__language must be the content dimension the form was rendered in — the controller uses it to resolve the form node again server-side. Deriving it from the node's contextPath (…@user;language=de) works.

Each field wrapper needs an empty container for its error message:

3. Wiring the client-side validator

The two schemas are validare-native (@validare/core), so the consumer feeds them to the constructor directly — no adapter step:

validationSchema is already validare-shaped: email (not emailAddress, and no requireGlobalDomain), choice: { min: 1, message } on radio/checkbox groups and consent checkboxes (validare validates each element and its group-aware choice counts checked ones, so a group passes as soon as one is selected), and notEmpty only on non-checkable required fields. triggerSchema is one DOM event string per field, passed straight to addEventListener. The messages are already translated server-side into the content dimension's language, so locale only covers validare's own built-in strings.

Run this in an effect that fires once (guard on the ref) and destroy() it on unmount — re-initialising on every render leaks listeners and double-renders messages.

validare's Message plugin renders into a single global container, so render each element's messages yourself into its own server-rendered .validation-result__container (the same node the server-side 422 handler writes into) via the core.element.validated event, and drive field-level error styling from the field events:

react-aria field components (dropdown, checkboxes, radio buttons) update the underlying form element programmatically, so the native DOM events Trigger listens to never fire. Bridge them: expose a revalidateField(name) through context that those components call on change — deferred one frame so react-aria has committed the value, and resetting the field first because validate() early-returns a cached result:

Both schemas are plain JSON, so a different client-side validator can consume them too. The adaptations that used to live in the frontend are now handled server-side — email is already named validare-style, each trigger is already a single event, and required groups already carry choice instead of notEmpty. The one thing any other library still has to provide is a group-aware "at least one checked" rule to back the choice entries: validate the whole element set (count elements.filter(el => el.checked)) rather than a single element's value, or a radio/checkbox group can never pass.

4. Submitting

Validate first, submit only when valid:

Post the entries as the JSON array described under POST /api/form/submit. Doing this from a server action keeps the Neos base URI off the client.

5. Honeypots

Field.Honeypot must render three hidden inputs named n<nodeIdentifier>[one|two|three]:

Input Value
[one] always empty — a bot filling it aborts the submission
[two] the signed timestamp, set on render
[three] the signed timestamp, set only after the first real user interaction

Fetch the timestamp from the honeypot-timestamp query rather than reading it off the document payload, so its cache lifecycle is independent of the document's (the signature is valid 24h; refresh every 12h). [three] is what actually separates humans from bots — set it from a listener on touchstart/keydown/mousemove/touchmove that removes itself after firing.

The server rejects a timestamp whose age is under 10 seconds or over 24 hours. Note the age is measured from when the timestamp was generated, not from page load — with the query cached for 12h the value is normally already old enough, so this check only bites when you generate a fresh timestamp per request. The actual bot deterrent is [one] staying empty and [three] requiring genuine interaction.

6. File uploads

Field.Upload uploads out of band, before the form is submitted:

  1. POST /api/form/upload — multipart, plus _hp_one / _hp_two / _hp_three copied from the parent form's honeypot inputs (the endpoint is public and applies the same gate).
  2. Store the returned identifier in a hidden input named <formIdentifier>[<fieldName>][_uploadedFileIdentifier] ([] before [_uploadedFileIdentifier] for multi-file fields).
  3. Strip the raw <input type="file"> from the submitted data — only the HMAC'd identifier goes to /api/form/submit.
  4. When the user removes a file before submitting, POST /api/form/upload-cancel with the identifier.

7. Server-side field errors

A 422 carries fieldErrors keyed by the same prefixed field name. Render them into the same .validation-result__container the client-side messages use, so both look identical:

Scroll the first offending field into view. Re-fire the effect on a monotonic counter, not on the fieldErrors object alone — an identical re-submission produces an equal object and React would skip the update.

8. After a successful submission

The response tells you what to do:

Reference implementation

The ABL monorepo (neos-next/next) implements all of the above: PaperTigerForm (components/clientComponents/content/paper-tiger-form/) owns the form element, the validare lifecycle and the useFormFieldName context; one server component per node type under components/serverComponents/content/SitegeistPaperTiger_* renders the fields; and serverActions/submitForm.ts performs the POST and the cache revalidation.

License

MIT — see LICENSE.


All versions of neos-papertiger-zebraadaptor with dependencies

PHP Build Version
Package Version
Requires php Version >=8.1
neos/flow Version ^8.3
neos/neos Version ^8.3
neos/fusion-form Version *
neos/media Version *
networkteam/neos-contentapi Version *
sitegeist/papertiger Version *
sitegeist/neos-symfonymailer Version *
sitegeist/fusionform-upload Version *
wegmeister/databasestorage Version *
flowpack/monolog Version *
soundasleep/html2text Version *
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package visol/neos-papertiger-zebraadaptor contains the following files

Loading the files please wait ...