.. index:: Configuration; Validations .. _configuration-validations: =================== Validation settings =================== Every field of :file:`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 * the editing frontend of `EXT:academic_persons_edit `__. This is why the file ships with :guilabel:`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 :yaml:`profile`, :yaml:`special`, :yaml:`contracts` and :yaml:`documentSections` maps - is documented on the :ref:`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. .. _configuration-validations-where: Where the flags are declared ============================ A profile, contract or contact field carries its flags as a list: .. code-block:: yaml profile: gender: section: information fieldType: select renderType: select validators: - required firstName: section: information fieldType: input renderType: text validators: - readonly - disabled A document section carries a map from field to flag list, with an expanded map for a rich text field: .. code-block:: yaml documentSections: publications: validators: title: - required year: - required - number link: - url description: editor: type: ckeditor limit: 500 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. .. _configuration-validations-flags: Available flags =============== Flag names are matched case insensitively. Anything not listed here is kept in the list and has no effect. .. list-table:: :header-rows: 1 * - Flag - Effect * - :yaml:`required` - The field must not be empty. Adds a *not empty* validation in the frontend and marks the field required in the backend. * - :yaml:`disabled` - The field must not be edited at all. See the note below. * - :yaml:`readonly` - The field is shown but cannot be written. * - :yaml:`frontendreadonly` - The field is shown but cannot be written in the editing frontend. The backend record editor keeps it editable. See the note below. * - :yaml:`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. * - :yaml:`url` - The value must be a valid URL; the field is rendered as a URL input. The TCA column is untouched. * - :yaml:`number` - The field is rendered as a number input and the TCA column becomes a ``number`` column. No additional server side validation is performed. * - :yaml:`date` - The field is rendered as a date input. The TCA column keeps its own ``datetime`` configuration. * - :yaml:`tel` - The field is rendered as a telephone input. No phone number format is enforced, and the TCA column is untouched. * - :yaml:`textarea` - The field is rendered as a multi line text control. The TCA column is untouched. * - :yaml:`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 :yaml:`required`, :yaml:`email` and :yaml:`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:: :yaml:`disabled` and :yaml:`readonly` both **cancel** :yaml:`required`. A field that cannot be edited cannot be demanded from the editor, so combining them has no effect - the field is simply locked. :yaml:`disabled` additionally implies :yaml:`readonly`. FormEngine has no equivalent of the HTML :html:`disabled` attribute, so a disabled field is presented as read only in the backend. :yaml:`frontendreadonly` locks the field for profile owners only. It cancels :yaml:`required` in the editing frontend, because an owner cannot correct a value they cannot edit, while the backend record editor still requires the field. Listed together with :yaml:`readonly` or :yaml:`disabled`, the field is locked in the backend as well. .. _configuration-validations-limits: 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: .. code-block:: yaml profile: miscellaneous: section: aboutme fieldType: textarea renderType: ckeditor characterLimit: 1000 validators: - html documentSections: publications: validators: description: editor: type: ckeditor limit: 500 The limit is effective only on a :yaml:`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. .. _configuration-validations-defaults: Fields that are locked by default ================================= The three name fields ship as :yaml:`readonly` and :yaml:`disabled`: .. code-block:: yaml profile: firstName: validators: - readonly - disabled middleName: validators: - readonly - disabled lastName: validators: - readonly - 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: * :guilabel:`First name`, :guilabel:`Middle name` and :guilabel:`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. Where some profiles are synchronised and others are created by editors, lock the names on the synchronised ones only, with :ref:`managed fields `. .. _configuration-validations-backend: Effects in the TYPO3 backend ============================ The flags of every section are merged into the TCA of the matching table, so a field locked with :yaml:`readonly` or :yaml:`disabled` is read only in the record editor and a required field is marked as such. :yaml:`frontendreadonly` is the one lock that does not reach the TCA. The sections map to these tables: .. list-table:: :header-rows: 1 * - Section - Table * - :yaml:`profile` fields and :yaml:`special.skipSync` - :sql:`tx_academicpersons_domain_model_profile` * - :yaml:`contracts.fields` - :sql:`tx_academicpersons_domain_model_contract` * - :yaml:`contracts.contactSections.emailAddresses` - :sql:`tx_academicpersons_domain_model_email` * - :yaml:`contracts.contactSections.phoneNumbers` - :sql:`tx_academicpersons_domain_model_phone_number` * - :yaml:`contracts.contactSections.physicalAddresses` - :sql:`tx_academicpersons_domain_model_address` * - every other :yaml:`documentSections` entry - :sql:`tx_academicpersons_domain_model_profile_information`, as ``columnsOverrides`` of the record type of that section :yaml:`special.hidden` is the one exception. Its flags decide whether owners may show or hide their profile in the profile editor, and an installation sets them when editors are meant to decide instead, so they never make the backend checkbox :guilabel:`Visible` read only. The property name is translated to the database column automatically: :yaml:`firstName` addresses :sql:`first_name`; a field that names a :yaml:`fieldName` addresses that column instead. Every column takes the flags of one field. When two fields of the profile, of the contracts or of one contact section, a profile field and a special field, or two special fields name the same column, the column gets the flags of the field applied later, and the flags of the earlier field are lost. The profile fields are applied in section order, then the special fields. A site package that adds a second profile field with :yaml:`fieldName: teaching_area` decides whether the teaching area is required in the backend, whatever the shipped :yaml:`teachingArea` field says. The same holds for two timeline sections of one record type. The console command :bash:`academic:upgrade:check` of :guilabel:`EXT:academic_base` and the status report under :guilabel:`Academic Persons` name such a column as a warning :bash:`shared-column`, with the table, the column and the fields. Give each field a column of its own. Two cases are not named. Two fields with the same property name, and a special field named like the property of a profile field, are combined into one before the flags are applied, so the later one replaces the earlier and no column is shared. And a project field is checked against the TCA as it is before the flags are applied, the warning against the TCA after them. Where another field of the same column turns it into an :yaml:`email` or :yaml:`number` column, the project field can be refused by one and accepted by the other. 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 :yaml:`fieldType` and :yaml:`renderType` of a field never reach the TCA - the column keeps the type its TCA file declares. The flags are applied once the TCA is compiled, after every :file:`Configuration/TCA/Overrides` file. A site package that replaces one of these columns keeps what the settings say about it, and a ``required`` or ``readOnly`` its TCA override sets on a configured column is replaced by the settings. State a lock in the settings instead. A value that has to differ in the backend only is set by a listener of :php:`TYPO3\CMS\Core\Configuration\Event\AfterTcaCompilationEvent` ordered after ``academic-persons/apply-settings-to-tca``, see :ref:`breaking-settings-apply-after-tca-overrides`. A project field, declared with :yaml:`custom: true`, gets its flags only when its column may be used: the column is in the profile TCA, it is no system column and no column of the shipped profile model, its type is ``input``, ``text``, ``email``, ``link``, ``number`` or ``check``, and the renderer and validators of the field fit that type. Any other column gets nothing and raises a deprecation notice naming the field, the column and the reason. The backend and the install tool keep working. A regular field whose column the TCA does not have is left out without a notice. .. _configuration-validations-frontend: Effects in the editing frontend =============================== When `EXT:academic_persons_edit `__ is installed, the same flags are used three times: #. The control is rendered with the matching :html:`disabled`, :html:`readonly` and :html:`required` attributes and the input type the flags select. #. :yaml:`required`, :yaml:`email` and :yaml:`url` add server side validation of the submitted data, and a character limit is enforced. #. A :yaml:`disabled`, :yaml:`readonly` or :yaml:`frontendreadonly` 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. A submitted value for it is ignored and the other values of the request are stored, so a contract or contact with a locked field is saved as usual. The same holds for a field the synchronisation owns on the record, see :ref:`configuration-managed-fields-editor`. .. note:: A record an owner creates in the editing frontend, such as a new contract, is stored without a value for a field locked with :yaml:`frontendreadonly`, even when the backend requires it. The backend record editor asks for it the next time the record is saved there. 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. .. _configuration-validations-override: Overriding the flags ==================== The flags live in the map that carries the field, so changing them means overriding that map - see :ref:`Overriding the file `. The files are merged recursively, so an override names the field and its :yaml:`validators` list and nothing else. The list itself is replaced as a whole, which is how a flag is dropped. Example - making the profile names editable again, in the backend and in the editing frontend. The three name fields ship with :yaml:`readonly` and :yaml:`disabled`; an empty list clears them, and every other key of those fields, of the other fields and of the layout stays as shipped: .. code-block:: yaml profile: firstName: validators: [] middleName: validators: [] lastName: validators: [] A flag is added the same way, by restating the list with it: .. code-block:: yaml profile: title: validators: - required Example - keeping the synchronised profile names away from their owners while backend editors can still correct them. :yaml:`frontendreadonly` replaces both shipped flags, and :yaml:`required` keeps the first and last name required in the backend record editor, as the TCA of the profile table declares them: .. code-block:: yaml profile: firstName: validators: - required - frontendreadonly middleName: validators: - frontendreadonly lastName: validators: - required - frontendreadonly .. note:: Because both editing contexts read the same configuration, an override changes them together, with :yaml:`frontendreadonly` as the one exception. 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. .. _configuration-validations-migration: Migrating a pre-3.0 override ============================ Before 3.0 the file had two top-level maps, :yaml:`validations` with one flag list per record type and :yaml:`profileInformationsTypes` with the seven timeline entry types, and the manual told integrators to restate the complete :yaml:`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. The console command prints the replacement: .. code-block:: bash vendor/bin/typo3 academic:persons:settings:migrate 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 :yaml:`profile`, :yaml:`special`, :yaml:`contracts` and :yaml:`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 :guilabel:`Status` report lists the same packages under :guilabel:`Academic Persons` as a warning. The printed maps restate everything the runtime produces. Once they replace the legacy keys, :bash:`vendor/bin/typo3 academic:persons:settings:migrate --delta` reduces the file to the entries that differ from what the packages loaded before it configure - see :ref:`Overriding the file `. How the legacy keys map: .. list-table:: :header-rows: 1 * - Legacy key - Mapped onto * - :yaml:`validations.profile.` - :yaml:`profile..validators` * - :yaml:`validations.contract.` - :yaml:`contracts.fields..validators` * - :yaml:`validations.emailAddress.{email, type}` - :yaml:`contracts.contactSections.emailAddresses.fields.{emailAddress, emailAddressType}.validators` * - :yaml:`validations.phoneNumber.{phoneNumber, type}` - :yaml:`contracts.contactSections.phoneNumbers.fields.{phoneNumber, phoneNumberType}.validators` * - :yaml:`validations.physicalAddress.` - :yaml:`contracts.contactSections.physicalAddresses.fields..validators`, :yaml:`type` onto :yaml:`physicalAddressType` * - :yaml:`validations.profileInformation.` - :yaml:`documentSections.
.validators.` of every timeline section; :yaml:`yearStart`, :yaml:`yearEnd` and :yaml:`bodytext` onto :yaml:`from`, :yaml:`to` and :yaml:`description` * - :yaml:`profileInformationsTypes.
` - the :yaml:`label` of :yaml:`documentSections.
`; its :yaml:`type` and :yaml:`fieldName` are reported, not applied A field is matched by its key or by the property it names, so :yaml:`emailAddress.email` reaches the :yaml:`emailAddress` field whose :yaml:`propertyName` is :yaml:`email`. A legacy set decides the six flags the old shape knew - :yaml:`required`, :yaml:`readonly`, :yaml:`frontendreadonly` (added in 2.4), :yaml:`disabled`, :yaml:`email` and :yaml:`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 - :yaml:`url`, :yaml:`date`, :yaml:`tel`, :yaml:`textarea`, :yaml:`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 :yaml:`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 :yaml:`type` and :yaml:`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 :ref:`configuration-sections-documents` and the :ref:`upgrade-from-2-4-to-3-0` page describe.