+It's recommended to copy the contents of the template you're going to replace and use that as a start. For instance, if
+you want to create a `MyCustomFormTemplate` copy the contents of `Form.ss` to a `MyCustomFormTemplate.ss` file and
+modify as you need.
+
+
+By default, Form and Fields follow the SilverStripe Template convention and are rendered into templates of the same
+class name (i.e EmailField will attempt to render into `EmailField.ss` and if that isn't found, `TextField.ss` or
+finally `FormField.ss`).
+
+
+While you can override all templates using normal view inheritance (i.e defining a `Form.ss`) other modules may rely on
+the core template structure. It is recommended to use `setTemplate` and unique templates for specific forms.
+
+
+For [api:FormField] instances, there are several other templates that are used on top of the main `setTemplate`.
+
+ :::php
+ $field = new TextField();
+
+ $field->setTemplate('CustomTextField');
+ // Sets the template for the '
+ //
+ // The actual FormField is rendered into the holder via the `$Field`
+ // variable.
+ //
+ // setFieldHolder() is used in most `Form` instances and needs to output
+ // labels, error messages and the like.
+
+ $field->setSmallFieldHolderTemplate('CustomTextField_Holder_Small');
+ // Sets the template for the wrapper around the text field.
+ //
+ // The difference here is the small field holder template is used when the
+ // field is embedded within another field. For example, if the field is
+ // part of a `FieldGroup` or `CompositeField` alongside other fields.
+
+All templates are rendered within the scope of the [api:FormField]. To understand more about Scope within Templates as
+well as the available syntax, see the [Templates](../templates) documentation.
+
+## Related Documentation
+
+* [How to: Create a lightweight Form](how_tos/lightweight_form)
+
+## API Documentation
+
+* [api:Form]
+* [api:FormField]
\ No newline at end of file
diff --git a/docs/en/02_Developer_Guides/03_Forms/04_Form_Security.md b/docs/en/02_Developer_Guides/03_Forms/04_Form_Security.md
new file mode 100644
index 000000000..d3947241d
--- /dev/null
+++ b/docs/en/02_Developer_Guides/03_Forms/04_Form_Security.md
@@ -0,0 +1,76 @@
+title: Form Security
+summary: Ensure Forms are secure against Cross-Site Request Forgery attacks, bots and other malicious intent.
+
+# Form Security
+
+Whenever you are accepting or asking users to input data to your application there comes an added responsibility that it
+should be done as safely as possible. Below outlines the things to consider when building your forms.
+
+## Cross-Site Request Forgery (CSRF)
+
+SilverStripe protect users against [Cross-Site Request Forgery](https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)
+(known as `CSRF`) by adding a SecurityID [api:HiddenField] to each [api:Form] instance. The `SecurityID` contains a
+random string generated by [api:SecurityToken] to identify the particular user request vs a third-party forging fake
+requests.
+
+
+For more information on Cross-Site Request Forgery, consult the [OWASP](https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)
+website.
+
+
+The `SecurityToken` automatically added looks something like:
+
+ :::php
+ $form = new Form(..);
+ echo $form->getSecurityToken()->getValue();
+
+ // 'c443076989a7f24cf6b35fe1360be8683a753e2c'
+
+This token value is passed through the rendered Form HTML as a [api:HiddenField].
+
+ :::html
+
+
+The token should be present whenever a operation has a side effect such as a `POST` operation.
+
+It can be safely disabled for `GET` requests as long as it does not modify the database (i.e a search form does not
+normally require a security token).
+
+ :::php
+ $form = new Form(..);
+ $form->disableSecurityToken();
+
+
+Do not disable the SecurityID for forms that perform some modification to the users session. This will open your
+application up to `CSRF` security holes.
+
+
+## Strict Form Submission
+
+Forms should be limited to the intended HTTP verb (mostly `GET` or `POST`) to further reduce attack exposure. Without
+this check, forms that rely on `GET` can be submitted via `POST` or `PUT` or vice-versa potentially leading to
+application errors or edge cases.
+
+ :::php
+ $form = new Form(..);
+
+ $form->setFormMethod('POST');
+ $form->setStrictFormMethodCheck(true);
+
+ // or alternative short notation..
+ $form->setFormMethod('POST', true);
+
+## Spam and Bot Attacks
+
+SilverStripe has no built-in protection for detailing with bots, captcha or other spam protection methods. This
+functionality is available as an additional [Spam Protection](https://github.com/silverstripe/silverstripe-spamprotection)
+module if required. The module provides an consistent API for allowing third-party spam protection handlers such as
+[Recaptcha](http://www.google.com/recaptcha/intro/) and [Mollom](https://mollom.com/) to work within the `Form` API.
+
+## Related Documentation
+
+* [Security](../security)
+
+## API Documentation
+
+* [api:SecurityToken]
\ No newline at end of file
diff --git a/docs/en/02_Developer_Guides/03_Forms/04_GridField.md b/docs/en/02_Developer_Guides/03_Forms/04_GridField.md
deleted file mode 100644
index 808050dd6..000000000
--- a/docs/en/02_Developer_Guides/03_Forms/04_GridField.md
+++ /dev/null
@@ -1,435 +0,0 @@
-# GridField
-
-Gridfield is SilverStripe's implementation of data grids. Its main purpose is to display tabular data
-in a format that is easy to view and modify. It can be thought of as a HTML table with some tricks.
-
-It's built in a way that provides developers with an extensible way to display tabular data in a
-table and minimise the amount of code that needs to be written.
-
-In order to quickly get data-focused UIs up and running,
-you might also be interested in the [/reference/modeladmin](ModelAdmin) class
-which is driven largely by the `GridField` class explained here.
-
-## Overview
-
-The `GridField` is a flexible form field for creating tables of data. It was introduced in
-SilverStripe 3.0 and replaced the `ComplexTableField`, `TableListField`, and `TableField` from
-previous versions of SilverStripe.
-
-Each GridField is built from a number of components. Without any components, a GridField has almost no
-functionality. The components are responsible for formatting data to be readable and also modifying it.
-
-A gridfield with only the `GridFieldDataColumn` component will display a set of read-only columns
-taken from your list, without any headers or pagination. Large datasets don't fit to one
-page, so you could add a `GridFieldPaginator` to paginatate the data. Sorting is supported by adding
- a `GridFieldSortableHeader` that enables sorting on fields that can be sorted.
-
-This document aims to explain the usage of GridFields with code examples.
-
-
-GridField can only be used with datasets that are of the type `SS_List` such as `DataList`
- or `ArrayList`
-
-
-## Creating a base GridField
-
-A gridfield is often setup from a `Controller` that will output a form to the user. Even if there
-are no other HTML input fields for gathering data from users, the gridfield itself must have a
-`Form` to support interactions with it.
-
-Here is an example where we display a basic gridfield with the default settings:
-
- :::php
- class GridController extends Page_Controller {
-
- private static $allowed_actions = array('index', 'AllPages');
-
- public function index(SS_HTTPRequest $request) {
- $this->Content = $this->AllPages();
- return $this->render();
- }
-
- public function AllPages() {
- $gridField = new GridField('pages', 'All pages', SiteTree::get());
- return new Form($this, "AllPages", new FieldList($gridField), new FieldList());
- }
- }
-
-__Note:__ This is example code and the gridfield might not be styled nicely depending on the rest of
- the css included.
-
-This gridfield will only contain a single column with the `Title` of each page. Gridfield by default
- uses the `DataObject::$display_fields` for guessing what fields to display.
-
-Instead of modifying a core `DataObject` we can tell the gridfield which fields to display by
-setting the display fields on the `GridFieldDataColumns` component.
-
- :::php
- public function AllPages() {
- $gridField = new GridField('pages', 'All pages', SiteTree::get());
- $dataColumns = $gridField->getConfig()->getComponentByType('GridFieldDataColumns');
- $dataColumns->setDisplayFields(array(
- 'Title' => 'Title',
- 'URLSegment'=> 'URL',
- 'LastEdited' => 'Changed'
- ));
- return new Form($this, "AllPages", new FieldList($gridField), new FieldList());
- }
-
-We will now move onto what the `GridFieldConfig`s are and how to use them.
-
-----
-
-## Configuration
-
-A gridfields's behaviour and look all depends on what config we're giving it. In the above example
-we did not specify one, so it picked a default config called `GridFieldConfig_Base`.
-
-A config object is a container for `GridFieldComponents` which contain the actual functionality and
-view for the gridfield.
-
-A config object can be either injected as the fourth argument of the GridField constructor,
-`$config` or set at a later stage by using a setter:
-
- :::php
- // On initialisation:
- $gridField = new GridField('pages', 'All pages', SiteTree::get(), GridFieldConfig_Base::create());
- // By a setter after initialisation:
- $gridField = new GridField('pages', 'All pages', SiteTree::get());
- $gridField->setConfig(GridFieldConfig_Base::create());
-
-By default the `[api:GridFieldConfig_Base]` constructor takes a single parameter to specify the number
-of items displayed on each page.
-
- :::php
- // I have lots of items, so increase the page size
- $myConfig = GridFieldConfig_Base::create(40);
-
-The default page size can also be tweaked via the config. (put in your mysite/_config/config.yml)
-
- :::yaml
- // For updating all gridfield defaults system wide
- GridFieldPaginator:
- default_items_per_page: 40
-
-Note that for [/reference/modeladmin](ModelAdmin) sections the default 30 number of pages can be
-controlled either by setting the base `ModelAdmin.page_length` config to the desired number, or
-by overriding this value in a custom subclass.
-
-The framework comes shipped with some base GridFieldConfigs:
-
-### Table listing with GridFieldConfig_Base
-
-A simple read-only and paginated view of records with sortable and searchable headers.
-
- :::php
- $gridField = new GridField('pages', 'All pages', SiteTree::get(), GridFieldConfig_Base::create());
-
-The fields displayed are from `DataObject::getSummaryFields()`
-
-### Viewing records with GridFieldConfig_RecordViewer
-
-Similar to `GridFieldConfig_Base` with the addition support of:
-
- - View read-only details of individual records.
-
-The fields displayed in the read-only view is from `DataObject::getCMSFields()`
-
- :::php
- $gridField = new GridField('pages', 'All pages', SiteTree::get(), GridFieldConfig_RecordViewer::create());
-
-### Editing records with GridFieldConfig_RecordEditor
-
-Similar to `GridFieldConfig_RecordViewer` with the addition support of:
-
- - Viewing and changing an individual records data.
- - Deleting a record
-
- :::php
- $gridField = new GridField('pages', 'All pages', SiteTree::get(), GridFieldConfig_RecordEditor::create());
-
-The fields displayed in the edit form are from `DataObject::getCMSFields()`
-
-### Editing relations with GridFieldConfig_RelationEditor
-
-Similar to `GridFieldConfig_RecordEditor`, but adds features to work on a record's has-many or
-many-many relationships. As such, it expects the list used with the `GridField` to be a
-`RelationList`. That is, the list returned by a has-many or many-many getter.
-
-The relations can be:
-
-- Searched for existing records and add a relationship
-- Detach records from the relationship (rather than removing them from the database)
-- Create new related records and automatically add them to the relationship.
-
- :::php
- $gridField = new GridField('images', 'Linked images', $this->Images(), GridFieldConfig_RelationEditor::create());
-
-The fields displayed in the edit form are from `DataObject::getCMSFields()`
-
-## Customizing Detail Forms
-
-The `GridFieldDetailForm` component drives the record editing form which is usually configured
-through the configs `GridFieldConfig_RecordEditor` and `GridFieldConfig_RelationEditor`
-described above. It takes its fields from `DataObject->getCMSFields()`,
-but can be customized to accept different fields via its `[api:GridFieldDetailForm->setFields()]` method.
-
-The component also has the ability to load and save data stored on join tables
-when two records are related via a "many_many" relationship, as defined through
-`[api:DataObject::$many_many_extraFields]`. While loading and saving works transparently,
-you need to add the necessary fields manually, they're not included in the `getCMSFields()` scaffolding.
-
-These extra fields act like usual form fields, but need to be "namespaced"
-in order for the gridfield logic to detect them as fields for relation extradata,
-and to avoid clashes with the other form fields.
-The namespace notation is `ManyMany[
]`, so for example
-`ManyMany[MyExtraField]`.
-
-Example:
-
- :::php
-
- class Player extends DataObject {
- private static $db = array('Name' => 'Text');
- public static $many_many = array('Teams' => 'Team');
- public static $many_many_extraFields = array(
- 'Teams' => array('Position' => 'Text')
- );
- public function getCMSFields() {
- $fields = parent::getCMSFields();
-
- if($this->ID) {
- $teamFields = singleton('Team')->getCMSFields();
- $teamFields->addFieldToTab(
- 'Root.Main',
- // Please follow the "ManyMany[]" convention
- new TextField('ManyMany[Position]', 'Current Position')
- );
- $config = GridFieldConfig_RelationEditor::create();
- $config->getComponentByType('GridFieldDetailForm')->setFields($teamFields);
- $gridField = new GridField('Teams', 'Teams', $this->Teams(), $config);
- $fields->findOrMakeTab('Root.Teams')->replaceField('Teams', $gridField);
- }
-
- return $fields;
- }
- }
-
- class Team extends DataObject {
- private static $db = array('Name' => 'Text');
- public static $many_many = array('Players' => 'Player');
- }
-
-## GridFieldComponents
-
-The `GridFieldComponent` classes are the actual workers in a gridfield. They can be responsible for:
-
- - Output some HTML to be rendered
- - Manipulate data
- - Recieve actions
- - Display links
-
-Components are added and removed from a config by setters and getters.
-
- :::php
- $config = GridFieldConfig::create();
-
- // Add the base data columns to the gridfield
- $config->addComponent(new GridFieldDataColumns());
- $gridField = new GridField('pages', 'All pages', SiteTree::get(), $config);
-
-It's also possible to insert a component before another component.
-
- :::php
- $config->addComponent(new GridFieldFilterHeader(), 'GridFieldDataColumns');
-
-Adding multiple components in one call:
-
- :::php
- $config->addComponents(new GridFieldDataColumns(), new GridFieldToolbarHeader());
-
-Removing a component:
-
- :::php
- $config->removeComponentsByType('GridFieldToolbarHeader');
-
-For more information, see the [API for GridFieldConfig](http://api.silverstripe.org/3.0/framework/GridFieldConfig.html).
-
-Here is a list of components for generic use:
-
- - `[api:GridFieldToolbarHeader]`
- - `[api:GridFieldSortableHeader]`
- - `[api:GridFieldFilterHeader]`
- - `[api:GridFieldDataColumns]`
- - `[api:GridFieldDeleteAction]`
- - `[api:GridFieldViewButton]`
- - `[api:GridFieldEditButton]`
- - `[api:GridFieldExportButton]`
- - `[api:GridFieldPrintButton]`
- - `[api:GridFieldPaginator]`
- - `[api:GridFieldDetailForm]`
-
-## Flexible Area Assignment through Fragments
-
-GridField layouts can contain many components other than the table itself,
-for example a search bar to find existing relations, a button to add those,
-and buttons to export and print the current data. The GridField has certain
-defined areas called "fragments" where these components can be placed.
-The goal is for multiple components to share the same space, for example a header row.
-
-Built-in components:
-
- - `header`/`footer`: Renders in a ``/` `, should contain table markup
- - `before`/`after`: Renders before/after the actual ``
- - `buttons-before-left`/`buttons-before-right`/`buttons-after-left`/`buttons-after-right`:
- Renders in a shared row before the table. Requires [api:GridFieldButtonRow].
-
-These built-ins can be used by passing the fragment names into the constructor
-of various components. Note that some [api:GridFieldConfig] classes
-will already have rows added to them. The following example will add a print button
-at the bottom right of the table.
-
- :::php
- $config->addComponent(new GridFieldButtonRow('after'));
- $config->addComponent(new GridFieldPrintButton('buttons-after-right'));
-
-Further down we'll explain how to write your own components using fragments.
-
-## Creating a custom GridFieldComponent
-
-A single component often uses a number of interfaces.
-
-### GridField_HTMLProvider
-
-Provides HTML for the header/footer rows in the table or before/after the template.
-
-Examples:
-
- - A header html provider displays a header before the table
- - A pagination html provider displays pagination controls under the table
- - A filter html fields displays filter fields on top of the table
- - A summary html field displays sums of a field at the bottom of the table
-
-### GridField_ColumnProvider
-
-Add a new column to the table display body, or modify existing columns. Used once per record/row.
-
-Examples:
-
- - A data columns provider that displays data from the list in rows and columns.
- - A delete button column provider that adds a delete button at the end of the row
-
-### GridField_ActionProvider
-
-Action providers runs actions, some examples are:
-
- - A delete action provider that deletes a DataObject.
- - An export action provider that will export the current list to a CSV file.
-
-### GridField_DataManipulator
-
-Modifies the data list. In general, the data manipulator will make use of `GridState` variables
-to decide how to modify the data list.
-
-Examples:
-
- - A paginating data manipulator can apply a limit to a list (show only 20 records)
- - A sorting data manipulator can sort the Title in a descending order.
-
-### GridField_URLHandler
-
-Sometimes an action isn't enough, we need to provide additional support URLs for the grid. It
-has a list of URL's that it can handle and the GridField passes request on to URLHandlers on matches.
-
-Examples:
-
- - A pop-up form for editing a record's details.
- - JSON formatted data used for javascript control of the gridfield.
-
-## GridField_FormAction
-
-This object is used for creating actions buttons, for example a delete button. When a user clicks on
-a FormAction, the gridfield finds a `GridField_ActionProvider` that listens on that action.
-`GridFieldDeleteAction` have a pretty basic implementation of how to use a Form action.
-
-## GridField_SaveHandler
-
-This is used to create a handler that is called when a form containing the grid
-field is saved into a record. This is useful for performing actions when saving
-the record.
-
-### GridState
-
-Gridstate is a class that is used to contain the current state and actions on the gridfield. It's
-transfered between page requests by being inserted as a hidden field in the form.
-
-A GridFieldComponent sets and gets data from the GridState.
-
-Data within this object can be nested, allowing for organisation of information in a logical fashion. Additionally,
-default values can be specified for any data field by invoking that field as a method, passing the default value
-as the first parameter. If no default is specified then a nested `GridState_Data` is returned, which may be chained
-to subsequently nest data values.
-
-Example:
-
- :::php
-
- public function getManipulatedData(GridField $gridField, SS_List $dataList) {
- // Accesses the GridState, returns a nested GridState_Data, and retrieve a single field with a default of null
- $objectID = $gridField->State->MyComponent->RecordID(null);
- if($objectID) $dataList = $dataList->filter('ParentID', $objectID);
- return $dataList;
- }
-
-
-## Permissions
-
-Since GridField is mostly used in the CMS, the controller managing a GridField instance
-will already do some permission checks for you, and can decline display or executing
-any logic on your field.
-
-If you need more granular control, e.g. to consistently deny non-admins from deleting
-records, use the `DataObject->can...()` methods
-(see [DataObject permissions](/reference/dataobject#permissions)).
-
-## Creating your own Fragments
-
-Fragments are designated areas within a GridField which can be shared between component templates.
-You can define your own fragments by using a `\$DefineFragment' placeholder in your components' template.
-This example will simply create an area rendered before the table wrapped in a simple ``.
-
- :::php
- class MyAreaComponent implements GridField_HTMLProvider {
- public function getHTMLFragments( $gridField) {
- return array(
- 'before' => '
$DefineFragment(my-area)
'
- );
- }
- }
-
-We're returning raw HTML from the component, usually this would be handled by a SilverStripe template.
-Please note that in templates, you'll need to escape the dollar sign on `$DefineFragment`:
-These are specially processed placeholders as opposed to native template syntax.
-
-Now you can add other components into this area by returning them as an array from
-your [api:GridFieldComponent->getHTMLFragments()] implementation:
-
- :::php
- class MyShareLinkComponent implements GridField_HTMLProvider {
- public function getHTMLFragments( $gridField) {
- return array(
- 'my-area' => '
... '
- );
- }
- }
-
-Your new area can also be used by existing components, e.g. the [api:GridFieldPrintButton]
-
- :::php
- new GridFieldPrintButton('my-component-area')
-
-## Related
-
- * [ModelAdmin: A UI driven by GridField](/reference/modeladmin)
- * [Tutorial 5: Dataobject Relationship Management](/tutorials/5-dataobject-relationship-management)
- * [How to add a custom action to a GridField row](/howto/gridfield-rowaction)
diff --git a/docs/en/02_Developer_Guides/03_Forms/05_Form_Transformations.md b/docs/en/02_Developer_Guides/03_Forms/05_Form_Transformations.md
new file mode 100644
index 000000000..f2eb38c7b
--- /dev/null
+++ b/docs/en/02_Developer_Guides/03_Forms/05_Form_Transformations.md
@@ -0,0 +1,53 @@
+title: Form Transformations
+summary: Provide read-only and disabled views of your Form data.
+
+# Read-only and Disabled Forms
+
+[api:Form] and [api:FormField] instances can be turned into a read-only version for things like confirmation pages or
+when certain fields cannot be edited due to permissions. Creating the form is done the same way and markup is similar,
+`readonly` mode converts the `input`, `select` and `textarea` tags to static HTML elements like `span`.
+
+To make an entire [api:Form] read-only.
+
+ :::php
+ $form = new Form(..);
+ $form->makeReadonly();
+
+
+To make all the fields within a [api:FieldList] read-only (i.e to make fields read-only but not buttons).
+
+ :::php
+ $fields = new FieldList(..);
+ $fields = $fields->makeReadonly();
+
+
+To make a [api:FormField] read-only you need to know the name of the form field or call it direct on the object
+
+ :::php
+ $field = new TextField(..);
+ $field = $field->performReadonlyTransformation();
+
+ $fields = new FieldList(
+ $field
+ );
+
+ // Or,
+ $field = new TextField(..);
+ $field->setReadonly(true);
+
+ $fields = new FieldList(
+ $field
+ );
+
+## Disabled FormFields
+
+Disabling [api:FormField] instances, sets the `disabled` property on the class. This will use the same HTML markup as
+a normal form, but set the `disabled` attribute on the `input` tag.
+
+ :::php
+ $field = new TextField(..);
+ $field->setDisabled(true);
+
+ echo $field->forTemplate();
+
+ // returns '
'
\ No newline at end of file
diff --git a/docs/en/02_Developer_Guides/03_Forms/06_Tabbed_Forms.md b/docs/en/02_Developer_Guides/03_Forms/06_Tabbed_Forms.md
new file mode 100644
index 000000000..c958749a8
--- /dev/null
+++ b/docs/en/02_Developer_Guides/03_Forms/06_Tabbed_Forms.md
@@ -0,0 +1,55 @@
+title: Tabbed Forms
+summary: Find out how CMS interfaces use jQuery UI tabs to provide nested FormFields.
+
+# Tabbed Forms
+
+SilverStripe's [api:FormScaffolder] can automatically generate [api:Form] instances for certain database models. In the
+CMS and other scaffolded interfaces, it will output [api:TabSet] and [api:Tab] objects and use jQuery Tabs to split
+parts of the data model.
+
+
+All interfaces within the CMS such as [api:ModelAdmin] and [api:LeftAndMain] use tabbed interfaces by default.
+
+
+When dealing with tabbed forms, modifying the fields in the form has a few differences. Each [api:Tab] will be given a
+name, and normally they all exist under the `Root` [api:TabSet].
+
+
+[api:TabSet] instances can contain child [api:Tab] and further [api:TabSet] instances, however the CMS UI will only
+display up to two levels of tabs in the interface. If you want to group data further than that, try [api:ToggleField].
+
+
+## Adding a field from a tab.
+
+ :::php
+ $fields->addFieldToTab('Root.Main', new TextField(..));
+
+## Removing a field from a tab
+
+ :::php
+ $fields->removeFieldFromTab('Root.Main', 'Content');
+
+## Creating a new tab
+
+ :::php
+ $fields->addFieldToTab('Root.MyNewTab', new TextField(..));
+
+## Moving a field between tabs
+
+ :::php
+ $field = $fields->dataFieldByName('Content');
+
+ $fields->removeFieldFromTab('Root.Main', 'Content');
+ $fields->addFieldToTab('Root.MyContent', $content);
+
+## Add multiple fields at once
+
+ :::php
+ $fields->addFieldsToTab('Root.Content', array(
+ TextField::create('Name'),
+ TextField::create('Email')
+ ));
+
+## API Documentation
+
+* [api:FormScaffolder]
\ No newline at end of file
diff --git a/docs/en/02_Developer_Guides/03_Forms/01_Form_Field_Types.md b/docs/en/02_Developer_Guides/03_Forms/Fields/01_Common_Subclasses.md
similarity index 93%
rename from docs/en/02_Developer_Guides/03_Forms/01_Form_Field_Types.md
rename to docs/en/02_Developer_Guides/03_Forms/Fields/01_Common_Subclasses.md
index b47a57d59..908cfb618 100644
--- a/docs/en/02_Developer_Guides/03_Forms/01_Form_Field_Types.md
+++ b/docs/en/02_Developer_Guides/03_Forms/Fields/01_Common_Subclasses.md
@@ -1,6 +1,10 @@
+title: Common FormField Types
+summary: A table containing a list of the common FormField subclasses.
+
# Form Field Types
-This is a highlevel overview of available `[api:FormField]` subclasses. An automatically generated list is available through our [API]
+This is a highlevel overview of available `[api:FormField]` subclasses. An automatically generated list is available
+on the SilverStripe API documentation.
## Basic
@@ -28,7 +32,7 @@ This is a highlevel overview of available `[api:FormField]` subclasses. An autom
* `[api:DatetimeField]`: Combined date- and time field.
* `[api:EmailField]`: Text input field with validation for correct email format according to RFC 2822.
* `[api:GroupedDropdownField]`: Grouped dropdown, using
tags.
- * `[api:HtmlEditorField]`.
+ * `[api:HtmlEditorField]`: A WYSIWYG editor interface.
* `[api:MoneyField]`: A form field that can save into a `[api:Money]` database field.
* `[api:NumericField]`: Text input field with validation for numeric values.
* `[api:OptionsetField]`: Set of radio buttons designed to emulate a dropdown.
@@ -66,6 +70,6 @@ doesn't necessarily have any visible styling.
* `[api:DatalessField]` - Base class for fields which add some HTML to the form but don't submit any data or
save it to the database
* `[api:HeaderField]`: Renders a simple HTML header element.
- * `[api:HiddenField]`.
+ * `[api:HiddenField]` - Renders a hidden input field.
* `[api:LabelField]`: Simple label tag. This can be used to add extra text in your forms.
* `[api:LiteralField]`: Renders arbitrary HTML into a form.
diff --git a/docs/en/02_Developer_Guides/03_Forms/Fields/02_DateField.md b/docs/en/02_Developer_Guides/03_Forms/Fields/02_DateField.md
new file mode 100644
index 000000000..560f6857d
--- /dev/null
+++ b/docs/en/02_Developer_Guides/03_Forms/Fields/02_DateField.md
@@ -0,0 +1,130 @@
+title: DateField
+summary: How to format and use the DateField class.
+
+# DateField
+
+This `FormField` subclass lets you display an editable date, either in a single text input field, or in three separate
+fields for day, month and year. It also provides a calendar date picker.
+
+The following example will add a simple DateField to your Page, allowing you to enter a date manually.
+
+**mysite/code/Page.php**
+
+ :::php
+ 'Date',
+ );
+
+ public function getCMSFields() {
+ $fields = parent::getCMSFields();
+
+ $fields->addFieldToTab(
+ 'Root.Main',
+ DateField::create('MyDate', 'Enter a date')
+ );
+
+ return $fields;
+ }
+ }
+
+## Custom Date Format
+
+A custom date format for a [api:DateField] can be provided through `setConfig`.
+
+ :::php
+ // will display a date in the following format: 31-06-2012
+ DateField::create('MyDate')->setConfig('dateformat', 'dd-MM-yyyy');
+
+
+The formats are based on [Zend_Date constants](http://framework.zend.com/manual/1.12/en/zend.date.constants.html).
+
+
+
+## Min and Max Dates
+
+Sets the minimum and maximum allowed date values using the `min` and `max` configuration settings (in ISO format or
+strtotime()).
+
+ :::php
+ DateField::create('MyDate')
+ ->setConfig('min', '-7 days')
+ ->setConfig('max', '2012-12-31')
+
+## Separate Day / Month / Year Fields
+
+The following setting will display your DateField as three input fields for day, month and year separately. HTML5
+placeholders 'day', 'month' and 'year' are enabled by default.
+
+ :::php
+ DateField::create('MyDate')
+ ->setConfig('dmyfields', true)
+ ->setConfig('dmyseparator', '/') // set the separator
+ ->setConfig('dmyplaceholders', 'true'); // enable HTML 5 Placeholders
+
+
+Any custom date format settings will be ignored.
+
+
+## Calendar Picker
+
+The following setting will add a Calendar to a single DateField, using the jQuery UI DatePicker widget.
+
+ :::php
+ DateField::create('MyDate')
+ ->setConfig('showcalendar', true);
+
+The jQuery DatePicker doesn't support every constant available for `Zend_Date`. If you choose to use the calendar, the
+following constants should at least be safe:
+
+Constant | xxxxx
+-------- | -----
+d | numeric day of the month (without leading zero)
+dd | numeric day of the month (with leading zero)
+EEE | dayname, abbreviated
+EEEE | dayname
+M | numeric month of the year (without leading zero)
+MM | numeric month of the year (with leading zero)
+MMM | monthname, abbreviated
+MMMM | monthname
+y | year (4 digits)
+yy | year (2 digits)
+yyyy | year (4 digits)
+
+Unfortunately the day- and monthname values in Zend Date do not always match those in the existing jQuery UI locale
+files, so constants like `EEE` or `MMM`, for day and month names could break validation. To fix this we had to slightly
+alter the jQuery locale files, situated in */framework/thirdparty/jquery-ui/datepicker/i18n/*, to match Zend_Date.
+
+
+At this moment not all locale files may be present. If a locale file is missing, the DatePicker calendar will fallback
+to 'yyyy-MM-dd' whenever day - and/or monthnames are used. After saving, the correct format will be displayed.
+
+
+## Formatting Hints
+
+It's often not immediate apparent which format a field accepts, and showing the technical format (e.g. `HH:mm:ss`) is
+of limited use to the average user. An alternative is to show the current date in the desired format alongside the
+field description as an example.
+
+ :::php
+ $dateField = DateField::create('MyDate');
+
+ // Show long format as text below the field
+ $dateField->setDescription(sprintf(
+ _t('FormField.Example', 'e.g. %s', 'Example format'),
+ Convert::raw2xml(Zend_Date::now()->toString($dateField->getConfig('dateformat')))
+ ));
+
+ // Alternatively, set short format as a placeholder in the field
+ $dateField->setAttribute('placeholder', $dateField->getConfig('dateformat'));
+
+
+Fields scaffolded through [api:DataObject::scaffoldCMSFields] automatically have a description attached to them.
+
+
+## API Documentation
+
+* [api:DateField]
\ No newline at end of file
diff --git a/docs/en/02_Developer_Guides/03_Forms/Fields/04_GridField.md b/docs/en/02_Developer_Guides/03_Forms/Fields/04_GridField.md
new file mode 100644
index 000000000..3f5eac5f4
--- /dev/null
+++ b/docs/en/02_Developer_Guides/03_Forms/Fields/04_GridField.md
@@ -0,0 +1,394 @@
+title: GridField
+summary: How to use the GridField class for managing tabular data.
+
+# GridField
+
+[api:GridField] is SilverStripe's implementation of data grids. The main purpose of the `FormField` is to display
+tabular data in a format that is easy to view and modify. It can be thought of as a HTML table with some tricks.
+
+ :::php
+ $field = new GridField($name, $title, $list);
+
+
+
+
+GridField can only be used with `$list` data sets that are of the type `SS_List` such as `DataList` or `ArrayList`.
+
+
+
+[api:GridField] powers the automated data UI of [api:ModelAdmin]. For more information about `ModelAdmin` see the
+[Customizing the CMS](../../customizing_the_cms) guide.
+
+
+Each `GridField` is built from a number of components grouped into the [api:GridFieldConfig]. Without any components,
+a `GridField` has almost no functionality. The `GridFieldConfig` instance and the attached [api:GridFieldComponent] are
+responsible for all the user interactions including formatting data to be readable, modifying data and performing any
+actions such as deleting records.
+
+**mysite/code/Page.php**
+
+ :::php
+ addFieldToTab('Root.Pages',
+ new GridField('Pages', 'All pages', SiteTree::get())
+ );
+
+ return $fields;
+ }
+ }
+
+This will display a bare bones `GridField` instance under `Pages` tab in the CMS. As we have not specified the
+`GridField` configuration, the default configuration is an instance of [api:GridFieldConfig_Base] which provides:
+
+ * [api:GridFieldToolbarHeader]
+ * [api:GridFieldSortableHeader]
+ * [api:GridFieldFilterHeader]
+ * [api:GridFieldDataColumns]
+ * [api:GridFieldPageCount]
+ * [api:GridFieldPaginator]
+
+The configuration of those `GridFieldComponent` instances and the addition or subtraction of components is done through
+the `getConfig()` method on `GridField`.
+
+**mysite/code/Page.php**
+
+ :::php
+ addFieldToTab('Root.Pages',
+ $grid = GridField('Pages', 'All pages', SiteTree::get())
+ );
+
+ // GridField configuration
+ $config = $gridField->getConfig();
+
+ //
+ // Modification of existing components can be done by fetching that component.
+ // Consult the API documentation for each component to determine the configuration
+ // you can do.
+ //
+ $dataColumns = $config->getComponentByType('GridFieldDataColumns');
+
+ $dataColumns->setDisplayFields(array(
+ 'Title' => 'Title',
+ 'Link'=> 'URL',
+ 'LastEdited' => 'Changed'
+ ));
+
+ return $fields;
+ }
+ }
+
+
+With the `GridFieldConfig` instance, we can modify the behavior of the `GridField`.
+
+ :::php
+ // `GridFieldConfig::create()` will create an empty configuration (no components).
+ $config = GridFieldConfig::create();
+
+ // add a component
+ $config->addComponent(new GridFieldDataColumns());
+
+ // Update the GridField with our custom configuration
+ $gridField->setConfig($config);
+
+`GridFieldConfig` provides a number of methods to make setting the configuration easier. We can insert a component
+before another component by passing the second parameter.
+
+ :::php
+ $config->addComponent(new GridFieldFilterHeader(), 'GridFieldDataColumns');
+
+We can add multiple components in one call.
+
+ :::php
+ $config->addComponents(
+ new GridFieldDataColumns(),
+ new GridFieldToolbarHeader()
+ );
+
+Or, remove a component.
+
+ :::php
+ $config->removeComponentsByType('GridFieldDeleteAction');
+
+Fetch a component to modify it later on.
+
+ :::php
+ $component = $config->getComponentByType('GridFieldFilterHeader')
+
+
+Here is a list of components for use bundled with the core framework. Many more components are provided by third-party
+modules and extensions.
+
+ - [api:GridFieldToolbarHeader]
+ - [api:GridFieldSortableHeader]
+ - [api:GridFieldFilterHeader]
+ - [api:GridFieldDataColumns]
+ - [api:GridFieldDeleteAction]
+ - [api:GridFieldViewButton]
+ - [api:GridFieldEditButton]
+ - [api:GridFieldExportButton]
+ - [api:GridFieldPrintButton]
+ - [api:GridFieldPaginator]
+ - [api:GridFieldDetailForm]
+
+## Bundled GridFieldConfig
+
+As a shortcut, `GridFieldConfig` subclasses can define a list of `GridFieldComponent` objects to use. This saves
+developers manually adding each component.
+
+### GridFieldConfig_Base
+
+A simple read-only and paginated view of records with sortable and searchable headers.
+
+ :::php
+ $config = GridFieldConfig_Base::create();
+
+ $gridField->setConfig($config);
+
+ // Is the same as adding the following components..
+ // .. new GridFieldToolbarHeader()
+ // .. new GridFieldSortableHeader()
+ // .. new GridFieldFilterHeader()
+ // .. new GridFieldDataColumns()
+ // .. new GridFieldPageCount('toolbar-header-right')
+ // .. new GridFieldPaginator($itemsPerPage)
+
+### GridFieldConfig_RecordViewer
+
+Similar to `GridFieldConfig_Base` with the addition support of the ability to view a `GridFieldDetailForm` containing
+a read-only view of the data record.
+
+
+The data row show must be a `DataObject` subclass. The fields displayed in the read-only view come from
+`DataObject::getCMSFields()`.
+
+
+
+The `DataObject` class displayed must define a `canView()` method that returns a boolean on whether the user can view
+this record.
+
+
+ :::php
+ $config = GridFieldConfig_RecordViewer::create();
+
+ $gridField->setConfig($config);
+
+ // Same as GridFieldConfig_Base with the addition of
+ // .. new GridFieldViewButton(),
+ // .. new GridFieldDetailForm()
+
+### GridFieldConfig_RecordEditor
+
+Similar to `GridFieldConfig_RecordViewer` with the addition support to edit or delete each of the records.
+
+
+The data row show must be a `DataObject` subclass. The fields displayed in the edit view come from
+`DataObject::getCMSFields()`.
+
+
+
+Permission control for editing and deleting the record uses the `canEdit()` and `canDelete()` methods on the
+`DataObject` object.
+
+
+ :::php
+ $config = GridFieldConfig_RecordEditor::create();
+
+ $gridField->setConfig($config);
+
+ // Same as GridFieldConfig_RecordViewer with the addition of
+ // .. new GridFieldAddNewButton(),
+ // .. new GridFieldEditButton(),
+ // .. new GridFieldDeleteAction()
+
+### GridFieldConfig_RelationEditor
+
+Similar to `GridFieldConfig_RecordEditor`, but adds features to work on a record's has-many or many-many relationships.
+As such, it expects the list used with the `GridField` to be a instance of `RelationList`.
+
+ :::php
+ $config = GridFieldConfig_RelationEditor::create();
+
+ $gridField->setConfig($config);
+
+This configuration adds the ability to searched for existing records and add a relationship
+(`GridFieldAddExistingAutocompleter`).
+
+Records created or deleted through the `GridFieldConfig_RelationEditor` automatically update the relationship in the
+database.
+
+## GridFieldDetailForm
+
+The `GridFieldDetailForm` component drives the record viewing and editing form. It takes its' fields from
+`DataObject->getCMSFields()` method but can be customized to accept different fields via the
+[api:GridFieldDetailForm->setFields] method.
+
+ :::php
+ $form = $gridField->getConfig()->getComponentByType('GridFieldDetailForm');
+ $form->setFields(new FieldList(
+ new TextField('Title')
+ ));
+
+### many_many_extraFields
+
+The component also has the ability to load and save data stored on join tables when two records are related via a
+"many_many" relationship, as defined through [api:DataObject::$many_many_extraFields]. While loading and saving works
+transparently, you need to add the necessary fields manually, they're not included in the `getCMSFields()` scaffolding.
+
+These extra fields act like usual form fields, but need to be "namespaced" in order for the `GridField` logic to detect
+them as fields for relation extra data, and to avoid clashes with the other form fields.
+
+The namespace notation is `ManyMany[]`, so for example `ManyMany[MyExtraField]`.
+
+ :::php
+ 'Text'
+ );
+
+ public static $many_many = array(
+ 'Players' => 'Player'
+ );
+ }
+
+ class Player extends DataObject {
+
+ private static $db = array(
+ 'Name' => 'Text'
+ );
+
+ public static $many_many = array(
+ 'Teams' => 'Team'
+ );
+
+ public static $many_many_extraFields = array(
+ 'Teams' => array(
+ 'Position' => 'Text'
+ )
+ );
+
+ public function getCMSFields() {
+ $fields = parent::getCMSFields();
+
+ if($this->ID) {
+ $teamFields = singleton('Team')->getCMSFields();
+ $teamFields->addFieldToTab(
+ 'Root.Main',
+ // The "ManyMany[]" convention
+ new TextField('ManyMany[Position]', 'Current Position')
+ );
+
+ $config = GridFieldConfig_RelationEditor::create();
+ $config->getComponentByType('GridFieldDetailForm')->setFields($teamFields);
+
+ $gridField = new GridField('Teams', 'Teams', $this->Teams(), $config);
+ $fields->findOrMakeTab('Root.Teams')->replaceField('Teams', $gridField);
+ }
+
+ return $fields;
+ }
+ }
+
+
+## Flexible Area Assignment through Fragments
+
+`GridField` layouts can contain many components other than the table itself, for example a search bar to find existing
+relations, a button to add those, and buttons to export and print the current data. The `GridField` has certain defined
+areas called `fragments` where these components can be placed.
+
+The goal is for multiple components to share the same space, for example a header row. The built-in components:
+
+ - `header`/`footer`: Renders in a ``/` `, should contain table markup
+ - `before`/`after`: Renders before/after the actual ``
+ - `buttons-before-left`/`buttons-before-right`/`buttons-after-left`/`buttons-after-right`:
+ Renders in a shared row before the table. Requires [api:GridFieldButtonRow].
+
+These built-ins can be used by passing the fragment names into the constructor of various components. Note that some
+[api:GridFieldConfig] classes will already have rows added to them. The following example will add a print button at the
+bottom right of the table.
+
+ :::php
+ $config->addComponent(new GridFieldButtonRow('after'));
+ $config->addComponent(new GridFieldPrintButton('buttons-after-right'));
+
+
+### Creating your own Fragments
+
+Fragments are designated areas within a `GridField` which can be shared between component templates. You can define
+your own fragments by using a `\$DefineFragment' placeholder in your components' template. This example will simply
+create an area rendered before the table wrapped in a simple ``.
+
+ :::php
+ '
$DefineFragment(my-area)
'
+ );
+ }
+ }
+
+
+Please note that in templates, you'll need to escape the dollar sign on `\$DefineFragment`. These are specially
+processed placeholders as opposed to native template syntax.
+
+
+Now you can add other components into this area by returning them as an array from your
+[api:GridFieldComponent->getHTMLFragments()] implementation:
+
+ :::php
+ '
... '
+ );
+ }
+ }
+
+Your new area can also be used by existing components, e.g. the [api:GridFieldPrintButton]
+
+ :::php
+ new GridFieldPrintButton('my-component-area');
+
+## Creating a Custom GridFieldComponent
+
+Customizing a `GridField` is easy, applications and modules can provide their own `GridFieldComponent` instances to add
+functionality. See [How to Create a GridFieldComponent](../how_tos/create_a_gridfield_component).
+
+## Creating a Custom GridField_ActionProvider
+
+[api:GridField_ActionProvider] provides row level actions such as deleting a record. See
+[How to Create a GridField_ActionProvider](../how_tos/create_a_gridfield_actionprovider).
+
+## Saving the GridField State
+
+`GridState` is a class that is used to contain the current state and actions on the `GridField`. It's transfered
+between page requests by being inserted as a hidden field in the form.
+
+The `GridState_Component` sets and gets data from the `GridState`.
+
+## API Documentation
+
+ * [api:GridField]
+ * [api:GridFieldConfig]
+ * [api:GridFieldComponent]
diff --git a/docs/en/02_Developer_Guides/03_Forms/How_Tos/01_Encapsulate_Forms.md b/docs/en/02_Developer_Guides/03_Forms/How_Tos/01_Encapsulate_Forms.md
new file mode 100644
index 000000000..ac1c3b67a
--- /dev/null
+++ b/docs/en/02_Developer_Guides/03_Forms/How_Tos/01_Encapsulate_Forms.md
@@ -0,0 +1,151 @@
+title: How to Encapsulate Forms
+
+# How to Encapsulate Forms
+
+Form definitions can often get long, complex and often end up cluttering up a `Controller` definition. We may also want
+to reuse the `Form` across multiple `Controller` classes rather than just one. A nice way to encapsulate the logic and
+code for a `Form` is to create it as a subclass to `Form`. Let's look at a example of a `Form` which is on our
+`Controller` but would be better written as a subclass.
+
+**mysite/code/Page.php**
+
+ :::php
+ 'Search Foo',
+ 'bar' => 'Search Bar',
+ 'baz' => 'Search Baz'
+ )),
+
+ CompositeField::create(
+ HeaderField::create('Header2', 'Step 2. Advanced '),
+ CheckboxSetField::create('Foo', 'Select Option', array(
+ 'qux' => 'Search Qux'
+ )),
+
+ CheckboxSetField::create('Category', 'Category', array(
+ 'Foo' => 'Foo',
+ 'Bar' => 'Bar'
+ )),
+
+ NumericField::create('Minimum', 'Minimum'),
+ NumericField::create('Maximum', 'Maximum')
+ )
+ );
+
+ $actions = new FieldList(
+ FormAction::create('doSearchForm', 'Search')
+ );
+
+ $required = new RequiredFields(array(
+ 'Type'
+ ));
+
+ $form = new Form($this, 'SearchForm', $fields, $actions, $required);
+ $form->setFormMethod('GET');
+
+ $form->addExtraClass('no-action-styles');
+ $form->disableSecurityToken();
+ $form->loadDataFrom($_REQUEST);
+
+ return $form;
+ }
+
+ ..
+ }
+
+Now that is a bit of code to include on our controller and generally makes the file look much more complex than it
+should be. Good practice would be to move this to a subclass and create a new instance for your particular controller.
+
+**mysite/code/forms/SearchForm.php**
+
+ :::php
+ 'Search Foo',
+ 'bar' => 'Search Bar',
+ 'baz' => 'Search Baz'
+ )),
+
+ CompositeField::create(
+ HeaderField::create('Header2', 'Step 2. Advanced '),
+ CheckboxSetField::create('Foo', 'Select Option', array(
+ 'qux' => 'Search Qux'
+ )),
+
+ CheckboxSetField::create('Category', 'Category', array(
+ 'Foo' => 'Foo',
+ 'Bar' => 'Bar'
+ )),
+
+ NumericField::create('Minimum', 'Minimum'),
+ NumericField::create('Maximum', 'Maximum')
+ )
+ );
+
+ $actions = new FieldList(
+ FormAction::create('doSearchForm', 'Search')
+ );
+
+ $required = new RequiredFields(array(
+ 'Type'
+ ));
+
+ // now we create the actual form with our fields and actions defined
+ // within this class
+ parent::__construct($controller, $name, $fields, $actions, $required);
+
+ // any modifications we need to make to the form.
+ $this->setFormMethod('GET');
+
+ $this->addExtraClass('no-action-styles');
+ $this->disableSecurityToken();
+ $this->loadDataFrom($_REQUEST);
+ }
+ }
+
+Our controller will now just have to create a new instance of this form object. Keeping the file light and easy to read.
+
+**mysite/code/Page.php**
+
+ :::php
+ setTemplate('SearchForm');
+
+ return $form;
+ }
+
+**mysite/templates/Includes/SearchForm.ss**
+
+ :::ss
+
+
+`SearchForm.ss` will be executed within the scope of the `Form` object so has access to any of the methods and
+properties on [api:Form] such as `$Fields` and `$Actions`.
+
+
+To understand more about Scope or the syntax for custom templates, read the [Templates](../../templates) guide.
+
+
+
diff --git a/docs/en/02_Developer_Guides/03_Forms/How_Tos/03_Create_a_GridFieldComponent.md b/docs/en/02_Developer_Guides/03_Forms/How_Tos/03_Create_a_GridFieldComponent.md
new file mode 100644
index 000000000..1cc5c6ccc
--- /dev/null
+++ b/docs/en/02_Developer_Guides/03_Forms/How_Tos/03_Create_a_GridFieldComponent.md
@@ -0,0 +1,49 @@
+
+A single component often uses a number of interfaces.
+
+### GridField_HTMLProvider
+
+Provides HTML for the header/footer rows in the table or before/after the template.
+
+Examples:
+
+ - A header html provider displays a header before the table
+ - A pagination html provider displays pagination controls under the table
+ - A filter html fields displays filter fields on top of the table
+ - A summary html field displays sums of a field at the bottom of the table
+
+### GridField_ColumnProvider
+
+Add a new column to the table display body, or modify existing columns. Used once per record/row.
+
+Examples:
+
+ - A data columns provider that displays data from the list in rows and columns.
+ - A delete button column provider that adds a delete button at the end of the row
+
+### GridField_ActionProvider
+
+Action providers runs actions, some examples are:
+
+ - A delete action provider that deletes a DataObject.
+ - An export action provider that will export the current list to a CSV file.
+
+### GridField_DataManipulator
+
+Modifies the data list. In general, the data manipulator will make use of `GridState` variables
+to decide how to modify the data list.
+
+Examples:
+
+ - A paginating data manipulator can apply a limit to a list (show only 20 records)
+ - A sorting data manipulator can sort the Title in a descending order.
+
+### GridField_URLHandler
+
+Sometimes an action isn't enough, we need to provide additional support URLs for the grid. It
+has a list of URL's that it can handle and the GridField passes request on to URLHandlers on matches.
+
+Examples:
+
+ - A pop-up form for editing a record's details.
+ - JSON formatted data used for javascript control of the gridfield.
\ No newline at end of file
diff --git a/docs/en/02_Developer_Guides/03_Forms/How_To/Gridfield_Rowaction.md b/docs/en/02_Developer_Guides/03_Forms/How_Tos/04_Create_a_GridField_ActionProvider.md
similarity index 100%
rename from docs/en/02_Developer_Guides/03_Forms/How_To/Gridfield_Rowaction.md
rename to docs/en/02_Developer_Guides/03_Forms/How_Tos/04_Create_a_GridField_ActionProvider.md
diff --git a/docs/en/02_Developer_Guides/03_Forms/How_To/Simple_Contact_Form.md b/docs/en/02_Developer_Guides/03_Forms/How_Tos/Simple_Contact_Form.md
similarity index 100%
rename from docs/en/02_Developer_Guides/03_Forms/How_To/Simple_Contact_Form.md
rename to docs/en/02_Developer_Guides/03_Forms/How_Tos/Simple_Contact_Form.md
diff --git a/docs/en/02_Developer_Guides/03_Forms/index.md b/docs/en/02_Developer_Guides/03_Forms/index.md
index 00c464fc2..cbbf4bcfc 100644
--- a/docs/en/02_Developer_Guides/03_Forms/index.md
+++ b/docs/en/02_Developer_Guides/03_Forms/index.md
@@ -1,8 +1,22 @@
title: Forms
-summary: Capture user information through Forms. This guide will work through making forms, custom form fields and adding fields to your data.
+summary: Capture user information through Forms. This guide will work through how to create SilverStripe forms, adding and modifying fields and how to handle form submissions.
+introduction: This guide will work through how to create SilverStripe forms, adding and modifying fields and how to handle form submissions.
-[CHILDREN]
+The [api:Form] class provides a way to create interactive forms in your web application with very little effort.
+SilverStripe handles generating the correct semantic HTML markup for the form and each of the fields, as well as the
+framework for dealing with submissions and validation.
-## How-to
+[CHILDREN Exclude="How_Tos,Fields"]
-[CHILDREN How_To]
\ No newline at end of file
+## FormField Documentation
+
+[CHILDREN Folder="Fields"]
+
+## How to's
+
+[CHILDREN How_Tos]
+
+## API Documentation
+
+* [api:Form]
+* [api:FormField]
\ No newline at end of file
diff --git a/docs/en/02_Developer_Guides/03_Forms/03_UploadField.md b/docs/en/02_Developer_Guides/UploadField.md
similarity index 99%
rename from docs/en/02_Developer_Guides/03_Forms/03_UploadField.md
rename to docs/en/02_Developer_Guides/UploadField.md
index 204086d56..1e44bf4a3 100644
--- a/docs/en/02_Developer_Guides/03_Forms/03_UploadField.md
+++ b/docs/en/02_Developer_Guides/UploadField.md
@@ -1,3 +1,6 @@
+title: UploadField
+summary: How to use the UploadField class for uploading assets.
+
# UploadField
## Introduction