Skip to content

Commit

Permalink
📝 [#1068] -- document formio service and utilities
Browse files Browse the repository at this point in the history
  • Loading branch information
sergei-maertens committed Nov 24, 2022
1 parent fdf0779 commit 1dc3088
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 12 deletions.
134 changes: 134 additions & 0 deletions docs/developers/backend/core/formio.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
.. _developers_backend_core_formio:

===========================
Core: formio.io integration
===========================

Open Forms uses `form.io`_ under the hood to build and render forms, and then adds its
own layers on top of that, such as:

* implementing multi-step forms where every step is a form.io definition
* evaluating backend logic using data from earlier steps
* dynamically adapting form.io definitions as needed

This means that we process the form.io datastructures in the backend, using Python. All
the code for this is organized in the ``openforms.formio`` package.

.. versionchanged:: 2.1.0

``openforms.custom_field_types`` was refactored into the ``openforms.formio`` package,
and all of the separate registries (formatters, normalizers...) were merged into a
single compoment registry.

Supported features
==================

.. currentmodule:: openforms.formio.service

Formatting values for display
-----------------------------

Value formatting is done for displaying form submission data summaries, rendering
confirmation PDFs and emails... It is aware if it's in a HTML context or not. It is
heavily used in the :ref:`renderers <developers_backend_core_submission_renderer>`.

Whenever a component plugin is registered, the
:attr:`openforms.formio.registry.BasePlugin.formatter` class attribute **must** be
specified.

.. autofunction:: format_value
:noindex:


Normalizing input data
----------------------

Data for a component can be sourced from external systems that employ different
formatting rules compared to what form.io expects. Normalizing this data helps to be
able to make proper comparisons at different stages in the submission life-cycle.

You can opt-in to this by configuring :attr:`openforms.formio.registry.BasePlugin.normalizer`.

.. autofunction:: normalize_value_for_component
:noindex:


Dynamically modifying component configuration
---------------------------------------------

Certain component types require on-the-fly configuration rewriting, such as applying
global configuration options that may change independently from when the form is
actually being designed.

Dynamic rewriting is enabled by implementing
:meth:`openforms.formio.registry.BasePlugin.mutate_config_dynamically`. It receives the
current :class:`openforms.submissions.models.Submission` instance and a mapping of all
the variable names and values at the time.

.. autofunction:: get_dynamic_configuration
:noindex:

For an example of a custom field type, see :class:`openforms.formio.components.custom.Date`.

Finally, the resulting resolved component definitions are evaluated with the template
engine where variable values are evaluated for compoment labels, descriptions... and
configuration based on the HTTP request is performed (see
:func:`openforms.formmio.service.rewrite_formio_components_for_request`).

Reference
=========

Public API - ``openforms.formio.service``
-----------------------------------------

.. automodule:: openforms.formio.service
:members:

.. autoclass:: openforms.formio.registry.BasePlugin
:members:
:exclude-members: verbose_name

Extending
---------

Using our :ref:`usual extension pattern <developers_extending>` you can register your
own types.

Extensions should inherit from :class:`openforms.formio.registry.BasePlugin` or
implement the same protocol(s) and be registered with their form.io type:

.. code-block:: python
from openforms.formio.formatters.formio import DefaultFormatter
from openforms.formio.registry import BasePlugin
@register("myCustomType")
class MyComponent(BasePlugin):
formatter = DefaultFormatter
You can find some examples in ``openforms.formio.components.custom``.


Private API
-----------

Module: ``openforms.formio.dynamic_config``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. automodule:: openforms.formio.dynamic_config
:members:

Module: ``openforms.formio.formatters``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. automodule:: openforms.formio.formatters
:members:

Module: ``openforms.formio.rendering``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. automodule:: openforms.formio.rendering
:members:

.. _form.io: https://www.form.io/
1 change: 1 addition & 0 deletions docs/developers/backend/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Core
:maxdepth: 1

core/index
core/formio
core/submissions
core/submission-renderer
core/variables
Expand Down
12 changes: 3 additions & 9 deletions docs/developers/backend/modules/index.rst
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
.. _developers_backend_modules_index:

=======
Modules
=======
=====================
Modules: introduction
=====================

Open Forms groups functionalities in *modules*. Within a module there are often one
or more plugins available for a vendor-specific implementation of the high-level
functionality. Modules can usually be :ref:`extended <developers_extending>` with custom
plugins.

.. todo:: This documentation is incomplete

.. toctree::
:caption: Contents
:maxdepth: 2

dmn
2 changes: 0 additions & 2 deletions src/openforms/formio/dynamic_config/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
"""
Implement component-specific dynamic configuration based on (submission) state.
This will eventually replace ``openforms.forms.custom_field_types``.
"""
from typing import Optional

Expand Down
6 changes: 5 additions & 1 deletion src/openforms/formio/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@


def format_value(component: Component, value: Any, as_html: bool = False):
"""
Format a submitted value in a way that is most appropriate for the component type.
"""
return register.format(component, value, as_html=as_html)


Expand All @@ -59,7 +62,8 @@ def get_dynamic_configuration(
"""
Given a static Formio configuration, apply the hooks to dynamically transform this.
The configuration is modified in the context of the provided :arg:`submission`.
The configuration is modified in the context of the provided ``submission``
parameter.
"""
rewrite_formio_components(config_wrapper, submission=submission, data=data)

Expand Down

0 comments on commit 1dc3088

Please sign in to comment.