Sites
Plugin and builder adapters
In short
Page builders and field plugins keep words in their own data, where Drupal’s and WordPress’s own saves do not reach. An adapter is a small module inside the site helper that saves those words through the plugin’s own public save, and refuses by name when a plugin update takes away what it relies on.
On this page
The problem
Wanigan reaches Drupal and WordPress through core first. But on many sites the words also live in modules and plugins: a page builder’s layout, a field plugin’s custom fields, a form module’s settings. Each ships new releases every few weeks, and an editor that knows each plugin by how it stores things breaks whenever that changes.
So Wanigan does not try to keep up with storage formats by hand. An adapter depends only on what a plugin promises: the save call the plugin itself, its importers and its add-ons use.
What an adapter is
An adapter is one class inside the site helper: a declaration of everything it calls in the plugin, plus its save. The same shape is used on Drupal and WordPress.
- An id and the plugin it works with: on Drupal the module’s machine name; on WordPress the plugin’s name, whether it is active, and its version as the plugin itself reports it.
- What it needs (
requires()): every class, method, function and service it calls, written down, and checked on the site before it does anything. - The versions it is verified on (
verified()): a range, from and through. In this build each range is the one release whose source the adapter was written from.
Adapters ship inside the helper. A fixed or new adapter is an ordinary helper update: Wanigan says the helper in the site is older, and Update the helper… puts the new one in. See Turning it on.
The rules every adapter follows
- Core first. Words core can save (a field, a block’s settings, a menu link, configuration) go through core, with no adapter.
- The plugin’s own save, never its storage. An adapter saves through the call the plugin publishes, such as Elementor’s
Document::save()or Webform’ssetElementProperties(). It never rewrites a plugin’s stored layout, JSON, YAML or table rows itself. - Check what is there, not the version number. Before it acts, an adapter checks that everything it calls exists on this site. The version decides only what you are told.
- Every save can be checked. After a save the page reloads and is read back, and Verify compares the site with the design. In this build Verify runs when you choose it, as for any edit.
- Say which adapter saved. Each save through an adapter answers with the adapter, the plugin’s name and version, and whether that version is verified. This build’s Edits panel does not show that yet: its message says the words were saved, with the revision.
The adapters in this build
| Adapter | Reads | Saves through | Verified on |
|---|---|---|---|
| Paragraphs (Drupal) | Words in a paragraph, or any piece kept as a revision of the page that holds it | The page that holds it, saved as a new revision | Paragraphs 1.23 |
| Paragraphs Library (Drupal) | Whether a paragraph lives in a library item, and how many pages use it | The library item, once you confirm its reach | Paragraphs 1.23 |
| Layout Paragraphs (Drupal) | Whether a Layout Paragraphs builder has the page open | Nothing: it refuses a save its builder would write over | Layout Paragraphs 3.0.0 |
| Webform (Drupal) | A form’s words, marked where the page shows them | Webform’s own API, then the form’s own save | Webform 6.3.1 |
| Elementor (WordPress) | The elements tree Elementor renders from | Elementor’s own Document::save() | Elementor 4.3.4 |
| ACF (WordPress) | Fields, repeater rows, ACF blocks and options | ACF’s own acf_update_value(), after its validation | No range yet |
| Known copies (WordPress) | Copies some plugins keep that never render, and builders’ own layouts | Nothing: found, said, and refused | Not versioned |
Pathauto, which makes Drupal addresses from titles, needs no adapter: every Drupal save answers with the content’s address before and after, so a new address takes the view there.
Paragraphs
Paragraphs builds a Drupal page from stacked pieces, each kept as a revision that belongs to the page holding it. A paragraph saved alone would change the page but skip its workflow and log, and the next publish of a draft would bring the old words back. So the adapter saves through the page that holds it, as Paragraphs’ own form does.
- It walks up from the paragraph to the page that holds it, checks the page has no newer draft and that you may update it, finds the very paragraph the page showed, changes it, and saves the page as a new revision with its log, user and time.
- Validation is judged as for any field save, the workflow’s included, which a paragraph’s own validation never sees. A problem the page already had in a field the change does not touch is noted, not refused.
- Refused, saying why: a paragraph with no translation in the page’s language, a translatable paragraphs field (a setup Paragraphs does not support), a paragraph held twice by one page, and a paragraph inside a Layout Builder inline block.
- A Paragraphs Library item is shown on every page that uses it: it says how many uses it has, and is saved only when you make it real again.
- Layout Paragraphs keeps an open builder’s own copy of the page and writes it back over anything saved meanwhile. A save while one is open is refused: save or close the builder first.
- On a site without Paragraphs, a piece of this kind is still never saved alone.
Webform
Webform is a Drupal module for building forms. Where it is installed, the adapter saves a form’s own words through Webform’s API: an element’s title, description, help, placeholder, prefix, suffix or option labels, its buttons and its confirmation message, in the form’s language or its translation.
- Words are read where Webform has prepared them, after its translation and tokens are applied, so only words stored exactly as the page shows them are saved.
- An element is saved with every property it stores kept, as Webform’s own element form saves it.
- A button showing the site’s default gets words of its own on that form only. The default for every form, and options several forms share, say how many forms they reach and save only when you confirm.
- Words a token makes, a computed value, a wizard page’s own button label, words a variant changes and untranslated words are shown with why they are not saved.
- A webform is configuration: the next configuration import puts back what is exported, so export the configuration to keep the change.
Elementor
Elementor is a WordPress page builder. A page built with it renders from Elementor’s own tree of elements, and the post’s stored content is only a plain-text copy Elementor writes for search. So the adapter reads the words in the tree Elementor renders, and saves through Elementor’s own save: its CSS and element cache are cleared and a revision is kept, as when you save in Elementor.
- Only an element’s settings are searched, never its id or type, and never the settings Elementor keeps for itself.
- The page’s own Elementor marks tell two widgets with the same words apart, and find a template the page shows, such as a header. A template says it shows wherever it is used, and is saved only when you say so.
- Refused: changes in Elementor’s editor not saved yet (a newer autosave), an element Elementor cannot load now (a widget whose plugin is off, which its save would drop), and a user Elementor says may not edit it.
- Undo saves the tree as it was through Elementor’s own save again, never by writing its data directly.
- For the live view’s own requests only, Elementor renders every element afresh rather than from its element cache, so the trace sees every element and a read-back sees the new words.
ACF
Advanced Custom Fields (ACF) adds custom fields to WordPress. The stored-words search finds a field’s value, and the adapter recognises it as ACF’s and saves it through ACF, after ACF’s own validation (a required field, a text field’s length).
- Text, text area, formatted text (one run of words inside its markup, every other byte kept) and a link’s title are saved through ACF.
- A field whose value is a choice, a number, a date, an address, a file or a reference is refused: change it in the editor. Another plugin’s field type is left to the core save.
- A row of a repeater or flexible content field, and the values of an ACF block, are found by their own names and saved as ACF saves them.
- An option every page shows is said first, saved only when you make it real again, and only by a user who may manage the site’s options.
- A custom field keeps no revision, so Undo, for ten minutes, is its only way back.
In this build ACF’s adapter runs whenever ACF’s own save functions are there. It does not yet declare a list of what it calls or a verified range, so it cannot yet refuse by name or say whether your ACF version is verified.
Copies and other builders
Some plugins keep a second copy of words that never renders: a stripped copy for search, a rendered cache, a mirror of their own table. Saved into, the copy would report success while the page never changed. So these are known and never saved into:
- Beaver Builder: a page renders from its own layout, and the post’s stored content is a copy. Its words are found in the layout, read only, and refused: Wanigan does not save into Beaver Builder’s layout yet, so change it in Beaver Builder.
- SiteOrigin Page Builder: the same; its layout is refused, saying so. A SiteOrigin widget block’s rendered copies are never saved, since the widget rebuilds them.
- SEO plugins’ mirrors: a copy of their own table, which is what the page shows, so saving the copy would change nothing. Never written.
- Other builders’ own page data, and Elementor’s own data when Elementor is not loaded: not saved into by the stored-words search. Each refusal says whose data it is.
None of these fields is offered by the trace as a field to type into, either.
Ready, unverified, broken, absent
Before an adapter marks or saves anything, the helper asks for its state:
| State | When | What happens |
|---|---|---|
| Ready | Everything it calls is there, on a verified version | It saves |
| Unverified | Everything it calls is there, but the version is outside the verified range, or unknown, as a development build is | It saves, and its answer marks the version unverified |
| Broken | Something it calls is missing | Every save through it is refused, naming what is missing |
| Absent | The module or plugin is not installed or active | It does nothing |
A broken adapter’s refusal reads, for example: “Elementor 5.0 no longer has Elementor\Core\Base\Document::save, which Wanigan saves Elementor words through. Nothing was saved: update the helper when a Wanigan release supports this version.” A plugin update that changes what an adapter relies on shows up as a refusal with its reason, never as words quietly saved in the wrong place.
Versions are compared as plain dotted numbers, after dropping a Drupal 8.x- prefix. A development build counts as unknown.
When an adapter refuses
Read the refusal
Make it real leaves the edit as a draft in Edits (On a Mac, ⇧E; on Windows and Linux, Shift+E), with the plugin’s name and what is missing or why, as the site said it.
Update the helper
If the plugin was updated, a newer Wanigan may carry a newer adapter. In Site settings…, the helper’s section says when this Wanigan has a newer helper; press Update it….
Or change it another way
Change the words in the plugin’s own editor, or choose Tell an agent…: the note carries why Wanigan did not save it.
What has been checked
| Adapter | Written from | Checked with |
|---|---|---|
| Paragraphs, Paragraphs Library, Layout Paragraphs | Paragraphs 1.23 and Layout Paragraphs 3.0.0, read from their source | A stand-in Drupal, through the helper’s real save |
| Webform | Webform 6.3.1, read from its source | A stand-in Drupal and Webform |
| Elementor | Elementor 4.3.4, read from its source | Stand-ins made in the shape of its public code |
| ACF | The free ACF 6.8.10; ACF PRO’s repeaters, blocks and options from ACF’s documentation | Stand-ins made in the shape of its public code |
| Known copies | Each plugin’s public source | Stand-ins |
Keeping up with plugin releases
The design is a compatibility watch: a job run every week and for every Wanigan release that checks each adapter against its plugin’s newest release.
- It reads the newest release of each adapter’s plugin from wordpress.org or drupal.org.
- A static check, in seconds: everything in the adapter’s
requires()list still exists in the release, methods with the same number of parameters. - A run check, in minutes: the release installed on a throwaway copy of Wanigan’s own demo sites, never a real site, and the adapter’s test run on it: trace, edit, save, verify.
- A compatibility table of adapter, plugin version, both results and the date. A pass extends the adapter’s verified range in the next helper release.
- On a failure, an issue carrying what changed in the plugin. An agent can draft the fix, but only when a person clicks, and the fix is reviewed like any other change.
The watch is not built yet: the design builds it after the first three adapters in its order of work, so it has something to check. Until then each verified range is the single release the adapter was written from, and a newer plugin release is marked unverified and still saved, or refused by name if something it calls is gone.
Adapter fixes reach sites only with Wanigan releases, through Update the helper. A signed feed that would bring fixes between releases would also mean code entering sites from outside a release, so it is an open decision, not built.
The design’s order of work, ranked by sites reached and by risk, starts with Paragraphs, Elementor, a cache bypass-and-purge adapter and language routing, then a commerce plugin, form plugins and Webform.
Plugins with no adapter
Most plugins will never have an adapter, and they do not need one:
- The stored-words search finds words a plugin keeps as plain words (in a post’s content, a custom field, a Drupal field’s value) and saves them through core. It leaves structured data and copies that never render alone.
- Anything else goes to an agent with its evidence: where the words were found and which plugin made them. The agent reads the plugin’s current code, and the same Verify checks its work.
Words a plugin works out each time and never stores, and a builder’s own cache, are beyond any save. See Make real.
A plugin that fights you
If a plugin keeps words Wanigan cannot reach, or an adapter refuses after an update, tell us which plugin and which version, and the refusal word for word. See how to tell us.
In depth
The adapter contract
| Drupal | WordPress | |
|---|---|---|
| Where | src/Adapter/*.php in the helper module | A class in the must-use plugin |
| Base | AdapterInterface | Wanigan_Live_Adapter |
| Id | id(), such as paragraphs | id(), such as elementor |
| What it adapts | module(): the module’s machine name | extension(): name, whether active, and version, from the plugin’s own constant or function |
| Checked on the site | class:, method:, function:, service:, entity_type: | class:, method:, function:, constant: |
| Checked by the watch only | hook:, config: | hook:, meta:, option: |
| Verified on | verified(): from and through | The same |
| State | Adapters::status() | Wanigan_Live_Adapters::status() |
The requires() list is also what the watch reads, so an adapter cannot depend on something the watch does not check. Hooks, configuration, meta and options are left to the watch: they cannot make an adapter broken on the site.
Each adapter calls its plugin only inside its own source, and the core path never depends on one: a site without the plugin loads nothing of it. On WordPress, page builders are asked first.
The design also has the trace report each adapter’s state, so the Inspector could say which adapter saves what. That is not built yet: the state is asked when an adapter marks or saves, and goes with its answer.
