diff --git a/site/plugins/form/LICENSE.txt b/site/plugins/form/LICENSE.txt
new file mode 100755
index 00000000..f628cc1e
--- /dev/null
+++ b/site/plugins/form/LICENSE.txt
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2018-2020 Sergey Romanenko
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/site/plugins/form/README.md b/site/plugins/form/README.md
new file mode 100755
index 00000000..b9024a96
--- /dev/null
+++ b/site/plugins/form/README.md
@@ -0,0 +1,8 @@
+# Form Plugin for [Flextype](http://flextype.org/)
+
+
+Form Plugin for Flextype.
+
+## LICENSE
+[The MIT License (MIT)](https://github.com/flextype/flextype/blob/master/LICENSE.txt)
+Copyright (c) 2018-2020 [Sergey Romanenko](https://github.com/Awilum)
diff --git a/site/plugins/form/app/Controllers/FormController.php b/site/plugins/form/app/Controllers/FormController.php
new file mode 100644
index 00000000..18979997
--- /dev/null
+++ b/site/plugins/form/app/Controllers/FormController.php
@@ -0,0 +1,658 @@
+ 'col w-1/12',
+ '2/12' => 'col w-2/12',
+ '3/12' => 'col w-3/12',
+ '4/12' => 'col w-4/12',
+ '5/12' => 'col w-5/12',
+ '6/12' => 'col w-6/12',
+ '7/12' => 'col w-7/12',
+ '8/12' => 'col w-8/12',
+ '9/12' => 'col w-9/12',
+ '10/12' => 'col w-10/12',
+ '12/12' => 'col w-full',
+ '12' => 'col w-full',
+ ];
+
+ /**
+ * Field class
+ *
+ * @var string
+ * @access private
+ */
+ private $field_class = 'form-control';
+
+ /**
+ * Constructor
+ *
+ * @access public
+ */
+ public function __construct($flextype)
+ {
+ $this->flextype = $flextype;
+ }
+
+ /**
+ * Render form
+ *
+ * @param array $fieldset Fieldset
+ * @param array $values Fieldset values
+ * @param Request $request PSR7 request
+ *
+ * @return string Returns form based on fieldsets
+ *
+ * @access public
+ */
+ public function render(array $fieldset, array $values = [], Request $request) : string
+ {
+ $form = Form::open(null, ['id' => 'form']);
+ $form .= $this->_csrfHiddenField();
+ $form .= $this->_actionHiddenField();
+
+ // Go through all sections
+ if (count($fieldset['sections']) > 0) {
+
+ $form .= '
';
+
+ // Go through all sections and create nav tabs
+ foreach ($fieldset['sections'] as $key => $section) {
+ $form .= '
';
+ $form .= '
';
+
+ foreach ($section['fields'] as $element => $properties) {
+ // Set empty form field element
+ $form_field = '';
+
+ // Set element name
+ $field_name = $this->getElementName($element);
+
+ // Set element id
+ $field_id = $this->getElementID($element);
+
+ // Set element default value
+ $field_value = $this->getElementValue($element, $values, $properties);
+
+ // Seletct field type
+ switch ($properties['type']) {
+ case 'textarea':
+ $form_field = $this->textareaField($field_id, $field_name, $field_value, $properties);
+ break;
+ case 'hidden':
+ $form_field = $this->hiddenField($field_id, $field_name, $field_value, $properties);
+ break;
+ case 'html':
+ $form_field = $this->htmlField($field_id, $field_name, $field_value, $properties);
+ break;
+ case 'select':
+ $form_field = $this->selectField($field_id, $field_name, $field_value, $properties);
+ break;
+ case 'template_select':
+ $form_field = $this->templateSelectField($field_id, $field_name, $field_value, $properties);
+ break;
+ case 'visibility_select':
+ $form_field = $this->visibilitySelectField($field_id, $field_name, $field_value, $properties);
+ break;
+ case 'heading':
+ $form_field = $this->headingField($field_id, $properties);
+ break;
+ case 'routable_select':
+ $form_field = $this->routableSelectField($field_id, $field_name, $field_value, $properties);
+ break;
+ case 'tags':
+ $form_field = $this->tagsField($field_id, $field_name, $field_value, $properties);
+ break;
+ case 'datetimepicker':
+ $form_field = $this->dateField($field_id, $field_name, $field_value, $properties);
+ break;
+ case 'media_select':
+ $form_field = $this->mediaSelectField($field_id, $field_name, $field_value, $properties, $request);
+ break;
+ default:
+ $form_field = $this->textField($field_id, $field_name, $field_value, $properties);
+ break;
+ }
+
+ $form .= $form_field;
+ }
+ $form .= '
';
+ $form .= '
';
+ }
+
+
+ $form .= '
';
+
+ // Go through all sections and create nav items
+ foreach ($fieldset['sections'] as $key => $section) {
+ $form .= '
' . __($section['title']) . ' ';
+ }
+
+ $form .= '
';
+
+ $form .= '
';
+ }
+
+ $form .= Form::close();
+
+ return $form;
+ }
+
+ /**
+ * Get element value
+ *
+ * @param string $element Form Element
+ * @param array $values Form Values
+ * @param array $properties Field properties
+ *
+ * @return mixed Returns form element value
+ *
+ * @access protected
+ */
+ protected function getElementValue(string $element, array $values, array $properties)
+ {
+ if (Arr::keyExists($values, $element)) {
+ $field_value = Arr::get($values, $element);
+ } elseif(Arr::keyExists($properties, 'default')) {
+ $field_value = $properties['default'];
+ } else {
+ $field_value = '';
+ }
+
+ return $field_value;
+ }
+
+ /**
+ * Get element name
+ *
+ * @param string $element Element
+ *
+ * @return string Returns form element name
+ *
+ * @access protected
+ */
+ protected function getElementName(string $element) : string
+ {
+ $pos = strpos($element, '.');
+
+ if ($pos === false) {
+ $field_name = $element;
+ } else {
+ $field_name = str_replace('.', '][', "$element") . ']';
+ }
+
+ $pos = strpos($field_name, ']');
+
+ if ($pos !== false) {
+ $field_name = substr_replace($field_name, '', $pos, strlen(']'));
+ }
+
+ return $field_name;
+ }
+
+ /**
+ * Get element id
+ *
+ * @param string $element Element
+ *
+ * @return string Returns form element id
+ *
+ * @access protected
+ */
+ protected function getElementID(string $element) : string
+ {
+ $pos = strpos($element, '.');
+
+ if ($pos === false) {
+ $field_name = $element;
+ } else {
+ $field_name = str_replace('.', '_', "$element");
+ }
+
+ return $field_name;
+ }
+
+ /**
+ * Media select field
+ *
+ * @param string $field_id Field ID
+ * @param string $field_name Field name
+ * @param mixed $field_value Field value
+ * @param array $properties Field properties
+ * @param Request $request PSR7 request
+ *
+ * @return string Returns field
+ *
+ * @access protected
+ */
+ protected function mediaSelectField(string $field_id, string $field_name, $field_value, array $properties, Request $request) : string
+ {
+ $title = isset($properties['title']) ? $properties['title'] : '';
+ $size = isset($properties['size']) ? $this->sizes[$properties['size']] : $this->sizes['12'];
+ $help = isset($properties['help']) ? $properties['help'] : '';
+ $options = $this->flextype->EntriesController->getMediaList($request->getQueryParams()['id'], false);
+
+ $attributes = isset($properties['attributes']) ? $properties['attributes'] : [];
+ $attributes['id'] = isset($attributes['id']) ? $attributes['id'] : $field_id;
+ $attributes['class'] = isset($attributes['class']) ? $attributes['class'] : $this->field_class . ' js-select';
+
+ $field = '';
+ $field .= ($title ? Form::label($field_id, __($title)) : '');
+ $field .= Form::select($field_name, $options, $field_value, $attributes);
+ $field .= ($help ? '' . __($help) . ' ' : '');
+ $field .= '
';
+
+ return $field;
+ }
+
+ /**
+ * Template select field
+ *
+ * @param string $field_id Field ID
+ * @param string $field_name Field name
+ * @param mixed $field_value Field value
+ * @param array $properties Field properties
+ *
+ * @return string Returns field
+ *
+ * @access protected
+ */
+ protected function templateSelectField(string $field_id, string $field_name, $field_value, array $properties) : string
+ {
+ $title = isset($properties['title']) ? $properties['title'] : '';
+ $size = isset($properties['size']) ? $this->sizes[$properties['size']] : $this->sizes['12'];
+ $help = isset($properties['help']) ? $properties['help'] : '';
+
+ $attributes = isset($properties['attributes']) ? $properties['attributes'] : [];
+ $attributes['id'] = isset($attributes['id']) ? $attributes['id'] : $field_id;
+ $attributes['class'] = isset($attributes['class']) ? $attributes['class'] : $this->field_class . ' js-select';
+
+ $_templates_list = $this->flextype['themes']->getTemplates($this->flextype['registry']->get('settings.theme'));
+
+ $options = [];
+
+ if (count($_templates_list) > 0) {
+ foreach ($_templates_list as $template) {
+ if ($template['type'] !== 'file' || $template['extension'] !== 'html') {
+ continue;
+ }
+
+ $options[$template['basename']] = $template['basename'];
+ }
+ }
+
+ $field = '';
+ $field .= ($title ? Form::label($field_id, __($title)) : '');
+ $field .= Form::select($field_name, $options, $field_value, $attributes);
+ $field .= ($help ? '' . __($help) . ' ' : '');
+ $field .= '
';
+
+ return $field;
+ }
+
+ /**
+ * Routable select field
+ *
+ * @param string $field_id Field ID
+ * @param string $field_name Field name
+ * @param mixed $field_value Field value
+ * @param array $properties Field properties
+ *
+ * @return string Returns field
+ *
+ * @access protected
+ */
+ protected function routableSelectField(string $field_id, string $field_name, $field_value, array $properties) : string
+ {
+ $title = isset($properties['title']) ? $properties['title'] : '';
+ $size = isset($properties['size']) ? $this->sizes[$properties['size']] : $this->sizes['12'];
+ $help = isset($properties['help']) ? $properties['help'] : '';
+ $options = [true => __('admin_yes'), false => __('admin_no')];
+
+ $attributes = isset($properties['attributes']) ? $properties['attributes'] : [];
+ $attributes['id'] = isset($attributes['id']) ? $attributes['id'] : $field_id;
+ $attributes['class'] = isset($attributes['class']) ? $attributes['class'] : $this->field_class . ' js-select';
+
+ $field = '';
+ $field .= ($title ? Form::label($field_id, __($title)) : '');
+ $field .= Form::select($field_name, $options, (is_string($field_value) ? true : ($field_value ? true : false)), $attributes);
+ $field .= ($help ? '' . __($help) . ' ' : '');
+ $field .= '
';
+
+ return $field;
+ }
+
+ /**
+ * Select field
+ *
+ * @param string $field_id Field ID
+ * @param string $field_name Field name
+ * @param mixed $field_value Field value
+ * @param array $properties Field properties
+ *
+ * @return string Returns field
+ *
+ * @access protected
+ */
+ protected function selectField(string $field_id, string $field_name, $field_value, array $properties) : string
+ {
+ $title = isset($properties['title']) ? $properties['title'] : '';
+ $size = isset($properties['size']) ? $this->sizes[$properties['size']] : $this->sizes['12'];
+ $help = isset($properties['help']) ? $properties['help'] : '';
+ $options = isset($properties['options']) ? $properties['options'] : [];
+
+ $attributes = isset($properties['attributes']) ? $properties['attributes'] : [];
+ $attributes['id'] = isset($attributes['id']) ? $attributes['id'] : $field_id;
+ $attributes['class'] = isset($attributes['class']) ? $attributes['class'] : $this->field_class . ' js-select';
+
+ $field = '';
+ $field .= ($title ? Form::label($field_id, __($title)) : '');
+ $field .= Form::select($field_name, $options, $field_value, $attributes);
+ $field .= ($help ? '' . __($help) . ' ' : '');
+ $field .= '
';
+
+ return $field;
+ }
+
+ /**
+ * Heading field
+ *
+ * @param string $field_id Field ID
+ * @param array $properties Field properties
+ *
+ * @return string Returns field
+ *
+ * @access protected
+ */
+ protected function headingField(string $field_id, array $properties) : string
+ {
+ $title = isset($properties['title']) ? $properties['title'] : '';
+ $size = isset($properties['size']) ? $this->sizes[$properties['size']] : $this->sizes['12'];
+ $h = isset($properties['h']) ? $properties['h'] : 3;
+
+ $attributes = isset($properties['attributes']) ? $properties['attributes'] : [];
+ $attributes['id'] = isset($attributes['id']) ? $attributes['id'] : $field_id;
+ $attributes['class'] = isset($attributes['class']) ? $attributes['class'] . 'text-3xl border-b border-black' : 'text-3xl border-b border-black';
+
+ $field = '';
+ $field .= Html::heading(__($title), $h, $attributes);
+ $field .= '
';
+
+ return $field;
+ }
+
+ /**
+ * Html field
+ *
+ * @param string $field_id Field ID
+ * @param string $field_name Field name
+ * @param mixed $field_value Field value
+ * @param array $properties Field properties
+ *
+ * @return string Returns field
+ *
+ * @access protected
+ */
+ protected function htmlField(string $field_id, string $field_name, $field_value, array $properties) : string
+ {
+ $title = isset($properties['title']) ? $properties['title'] : '';
+ $size = isset($properties['size']) ? $this->sizes[$properties['size']] : $this->sizes['12'];
+ $help = isset($properties['help']) ? $properties['help'] : '';
+
+ $attributes = isset($properties['attributes']) ? $properties['attributes'] : [];
+ $attributes['id'] = isset($attributes['id']) ? $attributes['id'] : $field_id;
+ $attributes['class'] = isset($attributes['class']) ? $attributes['class'] : $this->field_class;
+ $attributes['class'] .= ' js-html-editor';
+
+ $field = '';
+ $field .= ($title ? Form::label($field_id, __($title)) : '');
+ $field .= Form::textarea($field_name, $field_value, $attributes);
+ $field .= ($help ? '' . __($help) . ' ' : '');
+ $field .= '
';
+
+ return $field;
+ }
+
+ /**
+ * Hidden field
+ *
+ * @param string $field_id Field ID
+ * @param string $field_name Field name
+ * @param mixed $field_value Field value
+ * @param array $properties Field properties
+ *
+ * @return string Returns field
+ *
+ * @access protected
+ */
+ protected function hiddenField(string $field_id, string $field_name, $field_value, array $properties) : string
+ {
+ $attributes = isset($properties['attributes']) ? $properties['attributes'] : [];
+ $attributes['id'] = isset($attributes['id']) ? $attributes['id'] : $field_id;
+
+ return Form::hidden($field_name, $field_value, $attributes);
+ }
+
+ /**
+ * Textarea field
+ *
+ * @param string $field_id Field ID
+ * @param string $field_name Field name
+ * @param string $field_value Field value
+ * @param array $properties Field properties
+ *
+ * @return string Returns field
+ *
+ * @access protected
+ */
+ protected function textareaField(string $field_id, string $field_name, $field_value, array $properties) : string
+ {
+ $title = isset($properties['title']) ? $properties['title'] : '';
+ $size = isset($properties['size']) ? $this->sizes[$properties['size']] : $this->sizes['12'];
+ $help = isset($properties['help']) ? $properties['help'] : '';
+
+ $attributes = isset($properties['attributes']) ? $properties['attributes'] : [];
+ $attributes['id'] = isset($attributes['id']) ? $attributes['id'] : $field_id;
+ $attributes['class'] = isset($attributes['class']) ? $attributes['class'] : $this->field_class;
+
+ $field = '';
+ $field .= ($title ? Form::label($field_id, __($title)) : '');
+ $field .= Form::textarea($field_name, $field_value, $attributes);
+ $field .= ($help ? '' . __($help) . ' ' : '');
+ $field .= '
';
+
+ return $field;
+ }
+
+ /**
+ * Visibility field
+ *
+ * @param string $field_id Field ID
+ * @param string $field_name Field name
+ * @param mixed $field_value Field value
+ * @param array $properties Field properties
+ *
+ * @return string Returns field
+ *
+ * @access protected
+ */
+ protected function visibilitySelectField(string $field_id, string $field_name, $field_value, array $properties) : string
+ {
+ $title = isset($properties['title']) ? $properties['title'] : '';
+ $size = isset($properties['size']) ? $this->sizes[$properties['size']] : $this->sizes['12'];
+ $help = isset($properties['help']) ? $properties['help'] : '';
+ $options = ['draft' => __('admin_entries_draft'), 'visible' => __('admin_entries_visible'), 'hidden' => __('admin_entries_hidden')];
+
+ $attributes = isset($properties['attributes']) ? $properties['attributes'] : [];
+ $attributes['id'] = isset($attributes['id']) ? $attributes['id'] : $field_id;
+ $attributes['class'] = isset($attributes['class']) ? $attributes['class'] : $this->field_class . ' js-select';
+
+ $field = '';
+ $field .= ($title ? Form::label($field_id, __($title)) : '');
+ $field .= Form::select($field_name, $options, (! empty($field_value) ? $field_value : 'visible'), $attributes);
+ $field .= ($help ? '' . __($help) . ' ' : '');
+ $field .= '
';
+
+ return $field;
+ }
+
+ /**
+ * Text field
+ *
+ * @param string $field_id Field ID
+ * @param string $field_name Field name
+ * @param mixed $field_value Field value
+ * @param array $properties Field properties
+ *
+ * @return string Returns field
+ *
+ * @access protected
+ */
+ protected function textField(string $field_id, string $field_name, $field_value, array $properties) : string
+ {
+ $title = isset($properties['title']) ? $properties['title'] : '';
+ $size = isset($properties['size']) ? $this->sizes[$properties['size']] : $this->sizes['12'];
+ $help = isset($properties['help']) ? $properties['help'] : '';
+
+ $attributes = isset($properties['attributes']) ? $properties['attributes'] : [];
+ $attributes['id'] = isset($attributes['id']) ? $attributes['id'] : $field_id;
+ $attributes['class'] = isset($attributes['class']) ? $attributes['class'] : $this->field_class;
+
+ $field = '';
+ $field .= ($title ? Form::label($field_id, __($title)) : '');
+ $field .= Form::input($field_name, $field_value, $attributes);
+ $field .= ($help ? '' . __($help) . ' ' : '');
+ $field .= '
';
+
+ return $field;
+ }
+
+ /**
+ * Tags field
+ *
+ * @param string $field_id Field ID
+ * @param string $field_name Field name
+ * @param mixed $field_value Field value
+ *
+ * @return string Returns field
+ *
+ * @access protected
+ */
+ protected function tagsField(string $field_id, string $field_name, $field_value, array $properties) : string
+ {
+ $title = isset($properties['title']) ? $properties['title'] : '';
+ $size = isset($properties['size']) ? $this->sizes[$properties['size']] : $this->sizes['12'];
+ $help = isset($properties['help']) ? $properties['help'] : '';
+
+ $attributes = isset($properties['attributes']) ? $properties['attributes'] : [];
+ $attributes['id'] = isset($attributes['id']) ? $attributes['id'] : $field_id;
+ $attributes['class'] = isset($attributes['class']) ? $attributes['class'] : $this->field_class;
+
+ $field = '';
+ $field .= ($title ? Form::label($field_id, __($title)) : '');
+ $field .= ' ';
+ $field .= ($help ? '' . __($help) . ' ' : '');
+ $field .= '
';
+
+ return $field;
+ }
+
+ /**
+ * Date field
+ *
+ * @param string $field_id Field ID
+ * @param string $field_name Field name
+ * @param mixed $field_value Field value
+ * @param array $properties Field properties
+ *
+ * @return string Returns field
+ *
+ * @access protected
+ */
+ protected function dateField(string $field_id, string $field_name, $field_value, array $properties) : string
+ {
+ $title = isset($properties['title']) ? $properties['title'] : '';
+ $size = isset($properties['size']) ? $this->sizes[$properties['size']] : $this->sizes['12'];
+ $help = isset($properties['help']) ? $properties['help'] : '';
+
+ $field = '';
+
+ return $field;
+ }
+
+ /**
+ * _csrfHiddenField
+ *
+ * @return string Returns field
+ *
+ * @access protected
+ */
+ protected function _csrfHiddenField() : string
+ {
+ $field = ' ';
+ $field .= ' ';
+
+ return $field;
+ }
+
+ /**
+ * _actionHiddenField
+ *
+ * @return string Returns field
+ *
+ * @access protected
+ */
+ protected function _actionHiddenField() : string
+ {
+ return Form::hidden('action', 'save-form');
+ }
+}
diff --git a/site/plugins/form/bootstrap.php b/site/plugins/form/bootstrap.php
new file mode 100644
index 00000000..26501e6b
--- /dev/null
+++ b/site/plugins/form/bootstrap.php
@@ -0,0 +1,40 @@
+composer install');
+
+/**
+ * Register The Auto Loader
+ *
+ * Composer provides a convenient, automatically generated class loader for
+ * our application. We just need to utilize it! We'll simply require it
+ * into the script here so that we don't have to worry about manual
+ * loading any of our classes later on. It feels nice to relax.
+ * Register The Auto Loader
+ */
+$site_loader = require_once $site_autoload;
+
+/**
+ * Include web routes
+ */
+include_once 'routes/web.php';
+
+/**
+ * Include dependencies
+ */
+include_once 'dependencies.php';
diff --git a/site/plugins/form/composer.json b/site/plugins/form/composer.json
new file mode 100644
index 00000000..1ea3f327
--- /dev/null
+++ b/site/plugins/form/composer.json
@@ -0,0 +1,33 @@
+{
+ "name": "flextype-plugins/form",
+ "type": "project",
+ "description": "Form plugin for Flextype",
+ "keywords": ["form", "plugin", "flextype", "php", "html"],
+ "homepage": "https://github.com/flextype",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Sergey Romanenko",
+ "email": "hello@romanenko.digital",
+ "homepage": "http://digital.flextype.org"
+ }
+ ],
+ "support": {
+ "issues": "https://github.com/flextype/issues"
+ },
+ "require": {
+ "php": ">=7.2.0"
+ },
+ "config": {
+ "apcu-autoloader": true,
+ "optimize-autoloader": true,
+ "platform": {
+ "php": "7.2.0"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "app"
+ ]
+ }
+}
diff --git a/site/plugins/form/dependencies.php b/site/plugins/form/dependencies.php
new file mode 100644
index 00000000..f6ccdb9d
--- /dev/null
+++ b/site/plugins/form/dependencies.php
@@ -0,0 +1,19 @@
+