diff --git a/plugins/form/CHANGELOG.md b/plugins/form/CHANGELOG.md index 7c88629..074fa22 100644 --- a/plugins/form/CHANGELOG.md +++ b/plugins/form/CHANGELOG.md @@ -1,3 +1,18 @@ +# v7.3.0 +## 12/14/2023 + +1. [](#new) + * Added XHR/Ajax form submission as an option in the form blueprint. See [Learn Forms](https://learn.getgrav.org/17/forms/forms/how-to-ajax-submission) for details. + +# v7.2.2 +## 12/13/2023 + +1. [](#improved) + * Add _inline errors_ for `file` field. Useful in combination with `form: no-validate: true` form setting. + * Validate filename against `uploads_dangerous_extensions` when using the `save:` action +1. [](#bugfix) + * Cleared 'basic captcha' value when invalid + # v7.2.1 ## 06/27/2023 diff --git a/plugins/form/blueprints.yaml b/plugins/form/blueprints.yaml index 4a7ec33..ccb759a 100644 --- a/plugins/form/blueprints.yaml +++ b/plugins/form/blueprints.yaml @@ -1,7 +1,7 @@ name: Form slug: form type: plugin -version: 7.2.1 +version: 7.3.0 description: Enables forms handling and processing icon: check-square author: diff --git a/plugins/form/form.php b/plugins/form/form.php index 02eae7a..fb77515 100644 --- a/plugins/form/form.php +++ b/plugins/form/form.php @@ -518,7 +518,7 @@ class FormPlugin extends Plugin $captcha_value = trim($form->value('basic-captcha')); if (!$captcha->validateCaptcha($captcha_value)) { $message = $params['message'] ?? $this->grav['language']->translate('PLUGIN_FORM.ERROR_BASIC_CAPTCHA'); - + $form->setData('basic-captcha', ''); $this->grav->fireEvent('onFormValidationError', new Event([ 'form' => $form, 'message' => $message @@ -667,6 +667,11 @@ class FormPlugin extends Plugin $filename = $prefix . $this->udate($format, $raw_format) . $postfix . $ext; } + // Handle bad filenames. + if (!Utils::checkFilename($filename)) { + throw new RuntimeException(sprintf('Form save: File with extension not allowed: %s', $filename)); + } + /** @var Twig $twig */ $twig = $this->grav['twig']; $vars = [ @@ -1130,6 +1135,10 @@ class FormPlugin extends Plugin return false; } + if (isset($form->xhr_submit) && $form->xhr_submit) { + $form->set('template', $form->template ?? 'form-xhr'); + } + // Set page template if passed by form if (isset($form->template)) { $this->grav['page']->template($form->template); diff --git a/plugins/form/templates/form-xhr.html.twig b/plugins/form/templates/form-xhr.html.twig new file mode 100644 index 0000000..eea54d4 --- /dev/null +++ b/plugins/form/templates/form-xhr.html.twig @@ -0,0 +1 @@ +{% extends "forms/default/form.html.twig" %} \ No newline at end of file diff --git a/plugins/form/templates/forms/default/form.html.twig b/plugins/form/templates/forms/default/form.html.twig index 7be60d9..263972d 100644 --- a/plugins/form/templates/forms/default/form.html.twig +++ b/plugins/form/templates/forms/default/form.html.twig @@ -1,3 +1,4 @@ +{% block xhr %}{% endblock %} {% set form = form ?? grav.session.getFlashObject('form') %} {% set layout = layout ?? form.layout ?? 'default' %} {% set field_layout = field_layout ?? layout %} diff --git a/plugins/form/templates/forms/fields/file/file.html.twig b/plugins/form/templates/forms/fields/file/file.html.twig index 96ac87e..a4b9d47 100644 --- a/plugins/form/templates/forms/fields/file/file.html.twig +++ b/plugins/form/templates/forms/fields/file/file.html.twig @@ -89,7 +89,14 @@ {{ macro.preview(path, file, _context) }} {% endfor %} {% include 'forms/fields/hidden/hidden.html.twig' with {field: {name: '_json.' ~ field.name}, value: (value ?? [])|json_encode } %} + + + {% if inline_errors and errors %} +
+

{{ errors|first|raw }}

+
+ {% endif %} {% if grav.browser.browser == 'msie' and grav.browser.version < 12 %} {% do assets.addJs('plugin://form/assets/object.assign.polyfill.js') %} diff --git a/plugins/form/templates/forms/form.html.twig b/plugins/form/templates/forms/form.html.twig index 9d28cb3..53ec58c 100644 --- a/plugins/form/templates/forms/form.html.twig +++ b/plugins/form/templates/forms/form.html.twig @@ -26,4 +26,9 @@ You can also override individual fields by copying (using text field as an examp templates/forms/fields/text/text.html.twig -> templates/forms/fields/text/tailwind-text.html.twig #} + {% extends "forms/default/form.html.twig" %} + +{% block xhr %} +{% include 'forms/layouts/xhr.html.twig' %} +{% endblock %} diff --git a/plugins/form/templates/forms/layouts/xhr.html.twig b/plugins/form/templates/forms/layouts/xhr.html.twig new file mode 100644 index 0000000..c8d60bc --- /dev/null +++ b/plugins/form/templates/forms/layouts/xhr.html.twig @@ -0,0 +1,21 @@ +{% if form.xhr_submit == true %} + {% do assets.addInlineJs(" + document.addEventListener('DOMContentLoaded', function() { + var form = document.getElementById('" ~ form.id ~ "'); + form.addEventListener('submit', function(e) { + // prevent standard form submission + e.preventDefault(); + // submit the form via Ajax + var xhr = new XMLHttpRequest(); + xhr.open(form.getAttribute('method'), form.getAttribute('action')); + xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + xhr.onload = function() { + if (xhr.status === 200) { + document.getElementById('" ~ form.id ~ "').innerHTML = xhr.responseText; + } + }; + xhr.send(new URLSearchParams(new FormData(form)).toString()); + }); + }); + ", {'group': 'bottom', 'position': 'before', 'priority': 100}) %} +{% endif %} \ No newline at end of file