This TYPO3 extension adds a personal database to TYPO3, with requirements that
are usually used for colleges, universities or public institutions.
The profile data records can be automatically created and linked in conjunction
with the LDAP extension on the basis of FE user data records. The data can be
enriched with data via separate HiS-in-One synchronization. Data records can be
created, edited and displayed in the front end and in different display modes.
Learn about what has changed and which actions are required to
upgrade.
What does it do?
This extension provides record types for academic person profiles using the TYPO3
backend (list module/page layout module) to manage the profiles requiring backend
user account(s).
Further, plugins are provided to display the persons in the frontend as a list
view and detail view for each person.
The following profile data is available to users after installation:
Master data:
Salutation / Gender
title
First name
Last name
Middle name
Website + website link
Image
URL
Contracts:
Each person can receive any amount of contract data in order to be displayed
in individual roles, functions or organizational units.
Position
Organizational unit / department - link to own data type
Contract start / end
Location - e.g. for campus Link to own data type
Room information
Office hours
Address data
Email addresses
Telephone addresses
Linked pages (in combination with the Contact-For-Pages extension)
Employment category based on system categories
Profile text data:
All textual content can be freely designed using the standard text editor.
Learning areas/fields of activity
Research areas
Supervised dissertations
Supervised doctoral theses
Miscellaneous information
Profile timeline entries
All timeline entries allow the chronological presentation of content, usually
with a year or a start and/or end year, a title, a short description
and a link
Research projects
Academic career
Memberships/committee activities
Networks and cooperation's
Publications
Lectures
Press/Media Publications
Installation
The extension has to be installed like any other TYPO3 CMS extension. You can
download and install it using one of the following methods.
Version 3 requires TYPO3 13.4 or TYPO3 14.3, and PHP 8.2 or newer. Updating an
existing 2.4 installation is an ordered sequence rather than a single command -
see Upgrading from 2.4 to 3.0.0.
Install the stable release
composer require 'fgtclb/academic-persons':'^3'
Copied!
Tip
We recommend to pin academic extensions on minor level to mitigate
possible issues in projects in case composer update is used based
on the fact that projects commonly tends to override fluid templates
and changes for otherwise non-breaking changes are possible promoted
to be breaking in case template changes are not adopted why it has
been considered to mark template changes as breaking changes on
minor version updates. That means, we suggest to use for example
following command to ensure that we stay in the minor version range
but have the hightest patchlevel enforced for it and keep possible
bugfix releases for that minor version possible to install:
Get the current version from TER by downloading the ZIP version.
Alternatively, get the ZIP from the GitHub Releases page.
Switch to the module Admin Tools > Extensions.
Enable Upload Extension.
Select or drag the extension ZIP archive and upload the file.
Upgrading from 2.4 to 3.0.0
Version 3.0.0 changes the shape of
Configuration/AcademicPersons/Settings.yaml, the public detail template
and - together with
fgtclb/academic-persons-edit
- the complete
profile editing frontend. Each of those changes carries its own changelog entry
with the detail; this page is the order they have to be applied in.
The order matters. One wizard reads a database column TYPO3 v14 no longer has,
and one repairs relations the new editor then writes. Work through the steps
from top to bottom, on a copy of the production database first.
The steps at a glance
Step
What it does
What happens if it is skipped
Update the packages
Installs 3.0.0 of every academic extension of the installation.
Nothing below applies.
Run the plugin migration wizards, still on TYPO3 v13
Moves the list_type plugin records onto their own CType.
The content elements stop rendering on TYPO3 v14, where the column
the wizards read no longer exists.
Update the database schema
Applies the column changes of 3.0.0, among them the unsigned
timeline year columns and the workspace columns.
The wizard of step 4 finds no repaired schema and the editor
writes into columns the installation does not have.
Repair the profile image relations
Reduces duplicate references, corrects relation counters and marks
the translations that carry an image of their own.
A translation loses its own image at the next synchronisation, and
duplicate references keep rendering the wrong file.
Migrate the settings override
Replaces the pre-3.0 keys of a site package with the section maps.
The installation runs on the legacy overlay, which is removed in
4.0 - and a renamed type or fieldName stays silently
broken.
Adapt templates, icons and TypoScript
Re-applies project overrides to the new template tree and makes the
JSON page type reachable.
The editor cannot save, and an overridden detail view loses the
configurable layout.
Note
The TYPO3 core update itself is not a step of this page; it is an upgrade
of its own with its own manual. Where it belongs matters for exactly one
step: the plugin migration wizards of step 2 read a column TYPO3 v14 no
longer declares, so the core update comes after step 2. Every other
step works the same on TYPO3 v13 and on v14.
1. Update the packages
The academic extensions are released together and depend on each other, so they
are updated in one go:
In a classic (non-Composer) installation, update every academic extension in
the Extension Manager and activate rte_ckeditor, which
academic_persons_edit requires for its rich text fields since
3.0.0.
Flush all caches afterwards. The settings graph is cached in the core cache and
the cache identifier changed with the file format, so a stale entry is not read
back - but the TypoScript and TCA caches are.
2. Run the plugin migration wizards while still on TYPO3 v13
academicPersons_MigrateListTypeToCTypeContentElements and
academicPersonsEdit_pluginContent move the content elements of both
extensions from CType = list plus list_type onto their own CType.
Both read
tt_content.list_type
, and TYPO3 v14 removed that column.
Run them on TYPO3 v13, before the core update. TYPO3 v14 removed
tt_content.list_type
from the TCA, so the database analyzer of a v14
installation offers the column for removal and a fresh v14 installation never
has it. Both wizards ask the live schema: once the column is gone their content
element half finds nothing to do, and the records they would have migrated keep
a content type nothing renders. The academicPersons_ wizard also migrates
the explicit_allowdeny values of the backend user groups, and that half
stays available on both versions.
Warning
academicPersons_MigrateListTypeToCTypeContentElements is not
repeatable: a wizard that reports "nothing to do" is recorded as done and
disappears from the upgrade module. That is why this step names both
wizards instead of running a bare
vendor/bin/typo3 upgrade:run
- a
bare run on TYPO3 v14, with the column already removed, marks the wizard as
done although it migrated nothing. The flag is cleared with
but that only helps while
tt_content.list_type
still holds the
values. Once the column is dropped, nothing records which plugin such a
content element was, and the records have to be repaired by hand.
academicPersonsEdit_removeProfileSwitcherContent deletes the content
elements of the removed profile switcher plugin and handles both shapes, so it
can be run on either core version.
3. Update the database schema
vendor/bin/typo3 extension:setup
Copied!
In the backend the same thing is Admin Tools > Maintenance > Analyze
Database Structure. It applies every column change of 3.0.0. Nothing is
converted here and no value is rewritten: the timeline keeps its
year
,
year_start
and
year_end
columns, which only turn
unsigned because the corrected TCA declares a lower bound of 0.
Note
The analyzer reports a column that left ext_tables.sql as unused
and never drops it on its own. Accepting such an offer is a decision of the
installation, not a step of this upgrade.
4. Repair the profile image relations
Only relevant where academic_persons_edit is installed, and only
worth running where profiles have images.
The profile image column is translatable from 3.0.0 on. Until then it was
excluded from localisation and the upload path wrote the relation rows by hand,
which left three shapes behind that are defects under the new model: duplicate
references on one profile, a relation counter that disagrees with the number of
references, and a translation carrying its own reference without the custom
localisation state - the state that keeps the next synchronisation from
replacing it with a localisation of the default-language image.
The wizard repairs all three through the TYPO3 DataHandler, so the reference
index, the record history and the localisation state are the core's. No file
is deleted; a file left without a relation is for the unused files tooling
of the Install Tool to report. Prefer the command line over the Install Tool
module for it.
Run it after step 3 and before editors start working in the new editor of a
multilingual installation. It is repeatable as well; where a 3.0.0 pre-release
recorded it as done,
vendor/bin/typo3 upgrade:mark:undone
academicPersonsEdit_repairLocalizedProfileImages
offers it again. See
Breaking: The profile image translates.
5. Migrate the settings override
Only relevant for an installation whose site package ships
Configuration/AcademicPersons/Settings.yaml.
The pre-3.0 top-level keys
validations
and
profileInformationsTypes
are mapped onto the four section maps at
runtime, with a warning in the log, so such a file keeps working. The overlay is
transitional and is removed in academic_persons 4.0.
The command prints, for every active package that still ships a legacy key, the
profile
,
special
,
contracts
and
documentSections
maps the overlay produces - the document that replaces the legacy keys of that
package. It never writes the file, and it exits with 1 while such a package
exists, so a deployment pipeline can gate on it. Paste the printed maps into the
site package, review them, and flush all caches.
Warning
Read the notes the command prints. A renamed
type
or
fieldName
of a legacy
profileInformationsTypes
entry is
not applied, and is reported instead: since 3.0.0 the seven profile
relations and the record type each of them selects are declared by the TCA
of the profile table, and applying such a rename would move the editing
frontend alone, leaving the backend column and the editor writing
different record types. The section keeps the values that match the TCA,
so the installation is consistent - but the intention of the override is
silently not honoured. A timeline type of your own needs its own column in
a TCA override of the profile table, and its section under
documentSections
- see The document sections.
The same applies to a
type
or
fieldName
written directly
into the new
documentSections
map: that one is read, and it is
the shape that diverges from the TCA.
Resources/Private/Templates/Profile/Detail.html was rewritten: it is a
dispatcher over the
profile.structure
and
profile.details
layout, and every element is one partial below
Resources/Private/Partials/Profile/PublicProfile/.
An installation that overrides the detail template keeps rendering its own
copy, so nothing looks broken - and it loses the configurable layout
completely. The timeline properties it reads are unchanged: {item.year},
{item.yearStart} and {item.yearEnd} are still there and still integers.
Two of the partials the old detail
template rendered are now only used by the list and card views, and the third,
Partials/Profile/DataHeader.html, is deleted - a project template that
still renders it fails at render time. See
What an override of the detail template loses and
Breaking: The partials of the detail view change.
The profile editing view
The editing plugin of academic_persons_edit was replaced in place.
Every Fluid file of the removed form flow is gone, so an override of one of them
renders nothing; the new tree is Templates/Profile/Index.html with the
partials below Partials/Profile/, and the markup of the two regions the
browser builds is authored in Fluid as <template data-pe-proto> prototypes.
The profile editing chapter
of that extension names every file, every hook and the four prototype
attributes an override has to keep.
Icons
The icon set of the editor was replaced. Five identifiers of the form flow are
gone - academic-persons-edit-add-image, -add-item, -cancel,
-sort and -to-top - and the thirteen action icons of the new set are
registered in Configuration/Icons.php of
academic_persons_edit, under the identifiers listed in the icon
table.
A template or PHP file addressing a removed identifier renders TYPO3's
default-not-found placeholder. The new icons are inlined as <svg> rather
than emitted as <img>, so they follow the text colour - and a site
stylesheet that selects .t3js-icon img no longer matches them.
TypoScript and the JSON page type
The write path of the editor is a
PAGE
object with
typeNum = 1733735
, which is delivered by the site set
fgtclb/academic-persons-edit-profile-editing or by the static template
Academic Persons Edit: Profile editing. There was no such page type
in 2.4, because the old editor was a server-rendered form flow.
Check that the site really includes one of the two. A site package that
copied the extension's TypoScript into its own instead of including it renders
the new editor and would answer every save with the page's HTML instead of
JSON. The editor detects that case: where the request carries no such
PAGE
object it renders a role="alert" message above itself
and logs the cause, naming the site set. Such a site package has to add the
academicPersonsProfileEditingAjax
object by hand, or include the
delivered TypoScript.
A PageType route enhancer has to map the page type, and a web application
firewall or reverse proxy has to let it and the X-Requested-With header
through - see the page type section.
Verifying the result
A timeline entry of a profile shows its year in the frontend and in the
backend record editor, and the backend form rejects a year above 9999.
The profile editing plugin loads without the "cannot be saved" alert above
it, a field can be saved, and the browser console shows no failed request
to the page type 1733735.
A profile image is shown in every language of a translated profile, and
uploading a new one in one language does not change the other.
vendor/bin/typo3 academic:persons:settings:migrate
exits with
0, and the log carries no legacy settings warning.
Configuration
This extension ships its frontend TypoScript and its backend page TSconfig in
two forms: as TYPO3 site sets, and as classic static templates plus
page TSconfig files that are selected on a page. Both forms read the very
same files, so they configure an installation identically.
Pick one of them per site and stay with it — see
Do not combine both for what happens otherwise.
What the sets contain
This extension ships six content elements, so it ships six component sets and
one aggregate set that depends on all of them.
All six are driven by one Extbase plugin, so they share one TypoScript block,
plugin.tx_academicpersons
. That block is shipped once, in
Configuration/TypoScript/Default/, and every component includes it.
Which component sets a site names therefore decides which content elements the
backend offers, not how much TypoScript is loaded.
Set
Delivers
fgtclb/academic-persons-list
The Persons List content element.
fgtclb/academic-persons-list-and-detail
The Persons List and Detail content element.
fgtclb/academic-persons-detail
The Persons Detail content element.
fgtclb/academic-persons-card
The Contacts content element, and the FlexForm
restriction that hides the list, sorting and pagination fields for
it.
fgtclb/academic-persons-selected-profiles
The Profiles: Selected Profiles content element.
fgtclb/academic-persons-selected-contracts
The Profiles: Selected Contracts content element.
fgtclb/academic-persons
Everything above. This is the set to use unless you deliberately
want a subset.
fgtclb/academic-persons-default
The name this extension published before the sets were cut per
component. It delivers exactly what fgtclb/academic-persons
delivers, and is kept so that existing site configurations keep
working.
fgtclb/academic-persons-standalone
Everything the aggregate delivers, plus a
page
object
that renders content on a plain Bootstrap page. Meant for an
installation without a site package of its own — an alternative to
fgtclb/academic-persons, never an addition to it.
Every component set depends on fgtclb/academic-base-ctype-group, the set of
EXT:academic_base that labels the content element group all academic
extensions sort their elements into.
The site settings of this extension — the detail page, and the default grouping,
sorting and pagination of a profile list — are declared with the aggregate set.
A site that depends on a single component set still gets the shipped defaults,
but can only override them in Site Settings when it depends on
fgtclb/academic-persons.
The content elements are hidden by default
EXT:academic_persons hides all six of its content elements for the
whole installation and brings them back per component. Whichever of the two
mechanisms below you use, it is what makes an element selectable in the backend
again — without one of them the content element is not offered, and existing
records keep rendering.
Warning
This changed in version 2.4. Before it, all six elements were selectable on
every page of every installation. Read
Breaking: Site sets and static templates have been restructured before upgrading:
opening an existing record on a page that does not include the page
TSconfig of its component can rewrite the type of that record.
Include the site set
Add the set to the config.yaml of the site that should offer the content
elements:
For an installation that still configures its frontend through
sys_template
records, the same files are registered as static templates
and as selectable page TSconfig files.
Tip
On TYPO3 v13 and v14 we recommend the site set — and if you use it, do not
press the backend button Create a root TypoScript record on that
site. The
sys_template
record it creates carries the flag
Clear for constants and setup, and that flag discards everything
the site sets contributed. An installation that is already in that state
gets its configuration back by selecting the static templates below in that
very record.
Include static TypoScript
Edit the
sys_template
record of the site root and add the entry to
Include static (from extensions):
Entry
Delivers
Academic Persons: All components (academic_persons)
Every component this extension ships, in one entry. This is the
entry to use.
The shared
plugin.tx_academicpersons
block on its own.
This is what installations selected before version 2.4, then named
Academic Persons Settings. The stored value did not
change, so such a record keeps working untouched.
Everything All components delivers, plus the
page
object of the standalone flavour. Do not select
it on a site that has a site package of its own.
There is one further entry per component —
Academic Persons: Profile list,
Academic Persons: Profile list and detail,
Academic Persons: Profile detail,
Academic Persons: Profile card,
Academic Persons: Selected profiles and
Academic Persons: Selected contracts. They exist so that the static
mechanism has the same shape as the sets. Because all six components share one
TypoScript block, each of them delivers the same thing, and selecting more than
one of them changes nothing.
Include static page TSconfig
This is the half that decides which content elements the backend offers. Edit
the page record of the site root, tab Resources, field
Page TSconfig, and add the entries for the content elements the page
tree should offer:
Entry
Delivers
Academic Persons: All components (academic_persons)
Every component this extension ships, in one entry.
Academic Persons: Profile list (academic_persons)
Makes the Persons List content element selectable, and
configures its entry in the new content element wizard.
Academic Persons: Profile list and detail (academic_persons)
The setting is inherited by every page below the one it is set on.
Do not combine both
A site that uses the site set and the static template reads the shipped
files twice. The site set is applied before the
sys_template
record, so
the second read happens after the site settings and after
config/sites/<site>/constants.typoscript — and it resets every constant
the extension ships a default for back to that default.
Nothing else is damaged: the Constants and Setup fields
of the
sys_template
record, the page TSconfig of a page and the page
TSconfig files selected on a page are all applied afterwards and still win. Use
one mechanism per site and the question does not arise.
Extension configuration
There are some options for global extension configuration:
types.physicalAddressTypes
types.physicalAddressTypes
type
string
Default
private=Private,business=Business
The available types for physical addresses that can be chosen when adding a physical address to a profile.
types.emailAddressTypes
types.emailAddressTypes
type
string
Default
private=Private,business=Business
The available types for email addresses that can be chosen when adding an email address to a profile.
types.phoneNumberTypes
types.phoneNumberTypes
type
string
Default
private=Private,business=Business,mobile=Mobile
The available types for phone numbers that can be chosen when adding a phone number to a profile.
profile.feuser.telephoneNumberType
profile.feuser.telephoneNumberType
type
string
Default
business
The type assigned to telephone numbers imported from frontend users. The
value must be one of types.phoneNumberTypes. An unavailable
value is stored as the undefined type ''.
profile.feuser.faxNumberType
profile.feuser.faxNumberType
type
string
Default
business
The type assigned to fax numbers imported from frontend users. It is
validated independently from
profile.feuser.telephoneNumberType; an unavailable value is
stored as the undefined type ''.
Configuration/AcademicPersons/Settings.yaml describes a profile in four
top-level maps. It ships with academic_persons, which owns the
records and their TCA, and it is the one file the backend record editor, the
public detail view and the editing frontend of EXT:academic_persons_edit read.
Map
Describes
profile
The public detail layout (
structure
and
details
),
and every directly editable profile property with its section,
control and validators.
special
The components of the editing frontend that are not one property:
the composed display name, the image and the synchronisation
switch.
contracts
The contract fields, and the address, email and phone sections a
contract owns.
documentSections
The sortable lists attached to a profile: the seven timeline entry
types and the contracts.
The order of every map and list is preserved and is what the editing frontend
renders. The validator flags are documented
on their own page.
Attention
The syntax of this file is still considered experimental and may change in
a future release.
The profile map
Two keys describe the public layout, everything else is a field:
The layout columns and the ordered elements in each column. The shipped
detail template renders left as the desktop navigation and right as
the main content; on mobile, the left elements are inserted again
directly before subline.
details
Per element, the ordered profile properties, the relation map, the label
reference or the special renderer it renders. Supported elements are
menuSections
,
headline
,
position
,
profileImage
,
contact
,
subline
,
profileEntries
and
menuSectionsDatas
; an unknown element
renders nothing.
position
and
contact
take the special
renderer
special: datasFromContracts
.
menuSections
lists
stable navigation identifiers and
menuSectionsDatas
maps each of
them to the profile relation it shows.
How the layout is rendered
The shipped Resources/Private/Templates/Profile/Detail.html receives
the two keys as publicProfile and dispatches every identifier of
structure
to a partial of the same name below
Resources/Private/Partials/Profile/PublicProfile/:
Element
details
entry
Renders
menuSections
Ordered navigation identifiers
One link per identifier whose relation has records
headline
Ordered profile properties
The non-empty ones as the parts of the heading
position
special: datasFromContracts
The position of every contract
profileImage
Ordered image properties
Every non-empty one, as a figure
contact
special: datasFromContracts
Email addresses, phone numbers, postal addresses and location with
room of every contract
subline
A label reference
The translated heading, and the point before which the
left
elements are repeated below the large breakpoint
profileEntries
Ordered rich text properties
The non-empty ones as fold-out entries
links
Ordered link properties
The non-empty ones, each with its companion title property
(
website
with websiteTitle) as the link text
menuSectionsDatas
Navigation identifier to relation map
One timeline section per identifier whose relation has records
Overriding a partial changes how an element renders, overriding
profile
changes what renders and where. The years of a timeline entry
are printed as they are stored - a single year, a range, or an open range
prefixed with a "since" or "till" label - and nothing about them is locale
dependent.
Below the large breakpoint the elements of the
left
column are
rendered a second time, directly before the
subline
element of the
right
column. A layout without
subline
in
right
therefore has no mobile navigation.
The view is a content element and renders no <main>, no <aside> and no
<h1> - a page may carry two profile plugins, and those belong to the page
template. Its headings start at <h2> for the headline, with the block
headings one level below it.
The template loads the stylesheet
Resources/Public/Css/frontend/profile-detail.css and the module
@fgtclb/academic-persons/frontend/profile.js through the asset collector.
The module toggles the fold-out entries, keeps the sticky navigation below a
page header with the id page-header and, when the site loads Bootstrap,
marks the section in view through its ScrollSpy. The icons of the contact rows
and the fold-out entries are the identifiers academic-persons-envelope,
academic-persons-phone, academic-persons-address,
academic-persons-room, academic-persons-detail-plus and
academic-persons-detail-minus of Configuration/Icons.php; a site
package re-registers an identifier to replace the glyph. They are Bootstrap
Icons, and their MIT licence ships beside
them in Resources/Public/Icons/LICENSE-bootstrap-icons.txt.
The colours of the view are custom properties declared on the
.academic-persons-detail root element - --academic-persons-detail-text,
--academic-persons-detail-border and the two accents. Redeclaring them on
that class in the site's own stylesheet is how the view is themed; the shipped
stylesheet touches nothing outside that element.
Note
The navigation of the
left
column is sticky. A theme that wraps its
content sections in overflow: hidden clips it, and the extension
deliberately does not override that from its own stylesheet. Lift it in the
site's stylesheet on the wrapper that has it, for example:
Until 2.4 Templates/Profile/Detail.html was the view: it rendered the
image, the contact data and every timeline section itself. Since 3.0.0 it is a
dispatcher over the
structure
map, and the eleven partials below
Partials/Profile/PublicProfile/ are what renders.
A project that overrides the template keeps rendering its own copy. Nothing
looks broken, and two things are silently gone:
The configurable layout.
profile.structure
and
profile.details
are handed to the template as publicProfile and
are read by the new partials only, so changing them has no effect at all
while the old template renders.
Three partials the detail view no longer renders.Partials/Profile/Header.html and
Partials/Profile/SectionHeader.html are still shipped and still
rendered - by the list and card views, and by the profile editing view of
academic_persons_edit - so an override of one of them made for
the detail view no longer reaches it.
Partials/Profile/DataHeader.html had the detail view as its only
caller and is deleted: a project template that still renders
Profile/DataHeader fails at render time rather than rendering nothing.
Breaking: The partials of the detail view change has the migration.
Adopt the new template instead, and move the project's changes into the partial
of the element they belong to: they are one file per element, and overriding one
of them is what How the layout is rendered describes.
The fields
Every other key is a field, and fields share one shape across
profile
,
contracts.fields
and
contracts.contactSections.<section>.fields
:
Key
Meaning
section
Profile fields only. Fields with the same section are grouped and
rendered together, in file order; the first field of a section
decides where the section appears.
propertyName
The domain and form data property, when it differs from the key.
Optional.
fieldName
The database column, when it differs from the underscored property
name. Optional.
fieldType
input
,
select
,
textarea
or
check
.
Describes the frontend control; the TCA column keeps the type its
TCA file declares.
renderType
The renderer of the editing frontend:
text
,
select
,
checkbox
,
email
,
phone
,
date
,
combinedLink
or
ckeditor
.
Rich text fields (
renderType: ckeditor
) only: the maximum
number of readable characters. Checked on the server, never copied
into the TCA.
helptext
A label reference or literal text rendered next to the control.
autocomplete
Contract and contact fields only: an HTML autocomplete token.
options
Contract selects only:
organisationalUnits
,
functionTypes
or
locations
.
A field is dropped silently when it has no section (profile fields), no
fieldType
or no
renderType
. Removing a field from the file
removes it from the editing frontend and from the validation; it never removes
a column or stored data.
title
composes the display name from the listed profile properties,
image
is the profile image and
skipSync
the switch that keeps
a profile out of the synchronisation from its frontend user. A special entry
with a
fieldType
and without composed
fields
addresses one
profile column directly and takes part in the profile validation; the other
two do not.
fields
are the contract fields in editor order. The three contact
sections -
physicalAddresses
,
emailAddresses
and
phoneNumbers
- each carry their own
fields
map. Their keys
are unique across the file, which is why
emailAddress
names the
email property and column and each <section>Type key names the
type property of its own record.
label
,
type
,
fieldName
,
rowFields
and
actions
complete the
contracts
entry of the document sections
below.
Each key is a stable section identifier; the map order is the display order.
Key
Meaning
label
The label reference of the section heading.
type
The record type of the rows, i.e. the type of the profile
information records.
contracts
is the reserved value for
the contract section, which takes everything it does not declare
from the top-level
contracts
map.
fieldName
The profile relation the rows hang off.
readonly
true
disables creation and every mutating action; the
section still offers view.
rowFields
The values shown in a compact row, in order. Timeline entries
support from, to, year, title and description;
contracts support from, to and position.
actions
The actions offered per row, in order: view, down, up,
delete and edit. An action not listed is not available.
Listing both up and down also enables drag sorting.
validators
A map from field to flag list, or to a map with
validators
,
individual <flag>: true entries and an
editor
block.
editor.type: ckeditor
implies the html flag and takes a
readable-text
limit
;
editor.type: textarea
implies
textarea. The contract section validates against
contracts.fields
instead.
helptext
A map from field to label reference.
Warning
type
and
fieldName
describe the editing frontend. The seven
profile relations, and the record type each of them selects, are declared
by the TCA of the profile table since 3.0.0 and are not generated from
this file any more. Renaming either of them for one of the seven shipped
sections therefore leaves a backend inline column that stores one record
type and a frontend editor that writes another - the records created in one
context are invisible in the other. A section of an own record type needs
its own column in a TCA override of the profile table; the loop over the
seven relations in
Configuration/TCA/tx_academicpersons_domain_model_profile.php is the
template for it.
The validators of a timeline section address the record type of that section
only: a required title of publications does not make the title of a lecture
required, neither in the editing frontend nor in the backend, where the flags
land in the columnsOverrides of that record type. The field keys from,
to and description are aliases of the yearStart, yearEnd and
bodytext properties (columns year_start, year_end and
bodytext); year addresses the year property and column.
Unknown row fields and actions are discarded, as are duplicates; both lists
are matched without regard to case.
Overriding the file
The file is collected from all installed extensions: every package that
contains Configuration/AcademicPersons/Settings.yaml contributes, and
the package loaded last wins. The files are merged on the top level only -
a site package that defines
profile
replaces the shipped
profile
map completely, layout and fields alike, and the maps it does
not mention stay as shipped. There is no deep merge and no syntax for changing
a single flag of a single field.
To change the shipped configuration:
Add Configuration/AcademicPersons/Settings.yaml to your site
package.
Make the site package depend onacademic_persons in its
composer.json or ext_emconf.php, so that it is loaded after
it.
Copy the complete map you want to change from
EXT:academic_persons/Configuration/AcademicPersons/Settings.yaml
and edit the copy.
Flush the TYPO3 caches. The normalised graph is cached in the core cache.
There is no TypoScript and no site set equivalent for these settings.
Note
The backend record editor reads the same maps. Unlocking a field for the
editing frontend, or requiring one, changes the backend form of that record
the same way.
Validation settings
Every field of Configuration/AcademicPersons/Settings.yaml carries a
list of flags that say whether it is required, read only or disabled, and
what kind of value it takes. One list drives both editing contexts:
the TYPO3 backend record editor (FormEngine), through generated TCA, and
This is why the file ships with academic_persons, which owns the
records and their TCA, and not with the editing extension. The backend half
applies even when the editing extension is not installed. Where the fields
live - the
profile
,
special
,
contracts
and
documentSections
maps - is documented on the
Profile sections page; this page is about the
flags.
Attention
The syntax of this file is still considered experimental and may change in
a future release.
Where the flags are declared
A profile, contract or contact field carries its flags as a list:
A field without flags is unconfigured: it is editable, not required, and no
validator runs for it. A field that is not listed at all is not offered by the
editing frontend.
Available flags
Flag names are matched case insensitively. Anything not listed here is kept in
the list and has no effect.
Flag
Effect
required
The field must not be empty. Adds a not empty validation in the
frontend and marks the field required in the backend.
disabled
The field must not be edited at all. See the note below.
readonly
The field is shown but cannot be written.
email
The value must be a valid email address; the field is rendered as an
email input and the TCA column becomes an email column.
url
The value must be a valid URL; the field is rendered as a URL input.
The TCA column is untouched.
number
The field is rendered as a number input and the TCA column becomes a
number column. No additional server side validation is
performed.
date
The field is rendered as a date input. The TCA column keeps its own
datetime configuration.
tel
The field is rendered as a telephone input. No phone number format
is enforced, and the TCA column is untouched.
textarea
The field is rendered as a multi line text control. The TCA column
is untouched.
html
The field is rich text: the editing frontend renders the rich text
editor and sanitises the submitted markup. The TCA column is
untouched.
Only
required
,
email
and
url
run a validator on the
server; the other flags select the control and the input normalisation.
Validator class names and validator options cannot be put in the list.
Note
disabled
and
readonly
both cancel
required
. A
field that cannot be edited cannot be demanded from the editor, so combining
them has no effect - the field is simply locked.
disabled
additionally implies
readonly
. FormEngine has no
equivalent of the HTML
disabled
attribute, so a disabled field is
presented as read only in the backend.
Character limits
A rich text field may limit the number of readable characters - markup is
not counted. A profile or contract field declares it next to its render type,
a document field in its editor block:
The limit is effective only on a
ckeditor
control; on any other control
it is ignored. It is checked on the server and shown by the editing frontend;
it is never copied into the TCA, because FormEngine's max would count
the markup.
Fields that are locked by default
The three name fields ship as
readonly
and
disabled
:
This is intentional. Profile names are usually owned by the connected frontend
user record - commonly fed from a directory service such as LDAP or Active
Directory, and synchronised into the profile - so they must not be overwritten
from an editing form.
The consequences, which surprise people who did not expect them:
First name, Middle name and Last name
are read only in the backend record editor, for every backend user.
The same three fields are rendered locked in the editing frontend, and a
value submitted for them is discarded on the server.
If the profile names are maintained in TYPO3 rather than synchronised from
elsewhere, remove the two flags as described below.
Effects in the TYPO3 backend
The flags of every section are merged into the TCA of the matching table, so a
locked field is read only in the record editor and a required field is marked
as such:
Section
Table
profile
fields and
special.skipSync
tx_academicpersons_domain_model_profile
contracts.fields
tx_academicpersons_domain_model_contract
contracts.contactSections.emailAddresses
tx_academicpersons_domain_model_email
contracts.contactSections.phoneNumbers
tx_academicpersons_domain_model_phone_number
contracts.contactSections.physicalAddresses
tx_academicpersons_domain_model_address
every other
documentSections
entry
tx_academicpersons_domain_model_profile_information
, as
columnsOverrides of the record type of that section
The property name is translated to the database column automatically:
firstName
addresses
first_name
; a field that names a
fieldName
addresses that column instead.
The seven timeline sections share one table, so their flags apply to their
record type only: a required title of publications does not make the title
of a lecture required. The
fieldType
and
renderType
of a field
never reach the TCA - the column keeps the type its TCA file declares.
The control is rendered with the matching
disabled
,
readonly
and
required
attributes and the input type the
flags select.
required
,
email
and
url
add server side validation
of the submitted data, and a character limit is enforced.
A
disabled
or
readonly
property is never written to the
record, whatever the request contains. This is deliberate: it protects
already stored data, and it is what prevents a locked field from being
emptied when a form is submitted.
Validation never falls back from one section to another: a contact record is
validated against its contact section, a timeline entry against the section of
its record type, and the profile against its profile sections.
Overriding the flags
The flags live in the map that carries the field, so changing them means
overriding that map - see Overriding the file. The files are merged on the top level only:
a site package that defines
profile
replaces the shipped
profile
map completely, layout and fields alike, and there is no syntax
for changing a single flag of a single field.
Example - making the profile names editable again, in the backend and in the
editing frontend. The shipped
profile
map is repeated with the two
flags removed from the three name fields; the layout keys and the other fields
are copied unchanged and are shortened here for readability:
profile:structure:# ... as shippeddetails:# ... as shippedgender:section:informationfieldType:selectrenderType:selectvalidators:-requiredfirstName:section:informationfieldType:inputrenderType:textmiddleName:section:informationfieldType:inputrenderType:textlastName:section:informationfieldType:inputrenderType:text# ... the remaining fields as shipped
Copied!
Note
Because both editing contexts read the same configuration, an override
always changes them together. Unlocking the profile names for the editing
frontend also makes those columns writable in the backend record editor.
There is no TypoScript and no site set equivalent for these settings.
Migrating a pre-3.0 override
Before 3.0 the file had two top-level maps,
validations
with one flag
list per record type and
profileInformationsTypes
with the seven
timeline entry types, and the manual told integrators to restate the complete
validations
block in the site package. Such a file keeps working after
the update: the two keys are mapped onto the section maps at runtime, before
the settings graph is built, and a warning naming the package and the key is
logged once per cache build. The mapping is transitional and is removed in
academic_persons 4.0, so the override should be rewritten.
For every active package that still ships a legacy key it prints the package,
the keys it found, one comment line per entry that could not be mapped, and
the four maps
profile
,
special
,
contracts
and
documentSections
as the runtime mapping produces them - the complete
document that replaces the legacy keys in that package's file. It exits with
1 when such a package exists and with 0 otherwise, so a deployment pipeline
can run it as a check. The command never writes the file: the override lives
in a site package that is under version control and usually deployed read
only, so the printed maps are pasted into the file after review, and the
TYPO3 caches are flushed afterwards. When EXT:reports is installed, the
Status report lists the same packages under
Academic Persons as a warning.
contracts.contactSections.physicalAddresses.fields.<field>.validators
,
type
onto
physicalAddressType
validations.profileInformation.<property>
documentSections.<section>.validators.<field>
of every
timeline section;
yearStart
,
yearEnd
and
bodytext
onto
from
,
to
and
description
profileInformationsTypes.<section>
the
label
of
documentSections.<section>
; its
type
and
fieldName
are reported, not applied
A field is matched by its key or by the property it names, so
emailAddress.email
reaches the
emailAddress
field whose
propertyName
is
email
. A legacy set decides the five flags the
old shape knew -
required
,
readonly
,
disabled
,
email
and
number
- for every field of its target: a field
the set does not list has none of them, exactly as an unlisted property was
unconfigured before, which is what made the 2.x example above unlock the
profile names by not listing them. The flags the old shape could not express
-
url
,
date
,
tel
,
textarea
,
html
-
stay as the section maps declare them.
Two things are not mapped and are reported by the command and in the log:
A property the section maps do not know, and an eighth timeline entry
type declared under
profileInformationsTypes
, are skipped. The
type needs a profile relation and a TCA column the settings never
created; the Breaking entry on the section based settings describes how
to keep one.
The
type
and
fieldName
of a timeline entry type are not
applied. Until 2.4 the two generated the inline column of the profile
table, so overriding one moved the backend relation and the frontend
selection together; since 3.0.0 the seven relations are declared by the
TCA of the profile table, and applying the override would move the
frontend half alone - records created in the editing frontend would be
invisible in the backend, and the other way round. The section keeps the
record type and the relation field that match the TCA, and the value the
override named is printed as a note. Act on that note: a timeline type of
your own needs its own column in a TCA override of the profile table, as
The document sections and the Upgrading from 2.4 to 3.0.0 page
describe.
Route enhancers
This extension ships three ready made route enhancers below
Configuration/Routes/. TYPO3 does not read those files on its own —
they are fragments that have to be imported from the configuration of the site
which shows the plugins.
Each file covers exactly one plugin, so which of them you import follows from
which plugins the site actually uses.
What the files enhance
Detail.yaml
Enhancer
ProfileDetailPlugin
for the plugin
Detail
,
argument namespace
tx_academicpersons_detail
. One route,
/{profile_name}
, for the
detail
action, mapping the argument
profile
. The path segment is resolved by a
PersistedAliasMapper
on the table
tx_academicpersons_domain_model_profile
over the field
slug
.
List.yaml
Enhancer
ProfileListPlugin
for the plugin
List
, argument
namespace
tx_academicpersons_list
. Two routes for the
list
action:
{localized_page}-{page}
for
demand/currentPage
and
/{letter}
for
demand/alphabetFilter
. The page number is
limited to a
StaticRangeMapper
from 1 to 1000, the letter to a
StaticRangeMapper
from a to z, and the word in front of
the page number is translated by a
LocaleModifier
— page by
default, seite for German.
ListAndDetail.yaml
Enhancer
ProfileListAndDetailPlugin
for the plugin
ListAndDetail
, argument namespace
tx_academicpersons_listanddetail
. It is the union of the two above:
the detail route, the pagination route and the letter route, with the same
aspects, because that plugin renders both the list and the detail view.
Which file to import
Which of them you import follows from which plugins the site actually uses:
A site that puts the List plugin on one page and the
Detail plugin on another — the usual setup, where the list
links to the detail page through the plugin setting
detailPid
— imports List.yamlandDetail.yaml.
A site that puts the single ListAndDetail plugin on one page
imports ListAndDetail.yaml only.
A site that uses both variants imports all three, and then has to bound
each of them to its own pages — see the next section.
The remaining plugins of this extension —
SelectedProfiles
,
SelectedContracts
and
Card
— take no frontend arguments, so no
enhancer is shipped for them.
Limiting an enhancer to its pages
An enhancer is offered to every page of the site unless it says otherwise,
and TYPO3 takes the first candidate route whose path matches and whose
aspects resolve. Two enhancers are not kept apart by belonging to different
plugins, nor by carrying different keys — neither is what the matcher looks at.
The three files of this extension describe the same two views, so their routes
overlap by construction:
Route
Declared in
/{profile_name}
Detail.yaml and ListAndDetail.yaml
{localized_page}-{page}
List.yaml and ListAndDetail.yaml
/{letter}
List.yaml and ListAndDetail.yaml
Each pair is identical down to the mapper, so importing more than one file
without saying where it applies means the file imported first takes those URLs
on every page of the site. The plugin on the other page then never receives its
argument: the dedicated detail page answers 404, and the combined
plugin renders the unfiltered list where a letter was asked for.
Only resolving is ambiguous. Generating a URL is scoped to the plugin namespace
being linked, so the links keep looking right, which is why this surfaces as a
broken page rather than as a broken link.
limitToPages
is the answer, and with it in place the import order no
longer matters:
The uids are those of the pages carrying the plugin in question, and they are
the uids of the default language: matching derives the page as
l10n_parent ?: uid
, so one list covers every translation of that page.
Plain page uids work on every TYPO3 version this extension supports.
A site that imports a single file needs no limitation for this extension, but
adding it is still worth the two lines. What keeps a route path of the same
shape from another extension apart is only that its mapper rejects the value —
a slug that happens to exist in both tables is enough to make the two compete.
What the URLs look like
Assuming the list plugin sits on a page with the slug /persons and the
detail plugin on /persons/profile, the URLs change as follows.
The detail route needs a slug.
PersistedAliasMapper
resolves the
path segment against the
slug
field of the profile record, so a
profile whose slug is empty cannot be reached through the enhanced URL. The
slug is generated by the TCA
slug
field, which means it is filled
when the record is saved in the backend. Profiles created by
academicpersons:createprofiles
are persisted through the Extbase
persistence manager and therefore never pass the
DataHandler
, so
those records — and records that predate the field — start out with an
empty slug and have to be saved once in the backend before the enhanced
URL resolves.
The two list routes are alternatives, not a combination. A link that
carries a page number and a letter matches the pagination route, and the
letter stays behind as a query argument —
/persons/page-2?tx_academicpersons_list[demand][alphabetFilter]=m.
Only the mapped value ranges are put into the path. A page number above
1000, and the empty filter value that the A-Z reset link of the
alphabet pagination submits, are outside the mapped ranges, so those links
keep their query argument.
The
localeMap
of the
LocaleModifier
is matched against the
locale of the site language, with the underscores replaced by hyphens and
anchored at the start. The shipped map lists
en_EN.*
and
de_DE.*
, which means a German language configured as
de-DE
is translated to seite while a plain
de
is not. Adjust the map
to the locales your site actually uses.
Unlike the program list of academic_programs, the pagination
and the alphabet filter of this extension are rendered as links, not as a
form, so their own requests do carry the arguments in the URL and are
enhanced.
Templates
This chapter is all about templating EXT:academic_persons
EXT:academic_persons is using Fluid as template engine.
This documentation won't bring you all information about Fluid but only the
most important things you need for using it. You can get
more information in the section Fluid templates of the Sitepackage tutorial. A complete reference of Fluid ViewHelpers
provided by TYPO3 can be found in the ViewHelper Reference
Change the templates using TypoScript constants
As any Extbase based extension, you can find the templates in the directory
Resources/Private/.
If you want to change a template, copy the desired files to the directory
where you store the templates.
This chapter documents the programmatic surface of the translation
synchronisation this extension ships: the event that triggers it, the service
interface behind it, and how it behaves in workspaces. It also documents the
event that lets a project decide what is written as the metadata of a profile
image.
Warning
The whole surface is marked
@internal
and experimental. It works and
is covered by functional tests, but signatures may still change in a minor
release. Depend on it deliberately.
The trigger: AfterProfileUpdateEvent
\FGTCLB\AcademicPersons\Event\AfterProfileUpdateEvent
is a PSR-14 event
announcing that a profile aggregate — the profile record or one of its child
records — has changed and was persisted. This extension dispatches it after a
profile is auto-created for a frontend user
(
AbstractProfileFactory::createProfileForUser()
, also reached by the
academic:createprofiles
command); EXT:academic_persons_edit
dispatches it after every persisting frontend edit action, and project code —
typically a
DataHandler
hook reacting to backend edits — may dispatch it
as well to trigger the same synchronisation.
The dispatch contract:
The event carries the persisted default language profile: its
getUid()
returns a real uid, and the record is not a translation
overlay. Listeners read the database, not the object, so all changes must
be persisted before dispatching.
The profile's pid must resolve to a site — the synchronisation listener of
EXT:academic_persons_edit determines the site from the request or from
the pid and skips the event silently when it cannot.
Updating an existing profile from its frontend user record
(
AbstractProfileFactory::updateProfileForUser()
, command
academic:updateprofiles
) dispatches the event per profile the update
runs through — announced even when every value already matched, exactly like
the frontend editing flow. A profile whose
skip_sync
flag is set is
neither updated nor announced.
The synchronisation surface
\FGTCLB\AcademicPersons\Service\RecordSynchronizerInterface
declares one
method,
synchronize(SynchronizerContext $context)
. The shipped
implementation (
RecordSynchronizer
) routes every write through the
TYPO3 DataHandler — nothing in TYPO3 outside the DataHandler honours
l10n_mode=exclude
or keeps translations consistent, so going through it
is what makes the created translations indistinguishable from ones created in
the backend: inline children, file references, MM relations,
l10n_diffsource, reference index, history and hooks are all carried along.
\FGTCLB\AcademicPersons\Domain\Model\Dto\Syncronizer\SynchronizerContext
describes one synchronisation run. Build it through
SynchronizerContext::create()
, which takes the synchronizer instance,
the
Site
, the allowed language ids, the table name and the record uid —
and silently drops language ids that are not positive or that the site does not
define, so a run never targets a language the site cannot render.
For each remaining language,
synchronize()
:
creates a missing translation with a DataHandler localize command —
the full record, including its inline child tree;
for an existing translation, re-submits the default record's
l10n_mode=exclude
column values as a datamap (core's
DataMapProcessor propagates them into every translation) and issues an
inlineLocalizeSynchronize command per inline column, which carries
child records added to the default record after the translation was
created.
A missing record, a record that is not in the default language, or a record
that is invisible in the acting workspace makes the run a silent no-op.
Workspace behaviour
The synchronisation acts in the workspace of the acting backend user: run
from a backend context inside a workspace, it creates versioned rows only
(t3ver_wsid set, t3ver_state=1) and never touches the live records —
publishing the workspace publishes the translations. When no backend user is
available (frontend and CLI contexts), a synthetic in-memory admin user acting
in the workspace of the current
Context
is used.
Two refusals protect the live state:
A frontend request acting in a non-live workspace (a workspace preview)
is refused entirely; a notice is logged and nothing is written. This
policy is currently hardcoded.
A uid addressing a workspace version row (t3ver_oid > 0) is
refused: the DataHandler addresses versioned records through their live
uid, and accepting the version uid would publish draft values as live
translations.
Image metadata: ModifyProfileImageMetadataEvent
\FGTCLB\AcademicPersons\Event\ModifyProfileImageMetadataEvent
is
dispatched immediately before this extension writes the metadata of a profile
image, and a listener decides what is written: whatever it leaves in
getMetadata()
is the field map that goes to the database, and an empty
map writes nothing at all.
It is dispatched for each of the two records that carry image metadata, and
getTargetTable()
says which one:
getTargetTable()
Written when
Fields
sys_file_metadata
a profile image is uploaded in the frontend, once, for the file that
upload created — and only for the fields that record has empty
the name of a profile record changes, from a backend save, a
localization or a frontend edit
title
and
alternative
Both records are handed over, whichever of them is written.
getFile()
is the file — its own metadata record is
$event->getFile()->getMetaData()
— and
getFileReference()
is the
image relation of the profile,
null
only for a profile that has none.
getProfileUid()
is the profile record the image belongs to, a
translation for an image of its own, and
getRequest()
the request the
write happens in: the frontend request for an upload or a frontend edit, the
backend request for a save, and
null
on the command line or where the
caller has no request to pass on.
Fields the target table does not declare are dropped before the write, so a
listener may set a column unconditionally: where the installation does not have
it, the value goes nowhere.
copyright
is one such column — it belongs to
sys_file_metadata
and
typo3/cms-filemetadata
, and the
relation row has no equivalent.
Warning
System fields are refused. The identity of the record, the relation it
is part of, its localization, its workspace columns and its enable columns —
uid
,
pid
,
file
,
uid_local
,
uid_foreign
,
tablenames
,
fieldname
,
sys_language_uid
,
l10n_parent
,
t3ver_*
,
deleted
,
hidden
and the
rest of their kind — are dropped and a warning is written to the log. This
event writes metadata; repointing a relation or moving a record is the
DataHandler
's business.
The two dispatches are not interchangeable. The metadata record of the file is
written once, which makes it the place for a value that has to survive —
the required attributes of
typo3/cms-filemetadata
or
fgtclb/file-required-attributes
, for instance. The reference row is
rewritten on every save of the profile, so a listener that wants to own a
field there has to set it on every dispatch.
<?phpdeclare(strict_types=1);
namespaceMyVendor\MyExtension\EventListener;
useFGTCLB\AcademicPersons\Event\ModifyProfileImageMetadataEvent;
useTYPO3\CMS\Core\Attribute\AsEventListener;
finalclassAddImageRightOfUse{
#[AsEventListener(identifier: 'my-extension/add-image-right-of-use')]publicfunction__invoke(ModifyProfileImageMetadataEvent $event): void{
if ($event->getTargetTable() !== 'sys_file_metadata') {
return;
}
$metadata = $event->getMetadata();
$metadata['right_of_use'] = 'Portrait, own use only';
$event->setMetadata($metadata);
}
}
Copied!
Warning
A listener runs inside the write of the profile record — for a backend save
from within a
DataHandler
hook. Keep it short and do not write
profile records from it.
None of them did before, so none of the records of this extension could be
created or changed in a workspace, and the workspaces module did not offer
them.
Nothing reported the gap. The automatic TCA migration TYPO3 v14 ships for this,
TcaMigration::addWorkspaceAwarenessToInlineChildren()
, repairs an inline
child only when its parent table is already declared workspace aware. Here the
inline parents —
profile
for contracts and profile information,
contract
for addresses, email addresses and phone numbers, and
organisational_unit
as a second parent of
contract
— were
unflagged themselves, so it never fired and no deprecation was logged. TYPO3
v13 carries no such migration at all.
function_type
and
location
are plain select targets of
contract
rather than inline children. They are flagged for consistency,
so that an editor can add a function type or a location as part of the same
draft that uses it.
Impact
The database schema changes.
\TYPO3\CMS\Core\Database\Schema\DefaultTcaSchema
derives the
t3ver_oid
,
t3ver_wsid
,
t3ver_state
and
t3ver_stage
columns and an index over the first two from the declaration, so every one of
the nine tables needs those columns added.
This is not optional and it does not wait for someone to open a workspace. A
workspace aware table is queried with a
WorkspaceRestriction
in the live
workspace too, so until the database analyzer has run, both of these raise a
database error about the unknown columns:
the backend record lists of this extension —
DatabaseRecordList
adds
the restriction unconditionally, with the backend user's workspace,
including workspace
0
;
the frontend rendering of any translated profile — the language overlay
in
\TYPO3\CMS\Core\Domain\Repository\PageRepository
selects the
overlay record with a
FrontendRestrictionContainer
, which carries the
restriction by default.
Editing changes permanently, and running the analyzer does not undo it. In a
workspace with live editing enabled, a profile edit previously went straight to
live, because TYPO3 permits live editing only for tables that are not
workspace aware. It now becomes a workspace version that has to be published.
Custom queries against these tables have to be adapted. A plain
QueryBuilder
selecting from any of the nine tables now sees workspace
versions as ordinary rows and, without a version overlay, will render
unpublished drafts into the live frontend. Code that goes through the Extbase
repositories of this extension is not affected: Extbase adds the constraint and
performs the overlay itself, on TYPO3 v13 and v14 alike.
Affected Installations
Every installation of this extension, and every installation of
academic_contacts4pages
, whose contact records hang below
tx_academicpersons_domain_model_contract
as inline children.
No existing record is touched and no rendered output changes — but the database
analyzer has to run, and until it does the two places named under Impact are
broken. Development instances built from a committed database snapshot need the
same treatment.
Projects and extensions that query the nine tables with their own
QueryBuilder
are affected regardless of whether they use workspaces
today, because a workspace version created later becomes visible to them.
Migration
Run the database analyzer once after updating, in the
Admin Tools > Maintenance module or with
vendor/bin/typo3 extension:setup
.
Warning
On SQLite the command line path is broken.
vendor/bin/typo3 extension:setup
reports success, creates the index
over
t3ver_oid
and
t3ver_wsid
, and does not add the four
columns. The database is then left with an index over columns that do not
exist, and every later schema operation aborts with
Doctrine\DBAL\Schema\Index::_addColumn(): Argument #1 ($column)
must be of type string, null given
. Nothing is printed when it happens.
This is a TYPO3 Core defect, tracked as forge issue #110422 with a fix under review that is
scheduled for TYPO3 v13.4, v14.3 and main. Until it is released, take the
schema from a database built with the new state rather than migrating an
existing one, and check afterwards that the nine tables really carry the
four columns. Installations on MySQL, MariaDB or PostgreSQL are not
affected.
For custom queries, add the restriction and the overlay:
$queryBuilder->getRestrictions()->add(
GeneralUtility::makeInstance(
WorkspaceRestriction::class,
(int)$context->getPropertyFromAspect('workspace', 'id', 0),
),
);
// ... and per fetched row, before using it:
$pageRepository->versionOL($table, $row, true);
if (!is_array($row)) {
continue;
}
Copied!
Note that
versionOL()
keeps the live uid of an overlaid record, so
relations resolved through it — the profiles of a frontend user through
tx_academicpersons_feuser_mm
, for instance — still read the live
relation.
fe_users
is not workspace aware in TYPO3 itself. A relation
changed inside a workspace is therefore not part of the preview.
Breaking: Extbase plugins require CType on TYPO3 v14
TYPO3 v14 removed the tt_content sub-type feature (the list_type column) and
changed ExtensionManagementUtility::addPlugin() accordingly. The academic
plugins have been registered as first-class content elements (CType) since the
2.0 version line (see the 2.0 breaking note about migrating from list_type
to CType); for TYPO3 v14 support the internal registration was adapted to the
new addPlugin() signature and the vestigial list_type handling was dropped.
Impact
On TYPO3 v14 the tt_content.list_type column no longer exists. Any content
records still stored as CType=list with a list_type of one of the plugins
below will no longer resolve, and custom TypoScript, TSconfig, page TSconfig or
SQL that references list_type for these plugins stops working.
The change relates to the following plugins:
academicpersons_card
academicpersons_detail
academicpersons_list
academicpersons_listanddetail
academicpersons_selectedcontracts
academicpersons_selectedprofiles
Affected Installations
Installations that upgrade to TYPO3 v14 and still hold content elements stored
as CType=list + list_type=<plugin>, or that reference list_type for these
plugins in their own configuration.
Migration
Run the provided upgrade wizard
academicPersons_MigrateListTypeToCTypeContentElementsbefore upgrading to
TYPO3 v14 (it requires the list_type column, which v14 removes) to migrate the
tt_content records to the dedicated CType values. Update any custom
configuration referencing list_type to match on CType instead.
The
image
column of
tx_academicpersons_domain_model_profile
was
declared
'l10n_mode' => 'exclude'
with
'l10n_display' => 'defaultAsReadonly'
: every translation of a profile
carried the default-language image, the translation form showed it read-only,
and there was no way to give one language a picture of its own.
The column now declares
'behaviour' => ['allowLanguageSynchronization' => true]
instead. A translation starts out following the default-language image — the
core's parent localization state, which is also what every existing
translation is in, because no
l10n_state
has been stored for the column
yet — and switches to the custom state as soon as it gets an image of its
own. In the parent state an image the default-language profile gains or
changes reaches the translation on the next write of the default record
through the TYPO3 DataHandler — a backend save and the translation
synchronisation of this extension both are one — and a removal reaches it
through the DataHandler's own delete cascade, which deletes the localizations
of a deleted reference. In the custom state the translation keeps its
image whatever happens to the default one.
The relation itself is written by one service,
\FGTCLB\AcademicPersons\Service\ProfileImageRelationWriter
(
@internal
),
which drives every change through the DataHandler and maintains the
localization state. The frontend profile editing of
EXT:academic_persons_edit uses that service as well from 3.0.0 on - its
editor replacement of the same release no longer writes the image relation
through Extbase - so an image uploaded there for a translation reaches the
custom state exactly as a backend upload does.
The title and alternative text of the image reference follow the name of the
profile record the reference belongs to: a backend save, a localization and a
frontend profile update all rewrite the
title
and
alternative
of
the profile's own
sys_file_reference
row from that record's title and
names. Only the reference row is written; the
sys_file_metadata
row of
the file, which a file may share between the languages of a profile, stays the
backend editor's. The one write of that record is the frontend upload that
creates the file, which fills its empty required attributes once — see the
changelog entry Important: An uploaded profile image carries its metadata of
fgtclb/academic-persons-edit
.
Impact
The backend translation form shows the image field editable, with the
language synchronization toggle the core renders for such columns. An
editor can keep a translation in sync with the default language or give it
an image of its own.
The translation synchronisation of this extension (see
Important: Translation sync covers child exclude columns) needs no
change for it: the same core pass that propagates the exclude columns
honours the localization state of the image.
The image reference of a profile carries the profile's name as title and
alternative text after the next save of the record — from a backend save,
a localization, or a profile update announced through
AfterProfileUpdateEvent
. A title or alternative text maintained on
the reference row by hand is overwritten then; text maintained on the
file's metadata is never touched by it.
Workspaces are unchanged for an edit: the writer addresses live records and
lets the DataHandler produce the versioned rows, so a draft edit of a
profile image stays in its workspace. An installation-wide repair is the
exception and says so: the repair wizard of
fgtclb/academic-persons-edit
skips workspace rows and acts in
the live workspace regardless of the workspace the person running it has
selected.
Code that read the image of a translation through the default-language
record — because the translation could never differ — has to resolve the
translation's own reference now.
Affected Installations
Every installation with translated profiles. Existing data needs no change:
translations without a stored localization state follow the default-language
image exactly as before.
Installations that uploaded profile images through
academic_persons_edit
before 3.0 should run the upgrade wizard
academicPersonsEdit_repairLocalizedProfileImages of that extension, which
brings the relation rows the old upload and synchronisation paths wrote into
the shape the localization state expects.
The public profile detail view is assembled from configuration instead of a
fixed template (Feature: Configurable public profile), and
Resources/Private/Templates/Profile/Detail.html is rewritten for it.
It dispatches every configured element to one partial below
Resources/Private/Partials/Profile/PublicProfile/ and renders nothing
by itself:
Those eleven files are the override surface of the detail view from 3.0 on.
Three partials the previous Detail.html rendered are no longer part of
it:
Partials/Profile/DataHeader.htmlis deleted. The detail view
was its only caller. It rendered the heading of a data block through
{header -> f:format.raw()}
and a
f:switch
on a layout
value; the configurable view gives every element its own heading, in its
own partial, escaped.
Partials/Profile/Header.html and
Partials/Profile/SectionHeader.htmlare kept, and the detail view
no longer renders them. They still render the headings of the list and
card views of this extension and of the contact plugins of
EXT:academic_contacts4pages, so an override of either keeps
working there and stops having any effect on a profile detail page.
Impact
A project that overrode one of the three partials to style the detail view
loses that styling: the deleted one is not read any more, and the two that stay
are not reached from the detail view. Neither shows an error - the page renders,
without the override.
A project that overrides Templates/Profile/Detail.html itself keeps
rendering its own template and is affected differently: see
Feature: Configurable public profile.
Affected Installations
Every installation with a project override of
Partials/Profile/DataHeader.html, Partials/Profile/Header.html
or Partials/Profile/SectionHeader.html, and every project whose own
template renders Profile/DataHeader.
Migration
Remove an override of Partials/Profile/DataHeader.html, and any
<f:render partial="Profile/DataHeader" />
in project templates. A
partial that no longer exists is a render time error, not a silent empty
string.
Move detail view styling out of Partials/Profile/Header.html and
Partials/Profile/SectionHeader.html into the
PublicProfile/ partial of the element it belongs to. Keep the
override for the list and card views if it is still wanted there.
Flush the TYPO3 caches, so the Fluid template cache is rebuilt.
Support for TYPO3 v12 has been removed for the 3.x version line, based on
the dual TYPO3 core version support per major version of the academic
extensions support matrix.
This includes removing build, test and configuration parts only required for
TYPO3 v12. Version specific code paths are dropped in a dedicated step.
Impact
TYPO3 v12 or older instances can no longer install or update to the 3.x
version of the academic extensions and are required to upgrade TYPO3 first.
The extension cannot be installed on TYPO3 v12 anymore but does not break
otherwise.
Affected installations
All installations using an academic extension on TYPO3 v12 that want to
upgrade to the 3.x version line.
Migration
Upgrade the TYPO3 installation to a supported version - TYPO3 v13 or v14 -
beforehand or within the same upgrade step.
Configuration/AcademicPersons/Settings.yaml changes shape. The flat
schema of two top-level maps -
profileInformationsTypes
listing the
seven timeline entry types, and
validations
with one flag list per
record type - could say which fields are required or locked, and nothing
else. The editing frontend of
fgtclb/academic-persons-edit
needs
the order of the fields, the control each one is rendered with, its help
text, the rows and actions of a sortable list, and the character limit of a
rich text field. All of that is now declared in one place, in four top-level
maps:
profile
,
special
,
contracts
and
documentSections
. The
Profile sections page documents the shape,
the Validation settings page the flags.
There is still one file, one factory and one cache entry. The public
detail layout -
structure
and
details
- lives in the same
profile
map as the editable fields, so an override of the layout
restates the fields with it. The backend TCA does consume the graph: five
TCA files of this extension merge the validation set of their own section,
exactly as they merged the flat sets before, and the sixth - the profile
information table, one table shared by the seven timeline types - merges a
types
fragment so a section's flags land in the
columnsOverrides
of its own record type. The normalised graph is cached in
the core cache under AcademicPersons_Settings_v3, the identifier the move
of the validation primitives to EXT:academic_base introduced in the same
release; it is not changed a second time.
What an integrator sees:
profileInformationsTypes
is gone. The seven relations of a
profile to its timeline entries (
scientific_research
,
vita
,
memberships
,
cooperation
,
publications
,
lectures
,
press_media
) are part of the domain model and are
declared by the TCA file of the profile table. They used to be generated
from the map, so an override that dropped an entry silently lost a backend
column; they now exist whatever the settings say. The same seven appear as
documentSections
, which carry their label, record type, relation
field, row fields, actions and validators.
The record type and the relation field of a timeline type are no longer
configurable. They used to be one value each, generating the backend
column and selecting the frontend records together; since the column is
TCA, a changed
type
or
fieldName
would move the frontend
half alone and every record created through it would be invisible in the
backend. An override of either is therefore not applied, and the legacy
mapping reports it per key instead of copying it, see
Feature: Legacy settings overlay and migration command. A timeline
type of a project's own needs its own column in a TCA override of the
profile table, and a
documentSections
entry that names it.
validations
is gone. The flags of a field are declared on the field:
profile.<field>.validators
for the profile,
contracts.fields.<field>.validators
for the contract,
contracts.contactSections.<section>.fields.<field>.validators
for
the address, email and phone records, and
documentSections.<section>.validators.<field>
for the timeline
entries. Every set keeps to its own section: a timeline section reaches
the backend as columnsOverrides of its record type, never as a column
configuration all seven types share.
The flag vocabulary grows by
url
,
tel
,
textarea
and
html
, and a rich text field can carry a
characterLimit
.
fieldType
and
renderType
describe the frontend control
only; the TCA column keeps the type its TCA file declares.
The shipped flags change, and both editing contexts apply that. Newly
required: the profile's
gender
- the profile TCA column gets
required and minitems, so the backend record editor refuses to
save a profile without a gender, and the editing frontend runs its not
empty validation - and the contract's
validFrom
. Newly
validated:
website
,
publicationsLink
and the
link
of six of the seven timeline sections carry the
url
flag -
cooperation
offers no link field and therefore no flag on
one. Relaxed:
the
type
of an email address or phone number is no longer
required, and the address's
streetNumber
and
zip
lose
the
number
flag - their columns return from the number TCA
type the flag set to the input their TCA file declares, so a street
number like 12a is stored as entered instead of being cast to 12.
Unchanged: the address's
country
, and the
title
and
year
of every timeline entry were required before.
The timeline entry validators address the year columns:
year
is
the record's year, and the keys
from
and
to
alias its
yearStart
and
yearEnd
properties.
Every field of the shipped file carries a
helptext
, and the label
file of academic_persons gains the 38
helptext.*
units they reference - the help of the profile, contract, contact and
timeline fields, rendered by the editing frontend. A project replaces
the text either by pointing
helptext
at an
LLL:
key of
its own (or at literal text), or by overriding the shipped unit through
locallangXMLOverride
.
The internal PHP API changes with the file.
AcademicPersonsSettings
exposes the graph -
profileSections
,
specialFields
,
contractFields
,
contractContactSections
,
documentSections
and
publicProfile
- built from the new value
objects
ProfileSection
,
ProfileField
,
SpecialField
,
ContractField
,
ContractContactSection
,
ContractContactField
,
DocumentSection
and
PublicProfileSettings
, and answers validation questions per section.
Removed without replacement:
getProfileValidationSet()
,
getProfileUpdateValidationSet()
,
getContractContactValidationSet()
and
getDocumentValidationSet()
, plus
getProfileValidationSetForFields()
and
getContractContactValidationSetForFields()
for a subset
of a section's fields - every one of them returns an empty set
for an unknown identifier, there is no separate fallback method
All of it is
@internal
and was consumed by EXT:academic_persons_edit
only, which is adapted.
Impact
Every site package that overrides the file has to be migrated. The old
maps are not read as such any more. A file that still declares
validations
or
profileInformationsTypes
is mapped onto the
section maps at runtime with a logged warning, until 4.0 - see the Feature
entry on the legacy settings overlay and the migration command, and the
migration section of the
validation settings page. Without that mapping the installation would run on
the shipped defaults: locked name fields, the required contact fields, and the
seven shipped timeline sections.
An eighth timeline entry type that an override declared under
profileInformationsTypes
used to get a backend inline column for free.
It no longer does: the profile relations are fixed. Such a type can be kept by
declaring the column in a TCA override of the profile table and its section
under
documentSections
- the TCA file's loop over the seven relations
is the template for the column. The Extbase model of this extension never had
a property for an additional type, so it was reachable in the backend only.
Code that reads the removed methods or the removed class fails with an
undefined method or a class not found error.
Existing profiles without a gender can no longer be saved in the backend
until a gender is chosen, and a frontend profile form that does not post one
is rejected. An installation that does not want the gender required removes
the flag in its override. Street numbers and zip codes accept non-numeric
values again; values already cast to integers stay as they are.
Affected Installations
Every installation with a site package shipping
Configuration/AcademicPersons/Settings.yaml, and every installation
whose project code reads
AcademicPersonsSettings
directly.
Migration
Copy the shipped EXT:academic_persons/Configuration/AcademicPersons/Settings.yaml
over the override in the site package, and re-apply the project's changes
to it: a locked or unlocked profile field is its
validators
list
under
profile
, a required contact field its list under
contracts.contactSections
, a required timeline field its entry
under the
validators
map of every section it applies to.
Keep every map the override declares complete. The files are merged on
the top level only, so a
profile
map in the override replaces the
shipped one - the layout keys and every field included.
Decide on the changed defaults: drop
required
from
profile.gender.validators
if profiles without a gender are to stay
saveable, restore
number
on
streetNumber
and
zip
if numeric values are to be enforced, and add
required
back to
the two
<section>Type
fields if a contact type is mandatory.
Re-declare a renamed timeline relation in TCA. An override that changed
profileInformationsTypes.<type>.type
or
...fieldName
no longer reaches the profile table, and the value it
named is reported by
vendor/bin/typo3 academic:persons:settings:migrate
rather than applied. Either accept the shipped record type and relation, or
declare the column in a TCA override of
tx_academicpersons_domain_model_profile
and give it a
documentSections
entry.
Flush all TYPO3 caches.
Feature: Configurable document rows and actions
Description
Every entry below
documentSections
in
Configuration/AcademicPersons/Settings.yaml - the seven timeline entry
types and the contracts - declares what a compact row of that list shows and
which actions it offers:
rowFields
is the ordered list of values a row renders. Timeline
entries support from, to, year, title and description;
the contracts support from, to and position.
actions
is
the ordered list of per-row actions: hide, view, down, up,
delete and edit. An action that is not listed is not offered, and listing both
up and down is what enables drag-and-drop sorting of the list.
A section marked
readonly: true
offers view and nothing else,
whatever its
actions
list says, and does not allow creating a record.
Unknown values and duplicates in either list are discarded; both lists are
matched without regard to case.
The typed
\FGTCLB\AcademicPersons\Settings\DocumentSection
carries the
normalised lists and answers the capability questions -
allowsAction()
,
getAllowedActions()
,
allowsCreate()
and
allowsDragSorting()
- so the editing frontend of
fgtclb/academic-persons-edit
renders and offers exactly what the
file declares.
The lists are enforced on both sides. The rendered buttons come from them, and
so does the answer of every write endpoint of the editing frontend: an action a
section does not list is refused with HTTP 403 no matter how the request was
made. That includes the addresses, e-mail addresses and phone numbers of a
contract, which have no section of their own and follow the
actions
list and the
readonly
flag of the contracts section.
The shipped timeline descriptions declare editor.type: ckeditor, which
marks the field as rich text (the html flag) and carries a readable-text
limit of 500 characters, so the editing frontend renders the rich text editor
and enforces the limit with the same sanitisation contract as the rich text
fields of the profile.
Impact
The shipped sections list every row field and action that makes sense for
them. A site package that overrides
documentSections
restates the two
lists for every section it declares - a section without
actions
offers no action at all. Flush the TYPO3 caches after changing the file.
Feature: Configurable public profile
Description
The public profile detail view - the output of the Persons Detail
and Persons List and Detail content elements - is assembled from
the
profile
map of Configuration/AcademicPersons/Settings.yaml
instead of a fixed template. Its
structure
lists name the elements of
the left and the right layout column in render order, and
details
says per element which profile properties, which contract data or which label
it shows. The shipped file renders the section navigation on the left and, on
the right, the headline, the contract positions, the image, the contact data,
a subline, the fold-out profile entries, the link properties and the timeline
sections; below the large breakpoint the navigation moves into the content
column, directly before the subline element - an override that drops
subline from the right column therefore has no mobile navigation. See
The profile map.
Resources/Private/Templates/Profile/Detail.html receives the map as
publicProfile and dispatches every configured element to a partial of its
own below Resources/Private/Partials/Profile/PublicProfile/. An empty
property or relation renders nothing, and so does an element identifier the
template does not know.
Everything the old, static template rendered has an element: links carries
the website and publicationsLink properties with their companion title
properties, and the contact rows carry the type of each email address, phone
number and postal address again. What the rebuilt view has no equivalent for
are the two headings of its static sections, Contracts and
Additional information - every block is a configured element now
and carries its own heading. The label changes that follow from this are
listed in Important: Label changes of the public profile.
The view renders no <main>, no <aside> and no <h1>: it is a content
element, a page may carry two of them, and the page template owns those. Its
headings start at <h2> for the headline and step down from there.
The view ships its own stylesheet and a small ES module for the fold-out
entries, the sticky navigation and the Bootstrap ScrollSpy, both loaded by the
template through the asset collector. The six control icons of the view are
registered in Configuration/Icons.php as
academic-persons-envelope, academic-persons-phone,
academic-persons-address, academic-persons-room,
academic-persons-detail-plus and academic-persons-detail-minus, drawn in
currentColor and inlined, so they take the text colour of the page. They
are Bootstrap Icons and carry their MIT
licence in Resources/Public/Icons/LICENSE-bootstrap-icons.txt.
The colours of the view are custom properties declared on
.academic-persons-detail and are the theming hook: redeclare them on that
class to change them. The stylesheet stays inside its own container, so a
theme that clips its content sections with overflow: hidden cuts off the
sticky navigation of the left column - see
How the layout is rendered for the one rule that lifts
it.
Impact
A site package changes the layout of the public profile and the fields it
shows by shipping its own
profile
map, without replacing the detail
template. The maps are merged on the top level, so an override repeats the
complete
structure
and
details
it wants - and the editable
fields the editing frontend reads from the same map. Flush the TYPO3 caches
after changing the file.
An installation that overrides Templates/Profile/Detail.html keeps
rendering its own template; the publicProfile variable is available to it
from now on. Such an override switches this feature off entirely, and it
does so without any sign that it did: the page renders as it always did, the
profile.structure
and
profile.details
maps are read by the new
partials only, and a change to either has no effect on that page. The timeline
properties such an override prints - {item.year}, {item.yearStart},
{item.yearEnd} - are unchanged, so its entries keep rendering; what it
loses is the layout, not the values.
The way back is to delete the override and configure the layout instead, or to
re-derive the override from the shipped Detail.html, which is 45 lines
and dispatches to the eleven partials below
Resources/Private/Partials/Profile/PublicProfile/. Those eleven are the
override surface now, and overriding one of them keeps the configured layout
intact. Which partials the detail view stopped rendering, and which of them was
deleted with it, is listed in
Breaking: The partials of the detail view change.
Feature: Contracts and profile information can be hidden
Description
The two document record models gained access to the hidden enable field
that already exists on their database tables, exactly as the contact record
models did in 2.4:
Both order by sorting with uid breaking ties. Every other query, and
every relation of
Profile
, keeps respecting the enable fields, so the
public views hide a hidden record as before.
The document section vocabulary of
documentSections.<section>.actions
gains hide. It is listed first in the shipped settings of every section,
is offered only when listed, and is withdrawn by
readonly: true
with
every other mutating action. EXT:academic_persons_edit renders it as the
visibility switch of a row.
Impact
No database change: the hidden columns and their TCA existed already. A
site that overrides the
actions
of a section keeps its list - hide
is not implied - and adds hide where the switch is wanted.
Affected Installations
Installations that configure
documentSections
or read the two models
in PHP.
Feature: Legacy settings overlay and migration command
Description
A site package that still ships the pre-3.0 shape of
Configuration/AcademicPersons/Settings.yaml - the
validations
map with one flag list per record type, and the
profileInformationsTypes
map - is no longer ignored. Its two keys are mapped onto the four section
maps of 3.0 at runtime, before the settings graph is built, so the
installation keeps behaving as it was configured on the day of the update:
the backend record editor and the editing frontend see the flags the override
declared, not the shipped defaults.
The mapping is an overlay on the shipped maps. A legacy set decides the five
flags the old shape knew -
required
,
readonly
,
disabled
,
email
and
number
- for every field of its
target; a field the set does not list has none of them, exactly as it was
unconfigured before, and the flags the old shape could not express -
url
,
date
,
tel
,
textarea
,
html
-
stay as the section maps declare them. Two things are not mapped losslessly
and are reported:
An eighth timeline entry type declared under
profileInformationsTypes
is not migrated. It needs a profile
relation and a TCA column the settings never created; see the Breaking
entry on the section based settings for how to keep one.
The
type
and
fieldName
of a timeline entry type are not
applied. Until 2.4 the two generated the inline column of the profile
table, so overriding one moved the backend relation and the frontend
selection together; since 3.0 the seven relations are declared in the TCA
of the profile table, and applying the override would move the frontend
half alone - records created in the editing frontend would be invisible in
the backend, and the other way round. The section keeps the record type and
the relation field that match the TCA, and the value the override named is
printed as a note. The
label
of the type is mapped as before.
Every package that ships a legacy key is logged once per key at warning
level, naming the package, the key and the command below. The mapping is
transitional and is removed in academic_persons 4.0.
The console commandacademic:persons:settings:migrate prints, for
every active package that still ships a legacy key, the four section maps
those keys are mapped onto - the document that replaces the legacy keys in
that package's file - together with the notes about what could not be
mapped, and exits with 1 when such a package exists, so a deployment
pipeline can gate on it:
The command never writes the file. The override lives in a site package that
is under version control and usually deployed read-only, so a write would be
lost on the next deployment or leave a dirty working tree; the printed maps
are pasted into the package after review.
The status report of EXT:reports lists, under Academic Persons, every
active package that still ships a legacy key as a warning. The status
provider is registered only when EXT:reports is installed; there is no
dependency on it.
Impact
An installation with a pre-3.0 override runs on its own flags again after
the update, with a warning in the log and in the status report until the
override is rewritten. No cache flush is needed for the overlay to take
effect: the normalised settings graph is cached under the identifier
AcademicPersons_Settings_v3, while releases before 3.0 wrote
AcademicPersons_Settings, so the first request after the update is a
cache miss and rebuilds the graph through the overlay. A flush stays
necessary after every later edit of the file, as before. The migration itself - replacing the legacy keys with
the printed maps and flushing the caches - is described on the
Validation settings page.
Feature: Decide what a profile image's metadata will be
Description
\FGTCLB\AcademicPersons\Event\ModifyProfileImageMetadataEvent
announces
the metadata this extension is about to write for the image of a profile, and a
listener decides what is written. Whatever it leaves in
getMetadata()
is
the field map that reaches the database; an empty map writes nothing.
The event is dispatched for each of the two records that carry image metadata,
and
getTargetTable()
says which one is being written:
sys_file_metadata
, the record of the file itself, written once by
the frontend upload that created the file and only for the fields it found
empty —
title
,
alternative
and, where
typo3/cms-filemetadata
adds it,
copyright
. This is the
record an installation requiring file attributes reads, which is why the
File
is handed over rather than only its uid.
sys_file_reference
, the profile's own relation row, written whenever
the name of the profile record changes — from a backend save, a
localization or a frontend edit.
Both records are handed over either way:
getFile()
is the file, whose
own metadata record is
$event->getFile()->getMetaData()
, and
getFileReference()
the image relation of the profile.
getRequest()
is the request the write happens in, and
null
where
there is none — on the command line, for instance.
Fields the target table does not declare are dropped before the write, so a
listener may set
copyright
unconditionally: without
typo3/cms-filemetadata
the column does not exist and the value goes
nowhere. System fields are dropped as well, with a warning in the log: the
identity of the record, the relation it is part of, its localization, its
workspace and its enable columns are the
DataHandler
's, and this event
writes metadata.
Nothing changes without a listener: the composed name of the profile record is
written to the columns named above. With one, a project fills the columns its
own installation adds and requires —
right_of_use
of
fgtclb/file-required-attributes
, a copyright composed differently,
a caption of its own.
A listener runs inside the write of the profile record, for a backend save from
within a
DataHandler
hook. It has to be short, and it must not write
profile records itself.
Feature: Profile updates from user data announce themselves
Description
AbstractProfileFactory::updateProfileForUser()
— the path behind the
academic:updateprofiles
command — persisted its changes without
dispatching
AfterProfileUpdateEvent
; only the profile creation did. A
profile updated from its frontend user record therefore changed without its
translations being synchronised and without its slug being regenerated, while
the same change made through the frontend editing plugins did both.
The update path now dispatches the event per profile the update ran through,
after
persistAll()
, carrying the persisted default language profile —
the same contract as the creation path and the frontend editing flow, which
both announce a profile even when every value already matched.
The
skip_sync
flag gates the whole update per profile now: a profile
carrying it is neither data-updated nor announced. Previously the flag was
only evaluated per frontend user, so a user carrying a second, synchronisable
profile had the
skip_sync
profile updated through that side door.
Impact
An
academic:updateprofiles
run now triggers the registered listeners
for every synchronisable profile of the selected users. With
academic_persons_edit
installed that
means: profile slugs are regenerated, and — with
profile.allowedLanguages configured — the profile translations are
created or synchronised. Installations relying on the command not touching
slugs or translations should review their listener configuration before
updating.
Affected Installations
Every installation using the
academic:updateprofiles
command, and any
installation with a frontend user connected to several profiles of which some
carry
skip_sync
.
Important: Strict language fallback for selected profiles
Description
When profiles are selected explicitly (by uid) for a list plugin
(
academicpersons_list
/
academicpersons_listanddetail
with the
FlexForm "selected profiles" option), a site language configured with
fallbackType: strict
did not hide profiles that are not translated into
the requested language. Such profiles were rendered in their default language
instead of being removed, unless the plugin option "fallback for non
translated" was enabled on purpose.
This is not an
EXT:academic_persons
bug. The extension already
resolves the correct language overlay type from the site configuration and only
relies on Extbase persistence to apply it. The behaviour is caused by a
long-standing Extbase regression that ignored the resolved language overlay type
and always overlaid single records with
OVERLAYS_MIXED
, so untranslated
records were kept.
Gerrit change 66694
— "[BUGFIX] Respect language overlay type in Extbase" (TYPO3 main line)
Gerrit change 94935
— the TYPO3 14.3 backport (same Change-Id)
The fix is released with TYPO3 v14.3.6 and newer (and on the TYPO3 main
development line). It is not part of TYPO3 v13 and, being a behavioural
change, is not backported to the v13.4 LTS.
Impact
On TYPO3 v14.3.6 and newer the behaviour is correct out of the box:
untranslated selected profiles are removed under
fallbackType: strict
. No
configuration or code change is required in
EXT:academic_persons
.
On TYPO3 v13.4 (and on TYPO3 v14.3.0 - v14.3.5, before the fix shipped)
the affected Extbase code still overlays with
OVERLAYS_MIXED
, so
untranslated selected profiles keep being shown in their default language when the
site language uses
fallbackType: strict
and the plugin fallback option is
not enabled.
The two functional tests covering this behaviour
(
AcademicPersonsListPluginTest
and
AcademicPersonsListAndDetailPluginTest
,
test
...WithFallbackTypeStrictWhenNotAllProfilesAreLocalized
) are therefore
skipped on TYPO3 below v14.3.6 and run only where the core fix is present.
Affected Installations
Installations that use selected profiles in a list plugin with a site language
configured as
fallbackType: strict
and expect untranslated profiles to be
hidden, running on TYPO3 v13.4 or TYPO3 v14.3.0 - v14.3.5.
Solution
Upgrade to TYPO3 v14.3.6 or newer, which contains the core fix.
If the correct behaviour is required before that, apply the core change as a
composer patch against
typo3/cms-extbase
until it is part of the installed
core version. Cleaned patches (narrowed to
typo3/cms-extbase
, derived from
Gerrit changes 66694 / 94935) are shipped with this extension:
diff -ruN a/Classes/Persistence/Generic/Backend.php b/Classes/Persistence/Generic/Backend.php
--- a/Classes/Persistence/Generic/Backend.php+++ b/Classes/Persistence/Generic/Backend.php@@ -148,11 +148,14 @@
// This allows to fetch IDs for languages for default language AND language IDs
// This is especially important when using the PropertyMapper of the Extbase MVC part to get
// an object of the translated version of the incoming ID of a record.
+ // "Free" mode (OVERLAYS_OFF) is mapped to OVERLAYS_MIXED - overlays need to be enabled for the+ // identity lookup, but hiding untranslated records is not a configured intent in free mode.+ // This is consistent with the same handling for related objects in DataMapper->getPreparedQuery().
$languageAspect = $query->getQuerySettings()->getLanguageAspect();
$languageAspect = new LanguageAspect(
$languageAspect->getId(),
$languageAspect->getContentId(),
- $languageAspect->getOverlayType() === LanguageAspect::OVERLAYS_OFF ? LanguageAspect::OVERLAYS_ON_WITH_FLOATING : $languageAspect->getOverlayType(),+ $languageAspect->getOverlayType() === LanguageAspect::OVERLAYS_OFF ? LanguageAspect::OVERLAYS_MIXED : $languageAspect->getOverlayType(),
$languageAspect->getFallbackChain()
);
$query->getQuerySettings()->setLanguageAspect($languageAspect);
diff -ruN a/Classes/Persistence/Generic/Storage/Typo3DbBackend.php b/Classes/Persistence/Generic/Storage/Typo3DbBackend.php
--- a/Classes/Persistence/Generic/Storage/Typo3DbBackend.php+++ b/Classes/Persistence/Generic/Storage/Typo3DbBackend.php@@ -580,9 +580,16 @@
$row['uid'] = $row[$GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']];
$row[$GLOBALS['TCA'][$tableName]['ctrl']['languageField']] = 0;
}
- // Currently this needs to return the default record (OVERLAYS_MIXED) if no translation is found- //however this is a hack and should actually use the overlay functionality as given in the original LanguageAspect.- $customLanguageAspect = new LanguageAspect($languageUid, $languageUid, LanguageAspect::OVERLAYS_MIXED, $languageAspect->getFallbackChain());+ // The overlay type (and fallback chain) of the language aspect is respected, so translation+ // behavior is consistent with the regular page / content rendering. The content language+ // however may have been adjusted above to the language of the actually fetched record+ // (see Note #1 and the respectSysLanguage handling), so a custom aspect is passed here.+ $customLanguageAspect = new LanguageAspect(+ $languageAspect->getId(),+ $languageUid,+ $languageAspect->getOverlayType(),+ $languageAspect->getFallbackChain()+ );
$row = $pageRepository->getLanguageOverlay($tableName, $row, $customLanguageAspect);
}
} elseif (is_array($row)) {
--- a/Classes/Persistence/Generic/Backend.php+++ b/Classes/Persistence/Generic/Backend.php@@ -165,11 +165,14 @@
// This allows to fetch IDs for languages for default language AND language IDs
// This is especially important when using the PropertyMapper of the Extbase MVC part to get
// an object of the translated version of the incoming ID of a record.
+ // "Free" mode (OVERLAYS_OFF) is mapped to OVERLAYS_MIXED - overlays need to be enabled for the+ // identity lookup, but hiding untranslated records is not a configured intent in free mode.+ // This is consistent with the same handling for related objects in DataMapper->getPreparedQuery().
$languageAspect = $query->getQuerySettings()->getLanguageAspect();
$languageAspect = new LanguageAspect(
$languageAspect->getId(),
$languageAspect->getContentId(),
- $languageAspect->getOverlayType() === LanguageAspect::OVERLAYS_OFF ? LanguageAspect::OVERLAYS_ON_WITH_FLOATING : $languageAspect->getOverlayType(),+ $languageAspect->getOverlayType() === LanguageAspect::OVERLAYS_OFF ? LanguageAspect::OVERLAYS_MIXED : $languageAspect->getOverlayType(),
$languageAspect->getFallbackChain()
);
--- a/Classes/Persistence/Generic/Storage/Typo3DbBackend.php+++ b/Classes/Persistence/Generic/Storage/Typo3DbBackend.php@@ -590,9 +590,16 @@
$row['uid'] = $row[$translationParentPointerField];
$row[$languageField] = 0;
}
- // Currently this needs to return the default record (OVERLAYS_MIXED) if no translation is found- //however this is a hack and should actually use the overlay functionality as given in the original LanguageAspect.- $customLanguageAspect = new LanguageAspect($languageUid, $languageUid, LanguageAspect::OVERLAYS_MIXED, $languageAspect->getFallbackChain());+ // The overlay type (and fallback chain) of the language aspect is respected, so translation+ // behavior is consistent with the regular page / content rendering. The content language+ // however may have been adjusted above to the language of the actually fetched record+ // (see Note #1 and the respectSysLanguage handling), so a custom aspect is passed here.+ $customLanguageAspect = new LanguageAspect(+ $languageAspect->getId(),+ $languageUid,+ $languageAspect->getOverlayType(),+ $languageAspect->getFallbackChain()+ );
$row = $pageRepository->getLanguageOverlay($tableName, $row, $customLanguageAspect);
}
} elseif (is_array($row)) {
Copied!
Apply the matching patch with the composer plugin
cweagans/composer-patches
(see its README for installation and usage). Copy the patch into the project
(for example into a patches/ directory) and reference it:
{
"require": {
"cweagans/composer-patches": "^1.7"
},
"extra": {
"patches": {
"typo3/cms-extbase": {
"Respect language overlay type in Extbase (forge #88886, review 66694)": "patches/extbase-88886-respect-language-overlay-type-v13.patch"
}
}
}
}
Copied!
Both patches touch only
typo3/cms-extbase
(Classes/Persistence/Generic/Storage/Typo3DbBackend.php and
Classes/Persistence/Generic/Backend.php);
EXT:academic_persons
itself needs no change.
Important: Contract relation columns are NOT NULL now
Description
The organisational_unit, function_type and location columns
of tx_academicpersons_domain_model_contract were nullable integer columns,
and their TCA select fields used an empty string (respectively null) as the
value of their "please select" item. PostgreSQL rejects an empty string as an
integer value, so on that DBMS saving a contract with one of these selects empty
failed — and so did every DataHandler localize of a contract whose source
row held NULL in one of them, which the reworked translation
synchronisation runs into. MySQL, MariaDB and SQLite coerce the empty string to
0 silently, which is why the defect only surfaced on PostgreSQL.
Both sides now follow the core convention for optional single-value relation
selects: the empty item value is 0, the field default is 0, and the
three columns are int(11) unsigned DEFAULT '0' NOT NULL.
Impact
The database analyzer will suggest altering the three columns. On installations
where existing rows hold NULL in one of them — rows created before this
version and never saved since — the ALTER TABLE fails on PostgreSQL and on
MySQL in strict mode until those values are updated to 0:
UPDATE tx_academicpersons_domain_model_contract
SET organisational_unit = 0WHERE organisational_unit ISNULL;
UPDATE tx_academicpersons_domain_model_contract
SET function_type = 0WHERE function_type ISNULL;
UPDATE tx_academicpersons_domain_model_contract
SET location = 0WHERE location ISNULL;
Copied!
An empty relation is stored as 0 from now on; Extbase resolves both
NULL and 0 to an unset relation, so rendering is unaffected.
Affected Installations
Every installation — the schema change applies everywhere. Functionally broken
before this change: PostgreSQL installations only.
Important: The timeline year fields are constrained
Description
The three year columns of
tx_academicpersons_domain_model_profile_information
-
year
,
year_start
and
year_end
- declared
and enforced none of it.
min
and
max
are options of the
TCA type
input
; the type
number
reads its bounds from
range
alone. So the backend form rendered a number field without an
HTML
min
or
max
attribute, and
DataHandler
clamped
nothing on save.
which is what renders the HTML bounds and what
DataHandler
clamps a
submitted value against; ext_tables.sql declares the three columns
int(11) unsigned DEFAULT NULL, what the corrected TCA derives. The
palette, the labels, the property names and the frontend rendering are
unchanged.
Impact
The backend record editor now keeps a year within 0-9999: a value
above the upper bound is clamped to 9999 on save, a negative one to 0.
NULL
stays the empty value.
ext_tables.sql declares the three columns as
int(11) unsigned DEFAULT NULL
, matching what the corrected TCA derives.
The database analyzer therefore offers the change of signedness. An
installation that stored a negative year - which nothing in the extension ever
wrote - has to correct those rows before applying it.
Affected Installations
Every installation of
fgtclb/academic-persons
that edits profile
information records in the TYPO3 backend.
Important: Label changes of the public profile
Description
The rebuilt public profile detail view (see
Feature: Configurable public profile) changes three things about the
labels of Resources/Private/Language/locallang.xlf. All three are
relevant for an installation that ships its own translation of this file or
overrides single units through
$GLOBALS['TYPO3_CONF_VARS']['SYS']['locallangXMLOverride']
.
A misspelled unit id is corrected. The English file declared the unit
<trans-unitid="contracts.emailAdresses">
Copied!
with one d, while the German file always declared
contracts.emailAddresses
. The German target was therefore unreachable
and the English source was never rendered either: the label is looked up as
contracts.emailAddresses
by
Resources/Private/Partials/Profile/Contract/Field.html, which
translates
contracts.{fieldName}
for the fields a list or card plugin
was configured to show. The id is now spelled
contracts.emailAddresses
in both files. An override keyed on the old
id stops taking effect and has to be re-keyed.
Three units are added for the detail view:
detail.subline
,
detail.contact
and
detail.navigation
- the subline of the shipped layout, the heading of
the contact block and the accessible name of the section navigation.
Two units are no longer rendered by the shipped templates:
detail.contracts
and
detail.additionalInformation
. They were the
headings of the two static sections the old detail template had; the rebuilt
view has no equivalent, because its blocks are the configured elements and
each of them carries its own heading. Both units are kept in the file for
installations that reference them from their own templates.
Impact
Re-key an XLF override of
contracts.emailAdresses
to
contracts.emailAddresses
. Nothing else has to be changed: the added
units ship with an English source and a German target, and the two unused ones
are still there.
Affected Installations
Installations that translate or override
EXT:academic_persons/Resources/Private/Language/locallang.xlf.
Important: Record icons follow the backend colour scheme
Description
The record icons of this extension were registered with the core provider
\TYPO3\CMS\Core\Imaging\IconProvider\SvgIconProvider
, which renders
the default markup - the markup a
typeicon_classes
entry reaches - as an
<img>
tag. An image is opaque to CSS, so the icon kept the ink of its
file whatever the backend colour scheme said, and a dark drawing stayed dark on
the dark cards of the record list.
They are now registered with
\FGTCLB\AcademicBase\Imaging\IconProvider\CurrentColorSvgIconProvider
,
which inlines the file in both markups, and the files themselves are drawn in
currentColor with no colour of their own.
That covers the record icons of all nine tables this extension ships, from
tx_academicpersons_domain_model_address
to
tx_academicpersons_domain_model_profile_information
.
Impact
The nine record icons take the text colour of the backend, so they stay legible
in a dark colour scheme. Their markup is now the inlined
<svg>
rather
than an
<img>
, which matters to any CSS or test that addressed the
image.
The plugin icon
persons_icon
is a brand mark and keeps the core
provider. The six control icons of the public profile were already registered
with the currentColor provider and are unchanged.
Affected Installations
Every installation of this extension.
Important: Remaining unordered queries now order by uid
ContractRepository::findAll()
, which builds every contract select
item in the backend (TCA
itemsProcFunc
and FlexForm)
ContractRepository::findByUids()
, which resolves the contracts of
the "selected contracts" plugin
ProfileRepository::findByUids()
, which resolves the profiles of the
"selected profiles" plugin
ProfileRepository::findByFrontendUser()
, which resolves the profiles
of a frontend user for the frontend editing of
academic_persons_edit
ProfileRepository::findByDemand()
with a non-empty demanded ordering
— the list plugin's Sort by — which carried no tiebreaker, so
profiles equal in it (two people sharing a last name) had no defined
relative order
The first four now order by
uid
ascending; the demanded ordering keeps
winning and gets
uid
ascending appended as a tiebreaker.
Three further methods ordered by
sorting
alone:
AddressRepository::findByContractIncludingHidden()
,
EmailRepository::findByContractIncludingHidden()
and
PhoneNumberRepository::findByContractIncludingHidden()
, which list the
contact records of a contract for the frontend editing. Records an editor
never reordered share a
sorting
value, and their relative order was
whatever the database yielded. All three now append
uid
ascending as
the tiebreaker.
Impact
No visible change is expected:
uid
ascending is the order every
supported database returned in practice, it is simply guaranteed now rather
than coincidental.
For the two uid selection methods the order of the editor's selection is
deliberately not reproduced —
in()
does not preserve it, and it was
never delivered before. Honouring the selection order would be a behaviour
change beyond making the lists reproducible.
Affected Installations
Every installation of this extension.
Important: Route enhancers have to be limited to their pages
Description
The three route enhancers this extension ships below
Configuration/Routes/ describe the same two views, so three of their
routes are declared twice and are identical down to the mapper:
Route
Declared in
/{profile_name}
Detail.yaml and ListAndDetail.yaml
{localized_page}-{page}
List.yaml and ListAndDetail.yaml
/{letter}
List.yaml and ListAndDetail.yaml
TYPO3 offers every enhancer of a site to every page of that site unless the
enhancer says otherwise, and it takes the first candidate route whose path
matches and whose aspects resolve. So a site that imports more than one of
the three files without saying where each applies gives all of those URLs to
the file it imported first.
Only resolving is ambiguous. Generating a URL is scoped to the plugin namespace
being linked, so the links keep looking right and nothing points at the
configuration.
Impact
On a site that imports ListAndDetail.yaml before Detail.yaml,
the page carrying the Detail plugin answers 404 for every link
the list plugins generate for it — the profile argument arrives in the
namespace of the other plugin, and
ProfileController::detailAction()
receives nothing.
The two list routes fail more quietly, and in whichever direction the import
order points: the page number or the letter arrives in the wrong namespace, and
the plugin renders the unfiltered first page with status 200.
Affected Installations
Installations whose site configuration imports more than one of
List.yaml, ListAndDetail.yaml and Detail.yaml — which is
what a site showing both the separate and the combined plugin needs, and what
the documentation of this extension recommended without further qualification
until now.
Installations that import a single one of the three files are not affected.
Solution
Limit each enhancer to the pages that carry its plugin. With that in place the
import order no longer matters:
The uids are those of the pages carrying the plugin in question, in the
default language: matching derives the page as
l10n_parent ?: uid
,
so one list covers every translation of that page. Plain page uids work on
every TYPO3 version this extension supports.
The update path of the translation synchronisation — the case where a
translation already exists — re-submitted the
l10n_mode=exclude
values
of the profile record only. A child record's exclude value changed after the
child's translation existed therefore stayed stale in that translation: a
contract's
valid_from
, an address type, a profile information year.
The datamap now covers the whole default-language inline child tree: every
child's propagatable exclude values are part of the same single DataHandler
pass, and the core
DataMapProcessor
carries them into every translation
of every touched record.
Two things did not change, and are now pinned by tests:
File references and MM relations added to the default record after its
translation exists were always carried over — the core synchronizes all
exclude columns of a touched record from its database row, including the
relational ones. The previously documented gap was design-inferred and did
not exist. The profile image stopped being an exclude column with
Breaking: The profile image translates; the same core pass carries it
into every translation whose image follows the default language, so the
pin of the exclude behaviour moved to a test column of the test suite and
the synchronisation itself has no image-specific code.
enableLogging
stays on:
sys_log
rows with userid=0 are
the audit trail of what the synchronisation wrote.
Impact
Editing an exclude column of a contract, address, email address, phone number
or profile information record of an already-translated profile now reaches the
record's translations on the next synchronisation, the same way it always did
for the profile's own exclude columns.
Affected Installations
Every installation using the translation synchronisation of this extension —
through the frontend editing of
academic_persons_edit
, through the
academic:updateprofiles
command (which dispatches
AfterProfileUpdateEvent
since ACE-490), or by dispatching the event
from its own hooks.
Important: Translation sync is routed through the DataHandler
Description
\FGTCLB\AcademicPersons\Service\RecordSynchronizer
— the service behind
the AfterProfileUpdateEvent listener that keeps profile translations in
sync — previously wrote its translations with raw SQL. That implementation had
drifted a long way from what a translation write in TYPO3 involves.
The synchronisation only ever touched the top-level row. The recursion into
inline children was dead code since version 2.1.0: a contract, address, email or
phone number was never translated along with its profile, and a contract added
after the translation existed was never carried over. File references and MM
relations (the profile image, related frontend users) were skipped even by
design, and l10n_diffsource was left empty, so the backend diff view had
nothing to show.
Every write was a live row, regardless of the acting workspace — an
unpublished draft leaked into the live site the moment anything triggered a
synchronisation from within a workspace.
The service now routes every write through the TYPO3
DataHandler
. A
missing translation is created with a localize command, which carries the
full inline child tree, file references, MM relations and the diff source. For
an existing translation, the current values of the l10n_mode=exclude
columns are re-submitted as a datamap, so core's DataMapProcessor
propagates them, and an inlineLocalizeSynchronize command per inline
column carries children added later — including their own children.
Impact
Child records are actually synchronised again. A profile synchronisation
now translates contracts and their children, file references and MM relations,
as it did before 2.1.0. Note that this also re-activates the known
contacts4pages behaviour where a translated contact keeps pointing at an
untranslated default-language page.
Writes are workspace aware. A backend user acting in a workspace produces
versioned rows only (t3ver_wsid, t3ver_state); the live site is
untouched until the workspace is published. A frontend-triggered
synchronisation acting in a non-live workspace is refused entirely and logs a
notice instead of writing anything.
Because the writes go through the DataHandler they now also update the
reference index and record history, fire hooks, and bump tstamp on
updated translations.
Affected Installations
Every installation that enables the translation synchronisation through the
profile.allowedLanguages setting of EXT:academic_persons_edit, or that
dispatches AfterProfileUpdateEvent from its own hooks.
Important: Unordered profile queries now order by uid
Description
Five query paths of this extension executed without any ordering, so the order
of their result was whatever the database happened to yield:
ProfileRepository::findAll()
ProfileRepository::findByDemand()
whenever the demand yields no
ordering — which is what the list plugin's Sort by option
none means
LocationRepository::findAll()
,
FunctionTypeRepository::findAll()
and
OrganisationalUnitRepository::findAll()
, which fill the location,
function type and organisational unit selects of the contract form in
academic_persons_edit
All five now order by
uid
ascending when nothing else asks for an
order.
Impact
No visible change is expected. Every supported database returned insertion
order for these queries in practice, and
uid
ascending is that same
order — the assertions of the affected functional tests are unchanged, they
are simply guaranteed now rather than coincidental.
What changes is that the order is reproducible. It previously depended on the
database, its version and which indexes existed, and could therefore change
under an installation without any content change. That is not hypothetical:
making the person tables workspace aware in the same release added an index
over
t3ver_oid
, which gave the PostgreSQL planner a way to satisfy the
t3ver_oid = 0
constraint Extbase adds for a workspace aware table, and
reversed the result of exactly these queries.
The TCA
default_sortby
of the location, function type and organisational
unit tables still does not apply to their
findAll()
. That was never
the case and is not changed here — it orders the record lists of the backend,
not the selects of the frontend edit form.
Affected Installations
Every installation of this extension. An installation that already sets a
sorting on the list plugin is unaffected either way, because an explicit
ordering always won.
Important: Validation primitives moved to EXT:academic_base
Description
The value objects and the ViewHelper behind
Configuration/AcademicPersons/Settings.yaml moved to
EXT:academic_base, together with the loading, the flag normalisation and the
TCA merge that
AcademicPersonsSettingsFactory
and
AcademicPersonsSettings
carried inline:
FGTCLB\AcademicBase\Settings\TcaValidationMerger::merge()
,
applied by five of the six TCA files of this extension; the
profile information table merges a
types
fragment built
from the same value objects instead
All of them are
@internal
. No class aliases are registered for the
old names: the classes never were public API, and no extension of this set
referenced them from outside EXT:academic_persons and
EXT:academic_persons_edit, both of which are adapted.
Nothing changes for the YAML file itself - its location, its sets, its flags,
and the package walk and top-level merge of an installation's override are
all as before.
The normalised result is still cached in the core cache, but under the new
identifier AcademicPersons_Settings_v3 instead of
AcademicPersons_Settings. The cache entry is a PHP statement naming the
classes of the settings graph, so an entry written by an earlier version
references the removed
\FGTCLB\AcademicPersons\Settings\ValidationSet
and would fail to load before any check could reject it. The new identifier
leaves such an entry untouched and unused; it disappears with the next cache
flush.
Impact
Code that type hints or instantiates the old classes fails with a class not
found error and has to import the EXT:academic_base names.
A project that declares the ViewHelper namespace in a template of its own has
to point it at the new location, or the template no longer renders:
The p:validationEnsure calls in the template stay as they are.
A project that overrides one of the frontend editing form partials of
EXT:academic_persons_edit has nothing to repoint and nothing to keep: the
Extbase form flow those partials belonged to is replaced in 3.0, the
Partials/Profile/Forms/ directory is gone with it, and no template of
either extension declares the namespace any more. Delete the override and
start from what the new editing view renders - the changelog entry Breaking:
Replaced the profile editing view of EXT:academic_persons_edit lists the
removed templates and the partials that took their place.
No cache flush is required for the settings themselves - see the cache
identifier above - but flushing all caches after the update is harmless and
removes the orphaned entry.
3.x Changes by type
This lists all changes to the Academic Persons extension of minor versions
grouped by their type.
Breaking: Profile synchronization now includes hidden profiles and users
Description
The profile synchronization (profile update command) previously skipped
frontend users that were disabled or whose profile was hidden. Such
records were neither selected by
\FGTCLB\AcademicPersons\Provider\FrontendUserProvider::getUsersWithProfileResult()
(the automatic hidden restriction excluded them) nor resolved by
\FGTCLB\AcademicPersons\Domain\Repository\ProfileRepository::findByFrontendUser()
.
The synchronization now processes hidden profiles and disabled frontend
users as well and keeps their data up to date, without changing their
visibility.
The same applies to the profile creation (profile create command):
\FGTCLB\AcademicPersons\Provider\FrontendUserProvider::getUsersWithoutProfileResult()
now also returns disabled frontend users and
\FGTCLB\AcademicPersons\Profile\AbstractProfileFactory::createProfileForUser()
resolves the frontend user ignoring its visibility, so a profile is
created for a disabled frontend user as well.
Impact
A hidden profile or a profile of a disabled frontend user that was
relying on being skipped by the synchronization is now updated again on
the next synchronization run. Its data (name, contact records, ...) is
overwritten with the current frontend user data, while the hidden
state itself is kept untouched.
A disabled frontend user without a profile now also receives a newly
created profile on the next profile create run.
Only the deleted state is still respected; both the frontend user
visibility (fe_users.disable) and the profile hidden field are
ignored for the profile synchronization and creation.
Affected Installations
All installations using the profile synchronization
(EXT:academic_persons create/update profile commands) together with
manually hidden profiles or disabled frontend users.
Migration
If a profile should be excluded from the synchronization, use the
dedicated skip_sync flag of the profile instead of hiding the profile
or disabling the frontend user. Hiding a profile now only controls its
frontend visibility, not whether it is synchronized.
Breaking: Renamed itemsProcFunc handler classes to FormEngine namespace
Description
While introducing the shared PSR-14 event
\FGTCLB\AcademicBase\Event\ModifyTcaSelectFieldItemsEvent
to allow
modifying the select items of fields populated by an itemsProcFunc, the two
itemsProcFunc handler classes shipped by EXT:academic_persons received a
proper namespace and class names suitable for classes providing
itemsProcFunc implementations.
The following classes have been renamed and moved:
\FGTCLB\AcademicPersons\Backend\Form\ContractItemsProcFunc
is now
\FGTCLB\AcademicPersons\Backend\FormEngine\ContractItems
.
\FGTCLB\AcademicPersons\Backend\Form\ProfileShowFieldsItemProcFunc
is now
\FGTCLB\AcademicPersons\Backend\FormEngine\ProfileShowFieldsItems
.
Additionally the entry method of the profile show fields handler has been
streamlined from
showFields()
to
itemsProcFunc()
to match the
other handlers.
The references to these classes in the shipped plugin FlexForms have been
adjusted accordingly.
Impact
Referencing the old class names or the old
showFields()
method throws
a PHP error. This affects TCA/FlexForm itemsProcFunc configurations,
projects extending or replacing these classes and any code instantiating them
directly.
Affected Installations
Installations that reference the old classes in own TCA/FlexForm
configuration, extend or replace them, or call them directly.
Migration
Replace all usages of the old class names with the new ones and use the
itemsProcFunc()
method for both handlers:
Projects that only replaced the shipped items should consider using the new
\FGTCLB\AcademicBase\Event\ModifyTcaSelectFieldItemsEvent
event listener
instead of a custom itemsProcFunc.
Breaking: Site sets and static templates have been restructured
Description
The TypoScript and the page TSconfig of this extension were shipped twice: the
static template read Configuration/TypoScript/Default/, and the two site
sets
fgtclb/academic-persons-default
and
fgtclb/academic-persons-standalone
shipped their own
constants.typoscript and setup.typoscript, each of them a single
@import
of that folder. The page TSconfig existed as one flat file,
Configuration/TSconfig/page.tsconfig, and was not selectable on a page at
all.
Both mechanisms now read one physical copy of every file, and both of them
deliver the extension per content element instead of as one block:
Configuration/TypoScript/Default/ still holds the shared
plugin.tx_academicpersons
block — all six content elements are
driven by one Extbase plugin, so there is one copy of it and every component
folder names it in its own include_static_file.txt.
Configuration/TypoScript/<Component>/ is what the static template
registers and what the component set points its
typoscript
key at.
Configuration/TSconfig/<Component>/page.tsconfig holds the page
TSconfig of one content element and is what the page field
Page TSconfig offers and what the set points its
pagets
key at.
Configuration/TypoScript/Full/ and
Configuration/TSconfig/Full/page.tsconfig are the aggregates for
installations that do not use site sets.
The content elements are now hidden by default. The always-included
Configuration/page.tsconfig removes all six from the selectable content
element types, and the page TSconfig of a component adds its own back — so an
element is offered where it is wanted instead of on every page of every
installation. The TCA registration itself did not move, so the frontend renders
existing records exactly as before. Editing such a record in the backend is a
different matter — read the warning below before upgrading.
Two defaults changed as well.
plugin.tx_academicpersons.demand.groupBy
and
plugin.tx_academicpersons.demand.sortBy
were empty in
constants.typoscript while the site sets declared lastNameAlpha and
title for the very same paths, so the two mechanisms configured a site
differently. Both now carry the same value everywhere.
demand.sortBy
is additionally corrected to lastName. The value
the site sets shipped, title, is not one the extension accepts: the
extension configuration
demand.allowedSortByValues
allows
firstName and lastName only, and
ProfileRepository::getOrderingsFromDemand()
drops anything else. A site
on a site set therefore asked for an ordering it never got.
Impact
The
sys_template
records of this extension keep working. Both values that
installations store today — EXT:academic_persons/Configuration/TypoScript/Default
and EXT:academic_persons/Configuration/TypoScript/Standalone — still resolve,
and both still deliver the plugin configuration. Only the labels shown next to
them in the record changed.
A site that has no site set and did not set
plugin.tx_academicpersons.demand.groupBy
itself now groups a
profile list by the first letter of the last name, because that is the default
the site sets already applied. Set the constant to an empty value to keep an
ungrouped list.
A site on a site set that did not set
plugin.tx_academicpersons.demand.sortBy
itself now sorts a
profile list by last name. It asked for title before and got no ordering at
all, because the value was rejected. Set the setting to firstName for the
other accepted ordering; there is no supported way back to "no ordering"
through this setting.
A site package that imported one of the removed files by path fails to resolve
it.
@import
of a missing file is silent, so this shows up as
missing configuration rather than as an error message.
None of the six content elements is offered in the backend until the page
TSconfig of its component is included, through the site set or through the page
field Page TSconfig. This affects every installation: before this
change the extension hid nothing.
Warning
Do not open an existing record of one of these content elements in the
backend form on a page that does not include the page TSconfig of its
component. An item removed through
TCEFORM.tt_content.CType.removeItems
is excluded from the
[ invalid value ] fallback TYPO3 otherwise adds for a stored
value it does not know, and the stored value is dropped from the form data
as well. The field Type therefore comes up with nothing
selected, and saving the record writes whatever the browser preselected
into
CType
— the record silently becomes another content element.
The frontend keeps rendering it correctly until that happens.
Include the page TSconfig of the components on every page tree that holds
such records, and do it before editing them.
The sets
fgtclb/academic-persons-default
and
fgtclb/academic-persons-standalone
keep their names and keep delivering
everything, so a site configuration that depends on either needs no change.
Affected Installations
Every installation that uses one of the content elements of this extension, plus
installations that import one of the shipped files from an own site package.
Migration
Add the page TSconfig entry, which did not exist before, in the page record of
the site root, tab Resources, field Page TSconfig:
Academic Persons: All components (academic_persons), stored as
EXT:academic_persons/Configuration/TSconfig/Full/page.tsconfig. Without it the
content elements are not selectable any more, and existing records of them lose
their
CType
when they are saved from the backend form.
Sites that use a site set instead need no migration — but they must not use both
mechanisms at once, see the Configuration chapter.
The static template entries in the
sys_template
record need no migration
either. Their labels changed, their stored values did not:
EXT:academic_persons/Configuration/TypoScript/Default/setup.typoscript
and, for the
page
object,
EXT:academic_persons/Configuration/TypoScript/StandalonePage/setup.typoscript
The first two were shipped as
@deprecated
one line forwards to
Configuration/TypoScript/Default/ and are removed with this change.
A site configuration may name the new component sets instead of the aggregate:
Set
Delivers
fgtclb/academic-persons
New name of the aggregate, delivers through the component sets
below.
fgtclb/academic-persons-default
Unchanged in name, now an alias of fgtclb/academic-persons.
fgtclb/academic-persons-standalone
Unchanged in name, now the aggregate plus the
page
object.
fgtclb/academic-persons-list
The Persons List content element only.
fgtclb/academic-persons-list-and-detail
The Persons List and Detail content element only.
fgtclb/academic-persons-detail
The Persons Detail content element only.
fgtclb/academic-persons-card
The Contacts content element only.
fgtclb/academic-persons-selected-profiles
The Profiles: Selected Profiles content element only.
fgtclb/academic-persons-selected-contracts
The Profiles: Selected Contracts content element only.
Feature: Configurable frontend user phone number types
Description
Telephone and fax numbers imported from frontend users no longer carry the
hard-coded values phone and fax, which are not selectable in the shipped
phone-number type list. Two new extension configuration options say which type
each source field imports as:
profile.feuser.telephoneNumberType = business
profile.feuser.faxNumberType = business
Copied!
Both default to business, which is part of the shipped
types.phoneNumberTypes. Each value is validated against that list
before it is written: a value the installation does not offer falls back to the
undefined type '', which the backend select ships as its first item. The
synchronisation therefore never writes a type the backend cannot resolve.
An existing selectable type is an editor decision and is left alone. Only the
two historical values are corrected, and only where they are not selectable on
that installation: an installation that genuinely offers phone or fax as
types keeps them.
Impact
New and updated imports carry a configured, selectable type. Installations that
want the imported numbers typed differently — or not typed at all — set the two
options; no other configuration changes.
Feature: Dispatch ModifyTcaSelectFieldItemsEvent in itemsProcFunc handlers
Description
The following itemsProcFunc handlers provided by EXT:academic_persons now
dispatch the shared PSR-14 event
\FGTCLB\AcademicBase\Event\ModifyTcaSelectFieldItemsEvent
:
Previously the shipped select items could only be adjusted by adding items
directly (without influence on the ordering) or by implementing a custom
itemsProcFunc replacing the shipped one. Both ways were neither convenient
nor testable.
With the dispatched event, projects can modify the available select items for
the backend (FormEngine) - and, where the handler is reused, for the frontend
- through a single PSR-14 event listener, instead of dealing with a dozen
dedicatedly named events for the same purpose.
The List, List and detail and Card plugins share the
core-version-aware List.xml flexform; Selected profiles,
Selected contracts and Detail use their own SelectedProfiles.xml,
SelectedContracts.xml and Detail.xml flexforms. All are
provided for both the TYPO3 v12 and v13 flexform data structures.
When the option is enabled, the affected frontend listing includes hidden
(disabled) records, independent of the Context API visibility settings.
Only the hidden enable column (disabled) is ignored; the deleted,
starttime/endtime and fe_group restrictions stay in effect.
The single-profile Detail plugin resolves its profile through Extbase
argument mapping, which normally respects enable fields. When the option
is enabled, the detail view re-resolves the referenced profile including
hidden records, so a hidden profile can be displayed on its detail page.
Impact
Editors can now opt in per plugin instance to display hidden profiles or
contracts in the frontend, for example to preview intentionally hidden
records without changing the global preview settings. The option is off
by default, so existing plugin instances keep their current behaviour.
Affected Installations
All installations using the EXT:academic_persons extension starting
with version 2.4. No action is required for existing installations.
Feature: Synchronize hidden profiles
Description
The profile synchronization (profile update command,
\FGTCLB\AcademicPersons\Service\ProfileUpdateCommandService
) now
also keeps hidden profiles and profiles of disabled frontend
users up to date. Previously a frontend user that was disabled or whose
profile was hidden was excluded from the synchronization completely, so
the profile was never updated and no synchronization events were
dispatched for it.
The
\FGTCLB\AcademicPersons\Domain\Repository\ProfileRepository
method
findByFrontendUser()
gained an optional argument to include
hidden profiles:
The synchronization calls it with
$showHidden = true
, while the
frontend display keeps the default (
$showHidden = false
) and
therefore continues to respect the visibility.
The visibility itself is never changed by the synchronization — that
stays the responsibility of the
\FGTCLB\AcademicPersons\Profile\ProfileFactoryInterface
implementation — so a manually hidden profile stays hidden while its data
is kept in sync.
The profile create command keeps skipping frontend users that already
have a profile, including a hidden one, so no duplicate profiles are
created for them. It now also creates a profile for a disabled frontend
user that does not have one yet:
\FGTCLB\AcademicPersons\Provider\FrontendUserProvider::getUsersWithoutProfileResult()
returns disabled frontend users and
\FGTCLB\AcademicPersons\Profile\AbstractProfileFactory::createProfileForUser()
resolves the frontend user ignoring its visibility.
Hidden profiles and profiles of disabled frontend users are no longer
silently excluded from the synchronization. To exclude a profile from
synchronization, use the dedicated skip_sync flag of the profile
instead of hiding it or disabling the frontend user.
Affected Installations
All installations using the profile synchronization
(EXT:academic_persons create/update profile commands) starting with
version 2.4.
Feature: Visibility (hidden) support for contact record models
Description
The contact record models gained access to the hidden enable field
that already exists on their database tables:
\FGTCLB\AcademicPersons\Domain\Model\Address
\FGTCLB\AcademicPersons\Domain\Model\Email
\FGTCLB\AcademicPersons\Domain\Model\PhoneNumber
Each of them now provides:
getHidden(): bool
setHidden(bool $hidden): self
To work with hidden records the matching repositories
(
AddressRepository
,
EmailRepository
,
PhoneNumberRepository
) provide two new methods that ignore the
disabled enable field:
These are used by EXT:academic_persons_edit (optional) to let profile
owners show and hide their contact records in the frontend, while the
public profile display keeps excluding hidden records via the default
Extbase enable field handling.
Impact
The profile synchronization keeps matching and updating existing
contact records even when they are hidden, and no longer resets their
visibility. Integrators can use the new model accessors and repository
methods to handle contact record visibility programmatically.
Affected Installations
All installations using the EXT:academic_persons extension starting
with version 2.4.
Migration
No migration is required, except you extended and replaced the extbase
model in projects - then you need to adopt the newly added properties
and setter/getter methods with the same signatures.
Important: Extended ProfileDemand, findByUids() and demand handling
Description
To support the new "Show hidden records" plugin option, the person demand
and repository layer gained the following additions. All of them are
non-breaking (new optional parameter / new transport flag with defaults):
\FGTCLB\AcademicPersons\Domain\Model\Dto\ProfileDemand
has a new
showHiddenRecords
transport flag with
getShowHiddenRecords(): bool
and
setShowHiddenRecords(bool): ProfileDemand
accessors
(default
false
). It mirrors the existing transport-only
properties (
storagePages
,
fallbackForNonTranslated
) and is
likewise announced as a future addition to
\FGTCLB\AcademicPersons\Domain\Model\Dto\DemandInterface
(the
interface carries the commented signatures and the repository guards the
call with
method_exists()
).
\FGTCLB\AcademicPersons\Domain\Repository\ProfileRepository::findByDemand()
honours
ProfileDemand::getShowHiddenRecords()
via
applyDemandSettings()
.
\FGTCLB\AcademicPersons\Domain\Repository\ProfileRepository::findByUids()
and
\FGTCLB\AcademicPersons\Domain\Repository\ContractRepository::findByUids()
gained an appended optional
bool $showHidden = false
parameter.
\FGTCLB\AcademicPersons\Domain\Repository\ProfileRepository::findByUidIncludingHidden(int $uid): ?Profile
is a new method that resolves a single profile by uid including hidden
(disabled) records. It is used by the detail view.
\FGTCLB\AcademicPersons\Controller\ProfileController
reads
$this->settings['showHiddenRecords']
in
adoptSettings()
,
cardAction()
,
selectedProfilesAction()
and
selectedContractsAction()
and
threads it into the repositories. The new
initializeDetailAction()
re-resolves the profile argument via
findByUidIncludingHidden()
when the option is enabled, because the
default Extbase argument mapping respects enable fields.
When the flag/parameter is set, the query ignores only the disabled
(hidden) enable field via the Extbase query settings; the deleted,
starttime/endtime and fe_group restrictions stay in effect.
Impact
The change is non-breaking: the new demand flag defaults to
false
,
the new
findByUids()
parameter has a default value, and no existing
method signature changed in an incompatible way. Projects that build a
ProfileDemand
or call
findByUids()
themselves can opt in via
setShowHiddenRecords(true)
respectively the new parameter.
Affected Installations
Only installations that extend or replace the
ProfileDemand
DTO,
the
ProfileRepository
, the
ContractRepository
or the
ProfileController
need to take the additions into account. All
other installations are unaffected.
Important: Imported telephone records use a stable identifier
Description
The import identifier of a synchronised phone-number record used to be built
from its type, so it read phone:fe_users:<uid> for a telephone number. That
made the identity of the record depend on a value that is now configurable, and
a reconfiguration would have created a second record instead of updating the
first one.
The identifier is now built from the source field of fe_users and never
from the type:
The record is matched on that identifier alone. A record still carrying the
legacy phone:fe_users:<uid> is recognised, renamed and — where its type is
still the unselectable phone — retyped, in one step. Where a contract
carries both a legacy and a canonical record, the canonical one wins and the
legacy one is left untouched, because its provenance cannot be established
safely.
Impact
Two stored values change for records the synchronisation writes:
import_identifier for telephone records, and type wherever it still held
phone or fax and that value is not selectable on the installation.
Anything outside this extension that keys on phone:fe_users:<uid> has to be
adjusted.
No upgrade wizard is shipped, deliberately. Existing records are repaired by
the next
academic:updateprofiles
run — the same command that wrote them —
and that run is what the affected installations already schedule. A bulk
migration would have to decide what the synchronisation deliberately refuses to
decide: which of two colliding records is the real one, whether soft-deleted and
workspace rows take part, and what to write where the configured type is not
selectable.
Six cases are consequently not repaired by a synchronisation run, and an
installation that needs them corrected has to act deliberately:
profiles carrying skip_sync = 1
frontend users that are soft-deleted
profiles whose tx_academicpersons_feuser_mm relation was removed
records on page ids excluded by the configured --include-pids or
--exclude-pids
installations that ran an import once and never run it again
records whose fe_users.telephone has since been emptied, which the
synchronisation removes rather than repairs
Affected Installations
Every installation that imports profiles from frontend users with
academic:createprofiles
or
academic:updateprofiles
.
Important: New required dependency fgtclb/environment-state-manager
Description
EXT:academic_persons
now depends on the standalone extension
fgtclb/environment-state-manager and declares it consistently in both
composer.json (
"fgtclb/environment-state-manager": "^1.0"
) and
ext_emconf.php (
'environment_state_manager' => '1.0.0-1.99.99'
).
The extension was switched from the internal, now deprecated
\FGTCLB\AcademicBase\Environment
subsystem to the extracted
fgtclb/environment-state-manager extension (namespace
\FGTCLB\EnvironmentStateManager
), which it uses in its profile command
services. The dependency is therefore required at runtime.
Impact
Composer-managed installations pull fgtclb/environment-state-manager in
automatically when
EXT:academic_persons
is updated to 2.4; no action is
required.
Classic, non-composer installations (TER / extension manager) must install the
environment_state_manager extension in addition to
EXT:academic_persons
,
otherwise the extension cannot be activated.
Affected Installations
Only non-composer installations updating to
EXT:academic_persons
2.4
need to install the additional extension manually. Composer-managed
installations are unaffected.
This gives event listeners the ability to redo a query with the same
or further enriched demand object to replace the retrieved profiles
result already existing in the event and/or enrich data in the view.
Note
Extension supporting earlier versions of the extension need to
check for the existence of the getter and setter method before
calling them.
EXT:academic_persons provided the academic:createprofiles for quite a long
time now to create profile automatically for frontend users taking data from
TYPO3 fe_users records. That can be extended by extension or projects to get
import data from other sources like LDAP or other external identity providers.
To further improve that handling the academic:updateprofiles is now added in
a similar way to handle updates of profile data based on the source using the
scheduler, for example to update data imported from external LDAP.
import_identifier varchar(170) DEFAULT '' NOT NULL
is added to all
extension tables and all domain models got extended to have that property
along with setter und getter in place. Existing imported profile data needs
to be updated in the project to have the identifier in place and custom
create/update profile implementation can use that field to flag it with their
data.
Further a
skip_sync
field is added to
tx_academicpersons_domain_model_profile
defaulting to false ((INT)0). If this field is set to true/1 the update
command excludes these records in a early stage and do not call or dispatch any
further methods or events.
Important
Projects needs to implement own upgrade wizards to set the import_identifier
data before using the new academic:updateprofiles command and also ensure
that custom ProfileFactory implements the additional methods required by the
extended interface to full-fill the requirements for the update command.
Important note
Custom profile factory implementations needs to be updated due to the extended
\FGTCLB\AcademicPersons\Profile\ProfileFactoryInterface
interface and
implement the update handling part.
This is breaking and needs to be addressed on a update.
Breaking: ProfilesController::selectedProfilesAction() no longer dispatches ModifyListProfilesEvent
Description
ProfilesController::selectedProfilesAction() dispatched the ModifyListProfilesEvent
PSR14 event accidentally due to copy&paste when introducing the new plugin and action
for 2.0.x. This event is no longer dispatched for this action, instead the new and
correct event ModifySelectedProfilesEvent is now dispatched.
Affected Installations
EXT:academic_partners installations listening to the ModifyListProfilesEvent event.
Migration
Adapt any listeners/subscribers accordingly to the correct event.
Breaking: Removed partials
Description
Some partials got removed as the templating structure has changed.
EXT:academic_partners installations overriding those partials.
Migration
Adapt overrides accordingly to the new templating structure.
Breaking: Replace constructor DI with inject-methods in AbstractProfileFactory
Description
Using constructor dependency injection in abstract classes defines the constructor
as API, which should be avoided by using the inject-method approach and allows to
implement classes using constructor DI without the requirement to deal and align
with parent (abstract) class constructor and passing it down.
AbstractProfileFactory used constructor DI and therefore violated the above
described design pattern.
Constructor DI arguments are now replaced with inject-methods in the abstract
FGTCLBAcademicPersonsProfileAbstractProfileFactory.
AbstractProfileFactory used constructor DI and therefore violated the above
described design pattern.
Constructor DI arguments are now replaced with inject-methods in the abstract
FGTCLBAcademicPersonsProfileAbstractProfileFactory.
Affected Installations
Installations using the abstract and defining own constructor DI arguments.
Migration
Implementation using the abstract and defining own constructor DI arguments
needs to remove the removed parent arguments and avoid calling the parent
constructor.
Additionally, the SymfonyContractsServiceAttributeRequired attribute is
used for the inject methods to tell symfony DI that these inject methods needs
to be called and are mandatory - beside having a visually glue for developers.
Feature: Add academic:createprofiles options --include-pids and --exclude-pids
Description
EXT:academic_persons provides a command to create profiles for
frontend users, extendable by dispatching events to allow devs
customizing the profile creation in projects, for example to
base it on special needs like retrieving updated user data from
LDAP oder other services.
Until now, all frontend users without profiles on any pid has
been fetched, which does not respect use-cases where frontend
users for dedicated logins are required and profile creation
is not wanted, needed or suitable.
This change adds following new options to the provided command
vendor/bin/typo3 academic:createprofiles:
--include-pids: comma-separated list of storage pid's from
which frontend users should be fetched (only).
--exclude-pids: comma-separated list of storage pid's from
which frontend users should be ignored (skipped).
Important note
While both options can be used together it is important to
know that --exclude-pids takes higher priorities and are
ignored even if pid is also included in include-pids.
Feature: Allow modifying default and settings pageTitleFormat for detail view
Description
It's possible to set a pageTitleFormat in the plugin settings for
plugins using the ProfileController::detailAction(), which is
used in ProfileTitleProvider to set the HTML page title for the
person profile pages.
This change extends the existing ModifyDetailProfileEvent, which
is dispatched in the ProfileController::detailAction(), to make
the default and the setting pageTitleFormat changeable using an
PSR-14 event listener.
This gives developers the ability to implement a wide range of
use-cases in projects, for example adding a prefix to the format
based on the site configuration or similar.
The getPageTitleFormatToUse(): string is a calculated function to
get the aggregated format to use, which allows checking the result
in event listeners and determine the format finally used as detail
view page title.
The original default pageTitleFormat is ProfileTitleProvider::DETAIL_PAGE_TITLE_FORMAT.
Feature: Dispatch PSR-14 event ModifySelectedContractsEvent in ProfilesController::selectedContractsAction()
Description
ProfileController::selectedContractsAction() dispatches now the new PSR-14
ModifySelectedContractsEvent.
The event provides following methods:
getContracts(): QueryResultInterface return current result set.
setContracts(QueryResultInterface $contracts): void to allow setting a custom
resultset.
getView(): FluidViewInterface|CoreViewInterface return the current view to
allow assigning custom values to the view.
getPluginControllerActionContext(): PluginControllerActionContextInterface
to provide more context information
Feature: Dispatch PSR-14 event ModifySelectedProfilesEvent in ProfilesController::selectedProfilesAction()
Description
ProfileController::selectedProfilesAction() dispatches now the new PSR-14
ModifySelectedProfilesEvent instead of erroneous copied listAction event
ModifyListProfilesEvent, which is no longer dispatched. That should not
be that of an issue for most implementations.
The event provides following methods:
getProfiles(): QueryResultInterface return current result set.
setProfiles(QueryResultInterface $profiles): void to allow setting a custom
resultset.
getView(): FluidViewInterface|CoreViewInterface return the current view to
allow assigning custom values to the view.
getPluginControllerActionContext(): PluginControllerActionContextInterface
to provide more context information
It's a valid use-case to use a pageTitleFormat for the person
profile detail page view as HTML page title including localized
text as placeholders and could be implemented using the PSR-14
event ModifyProfileTitlePlaceholderReplacementEvent dispatched
in the ProfileTitleProvider.
Localization is a generic feature and it's most likely that it's
use-full for a broader audience this change adds now support for
localization placeholder in the format:
Note that no context fallback detection is made like within fluid
templates or extbase context areas and a valid relative path for
the default language file within a extension needs to be provided.
Functional tests are added to cover the new feature basically and
provide some examples, using a dedicated test fixture extension.
A new readonly DTO object PluginControllerActionContext is introduced and is
attached to dispatched PSR-14 events in ProfileController actions.
Impact
Following main getters are provided:
getApplicationType(): ApplicationType to return the TYPO3 application type
for the current request.
`getExtbaseRequestParameters(): ?ExtbaseRequestParameters` to retrieve extbase
attribute from request as a simple accessor.
getRequest(): ServerRequestInterface to return the current request.
`getSettings(): array` to retrieve raw plugin settings (TypoScript, FlexForm).
getSite(): ?Site to retrieve resolved site configuration.
* getLanguage(): ?SiteLanguage to retrieve resolves site language.
Following getters dispatches to ExtbaseRequestParameters methods and returning
null in case the request attribute is not set in the request:
getActionName(): ?string
getControllerName(): ?string
getControllerObjectName(): ?string
getControllerExtensionKey(): ?string
getControllerExtensionName(): ?string
getPluginName(): ?string
Feature: Introduce PSR-14 ModifyProfileTitlePlaceholderReplacementEvent in ProfileTitleProvider
Description
With recent changes a series of features has been implemented to
make the HTML title tag for person profile pages more flexible,
with a placeholder based FlexForm options and also allowing to
influence the default and the setting pageTitleFormat.
The used ProfileTitleProvider already looks into the format
string and provides the ability to replace placeholders, which
matches getters in the profile.
To provide even more flexibility, this change introduces a new
PSR-14 Event ModifyProfileTitlePlaceholderReplacementEvent,
which is dispatched for each placeholder enriched with quite a
handfull of use-full context information.
Following methods are available on the event:
getPluginControllerActionContext(): PluginControllerActionContextInterface
containing the request along with easy access methods to site,
siteLanguage and extbase plugin information and the plugin
settings.
getProfile(): Profile the current person profile to display.
getPlaceholder() the original/raw placeholder identifier.
getReplacement() the value to replace the placeholder with,
which may differ already if a earlier event listener changed
the value using setReplacement().
setReplacement(string $replacement): void to set the value
used to replace the placeholder.
This event allows project to implement custom placeholders and
the replacement without using old-school xclassing technique.
Feature: pageTitleFormat FlexForm option for person detail view
Description
It's now possible to define the format used to generate the HTML PageTitle for
the detail view of persons in the frontend, using the TYPO3 PageTitle API.
The default format used based on Profile extbase model data is:
To allow easier customization in project, a new FlexForm option pageTitleFormat
has been added to listanddetail plugin and as single new option for the detail
plugin, which uses TCA type=input combined with a ValuePicker to allow picking
from a list of pre-defined formats while still making it possible to define own
custom format directly on plugin usage.
The mapping from placeholder to extbase model is based on transforming the
placeholder to camelcase using first character after separators and prefix
it with get, and if the getter exists it is called to retrieve the value.
For example:
which calls Profile->getFirstName() to retrieve the replacement value from
the detail view profile.
The whole process contains some behaviour, which needs to be kept in mind:
Leading and trailing spaces are trimmed from each value(placeholder).
Multiple spaces are removed from the whole format string.
Leading and trailing spaces are trimmed from the whole format pattern, after
placeholder resolving has been processed.
Example for allowed characters as placeholder identifier:
Note that most of them has no handling for matching person profile getters, but
are use-full for advanced replacement using the experimental PSR-14 event.
Important: Basic bootstrap styling
Description
The default templating now supports basic bootstrap styling and is semantically
optimized to also not lack any major accessibility.
Breaking: Migrated extbase plugins from list_type to CType
Description
TYPO3 v13 deprecated the tt_content sub-type feature, only used for CType=list sub-typing also known
as list_type and mostly used based on old times for extbase based plugins. It has been possible since
the very beginning to register Extbase Plugins directly as CType instead of CType=list sub-type, which
has now done.
Technically this is a breaking change, and instances upgrading from 1.x version of the plugin needs to
update corresponding tt_content records in the database and eventually adopt addition, adjustments or
overrides requiring to use the correct CType.
Impact
The change relates to following plugins:
academicpersons_detail
academicpersons_list
academicpersons_listanddetail
academicpersons_selectedcontracts
academicpersons_selectedprofiles
Affected Installations
All installations using the above listed plugins prior V2.0.
Migration
A TYPO3 UpgradeWizard academicPersons_pluginUpgradeWizard is provided to migrate
plugins from CType=list to dedicated CTypes matching the new registration.
2.x Changes by type
This lists all changes to the Academic Persons extension of minor versions
grouped by their type.