.. container:: .. figure:: Resources/Public/Icons/Extension.svg :alt: Extension icon Extension icon .. rubric:: TYPO3 extension ``form_consent`` :name: typo3-extension-form_consent | |Coverage| |Maintainability| |Tests| |CGL| |Release| |License| | |Version| |Downloads| |Supported TYPO3 versions| |Extension stability| |Slack| :package: `Packagist `__ \| :hatched_chick: `TYPO3 extension repository `__ \| :floppy_disk: `Repository `__ \| :bug: `Issue tracker `__ -------------- An extension for TYPO3 CMS that adds double opt-in functionality to EXT:form. It allows the dynamic adaptation of the entire double opt-in process using various events. In addition, the extension integrates seamlessly into TYPO3, for example to delete expired consents in compliance with the GDPR. :rocket: Features ----------------- - Custom ``Consent`` form finisher for EXT:form - Stores all submitted form data as JSON in database - System-dependent hash-based validation system (using TYPO3’s HMAC functionality) - Plugin to approve or dismiss a consent - Possibility to `invoke finishers on consent approval or dismissal <#invoke-finishers-on-consent-approval-or-dismissal>`__ - Several `events <#events>`__ for better customization - Scheduler garbage collection task for expired consents - Dashboard widget for approved, non-approved and dismissed consents - Compatible with TYPO3 10.4 and 11.5 LTS :fire: Installation ------------------- .. code:: bash composer require eliashaeussler/typo3-form-consent Alternatively, you can download the extension via the `TYPO3 extension repository (TER) `__. Once installed, make sure to include the TypoScript setup at ``EXT:form_consent/Configuration/TypoScript`` in your root template. :zap: Usage ----------- Finisher ~~~~~~~~ A new finisher ``Consent`` is available in the backend form editor. It saves all submitted form data in the database and sends a corresponding mail to either approve or dismiss a given consent. The last inserted consent is populated with the finisher variable provider. It can be accessed as ``{Consent.lastInsertedConsent}`` in the ``.form.yaml`` configuration. Example: .. code:: yaml finishers: - options: table: tx_myextension_domain_model_mymodel mode: insert databaseColumnMappings: consent: value: '{Consent.lastInsertedConsent.uid}' identifier: SaveToDatabase Plugin ~~~~~~ A plugin is required for approval or dismiss of the consent. The associated page containing the plugin must then be specified in the finisher settings. :open_file_folder: Configuration -------------------------------- TypoScript ~~~~~~~~~~ The following TypoScript constants are available: +---------------------------+----------------+------------+-----------+ | TypoScript constant | Description | Required | Default | +===========================+================+============+===========+ | **``plugin.tx_formconsent | Default | – | ``0`` | | .persistence.storagePid`` | storage PID | | | | ** | for new | | | | | consents | | | +---------------------------+----------------+------------+-----------+ | **``plugin.tx_formconsent | Path to | – | – | | .view.templateRootPath``* | template root | | | | * | for consent | | | | | mail and | | | | | validation | | | | | plugin | | | +---------------------------+----------------+------------+-----------+ | **``plugin.tx_formconsent | Path to | – | – | | .view.partialRootPath``** | template | | | | | partials for | | | | | consent mail | | | | | and validation | | | | | plugin | | | +---------------------------+----------------+------------+-----------+ | **``plugin.tx_formconsent | Path to | – | – | | .view.layoutRootPath``** | template | | | | | layouts for | | | | | consent mail | | | | | and validation | | | | | plugin | | | +---------------------------+----------------+------------+-----------+ Finisher options ~~~~~~~~~~~~~~~~ The following options are available to the ``Consent`` finisher: .. table:: bulb: **Note:** Template paths that are configured via form finisher options are only applied to the appropriate form. They are merged with the default template paths configured via TypoScript. +-----------------------+------------------+-------------+------------+ | Finisher option | Description | Required | Default | +=======================+==================+=============+============+ | **``subject``** | Mail subject | – | ``Approve | | | | | your conse | | | | | nt`` | +-----------------------+------------------+-------------+------------+ | **``recipientAddress` | Recipient e-mail | :white_chec | – | | `** | address | k_mark: | | +-----------------------+------------------+-------------+------------+ | **``recipientName``** | Recipient name | – | – | +-----------------------+------------------+-------------+------------+ | **``senderAddress``** | Sender e-mail | – | *System | | | address | | default | | | | | sender | | | | | e-mail | | | | | address* | +-----------------------+------------------+-------------+------------+ | **``senderName``** | Sender name | – | *System | | | | | default | | | | | sender | | | | | name* | +-----------------------+------------------+-------------+------------+ | **``approvalPeriod``* | Approval period | :white_chec | ``86400`` | | * | | k_mark: | (1 day), | | | | | ``0`` = | | | | | unlimited | +-----------------------+------------------+-------------+------------+ | **``showDismissLink`` | Show dismiss | – | ``false`` | | ** | link in consent | | | | | mail | | | +-----------------------+------------------+-------------+------------+ | **``confirmationPid`` | Confirmation | :white_chec | – | | ** | page (contains | k_mark: | | | | plugin) | | | +-----------------------+------------------+-------------+------------+ | **``storagePid``** | Storage page | – | ``plugin.t | | | | | x_formcons | | | | | ent.persis | | | | | tence.stor | | | | | agePid`` | +-----------------------+------------------+-------------+------------+ | **``templateRootPaths | Additional paths | – | – | | ``** | to template root | | | +-----------------------+------------------+-------------+------------+ | **``partialRootPaths` | Additional paths | – | – | | `** | to template | | | | | partials | | | +-----------------------+------------------+-------------+------------+ | **``layoutRootPaths`` | Additional paths | – | – | | ** | to template | | | | | layouts | | | +-----------------------+------------------+-------------+------------+ Extension configuration ~~~~~~~~~~~~~~~~~~~~~~~ The following extension configuration options are available: +-------------------------+-----------------+-------------+-----------+ | Configuration key | Description | Required | Default | +=========================+=================+=============+===========+ | **``persistence.exclude | Form element | – | ``Honeypo | | dElements``** | types to be | | t`` | | | excluded from | | | | | persistence | | | | | (comma-separate | | | | | d | | | | | list) | | | +-------------------------+-----------------+-------------+-----------+ :writing_hand: Customization ---------------------------- The lifecycle of the entire consent process can be influenced in several ways. This leads to high flexibility in customization while maintaining high stability of the core components. Events ~~~~~~ PSR-14 events can be used to modify different areas in the consent process. The following events are available: - ```ApproveConsentEvent`` `__ - ```DismissConsentEvent`` `__ - ```GenerateHashEvent`` `__ - ```ModifyConsentEvent`` `__ - ```ModifyConsentMailEvent`` `__ Invoke finishers on consent approval or dismissal ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ After a user has given or revoked consent, it is often necessary to execute certain form finishers. For example, to send an admin email or redirect to a specific page. To achieve this, after the user gives or revokes consent, the originally completed form is resubmitted. During this resubmission of the form, the selected finishers can now be overwritten using the ``isConsentApproved()`` or ``isConsentDismissed()`` conditions in a form variant. Requirements ^^^^^^^^^^^^ The following requirements must be met for the form to be resubmitted: 1. Form variant at the root level of the form must exist 2. Form variant must redefine the finishers used 3. Conditions ``isConsentApproved()`` or ``isConsentDismissed()`` must exist in the variant Example ^^^^^^^ The following form variant is stored directly on the root level of the form definition (that is, your ``.form.yaml`` file). It specifies the form finishers to be executed in case of successful approval by the user. .. code:: yaml variants: - identifier: post-consent-approval-variant-1 condition: 'isConsentApproved()' finishers: - identifier: EmailToReceiver options: # ... - identifier: Redirect options: # ... In this example, an admin email would be sent after the consent has been given and a redirect to the configured confirmation page would take place. The same behavior can be achieved in case the user revokes his consent. The condition ``isConsentDismissed()`` must then be used instead. :construction: Migration ------------------------ 0.6.x → 0.7.0 ~~~~~~~~~~~~~ Global form settings ^^^^^^^^^^^^^^^^^^^^ Form settings for Frontend requests (``plugin.tx_form``) are no longer included globally. - Make sure to add the static TypoScript setup at ``EXT:form_consent/Configuration/TypoScript`` to your root template. 0.3.x → 0.4.0 ~~~~~~~~~~~~~ Generic consent state ^^^^^^^^^^^^^^^^^^^^^ The current state of form consents is now represented in a more generic way. - Database field ``tx_formconsent_domain_model_consent.approved`` was renamed to ``tx_formconsent_domain_model_consent.state``. - Upgrade wizard ```formConsentMigrateConsentState`` `__ needs to be executed. - Database field ``tx_formconsent_domain_model_consent.approval_date`` was renamed to ``tx_formconsent_domain_model_consent.update_date``. - Upgrade wizard ```formConsentMigrateConsentState`` `__ needs to be executed. - Note: The database column is now nullable. - ```$consent->setApproved()`` `__ does no longer accept any parameters. - Use ``$consent->setState()`` instead. - ```$consent->getApprovalDate()`` `__ was removed. - Use ``$consent->getUpdateDate()`` instead. - ```$consent->setApprovalDate()`` `__ was removed. - Use ``$consent->setUpdateDate()`` instead. Post-consent dismissal finishers ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Custom finishers can now be executed after consent was dismissed. - Event listener was renamed. - Change references to ```EliasHaeussler\Typo3FormConsent\Event\Listener\InvokeFinishersListener`` `__. - Adapt your service configuration, if needed. - Listener method was renamed. - Use ``onConsentApprove($event)`` instead of ``__invoke($event)``. - Event listener identifier ``formConsentInvokeFinishersOnApproveListener`` changed. - Change references to ``formConsentInvokeFinishersOnConsentApproveListener``. 0.2.x → 0.3.0 ~~~~~~~~~~~~~ Post-consent approval finishers ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Custom finishers can now be executed after consent was approved. - Database field ``tx_formconsent_domain_model_consent.original_request_parameters`` was added. - Manual migration required. - Database field should contain an JSON-encoded string of the parsed body sent with the original form submit request. - Database field ``tx_formconsent_domain_model_consent.original_content_element_uid`` was added. - Manual migration required. - Database field should contain the content element UID of the original form plugin. - Post-approval finishers can now be defined `as described above <#invoke-finishers-on-consent-approval>`__. - Manual migration required. - Create form variants and configure the post-approval finishers. .. _consent1-model: ```Consent`` `__ model ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Form values are now represented as an instance of ```JsonType`` `__. - Method ``getDataArray()`` was removed. - Use ``getData()->toArray()`` instead. - Return type of ``getData()`` was changed to ``JsonType|null``. - If you need the JSON-encoded string, use ``json_encode($consent->getData())`` instead. - Parameter ``$data`` of ``setData()`` was changed to ``JsonType|null``. - If you need to pass a JSON-encoded string, use ``$consent->setData(new JsonType($json))`` instead. - If you need to pass a JSON-decoded array, use ``$consent->setData(JsonType::fromArray($array))`` instead. Codebase ^^^^^^^^ - Minimum PHP version was raised to PHP 7.4. - Upgrade your codebase to support at least PHP 7.4. - Several classes were marked as ``final``. - If you still need to extend or override them, consider refactoring your code or `submit an issue `__. :technologist: Contributing --------------------------- Please have a look at ```CONTRIBUTING.md`` `__. :gem: Credits ------------- Icons made by `Google `__ from `www.flaticon.com `__. :star: License -------------- This project is licensed under `GNU General Public License 2.0 (or later) `__. |FOSSA Status| .. |Coverage| image:: https://codecov.io/gh/eliashaeussler/typo3-form-consent/branch/main/graph/badge.svg?token=PQ0101QE3S :target: https://codecov.io/gh/eliashaeussler/typo3-form-consent .. |Maintainability| image:: https://api.codeclimate.com/v1/badges/c88c6c0bbc31c02153ef/maintainability :target: https://codeclimate.com/github/eliashaeussler/typo3-form-consent/maintainability .. |Tests| image:: https://github.com/eliashaeussler/typo3-form-consent/actions/workflows/tests.yaml/badge.svg :target: https://github.com/eliashaeussler/typo3-form-consent/actions/workflows/tests.yaml .. |CGL| image:: https://github.com/eliashaeussler/typo3-form-consent/actions/workflows/cgl.yaml/badge.svg :target: https://github.com/eliashaeussler/typo3-form-consent/actions/workflows/cgl.yaml .. |Release| image:: https://github.com/eliashaeussler/typo3-form-consent/actions/workflows/release.yaml/badge.svg :target: https://github.com/eliashaeussler/typo3-form-consent/actions/workflows/release.yaml .. |License| image:: http://poser.pugx.org/eliashaeussler/typo3-form-consent/license :target: LICENSE.md .. |Version| image:: https://shields.io/endpoint?url=https://typo3-badges.dev/badge/form_consent/version/shields :target: https://extensions.typo3.org/extension/form_consent .. |Downloads| image:: https://shields.io/endpoint?url=https://typo3-badges.dev/badge/form_consent/downloads/shields :target: https://extensions.typo3.org/extension/form_consent .. |Supported TYPO3 versions| image:: https://shields.io/endpoint?url=https://typo3-badges.dev/badge/form_consent/typo3/shields :target: https://extensions.typo3.org/extension/form_consent .. |Extension stability| image:: https://shields.io/endpoint?url=https://typo3-badges.dev/badge/form_consent/stability/shields :target: https://extensions.typo3.org/extension/form_consent .. |Slack| image:: https://img.shields.io/badge/slack-%23ext--form__consent-4a154b?logo=slack :target: https://typo3.slack.com/archives/C03719PJJJD .. |FOSSA Status| image:: https://app.fossa.com/api/projects/git%2Bgithub.com%2Feliashaeussler%2Ftypo3-form-consent.svg?type=large :target: https://app.fossa.com/projects/git%2Bgithub.com%2Feliashaeussler%2Ftypo3-form-consent?ref=badge_large