Item and list partials
The profile item and the profile list are built from small partials. An integrator changes one aspect of them by putting one file of a few lines into the configured partial root path, instead of copying a whole template and maintaining it against every later correction.
The entry points stay Profile/,
Profile/, Profile/ and
Profile/. The first two delegate to the
partials below, with unchanged arguments. The page navigation is a leaf that
gained a class and the argument active. The letter navigation
gained both as well, and alphabet next to them; a project copy
of either keeps working and renders the links it rendered before - see
the letter navigation and
what the navigation links carry below.
An existing copy of the item or the list body keeps working - and stops profiting from the partials below, because a copy does not render them.
The item
Partials/ is rendered by the list, list-and-detail,
card, selected-profiles and selected-contracts elements, and by the contacts
element of EXT:. It renders:
| Partial | Renders | Class |
|---|---|---|
Profile/ | The address of the detail view, as text | none, it renders no element |
Profile/ | The academic title and the names, as text | ace-, next to ace- on the heading |
Profile/ | The contract block, through Profile/ | none, the list element belongs to that partial |
Profile/ | The image, through the shared partial of EXT: | ace-, and ace- on its
<picture>
, both from
the shared partial |
Which contracts an item renders
Profile/ renders the one contract a caller passes as
contract - the selected contracts element and
EXT: do. Otherwise it renders the contracts the
element's options select (see Which contracts a profile shows), through
the ViewHelper
<persons:. The detail view's
Profile/ and
Profile/ use it with their block of
Settings. instead:
<html
data-namespace-typo3-fluid="true"
xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
xmlns:persons="http://typo3.org/ns/FGTCLB/AcademicPersons/ViewHelpers"
>
<f:if condition="{contract}">
<f:then>
<p class="my-contract">{contract.position}</p>
</f:then>
<f:else>
<f:for each="{persons:contracts(profile: profile, settings: settings)}" as="contract">
<p class="my-contract">{contract.position}</p>
</f:for>
</f:else>
</f:if>
</html>
Keep the contract branch in a copy: without it, the selected contracts element
and EXT: would show the profile's contracts instead of
the chosen one.
<f:for each="{persons:contracts(profile: profile, detailBlock: publicProfile.details.contact)}" as="contract">
...
</f:for>
settings are the plugin settings, detail a block of
public; a passed block wins. Without either, every contract is
returned. The ViewHelper also limits the page cache lifetime while a validity
option applies, which is why a template should call it rather than filter
{profile. itself: a copy that loops over
{profile. keeps working, and ignores the options.
The name of a function type
Profile/ renders the name of a function
type for a profile's gender: the female name for ms, the male name for
mr, and the general name otherwise or where the gendered name is empty. It
takes the arguments function and gender. The position line of the
detail view renders it, and so does Profile/ for
the field contracts. of the fields to show. For that,
Profile/ hands the profile's gender to the field
partial as gender. A project copy of Profile/ that
does not pass it shows the general name.
Two of them render text, not markup
Profile/ and Profile/ produce
a value. The item hands it to Profile/ or
Profile/, which put it inside their heading element -
and escape it there, once, on the way out.
An override of either therefore renders the profile values raw:
<html
data-namespace-typo3-fluid="true"
xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
>
<f:format.raw>{profile.lastName}</f:format.raw>, <f:format.raw>{profile.firstName}</f:format.raw>
</html>
Without
<f: the value is escaped twice, and a name such as
O'Neill reaches the browser as the markup O&#039;Neill, which a visitor
reads as O'Neill. Markup written in one of these two partials arrives as
literal text for the same reason - the heading element is what the header
partials own.
Warning
These two partials render unescaped text, so they are only safe where
something else escapes them: as an argument of a partial that writes them
into an element, which is what Profile/ does. Never render
one of them in an output position of a template -
{f: written into markup would emit the stored profile
values unescaped.
Every other partial on this page renders markup and is written as any Fluid partial is.
Passing the detail page
Profile/ builds the address from
plugin., or stays on the current page for the
list-and-detail element, which shows the detail view itself. While
plugin. is none, it renders nothing for every
other element and the name is shown as text, see
Names without a detail link. An override of the partial follows that
setting only when it checks it as well.
A template that renders an item without those plugin settings passes the page
instead. A passed page wins over both branches, the list-and-detail one
included, and links whatever plugin. says:
<f:render
partial="Profile/Item"
arguments="{
profile: profile,
settings: settings,
data: data,
detailPid: 42
}"
/>
The list
Partials/ renders:
| Partial | Renders | Class |
|---|---|---|
Profile/ | Nothing | none, it renders no element |
Profile/ | The heading above one group of a grouped list | ace-, next to ace- on the heading |
Profile/ | The profiles, in the view mode of the element - once per group of a grouped list | depends on the mode |
Profile/ | The page navigation | ace-, on the
<nav>
|
Profile/ | The text shown when nothing was found | ace- |
Profile/ is the sixth, and the one
Item does not render: the list template
Templates/ renders it above the list body, when
settings. is switched on and no profiles are
selected by hand - a manual selection ignores the letter filter. Its
<nav>
carries ace-.
The list template passes it alphabet next to demand: every
letter from a to z, mapped to whether the list holds a profile under it
(see Which letters lead somewhere). The partial renders:
- A letter with profiles as a link
a.in a plainace- link li..ace- list- item - A letter without profiles as
li.holding aace- list- item. disabled span., no link, with aace- link visually-"no profiles" for assistive technology.hidden - The selected letter as
li., itsace- list- item. active ace-withlink aria-. It is a link back to the list without a letter, with acurrent="page" visually-"show all profiles", only when the reset setting is on, and ahidden span.otherwise.ace- link - A-Z as
li.withace- list- item. active aria-on its link while no letter is selected.current="page"
The
<nav>
is named by the label list.. A
list template of a project that renders this partial without
alphabet gets every letter as a link, as before.
The options of the visitor filters
While the content element offers a filter for visitors, the list action assigns its options to
filter: filter holds the function types,
filter the organisational units, each ordered by
name in the language of the page. Each is known by its uid in the default
language, which is the value a link carries. A filter the element does not
offer has no key, and a manual selection gets no options at all. The active
values are demand. and demand.,
0 for none.
The shipped list template renders them in Profile/,
above the letter navigation: one select per filter with options, "all" first,
the active filter selected, and a submit button. The form posts to the
filter
action, which redirects to the URL of the filtered list, see
Filtered lists. It keeps the view mode and the
letter, and contains no JavaScript. The partial takes filter,
demand, active and content, which keeps the ids of
a list and a list-and-detail element on one page apart. Two elements of the same
plugin on one page share their arguments, as their page links do, so a
submission is checked against the options of the element that renders first. A
project copy of Profile/ that does not render the partial shows
no form.
A project that prefers links to a form links a filtered list with the action link view helper, which adds the cHash the cached list needs, and keeps the other choices of the visitor:
<html
xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
xmlns:persons="http://typo3.org/ns/FGTCLB/AcademicPersons/ViewHelpers"
data-namespace-typo3-fluid="true"
>
<f:for each="{filterOptions.functionTypes}" as="functionType">
<f:link.action
arguments="{demand: '{persons:listArguments(arguments: activeListArguments, overrides: {functionTypeFilter: functionType.uid}, remove: \'currentPage\')}'}"
>{functionType.functionName}</f:link.action>
</f:for>
Profile/, the grid the view mode list renders,
carries the Bootstrap row and column classes, so overriding it changes the grid
of all four elements of this extension at once. It takes either profiles
or contracts: the selected contracts element passes the latter, because it
shows one item per selected contract, and each of those items renders that one
contract rather than every contract of its profile.
Profile/ is rendered by all four as well. The
selected contracts element passes the label about contracts to it through its
key argument; every other caller takes the default.
Note
The contacts element of EXT: is not one of the
four. It renders Profile/ through a partial of its own,
Contacts/, inside a grid of its own, and it has no empty
state at all - a page without contacts renders nothing. Overriding
Profile/ therefore does not change how its contacts
are arranged; overriding Profile/ or one of the item
partials does change how each of them looks, and a project that wants a
card of its own for the contacts only overrides Contacts/
of that extension instead.
The grid is an ace- and the empty state an ace- in all four
elements. What tells the elements apart is their outermost element,
academic-, academic-, academic- or
academic-.
The result count is an empty hook
Profile/ renders nothing on purpose - printing a
number by default would change every list that exists. It receives the number of
profiles rendered below it as count, the number the list found as total -
the same one unless the list paginates - those profiles as profiles, and the
paginator of a paginated list for its page number and page count:
<html
data-namespace-typo3-fluid="true"
xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
>
<p class="ace-count">{count} of {total}</p>
</html>
total is counted in Item rather than read from the paginator
on purpose: get is protected on every core version this
extension supports, so Fluid cannot reach it and
{paginator. renders an empty string.
View modes
The list, list-and-detail, selected profiles and selected contracts elements
render their profiles through the partial of their view mode, Profile/ - the mode with
an upper case first letter, Contact for contact. The
action assigns the mode as view and the partial name
as view; the list renders the partial from
Profile/, the selected profiles and contracts from
their templates.
| Partial | Renders | Class |
|---|---|---|
Profile/ | The mode list, labelled Tiles: the grid of
Profile/ | those of the grid |
Profile/ | The mode table: a header row, then one row per profile - per
contract in the selected contracts element - with one cell per
entry of settings. | ace-, inside an ace- |
Profile/ | One cell of the table, by its column | none |
Profile/ | The switch between the allowed modes, only when the content element offers it | ace-, on the
<nav>
|
Every mode partial receives the same arguments: profiles, or contracts in
the selected contracts element, settings, data and grouped from a
grouped list. The grid is an ace- and the table an ace-, so a
stylesheet tells the two apart by their own classes.
The switch renders one link per allowed mode - nothing while fewer than two
modes are allowed - with
rel="nofollow"
, and marks the active one with
aria-. Its
<nav>
is named by the label
list., a mode by list. of this extension -
or by its name, for a mode without a label. In the list elements a link keeps
active and changes only the mode; the link to the default mode
carries no mode, and is the page itself when the visitor chose nothing else.
A project copy of Profile/,
Templates/ or
Templates/ keeps rendering the grid
whatever the mode; a copy of Templates/ has no switch.
Adding a view mode
A view mode of your own needs no template override. Taking the mode contact:
- Provide the partial
Profile/in a partial path of the plugin, such as that of your site package.View Mode/ Contact. html - Allow the mode: the site setting
plugin.becomestx_ academicpersons. view Mode. allowed list,table,contact, see View modes. -
Offer it to the editor as an item of View Mode Default, in page TSconfig, for each element that shall have it:
EXT:my_sitepackage/Configuration/page.tsconfigTCEFORM.tt_content.pi_flexform { academicpersons_list.sDEF.settings\.viewMode\.default.addItems.contact = Contact cards academicpersons_listanddetail.sDEF.settings\.viewMode\.default.addItems.contact = Contact cards }Copied!
The switch offers the mode as soon as it is allowed; label it with
list. in a language override of this extension. A mode that is
allowed without a partial fails with Fluid's error for a missing partial. Its
links carry it as a query argument until it is added to the route enhancer, see
a view mode of your own in the URL.
To add a column to the table, override Profile/,
render your column there and keep the shipped ones; a column the partial does
not know renders an empty cell. Its header is the label list.,
or the column's name without one.
Contacts for pages resolves the partials separately
EXT: renders Profile/ through partial root
paths of its own. An override registered for the persons plugins alone does not
reach it, so register the same path twice:
plugin.tx_academicpersons.view.partialRootPath = EXT:my_sitepackage/Resources/Private/Extensions/academic_persons/Partials/
plugin.tx_academiccontacts4pages.view.partialRootPath = EXT:my_sitepackage/Resources/Private/Extensions/academic_persons/Partials/
That has always been true for a copy of the item; it is worth repeating here because a small override makes it easy to forget one of the two lines.