For developers¶
The module's extension surface is a set of Symfony events plus a handful of services. There is no plugin type of its own: the action links are an extra field, and the per-field switches are field formatter third-party settings.
| Extending | Use |
|---|---|
| Who may add, move, delete or duplicate an item | Access control events |
| Making links work in another rendering pipeline | Rendering pipeline events and Rendering pipelines |
| Reading or changing field lineage | FieldReferenceHelper |
| How requests are issued | AJAX transports |
Architecture¶
flowchart TB
Module[frontend_editing.module<br/>thin dispatcher] --> Hooks[src/Hook/ services]
Hooks --> ViewAlter[ViewAlter]
ViewAlter --> Helper[FieldReferenceHelper]
Helper --> Links[Action link render elements]
ViewAlter -. dispatches .-> Events[Collect / Resolve / Render events]
Events -. subscribed by .-> Subs[Display Builder and<br/>Layout Builder sub-modules]
Links --> Controller[FrontendEditingController]
Controller --> Helper
Hook implementations live in src/Hook/ as autowired services. The .module file is a dispatcher
that calls them through #[LegacyHook]. Do not put logic in the .module file.
Where things are¶
src/
Ajax/ Custom AJAX commands the controller returns
Controller/ Form loading, toggle, item operations, content updates
Event/ Access control events and rendering pipeline events
Form/ Settings, the bundle selector, the add-reference form
Hook/ Hook implementations as services
Plugin/ The extra field display plugin and the top bar item
Routing/ Route subscriber
modules/
frontend_editing_paragraphs/
frontend_editing_display_builder/
frontend_editing_layout_builder/
Key routes¶
| Route | Path | Purpose |
|---|---|---|
frontend_editing.settings_form |
/admin/config/frontend-editing |
Settings |
frontend_editing.entity_bundle_restrictions |
/admin/config/frontend-editing/entity-bundle-restrictions |
Entity types and bundles |
frontend_editing.form |
/frontend-editing/form/{type}/{id}/{operation}/{display} |
The entity form the sidebar iframe loads |
frontend_editing.item_add_page |
/frontend-editing/entity-reference/add/… |
The add panel |
frontend_editing.entity_reference_item_add |
/frontend-editing/entity-reference/{bundle}/add/… |
Create and insert a new item |
frontend_editing.item_move_up, …_move_down |
/frontend-editing/…/{up,down} |
Reorder |
frontend_editing.entity_reference_item_delete |
/frontend-editing/…/delete |
Delete |
frontend_editing.entity_reference_item_duplicate |
/frontend-editing/…/duplicate |
Duplicate |
frontend_editing.update_content |
/frontend-editing/update-content/{entity_type_id}/{entity_id}/{field_name}/{view_mode} |
Re-render one field for an AJAX swap |
frontend_editing.toggle |
/frontend-editing/toggle |
Flip the per-user on/off state |
Every route that mutates a specific field item takes an optional ?delta=N query parameter
identifying which occurrence is meant. A new route of that kind should follow the same convention
rather than inventing another one. See
Managing items.
Testing¶
# Kernel and Unit tests need a database connection.
ddev exec "cd /var/www/html && SIMPLETEST_DB='mysql://db:db@db/db' \
vendor/bin/phpunit -c web/core/phpunit.xml.dist \
web/modules/contrib/frontend_editing/tests"
Functional and FunctionalJavascript tests additionally need SIMPLETEST_BASE_URL and a
Selenium or Chromedriver service the web container can reach.
New test classes need #[RunTestsInSeparateProcesses], which Drupal 11.3 deprecates the absence of
and Drupal 12 requires.
Tests for content moderation live in two places. The main module's Kernel tests
(RevisionHelper*Test, RevisionRedirectTest, FieldReferenceHelperRevisionTest) run without
Paragraphs. Those that need paragraphs are in
modules/frontend_editing_paragraphs/tests/src/Kernel/, plus the browser test
FunctionalJavascriptTests/ModerationRedirectTest.php. They need workflows and
content_moderation, and ContentModerationTestTrait to set up the Editorial workflow.
The save queue is private to the current user. A test that changes user after queueing finds an empty queue.
Three things catch people out in kernel tests:
$entity->get($field)->get($delta)->entitydoes not set_referringItem. Only a field formatter's own render pass does. Since most of this module reads_referringItemto find the parent, test helpers have to set it by hand.- Node access needs
access contentas well as the operation permission. Core forbids everything without it, before it looks at the operation. - A field with no matching
EntityViewDisplay::setComponent()renders empty, becausefield_uiis not installed to add it for you. That looks identical to a real bug.