From ca373b685481988effff667f10461214766c5977 Mon Sep 17 00:00:00 2001 From: Giulia Ghisini <51911425+giuliaghisini@users.noreply.github.com> Date: Thu, 14 Mar 2024 15:33:34 +0100 Subject: [PATCH] feat: Validate field config and fix multiple form in page (#83) * feat: custom validators for config form fields and fix multiple form in same page * chore: updated README * chore: removed console log --- README.md | 18 ++++++ locales/de/LC_MESSAGES/volto.po | 5 ++ locales/en/LC_MESSAGES/volto.po | 5 ++ locales/es/LC_MESSAGES/volto.po | 5 ++ locales/eu/LC_MESSAGES/volto.po | 5 ++ locales/fr/LC_MESSAGES/volto.po | 5 ++ locales/it/LC_MESSAGES/volto.po | 7 ++- locales/ja/LC_MESSAGES/volto.po | 5 ++ locales/nl/LC_MESSAGES/volto.po | 5 ++ locales/pt/LC_MESSAGES/volto.po | 5 ++ locales/pt_BR/LC_MESSAGES/volto.po | 5 ++ locales/ro/LC_MESSAGES/volto.po | 5 ++ locales/volto.pot | 7 ++- src/actions/index.js | 8 ++- src/components/FormView.jsx | 10 +++- src/components/Sidebar.jsx | 1 + src/components/ValidateConfigForm.jsx | 40 ++++++++++++- src/components/View.jsx | 5 +- src/formSchema.js | 1 + src/index.js | 11 ++-- src/reducers/index.js | 85 ++++++++++++++++++++------- 21 files changed, 208 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 32b65cc..cc7a895 100644 --- a/README.md +++ b/README.md @@ -116,6 +116,24 @@ i.e.: aggregated data from user federated authentication: ![Static fields](./docs/form-static-fields.png) +## Schema validators + +If you want to validate configuration field (for example, testing if 'From email' is an address of a specific domain), you could add your validation functions to block config: + +```js +config.blocks.blocksConfig.form = { + ...config.blocks.blocksConfig.form, + schemaValidators: { + fieldname: yourValidationFN(data), + }, +}; +``` + +`yourValidationFN` have to return: + +- null if field is valid +- a string with the error message if field is invalid. + ## Upgrade guide To upgrade to version 2.4.0 you need to: diff --git a/locales/de/LC_MESSAGES/volto.po b/locales/de/LC_MESSAGES/volto.po index 5452568..aa32fae 100644 --- a/locales/de/LC_MESSAGES/volto.po +++ b/locales/de/LC_MESSAGES/volto.po @@ -298,6 +298,11 @@ msgstr "Daten exportieren" msgid "form_edit_fill_required_configuration_fields" msgstr "" +#: components/ValidateConfigForm +# defaultMessage: Please, verify this configuration errors in sidebar. The form will be not displayed in view mode until this errors fields are not fixed. +msgid "form_edit_other_errors" +msgstr "" + #: components/Edit # defaultMessage: Attenzione! msgid "form_edit_warning" diff --git a/locales/en/LC_MESSAGES/volto.po b/locales/en/LC_MESSAGES/volto.po index 65f06ad..55af392 100644 --- a/locales/en/LC_MESSAGES/volto.po +++ b/locales/en/LC_MESSAGES/volto.po @@ -298,6 +298,11 @@ msgstr "Export in CSV" msgid "form_edit_fill_required_configuration_fields" msgstr "" +#: components/ValidateConfigForm +# defaultMessage: Please, verify this configuration errors in sidebar. The form will be not displayed in view mode until this errors fields are not fixed. +msgid "form_edit_other_errors" +msgstr "" + #: components/Edit # defaultMessage: Attenzione! msgid "form_edit_warning" diff --git a/locales/es/LC_MESSAGES/volto.po b/locales/es/LC_MESSAGES/volto.po index 35e05a7..b47bcf5 100644 --- a/locales/es/LC_MESSAGES/volto.po +++ b/locales/es/LC_MESSAGES/volto.po @@ -307,6 +307,11 @@ msgstr "Exportar datos" msgid "form_edit_fill_required_configuration_fields" msgstr "" +#: components/ValidateConfigForm +# defaultMessage: Please, verify this configuration errors in sidebar. The form will be not displayed in view mode until this errors fields are not fixed. +msgid "form_edit_other_errors" +msgstr "" + #: components/Edit # defaultMessage: Attenzione! msgid "form_edit_warning" diff --git a/locales/eu/LC_MESSAGES/volto.po b/locales/eu/LC_MESSAGES/volto.po index a6c0207..e16df33 100644 --- a/locales/eu/LC_MESSAGES/volto.po +++ b/locales/eu/LC_MESSAGES/volto.po @@ -300,6 +300,11 @@ msgstr "Esportatu datuak" msgid "form_edit_fill_required_configuration_fields" msgstr "" +#: components/ValidateConfigForm +# defaultMessage: Please, verify this configuration errors in sidebar. The form will be not displayed in view mode until this errors fields are not fixed. +msgid "form_edit_other_errors" +msgstr "" + #: components/Edit # defaultMessage: Attenzione! msgid "form_edit_warning" diff --git a/locales/fr/LC_MESSAGES/volto.po b/locales/fr/LC_MESSAGES/volto.po index c523442..339be88 100644 --- a/locales/fr/LC_MESSAGES/volto.po +++ b/locales/fr/LC_MESSAGES/volto.po @@ -298,6 +298,11 @@ msgstr "Exporter au format CSV" msgid "form_edit_fill_required_configuration_fields" msgstr "" +#: components/ValidateConfigForm +# defaultMessage: Please, verify this configuration errors in sidebar. The form will be not displayed in view mode until this errors fields are not fixed. +msgid "form_edit_other_errors" +msgstr "" + #: components/Edit # defaultMessage: Attenzione! msgid "form_edit_warning" diff --git a/locales/it/LC_MESSAGES/volto.po b/locales/it/LC_MESSAGES/volto.po index 48f1cba..8c28fd0 100644 --- a/locales/it/LC_MESSAGES/volto.po +++ b/locales/it/LC_MESSAGES/volto.po @@ -296,7 +296,12 @@ msgstr "Esporta in CSV" #: components/ValidateConfigForm # defaultMessage: Please, fill-in required configuration fields in sidebar. The form will be not displayed in view mode until required fields are filled-in. msgid "form_edit_fill_required_configuration_fields" -msgstr "Inserire i campi obbligatori per la configurazione del form nella sidebar di destra. Il form non verrà mostrata sul sito finché i campi obbligatori non saranno configurati." +msgstr "Inserire i campi obbligatori per la configurazione del form nella sidebar di destra. Il form non verrà mostrato sul sito finché i campi obbligatori non saranno configurati." + +#: components/ValidateConfigForm +# defaultMessage: Please, verify this configuration errors in sidebar. The form will be not displayed in view mode until this errors fields are not fixed. +msgid "form_edit_other_errors" +msgstr "Verifica i seguenti errori di configurazione nella sidebar di destra. Il form non verrà mostrato sul sito finchè questi errori non sono stati sistemati." #: components/Edit # defaultMessage: Attenzione! diff --git a/locales/ja/LC_MESSAGES/volto.po b/locales/ja/LC_MESSAGES/volto.po index 1e11041..6926fc0 100644 --- a/locales/ja/LC_MESSAGES/volto.po +++ b/locales/ja/LC_MESSAGES/volto.po @@ -298,6 +298,11 @@ msgstr "" msgid "form_edit_fill_required_configuration_fields" msgstr "" +#: components/ValidateConfigForm +# defaultMessage: Please, verify this configuration errors in sidebar. The form will be not displayed in view mode until this errors fields are not fixed. +msgid "form_edit_other_errors" +msgstr "" + #: components/Edit # defaultMessage: Attenzione! msgid "form_edit_warning" diff --git a/locales/nl/LC_MESSAGES/volto.po b/locales/nl/LC_MESSAGES/volto.po index 1e11041..6926fc0 100644 --- a/locales/nl/LC_MESSAGES/volto.po +++ b/locales/nl/LC_MESSAGES/volto.po @@ -298,6 +298,11 @@ msgstr "" msgid "form_edit_fill_required_configuration_fields" msgstr "" +#: components/ValidateConfigForm +# defaultMessage: Please, verify this configuration errors in sidebar. The form will be not displayed in view mode until this errors fields are not fixed. +msgid "form_edit_other_errors" +msgstr "" + #: components/Edit # defaultMessage: Attenzione! msgid "form_edit_warning" diff --git a/locales/pt/LC_MESSAGES/volto.po b/locales/pt/LC_MESSAGES/volto.po index 1e11041..6926fc0 100644 --- a/locales/pt/LC_MESSAGES/volto.po +++ b/locales/pt/LC_MESSAGES/volto.po @@ -298,6 +298,11 @@ msgstr "" msgid "form_edit_fill_required_configuration_fields" msgstr "" +#: components/ValidateConfigForm +# defaultMessage: Please, verify this configuration errors in sidebar. The form will be not displayed in view mode until this errors fields are not fixed. +msgid "form_edit_other_errors" +msgstr "" + #: components/Edit # defaultMessage: Attenzione! msgid "form_edit_warning" diff --git a/locales/pt_BR/LC_MESSAGES/volto.po b/locales/pt_BR/LC_MESSAGES/volto.po index ffe705a..3040eab 100644 --- a/locales/pt_BR/LC_MESSAGES/volto.po +++ b/locales/pt_BR/LC_MESSAGES/volto.po @@ -304,6 +304,11 @@ msgstr "Exportar dados" msgid "form_edit_fill_required_configuration_fields" msgstr "" +#: components/ValidateConfigForm +# defaultMessage: Please, verify this configuration errors in sidebar. The form will be not displayed in view mode until this errors fields are not fixed. +msgid "form_edit_other_errors" +msgstr "" + #: components/Edit # defaultMessage: Attenzione! msgid "form_edit_warning" diff --git a/locales/ro/LC_MESSAGES/volto.po b/locales/ro/LC_MESSAGES/volto.po index 1e11041..6926fc0 100644 --- a/locales/ro/LC_MESSAGES/volto.po +++ b/locales/ro/LC_MESSAGES/volto.po @@ -298,6 +298,11 @@ msgstr "" msgid "form_edit_fill_required_configuration_fields" msgstr "" +#: components/ValidateConfigForm +# defaultMessage: Please, verify this configuration errors in sidebar. The form will be not displayed in view mode until this errors fields are not fixed. +msgid "form_edit_other_errors" +msgstr "" + #: components/Edit # defaultMessage: Attenzione! msgid "form_edit_warning" diff --git a/locales/volto.pot b/locales/volto.pot index 39b9cf3..39c2d47 100644 --- a/locales/volto.pot +++ b/locales/volto.pot @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: Plone\n" -"POT-Creation-Date: 2024-03-11T13:38:04.642Z\n" +"POT-Creation-Date: 2024-03-14T13:21:14.614Z\n" "Last-Translator: Plone i18n \n" "Language-Team: Plone i18n \n" "MIME-Version: 1.0\n" @@ -300,6 +300,11 @@ msgstr "" msgid "form_edit_fill_required_configuration_fields" msgstr "" +#: components/ValidateConfigForm +# defaultMessage: Please, verify this configuration errors in sidebar. The form will be not displayed in view mode until this errors fields are not fixed. +msgid "form_edit_other_errors" +msgstr "" + #: components/Edit # defaultMessage: Attenzione! msgid "form_edit_warning" diff --git a/src/actions/index.js b/src/actions/index.js index fdfa9c0..2bb5a6c 100644 --- a/src/actions/index.js +++ b/src/actions/index.js @@ -16,6 +16,7 @@ export const SUBMIT_FORM_ACTION = 'SUBMIT_FORM_ACTION'; export function submitForm(path = '', block_id, data, attachments, captcha) { return { type: SUBMIT_FORM_ACTION, + subrequest: block_id, request: { op: 'post', path: path + '/@submit-form', @@ -35,12 +36,15 @@ export function submitForm(path = '', block_id, data, attachments, captcha) { */ export const EXPORT_CSV_FORMDATA = 'EXPORT_CSV_FORMDATA'; -export function exportCsvFormData(path = '') { +export function exportCsvFormData(path = '', filename, block_id) { return { type: EXPORT_CSV_FORMDATA, + filename: filename, request: { op: 'get', - path: path + '/@form-data-export', + path: `${path}/@form-data-export${ + block_id ? '?block_id=' + block_id : '' + }`, }, }; } diff --git a/src/components/FormView.jsx b/src/components/FormView.jsx index 0840963..faf5242 100644 --- a/src/components/FormView.jsx +++ b/src/components/FormView.jsx @@ -220,7 +220,15 @@ const FormView = ({ {data?.show_cancel && ( - diff --git a/src/components/Sidebar.jsx b/src/components/Sidebar.jsx index 939ac03..3c5a229 100644 --- a/src/components/Sidebar.jsx +++ b/src/components/Sidebar.jsx @@ -159,6 +159,7 @@ const Sidebar = ({ exportCsvFormData( flattenToAppURL(properties['@id']), `export-${properties.id ?? 'form'}.csv`, + block, ), ) } diff --git a/src/components/ValidateConfigForm.jsx b/src/components/ValidateConfigForm.jsx index b46328d..4d88312 100644 --- a/src/components/ValidateConfigForm.jsx +++ b/src/components/ValidateConfigForm.jsx @@ -8,14 +8,20 @@ const messages = defineMessages({ defaultMessage: 'Please, fill-in required configuration fields in sidebar. The form will be not displayed in view mode until required fields are filled-in.', }, + other_errors: { + id: 'form_edit_other_errors', + defaultMessage: + 'Please, verify this configuration errors in sidebar. The form will be not displayed in view mode until this errors fields are not fixed.', + }, }); const ValidateConfigForm = ({ data = {}, children, onEdit }) => { const intl = useIntl(); var Schema = config.blocks.blocksConfig.form.formSchema; + var SchemaValidators = config.blocks.blocksConfig.form.schemaValidators; var blockSchema = Schema(data); const required_fields = blockSchema.required; - const valid = + const noRequired = required_fields.filter( (r) => data[r] === null || @@ -24,19 +30,47 @@ const ValidateConfigForm = ({ data = {}, children, onEdit }) => { (blockSchema.properties[r].type === 'boolean' && !data[r]), ).length === 0; + let schema_validation = []; + if (SchemaValidators) { + Object.keys(SchemaValidators).forEach((fieldName) => { + const validateFieldFN = SchemaValidators[fieldName]; + const validation = validateFieldFN(data); + + if (validation) { + schema_validation.push({ field: fieldName, message: validation }); + } + }); + } + + let noInvalidFields = schema_validation.length === 0; + + const valid = noRequired && noInvalidFields; + return ( <> {!valid && onEdit && (
- {intl.formatMessage(messages.fill_required_config_fields)} + {!noRequired && ( + <>{intl.formatMessage(messages.fill_required_config_fields)} + )} + {schema_validation?.length > 0 && ( +
+ {intl.formatMessage(messages.other_errors)} +
    + {schema_validation.map((v) => ( +
  • {v.message}
  • + ))} +
+
+ )}
)} {(valid || onEdit) && <>{children}} diff --git a/src/components/View.jsx b/src/components/View.jsx index ebefeaf..033076d 100644 --- a/src/components/View.jsx +++ b/src/components/View.jsx @@ -110,7 +110,10 @@ const View = ({ data, id, path }) => { const [formState, setFormState] = useReducer(formStateReducer, initialState); const [formErrors, setFormErrors] = useState([]); - const submitResults = useSelector((state) => state.submitForm); + + const submitResults = useSelector( + (state) => state.submitForm?.subrequests?.[id], + ); const captchaToken = useRef(); const onChangeFormData = (field_id, field, value, extras) => { diff --git a/src/formSchema.js b/src/formSchema.js index 0f79642..4edb803 100644 --- a/src/formSchema.js +++ b/src/formSchema.js @@ -172,6 +172,7 @@ const Schema = (data) => { 'default_to', 'default_from', 'default_subject', + 'captcha', ...conditional_required, ], }; diff --git a/src/index.js b/src/index.js index e7f614c..fca79cd 100644 --- a/src/index.js +++ b/src/index.js @@ -46,6 +46,9 @@ const applyConfig = (config) => { from: FromSchemaExtender, hidden: HiddenSchemaExtender, }, + schemaValidators: { + /*fieldname: validationFN(data)*/ + }, attachment_fields: ['attachment'], restricted: false, mostUsed: true, @@ -65,11 +68,11 @@ const applyConfig = (config) => { clearFormData, }; - config.settings.loadables['HCaptcha'] = loadable(() => - import('@hcaptcha/react-hcaptcha'), + config.settings.loadables['HCaptcha'] = loadable( + () => import('@hcaptcha/react-hcaptcha'), ); - config.settings.loadables['GoogleReCaptcha'] = loadable.lib(() => - import('react-google-recaptcha-v3'), + config.settings.loadables['GoogleReCaptcha'] = loadable.lib( + () => import('react-google-recaptcha-v3'), ); return config; diff --git a/src/reducers/index.js b/src/reducers/index.js index a976d8c..2df6cca 100644 --- a/src/reducers/index.js +++ b/src/reducers/index.js @@ -30,6 +30,7 @@ const initialState = { error: null, loaded: false, loading: false, + subrequests: {}, }; /** @@ -42,26 +43,69 @@ const initialState = { export const submitForm = (state = initialState, action = {}) => { switch (action.type) { case `${SUBMIT_FORM_ACTION}_PENDING`: - return { - ...state, - error: null, - loaded: false, - loading: true, - }; + return action.subrequest + ? { + ...state, + subrequests: { + ...state.subrequests, + [action.subrequest]: { + ...(state.subrequests[action.subrequest] || { + items: [], + total: 0, + batching: {}, + }), + error: null, + loaded: false, + loading: true, + }, + }, + } + : { + ...state, + error: null, + loading: true, + loaded: false, + }; case `${SUBMIT_FORM_ACTION}_SUCCESS`: - return { - ...state, - error: null, - loaded: true, - loading: false, - }; + return action.subrequest + ? { + ...state, + subrequests: { + ...state.subrequests, + [action.subrequest]: { + ...(state.subrequests[action.subrequest] || {}), + error: null, + loaded: true, + loading: false, + }, + }, + } + : { + ...state, + error: null, + loaded: true, + loading: false, + }; case `${SUBMIT_FORM_ACTION}_FAIL`: - return { - ...state, - error: action.error, - loaded: false, - loading: false, - }; + return action.subrequest + ? { + ...state, + subrequests: { + ...state.subrequests, + [action.subrequest]: { + ...(state.subrequests[action.subrequest] || {}), + loading: false, + loaded: false, + }, + }, + } + : { + ...state, + error: action.error, + loading: false, + loaded: false, + }; + default: return state; } @@ -85,10 +129,7 @@ export const exportCsvFormData = (state = initialState, action = {}) => { loading: true, }; case `${EXPORT_CSV_FORMDATA}_SUCCESS`: - download( - `export-${state.content?.data?.id ?? 'form'}.csv`, - action.result, - ); + download(action.filename ?? `export-form.csv`, action.result); return { ...state,