diff --git a/CHANGELOG.md b/CHANGELOG.md index b1de67b4..5c1da102 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,24 @@ -# v2.3.2 -## 03/xx/2017 +# v2.4.0 +## 04/19/2017 +1. [](#new) + * Added the ability for front-end forms to use advanced blueprint features such as `data-*@` and `config-*@` + * Added support for dynamically added pages to process forms properly + * Added a new avatar field for displaying account avatar + * Added method to get all `data` from a form + * Support `task` in button types 1. [](#improved) * Added `step` to range field [#136](https://github.com/getgrav/grav-plugin-form/issues/136) + * Added a new default ajax handler twig template + * Moved twig events to always process even if forms are not defined + * Some code cleanup + * Handle `null` with session-based form + * Added support for append/prepend to number field +1. [](#bugfix) + * Always process form events as long as a `$_POST` exists [login #101](https://github.com/getgrav/grav-plugin-login/issues/101) + * Various fixes for `file` field + * Allow manually added pages to process forms and upload files + * Fixed issue with nested fileds not showing up in `data.*.twig` templates # v2.3.1 ## 03/23/2017 diff --git a/blueprints.yaml b/blueprints.yaml index fb247b6f..c3163811 100644 --- a/blueprints.yaml +++ b/blueprints.yaml @@ -1,5 +1,5 @@ name: Form -version: 2.3.1 +version: 2.4.0 description: Enables the forms handling icon: check-square author: @@ -12,7 +12,7 @@ bugs: https://github.com/getgrav/grav-plugin-form/issues license: MIT dependencies: - - { name: grav, version: '>=1.1.16' } + - { name: grav, version: '>=1.2.3' } form: validation: strict diff --git a/classes/form.php b/classes/form.php index 997b7a9a..4607df0b 100644 --- a/classes/form.php +++ b/classes/form.php @@ -155,7 +155,8 @@ public function unserialize($data) $rules = $this->rules; $blueprint = function() use ($name, $items, $rules) { - return new Blueprint($name, ['form' => $items, 'rules' => $rules]); + $blueprint = new Blueprint($name, ['form' => $items, 'rules' => $rules]); + return $blueprint->load()->init(); }; $this->data = new Data($data['data'], $blueprint); @@ -225,20 +226,16 @@ public function reset() $items = $this->items; $rules = $this->rules; + $blueprint = function() use ($name, $items, $rules) { - return new Blueprint($name, ['form' => $items, 'rules' => $rules]); + $blueprint = new Blueprint($name, ['form' => $items, 'rules' => $rules]); + return $blueprint->load()->init(); }; - if (method_exists($blueprint, 'load')) { - // init the form to process directives - $blueprint->load()->init(); - - // fields set to processed blueprint fields - $this->fields = $blueprint->fields(); - } - $this->data = new Data($this->header_data, $blueprint); $this->values = new Data(); + $this->fields = null; + $this->fields = $this->fields(); // Fire event $grav->fireEvent('onFormInitialized', new Event(['form' => $this])); @@ -324,6 +321,16 @@ public function getValue($name) return $this->values->get($name); } + /** + * Get all data + * + * @return Data + */ + public function getData() + { + return $this->data; + } + /** * Set value of given variable in the data array * @@ -390,7 +397,7 @@ public function uploadFiles() // we need to move the file at this stage or else // it won't be available upon save later on // since php removes it from the upload location - $tmp_dir = Grav::instance()['locator']->findResource('tmp://', true, true); + $tmp_dir = $grav['locator']->findResource('tmp://', true, true); $tmp_file = $upload->file->tmp_name; $tmp = $tmp_dir . '/uploaded-files/' . basename($tmp_file); @@ -612,6 +619,7 @@ public function post() public function getPagePathFromToken($path) { $grav = Grav::instance(); + $path_parts = pathinfo($path); $basename = ''; diff --git a/form.php b/form.php index 103b92b9..ddf871a2 100644 --- a/form.php +++ b/form.php @@ -54,8 +54,6 @@ public function onPluginsInitialized() { require_once(__DIR__ . '/classes/form.php'); - - if ($this->isAdmin()) { $this->enable([ 'onPagesInitialized' => ['onPagesInitialized', 0] @@ -67,6 +65,8 @@ public function onPluginsInitialized() 'onPageProcessed' => ['onPageProcessed', 0], 'onPagesInitialized' => ['onPagesInitialized', 0], 'onTwigInitialized' => ['onTwigInitialized', 0], + 'onTwigPageVariables' => ['onTwigVariables', 0], + 'onTwigSiteVariables' => ['onTwigVariables', 0], 'onFormValidationProcessed' => ['onFormValidationProcessed', 0], ]); } @@ -141,6 +141,14 @@ public function onPagesInitialized() $this->flat_forms = $flat_forms; } + // Enable form events if there's a POST + if (!empty($_POST)) { + $this->enable([ + 'onFormProcessed' => ['onFormProcessed', 0], + 'onFormValidationError' => ['onFormValidationError', 0] + ]); + } + if ($this->isAdmin() && !empty($_POST)) { $page = $this->grav['page']; @@ -153,19 +161,13 @@ public function onPagesInitialized() if (isset($header->form) && is_array($header->form)) { // Create form $this->form = new Form($page); - $this->enable([ - 'onFormProcessed' => ['onFormProcessed', 0], - 'onFormValidationError' => ['onFormValidationError', 0] - ]); $this->form->post(); } } elseif ($this->forms) { $this->enable([ - 'onTwigPageVariables' => ['onTwigVariables', 0], - 'onTwigSiteVariables' => ['onTwigVariables', 0], - 'onFormFieldTypes' => ['onFormFieldTypes', 0] + 'onFormFieldTypes' => ['onFormFieldTypes', 0], ]); // Regenerate list of flat_forms if not already populated @@ -181,23 +183,17 @@ public function onPagesInitialized() // Handle posting if needed. if (!empty($_POST)) { - $this->enable([ - 'onFormProcessed' => ['onFormProcessed', 0], - 'onFormValidationError' => ['onFormValidationError', 0] - ]); - $current_form_name = $this->getFormName($this->grav['page']); $this->json_response = []; + $this->form = $this->getFormByName($current_form_name); - if ($this->form = $this->getFormByName($current_form_name)) { - if ($this->grav['uri']->extension() === 'json' && isset($_POST['__form-file-uploader__'])) { - $this->json_response = $this->form->uploadFiles(); - } else { - $this->form->post(); - $submitted = true; - } - } elseif (isset($this->grav['page']->header()->form)) { + if (!$this->form && isset($this->grav['page']->header()->form)) { $this->form = new Form($this->grav['page']); + } + + if ($this->grav['uri']->extension() === 'json' && isset($_POST['__form-file-uploader__'])) { + $this->json_response = $this->form->uploadFiles(); + } else { $this->form->post(); $submitted = true; } @@ -253,6 +249,8 @@ public function onTwigVariables(Event $event = null) $page = $this->grav['page']; } + $header = $page->header(); + // get route to calculated page $page_route = $page->route(); // get route to current page @@ -266,6 +264,8 @@ public function onTwigVariables(Event $event = null) $found_forms = $this->forms[$page_route]; } elseif (isset($this->forms[$current_page_route])) { $found_forms = $this->forms[$current_page_route]; + } elseif (isset($header->form)) { + $found_forms = [new Form($page)]; } $this->grav['twig']->twig_vars['form'] = array_shift($found_forms); diff --git a/templates/form.json.twig b/templates/form.json.twig index 28498895..f6ba33fc 100644 --- a/templates/form.json.twig +++ b/templates/form.json.twig @@ -1,5 +1 @@ -{% if form_json_response %} -{{ form_json_response|json_encode|raw }} -{% else %} -{} -{% endif %} +{% extends 'forms/ajax.json.twig' %} diff --git a/templates/forms/ajax.json.twig b/templates/forms/ajax.json.twig new file mode 100644 index 00000000..ccec0390 --- /dev/null +++ b/templates/forms/ajax.json.twig @@ -0,0 +1,5 @@ +{% if form_json_response %} +{{ form_json_response|json_encode|raw }} +{% else %} +{} +{% endif %} \ No newline at end of file diff --git a/templates/forms/default/data.html.twig b/templates/forms/default/data.html.twig index e5186082..9f54f282 100644 --- a/templates/forms/default/data.html.twig +++ b/templates/forms/default/data.html.twig @@ -1,29 +1,39 @@ -{% for index, field in form.fields %} - {% set input = attribute(field, "input@") %} - {% if input is null or input == true %} - {% block field %} -
- {% block field_label %} - {{ field.label|t|e }}: - {% endblock %} +{% macro render_field(form, fields) %} + {% for index, field in fields %} + {% set input = attribute(field, "input@") %} + {% if input is null or input == true %} + {% if form.value(field.name) %} + {% block field %} +
+ {% block field_label %} + {{ field.label|t|e }}: + {% endblock %} - {% block field_value %} - {% if field.type == 'checkboxes' %} - - {% elseif field.type == 'checkbox' %} - {{ (form.value(field.name) == 1) ? "PLUGIN_FORM.YES"|t|e : "PLUGIN_FORM.NO"|t|e }} - {% elseif field.type == 'select' %} - {{ field.options[form.value(field.name)]|e }} - {% else %} - {{ string(form.value(field.name)|e)|nl2br }} - {% endif %} + {% block field_value %} + {% if field.type == 'checkboxes' %} + + {% elseif field.type == 'checkbox' %} + {{ (form.value(field.name) == 1) ? "PLUGIN_FORM.YES"|t|e : "PLUGIN_FORM.NO"|t|e }} + {% elseif field.type == 'select' %} + {{ field.options[form.value(field.name)]|e }} + {% else %} + {{ string(form.value(field.name)|e)|nl2br }} + {% endif %} + {% endblock %} +
{% endblock %} -
- {% endblock %} + {% endif %} + {% else %} + {% if field.fields is iterable %} + {{ _self.render_field(form, field.fields) }} + {% endif %} + {% endif %} + {% endfor %} +{% endmacro %} + +{{ _self.render_field(form, form.fields) }} - {% endif %} -{% endfor %} \ No newline at end of file diff --git a/templates/forms/default/data.txt.twig b/templates/forms/default/data.txt.twig index 93c92a57..ff9eb864 100644 --- a/templates/forms/default/data.txt.twig +++ b/templates/forms/default/data.txt.twig @@ -1,11 +1,16 @@ -{% autoescape false %} - -{% for index, field in form.fields %} -{% set input = attribute(field, "input@") %} -{% if input is null or input == true %} -{% set value = form.value(field.name ?? index) %} -{{ field.name ?? index }}: {{ string((value is iterable ? value|json_encode : value)|nl2br)|replace({"\n":' ', "\r":' '}) }} -{% endif %} -{% endfor %} - -{% endautoescape %} \ No newline at end of file +{%- macro render_field(form, fields) %} +{%- for index, field in fields %} + {%- set input = attribute(field, "input@") %} + {%- if input is null or input == true %} + {%- set value = form.value(field.name ?? index) %} + {{- field.name ?? index }}: {{ string((value is iterable ? value|json_encode : value)) ~ "\r\n" }} + {%- else %} + {%- if field.fields is iterable %} + {{- _self.render_field(form, field.fields) }} + {%- endif %} + {%- endif %} +{%- endfor %} +{%- endmacro %} +{%- autoescape false %} +{{- _self.render_field(form, form.fields) ~ "\r\n" }} +{%- endautoescape %} \ No newline at end of file diff --git a/templates/forms/default/form.html.twig b/templates/forms/default/form.html.twig index 00068f1d..a2ab0eb7 100644 --- a/templates/forms/default/form.html.twig +++ b/templates/forms/default/form.html.twig @@ -1,3 +1,7 @@ +{% if form is null %} + {% set form = grav.session.getFlashObject('form') %} +{% endif %} + {% if form.message %} {% if form.inline_errors and form.messages %}

{{ "FORM.VALIDATION_FAIL"|t|raw }}

@@ -59,7 +63,12 @@ class="{{ button.classes|default('button') }}" {% endblock %} {% if button.disabled %}disabled="disabled"{% endif %} + type="{{ button.type|default('submit') }}" + + {% if button.task %} + name="task" value="{{ button.task }}" + {% endif %} > {{ button.value|t|default('Submit') }} diff --git a/templates/forms/fields/avatar/avatar.html.twig b/templates/forms/fields/avatar/avatar.html.twig new file mode 100644 index 00000000..33862880 --- /dev/null +++ b/templates/forms/fields/avatar/avatar.html.twig @@ -0,0 +1,5 @@ +{% if form.data.avatar %} + +{% else %} + +{% endif %} \ No newline at end of file diff --git a/templates/forms/fields/file/file.html.twig b/templates/forms/fields/file/file.html.twig index 29dd5a59..d237f72b 100644 --- a/templates/forms/fields/file/file.html.twig +++ b/templates/forms/fields/file/file.html.twig @@ -36,7 +36,7 @@ {% set blueprint_name = type ~ '/' ~ blueprint_name %} {% endif %} {% set blueprint = base64_encode(blueprint_name) %} - {% set real_path = global.admin.getPagePathFromToken(path) %} + {% set real_path = global.form.getPagePathFromToken(path) %} {% set remove = uri.addNonce(global.base_url_relative ~ '/media.json' ~ '/route' ~ config.system.param_sep ~ base64_encode(global.base_path ~ '/' ~ real_path) ~ @@ -72,7 +72,7 @@ {% for path, file in value %} {{ _self.preview(path, file, _context) }} {% endfor %} - {% include 'forms/fields/hidden/hidden.html.twig' with {field: {name: '_json.' ~ field.name}, value:value|raw|json_encode} %} + {% include 'forms/fields/hidden/hidden.html.twig' with {field: {name: '_json.' ~ field.name}, value:value|json_encode|e('html_attr')} %} {% do assets.addJs('jquery', 101) %} diff --git a/templates/forms/fields/number/number.html.twig b/templates/forms/fields/number/number.html.twig index 0a0c14d7..116c7cab 100644 --- a/templates/forms/fields/number/number.html.twig +++ b/templates/forms/fields/number/number.html.twig @@ -1,4 +1,4 @@ -{% extends "forms/field.html.twig" %} +{% extends "forms/fields/text/text.html.twig" %} {% block input_attributes %} type="number"