diff --git a/src/openforms/conf/base.py b/src/openforms/conf/base.py
index cb0aebf0d1..67665c1cea 100644
--- a/src/openforms/conf/base.py
+++ b/src/openforms/conf/base.py
@@ -224,6 +224,7 @@
"openforms.registrations.contrib.objects_api",
"openforms.registrations.contrib.microsoft_graph.apps.MicrosoftGraphApp",
"openforms.registrations.contrib.camunda.apps.CamundaApp",
+ "openforms.registrations.contrib.new_plugin",
"openforms.prefill",
"openforms.prefill.contrib.demo.apps.DemoApp",
"openforms.prefill.contrib.kvk.apps.KVKPrefillApp",
diff --git a/src/openforms/js/components/admin/form_design/RegistrationFields.stories.js b/src/openforms/js/components/admin/form_design/RegistrationFields.stories.js
index a35a3378e5..986af24c15 100644
--- a/src/openforms/js/components/admin/form_design/RegistrationFields.stories.js
+++ b/src/openforms/js/components/admin/form_design/RegistrationFields.stories.js
@@ -390,6 +390,21 @@ export default {
type: 'object',
},
},
+ {
+ id: 'json',
+ label: 'JSON',
+ schema: {
+ properties: {
+ // TODO-4098: add actual relevant properties
+ extraLine: {
+ minLength: 1,
+ title: 'Extra print statement',
+ type: 'string',
+ },
+ },
+ type: 'object',
+ },
+ },
],
configuredBackends: [],
onChange: fn(),
@@ -663,6 +678,12 @@ export const ConfiguredBackends = {
],
},
},
+ {
+ key: 'backend11',
+ name: 'JSON',
+ backend: 'json',
+ options: {extraLine: 'We are checking.'},
+ },
],
validationErrors: [
['form.registrationBackends.1.options.zgwApiGroup', 'You sure about this?'],
@@ -910,3 +931,25 @@ export const STUFZDS = {
await userEvent.click(canvas.getByRole('button', {name: 'Opties instellen'}));
},
};
+
+
+export const JSON = {
+ args: {
+ configuredBackends: [
+ {
+ key: 'backend11',
+ name: 'JSON',
+ backend: 'json',
+ options: {
+ extraLine: 'We are checking.',
+ },
+ },
+ ],
+ },
+
+ play: async ({canvasElement}) => {
+ const canvas = within(canvasElement);
+
+ await userEvent.click(canvas.getByRole('button', {name: 'Opties instellen'}));
+ },
+};
diff --git a/src/openforms/js/components/admin/form_design/registrations/index.js b/src/openforms/js/components/admin/form_design/registrations/index.js
index c5de3f8053..4c549674ee 100644
--- a/src/openforms/js/components/admin/form_design/registrations/index.js
+++ b/src/openforms/js/components/admin/form_design/registrations/index.js
@@ -1,6 +1,7 @@
import CamundaOptionsForm from './camunda';
import DemoOptionsForm from './demo';
import EmailOptionsForm from './email';
+import JSONOptionsForm from './json';
import MSGraphOptionsForm from './ms_graph';
import ObjectsApiOptionsForm from './objectsapi/ObjectsApiOptionsForm';
import ObjectsApiSummaryHandler from './objectsapi/ObjectsApiSummaryHandler';
@@ -46,6 +47,7 @@ export const BACKEND_OPTIONS_FORMS = {
form: StufZDSOptionsForm,
},
'microsoft-graph': {form: MSGraphOptionsForm},
+ 'json': {form: JSONOptionsForm},
// demo plugins
demo: {form: DemoOptionsForm},
'failing-demo': {form: DemoOptionsForm},
diff --git a/src/openforms/js/components/admin/form_design/registrations/json/JSONOptionsForm.js b/src/openforms/js/components/admin/form_design/registrations/json/JSONOptionsForm.js
new file mode 100644
index 0000000000..1dde8ff9f0
--- /dev/null
+++ b/src/openforms/js/components/admin/form_design/registrations/json/JSONOptionsForm.js
@@ -0,0 +1,74 @@
+import {useField} from 'formik';
+import PropTypes from 'prop-types';
+import React, {useContext} from 'react';
+import {FormattedMessage} from 'react-intl';
+
+import Field from 'components/admin/forms/Field';
+import Fieldset from 'components/admin/forms/Fieldset';
+import FormRow from 'components/admin/forms/FormRow';
+import {TextInput} from 'components/admin/forms/Inputs';
+import ModalOptionsConfiguration from 'components/admin/forms/ModalOptionsConfiguration';
+import {
+ ValidationErrorContext,
+ ValidationErrorsProvider,
+ filterErrors,
+} from 'components/admin/forms/ValidationErrors';
+
+
+const ExtraLine = () => {
+ const [fieldProps] = useField('extraLine');
+ return (
+
+
+ }
+ >
+
+
+
+ );
+};
+
+
+const JSONOptionsForm = ({name, label, formData, onChange}) => {
+ const validationErrors = useContext(ValidationErrorContext);
+ const relevantErrors = filterErrors(name, validationErrors);
+ return (
+
+ }
+ initialFormData={{extraLine: '', ...formData}}
+ onSubmit={values => onChange({formData: values})}
+ modalSize="small"
+ >
+
+
+
+
+ );
+};
+
+JSONOptionsForm.propTypes = {
+ name: PropTypes.string.isRequired,
+ label: PropTypes.node.isRequired,
+ formData: PropTypes.shape({
+ extraLine: PropTypes.string,
+ }),
+ onChange: PropTypes.func.isRequired,
+};
+
+export default JSONOptionsForm;
diff --git a/src/openforms/js/components/admin/form_design/registrations/json/index.js b/src/openforms/js/components/admin/form_design/registrations/json/index.js
new file mode 100644
index 0000000000..97a74031a6
--- /dev/null
+++ b/src/openforms/js/components/admin/form_design/registrations/json/index.js
@@ -0,0 +1,3 @@
+import JSONOptionsForm from './JSONOptionsForm';
+
+export default JSONOptionsForm;
diff --git a/src/openforms/registrations/contrib/new_plugin/__init__.py b/src/openforms/registrations/contrib/new_plugin/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/src/openforms/registrations/contrib/new_plugin/apps.py b/src/openforms/registrations/contrib/new_plugin/apps.py
new file mode 100644
index 0000000000..70b007c941
--- /dev/null
+++ b/src/openforms/registrations/contrib/new_plugin/apps.py
@@ -0,0 +1,12 @@
+from django.apps import AppConfig
+from django.utils.translation import gettext_lazy as _
+
+
+# TODO-4908: come up with good name: 'JSON' or '(Form) Variables API'?
+class NewPluginConfig(AppConfig):
+ name = "openforms.registrations.contrib.new_plugin"
+ label = "registrations_new_plugin"
+ verbose_name = _("New plugin")
+
+ def ready(self):
+ from . import plugin # noqa
diff --git a/src/openforms/registrations/contrib/new_plugin/config.py b/src/openforms/registrations/contrib/new_plugin/config.py
new file mode 100644
index 0000000000..48d26487fa
--- /dev/null
+++ b/src/openforms/registrations/contrib/new_plugin/config.py
@@ -0,0 +1,33 @@
+from django.utils.translation import gettext_lazy as _
+
+from rest_framework import serializers
+from zgw_consumers.models import Service
+
+from openforms.api.fields import PrimaryKeyRelatedAsChoicesField
+from openforms.formio.api.fields import FormioVariableKeyField
+from openforms.utils.mixins import JsonSchemaSerializerMixin
+
+
+class NewPluginOptionsSerializer(JsonSchemaSerializerMixin, serializers.Serializer):
+ # TODO-4098: is service enough, or do we need an API group like the ObjectsAPI?
+ service = PrimaryKeyRelatedAsChoicesField(
+ queryset=Service.objects.all(),
+ label=_("Service"),
+ help_text=_("Which service to use."),
+ )
+ # TODO-4098: show the complete API endpoint as a (tooltip) hint after user entry?
+ api_endpoint = serializers.CharField(
+ max_length=255,
+ label=_("Relative API endpoint"),
+ help_text=_("The API endpoint to send the data to (relative to the service API root)."),
+ )
+ # TODO-4098: should be linked to the checkboxes in the form variable table
+ # TODO-4098: is it possible to have a choices field of variable keys, where you can select them
+ # from a drop-down list?
+ form_variables = serializers.ListField(
+ child=FormioVariableKeyField(max_length=50),
+ label=_("Form variable key list"),
+ help_text=_(
+ "A comma-separated list of form variables (can also include static variables) to use."
+ )
+ )
diff --git a/src/openforms/registrations/contrib/new_plugin/plugin.py b/src/openforms/registrations/contrib/new_plugin/plugin.py
new file mode 100644
index 0000000000..d729e032da
--- /dev/null
+++ b/src/openforms/registrations/contrib/new_plugin/plugin.py
@@ -0,0 +1,18 @@
+from django.utils.translation import gettext_lazy as _
+
+from openforms.submissions.models import Submission
+from ...base import BasePlugin, OptionsT # openforms.registrations.base
+from ...registry import register # openforms.registrations.registry
+from .config import NewPluginOptionsSerializer
+
+
+@register("new_plugin")
+class NewPlugin(BasePlugin):
+ verbose_name = _("New fancy plugin")
+ configuration_options = NewPluginOptionsSerializer
+
+ def register_submission(self, submission: Submission, options: OptionsT) -> None:
+ print(options)
+
+ def check_config(self):
+ pass