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/Private/, 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.
  • 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-persons/apply-settings-to-tca and academic-jobs/apply-settings-to-tca of the listeners that apply the persons and the jobs settings to the compiled TCA, so that a listener of \TYPO3\CMS\Core\Configuration\Event\AfterTcaCompilationEvent can be ordered after them. The listener classes are not public API.
  • The format of Configuration/CategoryTypes.yaml, see the category types chapter of category_types.
  • The routing aspect type CategoryFilterMapper of category_types and its settings, see the routing chapter of category_types. The class behind it is not public API.
  • The format of Configuration/FrontendIcons.php, the frontend icon registry of academic_base: the format of Configuration/Icons.php, read from every active package, and a package loaded later replaces an identifier, see Frontend icons.
  • The view helper icon of academic_base, in the namespace http://typo3.org/ns/FGTCLB/AcademicBase/ViewHelpers, by tag name and arguments, which renders an icon of the frontend icon registry, and the identifier default-not-found of the icon it renders for an unknown identifier. The class behind it is not public API.
  • The view helper frontendIconMap of academic_base, in the same namespace, by tag name and arguments, and the JSON data block it renders: the element <script type="application/json" data-academic-icons> , its data-academic-icons-* 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.
  • The icon endpoint of academic_base, _academic/icons.json 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.
  • The JavaScript module @fgtclb/academic-base/frontend/icons.js of academic_base and its exports, IconFactory , endpointFrom() , Sizes , Size , IconEndpoint and IDENTIFIER_PATTERN , see The icon factory.
  • The view helper ct:categoryTypeTitle 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.

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 @api says 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 #[AsEventListener] 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\AcademicBase\Event\ModifyTcaSelectFieldItemsEvent 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\AcademicBase\Event\ModifyPluginViewEvent 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\AcademicBase\Event\CollectFrontendIconsEvent 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/FrontendIcons.php with the same identifier replaces a contributed icon
\FGTCLB\AcademicBiteJobs\Event\ModifyBiteJobPostingsRequestEvent 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\AcademicBiteJobs\Event\ModifyBiteJobPostingsEvent 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\AcademicContacts4pages\Event\ModifyPageContactsEvent 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\AcademicJobs\Event\AfterSaveJobEvent in the job form plugin, after a submitted job is saved change the page redirected to, and how the confirmation message is shown
\FGTCLB\AcademicPartners\Event\ModifyPartnerDemandEvent in the partner list and the partner map, before the partners are queried replace the demand
\FGTCLB\AcademicPartners\Event\ModifyPartnerListEvent in the partner list and the partner map, after the query replace the partners and the categories, assign further view variables
\FGTCLB\AcademicPrograms\Event\ModifyProgramDemandEvent in the program list and the program finder, before the programs are queried replace the demand
\FGTCLB\AcademicPrograms\Event\ModifyProgramListEvent in the program list and the program finder, after the query replace the programs and the categories, assign further view variables
\FGTCLB\AcademicPrograms\Event\ModifyProgramDataEvent 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\AcademicProjects\Event\ModifyProjectDemandEvent in the project list, before the projects are queried replace the demand
\FGTCLB\AcademicProjects\Event\ModifyProjectListEvent in the project list, after the query replace the projects and the categories, assign further view variables
\FGTCLB\AcademicPersons\Event\ModifyProfileDemandEvent 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\AcademicPersons\Event\ModifyProfileQueryEvent 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\AcademicPersons\Event\ModifyContractQueryEvent right before the contract query of the selected-contracts plugin is executed add conditions
\FGTCLB\AcademicPersons\Event\ModifyProfileTitlePlaceholderReplacementEvent 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\AcademicPersons\Event\ModifyProfileImageMetadataEvent 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\AcademicPersons\Event\AfterProfileUpdateEvent after a profile was created or updated: by a backend save or an import through the DataHandler, by the commands academic:createprofiles and academic:updateprofiles , 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\AcademicPersons\Event\BeforeImportedRecordWriteEvent 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\AcademicPersons\Event\ChooseProfileFactoryEvent in the commands academic:createprofiles and academic:updateprofiles , once per frontend user choose the profile factory that creates or updates the profile
\FGTCLB\AcademicPersons\Event\BeforeProfileMappedFromFrontendUserEvent in the commands academic:createprofiles and academic:updateprofiles , 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_sync flag is set) add or change values of the data, skip the frontend user on creation or the profile on update
\FGTCLB\AcademicPersons\Event\AfterProfileMappedFromFrontendUserEvent in the same commands, after the data of a frontend user was mapped and before the profile is saved change the profile
\FGTCLB\AcademicPersonsEdit\Event\BeforeProfileEditingWriteEvent 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:createprofiles and academic:updateprofiles dispatch before they set up the environment of a frontend user's site, ModifyProfileCommandEnvironmentStateBuildContextForFrontendUserEvent , 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 BeforeProfileEditingWriteEvent from 3.0 on.

Adding a variable to the view of a plugin 

One listener of ModifyPluginViewEvent 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 ProjectListSingle for example, and the extension name tells two plugins of the same name apart:

EXT:my_sitepackage/Classes/EventListener/AddOfficeHoursLink.php
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);
    }
}
Copied!

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\AcademicBase\Domain\Model\Dto\PluginControllerActionContextInterface 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\AcademicPersons\Domain\Model\Dto\PluginControllerActionContextInterface 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\AcademicPersons\Domain\Model\Dto\DemandInterface the profile demand and query events
\FGTCLB\AcademicPersons\Domain\Model\Dto\ProfileDemand 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\AcademicPartners\Domain\Model\Dto\PartnerDemand the partner demand and list events
\FGTCLB\AcademicPrograms\Domain\Model\Dto\ProgramDemand the program demand and list events
\FGTCLB\AcademicPrograms\Domain\Model\ProgramData the program data event
\FGTCLB\AcademicProjects\Domain\Model\Dto\ProjectDemand the project demand and list events
\FGTCLB\CategoryTypes\Collection\CategoryCollection the partner, program and project list events
\FGTCLB\CategoryTypes\Collection\FilterCollection the partner, program and project demands, which carry the category filter of the request
\FGTCLB\AcademicPersons\Profile\ProfileFactoryInterface the profile factory event, which takes an implementation of it
\FGTCLB\AcademicPersons\Event\ProfileUpdateOrigin the profile update event
\FGTCLB\AcademicPersons\Profile\ProfileActionType the profile factory event and the two events of the frontend user synchronisation
\FGTCLB\AcademicBase\Form\FlashMessageCreationMode the job save event (its old name in EXT:academic_jobs, \SaveForm\FlashMessageCreationMode , is a deprecated alias)
\FGTCLB\AcademicPersonsEdit\Event\ProfileEditingAction the write event of the profile editing
\FGTCLB\AcademicContacts4pages\Event\PageContactsOutput 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\CategoryTypes\Collection\GetCategoryCollectionInterface A model that carries categories of category_types implements it, as the partner, program and project models do.
\FGTCLB\AcademicPersonsEdit\Service\ProfileRichTextSanitizerInterface 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\AcademicPersons\Types\TypesInterface 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\AcademicPersons\DemandValues\DemandValuesInterface The same for the sorting and grouping values the profile list offers.
\FGTCLB\AcademicPrograms\Domain\Model\ProgramFactsSourceInterface 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\AcademicBase\Imaging\IconProvider\CurrentColorSvgIconProvider as the provider of an icon in Configuration/Icons.php or Configuration/FrontendIcons.php, for an SVG drawn in currentColor
\FGTCLB\AcademicContacts4pages\DataProcessing\ContactsProcessor as a data processor of a page template, for the contacts of the page, by its identifier academic-page-contacts or by class name
\FGTCLB\CategoryTypes\Backend\FormEngine\CategoryTypeItemsProcFunc as the itemsProcFunc of a select field of a project's TCA or FlexForm that offers category types
\FGTCLB\CategoryTypes\Registry\CategoryTypeRegistry injected, to read the registered category types
\FGTCLB\CategoryTypes\Backend\PageCategorySummaryRenderer injected into a listener that shows the category summary of a page type of its own in the page module
\FGTCLB\AcademicPersons\Profile\FrontendUserProfileMapper injected into a profile factory of its own, to apply the configured map of frontend user fields
\FGTCLB\AcademicPersons\DataHandling\ProfileWriteCorrelation in import code, as ProfileWriteCorrelation::Import , to mark a DataHandler run that writes profiles, so the profile update event announces it with the origin ProfileUpdateOrigin::Import ; and as ProfileWriteCorrelation::Internal in a listener of the profile update event that writes profiles through the DataHandler, so that write is not announced again
\FGTCLB\AcademicPersons\Import\ImportedRecordFinder injected into import code, to find the person record that carries an import identifier, so the next run updates it
\FGTCLB\AcademicPersons\Import\ProfileImportWriter injected into import code, to create and update the persons of a source and to retire the ones it no longer supplies
\FGTCLB\AcademicPersons\Import\ImportedProfile , \FGTCLB\AcademicPersons\Import\ImportedContract , \FGTCLB\AcademicPersons\Import\ImportedContact created by import code, one person each, and handed to the import writer
\FGTCLB\AcademicPersons\Import\RetirePolicy handed to the import writer, to hide or to delete what a source no longer supplies
\FGTCLB\AcademicPersons\Import\ImportResult , \FGTCLB\AcademicPersons\Import\ImportedRecordResult , \FGTCLB\AcademicPersons\Import\ImportedRecordOutcome returned by the import writer, what happened to each record

Base classes 

Class What it is for
\FGTCLB\AcademicPersons\Profile\AbstractProfileFactory 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 createProfileFromFrontendUser() , which may return null to create nothing, and updateProfileFromFrontendUser() .

Traits 

Trait What it is for
\FGTCLB\AcademicBase\Controller\GetCurrentContentRecordMethodTrait 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\AcademicBase\Controller\GetSelectItemsForTcaManagedTableFieldMethodTrait 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 GeneralUtility::getClassName() , so the way to do that is a subclass registered as the XCLASS of the model:

EXT:my_extension/ext_localconf.php
$GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][\FGTCLB\AcademicPersons\Domain\Model\Profile::class] = [
    'className' => \MyVendor\MyExtension\Domain\Model\Profile::class,
];
Copied!

Extbase maps the subclass by its own class name, so it needs an entry in the project's Configuration/Extbase/Persistence/Classes.php: the tableName of the model, and its recordType 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\AcademicContacts4pages\Domain\Model\Contact , \FGTCLB\AcademicContacts4pages\Domain\Model\Role
academic_jobs \FGTCLB\AcademicJobs\Domain\Model\Job
academic_partners \FGTCLB\AcademicPartners\Domain\Model\Partner , \FGTCLB\AcademicPartners\Domain\Model\Partnership , \FGTCLB\AcademicPartners\Domain\Model\Role
academic_persons \FGTCLB\AcademicPersons\Domain\Model\Address , \FGTCLB\AcademicPersons\Domain\Model\Contract , \FGTCLB\AcademicPersons\Domain\Model\Email , \FGTCLB\AcademicPersons\Domain\Model\FrontendUser , \FGTCLB\AcademicPersons\Domain\Model\FunctionType , \FGTCLB\AcademicPersons\Domain\Model\Location , \FGTCLB\AcademicPersons\Domain\Model\OrganisationalUnit , \FGTCLB\AcademicPersons\Domain\Model\PhoneNumber , \FGTCLB\AcademicPersons\Domain\Model\Profile , \FGTCLB\AcademicPersons\Domain\Model\ProfileInformation
academic_persons_sync \FGTCLB\AcademicPersonsSync\Domain\Model\FrontendUser
academic_programs \FGTCLB\AcademicPrograms\Domain\Model\Program
academic_projects \FGTCLB\AcademicProjects\Domain\Model\Project
category_types \FGTCLB\CategoryTypes\Domain\Model\Category , \FGTCLB\CategoryTypes\Domain\Model\CategoryType

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.