Extension points
This page is the contract between the academic extensions and the code of a project. It covers every academic extension and category_types, and it is a complete list: what it names is public API, and what it does not name is not.
Every class, interface, trait and enum listed here carries the
@api
tag in its docblock, so the promise is visible in the code as well. A test of
the extensions' own repository makes sure the tags and this page name the
same classes.
What the promise means
Public API does not change silently. A release that breaks or deprecates anything listed on this page says so in the changelog of the extension:
- a Breaking entry when existing code stops working, with the migration;
- a Deprecation entry when the old way keeps working until the next major version, and says what replaces it.
Anything else may change in any release, a bugfix release included, and without a changelog entry. Code that relies on it has to be checked on every update.
What is public API
- The events, and the types they hand to a listener.
- The interfaces.
- The services and classes a project names in its configuration or its code.
- The base class of a profile factory.
- The two traits of academic_base for Extbase controllers.
- The domain models: their public getters, which the templates read, and extending them to add fields.
- Templates, partials and sections: their paths below
Resources/, the names of their sections, the variables they receive, and the ViewHelpers they call, by tag name and arguments. The Templates chapter of each extension describes how to override them.Private/ - Settings: the site settings of the site sets, the TypoScript constants and settings, and the settings of the content elements.
- The TypoScript and page TSconfig keys the extensions document.
- The keys of the language files, which a site overrides labels by.
- The identifiers
academic-andpersons/ apply- settings- to- tca academic-of the listeners that apply the persons and the jobs settings to the compiled TCA, so that a listener ofjobs/ apply- settings- to- tca \TYPO3\can be ordered after them. The listener classes are not public API.CMS\ Core\ Configuration\ Event\ After Tca Compilation Event - The format of
Configuration/, see the category types chapter of category_types.Category Types. yaml - The routing aspect type
Categoryof category_types and its settings, see the routing chapter of category_types. The class behind it is not public API.Filter Mapper - The format of
Configuration/, the frontend icon registry of academic_base: the format ofFrontend Icons. php Configuration/Icons.php, read from every active package, and a package loaded later replaces an identifier, see Frontend icons. - The view helper
iconof academic_base, in the namespacehttp://, by tag name and arguments, which renders an icon of the frontend icon registry, and the identifiertypo3. org/ ns/ FGTCLB/ Academic Base/ View Helpers default-of the icon it renders for an unknown identifier. The class behind it is not public API.not- found - The view helper
frontendof academic_base, in the same namespace, by tag name and arguments, and the JSON data block it renders: the elementIcon Map <script type="application/, itsjson" data- academic- icons> data-attributes and the object of identifier and markup it holds, see Icons in frontend JavaScript. Which icons it serves is part of the contract, see Which icons are served. The class behind it is not public API.academic- icons-* - The icon endpoint of academic_base,
_academic/below the base of every site and site language: its path, its parameters, the JSON object it answers, its status codes and its caching headers, see The icon endpoint. The middleware behind it is not public API.icons. json - The JavaScript module
@fgtclb/of academic_base and its exports,academic- base/ frontend/ icons. js Icon,Factory endpoint,From () Sizes,Size,IconandEndpoint IDENTIFIER_, see The icon factory.PATTERN - The view helper
ct:of category_types, by tag name and arguments, which names a category type in a template by its registered title, see naming a type in a template. The class behind it is not public API.category Type Title
What is not public API
Everything else. In particular:
- Controllers. Every plugin controller is
final, so neither a subclass nor an XCLASS of one loads. A plugin is extended through its events: every plugin action that renders a view dispatches the plugin view event, except the profile editing of academic_persons_edit, which offers each of its writes to an event instead. The partner, program and project lists and the program finder dispatch a demand and a list event as well, and the B-ITE job list a request and a result event. - Repositories. A condition a plugin should apply belongs in a demand or a query event, not in an XCLASS of the repository.
- Services, data processors, ViewHelper classes, backend item providers, hooks, event listeners, commands and upgrade wizards, except the few a project names. A service is replaced through the container where it is registered behind one of the interfaces below, never by subclassing it.
- Everything marked
@internal. The marker says in the code what the absence of@apisays for the rest.
An XCLASS is unsupported for every class but a domain model, listed or not: a listed class keeps what it promises to a caller, not what it offers to a subclass. The upgrade check reports such an XCLASS as a warning, and as an error when the class is final or gone. How to extend a model is described under Domain models.
Events
Every event is a
final
class, dispatched through the PSR-14 event
dispatcher of TYPO3. A listener registers for it with the
# attribute of TYPO3. The developer chapters of
academic_bite_jobs,
academic_contacts4pages,
academic_persons,
academic_persons_edit,
academic_partners,
academic_programs
and
academic_projects
describe their events in detail, with examples.
| Event | Dispatched | A listener may |
|---|---|---|
\FGTCLB\ | when the backend builds the items of a select field of academic_jobs (type, employment type), academic_persons (the contract and the shown fields of the plugins) or academic_contacts4pages (the contract and the addresses of a contact), and when the job form of academic_jobs builds its type and employment type selects in the frontend | replace the items and the other item provider parameters |
\FGTCLB\ | in every plugin of academic_bite_jobs, academic_contacts4pages, academic_jobs, academic_partners, academic_persons, academic_programs and academic_projects, once each time an action renders its view, after the action assigned its own variables | assign further view variables, see Adding a variable to the view of a plugin |
\FGTCLB\ | once each time the frontend icon registry is built, when the system caches are built or warmed up, not per request | contribute icons from code, see Frontend icons.
An entry of a Configuration/ with the
same identifier replaces a contributed icon |
\FGTCLB\ | in the job list of academic_bite_jobs, after the request to the B-ITE API is built from the plugin settings and before it is sent | replace the payload of the request |
\FGTCLB\ | in the job list of academic_bite_jobs, after the response of the B-ITE API is decoded, also after a failed request, and before the limit is applied | replace the postings |
\FGTCLB\ | in the contacts content element and in the data processor of the page contacts, after the contacts of the page are read and before they are grouped by role | replace the contacts |
\FGTCLB\ | in the job form plugin, after a submitted job is saved | change the page redirected to, and how the confirmation message is shown |
\FGTCLB\ | in the partner list and the partner map, before the partners are queried | replace the demand |
\FGTCLB\ | in the partner list and the partner map, after the query | replace the partners and the categories, assign further view variables |
\FGTCLB\ | in the program list and the program finder, before the programs are queried | replace the demand |
\FGTCLB\ | in the program list and the program finder, after the query | replace the programs and the categories, assign further view variables |
\FGTCLB\ | on a program page, after its data is built from the page record and before the facts are built from it | replace the data the page template receives |
\FGTCLB\ | in the project list, before the projects are queried | replace the demand |
\FGTCLB\ | in the project list, after the query | replace the projects and the categories, assign further view variables |
\FGTCLB\ | in the list, list-and-detail and card plugins, before the profile query and the query of the letter navigation are built from the demand | change or replace the demand |
\FGTCLB\ | right before the profile query of the list, list-and-detail and card plugins, of the letter navigation and of the selected-profiles plugin is executed | add conditions |
\FGTCLB\ | right before the contract query of the selected-contracts plugin is executed | add conditions |
\FGTCLB\ | when the page title of a profile's detail view is built, once per placeholder of the title format | replace the value of the placeholder |
\FGTCLB\ | right before the metadata of a profile image is written, for the file and for the file reference | change or empty the fields that are written |
\FGTCLB\ | after a profile was created or updated: by a backend save or an
import through the DataHandler, by the commands
academic: and
academic:,
and by the profile editing of academic_persons_edit | react to it; the profile, its site and the origin of the update are read only |
\FGTCLB\ | in the import writer of academic_persons, once for every record of a person it is about to write | change the values that are written, veto the record |
\FGTCLB\ | in the commands
academic: and
academic:, once per frontend user | choose the profile factory that creates or updates the profile |
\FGTCLB\ | in the commands
academic: and
academic:, before the data of a frontend
user is mapped: once per frontend user on creation, once per
synchronised profile on update (not for a profile whose
skip_ flag is set) | add or change values of the data, skip the frontend user on creation or the profile on update |
\FGTCLB\ | in the same commands, after the data of a frontend user was mapped and before the profile is saved | change the profile |
\FGTCLB\ | in the profile editing of academic_persons_edit, once for every write the editor accepted, before anything of it is stored: the profile, its switches and image, and its documents and contacts | refuse the write with a reason the person is shown, or replace the values it stores, which are validated again |
One event is not on this list. The event the commands
academic: and
academic: dispatch
before they set up the environment of a frontend user's site,
Modify,
is marked
@internal
as experimental, like the environment handling it
belongs to. The event the 2.4 changelog of academic_persons_edit
mentions for filling form data from other sources before it is written is
Before from 3.0 on.
Adding a variable to the view of a plugin
One listener of
Modify serves every plugin. The context
it hands over names the plugin and the action, so a listener that means one of
them checks both. The plugin name is the one the plugin is registered with,
List
,
Detail
or
Project for example, and the
extension name tells two plugins of the same name apart:
namespace MyVendor\MySitepackage\EventListener;
use FGTCLB\AcademicBase\Event\ModifyPluginViewEvent;
use TYPO3\CMS\Core\Attribute\AsEventListener;
final class AddOfficeHoursLink
{
#[AsEventListener]
public function __invoke(ModifyPluginViewEvent $event): void
{
$context = $event->getPluginControllerActionContext();
if ($context->getControllerExtensionName() !== 'AcademicPersons'
|| $context->getActionName() !== 'detail'
) {
return;
}
$event->getView()->assign('officeHoursPageId', 42);
}
}
The variable is then available to the templates, partials and sections of that plugin, and a template override renders it.
The event runs after the action assigned its own variables, so a listener that assigns one of them again replaces it in the view, and only there: the pagination and the page title keep the profiles the query returned, for example. A change of which records are shown belongs in a demand or a query event. One variable is assigned after the event on purpose and cannot be replaced at all: the validations of the job form.
What an event hands over
A listener types against whatever an event's methods declare, so these types are public API together with the events. Classes of TYPO3 and of other packages are not listed; their own documentation applies.
| Type | Handed over by |
|---|---|
\FGTCLB\ | every event a plugin action dispatches: the request, the site and
its language, the content object, the settings of the content
element and the plugin name. Every getter that can lack its value
returns
null
, without an error. The application type is the
exception: for a request without a type it can resolve, TYPO3
raises its own exception, which only a request built by hand can
cause. |
\FGTCLB\ | the title placeholder event of academic_persons, which still declares this copy of the interface above. It adds nothing to it, is deprecated, and is removed in 4.0; type a listener against the academic_base interface. |
\FGTCLB\ | the profile demand and query events |
\FGTCLB\ | the profile demand and query events as the demand of the list, list-and-detail and card plugins, which they declare as the interface above |
\FGTCLB\ | the partner demand and list events |
\FGTCLB\ | the program demand and list events |
\FGTCLB\ | the program data event |
\FGTCLB\ | the project demand and list events |
\FGTCLB\ | the partner, program and project list events |
\FGTCLB\ | the partner, program and project demands, which carry the category filter of the request |
\FGTCLB\ | the profile factory event, which takes an implementation of it |
\FGTCLB\ | the profile update event |
\FGTCLB\ | the profile factory event and the two events of the frontend user synchronisation |
\FGTCLB\ | the job save event (its old name in EXT:academic_jobs,
\Save, is a deprecated alias) |
\FGTCLB\ | the write event of the profile editing |
\FGTCLB\ | the page contacts event |
Interfaces
A project implements an interface, types against it, or aliases it to an implementation of its own where the container hands one out.
| Interface | What it is for |
|---|---|
\FGTCLB\ | A model that carries categories of category_types implements it, as the partner, program and project models do. |
\FGTCLB\ | The sanitizer the profile editing runs rich text through. A project replaces it by aliasing this interface to its own implementation, in an extension that depends on academic_persons_edit. |
\FGTCLB\ | What the lists of address, email and phone number types are read through. The lists come from the extension configuration, and the extension asks for them by their own class names, so a project changes them there; an implementation of its own is not used. |
\FGTCLB\ | The same for the sorting and grouping values the profile list offers. |
\FGTCLB\ | What the facts of a program are built from: the program model of the content elements and the data of the program page implement it. |
The abstract classes that implement some of them for the extensions themselves are not public API.
Services and classes a project names
These are named in a project's configuration, or injected into its code. Use them as they are; they are not meant to be subclassed or replaced.
| Class | Named |
|---|---|
\FGTCLB\ | as the
provider
of an icon in Configuration/
or Configuration/, for an SVG drawn in
current |
\FGTCLB\ | as a data processor of a page template, for the contacts of the
page, by its identifier academic- or by class name |
\FGTCLB\ | as the
items of a select field of a project's TCA or
FlexForm that offers category types |
\FGTCLB\ | injected, to read the registered category types |
\FGTCLB\ | injected into a listener that shows the category summary of a page type of its own in the page module |
\FGTCLB\ | injected into a profile factory of its own, to apply the configured map of frontend user fields |
\FGTCLB\ | in import code, as
Profile, to mark
a DataHandler run that writes profiles, so the profile update event
announces it with the origin
Profile;
and as
Profile in a listener of
the profile update event that writes profiles through the
DataHandler, so that write is not announced again |
\FGTCLB\ | injected into import code, to find the person record that carries an import identifier, so the next run updates it |
\FGTCLB\ | injected into import code, to create and update the persons of a source and to retire the ones it no longer supplies |
\FGTCLB\,
\FGTCLB\,
\FGTCLB\ | created by import code, one person each, and handed to the import writer |
\FGTCLB\ | handed to the import writer, to hide or to delete what a source no longer supplies |
\FGTCLB\,
\FGTCLB\,
\FGTCLB\ | returned by the import writer, what happened to each record |
Base classes
| Class | What it is for |
|---|---|
\FGTCLB\ | The base of a profile factory a project chooses with the profile
factory event. It dispatches the two events of the frontend user
synchronisation. A subclass implements the protected methods
create, which may return
null
to create nothing, and
update. |
Traits
| Trait | What it is for |
|---|---|
\FGTCLB\ | Returns the record of the current content element, which a
plugin assigns as the
record
view variable so that the
header of the content element renders on TYPO3 v14. |
\FGTCLB\ | Reads the items of a select field as the backend would offer them, with the item provider applied and the labels translated. |
Domain models
The domain models are what the templates read, so their public getters are
public API. A project may extend a model to add fields of its own. Extbase
creates a model through
General, so the way to
do that is a subclass registered as the XCLASS of the model:
$GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][\FGTCLB\AcademicPersons\Domain\Model\Profile::class] = [
'className' => \MyVendor\MyExtension\Domain\Model\Profile::class,
];
Extbase maps the subclass by its own class name, so it needs an entry in the
project's Configuration/Extbase/Persistence/Classes.php: the
table of the model, and its
record if the extension
declares one - a subclass inherits the
properties
of the parent entry,
but neither of those two. The new columns need their TCA and their database
fields; a
properties
entry is needed only for a column whose name does
not match the property. A model the extensions create themselves with
new
, a profile a profile factory creates for instance, is still of the
original class. The upgrade check lists such an XCLASS as a notice: it does not
fail the check, and it is a reminder to make sure the getters and setters the
subclass overrides still exist after an update.
| Extension | Models |
|---|---|
| academic_contacts4pages |
\FGTCLB\,
\FGTCLB\ |
| academic_jobs |
\FGTCLB\ |
| academic_partners |
\FGTCLB\,
\FGTCLB\,
\FGTCLB\ |
| academic_persons |
\FGTCLB\,
\FGTCLB\,
\FGTCLB\,
\FGTCLB\,
\FGTCLB\,
\FGTCLB\,
\FGTCLB\,
\FGTCLB\,
\FGTCLB\,
\FGTCLB\ |
| academic_persons_sync |
\FGTCLB\ |
| academic_programs |
\FGTCLB\ |
| academic_projects |
\FGTCLB\ |
| category_types |
\FGTCLB\,
\FGTCLB\ |
The models of category_types are not Extbase models; the extension builds them itself. Their getters are public API all the same, but they cannot be extended through an XCLASS.
The extension points every extension should have
Three kinds of extension point together make subclassing and XCLASSing unnecessary. Not every extension offers all three yet:
| Extension point | Offered today | Planned |
|---|---|---|
| A view event per plugin action, to assign further view variables | the plugins of the seven extensions named at the event above, through one event | none: the profile editing of academic_persons_edit has an event before each of its writes instead |
| A demand event per repository query a plugin runs, to change what is queried | academic_persons, academic_partners, academic_programs, academic_projects | the list plugins of the other extensions |
| An after-save event per write, to react to a saved record | the profile of academic_persons, the job form of academic_jobs | the other writes of the frontend editing |
A planned extension point is listed here once it is dispatched, and not before.