mirror of
https://github.com/silverstripe/silverstripe-multiform
synced 2024-10-22 11:05:49 +02:00
Merge pull request #63 from creative-commoners/pulls/2.0/ss4-compatibility
Pulls/2.0/ss4 compatibility
This commit is contained in:
commit
a3e7422f25
43
.travis.yml
43
.travis.yml
@ -1,34 +1,35 @@
|
||||
# See https://github.com/silverstripe-labs/silverstripe-travis-support for setup details
|
||||
|
||||
sudo: false
|
||||
|
||||
language: php
|
||||
|
||||
php:
|
||||
- 5.3
|
||||
- 5.4
|
||||
- 5.5
|
||||
- 5.6
|
||||
- 7.0
|
||||
dist: trusty
|
||||
|
||||
env:
|
||||
- DB=MYSQL CORE_RELEASE=3.2
|
||||
global:
|
||||
- COMPOSER_ROOT_VERSION=4.0.x-dev
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- php: 5.6
|
||||
env: DB=MYSQL CORE_RELEASE=3
|
||||
- php: 5.6
|
||||
env: DB=PGSQL CORE_RELEASE=3.2
|
||||
allow_failures:
|
||||
env: DB=MYSQL PHPCS_TEST=1 PHPUNIT_TEST=1
|
||||
- php: 7.0
|
||||
env: DB=PGSQL PHPUNIT_TEST=1
|
||||
- php: 7.1
|
||||
env: DB=MYSQL PHPUNIT_COVERAGE_TEST=1
|
||||
|
||||
before_script:
|
||||
- composer self-update || true
|
||||
- git clone git://github.com/silverstripe-labs/silverstripe-travis-support.git ~/travis-support
|
||||
- php ~/travis-support/travis_setup.php --source `pwd` --target ~/builds/ss
|
||||
- cd ~/builds/ss
|
||||
- composer install
|
||||
# Init PHP
|
||||
- phpenv rehash
|
||||
- phpenv config-rm xdebug.ini
|
||||
|
||||
# Install composer dependencies
|
||||
- composer install --prefer-dist
|
||||
- composer require --prefer-dist --no-update silverstripe/recipe-core:1.0.x-dev
|
||||
- if [[ $DB == PGSQL ]]; then composer require --prefer-dist --no-update silverstripe/postgresql:2.0.x-dev; fi
|
||||
- composer update
|
||||
|
||||
script:
|
||||
- vendor/bin/phpunit multiform/tests
|
||||
- if [[ $PHPUNIT_TEST ]]; then vendor/bin/phpunit; fi
|
||||
- if [[ $PHPUNIT_COVERAGE_TEST ]]; then phpdbg -qrr vendor/bin/phpunit --coverage-clover=coverage.xml; fi
|
||||
- if [[ $PHPCS_TEST ]]; then vendor/bin/phpcs --standard=framework/phpcs.xml.dist src/ tests/; fi
|
||||
|
||||
after_success:
|
||||
- if [[ $PHPUNIT_COVERAGE_TEST ]]; then bash <(curl -s https://codecov.io/bash) -f coverage.xml; fi
|
||||
|
14
.upgrade.yml
Normal file
14
.upgrade.yml
Normal file
@ -0,0 +1,14 @@
|
||||
mappings:
|
||||
MultiFormObjectDecorator: SilverStripe\MultiForm\Extensions\MultiFormObjectDecorator
|
||||
MultiForm: SilverStripe\MultiForm\Models\MultiForm
|
||||
MultiFormSession: SilverStripe\MultiForm\Models\MultiFormSession
|
||||
MultiFormStep: SilverStripe\MultiForm\Models\MultiFormStep
|
||||
MultiFormPurgeTask: SilverStripe\MultiForm\Tasks\MultiFormPurgeTask
|
||||
MultiFormObjectDecoratorTest: SilverStripe\MultiForm\Tests\MultiFormObjectDecoratorTest
|
||||
MultiFormObjectDecoratorDataObject: SilverStripe\MultiForm\Tests\Stubs\MultiFormObjectDecoratorDataObject
|
||||
MultiFormTest: SilverStripe\MultiForm\Tests\MultiFormTest
|
||||
MultiFormTestController: SilverStripe\MultiForm\Tests\Stubs\MultiFormTestController
|
||||
MultiFormTestForm: SilverStripe\MultiForm\Tests\Stubs\MultiFormTestForm
|
||||
MultiFormTestStepOne: SilverStripe\MultiForm\Tests\Stubs\MultiFormTestStepOne
|
||||
MultiFormTestStepTwo: SilverStripe\MultiForm\Tests\Stubs\MultiFormTestStepTwo
|
||||
MultiFormTestStepThree: SilverStripe\MultiForm\Tests\Stubs\MultiFormTestStepThree
|
493
README.md
493
README.md
@ -1,6 +1,6 @@
|
||||
# MultiForm Module
|
||||
|
||||
[![Build Status](https://api.travis-ci.org/silverstripe/silverstripe-multiform.svg?branch=master)](https://travis-ci.org/silverstripe/silverstripe-multiform)
|
||||
[![Build Status](https://travis-ci.org/silverstripe/silverstripe-multiform.svg?branch=master)](https://travis-ci.org/silverstripe/silverstripe-multiform)
|
||||
[![Latest Stable Version](https://poser.pugx.org/silverstripe/multiform/version.svg)](https://github.com/silverstripe/silverstripe-multiform/releases)
|
||||
[![Latest Unstable Version](https://poser.pugx.org/silverstripe/multiform/v/unstable.svg)](https://packagist.org/packages/silverstripe/multiform)
|
||||
[![Total Downloads](https://poser.pugx.org/silverstripe/multiform/downloads.svg)](https://packagist.org/packages/silverstripe/multiform)
|
||||
@ -83,7 +83,9 @@ Using [Composer](https://getcomposer.org/), you can install multiform into your
|
||||
SilverStripe site using this command (while in the directory where your site is
|
||||
currently located)
|
||||
|
||||
composer require "silverstripe/multiform:*"
|
||||
```
|
||||
composer require "silverstripe/multiform:*"
|
||||
```
|
||||
|
||||
### 2. Create subclass of MultiForm
|
||||
|
||||
@ -91,13 +93,11 @@ First of all, we need to create a new subclass of *MultiForm*.
|
||||
|
||||
For the above example, our multi-form will be called *SurveyForm*
|
||||
|
||||
:::php
|
||||
<?php
|
||||
|
||||
class SurveyForm extends MultiForm {
|
||||
|
||||
}
|
||||
```php
|
||||
class SurveyForm extends MultiForm {
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Set up first step
|
||||
|
||||
@ -111,27 +111,23 @@ form.
|
||||
So, for example, if we were going to have a first step which collects the
|
||||
personal details of the form user, then we might have this class:
|
||||
|
||||
:::php
|
||||
<?php
|
||||
|
||||
class SurveyFormPersonalDetailsStep extends MultiFormStep {
|
||||
|
||||
}
|
||||
```php
|
||||
class SurveyFormPersonalDetailsStep extends MultiFormStep {
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
Now that we've got our first step of the form defined, we need to go back to our
|
||||
subclass of MultiForm, SurveyForm, and tell it that SurveyFormPersonalDetailsStep
|
||||
is the first step.
|
||||
|
||||
:::php
|
||||
<?php
|
||||
|
||||
class SurveyForm extends MultiForm {
|
||||
|
||||
public static $start_step = 'SurveyFormPersonalDetailsStep';
|
||||
|
||||
}
|
||||
```php
|
||||
class SurveyForm extends MultiForm {
|
||||
|
||||
private static $start_step = 'SurveyFormPersonalDetailsStep';
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Define next step, and final step
|
||||
|
||||
@ -144,43 +140,39 @@ order to use flow control in our system.
|
||||
To let the step know what step is next in the process, we do the same as setting
|
||||
the `$start_step` variable *SurveyForm*, but we call it `$next_steps`.
|
||||
|
||||
:::php
|
||||
<?php
|
||||
|
||||
class SurveyFormPersonalDetailsStep extends MultiFormStep {
|
||||
|
||||
public static $next_steps = 'SurveyFormOrganisationDetailsStep';
|
||||
```php
|
||||
class SurveyFormPersonalDetailsStep extends MultiFormStep {
|
||||
|
||||
public function getFields() {
|
||||
return new FieldList(
|
||||
new TextField('FirstName', 'First name'),
|
||||
new TextField('Surname', 'Surname')
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
private static $next_steps = 'SurveyFormOrganisationDetailsStep';
|
||||
|
||||
public function getFields() {
|
||||
return new FieldList(
|
||||
new TextField('FirstName', 'First name'),
|
||||
new TextField('Surname', 'Surname')
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
At the very least, each step also has to have a `getFields()` method returning
|
||||
a *FieldSet* with some form field objects. These are the fields that the form
|
||||
will render for the given step.
|
||||
|
||||
Keep in mind that our multi-form also requires an end point. This step is the
|
||||
final, and needs to have another variable set to let the multi-form system know
|
||||
final one, and needs to have another variable set to let the multi-form system know
|
||||
this is the final step.
|
||||
|
||||
So, if we assume that the last step in our process is
|
||||
SurveyFormOrganisationDetailsStep, then we can do something like this:
|
||||
|
||||
:::php
|
||||
<?php
|
||||
|
||||
class SurveyFormOrganisationDetailsStep extends MultiFormStep {
|
||||
|
||||
public static $is_final_step = true;
|
||||
|
||||
}
|
||||
```php
|
||||
class SurveyFormOrganisationDetailsStep extends MultiFormStep {
|
||||
|
||||
private static $is_final_step = true;
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Run database integrity check
|
||||
|
||||
@ -197,42 +189,40 @@ that the form can be rendered into a given template.
|
||||
So, if we want to render our multi-form as `$SurveyForm` in the *Page.ss*
|
||||
template, we need to create a SurveyForm method (function) on the controller:
|
||||
|
||||
:::php
|
||||
<?php
|
||||
|
||||
class Page extends SiteTree {
|
||||
|
||||
// ...
|
||||
|
||||
}
|
||||
|
||||
class Page_Controller extends ContentController {
|
||||
|
||||
// ...
|
||||
```php
|
||||
class Page extends SiteTree {
|
||||
|
||||
//
|
||||
private static $allowed_actions = array(
|
||||
'SurveyForm',
|
||||
'finished'
|
||||
);
|
||||
// ...
|
||||
|
||||
public function SurveyForm() {
|
||||
return new SurveyForm($this, 'Form');
|
||||
}
|
||||
|
||||
public function finished() {
|
||||
return array(
|
||||
'Title' => 'Thank you for your submission',
|
||||
'Content' => '<p>You have successfully submitted the form!</p>'
|
||||
);
|
||||
}
|
||||
|
||||
// ...
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class Page_Controller extends ContentController {
|
||||
|
||||
The `SurveyForm()` function will create a new instance our subclass of
|
||||
// ...
|
||||
|
||||
//
|
||||
private static $allowed_actions = array(
|
||||
'SurveyForm',
|
||||
'finished'
|
||||
);
|
||||
|
||||
public function SurveyForm() {
|
||||
return new SurveyForm($this, 'Form');
|
||||
}
|
||||
|
||||
public function finished() {
|
||||
return array(
|
||||
'Title' => 'Thank you for your submission',
|
||||
'Content' => '<p>You have successfully submitted the form!</p>'
|
||||
);
|
||||
}
|
||||
|
||||
// ...
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
The `SurveyForm()` function will create a new instance of our subclass of
|
||||
MultiForm, which in this example, is *SurveyForm*. This in turn will then set
|
||||
up all the form fields, actions, and validation available to each step, as well
|
||||
as the session.
|
||||
@ -242,21 +232,21 @@ like.
|
||||
|
||||
Your template should look something like this, to render the form in:
|
||||
|
||||
:::html
|
||||
<div id="content">
|
||||
<% if $Content %>
|
||||
$Content
|
||||
<% end_if %>
|
||||
|
||||
<% if $SurveyForm %>
|
||||
$SurveyForm
|
||||
<% end_if %>
|
||||
|
||||
<% if $Form %>
|
||||
$Form
|
||||
<% end_if %>
|
||||
</div>
|
||||
|
||||
```html
|
||||
<div id="content">
|
||||
<% if $Content %>
|
||||
$Content
|
||||
<% end_if %>
|
||||
|
||||
<% if $SurveyForm %>
|
||||
$SurveyForm
|
||||
<% end_if %>
|
||||
|
||||
<% if $Form %>
|
||||
$Form
|
||||
<% end_if %>
|
||||
</div>
|
||||
```
|
||||
|
||||
In this case, the above template example is a *sub-template* inside the *Layout*
|
||||
directory for the templates. Note that we have also included `$Form`, so
|
||||
@ -281,11 +271,11 @@ To include these with our instance of multiform, we just need to add an
|
||||
|
||||
For example:
|
||||
|
||||
:::html
|
||||
<% with $SurveyForm %>
|
||||
<% include MultiFormProgressList %>
|
||||
<% end_with %>
|
||||
|
||||
```html
|
||||
<% with $SurveyForm %>
|
||||
<% include MultiFormProgressList %>
|
||||
<% end_with %>
|
||||
```
|
||||
|
||||
This means the included template is rendered within the scope of the
|
||||
SurveyForm instance returned, instead of the top level controller context.
|
||||
@ -293,24 +283,24 @@ This gives us the data to show the progression of the steps.
|
||||
|
||||
Putting it together, we might have something looking like this:
|
||||
|
||||
:::html
|
||||
<div id="content">
|
||||
<% if $Content %>
|
||||
$Content
|
||||
<% end_if %>
|
||||
|
||||
<% if $SurveyForm %>
|
||||
<% with $SurveyForm %>
|
||||
<% include MultiFormProgressList %>
|
||||
<% end_with %>
|
||||
|
||||
$SurveyForm
|
||||
<% end_if %>
|
||||
<% if $Form %>
|
||||
$Form
|
||||
<% end_if %>
|
||||
</div>
|
||||
```html
|
||||
<div id="content">
|
||||
<% if $Content %>
|
||||
$Content
|
||||
<% end_if %>
|
||||
|
||||
<% if $SurveyForm %>
|
||||
<% with $SurveyForm %>
|
||||
<% include MultiFormProgressList %>
|
||||
<% end_with %>
|
||||
|
||||
$SurveyForm
|
||||
<% end_if %>
|
||||
<% if $Form %>
|
||||
$Form
|
||||
<% end_if %>
|
||||
</div>
|
||||
```
|
||||
|
||||
Feel free to play around with the progress indicators. If you need something
|
||||
specific to your project, just create a new "Include" template inside your own
|
||||
@ -343,37 +333,35 @@ based on the submission value of another step. There are two methods supporting
|
||||
|
||||
Here is an example of how to populate the email address from step 1 in step2 :
|
||||
|
||||
:::php
|
||||
<?php
|
||||
```php
|
||||
class Step1 extends MultiFormStep
|
||||
{
|
||||
private static $next_steps = 'Step2';
|
||||
|
||||
class Step1 extends MultiFormStep
|
||||
{
|
||||
public static $next_steps = 'Step2';
|
||||
public function getFields() {
|
||||
return new FieldList(
|
||||
new EmailField('Email', 'Your email')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function getFields() {
|
||||
return new FieldList(
|
||||
new EmailField('Email', 'Your email')
|
||||
);
|
||||
}
|
||||
}
|
||||
class Step2 extends MultiFormStep
|
||||
{
|
||||
private static $next_steps = 'Step3';
|
||||
|
||||
class Step2 extends MultiFormStep
|
||||
{
|
||||
public static $next_steps = 'Step3';
|
||||
public function getFields() {
|
||||
$fields = new FieldList(
|
||||
new EmailField('Email', 'E-mail'),
|
||||
new EmailField('Email2', 'Verify E-Mail')
|
||||
);
|
||||
|
||||
public function getFields() {
|
||||
$fields = new FieldList(
|
||||
new EmailField('Email', 'E-mail'),
|
||||
new EmailField('Email2', 'Verify E-Mail')
|
||||
);
|
||||
|
||||
// set the email field to the input from Step 1
|
||||
$this->copyValueFromOtherStep($fields, 'Step1', 'Email');
|
||||
|
||||
return $fields;
|
||||
}
|
||||
}
|
||||
// set the email field to the input from Step 1
|
||||
$this->copyValueFromOtherStep($fields, 'Step1', 'Email');
|
||||
|
||||
return $fields;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 8. Finishing it up
|
||||
|
||||
@ -389,56 +377,54 @@ So, we must write some code on our subclass of *MultiForm*, overloading
|
||||
|
||||
Here is an example of what we could do here:
|
||||
|
||||
:::php
|
||||
<?php
|
||||
|
||||
class SurveyForm extends MultiForm {
|
||||
|
||||
public static $start_step = 'SurveyFormPersonalDetailsStep';
|
||||
|
||||
public function finish($data, $form) {
|
||||
parent::finish($data, $form);
|
||||
```php
|
||||
class SurveyForm extends MultiForm {
|
||||
|
||||
private static $start_step = 'SurveyFormPersonalDetailsStep';
|
||||
|
||||
public function finish($data, $form) {
|
||||
parent::finish($data, $form);
|
||||
|
||||
$steps = DataObject::get(
|
||||
'MultiFormStep',
|
||||
"SessionID = {$this->session->ID}"
|
||||
);
|
||||
|
||||
if($steps) {
|
||||
foreach($steps as $step) {
|
||||
if($step->class == 'SurveyFormPersonalDetailsStep') {
|
||||
$member = new Member();
|
||||
$data = $step->loadData();
|
||||
$steps = DataObject::get(
|
||||
MultiFormStep::class,
|
||||
"SessionID = {$this->session->ID}"
|
||||
);
|
||||
|
||||
if($steps) {
|
||||
foreach($steps as $step) {
|
||||
if($step->class == 'SurveyFormPersonalDetailsStep') {
|
||||
$member = new Member();
|
||||
$data = $step->loadData();
|
||||
|
||||
if($data) {
|
||||
$member->update($data);
|
||||
$member->write();
|
||||
}
|
||||
}
|
||||
|
||||
if($step->class == 'SurveyOrganisationDetailsStep') {
|
||||
$organisation = new Organisation();
|
||||
$data = $step->loadData();
|
||||
if($data) {
|
||||
$member->update($data);
|
||||
$member->write();
|
||||
}
|
||||
}
|
||||
|
||||
if($data) {
|
||||
$organisation->update($data);
|
||||
if($step->class == 'SurveyOrganisationDetailsStep') {
|
||||
$organisation = new Organisation();
|
||||
$data = $step->loadData();
|
||||
|
||||
if($member && $member->ID) {
|
||||
$organisation->MemberID = $member->ID;
|
||||
}
|
||||
if($data) {
|
||||
$organisation->update($data);
|
||||
|
||||
$organisation->write();
|
||||
}
|
||||
}
|
||||
// Shows the step data (unserialized by loadData)
|
||||
// Debug::show($step->loadData());
|
||||
}
|
||||
}
|
||||
if($member && $member->ID) {
|
||||
$organisation->MemberID = $member->ID;
|
||||
}
|
||||
|
||||
$this->controller->redirect($this->controller->Link() . 'finished');
|
||||
}
|
||||
}
|
||||
$organisation->write();
|
||||
}
|
||||
}
|
||||
// Shows the step data (unserialized by loadData)
|
||||
// Debug::show($step->loadData());
|
||||
}
|
||||
}
|
||||
|
||||
$this->controller->redirect($this->controller->Link() . 'finished');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 9. Organisation data model
|
||||
|
||||
@ -449,17 +435,14 @@ groups in SilverStripe) so we need to create it:
|
||||
This example has been chosen as a separate DataObject but you may wish to change
|
||||
the code and add the data to the Member class instead.
|
||||
|
||||
:::php
|
||||
<?php
|
||||
|
||||
class Organisation extends DataObject {
|
||||
|
||||
private static $db = array(
|
||||
// Add your Organisation fields here
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
```php
|
||||
class Organisation extends DataObject {
|
||||
|
||||
private static $db = array(
|
||||
// Add your Organisation fields here
|
||||
);
|
||||
}
|
||||
```
|
||||
#### Warning
|
||||
|
||||
If you're dealing with sensitive data, it's best to delete the session and step
|
||||
@ -468,9 +451,9 @@ data immediately after the form is successfully submitted.
|
||||
You can delete it by calling this method on the finish() for your MultiForm
|
||||
subclass:
|
||||
|
||||
:::php
|
||||
$this->session->delete();
|
||||
|
||||
```php
|
||||
$this->session->delete();
|
||||
```
|
||||
|
||||
This will also go through each of it's steps and delete them as well.
|
||||
|
||||
@ -499,42 +482,42 @@ MultiForm class:
|
||||
|
||||
More than likely, you'll want the first one to be available when the form
|
||||
renders. To that effect, you can start placing templates in the
|
||||
*templates/Includes* directory for your project. You need to call them the same
|
||||
*templates/Includes* directory for your project. You need to name them the same
|
||||
as the class name for each step. For example, if you want *MembershipForm*, a
|
||||
subclass of *MultiFormStep* to have it's own template, you would put
|
||||
*MembershipForm.ss* into that directory, and run *?flush=1*.
|
||||
|
||||
If you'd like a pre-existing template on how to customise the form step, have a
|
||||
look at Form.ss that's found within the sapphire module. Use that template, as
|
||||
look at Form.ss that's found within the framework module. Use that template, as
|
||||
a base for your new MembershipForm.ss template in your project templates.
|
||||
|
||||
For more information on this, please [look at the Form documentation](http://doc.silverstripe.org/framework/en/topics/forms#custom-form-templates).
|
||||
|
||||
### getNextStep()
|
||||
|
||||
If you are wanting to override the next step (so, if you want the next step to
|
||||
be something different based on a user's choice of input during the step, you
|
||||
If you are wanting to override the next step (for example if you want the next step to
|
||||
be something different based on a user's choice of input during the step) you
|
||||
can override getNextStep() on any given step to manually override what the next
|
||||
step should be. An example:
|
||||
|
||||
:::php
|
||||
class MyStep extends MultiFormStep
|
||||
|
||||
// ...
|
||||
|
||||
public function getNextStep() {
|
||||
$data = $this->loadData();
|
||||
if(@$data['Gender'] == 'Male') {
|
||||
return 'TestThirdCase1Step';
|
||||
} else {
|
||||
return 'TestThirdCase2Step';
|
||||
}
|
||||
}
|
||||
|
||||
// ...
|
||||
|
||||
}
|
||||
```php
|
||||
class MyStep extends MultiFormStep
|
||||
|
||||
// ...
|
||||
|
||||
public function getNextStep() {
|
||||
$data = $this->loadData();
|
||||
if(@$data['Gender'] == 'Male') {
|
||||
return 'TestThirdCase1Step';
|
||||
} else {
|
||||
return 'TestThirdCase2Step';
|
||||
}
|
||||
}
|
||||
|
||||
// ...
|
||||
|
||||
}
|
||||
```
|
||||
### Validation
|
||||
|
||||
To define validation on a step-by-step basis, please define getValidator() and
|
||||
@ -543,22 +526,22 @@ validation see [:form](http://doc.silverstripe.org/form-validation).
|
||||
|
||||
e.g.
|
||||
|
||||
:::php
|
||||
class MyStep extends MultiFormStep {
|
||||
|
||||
...
|
||||
|
||||
public function getValidator() {
|
||||
return new RequiredFields(array(
|
||||
'Name',
|
||||
'Email'
|
||||
));
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
}
|
||||
```php
|
||||
class MyStep extends MultiFormStep {
|
||||
|
||||
...
|
||||
|
||||
public function getValidator() {
|
||||
return new RequiredFields(array(
|
||||
'Name',
|
||||
'Email'
|
||||
));
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
### finish()
|
||||
|
||||
@ -573,29 +556,27 @@ won't be saved.
|
||||
|
||||
For example:
|
||||
|
||||
:::php
|
||||
<?php
|
||||
|
||||
class SurveyForm extends MultiForm {
|
||||
|
||||
public static $start_step = 'SurveyFormPersonalDetailsStep';
|
||||
|
||||
public function finish($data, $form) {
|
||||
parent::finish($data, $form);
|
||||
```php
|
||||
class SurveyForm extends MultiForm {
|
||||
|
||||
$steps = MultiFormStep::get()->filter(array(
|
||||
"SessionID" => $this->session->ID
|
||||
));
|
||||
private static $start_step = 'SurveyFormPersonalDetailsStep';
|
||||
|
||||
if($steps) {
|
||||
foreach($steps as $step) {
|
||||
// Shows the step data (unserialized by loadData)
|
||||
Debug::show($step->loadData());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public function finish($data, $form) {
|
||||
parent::finish($data, $form);
|
||||
|
||||
$steps = MultiFormStep::get()->filter(array(
|
||||
"SessionID" => $this->session->ID
|
||||
));
|
||||
|
||||
if($steps) {
|
||||
foreach($steps as $step) {
|
||||
// Shows the step data (unserialized by loadData)
|
||||
Debug::show($step->loadData());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The above is a sample bit of code that simply fetches all the steps in the
|
||||
database that were saved. Further refinement could include getting steps only
|
||||
@ -613,9 +594,9 @@ idea to immediately delete this data after the user has submitted.
|
||||
This can be easily achieved by adding the following line at the end of your
|
||||
`finish()` method on your MultiForm subclass.
|
||||
|
||||
:::php
|
||||
$this->session->delete();
|
||||
|
||||
```php
|
||||
$this->session->delete();
|
||||
```
|
||||
|
||||
### Expiring old session data
|
||||
|
||||
|
@ -1,2 +1 @@
|
||||
<?php
|
||||
|
||||
|
3
_config/config.yml
Normal file
3
_config/config.yml
Normal file
@ -0,0 +1,3 @@
|
||||
---
|
||||
Name: multisiteconfig
|
||||
---
|
7
_config/legacy.yml
Normal file
7
_config/legacy.yml
Normal file
@ -0,0 +1,7 @@
|
||||
---
|
||||
Name: multisitelegacy
|
||||
---
|
||||
SilverStripe\ORM\DatabaseAdmin:
|
||||
classname_value_remapping:
|
||||
MultiFormSession: SilverStripe\MultiForm\Models\MultiFormSession
|
||||
MultiFormStep: SilverStripe\MultiForm\Models\MultiFormStep
|
@ -1,69 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Decorate {@link DataObject}s which are required to be saved
|
||||
* to the database directly by a {@link MultiFormStep}.
|
||||
* Only needed for objects which aren't stored in the session,
|
||||
* which is the default.
|
||||
*
|
||||
* This decorator also augments get() requests to the datalayer
|
||||
* by automatically filtering out temporary objects.
|
||||
* You can override this filter by putting the following statement
|
||||
* in your WHERE clause:
|
||||
* `<MyDataObjectClass>`.`MultiFormIsTemporary` = 1
|
||||
*
|
||||
* @package multiform
|
||||
*/
|
||||
class MultiFormObjectDecorator extends DataExtension {
|
||||
|
||||
private static $db = array(
|
||||
'MultiFormIsTemporary' => 'Boolean',
|
||||
);
|
||||
|
||||
private static $has_one = array(
|
||||
'MultiFormSession' => 'MultiFormSession',
|
||||
);
|
||||
|
||||
/**
|
||||
* Augment any queries to MultiFormObjectDecorator and only
|
||||
* return anything that isn't considered temporary.
|
||||
*/
|
||||
public function augmentSQL(SQLQuery &$query) {
|
||||
$where = $query->getWhere();
|
||||
if(!$where && !$this->wantsTemporary($query)) {
|
||||
$from = array_values($query->getFrom());
|
||||
$query->addWhere("{$from[0]}.\"MultiFormIsTemporary\" = '0'");
|
||||
return;
|
||||
}
|
||||
|
||||
if(
|
||||
strpos($where[0], ".`ID` = ") === false
|
||||
&& strpos($where[0], ".ID = ") === false
|
||||
&& strpos($where[0], "ID = ") !== 0
|
||||
&& !$this->wantsTemporary($query)
|
||||
) {
|
||||
$from = array_values($query->getFrom());
|
||||
$query->addWhere("{$from[0]}.\"MultiFormIsTemporary\" = '0'");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the current query is supposed
|
||||
* to be exempt from the automatic filtering out
|
||||
* of temporary records.
|
||||
*
|
||||
* @param SQLQuery $query
|
||||
* @return boolean
|
||||
*/
|
||||
protected function wantsTemporary($query) {
|
||||
foreach($query->getWhere() as $whereClause) {
|
||||
$from = array_values($query->getFrom());
|
||||
// SQLQuery will automatically add double quotes and single quotes to values, so check against that.
|
||||
if($whereClause == "{$from[0]}.\"MultiFormIsTemporary\" = '1'") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -1,699 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* MultiForm manages the loading of single form steps, and acts as a state
|
||||
* machine that connects to a {@link MultiFormSession} object as a persistence
|
||||
* layer.
|
||||
*
|
||||
* CAUTION: If you're using controller permission control,
|
||||
* you have to allow the following methods:
|
||||
*
|
||||
* <code>
|
||||
* private static $allowed_actions = array('next','prev');
|
||||
* </code>
|
||||
*
|
||||
* @package multiform
|
||||
*/
|
||||
abstract class MultiForm extends Form {
|
||||
|
||||
/**
|
||||
* A session object stored in the database, to identify and store
|
||||
* data for this MultiForm instance.
|
||||
*
|
||||
* @var MultiFormSession
|
||||
*/
|
||||
protected $session;
|
||||
|
||||
/**
|
||||
* The current encrypted MultiFormSession identification.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $currentSessionHash;
|
||||
|
||||
/**
|
||||
* Defines which subclass of {@link MultiFormStep} should be the first
|
||||
* step in the multi-step process.
|
||||
*
|
||||
* @var string Classname of a {@link MultiFormStep} subclass
|
||||
*/
|
||||
public static $start_step;
|
||||
|
||||
/**
|
||||
* Set the casting for these fields.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $casting = array(
|
||||
'CompletedStepCount' => 'Int',
|
||||
'TotalStepCount' => 'Int',
|
||||
'CompletedPercent' => 'Float'
|
||||
);
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private static $get_var = 'MultiFormSessionID';
|
||||
|
||||
/**
|
||||
* These fields are ignored when saving the raw form data into session.
|
||||
* This ensures only field data is saved, and nothing else that's useless
|
||||
* or potentially dangerous.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $ignored_fields = array(
|
||||
'url',
|
||||
'executeForm',
|
||||
'SecurityID'
|
||||
);
|
||||
|
||||
/**
|
||||
* Any of the actions defined in this variable are exempt from
|
||||
* being validated.
|
||||
*
|
||||
* This is most useful for the "Back" (action_prev) action, as
|
||||
* you typically don't validate the form when the user is going
|
||||
* back a step.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $actions_exempt_from_validation = array(
|
||||
'action_prev'
|
||||
);
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $displayLink;
|
||||
|
||||
/**
|
||||
* Flag which is being used in getAllStepsRecursive() to allow adding the completed flag on the steps
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $currentStepHasBeenFound = false;
|
||||
|
||||
/**
|
||||
* Start the MultiForm instance.
|
||||
*
|
||||
* @param Controller instance $controller Controller this form is created on
|
||||
* @param string $name The form name, typically the same as the method name
|
||||
*/
|
||||
public function __construct($controller, $name) {
|
||||
// First set the controller and name manually so they are available for
|
||||
// field construction.
|
||||
$this->controller = $controller;
|
||||
$this->name = $name;
|
||||
|
||||
// Set up the session for this MultiForm instance
|
||||
$this->setSession();
|
||||
|
||||
// Get the current step available (Note: either returns an existing
|
||||
// step or creates a new one if none available)
|
||||
$currentStep = $this->getCurrentStep();
|
||||
|
||||
// Set the step returned above as the current step
|
||||
$this->setCurrentStep($currentStep);
|
||||
|
||||
// Set the form of the step to this form instance
|
||||
$currentStep->setForm($this);
|
||||
|
||||
// Set up the fields for the current step
|
||||
$fields = $currentStep->getFields();
|
||||
|
||||
// Set up the actions for the current step
|
||||
$actions = $this->actionsFor($currentStep);
|
||||
|
||||
// Set up validation (if necessary)
|
||||
$validator = null;
|
||||
$applyValidation = true;
|
||||
|
||||
$actionNames = static::$actions_exempt_from_validation;
|
||||
|
||||
if($actionNames) {
|
||||
foreach ($actionNames as $exemptAction) {
|
||||
if(!empty($_REQUEST[$exemptAction])) {
|
||||
$applyValidation = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply validation if the current step requires validation (is not exempt)
|
||||
if($applyValidation) {
|
||||
if($currentStep->getValidator()) {
|
||||
$validator = $currentStep->getValidator();
|
||||
}
|
||||
}
|
||||
|
||||
// Give the fields, actions, and validation for the current step back to the parent Form class
|
||||
parent::__construct($controller, $name, $fields, $actions, $validator);
|
||||
|
||||
$getVar = $this->config()->get_var;
|
||||
|
||||
// Set a hidden field in our form with an encrypted hash to identify this session.
|
||||
$this->fields->push(new HiddenField($getVar, false, $this->session->Hash));
|
||||
|
||||
// If there is saved data for the current step, we load it into the form it here
|
||||
//(CAUTION: loadData() MUST unserialize first!)
|
||||
if($data = $currentStep->loadData()) {
|
||||
$this->loadDataFrom($data);
|
||||
}
|
||||
|
||||
// Disable security token - we tie a form to a session ID instead
|
||||
$this->disableSecurityToken();
|
||||
|
||||
self::$ignored_fields[] = $getVar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor method to $this->controller.
|
||||
*
|
||||
* @return Controller this MultiForm was instanciated on.
|
||||
*/
|
||||
public function getController() {
|
||||
return $this->controller;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the get_var to the template engine
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getGetVar() {
|
||||
return $this->config()->get_var;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current step.
|
||||
*
|
||||
* If StepID has been set in the URL, we attempt to get that record
|
||||
* by the ID. Otherwise, we check if there's a current step ID in
|
||||
* our session record. Failing those cases, we assume that the form has
|
||||
* just been started, and so we create the first step and return it.
|
||||
*
|
||||
* @return MultiFormStep subclass
|
||||
*/
|
||||
public function getCurrentStep() {
|
||||
$startStepClass = static::$start_step;
|
||||
|
||||
// Check if there was a start step defined on the subclass of MultiForm
|
||||
if(!isset($startStepClass)) user_error(
|
||||
'MultiForm::init(): Please define a $start_step on ' . $this->class,
|
||||
E_USER_ERROR
|
||||
);
|
||||
|
||||
// Determine whether we use the current step, or create one if it doesn't exist
|
||||
$currentStep = null;
|
||||
$StepID = $this->controller->request->getVar('StepID');
|
||||
if(isset($StepID)) {
|
||||
$currentStep = DataObject::get_one(
|
||||
'MultiFormStep',
|
||||
array(
|
||||
'SessionID' => $this->session->ID,
|
||||
'ID' => $StepID
|
||||
)
|
||||
);
|
||||
} elseif($this->session->CurrentStepID) {
|
||||
$currentStep = $this->session->CurrentStep();
|
||||
}
|
||||
|
||||
// Always fall back to creating a new step (in case the session or request data is invalid)
|
||||
if(!$currentStep || !$currentStep->ID) {
|
||||
$currentStep = Object::create($startStepClass);
|
||||
$currentStep->SessionID = $this->session->ID;
|
||||
$currentStep->write();
|
||||
$this->session->CurrentStepID = $currentStep->ID;
|
||||
$this->session->write();
|
||||
$this->session->flushCache();
|
||||
}
|
||||
|
||||
if($currentStep) $currentStep->setForm($this);
|
||||
|
||||
return $currentStep;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the step passed in as the current step.
|
||||
*
|
||||
* @param MultiFormStep $step A subclass of MultiFormStep
|
||||
* @return boolean The return value of write()
|
||||
*/
|
||||
protected function setCurrentStep($step) {
|
||||
$this->session->CurrentStepID = $step->ID;
|
||||
$step->setForm($this);
|
||||
|
||||
return $this->session->write();
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor method to $this->session.
|
||||
*
|
||||
* @return MultiFormSession
|
||||
*/
|
||||
public function getSession() {
|
||||
return $this->session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the session.
|
||||
*
|
||||
* If MultiFormSessionID isn't set, we assume that this is a new
|
||||
* multiform that requires a new session record to be created.
|
||||
*
|
||||
* @TODO Fix the fact you can continually refresh and create new records
|
||||
* if MultiFormSessionID isn't set.
|
||||
*
|
||||
* @TODO Not sure if we should bake the session stuff directly into MultiForm.
|
||||
* Perhaps it would be best dealt with on a separate class?
|
||||
*/
|
||||
protected function setSession() {
|
||||
$this->session = $this->getCurrentSession();
|
||||
|
||||
// If there was no session found, create a new one instead
|
||||
if(!$this->session) {
|
||||
$this->session = new MultiFormSession();
|
||||
}
|
||||
|
||||
// Create encrypted identification to the session instance if it doesn't exist
|
||||
if(!$this->session->Hash) {
|
||||
$this->session->Hash = sha1($this->session->ID . '-' . microtime());
|
||||
$this->session->write();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the currently used encrypted hash to identify
|
||||
* the MultiFormSession.
|
||||
*
|
||||
* @param string $hash Encrypted identification to session
|
||||
*/
|
||||
public function setCurrentSessionHash($hash) {
|
||||
$this->currentSessionHash = $hash;
|
||||
$this->setSession();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the currently used {@link MultiFormSession}
|
||||
* @return MultiFormSession|boolean FALSE
|
||||
*/
|
||||
public function getCurrentSession() {
|
||||
if(!$this->currentSessionHash) {
|
||||
$this->currentSessionHash = $this->controller->request->getVar($this->config()->get_var);
|
||||
|
||||
if(!$this->currentSessionHash) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$this->session = MultiFormSession::get()->filter(array(
|
||||
"Hash" => $this->currentSessionHash,
|
||||
"IsComplete" => 0
|
||||
))->first();
|
||||
|
||||
return $this->session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all steps saved in the database for the currently active session,
|
||||
* in the order they were saved, oldest to newest (automatically ordered by ID).
|
||||
* If you want a full chain of steps regardless if they've already been saved
|
||||
* to the database, use {@link getAllStepsLinear()}.
|
||||
*
|
||||
* @param string $filter SQL WHERE statement
|
||||
* @return DataObjectSet|boolean A set of MultiFormStep subclasses
|
||||
*/
|
||||
public function getSavedSteps($filter = null) {
|
||||
$filter .= ($filter) ? ' AND ' : '';
|
||||
$filter .= sprintf("\"SessionID\" = '%s'", $this->session->ID);
|
||||
return DataObject::get('MultiFormStep', $filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a step which was previously saved to the database in the current session.
|
||||
* Caution: This might cause unexpected behaviour if you have multiple steps
|
||||
* in your chain with the same classname.
|
||||
*
|
||||
* @param string $className Classname of a {@link MultiFormStep} subclass
|
||||
* @return MultiFormStep
|
||||
*/
|
||||
public function getSavedStepByClass($className) {
|
||||
return DataObject::get_one(
|
||||
'MultiFormStep',
|
||||
sprintf("\"SessionID\" = '%s' AND \"ClassName\" = '%s'",
|
||||
$this->session->ID,
|
||||
Convert::raw2sql($className)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a FieldList of the FormAction fields for the given step.
|
||||
*
|
||||
* If the current step is the final step, we push in a submit button, which
|
||||
* calls the action {@link finish()} to finalise the submission. Otherwise,
|
||||
* we push in a next button which calls the action {@link next()} to determine
|
||||
* where to go next in our step process, and save any form data collected.
|
||||
*
|
||||
* If there's a previous step (a step that has the current step as it's next
|
||||
* step class), then we allow a previous button, which calls the previous action
|
||||
* to determine which step to go back to.
|
||||
*
|
||||
* If there are any extra actions defined in MultiFormStep->getExtraActions()
|
||||
* then that set of actions is appended to the end of the actions FieldSet we
|
||||
* have created in this method.
|
||||
*
|
||||
* @param $currentStep Subclass of MultiFormStep
|
||||
* @return FieldList of FormAction objects
|
||||
*/
|
||||
public function actionsFor($step) {
|
||||
// Create default multi step actions (next, prev), and merge with extra actions, if any
|
||||
$actions = (class_exists('FieldList')) ? new FieldList() : new FieldSet();
|
||||
|
||||
// If the form is at final step, create a submit button to perform final actions
|
||||
// The last step doesn't have a next button, so add that action to any step that isn't the final one
|
||||
if($step->isFinalStep()) {
|
||||
$actions->push(new FormAction('finish', $step->getSubmitText()));
|
||||
} else {
|
||||
$actions->push(new FormAction('next', $step->getNextText()));
|
||||
}
|
||||
|
||||
// If there is a previous step defined, add the back button
|
||||
if($step->getPreviousStep() && $step->canGoBack()) {
|
||||
// If there is a next step, insert the action before the next action
|
||||
if($step->getNextStep()) {
|
||||
$actions->insertBefore($prev = new FormAction('prev', $step->getPrevText()), 'action_next');
|
||||
// Assume that this is the last step, insert the action before the finish action
|
||||
} else {
|
||||
$actions->insertBefore($prev = new FormAction('prev', $step->getPrevText()), 'action_finish');
|
||||
}
|
||||
//remove browser validation from prev action
|
||||
$prev->setAttribute("formnovalidate", "formnovalidate");
|
||||
}
|
||||
|
||||
// Merge any extra action fields defined on the step
|
||||
$actions->merge($step->getExtraActions());
|
||||
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a rendered version of this form, with a specific template.
|
||||
* Looks through the step ancestory templates (MultiFormStep, current step
|
||||
* subclass template) to see if one is available to render the form with. If
|
||||
* any of those don't exist, look for a default Form template to render
|
||||
* with instead.
|
||||
*
|
||||
* @return SSViewer object to render the template with
|
||||
*/
|
||||
public function forTemplate() {
|
||||
$return = $this->renderWith(array(
|
||||
$this->getCurrentStep()->class,
|
||||
'MultiFormStep',
|
||||
$this->class,
|
||||
'MultiForm',
|
||||
'Form'
|
||||
));
|
||||
|
||||
$this->clearMessage();
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method saves the data on the final step, after submitting.
|
||||
* It should always be overloaded with parent::finish($data, $form)
|
||||
* so you can create your own functionality which handles saving
|
||||
* of all the data collected through each step of the form.
|
||||
*
|
||||
* @param array $data The request data returned from the form
|
||||
* @param object $form The form that the action was called on
|
||||
*/
|
||||
public function finish($data, $form) {
|
||||
// Save the form data for the current step
|
||||
$this->save($data);
|
||||
|
||||
if(!$this->getCurrentStep()->isFinalStep()) {
|
||||
$this->controller->redirectBack();
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!$this->getCurrentStep()->validateStep($data, $form)) {
|
||||
Session::set("FormInfo.{$form->FormName()}.data", $form->getData());
|
||||
$this->controller->redirectBack();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine what to do when the next action is called.
|
||||
*
|
||||
* Saves the current step session data to the database, creates the
|
||||
* new step based on getNextStep() of the current step (or fetches
|
||||
* an existing one), resets the current step to the next step,
|
||||
* then redirects to the newly set step.
|
||||
*
|
||||
* @param array $data The request data returned from the form
|
||||
* @param object $form The form that the action was called on
|
||||
*/
|
||||
public function next($data, $form) {
|
||||
// Save the form data for the current step
|
||||
$this->save($form->getData());
|
||||
|
||||
// Get the next step class
|
||||
$nextStepClass = $this->getCurrentStep()->getNextStep();
|
||||
|
||||
if(!$nextStepClass) {
|
||||
$this->controller->redirectBack();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Perform custom step validation (use MultiFormStep->getValidator() for
|
||||
// built-in functionality). The data needs to be manually saved on error
|
||||
// so the form is re-populated.
|
||||
if(!$this->getCurrentStep()->validateStep($data, $form)) {
|
||||
Session::set("FormInfo.{$form->FormName()}.data", $form->getData());
|
||||
$this->controller->redirectBack();
|
||||
return false;
|
||||
}
|
||||
|
||||
// validation succeeded so we reset it to remove errors and messages
|
||||
$this->resetValidation();
|
||||
|
||||
// Determine whether we can use a step already in the DB, or have to create a new one
|
||||
if(!$nextStep = DataObject::get_one($nextStepClass, "\"SessionID\" = {$this->session->ID}")) {
|
||||
$nextStep = Object::create($nextStepClass);
|
||||
$nextStep->SessionID = $this->session->ID;
|
||||
$nextStep->write();
|
||||
}
|
||||
|
||||
// Set the next step found as the current step
|
||||
$this->setCurrentStep($nextStep);
|
||||
|
||||
// Redirect to the next step
|
||||
$this->controller->redirect($nextStep->Link());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine what to do when the previous action is called.
|
||||
*
|
||||
* Retrieves the previous step class, finds the record for that
|
||||
* class in the DB, and sets the current step to that step found.
|
||||
* Finally, it redirects to that step.
|
||||
*
|
||||
* @param array $data The request data returned from the form
|
||||
* @param object $form The form that the action was called on
|
||||
*/
|
||||
public function prev($data, $form) {
|
||||
// Save the form data for the current step
|
||||
$this->save($form->getData());
|
||||
|
||||
// Get the previous step class
|
||||
$prevStepClass = $this->getCurrentStep()->getPreviousStep();
|
||||
|
||||
if(!$prevStepClass && !$this->getCurrentStep()->canGoBack()) {
|
||||
$this->controller->redirectBack();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the previous step of the class instance returned from $currentStep->getPreviousStep()
|
||||
$prevStep = DataObject::get_one($prevStepClass, "\"SessionID\" = {$this->session->ID}");
|
||||
|
||||
// Set the current step as the previous step
|
||||
$this->setCurrentStep($prevStep);
|
||||
|
||||
// Redirect to the previous step
|
||||
$this->controller->redirect($prevStep->Link());
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the raw data given back from the form into session.
|
||||
*
|
||||
* Take the submitted form data for the current step, removing
|
||||
* any key => value pairs that shouldn't be saved, then saves
|
||||
* the data into the session.
|
||||
*
|
||||
* @param array $data An array of data to save
|
||||
*/
|
||||
protected function save($data) {
|
||||
$currentStep = $this->getCurrentStep();
|
||||
if(is_array($data)) {
|
||||
foreach($data as $field => $value) {
|
||||
if(in_array($field, static::$ignored_fields)) {
|
||||
unset($data[$field]);
|
||||
}
|
||||
}
|
||||
$currentStep->saveData($data);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// ############ Misc ############
|
||||
|
||||
/**
|
||||
* Add the MultiFormSessionID variable to the URL on form submission.
|
||||
* This is a means to persist the session, by adding it's identification
|
||||
* to the URL, which ties it back to this MultiForm instance.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function FormAction() {
|
||||
$action = parent::FormAction();
|
||||
$action .= (strpos($action, '?')) ? '&' : '?';
|
||||
$action .= "{$this->config()->get_var}={$this->session->Hash}";
|
||||
|
||||
return $action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the link to the page where the form is displayed. The user is
|
||||
* redirected to this link with a session param after each step is
|
||||
* submitted.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDisplayLink() {
|
||||
return $this->displayLink ? $this->displayLink : Controller::curr()->Link();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the link to the page on which the form is displayed.
|
||||
*
|
||||
* The link defaults to the controllers current link. However if the form
|
||||
* is displayed inside an action the display link must be explicitly set.
|
||||
*
|
||||
* @param string $link
|
||||
*/
|
||||
public function setDisplayLink($link) {
|
||||
$this->displayLink = $link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the steps to show in a linear fashion, starting from the
|
||||
* first step. We run {@link getAllStepsRecursive} passing the steps found
|
||||
* by reference to get a listing of the steps.
|
||||
*
|
||||
* @return DataObjectSet of MultiFormStep instances
|
||||
*/
|
||||
public function getAllStepsLinear() {
|
||||
$stepsFound = (class_exists('ArrayList')) ? new ArrayList() : new DataObjectSet();
|
||||
|
||||
$firstStep = DataObject::get_one(static::$start_step, "\"SessionID\" = {$this->session->ID}");
|
||||
$firstStep->LinkingMode = ($firstStep->ID == $this->getCurrentStep()->ID) ? 'current' : 'link';
|
||||
$firstStep->setForm($this);
|
||||
$stepsFound->push($firstStep);
|
||||
|
||||
// mark the further steps as non-completed if the first step is the current
|
||||
if ($firstStep->ID == $this->getCurrentStep()->ID) {
|
||||
$this->currentStepHasBeenFound = true;
|
||||
} else {
|
||||
$firstStep->addExtraClass('completed');
|
||||
}
|
||||
|
||||
$this->getAllStepsRecursive($firstStep, $stepsFound);
|
||||
|
||||
return $stepsFound;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively run through steps using the getNextStep() method on each step
|
||||
* to determine what the next step is, gathering each step along the way.
|
||||
* We stop on the last step, and return the results.
|
||||
* If a step in the chain was already saved to the database in the current
|
||||
* session, its used - otherwise a singleton of this step is used.
|
||||
* Caution: Doesn't consider branching for steps which aren't in the database yet.
|
||||
*
|
||||
* @param $step Subclass of MultiFormStep to find the next step of
|
||||
* @param $stepsFound $stepsFound DataObjectSet reference, the steps found to call back on
|
||||
* @return DataObjectSet of MultiFormStep instances
|
||||
*/
|
||||
protected function getAllStepsRecursive($step, &$stepsFound) {
|
||||
// Find the next step to the current step, the final step has no next step
|
||||
if(!$step->isFinalStep()) {
|
||||
if($step->getNextStep()) {
|
||||
// Is this step in the DB? If it is, we use that
|
||||
$nextStep = $step->getNextStepFromDatabase();
|
||||
if(!$nextStep) {
|
||||
// If it's not in the DB, we use a singleton instance of it instead -
|
||||
// - this step hasn't been accessed yet
|
||||
$nextStep = singleton($step->getNextStep());
|
||||
}
|
||||
|
||||
// once the current steps has been found we won't add the completed class anymore.
|
||||
if ($nextStep->ID == $this->getCurrentStep()->ID) $this->currentStepHasBeenFound = true;
|
||||
|
||||
$nextStep->LinkingMode = ($nextStep->ID == $this->getCurrentStep()->ID) ? 'current' : 'link';
|
||||
|
||||
// add the completed class
|
||||
if (!$this->currentStepHasBeenFound) $nextStep->addExtraClass('completed');
|
||||
|
||||
$nextStep->setForm($this);
|
||||
|
||||
// Add the array data, and do a callback
|
||||
$stepsFound->push($nextStep);
|
||||
$this->getAllStepsRecursive($nextStep, $stepsFound);
|
||||
}
|
||||
// Once we've reached the final step, we just return what we've collected
|
||||
} else {
|
||||
return $stepsFound;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of steps already completed (excluding currently started step).
|
||||
* The way we determine a step is complete is to check if it has the Data
|
||||
* field filled out with a serialized value, then we know that the user has
|
||||
* clicked next on the given step, to proceed.
|
||||
*
|
||||
* @TODO Not sure if it's entirely appropriate to check if Data is set as a
|
||||
* way to determine a step is "completed".
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getCompletedStepCount() {
|
||||
$steps = DataObject::get('MultiFormStep', "\"SessionID\" = {$this->session->ID} && \"Data\" IS NOT NULL");
|
||||
|
||||
return $steps ? $steps->Count() : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Total number of steps in the shortest path (only counting straight path without any branching)
|
||||
* The way we determine this is to check if each step has a next_step string variable set. If it's
|
||||
* anything else (like an array, for defining multiple branches) then it gets counted as a single step.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getTotalStepCount() {
|
||||
return $this->getAllStepsLinear() ? $this->getAllStepsLinear()->Count() : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Percentage of steps completed (excluding currently started step)
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getCompletedPercent() {
|
||||
return (float) $this->getCompletedStepCount() * 100 / $this->getTotalStepCount();
|
||||
}
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Serializes one or more {@link MultiFormStep}s into
|
||||
* a database object.
|
||||
*
|
||||
* MultiFormSession also stores the current step, so that
|
||||
* the {@link MultiForm} and {@link MultiFormStep} classes
|
||||
* know what the current step is.
|
||||
*
|
||||
* @package multiform
|
||||
*/
|
||||
class MultiFormSession extends DataObject {
|
||||
|
||||
private static $db = array(
|
||||
'Hash' => 'Varchar(40)', // cryptographic hash identification to this session
|
||||
'IsComplete' => 'Boolean' // flag to determine if this session is marked completed
|
||||
);
|
||||
|
||||
private static $has_one = array(
|
||||
'Submitter' => 'Member',
|
||||
'CurrentStep' => 'MultiFormStep'
|
||||
);
|
||||
|
||||
private static $has_many = array(
|
||||
'FormSteps' => 'MultiFormStep'
|
||||
);
|
||||
|
||||
/**
|
||||
* Mark this session as completed.
|
||||
*
|
||||
* This sets the flag "IsComplete" to true,
|
||||
* and writes the session back.
|
||||
*/
|
||||
public function markCompleted() {
|
||||
$this->IsComplete = 1;
|
||||
$this->write();
|
||||
}
|
||||
|
||||
/**
|
||||
* These actions are performed when write() is called on this object.
|
||||
*/
|
||||
public function onBeforeWrite() {
|
||||
// save submitter if a Member is logged in
|
||||
$currentMember = Member::currentUser();
|
||||
if(!$this->SubmitterID && $currentMember) $this->SubmitterID = $currentMember->ID;
|
||||
|
||||
parent::onBeforeWrite();
|
||||
}
|
||||
|
||||
/**
|
||||
* These actions are performed when delete() is called on this object.
|
||||
*/
|
||||
public function onBeforeDelete() {
|
||||
// delete dependent form steps and relation
|
||||
$steps = $this->FormSteps();
|
||||
if($steps) foreach($steps as $step) {
|
||||
if($step && $step->exists()) {
|
||||
$steps->remove($step);
|
||||
$step->delete();
|
||||
$step->destroy();
|
||||
}
|
||||
}
|
||||
|
||||
parent::onBeforeDelete();
|
||||
}
|
||||
|
||||
}
|
@ -1,465 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* MultiFormStep controls the behaviour of a single form step in the MultiForm
|
||||
* process. All form steps are required to be subclasses of this class, as it
|
||||
* encapsulates the functionality required for the step to be aware of itself
|
||||
* in the process by knowing what it's next step is, and if applicable, it's previous
|
||||
* step.
|
||||
*
|
||||
* @package multiform
|
||||
*/
|
||||
class MultiFormStep extends DataObject {
|
||||
|
||||
private static $db = array(
|
||||
'Data' => 'Text' // stores serialized maps with all session information
|
||||
);
|
||||
|
||||
private static $has_one = array(
|
||||
'Session' => 'MultiFormSession'
|
||||
);
|
||||
|
||||
/**
|
||||
* Centerpiece of the flow control for the form.
|
||||
*
|
||||
* If set to a string, you have a linear form flow
|
||||
* If set to an array, you should use {@link getNextStep()}
|
||||
* to enact flow control and branching to different form
|
||||
* steps, most likely based on previously set session data
|
||||
* (e.g. a checkbox field or a dropdown).
|
||||
*
|
||||
* @var array|string
|
||||
*/
|
||||
public static $next_steps;
|
||||
|
||||
/**
|
||||
* Each {@link MultiForm} subclass needs at least
|
||||
* one step which is marked as the "final" one
|
||||
* and triggers the {@link MultiForm->finish()}
|
||||
* method that wraps up the whole submission.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public static $is_final_step = false;
|
||||
|
||||
/**
|
||||
* This variable determines whether a user can use
|
||||
* the "back" action from this step.
|
||||
*
|
||||
* @TODO This does not check if the arbitrarily chosen step
|
||||
* using the step indicator is actually a previous step, so
|
||||
* unless you remove the link from the indicator template, or
|
||||
* type in StepID=23 to the address bar you can still go back
|
||||
* using the step indicator.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected static $can_go_back = true;
|
||||
|
||||
/**
|
||||
* Title of this step.
|
||||
*
|
||||
* Used for the step indicator templates.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $title;
|
||||
|
||||
/**
|
||||
* Form class that this step is directly related to.
|
||||
*
|
||||
* @var MultiForm subclass
|
||||
*/
|
||||
protected $form;
|
||||
|
||||
/**
|
||||
* List of additional CSS classes for this step
|
||||
*
|
||||
* @var array $extraClasses
|
||||
*/
|
||||
protected $extraClasses = array();
|
||||
|
||||
/**
|
||||
* Temporary cache to increase the performance for repeated look ups.
|
||||
*
|
||||
* @var array $cache
|
||||
*/
|
||||
protected $step_data_cache = array();
|
||||
|
||||
/**
|
||||
* Form fields to be rendered with this step.
|
||||
* (Form object is created in {@link MultiForm}.
|
||||
*
|
||||
* This function needs to be implemented on your
|
||||
* subclasses of MultiFormStep.
|
||||
*
|
||||
* @return FieldList
|
||||
*/
|
||||
public function getFields() {
|
||||
user_error('Please implement getFields on your MultiFormStep subclass', E_USER_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional form actions to be added to this step.
|
||||
* (Form object is created in {@link MultiForm}.
|
||||
*
|
||||
* Note: This is optional, and is to be implemented
|
||||
* on your subclasses of MultiFormStep.
|
||||
*
|
||||
* @return FieldList
|
||||
*/
|
||||
public function getExtraActions() {
|
||||
return (class_exists('FieldList')) ? new FieldList() : new FieldSet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a validator specific to this form.
|
||||
* The form is automatically validated in {@link Form->httpSubmission()}.
|
||||
*
|
||||
* @return Validator
|
||||
*/
|
||||
public function getValidator() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor method for $this->title
|
||||
*
|
||||
* @return string Title of this step
|
||||
*/
|
||||
public function getTitle() {
|
||||
return $this->title ? $this->title : $this->class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a direct link to this step (only works
|
||||
* if you're allowed to skip steps, or this step
|
||||
* has already been saved to the database
|
||||
* for the current {@link MultiFormSession}).
|
||||
*
|
||||
* @return string Relative URL to this step
|
||||
*/
|
||||
public function Link() {
|
||||
$form = $this->form;
|
||||
return Controller::join_links($form->getDisplayLink(), "?{$form->config()->get_var}={$this->Session()->Hash}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Unserialize stored session data and return it.
|
||||
* This is used for loading data previously saved
|
||||
* in session back into the form.
|
||||
*
|
||||
* You need to overload this method onto your own
|
||||
* step if you require custom loading. An example
|
||||
* would be selective loading specific fields, leaving
|
||||
* others that are not required.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function loadData() {
|
||||
return ($this->Data && is_string($this->Data)) ? unserialize($this->Data) : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the data for this step into session, serializing it first.
|
||||
*
|
||||
* To selectively save fields, instead of it all, this
|
||||
* method would need to be overloaded on your step class.
|
||||
*
|
||||
* @param array $data The processed data from save() on {@link MultiForm}
|
||||
*/
|
||||
public function saveData($data) {
|
||||
$this->Data = serialize($data);
|
||||
$this->write();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the data on this step into an object,
|
||||
* similiar to {@link Form->saveInto()} - by building
|
||||
* a stub form from {@link getFields()}. This is necessary
|
||||
* to trigger each {@link FormField->saveInto()} method
|
||||
* individually, rather than assuming that all data
|
||||
* serialized through {@link saveData()} can be saved
|
||||
* as a simple value outside of the original FormField context.
|
||||
*
|
||||
* @param DataObject $obj
|
||||
*/
|
||||
public function saveInto($obj) {
|
||||
$form = new Form(
|
||||
Controller::curr(),
|
||||
'Form',
|
||||
$this->getFields(),
|
||||
((class_exists('FieldList')) ? new FieldList() : new FieldSet())
|
||||
);
|
||||
$form->loadDataFrom($this->loadData());
|
||||
$form->saveInto($obj);
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom validation for a step. In most cases, it should be sufficient
|
||||
* to have built-in validation through the {@link Validator} class
|
||||
* on the {@link getValidator()} method.
|
||||
*
|
||||
* Use {@link Form->sessionMessage()} to feed back validation messages
|
||||
* to the user. Please don't redirect from this method,
|
||||
* this is taken care of in {@link next()}.
|
||||
*
|
||||
* @param array $data Request data
|
||||
* @param Form $form
|
||||
* @return boolean Validation success
|
||||
*/
|
||||
public function validateStep($data, $form) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first value of $next_step
|
||||
*
|
||||
* @return string Classname of a {@link MultiFormStep} subclass
|
||||
*/
|
||||
public function getNextStep() {
|
||||
$nextSteps = static::$next_steps;
|
||||
|
||||
// Check if next_steps have been implemented properly if not the final step
|
||||
if(!$this->isFinalStep()) {
|
||||
if(!isset($nextSteps)) user_error('MultiFormStep->getNextStep(): Please define at least one $next_steps on ' . $this->class, E_USER_ERROR);
|
||||
}
|
||||
|
||||
if(is_string($nextSteps)) {
|
||||
return $nextSteps;
|
||||
} elseif(is_array($nextSteps) && count($nextSteps)) {
|
||||
// custom flow control goes here
|
||||
return $nextSteps[0];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next step to the current step in the database.
|
||||
*
|
||||
* This will only return something if you've previously visited
|
||||
* the step ahead of the current step, and then gone back a step.
|
||||
*
|
||||
* @return MultiFormStep|boolean
|
||||
*/
|
||||
public function getNextStepFromDatabase() {
|
||||
if($this->SessionID && is_numeric($this->SessionID)) {
|
||||
$nextSteps = static::$next_steps;
|
||||
|
||||
if(is_string($nextSteps)) {
|
||||
return DataObject::get_one($nextSteps, "\"SessionID\" = {$this->SessionID}");
|
||||
} elseif(is_array($nextSteps)) {
|
||||
return DataObject::get_one($nextSteps[0], "\"SessionID\" = {$this->SessionID}");
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor method for self::$next_steps
|
||||
*
|
||||
* @return string|array
|
||||
*/
|
||||
public function getNextSteps() {
|
||||
return static::$next_steps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the previous step, if there is one.
|
||||
*
|
||||
* To determine if there is a previous step, we check the database to see if there's
|
||||
* a previous step for this multi form session ID.
|
||||
*
|
||||
* @return string Classname of a {@link MultiFormStep} subclass
|
||||
*/
|
||||
public function getPreviousStep() {
|
||||
$steps = DataObject::get('MultiFormStep', "\"SessionID\" = {$this->SessionID}", '"LastEdited" DESC');
|
||||
if($steps) {
|
||||
foreach($steps as $step) {
|
||||
$step->setForm($this->form);
|
||||
|
||||
if($step->getNextStep()) {
|
||||
if($step->getNextStep() == $this->class) {
|
||||
return $step->class;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the previous step class record from the database.
|
||||
*
|
||||
* This will only return a record if you've previously been on the step.
|
||||
*
|
||||
* @return MultiFormStep subclass
|
||||
*/
|
||||
public function getPreviousStepFromDatabase() {
|
||||
if($prevStepClass = $this->getPreviousStep()) {
|
||||
return DataObject::get_one($prevStepClass, "\"SessionID\" = {$this->SessionID}");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the text to the use on the button to the previous step.
|
||||
* @return string
|
||||
*/
|
||||
public function getPrevText() {
|
||||
return _t('MultiForm.BACK', 'Back');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the text to use on the button to the next step.
|
||||
* @return string
|
||||
*/
|
||||
public function getNextText() {
|
||||
return _t('MultiForm.NEXT', 'Next');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the text to use on the button to submit the form.
|
||||
* @return string
|
||||
*/
|
||||
public function getSubmitText() {
|
||||
return _t('MultiForm.SUBMIT', 'Submit');
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the form that this step is directly related to.
|
||||
*
|
||||
* @param MultiForm subclass $form
|
||||
*/
|
||||
public function setForm($form) {
|
||||
$this->form = $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Form
|
||||
*/
|
||||
public function getForm() {
|
||||
return $this->form;
|
||||
}
|
||||
|
||||
// ##################### Utility ####################
|
||||
|
||||
/**
|
||||
* Determines whether the user is able to go back using the "action_back"
|
||||
* Determines whether the user is able to go back using the "action_back"
|
||||
* Determines whether the user is able to go back using the "action_back"
|
||||
* form action, based on the boolean value of $can_go_back.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function canGoBack() {
|
||||
return static::$can_go_back;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether this step is the final step in the multi-step process or not,
|
||||
* based on the variable $is_final_step - which must be defined on at least one step.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isFinalStep() {
|
||||
return static::$is_final_step;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the currently viewed step is the current step set in the session.
|
||||
* This assumes you are checking isCurrentStep() against a data record of a MultiFormStep
|
||||
* subclass, otherwise it doesn't work. An example of this is using a singleton instance - it won't
|
||||
* work because there's no data.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isCurrentStep() {
|
||||
return ($this->class == $this->Session()->CurrentStep()->class) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a CSS-class to the step. If needed, multiple classes can be added by delimiting a string with spaces.
|
||||
*
|
||||
* @param string $class A string containing a classname or several class names delimited by a space.
|
||||
* @return MultiFormStep
|
||||
*/
|
||||
public function addExtraClass($class) {
|
||||
// split at white space
|
||||
$classes = preg_split('/\s+/', $class);
|
||||
foreach($classes as $class) {
|
||||
// add classes one by one
|
||||
$this->extraClasses[$class] = $class;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a CSS-class from the step. Multiple classes names can be passed through as a space delimited string.
|
||||
*
|
||||
* @param string $class
|
||||
* @return MultiFormStep
|
||||
*/
|
||||
public function removeExtraClass($class) {
|
||||
// split at white space
|
||||
$classes = preg_split('/\s+/', $class);
|
||||
foreach ($classes as $class) {
|
||||
// unset one by one
|
||||
unset($this->extraClasses[$class]);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getExtraClasses() {
|
||||
return join(' ', array_keys($this->extraClasses));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the submitted value, if any, of any steps.
|
||||
*
|
||||
* @param string $fromStep (classname)
|
||||
* @param string $key
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getValueFromOtherStep($fromStep, $key) {
|
||||
// load the steps in the cache, if this one doesn't exist
|
||||
if (!array_key_exists('steps_' . $fromStep, $this->step_data_cache)) {
|
||||
$steps = MultiFormStep::get()->filter('SessionID', $this->form->session->ID);
|
||||
|
||||
if($steps) {
|
||||
foreach($steps as $step) {
|
||||
$this->step_data_cache['steps_' . $step->ClassName] = $step->loadData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check both as PHP isn't recursive
|
||||
if(isset($this->step_data_cache['steps_' . $fromStep])) {
|
||||
if(isset($this->step_data_cache['steps_' . $fromStep][$key])) {
|
||||
return $this->step_data_cache['steps_' . $fromStep][$key];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* allows to get a value from another step copied over
|
||||
*
|
||||
* @param FieldList $fields
|
||||
* @param string $formStep
|
||||
* @param string $fieldName
|
||||
* @param string $fieldNameTarget (optional)
|
||||
*/
|
||||
public function copyValueFromOtherStep(FieldList $fields, $formStep, $fieldName, $fieldNameTarget = null) {
|
||||
// if a target field isn't defined use the same fieldname
|
||||
if (!$fieldNameTarget) $fieldNameTarget = $fieldName;
|
||||
|
||||
$fields->fieldByName($fieldNameTarget)->setValue($this->getValueFromOtherStep($formStep, $fieldName));
|
||||
}
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Task to clean out all {@link MultiFormSession} objects from the database.
|
||||
*
|
||||
* Setup Instructions:
|
||||
* You need to create an automated task for your system (cronjobs on unix)
|
||||
* which triggers the process() method through cli-script.php:
|
||||
* `php framework/cli-script.php MultiFormPurgeTask`
|
||||
* or
|
||||
* `framework/sake MultiFormPurgeTask`
|
||||
*
|
||||
* @package multiform
|
||||
*/
|
||||
class MultiFormPurgeTask extends BuildTask {
|
||||
|
||||
/**
|
||||
* Days after which sessions expire and
|
||||
* are automatically deleted.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public static $session_expiry_days = 7;
|
||||
|
||||
/**
|
||||
* Run this cron task.
|
||||
*
|
||||
* Go through all MultiFormSession records that
|
||||
* are older than the days specified in $session_expiry_days
|
||||
* and delete them.
|
||||
*/
|
||||
public function run($request) {
|
||||
$sessions = $this->getExpiredSessions();
|
||||
$delCount = 0;
|
||||
if($sessions) foreach($sessions as $session) {
|
||||
$session->delete();
|
||||
$delCount++;
|
||||
}
|
||||
echo $delCount . ' session records deleted that were older than ' . self::$session_expiry_days . ' days.';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all MultiFormSession database records that are older than
|
||||
* the days specified in $session_expiry_days
|
||||
*
|
||||
* @return DataObjectSet
|
||||
*/
|
||||
protected function getExpiredSessions() {
|
||||
return DataObject::get(
|
||||
'MultiFormSession',
|
||||
"DATEDIFF(NOW(), \"MultiFormSession\".\"Created\") > " . self::$session_expiry_days
|
||||
);
|
||||
}
|
||||
|
||||
}
|
1
codecov.yml
Normal file
1
codecov.yml
Normal file
@ -0,0 +1 @@
|
||||
comment: false
|
@ -1,22 +1,41 @@
|
||||
{
|
||||
"name": "silverstripe/multiform",
|
||||
"description": "SilverStripe forms with multiple steps, flow control and state persistence",
|
||||
"type": "silverstripe-module",
|
||||
"keywords": ["silverstripe", "forms"],
|
||||
"authors": [{
|
||||
"name": "Ingo Schommer",
|
||||
"email": "ingo@silverstripe.com"
|
||||
}, {
|
||||
"name": "Sean Harvey",
|
||||
"email": "sean@silverstripe.com"
|
||||
}],
|
||||
"require": {
|
||||
"silverstripe/framework": "~3.2"
|
||||
},
|
||||
"license": "BSD-3-Clause",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.x-dev"
|
||||
}
|
||||
}
|
||||
"name": "silverstripe/multiform",
|
||||
"description": "SilverStripe forms with multiple steps, flow control and state persistence",
|
||||
"type": "silverstripe-module",
|
||||
"keywords": [
|
||||
"silverstripe",
|
||||
"forms"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Ingo Schommer",
|
||||
"email": "ingo@silverstripe.com"
|
||||
},
|
||||
{
|
||||
"name": "Sean Harvey",
|
||||
"email": "sean@silverstripe.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"silverstripe/framework": "^4@dev"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^5.7",
|
||||
"squizlabs/php_codesniffer": "^3.0",
|
||||
"silverstripe/versioned": "^1@dev"
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"SilverStripe\\MultiForm\\": "src/",
|
||||
"SilverStripe\\MultiForm\\Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"license": "BSD-3-Clause",
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true
|
||||
}
|
||||
|
@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Arabic (Saudi Arabia) language pack
|
||||
* @package modules: multiform
|
||||
* @subpackage i18n
|
||||
*/
|
||||
|
||||
i18n::include_locale_file('modules: multiform', 'en_US');
|
||||
|
||||
global $lang;
|
||||
|
||||
if(array_key_exists('ar_SA', $lang) && is_array($lang['ar_SA'])) {
|
||||
$lang['ar_SA'] = array_merge($lang['en_US'], $lang['ar_SA']);
|
||||
} else {
|
||||
$lang['ar_SA'] = $lang['en_US'];
|
||||
}
|
||||
|
||||
$lang['ar_SA']['MultiForm']['BACK'] = 'السابق';
|
||||
$lang['ar_SA']['MultiForm']['NEXT'] = 'التالي';
|
||||
$lang['ar_SA']['MultiForm']['SUBMIT'] = 'إرسال';
|
||||
$lang['ar_SA']['MultiFormSession']['db_Hash'] = 'Hash';
|
||||
$lang['ar_SA']['MultiFormSession']['db_IsComplete'] = 'مكتمل؟';
|
||||
$lang['ar_SA']['MultiFormSession']['has_many_FormSteps'] = 'خطوات النموذج';
|
||||
$lang['ar_SA']['MultiFormSession']['plural_name'] = '(لايوجد)';
|
||||
$lang['ar_SA']['MultiFormSession']['singular_name'] = '(لايوجد)';
|
||||
$lang['ar_SA']['MultiFormStep']['db_Data'] = 'بيانات';
|
||||
$lang['ar_SA']['MultiFormStep']['plural_name'] = '(لايوجد)';
|
||||
$lang['ar_SA']['MultiFormStep']['singular_name'] = '(لايوجد)';
|
||||
|
||||
?>
|
@ -1,27 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Bulgarian (Bulgaria) language pack
|
||||
* @package modules: multiform
|
||||
* @subpackage i18n
|
||||
*/
|
||||
|
||||
i18n::include_locale_file('modules: multiform', 'en_US');
|
||||
|
||||
global $lang;
|
||||
|
||||
if(array_key_exists('bg_BG', $lang) && is_array($lang['bg_BG'])) {
|
||||
$lang['bg_BG'] = array_merge($lang['en_US'], $lang['bg_BG']);
|
||||
} else {
|
||||
$lang['bg_BG'] = $lang['en_US'];
|
||||
}
|
||||
|
||||
$lang['bg_BG']['MultiForm']['BACK'] = 'Назад';
|
||||
$lang['bg_BG']['MultiForm']['NEXT'] = 'Следващо';
|
||||
$lang['bg_BG']['MultiForm']['SUBMIT'] = 'Прати';
|
||||
$lang['bg_BG']['MultiFormSession']['plural_name'] = '(никакви)';
|
||||
$lang['bg_BG']['MultiFormSession']['singular_name'] = '(никакво)';
|
||||
$lang['bg_BG']['MultiFormStep']['plural_name'] = '(никакви)';
|
||||
$lang['bg_BG']['MultiFormStep']['singular_name'] = '(никакво)';
|
||||
|
||||
?>
|
@ -1,28 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Bosnian (Bosnia and Herzegovina) language pack
|
||||
* @package modules: multiform
|
||||
* @subpackage i18n
|
||||
*/
|
||||
|
||||
i18n::include_locale_file('modules: multiform', 'en_US');
|
||||
|
||||
global $lang;
|
||||
|
||||
if(array_key_exists('bs_BA', $lang) && is_array($lang['bs_BA'])) {
|
||||
$lang['bs_BA'] = array_merge($lang['en_US'], $lang['bs_BA']);
|
||||
} else {
|
||||
$lang['bs_BA'] = $lang['en_US'];
|
||||
}
|
||||
|
||||
$lang['bs_BA']['MultiFormSession']['db_Hash'] = 'Hash';
|
||||
$lang['bs_BA']['MultiFormSession']['db_IsComplete'] = 'JeZavršen';
|
||||
$lang['bs_BA']['MultiFormSession']['has_many_FormSteps'] = 'KoraciForme';
|
||||
$lang['bs_BA']['MultiFormSession']['plural_name'] = '(ništa)';
|
||||
$lang['bs_BA']['MultiFormSession']['singular_name'] = '(ništa)';
|
||||
$lang['bs_BA']['MultiFormStep']['db_Data'] = 'Podaci';
|
||||
$lang['bs_BA']['MultiFormStep']['plural_name'] = '(ništa)';
|
||||
$lang['bs_BA']['MultiFormStep']['singular_name'] = '(ništa)';
|
||||
|
||||
?>
|
@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Czech (Czech Republic) language pack
|
||||
* @package modules: multiform
|
||||
* @subpackage i18n
|
||||
*/
|
||||
|
||||
i18n::include_locale_file('modules: multiform', 'en_US');
|
||||
|
||||
global $lang;
|
||||
|
||||
if(array_key_exists('cs_CZ', $lang) && is_array($lang['cs_CZ'])) {
|
||||
$lang['cs_CZ'] = array_merge($lang['en_US'], $lang['cs_CZ']);
|
||||
} else {
|
||||
$lang['cs_CZ'] = $lang['en_US'];
|
||||
}
|
||||
|
||||
$lang['cs_CZ']['MultiForm']['BACK'] = 'Zpět';
|
||||
$lang['cs_CZ']['MultiForm']['NEXT'] = 'Další';
|
||||
$lang['cs_CZ']['MultiForm']['SUBMIT'] = 'Odeslat';
|
||||
$lang['cs_CZ']['MultiFormSession']['db_Hash'] = 'Hash';
|
||||
$lang['cs_CZ']['MultiFormSession']['db_IsComplete'] = 'JeKompletni';
|
||||
$lang['cs_CZ']['MultiFormSession']['has_many_FormSteps'] = 'FormularoveKroky';
|
||||
$lang['cs_CZ']['MultiFormSession']['plural_name'] = '(žádný)';
|
||||
$lang['cs_CZ']['MultiFormSession']['singular_name'] = '(žádný)';
|
||||
$lang['cs_CZ']['MultiFormStep']['db_Data'] = 'Data';
|
||||
$lang['cs_CZ']['MultiFormStep']['plural_name'] = '(žádný)';
|
||||
$lang['cs_CZ']['MultiFormStep']['singular_name'] = '(žádný)';
|
||||
|
||||
?>
|
@ -1,28 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Danish (Denmark) language pack
|
||||
* @package modules: multiform
|
||||
* @subpackage i18n
|
||||
*/
|
||||
|
||||
i18n::include_locale_file('modules: multiform', 'en_US');
|
||||
|
||||
global $lang;
|
||||
|
||||
if(array_key_exists('da_DK', $lang) && is_array($lang['da_DK'])) {
|
||||
$lang['da_DK'] = array_merge($lang['en_US'], $lang['da_DK']);
|
||||
} else {
|
||||
$lang['da_DK'] = $lang['en_US'];
|
||||
}
|
||||
|
||||
$lang['da_DK']['MultiFormSession']['db_Hash'] = 'Havelåge';
|
||||
$lang['da_DK']['MultiFormSession']['db_IsComplete'] = 'IsComplete';
|
||||
$lang['da_DK']['MultiFormSession']['has_many_FormSteps'] = 'FormSteps';
|
||||
$lang['da_DK']['MultiFormSession']['plural_name'] = '(ingen)';
|
||||
$lang['da_DK']['MultiFormSession']['singular_name'] = '(ingen)';
|
||||
$lang['da_DK']['MultiFormStep']['db_Data'] = 'Data';
|
||||
$lang['da_DK']['MultiFormStep']['plural_name'] = '(ingen)';
|
||||
$lang['da_DK']['MultiFormStep']['singular_name'] = '(none)';
|
||||
|
||||
?>
|
@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* German (Germany) language pack
|
||||
* @package modules: multiform
|
||||
* @subpackage i18n
|
||||
*/
|
||||
|
||||
i18n::include_locale_file('modules: multiform', 'en_US');
|
||||
|
||||
global $lang;
|
||||
|
||||
if(array_key_exists('de_DE', $lang) && is_array($lang['de_DE'])) {
|
||||
$lang['de_DE'] = array_merge($lang['en_US'], $lang['de_DE']);
|
||||
} else {
|
||||
$lang['de_DE'] = $lang['en_US'];
|
||||
}
|
||||
|
||||
$lang['de_DE']['MultiForm']['BACK'] = 'Zurück';
|
||||
$lang['de_DE']['MultiForm']['NEXT'] = 'Weiter';
|
||||
$lang['de_DE']['MultiForm']['SUBMIT'] = 'Absenden';
|
||||
$lang['de_DE']['MultiFormSession']['db_Hash'] = 'Hash';
|
||||
$lang['de_DE']['MultiFormSession']['db_IsComplete'] = 'Abgeschlossen?';
|
||||
$lang['de_DE']['MultiFormSession']['has_many_FormSteps'] = 'Formularschritte';
|
||||
$lang['de_DE']['MultiFormSession']['plural_name'] = 'Multi-Formulare';
|
||||
$lang['de_DE']['MultiFormSession']['singular_name'] = 'Multi-Formular';
|
||||
$lang['de_DE']['MultiFormStep']['db_Data'] = 'Daten';
|
||||
$lang['de_DE']['MultiFormStep']['plural_name'] = 'Multi-Formular-Schritte';
|
||||
$lang['de_DE']['MultiFormStep']['singular_name'] = 'Multi-Formular-Schritt';
|
||||
|
||||
?>
|
@ -1,11 +1,11 @@
|
||||
en:
|
||||
MultiForm:
|
||||
SilverStripe\MultiForm\MultiForm:
|
||||
BACK: Back
|
||||
NEXT: Next
|
||||
SUBMIT: Submit
|
||||
MultiFormSession:
|
||||
SilverStripe\MultiForm\MultiFormSession:
|
||||
PLURALNAME: 'Multi Form Sessions'
|
||||
SINGULARNAME: 'Multi Form Session'
|
||||
MultiFormStep:
|
||||
SilverStripe\MultiForm\MultiFormStep:
|
||||
PLURALNAME: 'Multi Form Steps'
|
||||
SINGULARNAME: 'Multi Form Step'
|
||||
|
@ -1,29 +0,0 @@
|
||||
<?php
|
||||
|
||||
global $lang;
|
||||
|
||||
$lang['en_US']['MultiForm']['BACK'] = 'Back';
|
||||
$lang['en_US']['MultiForm']['NEXT'] = 'Next';
|
||||
$lang['en_US']['MultiForm']['SUBMIT'] = 'Submit';
|
||||
$lang['en_US']['MultiFormSession']['PLURALNAME'] = array(
|
||||
'Multi Form Sessions',
|
||||
50,
|
||||
'Pural name of the object, used in dropdowns and to generally identify a collection of this object in the interface'
|
||||
);
|
||||
$lang['en_US']['MultiFormSession']['SINGULARNAME'] = array(
|
||||
'Multi Form Session',
|
||||
50,
|
||||
'Singular name of the object, used in dropdowns and to generally identify a single object in the interface'
|
||||
);
|
||||
$lang['en_US']['MultiFormStep']['PLURALNAME'] = array(
|
||||
'Multi Form Steps',
|
||||
50,
|
||||
'Pural name of the object, used in dropdowns and to generally identify a collection of this object in the interface'
|
||||
);
|
||||
$lang['en_US']['MultiFormStep']['SINGULARNAME'] = array(
|
||||
'Multi Form Step',
|
||||
50,
|
||||
'Singular name of the object, used in dropdowns and to generally identify a single object in the interface'
|
||||
);
|
||||
|
||||
?>
|
@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Esperanto language pack
|
||||
* @package modules: multiform
|
||||
* @subpackage i18n
|
||||
*/
|
||||
|
||||
i18n::include_locale_file('modules: multiform', 'en_US');
|
||||
|
||||
global $lang;
|
||||
|
||||
if(array_key_exists('eo_XX', $lang) && is_array($lang['eo_XX'])) {
|
||||
$lang['eo_XX'] = array_merge($lang['en_US'], $lang['eo_XX']);
|
||||
} else {
|
||||
$lang['eo_XX'] = $lang['en_US'];
|
||||
}
|
||||
|
||||
$lang['eo_XX']['MultiForm']['BACK'] = 'Retro';
|
||||
$lang['eo_XX']['MultiForm']['NEXT'] = 'Sekva';
|
||||
$lang['eo_XX']['MultiForm']['SUBMIT'] = 'Transsendi';
|
||||
$lang['eo_XX']['MultiFormSession']['db_Hash'] = 'Haketo';
|
||||
$lang['eo_XX']['MultiFormSession']['db_IsComplete'] = 'IsComplete';
|
||||
$lang['eo_XX']['MultiFormSession']['has_many_FormSteps'] = 'FormSteps';
|
||||
$lang['eo_XX']['MultiFormSession']['plural_name'] = '(neniu)';
|
||||
$lang['eo_XX']['MultiFormSession']['singular_name'] = '(neniu)';
|
||||
$lang['eo_XX']['MultiFormStep']['db_Data'] = 'Datumoj';
|
||||
$lang['eo_XX']['MultiFormStep']['plural_name'] = '(neniu)';
|
||||
$lang['eo_XX']['MultiFormStep']['singular_name'] = '(neniu)';
|
||||
|
||||
?>
|
@ -1,20 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* language pack
|
||||
* @package modules: multiform
|
||||
* @subpackage i18n
|
||||
*/
|
||||
|
||||
i18n::include_locale_file('modules: multiform', 'en_US');
|
||||
|
||||
global $lang;
|
||||
|
||||
if(array_key_exists('es_', $lang) && is_array($lang['es_'])) {
|
||||
$lang['es_'] = array_merge($lang['en_US'], $lang['es_']);
|
||||
} else {
|
||||
$lang['es_'] = $lang['en_US'];
|
||||
}
|
||||
|
||||
|
||||
?>
|
@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Spanish (Argentina) language pack
|
||||
* @package modules: multiform
|
||||
* @subpackage i18n
|
||||
*/
|
||||
|
||||
i18n::include_locale_file('modules: multiform', 'en_US');
|
||||
|
||||
global $lang;
|
||||
|
||||
if(array_key_exists('es_AR', $lang) && is_array($lang['es_AR'])) {
|
||||
$lang['es_AR'] = array_merge($lang['en_US'], $lang['es_AR']);
|
||||
} else {
|
||||
$lang['es_AR'] = $lang['en_US'];
|
||||
}
|
||||
|
||||
$lang['es_AR']['MultiForm']['BACK'] = 'Volver';
|
||||
$lang['es_AR']['MultiForm']['NEXT'] = 'Siguiente';
|
||||
$lang['es_AR']['MultiForm']['SUBMIT'] = 'Enviar';
|
||||
$lang['es_AR']['MultiFormSession']['db_Hash'] = 'Hash';
|
||||
$lang['es_AR']['MultiFormSession']['db_IsComplete'] = 'IsComplete';
|
||||
$lang['es_AR']['MultiFormSession']['has_many_FormSteps'] = 'FormSteps';
|
||||
$lang['es_AR']['MultiFormSession']['plural_name'] = '(ninguno)';
|
||||
$lang['es_AR']['MultiFormSession']['singular_name'] = '(ninguno)';
|
||||
$lang['es_AR']['MultiFormStep']['db_Data'] = 'Datos';
|
||||
$lang['es_AR']['MultiFormStep']['plural_name'] = '(ninguno)';
|
||||
$lang['es_AR']['MultiFormStep']['singular_name'] = '(ninguno)';
|
||||
|
||||
?>
|
@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Spanish (Mexico) language pack
|
||||
* @package modules: multiform
|
||||
* @subpackage i18n
|
||||
*/
|
||||
|
||||
i18n::include_locale_file('modules: multiform', 'en_US');
|
||||
|
||||
global $lang;
|
||||
|
||||
if(array_key_exists('es_MX', $lang) && is_array($lang['es_MX'])) {
|
||||
$lang['es_MX'] = array_merge($lang['en_US'], $lang['es_MX']);
|
||||
} else {
|
||||
$lang['es_MX'] = $lang['en_US'];
|
||||
}
|
||||
|
||||
$lang['es_MX']['MultiForm']['BACK'] = 'Atrás';
|
||||
$lang['es_MX']['MultiForm']['NEXT'] = 'Siguiente';
|
||||
$lang['es_MX']['MultiForm']['SUBMIT'] = 'Envíar';
|
||||
$lang['es_MX']['MultiFormSession']['db_Hash'] = 'Desclose';
|
||||
$lang['es_MX']['MultiFormSession']['db_IsComplete'] = 'Concluido';
|
||||
$lang['es_MX']['MultiFormSession']['has_many_FormSteps'] = 'Formularios';
|
||||
$lang['es_MX']['MultiFormSession']['plural_name'] = '(ningunos)';
|
||||
$lang['es_MX']['MultiFormSession']['singular_name'] = '(ningún)';
|
||||
$lang['es_MX']['MultiFormStep']['db_Data'] = 'Datos';
|
||||
$lang['es_MX']['MultiFormStep']['plural_name'] = '(ningunos)';
|
||||
$lang['es_MX']['MultiFormStep']['singular_name'] = '(ningún)';
|
||||
|
||||
?>
|
@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Estonian (Estonia) language pack
|
||||
* @package modules: multiform
|
||||
* @subpackage i18n
|
||||
*/
|
||||
|
||||
i18n::include_locale_file('modules: multiform', 'en_US');
|
||||
|
||||
global $lang;
|
||||
|
||||
if(array_key_exists('et_EE', $lang) && is_array($lang['et_EE'])) {
|
||||
$lang['et_EE'] = array_merge($lang['en_US'], $lang['et_EE']);
|
||||
} else {
|
||||
$lang['et_EE'] = $lang['en_US'];
|
||||
}
|
||||
|
||||
$lang['et_EE']['MultiForm']['BACK'] = 'Tagasi';
|
||||
$lang['et_EE']['MultiForm']['NEXT'] = 'Järgmine';
|
||||
$lang['et_EE']['MultiForm']['SUBMIT'] = 'Saada';
|
||||
$lang['et_EE']['MultiFormSession']['db_Hash'] = 'Hash';
|
||||
$lang['et_EE']['MultiFormSession']['db_IsComplete'] = 'IsComplete';
|
||||
$lang['et_EE']['MultiFormSession']['has_many_FormSteps'] = 'FormSteps';
|
||||
$lang['et_EE']['MultiFormSession']['plural_name'] = '(none)';
|
||||
$lang['et_EE']['MultiFormSession']['singular_name'] = '(none)';
|
||||
$lang['et_EE']['MultiFormStep']['db_Data'] = 'Andmed';
|
||||
$lang['et_EE']['MultiFormStep']['plural_name'] = '(none)';
|
||||
$lang['et_EE']['MultiFormStep']['singular_name'] = '(none)';
|
||||
|
||||
?>
|
@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* French (France) language pack
|
||||
* @package modules: multiform
|
||||
* @subpackage i18n
|
||||
*/
|
||||
|
||||
i18n::include_locale_file('modules: multiform', 'en_US');
|
||||
|
||||
global $lang;
|
||||
|
||||
if(array_key_exists('fr_FR', $lang) && is_array($lang['fr_FR'])) {
|
||||
$lang['fr_FR'] = array_merge($lang['en_US'], $lang['fr_FR']);
|
||||
} else {
|
||||
$lang['fr_FR'] = $lang['en_US'];
|
||||
}
|
||||
|
||||
$lang['fr_FR']['MultiForm']['BACK'] = 'Retour';
|
||||
$lang['fr_FR']['MultiForm']['NEXT'] = 'Suivant';
|
||||
$lang['fr_FR']['MultiForm']['SUBMIT'] = 'Envoyez';
|
||||
$lang['fr_FR']['MultiFormSession']['db_Hash'] = 'Hash';
|
||||
$lang['fr_FR']['MultiFormSession']['db_IsComplete'] = 'IsComplete';
|
||||
$lang['fr_FR']['MultiFormSession']['has_many_FormSteps'] = 'Formulaire multi-étapes';
|
||||
$lang['fr_FR']['MultiFormSession']['plural_name'] = '(aucun)';
|
||||
$lang['fr_FR']['MultiFormSession']['singular_name'] = '(aucun)';
|
||||
$lang['fr_FR']['MultiFormStep']['db_Data'] = 'Data';
|
||||
$lang['fr_FR']['MultiFormStep']['plural_name'] = '(aucun)';
|
||||
$lang['fr_FR']['MultiFormStep']['singular_name'] = '(aucun)';
|
||||
|
||||
?>
|
@ -1,30 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Indonesian (Indonesia) language pack
|
||||
* @package modules: multiform
|
||||
* @subpackage i18n
|
||||
*/
|
||||
|
||||
i18n::include_locale_file('modules: multiform', 'en_US');
|
||||
|
||||
global $lang;
|
||||
|
||||
if(array_key_exists('id_ID', $lang) && is_array($lang['id_ID'])) {
|
||||
$lang['id_ID'] = array_merge($lang['en_US'], $lang['id_ID']);
|
||||
} else {
|
||||
$lang['id_ID'] = $lang['en_US'];
|
||||
}
|
||||
|
||||
$lang['id_ID']['MultiForm']['BACK'] = 'Kembali';
|
||||
$lang['id_ID']['MultiForm']['NEXT'] = 'Berikutnya';
|
||||
$lang['id_ID']['MultiForm']['SUBMIT'] = 'Kirim';
|
||||
$lang['id_ID']['MultiFormSession']['db_Hash'] = 'Tanda Pagar';
|
||||
$lang['id_ID']['MultiFormSession']['db_IsComplete'] = 'TelahSelesai';
|
||||
$lang['id_ID']['MultiFormSession']['plural_name'] = '(tidak ada)';
|
||||
$lang['id_ID']['MultiFormSession']['singular_name'] = '(tidak ada)';
|
||||
$lang['id_ID']['MultiFormStep']['db_Data'] = 'Data';
|
||||
$lang['id_ID']['MultiFormStep']['plural_name'] = '(tidak ada)';
|
||||
$lang['id_ID']['MultiFormStep']['singular_name'] = '(tidak ada)';
|
||||
|
||||
?>
|
@ -1,25 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Icelandic (Iceland) language pack
|
||||
* @package modules: multiform
|
||||
* @subpackage i18n
|
||||
*/
|
||||
|
||||
i18n::include_locale_file('modules: multiform', 'en_US');
|
||||
|
||||
global $lang;
|
||||
|
||||
if(array_key_exists('is_IS', $lang) && is_array($lang['is_IS'])) {
|
||||
$lang['is_IS'] = array_merge($lang['en_US'], $lang['is_IS']);
|
||||
} else {
|
||||
$lang['is_IS'] = $lang['en_US'];
|
||||
}
|
||||
|
||||
$lang['is_IS']['MultiFormSession']['plural_name'] = '(ekkert)';
|
||||
$lang['is_IS']['MultiFormSession']['singular_name'] = '(ekkert)';
|
||||
$lang['is_IS']['MultiFormStep']['db_Data'] = 'Gögn';
|
||||
$lang['is_IS']['MultiFormStep']['plural_name'] = '(ekkert)';
|
||||
$lang['is_IS']['MultiFormStep']['singular_name'] = '(ekkert)';
|
||||
|
||||
?>
|
@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Italian (Italy) language pack
|
||||
* @package modules: multiform
|
||||
* @subpackage i18n
|
||||
*/
|
||||
|
||||
i18n::include_locale_file('modules: multiform', 'en_US');
|
||||
|
||||
global $lang;
|
||||
|
||||
if(array_key_exists('it_IT', $lang) && is_array($lang['it_IT'])) {
|
||||
$lang['it_IT'] = array_merge($lang['en_US'], $lang['it_IT']);
|
||||
} else {
|
||||
$lang['it_IT'] = $lang['en_US'];
|
||||
}
|
||||
|
||||
$lang['it_IT']['MultiForm']['BACK'] = 'Indietro';
|
||||
$lang['it_IT']['MultiForm']['NEXT'] = 'Successivo';
|
||||
$lang['it_IT']['MultiForm']['SUBMIT'] = 'Invia';
|
||||
$lang['it_IT']['MultiFormSession']['db_Hash'] = 'Hash';
|
||||
$lang['it_IT']['MultiFormSession']['db_IsComplete'] = 'IsComplete';
|
||||
$lang['it_IT']['MultiFormSession']['has_many_FormSteps'] = 'FormSteps';
|
||||
$lang['it_IT']['MultiFormSession']['plural_name'] = '(nessuno)';
|
||||
$lang['it_IT']['MultiFormSession']['singular_name'] = '(nessuno)';
|
||||
$lang['it_IT']['MultiFormStep']['db_Data'] = 'Dati';
|
||||
$lang['it_IT']['MultiFormStep']['plural_name'] = '(nessuno)';
|
||||
$lang['it_IT']['MultiFormStep']['singular_name'] = '(nessuno)';
|
||||
|
||||
?>
|
@ -1,25 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Japanese (Japan) language pack
|
||||
* @package modules: multiform
|
||||
* @subpackage i18n
|
||||
*/
|
||||
|
||||
i18n::include_locale_file('modules: multiform', 'en_US');
|
||||
|
||||
global $lang;
|
||||
|
||||
if(array_key_exists('ja_JP', $lang) && is_array($lang['ja_JP'])) {
|
||||
$lang['ja_JP'] = array_merge($lang['en_US'], $lang['ja_JP']);
|
||||
} else {
|
||||
$lang['ja_JP'] = $lang['en_US'];
|
||||
}
|
||||
|
||||
$lang['ja_JP']['MultiForm']['BACK'] = '戻る';
|
||||
$lang['ja_JP']['MultiForm']['NEXT'] = '次へ';
|
||||
$lang['ja_JP']['MultiForm']['SUBMIT'] = '送信';
|
||||
$lang['ja_JP']['MultiFormSession']['db_Hash'] = 'ハッシュ';
|
||||
$lang['ja_JP']['MultiFormStep']['db_Data'] = 'データ';
|
||||
|
||||
?>
|
@ -1,28 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Malay (Malaysia) language pack
|
||||
* @package modules: multiform
|
||||
* @subpackage i18n
|
||||
*/
|
||||
|
||||
i18n::include_locale_file('modules: multiform', 'en_US');
|
||||
|
||||
global $lang;
|
||||
|
||||
if(array_key_exists('ms_MY', $lang) && is_array($lang['ms_MY'])) {
|
||||
$lang['ms_MY'] = array_merge($lang['en_US'], $lang['ms_MY']);
|
||||
} else {
|
||||
$lang['ms_MY'] = $lang['en_US'];
|
||||
}
|
||||
|
||||
$lang['ms_MY']['MultiFormSession']['db_Hash'] = 'Cincangan';
|
||||
$lang['ms_MY']['MultiFormSession']['db_IsComplete'] = 'TelahLengkap';
|
||||
$lang['ms_MY']['MultiFormSession']['has_many_FormSteps'] = 'JejakLangkahBorang';
|
||||
$lang['ms_MY']['MultiFormSession']['plural_name'] = '(tiada)';
|
||||
$lang['ms_MY']['MultiFormSession']['singular_name'] = '(tiada)';
|
||||
$lang['ms_MY']['MultiFormStep']['db_Data'] = 'Data';
|
||||
$lang['ms_MY']['MultiFormStep']['plural_name'] = '(tiada)';
|
||||
$lang['ms_MY']['MultiFormStep']['singular_name'] = '(tiada)';
|
||||
|
||||
?>
|
@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Norwegian Bokmal (Norway) language pack
|
||||
* @package modules: multiform
|
||||
* @subpackage i18n
|
||||
*/
|
||||
|
||||
i18n::include_locale_file('modules: multiform', 'en_US');
|
||||
|
||||
global $lang;
|
||||
|
||||
if(array_key_exists('nb_NO', $lang) && is_array($lang['nb_NO'])) {
|
||||
$lang['nb_NO'] = array_merge($lang['en_US'], $lang['nb_NO']);
|
||||
} else {
|
||||
$lang['nb_NO'] = $lang['en_US'];
|
||||
}
|
||||
|
||||
$lang['nb_NO']['MultiForm']['BACK'] = 'Forrige';
|
||||
$lang['nb_NO']['MultiForm']['NEXT'] = 'Neste';
|
||||
$lang['nb_NO']['MultiForm']['SUBMIT'] = 'Send';
|
||||
$lang['nb_NO']['MultiFormSession']['db_Hash'] = 'Hash';
|
||||
$lang['nb_NO']['MultiFormSession']['db_IsComplete'] = 'ErFullflørt';
|
||||
$lang['nb_NO']['MultiFormSession']['has_many_FormSteps'] = 'SkjemaSteg';
|
||||
$lang['nb_NO']['MultiFormSession']['plural_name'] = '(ingen)';
|
||||
$lang['nb_NO']['MultiFormSession']['singular_name'] = '(ingen)';
|
||||
$lang['nb_NO']['MultiFormStep']['db_Data'] = 'Data';
|
||||
$lang['nb_NO']['MultiFormStep']['plural_name'] = '(ingen)';
|
||||
$lang['nb_NO']['MultiFormStep']['singular_name'] = '(ingen)';
|
||||
|
||||
?>
|
@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Dutch (Netherlands) language pack
|
||||
* @package modules: multiform
|
||||
* @subpackage i18n
|
||||
*/
|
||||
|
||||
i18n::include_locale_file('modules: multiform', 'en_US');
|
||||
|
||||
global $lang;
|
||||
|
||||
if(array_key_exists('nl_NL', $lang) && is_array($lang['nl_NL'])) {
|
||||
$lang['nl_NL'] = array_merge($lang['en_US'], $lang['nl_NL']);
|
||||
} else {
|
||||
$lang['nl_NL'] = $lang['en_US'];
|
||||
}
|
||||
|
||||
$lang['nl_NL']['MultiForm']['BACK'] = 'Terug';
|
||||
$lang['nl_NL']['MultiForm']['NEXT'] = 'Volgende';
|
||||
$lang['nl_NL']['MultiForm']['SUBMIT'] = 'Versturen';
|
||||
$lang['nl_NL']['MultiFormSession']['db_Hash'] = 'Hash';
|
||||
$lang['nl_NL']['MultiFormSession']['db_IsComplete'] = 'IsCompleet';
|
||||
$lang['nl_NL']['MultiFormSession']['has_many_FormSteps'] = 'FormulierStappen';
|
||||
$lang['nl_NL']['MultiFormSession']['plural_name'] = '(geen)';
|
||||
$lang['nl_NL']['MultiFormSession']['singular_name'] = '(geen)';
|
||||
$lang['nl_NL']['MultiFormStep']['db_Data'] = 'Data';
|
||||
$lang['nl_NL']['MultiFormStep']['plural_name'] = '(geen)';
|
||||
$lang['nl_NL']['MultiFormStep']['singular_name'] = '(geen)';
|
||||
|
||||
?>
|
@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Polish (Poland) language pack
|
||||
* @package modules: multiform
|
||||
* @subpackage i18n
|
||||
*/
|
||||
|
||||
i18n::include_locale_file('modules: multiform', 'en_US');
|
||||
|
||||
global $lang;
|
||||
|
||||
if(array_key_exists('pl_PL', $lang) && is_array($lang['pl_PL'])) {
|
||||
$lang['pl_PL'] = array_merge($lang['en_US'], $lang['pl_PL']);
|
||||
} else {
|
||||
$lang['pl_PL'] = $lang['en_US'];
|
||||
}
|
||||
|
||||
$lang['pl_PL']['MultiForm']['BACK'] = 'Wstecz';
|
||||
$lang['pl_PL']['MultiForm']['NEXT'] = 'Dalej';
|
||||
$lang['pl_PL']['MultiForm']['SUBMIT'] = 'Wyślij';
|
||||
$lang['pl_PL']['MultiFormSession']['db_Hash'] = 'Hash';
|
||||
$lang['pl_PL']['MultiFormSession']['db_IsComplete'] = 'CzyGotowe';
|
||||
$lang['pl_PL']['MultiFormSession']['has_many_FormSteps'] = 'KrokiFormularza';
|
||||
$lang['pl_PL']['MultiFormSession']['plural_name'] = '(brak)';
|
||||
$lang['pl_PL']['MultiFormSession']['singular_name'] = '(brak)';
|
||||
$lang['pl_PL']['MultiFormStep']['db_Data'] = 'Dane';
|
||||
$lang['pl_PL']['MultiFormStep']['plural_name'] = '(brak)';
|
||||
$lang['pl_PL']['MultiFormStep']['singular_name'] = '(brak)';
|
||||
|
||||
?>
|
@ -1,27 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Portuguese (Portugal) language pack
|
||||
* @package modules: multiform
|
||||
* @subpackage i18n
|
||||
*/
|
||||
|
||||
i18n::include_locale_file('modules: multiform', 'en_US');
|
||||
|
||||
global $lang;
|
||||
|
||||
if(array_key_exists('pt_PT', $lang) && is_array($lang['pt_PT'])) {
|
||||
$lang['pt_PT'] = array_merge($lang['en_US'], $lang['pt_PT']);
|
||||
} else {
|
||||
$lang['pt_PT'] = $lang['en_US'];
|
||||
}
|
||||
|
||||
$lang['pt_PT']['MultiFormSession']['db_IsComplete'] = 'Está completa';
|
||||
$lang['pt_PT']['MultiFormSession']['has_many_FormSteps'] = 'Passos do formulário';
|
||||
$lang['pt_PT']['MultiFormSession']['plural_name'] = '(nenhum)';
|
||||
$lang['pt_PT']['MultiFormSession']['singular_name'] = '(nenhum)';
|
||||
$lang['pt_PT']['MultiFormStep']['db_Data'] = 'Dados';
|
||||
$lang['pt_PT']['MultiFormStep']['plural_name'] = '(nenhum)';
|
||||
$lang['pt_PT']['MultiFormStep']['singular_name'] = '(nenhum)';
|
||||
|
||||
?>
|
@ -1,26 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Serbian (Serbia) language pack
|
||||
* @package modules: multiform
|
||||
* @subpackage i18n
|
||||
*/
|
||||
|
||||
i18n::include_locale_file('modules: multiform', 'en_US');
|
||||
|
||||
global $lang;
|
||||
|
||||
if(array_key_exists('sr_RS', $lang) && is_array($lang['sr_RS'])) {
|
||||
$lang['sr_RS'] = array_merge($lang['en_US'], $lang['sr_RS']);
|
||||
} else {
|
||||
$lang['sr_RS'] = $lang['en_US'];
|
||||
}
|
||||
|
||||
$lang['sr_RS']['MultiFormSession']['db_IsComplete'] = 'ЈеЗавршен';
|
||||
$lang['sr_RS']['MultiFormSession']['plural_name'] = '(без)';
|
||||
$lang['sr_RS']['MultiFormSession']['singular_name'] = '(без)';
|
||||
$lang['sr_RS']['MultiFormStep']['db_Data'] = 'Подаци';
|
||||
$lang['sr_RS']['MultiFormStep']['plural_name'] = '(без)';
|
||||
$lang['sr_RS']['MultiFormStep']['singular_name'] = '(без)';
|
||||
|
||||
?>
|
@ -1,28 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Swedish (Sweden) language pack
|
||||
* @package modules: multiform
|
||||
* @subpackage i18n
|
||||
*/
|
||||
|
||||
i18n::include_locale_file('modules: multiform', 'en_US');
|
||||
|
||||
global $lang;
|
||||
|
||||
if(array_key_exists('sv_SE', $lang) && is_array($lang['sv_SE'])) {
|
||||
$lang['sv_SE'] = array_merge($lang['en_US'], $lang['sv_SE']);
|
||||
} else {
|
||||
$lang['sv_SE'] = $lang['en_US'];
|
||||
}
|
||||
|
||||
$lang['sv_SE']['MultiForm']['BACK'] = 'Tillbaka';
|
||||
$lang['sv_SE']['MultiForm']['NEXT'] = 'Nästa';
|
||||
$lang['sv_SE']['MultiForm']['SUBMIT'] = 'Skicka';
|
||||
$lang['sv_SE']['MultiFormSession']['db_Hash'] = 'Hash';
|
||||
$lang['sv_SE']['MultiFormSession']['plural_name'] = '(ingen)';
|
||||
$lang['sv_SE']['MultiFormStep']['db_Data'] = 'Data';
|
||||
$lang['sv_SE']['MultiFormStep']['plural_name'] = '(inga)';
|
||||
$lang['sv_SE']['MultiFormStep']['singular_name'] = '(ingen)';
|
||||
|
||||
?>
|
@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Turkish (Turkey) language pack
|
||||
* @package modules: multiform
|
||||
* @subpackage i18n
|
||||
*/
|
||||
|
||||
i18n::include_locale_file('modules: multiform', 'en_US');
|
||||
|
||||
global $lang;
|
||||
|
||||
if(array_key_exists('tr_TR', $lang) && is_array($lang['tr_TR'])) {
|
||||
$lang['tr_TR'] = array_merge($lang['en_US'], $lang['tr_TR']);
|
||||
} else {
|
||||
$lang['tr_TR'] = $lang['en_US'];
|
||||
}
|
||||
|
||||
$lang['tr_TR']['MultiForm']['BACK'] = 'Geri';
|
||||
$lang['tr_TR']['MultiForm']['NEXT'] = 'İleri';
|
||||
$lang['tr_TR']['MultiForm']['SUBMIT'] = 'Gönder';
|
||||
$lang['tr_TR']['MultiFormSession']['db_Hash'] = 'Hash';
|
||||
$lang['tr_TR']['MultiFormSession']['db_IsComplete'] = 'IsComplete';
|
||||
$lang['tr_TR']['MultiFormSession']['has_many_FormSteps'] = 'FormSteps';
|
||||
$lang['tr_TR']['MultiFormSession']['plural_name'] = '(hiçbiri)';
|
||||
$lang['tr_TR']['MultiFormSession']['singular_name'] = '(hiçbiri)';
|
||||
$lang['tr_TR']['MultiFormStep']['db_Data'] = 'Veri';
|
||||
$lang['tr_TR']['MultiFormStep']['plural_name'] = '(hiçbiri)';
|
||||
$lang['tr_TR']['MultiFormStep']['singular_name'] = '(hiçbiri)';
|
||||
|
||||
?>
|
@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chinese (China) language pack
|
||||
* @package modules: multiform
|
||||
* @subpackage i18n
|
||||
*/
|
||||
|
||||
i18n::include_locale_file('modules: multiform', 'en_US');
|
||||
|
||||
global $lang;
|
||||
|
||||
if(array_key_exists('zh_CN', $lang) && is_array($lang['zh_CN'])) {
|
||||
$lang['zh_CN'] = array_merge($lang['en_US'], $lang['zh_CN']);
|
||||
} else {
|
||||
$lang['zh_CN'] = $lang['en_US'];
|
||||
}
|
||||
|
||||
$lang['zh_CN']['MultiForm']['BACK'] = '区域';
|
||||
$lang['zh_CN']['MultiForm']['NEXT'] = '下一个';
|
||||
$lang['zh_CN']['MultiForm']['SUBMIT'] = '提交';
|
||||
$lang['zh_CN']['MultiFormSession']['db_Hash'] = 'Hash 字符串';
|
||||
$lang['zh_CN']['MultiFormSession']['db_IsComplete'] = '是否已完成';
|
||||
$lang['zh_CN']['MultiFormSession']['has_many_FormSteps'] = '表单步骤';
|
||||
$lang['zh_CN']['MultiFormSession']['plural_name'] = '多名称';
|
||||
$lang['zh_CN']['MultiFormSession']['singular_name'] = '单名称';
|
||||
$lang['zh_CN']['MultiFormStep']['db_Data'] = '数据';
|
||||
$lang['zh_CN']['MultiFormStep']['plural_name'] = '多名称';
|
||||
$lang['zh_CN']['MultiFormStep']['singular_name'] = '单名称';
|
||||
|
||||
?>
|
@ -1,4 +1,4 @@
|
||||
Copyright (c) 2016, SilverStripe Limited
|
||||
Copyright (c) 2017, SilverStripe Limited
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
13
phpunit.xml.dist
Normal file
13
phpunit.xml.dist
Normal file
@ -0,0 +1,13 @@
|
||||
<phpunit bootstrap="framework/tests/bootstrap.php" colors="true">
|
||||
<testsuite name="Default">
|
||||
<directory>tests/</directory>
|
||||
</testsuite>
|
||||
<filter>
|
||||
<whitelist addUncoveredFilesFromWhitelist="true">
|
||||
<directory suffix=".php">src/</directory>
|
||||
<exclude>
|
||||
<directory suffix=".php">tests/</directory>
|
||||
</exclude>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
754
src/Models/MultiForm.php
Normal file
754
src/Models/MultiForm.php
Normal file
@ -0,0 +1,754 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\MultiForm\Models;
|
||||
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Control\HTTPResponse;
|
||||
use SilverStripe\Control\Session;
|
||||
use SilverStripe\Core\Convert;
|
||||
use SilverStripe\Core\Injector\Injector;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\Form;
|
||||
use SilverStripe\Forms\FormAction;
|
||||
use SilverStripe\Forms\HiddenField;
|
||||
use SilverStripe\ORM\ArrayList;
|
||||
use SilverStripe\ORM\DataList;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\View\SSViewer;
|
||||
|
||||
/**
|
||||
* MultiForm manages the loading of single form steps, and acts as a state
|
||||
* machine that connects to a {@link MultiFormSession} object as a persistence
|
||||
* layer.
|
||||
*
|
||||
* CAUTION: If you're using controller permission control,
|
||||
* you have to allow the following methods:
|
||||
*
|
||||
* <code>
|
||||
* private static $allowed_actions = array('next','prev');
|
||||
* </code>
|
||||
*
|
||||
*/
|
||||
abstract class MultiForm extends Form
|
||||
{
|
||||
/**
|
||||
* A session object stored in the database, to identify and store
|
||||
* data for this MultiForm instance.
|
||||
*
|
||||
* @var MultiFormSession
|
||||
*/
|
||||
protected $session;
|
||||
|
||||
/**
|
||||
* The current encrypted MultiFormSession identification.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $currentSessionHash;
|
||||
|
||||
/**
|
||||
* Defines which subclass of {@link MultiFormStep} should be the first
|
||||
* step in the multi-step process.
|
||||
*
|
||||
* @var string Classname of a {@link MultiFormStep} subclass
|
||||
*/
|
||||
private static $start_step;
|
||||
|
||||
/**
|
||||
* Set the casting for these fields.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $casting = [
|
||||
'CompletedStepCount' => 'Int',
|
||||
'TotalStepCount' => 'Int',
|
||||
'CompletedPercent' => 'Float'
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private static $get_var = 'MultiFormSessionID';
|
||||
|
||||
/**
|
||||
* These fields are ignored when saving the raw form data into session.
|
||||
* This ensures only field data is saved, and nothing else that's useless
|
||||
* or potentially dangerous.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $ignored_fields = [
|
||||
'url',
|
||||
'executeForm',
|
||||
'SecurityID'
|
||||
];
|
||||
|
||||
/**
|
||||
* Any of the actions defined in this variable are exempt from
|
||||
* being validated.
|
||||
*
|
||||
* This is most useful for the "Back" (action_prev) action, as
|
||||
* you typically don't validate the form when the user is going
|
||||
* back a step.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $actions_exempt_from_validation = [
|
||||
'action_prev'
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $displayLink;
|
||||
|
||||
/**
|
||||
* Flag which is being used in getAllStepsRecursive() to allow adding the completed flag on the steps
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $currentStepHasBeenFound = false;
|
||||
|
||||
/**
|
||||
* Start the MultiForm instance.
|
||||
*
|
||||
* @param Controller $controller Controller instance this form is created on
|
||||
* @param string $name The form name, typically the same as the method name
|
||||
*/
|
||||
public function __construct($controller, $name)
|
||||
{
|
||||
// First set the controller and name manually so they are available for
|
||||
// field construction.
|
||||
$this->controller = $controller;
|
||||
$this->name = $name;
|
||||
|
||||
// Set up the session for this MultiForm instance
|
||||
$this->setSession();
|
||||
|
||||
// Get the current step available (Note: either returns an existing
|
||||
// step or creates a new one if none available)
|
||||
$currentStep = $this->getCurrentStep();
|
||||
|
||||
// Set the step returned above as the current step
|
||||
$this->setCurrentStep($currentStep);
|
||||
|
||||
// Set the form of the step to this form instance
|
||||
$currentStep->setForm($this);
|
||||
|
||||
// Set up the fields for the current step
|
||||
$fields = $currentStep->getFields();
|
||||
|
||||
// Set up the actions for the current step
|
||||
$actions = $this->actionsFor($currentStep);
|
||||
|
||||
// Set up validation (if necessary)
|
||||
$validator = null;
|
||||
$applyValidation = true;
|
||||
|
||||
$actionNames = $this->config()->get('actions_exempt_from_validation');
|
||||
|
||||
if ($actionNames) {
|
||||
foreach ($actionNames as $exemptAction) {
|
||||
if (!empty($this->getRequest()->requestVar($exemptAction))) {
|
||||
$applyValidation = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply validation if the current step requires validation (is not exempt)
|
||||
if ($applyValidation) {
|
||||
if ($currentStep->getValidator()) {
|
||||
$this->setValidator($currentStep->getValidator());
|
||||
}
|
||||
}
|
||||
|
||||
// Give the fields, actions, and validation for the current step back to the parent Form class
|
||||
parent::__construct($controller, $name, $fields, $actions);
|
||||
|
||||
$getVar = $this->getGetVar();
|
||||
|
||||
// Set a hidden field in our form with an encrypted hash to identify this session.
|
||||
$this->fields->push(HiddenField::create($getVar, false, $this->session->Hash));
|
||||
|
||||
// If there is saved data for the current step, we load it into the form it here
|
||||
//(CAUTION: loadData() MUST unserialize first!)
|
||||
if ($data = $currentStep->loadData()) {
|
||||
$this->loadDataFrom($data);
|
||||
}
|
||||
|
||||
// Disable security token - we tie a form to a session ID instead
|
||||
$this->disableSecurityToken();
|
||||
|
||||
$this->config()->merge('ignored_fields', $getVar);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor method to $this->controller.
|
||||
*
|
||||
* @return Controller this MultiForm was instanciated on.
|
||||
*/
|
||||
public function getController()
|
||||
{
|
||||
return $this->controller;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the get_var to the template engine
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getGetVar()
|
||||
{
|
||||
return $this->config()->get('get_var');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current step.
|
||||
*
|
||||
* If StepID has been set in the URL, we attempt to get that record
|
||||
* by the ID. Otherwise, we check if there's a current step ID in
|
||||
* our session record. Failing those cases, we assume that the form has
|
||||
* just been started, and so we create the first step and return it.
|
||||
*
|
||||
* @return MultiFormStep subclass
|
||||
*/
|
||||
public function getCurrentStep()
|
||||
{
|
||||
$startStepClass = $this->config()->get('start_step');
|
||||
|
||||
// Check if there was a start step defined on the subclass of MultiForm
|
||||
if (!isset($startStepClass)) {
|
||||
user_error(
|
||||
'MultiForm::init(): Please define a $start_step on ' . $this->class,
|
||||
E_USER_ERROR
|
||||
);
|
||||
}
|
||||
|
||||
// Determine whether we use the current step, or create one if it doesn't exist
|
||||
$currentStep = null;
|
||||
$StepID = $this->controller->getRequest()->getVar('StepID');
|
||||
if (isset($StepID)) {
|
||||
$currentStep = DataObject::get_one(
|
||||
MultiFormStep::class,
|
||||
[
|
||||
'SessionID' => $this->session->ID,
|
||||
'ID' => $StepID
|
||||
]
|
||||
);
|
||||
} elseif ($this->session->CurrentStepID) {
|
||||
$currentStep = $this->session->CurrentStep();
|
||||
}
|
||||
|
||||
// Always fall back to creating a new step (in case the session or request data is invalid)
|
||||
if (!$currentStep || !$currentStep->ID) {
|
||||
$currentStep = Injector::inst()->create($startStepClass);
|
||||
$currentStep->SessionID = $this->session->ID;
|
||||
$currentStep->write();
|
||||
$this->session->CurrentStepID = $currentStep->ID;
|
||||
$this->session->write();
|
||||
$this->session->flushCache();
|
||||
}
|
||||
|
||||
if ($currentStep) {
|
||||
$currentStep->setForm($this);
|
||||
}
|
||||
|
||||
return $currentStep;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the step passed in as the current step.
|
||||
*
|
||||
* @param MultiFormStep $step A subclass of MultiFormStep
|
||||
* @return boolean The return value of write()
|
||||
*/
|
||||
protected function setCurrentStep($step)
|
||||
{
|
||||
$this->session->CurrentStepID = $step->ID;
|
||||
$step->setForm($this);
|
||||
|
||||
return $this->session->write();
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor method to $this->session.
|
||||
*
|
||||
* @return MultiFormSession
|
||||
*/
|
||||
public function getMultiFormSession()
|
||||
{
|
||||
if (!$this->session) {
|
||||
$this->setSession();
|
||||
}
|
||||
return $this->session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the session.
|
||||
*
|
||||
* If MultiFormSessionID isn't set, we assume that this is a new
|
||||
* multiform that requires a new session record to be created.
|
||||
*
|
||||
* @TODO Fix the fact you can continually refresh and create new records
|
||||
* if MultiFormSessionID isn't set.
|
||||
*
|
||||
* @TODO Not sure if we should bake the session stuff directly into MultiForm.
|
||||
* Perhaps it would be best dealt with on a separate class?
|
||||
*/
|
||||
protected function setSession()
|
||||
{
|
||||
$this->session = $this->getCurrentSession();
|
||||
|
||||
// If there was no session found, create a new one instead
|
||||
if (!$this->session) {
|
||||
$this->session = MultiFormSession::create();
|
||||
}
|
||||
|
||||
// Create encrypted identification to the session instance if it doesn't exist
|
||||
if (!$this->session->Hash) {
|
||||
$this->session->Hash = sha1($this->session->ID . '-' . microtime());
|
||||
$this->session->write();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the currently used encrypted hash to identify
|
||||
* the MultiFormSession.
|
||||
*
|
||||
* @param string $hash Encrypted identification to session
|
||||
*/
|
||||
public function setCurrentSessionHash($hash)
|
||||
{
|
||||
$this->currentSessionHash = $hash;
|
||||
$this->setSession();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the currently used {@link MultiFormSession}
|
||||
* @return MultiFormSession|boolean FALSE
|
||||
*/
|
||||
public function getCurrentSession()
|
||||
{
|
||||
if (!$this->currentSessionHash) {
|
||||
$this->currentSessionHash = $this->controller->getRequest()->getVar($this->getGetVar());
|
||||
|
||||
if (!$this->currentSessionHash) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$this->session = MultiFormSession::get()->filter([
|
||||
"Hash" => $this->currentSessionHash,
|
||||
"IsComplete" => 0
|
||||
])->first();
|
||||
|
||||
return $this->session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all steps saved in the database for the currently active session,
|
||||
* in the order they were saved, oldest to newest (automatically ordered by ID).
|
||||
* If you want a full chain of steps regardless if they've already been saved
|
||||
* to the database, use {@link getAllStepsLinear()}.
|
||||
*
|
||||
* @param string $filter SQL WHERE statement
|
||||
* @return DataList|boolean A set of MultiFormStep subclasses
|
||||
*/
|
||||
public function getSavedSteps($filter = null)
|
||||
{
|
||||
$filter .= ($filter) ? ' AND ' : '';
|
||||
$filter .= sprintf("\"SessionID\" = '%s'", $this->session->ID);
|
||||
return DataObject::get(MultiFormStep::class, $filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a step which was previously saved to the database in the current session.
|
||||
* Caution: This might cause unexpected behaviour if you have multiple steps
|
||||
* in your chain with the same classname.
|
||||
*
|
||||
* @param string $className Classname of a {@link MultiFormStep} subclass
|
||||
* @return DataObject
|
||||
*/
|
||||
public function getSavedStepByClass($className)
|
||||
{
|
||||
return DataObject::get_one(
|
||||
MultiFormStep::class,
|
||||
sprintf(
|
||||
"\"SessionID\" = '%s' AND \"ClassName\" = '%s'",
|
||||
$this->session->ID,
|
||||
Convert::raw2sql($className)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a FieldList of the FormAction fields for the given step.
|
||||
*
|
||||
* If the current step is the final step, we push in a submit button, which
|
||||
* calls the action {@link finish()} to finalise the submission. Otherwise,
|
||||
* we push in a next button which calls the action {@link next()} to determine
|
||||
* where to go next in our step process, and save any form data collected.
|
||||
*
|
||||
* If there's a previous step (a step that has the current step as it's next
|
||||
* step class), then we allow a previous button, which calls the previous action
|
||||
* to determine which step to go back to.
|
||||
*
|
||||
* If there are any extra actions defined in MultiFormStep->getExtraActions()
|
||||
* then that set of actions is appended to the end of the actions FieldSet we
|
||||
* have created in this method.
|
||||
*
|
||||
* @param MultiFormStep $step Subclass of MultiFormStep
|
||||
* @return FieldList of FormAction objects
|
||||
*/
|
||||
public function actionsFor($step)
|
||||
{
|
||||
// Create default multi step actions (next, prev), and merge with extra actions, if any
|
||||
$actions = FieldList::create();
|
||||
|
||||
// If the form is at final step, create a submit button to perform final actions
|
||||
// The last step doesn't have a next button, so add that action to any step that isn't the final one
|
||||
if ($step->isFinalStep()) {
|
||||
$actions->push(FormAction::create('finish', $step->getSubmitText()));
|
||||
} else {
|
||||
$actions->push(FormAction::create('next', $step->getNextText()));
|
||||
}
|
||||
|
||||
// If there is a previous step defined, add the back button
|
||||
if ($step->getPreviousStep() && $step->canGoBack()) {
|
||||
// If there is a next step, insert the action before the next action
|
||||
if ($step->getNextStep()) {
|
||||
$actions->insertBefore($prev = FormAction::create('prev', $step->getPrevText()), 'action_next');
|
||||
// Assume that this is the last step, insert the action before the finish action
|
||||
} else {
|
||||
$actions->insertBefore($prev = FormAction::create('prev', $step->getPrevText()), 'action_finish');
|
||||
}
|
||||
//remove browser validation from prev action
|
||||
$prev->setAttribute("formnovalidate", "formnovalidate");
|
||||
}
|
||||
|
||||
// Merge any extra action fields defined on the step
|
||||
$actions->merge($step->getExtraActions());
|
||||
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a rendered version of this form, with a specific template.
|
||||
* Looks through the step ancestory templates (MultiFormStep, current step
|
||||
* subclass template) to see if one is available to render the form with. If
|
||||
* any of those don't exist, look for a default Form template to render
|
||||
* with instead.
|
||||
*
|
||||
* @return SSViewer object to render the template with
|
||||
*/
|
||||
public function forTemplate()
|
||||
{
|
||||
$return = $this->renderWith([
|
||||
$this->getCurrentStep()->class,
|
||||
'MultiFormStep',
|
||||
$this->class,
|
||||
'MultiForm',
|
||||
'Form'
|
||||
]);
|
||||
|
||||
$this->clearMessage();
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method saves the data on the final step, after submitting.
|
||||
* It should always be overloaded with parent::finish($data, $form)
|
||||
* so you can create your own functionality which handles saving
|
||||
* of all the data collected through each step of the form.
|
||||
*
|
||||
* @param array $data The request data returned from the form
|
||||
* @param Form $form The form that the action was called on
|
||||
* @return bool
|
||||
*/
|
||||
public function finish($data, $form)
|
||||
{
|
||||
// Save the form data for the current step
|
||||
$this->save($data);
|
||||
|
||||
if (!$this->getCurrentStep()->isFinalStep()) {
|
||||
$this->controller->redirectBack();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$this->getCurrentStep()->validateStep($data, $form)) {
|
||||
$this->getRequest()->getSession()->set("FormInfo.{$form->FormName()}.data", $form->getData());
|
||||
$this->controller->redirectBack();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine what to do when the next action is called.
|
||||
*
|
||||
* Saves the current step session data to the database, creates the
|
||||
* new step based on getNextStep() of the current step (or fetches
|
||||
* an existing one), resets the current step to the next step,
|
||||
* then redirects to the newly set step.
|
||||
*
|
||||
* @param array $data The request data returned from the form
|
||||
* @param Form $form The form that the action was called on
|
||||
* @return bool
|
||||
*/
|
||||
public function next($data, $form)
|
||||
{
|
||||
// Save the form data for the current step
|
||||
$this->save($form->getData());
|
||||
|
||||
// Get the next step class
|
||||
$nextStepClass = $this->getCurrentStep()->getNextStep();
|
||||
|
||||
if (!$nextStepClass) {
|
||||
$this->controller->redirectBack();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Perform custom step validation (use MultiFormStep->getValidator() for
|
||||
// built-in functionality). The data needs to be manually saved on error
|
||||
// so the form is re-populated.
|
||||
if (!$this->getCurrentStep()->validateStep($data, $form)) {
|
||||
$this->getRequest()->getSession()->set("FormInfo.{$form->FormName()}.data", $form->getData());
|
||||
$this->controller->redirectBack();
|
||||
return false;
|
||||
}
|
||||
|
||||
// validation succeeded so we reset it to remove errors and messages
|
||||
$this->clearFormState();
|
||||
|
||||
// Determine whether we can use a step already in the DB, or have to create a new one
|
||||
if (!$nextStep = DataObject::get_one($nextStepClass, "\"SessionID\" = {$this->session->ID}")) {
|
||||
$nextStep = Injector::inst()->create($nextStepClass);
|
||||
$nextStep->SessionID = $this->session->ID;
|
||||
$nextStep->write();
|
||||
}
|
||||
|
||||
// Set the next step found as the current step
|
||||
$this->setCurrentStep($nextStep);
|
||||
|
||||
// Redirect to the next step
|
||||
$this->controller->redirect($nextStep->Link());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine what to do when the previous action is called.
|
||||
*
|
||||
* Retrieves the previous step class, finds the record for that
|
||||
* class in the DB, and sets the current step to that step found.
|
||||
* Finally, it redirects to that step.
|
||||
*
|
||||
* @param array $data The request data returned from the form
|
||||
* @param Form $form The form that the action was called on
|
||||
* @return bool|HTTPResponse
|
||||
*/
|
||||
public function prev($data, $form)
|
||||
{
|
||||
// Save the form data for the current step
|
||||
$this->save($form->getData());
|
||||
|
||||
// Get the previous step class
|
||||
$prevStepClass = $this->getCurrentStep()->getPreviousStep();
|
||||
|
||||
if (!$prevStepClass && !$this->getCurrentStep()->canGoBack()) {
|
||||
$this->controller->redirectBack();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the previous step of the class instance returned from $currentStep->getPreviousStep()
|
||||
$prevStep = DataObject::get_one($prevStepClass, "\"SessionID\" = {$this->session->ID}");
|
||||
|
||||
// Set the current step as the previous step
|
||||
$this->setCurrentStep($prevStep);
|
||||
|
||||
// Redirect to the previous step
|
||||
return $this->controller->redirect($prevStep->Link());
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the raw data given back from the form into session.
|
||||
*
|
||||
* Take the submitted form data for the current step, removing
|
||||
* any key => value pairs that shouldn't be saved, then saves
|
||||
* the data into the session.
|
||||
*
|
||||
* @param array $data An array of data to save
|
||||
*/
|
||||
protected function save($data)
|
||||
{
|
||||
$currentStep = $this->getCurrentStep();
|
||||
if (is_array($data)) {
|
||||
foreach ($data as $field => $value) {
|
||||
if (in_array($field, $this->config()->get('ignored_fields'))) {
|
||||
unset($data[$field]);
|
||||
}
|
||||
}
|
||||
$currentStep->saveData($data);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// ############ Misc ############
|
||||
|
||||
/**
|
||||
* Add the MultiFormSessionID variable to the URL on form submission.
|
||||
* This is a means to persist the session, by adding it's identification
|
||||
* to the URL, which ties it back to this MultiForm instance.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function FormAction()
|
||||
{
|
||||
$action = parent::FormAction();
|
||||
$action .= (strpos($action, '?')) ? '&' : '?';
|
||||
$action .= "{$this->getGetVar()}={$this->session->Hash}";
|
||||
|
||||
return $action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the link to the page where the form is displayed. The user is
|
||||
* redirected to this link with a session param after each step is
|
||||
* submitted.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDisplayLink()
|
||||
{
|
||||
return $this->displayLink ? $this->displayLink : Controller::curr()->Link();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the link to the page on which the form is displayed.
|
||||
*
|
||||
* The link defaults to the controllers current link. However if the form
|
||||
* is displayed inside an action the display link must be explicitly set.
|
||||
*
|
||||
* @param string $link
|
||||
*/
|
||||
public function setDisplayLink($link)
|
||||
{
|
||||
$this->displayLink = $link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the steps to show in a linear fashion, starting from the
|
||||
* first step. We run {@link getAllStepsRecursive} passing the steps found
|
||||
* by reference to get a listing of the steps.
|
||||
*
|
||||
* @return ArrayList of MultiFormStep instances
|
||||
*/
|
||||
public function getAllStepsLinear()
|
||||
{
|
||||
$stepsFound = ArrayList::create();
|
||||
|
||||
$firstStep = DataObject::get_one($this->config()->get('start_step'), "\"SessionID\" = {$this->session->ID}");
|
||||
$firstStep->LinkingMode = ($firstStep->ID == $this->getCurrentStep()->ID) ? 'current' : 'link';
|
||||
$firstStep->setForm($this);
|
||||
$stepsFound->push($firstStep);
|
||||
|
||||
// mark the further steps as non-completed if the first step is the current
|
||||
if ($firstStep->ID == $this->getCurrentStep()->ID) {
|
||||
$this->currentStepHasBeenFound = true;
|
||||
} else {
|
||||
$firstStep->addExtraClass('completed');
|
||||
}
|
||||
|
||||
$this->getAllStepsRecursive($firstStep, $stepsFound);
|
||||
|
||||
return $stepsFound;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively run through steps using the getNextStep() method on each step
|
||||
* to determine what the next step is, gathering each step along the way.
|
||||
* We stop on the last step, and return the results.
|
||||
* If a step in the chain was already saved to the database in the current
|
||||
* session, its used - otherwise a singleton of this step is used.
|
||||
* Caution: Doesn't consider branching for steps which aren't in the database yet.
|
||||
*
|
||||
* @param MultiFormStep $step Subclass of MultiFormStep to find the next step of
|
||||
* @param $stepsFound $stepsFound DataObjectSet reference, the steps found to call back on
|
||||
* @return DataList of MultiFormStep instances
|
||||
*/
|
||||
protected function getAllStepsRecursive($step, &$stepsFound)
|
||||
{
|
||||
// Find the next step to the current step, the final step has no next step
|
||||
if (!$step->isFinalStep()) {
|
||||
if ($step->getNextStep()) {
|
||||
// Is this step in the DB? If it is, we use that
|
||||
$nextStep = $step->getNextStepFromDatabase();
|
||||
if (!$nextStep) {
|
||||
// If it's not in the DB, we use a singleton instance of it instead -
|
||||
// - this step hasn't been accessed yet
|
||||
$nextStep = singleton($step->getNextStep());
|
||||
}
|
||||
|
||||
// once the current steps has been found we won't add the completed class anymore.
|
||||
if ($nextStep->ID == $this->getCurrentStep()->ID) {
|
||||
$this->currentStepHasBeenFound = true;
|
||||
}
|
||||
|
||||
$nextStep->LinkingMode = ($nextStep->ID == $this->getCurrentStep()->ID) ? 'current' : 'link';
|
||||
|
||||
// add the completed class
|
||||
if (!$this->currentStepHasBeenFound) {
|
||||
$nextStep->addExtraClass('completed');
|
||||
}
|
||||
|
||||
$nextStep->setForm($this);
|
||||
|
||||
// Add the array data, and do a callback
|
||||
$stepsFound->push($nextStep);
|
||||
$this->getAllStepsRecursive($nextStep, $stepsFound);
|
||||
}
|
||||
// Once we've reached the final step, we just return what we've collected
|
||||
} else {
|
||||
return $stepsFound;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of steps already completed (excluding currently started step).
|
||||
* The way we determine a step is complete is to check if it has the Data
|
||||
* field filled out with a serialized value, then we know that the user has
|
||||
* clicked next on the given step, to proceed.
|
||||
*
|
||||
* @TODO Not sure if it's entirely appropriate to check if Data is set as a
|
||||
* way to determine a step is "completed".
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getCompletedStepCount()
|
||||
{
|
||||
$steps = DataObject::get(MultiFormStep::class, "\"SessionID\" = {$this->session->ID} && \"Data\" IS NOT NULL");
|
||||
|
||||
return $steps ? $steps->Count() : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Total number of steps in the shortest path (only counting straight path without any branching)
|
||||
* The way we determine this is to check if each step has a next_step string variable set. If it's
|
||||
* anything else (like an array, for defining multiple branches) then it gets counted as a single step.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getTotalStepCount()
|
||||
{
|
||||
return $this->getAllStepsLinear() ? $this->getAllStepsLinear()->Count() : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Percentage of steps completed (excluding currently started step)
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getCompletedPercent()
|
||||
{
|
||||
return (float) $this->getCompletedStepCount() * 100 / $this->getTotalStepCount();
|
||||
}
|
||||
}
|
81
src/Models/MultiFormSession.php
Normal file
81
src/Models/MultiFormSession.php
Normal file
@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\MultiForm\Models;
|
||||
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\Security\Member;
|
||||
use SilverStripe\Security\Security;
|
||||
|
||||
/**
|
||||
* Serializes one or more {@link MultiFormStep}s into
|
||||
* a database object.
|
||||
*
|
||||
* MultiFormSession also stores the current step, so that
|
||||
* the {@link MultiForm} and {@link MultiFormStep} classes
|
||||
* know what the current step is.
|
||||
*
|
||||
*/
|
||||
class MultiFormSession extends DataObject
|
||||
{
|
||||
private static $db = [
|
||||
'Hash' => 'Varchar(40)', // cryptographic hash identification to this session
|
||||
'IsComplete' => 'Boolean' // flag to determine if this session is marked completed
|
||||
];
|
||||
|
||||
private static $has_one = [
|
||||
'Submitter' => Member::class,
|
||||
'CurrentStep' => MultiFormStep::class
|
||||
];
|
||||
|
||||
private static $has_many = [
|
||||
'FormSteps' => MultiFormStep::class
|
||||
];
|
||||
|
||||
private static $table_name = 'MultiFormSession';
|
||||
|
||||
/**
|
||||
* Mark this session as completed.
|
||||
*
|
||||
* This sets the flag "IsComplete" to true,
|
||||
* and writes the session back.
|
||||
*/
|
||||
public function markCompleted()
|
||||
{
|
||||
$this->IsComplete = 1;
|
||||
$this->write();
|
||||
}
|
||||
|
||||
/**
|
||||
* These actions are performed when write() is called on this object.
|
||||
*/
|
||||
public function onBeforeWrite()
|
||||
{
|
||||
// save submitter if a Member is logged in
|
||||
$currentMember = Security::getCurrentUser();
|
||||
if (!$this->SubmitterID && $currentMember) {
|
||||
$this->SubmitterID = $currentMember->ID;
|
||||
}
|
||||
|
||||
parent::onBeforeWrite();
|
||||
}
|
||||
|
||||
/**
|
||||
* These actions are performed when delete() is called on this object.
|
||||
*/
|
||||
public function onBeforeDelete()
|
||||
{
|
||||
// delete dependent form steps and relation
|
||||
$steps = $this->FormSteps();
|
||||
if ($steps) {
|
||||
foreach ($steps as $step) {
|
||||
if ($step && $step->exists()) {
|
||||
$steps->remove($step);
|
||||
$step->delete();
|
||||
$step->destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parent::onBeforeDelete();
|
||||
}
|
||||
}
|
522
src/Models/MultiFormStep.php
Normal file
522
src/Models/MultiFormStep.php
Normal file
@ -0,0 +1,522 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\MultiForm\Models;
|
||||
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\Form;
|
||||
use SilverStripe\Forms\Validator;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
|
||||
/**
|
||||
* MultiFormStep controls the behaviour of a single form step in the MultiForm
|
||||
* process. All form steps are required to be subclasses of this class, as it
|
||||
* encapsulates the functionality required for the step to be aware of itself
|
||||
* in the process by knowing what it's next step is, and if applicable, it's previous
|
||||
* step.
|
||||
*
|
||||
*/
|
||||
class MultiFormStep extends DataObject
|
||||
{
|
||||
private static $db = [
|
||||
'Data' => 'Text' // stores serialized maps with all session information
|
||||
];
|
||||
|
||||
private static $has_one = [
|
||||
'Session' => MultiFormSession::class
|
||||
];
|
||||
|
||||
private static $table_name = 'MultiFormStep';
|
||||
|
||||
/**
|
||||
* Centerpiece of the flow control for the form.
|
||||
*
|
||||
* If set to a string, you have a linear form flow
|
||||
* If set to an array, you should use {@link getNextStep()}
|
||||
* to enact flow control and branching to different form
|
||||
* steps, most likely based on previously set session data
|
||||
* (e.g. a checkbox field or a dropdown).
|
||||
*
|
||||
* @var array|string
|
||||
*/
|
||||
private static $next_steps;
|
||||
|
||||
/**
|
||||
* Each {@link MultiForm} subclass needs at least
|
||||
* one step which is marked as the "final" one
|
||||
* and triggers the {@link MultiForm->finish()}
|
||||
* method that wraps up the whole submission.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
private static $is_final_step = false;
|
||||
|
||||
/**
|
||||
* This variable determines whether a user can use
|
||||
* the "back" action from this step.
|
||||
*
|
||||
* @TODO This does not check if the arbitrarily chosen step
|
||||
* using the step indicator is actually a previous step, so
|
||||
* unless you remove the link from the indicator template, or
|
||||
* type in StepID=23 to the address bar you can still go back
|
||||
* using the step indicator.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected static $can_go_back = true;
|
||||
|
||||
/**
|
||||
* Title of this step.
|
||||
*
|
||||
* Used for the step indicator templates.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $title;
|
||||
|
||||
/**
|
||||
* Form class that this step is directly related to.
|
||||
*
|
||||
* @var MultiForm subclass
|
||||
*/
|
||||
protected $form;
|
||||
|
||||
/**
|
||||
* List of additional CSS classes for this step
|
||||
*
|
||||
* @var array $extraClasses
|
||||
*/
|
||||
protected $extraClasses = [];
|
||||
|
||||
/**
|
||||
* Temporary cache to increase the performance for repeated look ups.
|
||||
*
|
||||
* @var array $cache
|
||||
*/
|
||||
protected $step_data_cache = [];
|
||||
|
||||
/**
|
||||
* Form fields to be rendered with this step.
|
||||
* (Form object is created in {@link MultiForm}.
|
||||
*
|
||||
* This function needs to be implemented on your
|
||||
* subclasses of MultiFormStep.
|
||||
*
|
||||
* @return FieldList
|
||||
*/
|
||||
public function getFields()
|
||||
{
|
||||
user_error('Please implement getFields on your MultiFormStep subclass', E_USER_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional form actions to be added to this step.
|
||||
* (Form object is created in {@link MultiForm}.
|
||||
*
|
||||
* Note: This is optional, and is to be implemented
|
||||
* on your subclasses of MultiFormStep.
|
||||
*
|
||||
* @return FieldList
|
||||
*/
|
||||
public function getExtraActions()
|
||||
{
|
||||
return FieldList::create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a validator specific to this form.
|
||||
* The form is automatically validated in {@link Form->httpSubmission()}.
|
||||
*
|
||||
* @return bool|Validator
|
||||
*/
|
||||
public function getValidator()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor method for $this->title
|
||||
*
|
||||
* @return string Title of this step
|
||||
*/
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->title ? $this->title : get_class($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a direct link to this step (only works
|
||||
* if you're allowed to skip steps, or this step
|
||||
* has already been saved to the database
|
||||
* for the current {@link MultiFormSession}).
|
||||
*
|
||||
* @return string Relative URL to this step
|
||||
*/
|
||||
public function Link()
|
||||
{
|
||||
$form = $this->form;
|
||||
return Controller::join_links(
|
||||
$form->getDisplayLink(),
|
||||
"?{$form->getGetVar()}={$this->getSession()->Hash}"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unserialize stored session data and return it.
|
||||
* This is used for loading data previously saved
|
||||
* in session back into the form.
|
||||
*
|
||||
* You need to overload this method onto your own
|
||||
* step if you require custom loading. An example
|
||||
* would be selective loading specific fields, leaving
|
||||
* others that are not required.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function loadData()
|
||||
{
|
||||
return ($this->Data && is_string($this->Data)) ? unserialize($this->Data) : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the data for this step into session, serializing it first.
|
||||
*
|
||||
* To selectively save fields, instead of it all, this
|
||||
* method would need to be overloaded on your step class.
|
||||
*
|
||||
* @param array $data The processed data from save() on {@link MultiForm}
|
||||
*/
|
||||
public function saveData($data)
|
||||
{
|
||||
$this->Data = serialize($data);
|
||||
$this->write();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the data on this step into an object,
|
||||
* similiar to {@link Form->saveInto()} - by building
|
||||
* a stub form from {@link getFields()}. This is necessary
|
||||
* to trigger each {@link FormField->saveInto()} method
|
||||
* individually, rather than assuming that all data
|
||||
* serialized through {@link saveData()} can be saved
|
||||
* as a simple value outside of the original FormField context.
|
||||
*
|
||||
* @param DataObject $obj
|
||||
* @return DataObject
|
||||
*/
|
||||
public function saveInto($obj)
|
||||
{
|
||||
$form = Form::create(
|
||||
Controller::curr(),
|
||||
'Form',
|
||||
$this->getFields(),
|
||||
FieldList::create()
|
||||
);
|
||||
$form->loadDataFrom($this->loadData());
|
||||
$form->saveInto($obj);
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom validation for a step. In most cases, it should be sufficient
|
||||
* to have built-in validation through the {@link Validator} class
|
||||
* on the {@link getValidator()} method.
|
||||
*
|
||||
* Use {@link Form->sessionMessage()} to feed back validation messages
|
||||
* to the user. Please don't redirect from this method,
|
||||
* this is taken care of in {@link next()}.
|
||||
*
|
||||
* @param array $data Request data
|
||||
* @param Form $form
|
||||
* @return boolean Validation success
|
||||
*/
|
||||
public function validateStep($data, $form)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first value of $next_step
|
||||
*
|
||||
* @return string Classname of a {@link MultiFormStep} subclass
|
||||
*/
|
||||
public function getNextStep()
|
||||
{
|
||||
$nextSteps = $this->config()->get('next_steps');
|
||||
|
||||
// Check if next_steps have been implemented properly if not the final step
|
||||
if (!$this->isFinalStep()) {
|
||||
if (!isset($nextSteps)) {
|
||||
user_error(
|
||||
'MultiFormStep->getNextStep(): Please define at least one $next_steps on '
|
||||
. $this->class,
|
||||
E_USER_ERROR
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_string($nextSteps)) {
|
||||
return $nextSteps;
|
||||
} elseif (is_array($nextSteps) && count($nextSteps)) {
|
||||
// custom flow control goes here
|
||||
return $nextSteps[0];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next step to the current step in the database.
|
||||
*
|
||||
* This will only return something if you've previously visited
|
||||
* the step ahead of the current step, and then gone back a step.
|
||||
*
|
||||
* @return MultiFormStep|boolean|void
|
||||
*/
|
||||
public function getNextStepFromDatabase()
|
||||
{
|
||||
if ($this->SessionID && is_numeric($this->SessionID)) {
|
||||
$nextSteps = $this->config()->get('next_steps');
|
||||
|
||||
if (is_string($nextSteps)) {
|
||||
return DataObject::get_one($nextSteps, "\"SessionID\" = {$this->SessionID}");
|
||||
} elseif (is_array($nextSteps)) {
|
||||
return DataObject::get_one($nextSteps[0], "\"SessionID\" = {$this->SessionID}");
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor method for self::$next_steps
|
||||
*
|
||||
* @return string|array
|
||||
*/
|
||||
public function getNextSteps()
|
||||
{
|
||||
return $this->config()->get('next_steps');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the previous step, if there is one.
|
||||
*
|
||||
* To determine if there is a previous step, we check the database to see if there's
|
||||
* a previous step for this multi form session ID.
|
||||
*
|
||||
* @return string|void Classname of a {@link MultiFormStep} subclass
|
||||
*/
|
||||
public function getPreviousStep()
|
||||
{
|
||||
$steps = DataObject::get(MultiFormStep::class, "\"SessionID\" = {$this->SessionID}", '"LastEdited" DESC');
|
||||
if ($steps) {
|
||||
foreach ($steps as $step) {
|
||||
$step->setForm($this->form);
|
||||
|
||||
if ($step->getNextStep()) {
|
||||
if ($step->getNextStep() == $this->class) {
|
||||
return $step->class;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the previous step class record from the database.
|
||||
*
|
||||
* This will only return a record if you've previously been on the step.
|
||||
*
|
||||
* @return MultiFormStep subclass
|
||||
*/
|
||||
public function getPreviousStepFromDatabase()
|
||||
{
|
||||
if ($prevStepClass = $this->getPreviousStep()) {
|
||||
return DataObject::get_one($prevStepClass, "\"SessionID\" = {$this->SessionID}");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the text to the use on the button to the previous step.
|
||||
* @return string
|
||||
*/
|
||||
public function getPrevText()
|
||||
{
|
||||
return _t(__CLASS__ . '.BACK', 'Back');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the text to use on the button to the next step.
|
||||
* @return string
|
||||
*/
|
||||
public function getNextText()
|
||||
{
|
||||
return _t(__CLASS__ . '.NEXT', 'Next');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the text to use on the button to submit the form.
|
||||
* @return string
|
||||
*/
|
||||
public function getSubmitText()
|
||||
{
|
||||
return _t(__CLASS__ . '.SUBMIT', 'Submit');
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the form that this step is directly related to.
|
||||
*
|
||||
* @param MultiForm $form subclass
|
||||
*/
|
||||
public function setForm($form)
|
||||
{
|
||||
$this->form = $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Form
|
||||
*/
|
||||
public function getForm()
|
||||
{
|
||||
return $this->form;
|
||||
}
|
||||
|
||||
// ##################### Utility ####################
|
||||
|
||||
/**
|
||||
* Determines whether the user is able to go back using the "action_back"
|
||||
* Determines whether the user is able to go back using the "action_back"
|
||||
* Determines whether the user is able to go back using the "action_back"
|
||||
* form action, based on the boolean value of $can_go_back.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function canGoBack()
|
||||
{
|
||||
return $this->config()->get('can_go_back');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether this step is the final step in the multi-step process or not,
|
||||
* based on the variable $is_final_step - which must be defined on at least one step.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isFinalStep()
|
||||
{
|
||||
return $this->config()->get('is_final_step');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the currently viewed step is the current step set in the session.
|
||||
* This assumes you are checking isCurrentStep() against a data record of a MultiFormStep
|
||||
* subclass, otherwise it doesn't work. An example of this is using a singleton instance - it won't
|
||||
* work because there's no data.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isCurrentStep()
|
||||
{
|
||||
return ($this->class == $this->getSession()->CurrentStep()->class) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a CSS-class to the step. If needed, multiple classes can be added by delimiting a string with spaces.
|
||||
*
|
||||
* @param string $class A string containing a classname or several class names delimited by a space.
|
||||
* @return MultiFormStep
|
||||
*/
|
||||
public function addExtraClass($class)
|
||||
{
|
||||
// split at white space
|
||||
$classes = preg_split('/\s+/', $class);
|
||||
foreach ($classes as $class) {
|
||||
// add classes one by one
|
||||
$this->extraClasses[$class] = $class;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a CSS-class from the step. Multiple classes names can be passed through as a space delimited string.
|
||||
*
|
||||
* @param string $class
|
||||
* @return MultiFormStep
|
||||
*/
|
||||
public function removeExtraClass($class)
|
||||
{
|
||||
// split at white space
|
||||
$classes = preg_split('/\s+/', $class);
|
||||
foreach ($classes as $class) {
|
||||
// unset one by one
|
||||
unset($this->extraClasses[$class]);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getExtraClasses()
|
||||
{
|
||||
return join(' ', array_keys($this->extraClasses));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the submitted value, if any, of any steps.
|
||||
*
|
||||
* @param string $fromStep (classname)
|
||||
* @param string $key
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getValueFromOtherStep($fromStep, $key)
|
||||
{
|
||||
// load the steps in the cache, if this one doesn't exist
|
||||
if (!array_key_exists('steps_' . $fromStep, $this->step_data_cache)) {
|
||||
$steps = self::get()->filter('SessionID', $this->form->session->ID);
|
||||
|
||||
if ($steps) {
|
||||
foreach ($steps as $step) {
|
||||
$this->step_data_cache['steps_' . $step->ClassName] = $step->loadData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check both as PHP isn't recursive
|
||||
if (isset($this->step_data_cache['steps_' . $fromStep])) {
|
||||
if (isset($this->step_data_cache['steps_' . $fromStep][$key])) {
|
||||
return $this->step_data_cache['steps_' . $fromStep][$key];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* allows to get a value from another step copied over
|
||||
*
|
||||
* @param FieldList $fields
|
||||
* @param string $formStep
|
||||
* @param string $fieldName
|
||||
* @param string $fieldNameTarget (optional)
|
||||
*/
|
||||
public function copyValueFromOtherStep(FieldList $fields, $formStep, $fieldName, $fieldNameTarget = null)
|
||||
{
|
||||
// if a target field isn't defined use the same fieldname
|
||||
if (!$fieldNameTarget) {
|
||||
$fieldNameTarget = $fieldName;
|
||||
}
|
||||
|
||||
$fields->fieldByName($fieldNameTarget)->setValue($this->getValueFromOtherStep($formStep, $fieldName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the linked MultiFormSession
|
||||
* @return MultiFormSession
|
||||
*/
|
||||
public function getSession()
|
||||
{
|
||||
return $this->Session();
|
||||
}
|
||||
}
|
79
src/extensions/MultiFormObjectDecorator.php
Normal file
79
src/extensions/MultiFormObjectDecorator.php
Normal file
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\MultiForm\Extensions;
|
||||
|
||||
use SilverStripe\MultiForm\Models\MultiFormSession;
|
||||
use SilverStripe\ORM\DataExtension;
|
||||
use SilverStripe\ORM\DataQuery;
|
||||
use SilverStripe\ORM\Queries\SQLSelect;
|
||||
|
||||
/**
|
||||
* Decorate {@link DataObject}s which are required to be saved
|
||||
* to the database directly by a {@link MultiFormStep}.
|
||||
* Only needed for objects which aren't stored in the session,
|
||||
* which is the default.
|
||||
*
|
||||
* This decorator also augments get() requests to the datalayer
|
||||
* by automatically filtering out temporary objects.
|
||||
* You can override this filter by putting the following statement
|
||||
* in your WHERE clause:
|
||||
* `<MyDataObjectClass>`.`MultiFormIsTemporary` = 1
|
||||
*
|
||||
*/
|
||||
class MultiFormObjectDecorator extends DataExtension
|
||||
{
|
||||
private static $db = [
|
||||
'MultiFormIsTemporary' => 'Boolean',
|
||||
];
|
||||
|
||||
private static $has_one = [
|
||||
'MultiFormSession' => MultiFormSession::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Augment any queries to MultiFormObjectDecorator and only
|
||||
* return anything that isn't considered temporary.
|
||||
* @param SQLSelect $query
|
||||
* @param DataQuery|null $dataQuery
|
||||
*/
|
||||
public function augmentSQL(SQLSelect $query, DataQuery $dataQuery = null)
|
||||
{
|
||||
$where = $query->getWhere();
|
||||
if (!$where && !$this->wantsTemporary($query)) {
|
||||
$from = array_values($query->getFrom());
|
||||
$query->addWhere("{$from[0]}.\"MultiFormIsTemporary\" = '0'");
|
||||
return;
|
||||
}
|
||||
$filterKey = key($where[0]);
|
||||
if (strpos($filterKey, ".`ID` = ") === false
|
||||
&& strpos($filterKey, ".ID = ") === false
|
||||
&& strpos($filterKey, "ID = ") !== 0
|
||||
&& !$this->wantsTemporary($query)
|
||||
) {
|
||||
$from = array_values($query->getFrom());
|
||||
$query->addWhere("{$from[0]}.\"MultiFormIsTemporary\" = '0'");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the current query is supposed
|
||||
* to be exempt from the automatic filtering out
|
||||
* of temporary records.
|
||||
*
|
||||
* @param SQLSelect $query
|
||||
* @return boolean
|
||||
*/
|
||||
protected function wantsTemporary($query)
|
||||
{
|
||||
foreach ($query->getWhere() as $whereClause) {
|
||||
$from = array_values($query->getFrom());
|
||||
// SQLQuery will automatically add double quotes and single quotes to values, so check against that.
|
||||
$key = key($whereClause);
|
||||
if ($key == "{$from[0]}.\"MultiFormIsTemporary\" = ?" && current($whereClause[$key]) == 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
67
src/tasks/MultiFormPurgeTask.php
Normal file
67
src/tasks/MultiFormPurgeTask.php
Normal file
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\MultiForm\Tasks;
|
||||
|
||||
use SilverStripe\Dev\BuildTask;
|
||||
use SilverStripe\MultiForm\Models\MultiFormSession;
|
||||
use SilverStripe\ORM\DataList;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
|
||||
/**
|
||||
* Task to clean out all {@link MultiFormSession} objects from the database.
|
||||
*
|
||||
* Setup Instructions:
|
||||
* You need to create an automated task for your system (cronjobs on unix)
|
||||
* which triggers the process() method through cli-script.php:
|
||||
* `php framework/cli-script.php MultiFormPurgeTask`
|
||||
* or
|
||||
* `framework/sake MultiFormPurgeTask`
|
||||
*
|
||||
*/
|
||||
class MultiFormPurgeTask extends BuildTask
|
||||
{
|
||||
/**
|
||||
* Days after which sessions expire and
|
||||
* are automatically deleted.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private static $session_expiry_days = 7;
|
||||
|
||||
private static $segment = 'MultiFormPurgeTask';
|
||||
|
||||
/**
|
||||
* Run this cron task.
|
||||
*
|
||||
* Go through all MultiFormSession records that
|
||||
* are older than the days specified in $session_expiry_days
|
||||
* and delete them.
|
||||
*/
|
||||
public function run($request)
|
||||
{
|
||||
$sessions = $this->getExpiredSessions();
|
||||
$delCount = 0;
|
||||
if ($sessions) {
|
||||
foreach ($sessions as $session) {
|
||||
$session->delete();
|
||||
$delCount++;
|
||||
}
|
||||
}
|
||||
echo $delCount . ' session records deleted that were older than '
|
||||
. $this->config()->get('session_expiry_days') . ' days.'. PHP_EOL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all MultiFormSession database records that are older than
|
||||
* the days specified in $session_expiry_days
|
||||
*
|
||||
* @return DataList
|
||||
*/
|
||||
protected function getExpiredSessions()
|
||||
{
|
||||
return DataObject::get(
|
||||
MultiFormSession::class,
|
||||
"DATEDIFF(NOW(), \"MultiFormSession\".\"Created\") > " . $this->config()->get('session_expiry_days')
|
||||
);
|
||||
}
|
||||
}
|
@ -1,9 +1,19 @@
|
||||
<ul class="stepIndicator current-$CurrentStep.class">
|
||||
<% loop AllStepsLinear %>
|
||||
<li class="$ClassName<% if LinkingMode %> $LinkingMode<% end_if %><% if FirstLast %> $FirstLast<% end_if %><% if $ExtraClasses %> $ExtraClasses<% end_if %>">
|
||||
<% if LinkingMode = current %><% else %><% if ID %><a href="{$Top.URLSegment}/?${Top.GetVar}={$SessionID}&StepID={$ID}"><% end_if %><% end_if %>
|
||||
<% if Title %>$Title<% else %>$ClassName<% end_if %>
|
||||
<% if LinkingMode = current %><% else %><% if ID %></a><% end_if %><% end_if %>
|
||||
</li>
|
||||
<% end_loop %>
|
||||
<% loop $AllStepsLinear %>
|
||||
<li class="$ClassName<% if $LinkingMode %> $LinkingMode<% end_if %><% if $FirstLast %> $FirstLast<% end_if %><% if $ExtraClasses %> $ExtraClasses<% end_if %>">
|
||||
<% if $LinkingMode = current %>
|
||||
<% else %>
|
||||
<% if $ID %>
|
||||
<a href="{$Top.URLSegment}/?${Top.GetVar}={$SessionID}&StepID={$ID}">
|
||||
<% end_if %>
|
||||
<% end_if %>
|
||||
|
||||
<% if $Title %>$Title<% else %>$ClassName<% end_if %>
|
||||
|
||||
<% if $LinkingMode = current %>
|
||||
<% else %>
|
||||
<% if $ID %></a><% end_if %>
|
||||
<% end_if %>
|
||||
</li>
|
||||
<% end_loop %>
|
||||
</ul>
|
||||
|
@ -1 +1 @@
|
||||
<p>You've completed {$CompletedPercent.Nice}% ($CompletedStepCount/$TotalStepCount)</p>
|
||||
<p><%t SilverStripe\\MultiForm\\MultiForm.ProgressPercent "You've completed {percent}% ({completedSteps}/{totalSteps})" percent=$CompletedPercent.Nice completedSteps=$CompletedStepCount totalSteps$TotalStepCount %></p>
|
||||
|
@ -1,44 +1,42 @@
|
||||
<?php
|
||||
class MultiFormObjectDecoratorTest extends SapphireTest {
|
||||
|
||||
protected static $fixture_file = 'MultiFormObjectDecoratorTest.yml';
|
||||
namespace SilverStripe\MultiForm\Tests;
|
||||
|
||||
protected $requiredExtensions = array(
|
||||
'MultiFormObjectDecorator_DataObject' => array('MultiFormObjectDecorator')
|
||||
);
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\MultiForm\Extensions\MultiFormObjectDecorator;
|
||||
use SilverStripe\MultiForm\Tests\Stubs\MultiFormObjectDecoratorDataObject;
|
||||
|
||||
protected $extraDataObjects = array(
|
||||
'MultiFormObjectDecorator_DataObject'
|
||||
);
|
||||
class MultiFormObjectDecoratorTest extends SapphireTest
|
||||
{
|
||||
protected static $fixture_file = 'MultiFormObjectDecoratorTest.yml';
|
||||
|
||||
public function testTemporaryDataFilteredQuery() {
|
||||
$records = MultiFormObjectDecorator_DataObject::get()
|
||||
->map('Name')
|
||||
->toArray();
|
||||
protected static $required_extensions = [
|
||||
MultiFormObjectDecoratorDataObject::class => [MultiFormObjectDecorator::class]
|
||||
];
|
||||
|
||||
$this->assertContains('Test 1', $records);
|
||||
$this->assertContains('Test 2', $records);
|
||||
$this->assertNotContains('Test 3', $records);
|
||||
protected static $extra_dataobjects = [
|
||||
MultiFormObjectDecoratorDataObject::class
|
||||
];
|
||||
|
||||
}
|
||||
public function testTemporaryDataFilteredQuery()
|
||||
{
|
||||
$records = MultiFormObjectDecoratorDataObject::get()
|
||||
->map('Name')
|
||||
->toArray();
|
||||
|
||||
public function testTemporaryDataQuery() {
|
||||
$records = MultiFormObjectDecorator_DataObject::get()
|
||||
->filter(array('MultiFormIsTemporary' => 1))
|
||||
->map('Name')
|
||||
->toArray();
|
||||
|
||||
$this->assertNotContains('Test 1', $records);
|
||||
$this->assertNotContains('Test 2', $records);
|
||||
$this->assertContains('Test 3', $records);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class MultiFormObjectDecorator_DataObject extends DataObject {
|
||||
|
||||
private static $db = array(
|
||||
'Name' => 'Varchar'
|
||||
);
|
||||
$this->assertContains('Test 1', $records);
|
||||
$this->assertContains('Test 2', $records);
|
||||
$this->assertNotContains('Test 3', $records);
|
||||
}
|
||||
|
||||
public function testTemporaryDataQuery()
|
||||
{
|
||||
$records = MultiFormObjectDecoratorDataObject::get()
|
||||
->filter(['MultiFormIsTemporary' => 1])
|
||||
->map('Name')
|
||||
->toArray();
|
||||
$this->assertNotContains('Test 1', $records);
|
||||
$this->assertNotContains('Test 2', $records);
|
||||
$this->assertContains('Test 3', $records);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
MultiFormObjectDecorator_DataObject:
|
||||
SilverStripe\MultiForm\Tests\Stubs\MultiFormObjectDecoratorDataObject:
|
||||
test-data-1:
|
||||
Name: Test 1
|
||||
MultiFormIsTemporary: 0
|
||||
|
@ -1,178 +1,141 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\MultiForm\Tests;
|
||||
|
||||
use SilverStripe\Control\HTTPRequest;
|
||||
use SilverStripe\Control\Session;
|
||||
use SilverStripe\Core\Config\Config;
|
||||
use SilverStripe\Core\Injector\Injector;
|
||||
use SilverStripe\Dev\FunctionalTest;
|
||||
use SilverStripe\MultiForm\Models\MultiForm;
|
||||
use SilverStripe\MultiForm\Models\MultiFormSession;
|
||||
use SilverStripe\MultiForm\Tests\Stubs\MultiFormTestController;
|
||||
use SilverStripe\MultiForm\Tests\Stubs\MultiFormTestForm;
|
||||
use SilverStripe\MultiForm\Tests\Stubs\MultiFormTestStepOne;
|
||||
use SilverStripe\MultiForm\Tests\Stubs\MultiFormTestStepTwo;
|
||||
|
||||
/**
|
||||
* MultiFormTest
|
||||
* For testing purposes, we have some test classes:
|
||||
*
|
||||
* - MultiFormTest_Controller (simulation of a real Controller class)
|
||||
* - MultiFormTest_Form (subclass of MultiForm)
|
||||
* - MultiFormTest_StepOne (subclass of MultiFormStep)
|
||||
* - MultiFormTest_StepTwo (subclass of MultiFormStep)
|
||||
* - MultiFormTest_StepThree (subclass of MultiFormStep)
|
||||
* - MultiFormTestController (simulation of a real Controller class)
|
||||
* - MultiFormTestForm (subclass of MultiForm)
|
||||
* - MultiFormTestStepOne (subclass of MultiFormStep)
|
||||
* - MultiFormTestStepTwo (subclass of MultiFormStep)
|
||||
* - MultiFormTestStepThree (subclass of MultiFormStep)
|
||||
*
|
||||
* The above classes are used to simulate real-world behaviour
|
||||
* of the multiform module - for example, MultiFormTest_Controller
|
||||
* of the multiform module - for example, MultiFormTestController
|
||||
* is a simulation of a page where MultiFormTest_Form is a simple
|
||||
* multi-step contact form it belongs to.
|
||||
*
|
||||
* @package multiform
|
||||
* @subpackage tests
|
||||
*/
|
||||
class MultiFormTest extends FunctionalTest {
|
||||
class MultiFormTest extends FunctionalTest
|
||||
{
|
||||
protected static $fixture_file = 'MultiFormTest.yml';
|
||||
|
||||
public static $fixture_file = 'multiform/tests/MultiFormTest.yml';
|
||||
/**
|
||||
* @var MultiFormTestController
|
||||
*/
|
||||
protected $controller;
|
||||
|
||||
protected $controller;
|
||||
/**
|
||||
* @var MultiFormTestForm
|
||||
*/
|
||||
protected $form;
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->controller = new MultiFormTest_Controller();
|
||||
$this->form = $this->controller->Form();
|
||||
}
|
||||
$this->controller = new MultiFormTestController();
|
||||
$this->controller->setRequest(new HTTPRequest('GET', '/'));
|
||||
$this->controller->getRequest()->setSession(new Session([]));
|
||||
$this->controller->pushCurrent();
|
||||
$form = $this->form = $this->controller->Form();
|
||||
Injector::inst()->registerService($form, MultiForm::class);
|
||||
$this->form = $form;
|
||||
}
|
||||
|
||||
public function testInitialisingForm() {
|
||||
$this->assertTrue(is_numeric($this->form->getCurrentStep()->ID) && ($this->form->getCurrentStep()->ID > 0));
|
||||
$this->assertTrue(is_numeric($this->form->getSession()->ID) && ($this->form->getSession()->ID > 0));
|
||||
$this->assertEquals('MultiFormTest_StepOne', $this->form->getStartStep());
|
||||
}
|
||||
public function testInitialisingForm()
|
||||
{
|
||||
$this->assertTrue(is_numeric($this->form->getCurrentStep()->ID) && ($this->form->getCurrentStep()->ID > 0));
|
||||
$this->assertTrue(
|
||||
is_numeric($this->form->getMultiFormSession()->ID)
|
||||
&& ($this->form->getMultiFormSession()->ID > 0)
|
||||
);
|
||||
$this->assertEquals(MultiFormTestStepOne::class, $this->form->getStartStep());
|
||||
}
|
||||
|
||||
public function testSessionGeneration() {
|
||||
$this->assertTrue($this->form->session->ID > 0);
|
||||
}
|
||||
public function testSessionGeneration()
|
||||
{
|
||||
$this->assertTrue($this->form->getMultiFormSession()->ID > 0);
|
||||
}
|
||||
|
||||
public function testMemberLogging() {
|
||||
// Grab any user to fake being logged in as, and ensure that after a session is written it has
|
||||
// that user as the submitter.
|
||||
$userId = Member::get_one("Member")->ID;
|
||||
$this->session()->inst_set('loggedInAs', $userId);
|
||||
public function testMemberLogging()
|
||||
{
|
||||
// Grab any user to fake being logged in as, and ensure that after a session is written it has
|
||||
// that user as the submitter.
|
||||
|
||||
$session = $this->form->session;
|
||||
$session->write();
|
||||
$userId = $this->logInWithPermission('ADMIN');
|
||||
|
||||
$this->assertEquals($userId, $session->SubmitterID);
|
||||
}
|
||||
$session = $this->form->getMultiFormSession();
|
||||
$session->write();
|
||||
|
||||
public function testSecondStep() {
|
||||
$this->assertEquals('MultiFormTest_StepTwo', $this->form->getCurrentStep()->getNextStep());
|
||||
}
|
||||
$this->assertEquals($userId, $session->SubmitterID);
|
||||
}
|
||||
|
||||
public function testParentForm() {
|
||||
$currentStep = $this->form->getCurrentStep();
|
||||
$this->assertEquals($currentStep->getForm()->class, $this->form->class);
|
||||
}
|
||||
public function testSecondStep()
|
||||
{
|
||||
$this->assertEquals(MultiFormTestStepTwo::class, $this->form->getCurrentStep()->getNextStep());
|
||||
}
|
||||
|
||||
public function testTotalStepCount() {
|
||||
$this->assertEquals(3, $this->form->getAllStepsLinear()->Count());
|
||||
}
|
||||
public function testParentForm()
|
||||
{
|
||||
$currentStep = $this->form->getCurrentStep();
|
||||
$this->assertEquals($currentStep->getForm()->class, $this->form->class);
|
||||
}
|
||||
|
||||
public function testCompletedSession() {
|
||||
$this->form->setCurrentSessionHash($this->form->session->Hash);
|
||||
$this->assertInstanceOf('MultiFormSession', $this->form->getCurrentSession());
|
||||
$this->form->session->markCompleted();
|
||||
$this->assertNull($this->form->getCurrentSession());
|
||||
}
|
||||
public function testTotalStepCount()
|
||||
{
|
||||
$this->assertEquals(3, $this->form->getAllStepsLinear()->Count());
|
||||
}
|
||||
|
||||
public function testIncorrectSessionIdentifier() {
|
||||
$this->form->setCurrentSessionHash('sdfsdf3432325325sfsdfdf'); // made up!
|
||||
public function testCompletedSession()
|
||||
{
|
||||
$this->form->setCurrentSessionHash($this->form->getMultiFormSession()->Hash);
|
||||
$this->assertInstanceOf(MultiFormSession::class, $this->form->getCurrentSession());
|
||||
$this->form->getMultiFormSession()->markCompleted();
|
||||
$this->assertNull($this->form->getCurrentSession());
|
||||
}
|
||||
|
||||
// A new session is generated, even though we made up the identifier
|
||||
$this->assertInstanceOf('MultiFormSession', $this->form->session);
|
||||
}
|
||||
public function testIncorrectSessionIdentifier()
|
||||
{
|
||||
$this->form->setCurrentSessionHash('sdfsdf3432325325sfsdfdf'); // made up!
|
||||
|
||||
function testCustomGetVar() {
|
||||
Config::nest();
|
||||
Config::inst()->update('MultiForm', 'get_var', 'SuperSessionID');
|
||||
// A new session is generated, even though we made up the identifier
|
||||
$this->assertInstanceOf(MultiFormSession::class, $this->form->getMultiFormSession());
|
||||
}
|
||||
|
||||
$form = $this->controller->Form();
|
||||
$this->assertContains('SuperSessionID', $form::$ignored_fields, "GET var wasn't added to ignored fields");
|
||||
$this->assertContains('SuperSessionID', $form->FormAction(), "Form action doesn't contain correct session
|
||||
ID parameter");
|
||||
$this->assertContains('SuperSessionID', $form->getCurrentStep()->Link(), "Form step doesn't contain correct
|
||||
session ID parameter");
|
||||
|
||||
Config::unnest();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @package multiform
|
||||
* @subpackage tests
|
||||
*/
|
||||
class MultiFormTest_Controller extends Controller implements TestOnly {
|
||||
|
||||
public function Link() {
|
||||
return 'MultiFormTest_Controller';
|
||||
}
|
||||
|
||||
public function Form($request = null) {
|
||||
$form = new MultiFormTest_Form($this, 'Form');
|
||||
$form->setHTMLID('MultiFormTest_Form');
|
||||
return $form;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @package multiform
|
||||
* @subpackage tests
|
||||
*/
|
||||
class MultiFormTest_Form extends MultiForm implements TestOnly {
|
||||
|
||||
public static $start_step = 'MultiFormTest_StepOne';
|
||||
|
||||
public function getStartStep() {
|
||||
return self::$start_step;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @package multiform
|
||||
* @subpackage tests
|
||||
*/
|
||||
class MultiFormTest_StepOne extends MultiFormStep implements TestOnly {
|
||||
|
||||
public static $next_steps = 'MultiFormTest_StepTwo';
|
||||
|
||||
public function getFields() {
|
||||
$class = (class_exists('FieldList')) ? 'FieldList' : 'FieldSet';
|
||||
return new $class(
|
||||
new TextField('FirstName', 'First name'),
|
||||
new TextField('Surname', 'Surname'),
|
||||
new EmailField('Email', 'Email address')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @package multiform
|
||||
* @subpackage tests
|
||||
*/
|
||||
class MultiFormTest_StepTwo extends MultiFormStep implements TestOnly {
|
||||
|
||||
public static $next_steps = 'MultiFormTest_StepThree';
|
||||
|
||||
public function getFields() {
|
||||
$class = (class_exists('FieldList')) ? 'FieldList' : 'FieldSet';
|
||||
return new $class(
|
||||
new TextareaField('Comments', 'Tell us a bit about yourself...')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @package multiform
|
||||
* @subpackage tests
|
||||
*/
|
||||
class MultiFormTest_StepThree extends MultiFormStep implements TestOnly {
|
||||
|
||||
public static $is_final_step = true;
|
||||
|
||||
public function getFields() {
|
||||
$class = (class_exists('FieldList')) ? 'FieldList' : 'FieldSet';
|
||||
return new $class(
|
||||
new TextField('Test', 'Anything else you\'d like to tell us?')
|
||||
);
|
||||
}
|
||||
public function testCustomGetVar()
|
||||
{
|
||||
Config::modify()->set(MultiForm::class, 'get_var', 'SuperSessionID');
|
||||
|
||||
$form = $this->controller->Form();
|
||||
$this->assertContains(
|
||||
'SuperSessionID',
|
||||
$form->config()->get('ignored_fields'),
|
||||
'GET var wasn\'t added to ignored fields'
|
||||
);
|
||||
$this->assertContains(
|
||||
'SuperSessionID',
|
||||
$form->FormAction(),
|
||||
"Form action doesn't contain correct session ID parameter"
|
||||
);
|
||||
$this->assertContains(
|
||||
'SuperSessionID',
|
||||
$form->getCurrentStep()->Link(),
|
||||
"Form step doesn't contain correct session ID parameter"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
Member:
|
||||
SilverStripe\Security\Member:
|
||||
admin:
|
||||
FirstName: Admin
|
||||
Surname: Admin
|
||||
|
15
tests/Stubs/MultiFormObjectDecoratorDataObject.php
Normal file
15
tests/Stubs/MultiFormObjectDecoratorDataObject.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\MultiForm\Tests\Stubs;
|
||||
|
||||
use SilverStripe\Dev\TestOnly;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
|
||||
class MultiFormObjectDecoratorDataObject extends DataObject implements TestOnly
|
||||
{
|
||||
private static $db = [
|
||||
'Name' => 'Varchar'
|
||||
];
|
||||
|
||||
private static $table_name = 'MultiFormObjectDecoratorDataObject';
|
||||
}
|
23
tests/Stubs/MultiFormStepOne.php
Normal file
23
tests/Stubs/MultiFormStepOne.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\MultiForm\Tests\Stubs;
|
||||
|
||||
use SilverStripe\Dev\TestOnly;
|
||||
use SilverStripe\Forms\EmailField;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\TextField;
|
||||
use SilverStripe\MultiForm\Models\MultiFormStep;
|
||||
|
||||
class MultiFormTestStepOne extends MultiFormStep implements TestOnly
|
||||
{
|
||||
private static $next_steps = MultiFormTestStepTwo::class;
|
||||
|
||||
public function getFields()
|
||||
{
|
||||
return FieldList::create(
|
||||
new TextField('FirstName', 'First name'),
|
||||
new TextField('Surname', 'Surname'),
|
||||
new EmailField('Email', 'Email address')
|
||||
);
|
||||
}
|
||||
}
|
18
tests/Stubs/MultiFormTestController.php
Normal file
18
tests/Stubs/MultiFormTestController.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\MultiForm\Tests\Stubs;
|
||||
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Core\Injector\Injector;
|
||||
use SilverStripe\Dev\TestOnly;
|
||||
|
||||
class MultiFormTestController extends Controller implements TestOnly
|
||||
{
|
||||
private static $url_segment = 'MultiFormTestController';
|
||||
|
||||
public function Form()
|
||||
{
|
||||
return Injector::inst()->get(MultiFormTestForm::class, false, [$this, 'Form'])
|
||||
->setHTMLID(MultiFormTestForm::class);
|
||||
}
|
||||
}
|
16
tests/Stubs/MultiFormTestForm.php
Normal file
16
tests/Stubs/MultiFormTestForm.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\MultiForm\Tests\Stubs;
|
||||
|
||||
use SilverStripe\Dev\TestOnly;
|
||||
use SilverStripe\MultiForm\Models\MultiForm;
|
||||
|
||||
class MultiFormTestForm extends MultiForm implements TestOnly
|
||||
{
|
||||
private static $start_step = MultiFormTestStepOne::class;
|
||||
|
||||
public function getStartStep()
|
||||
{
|
||||
return $this->config()->get('start_step');
|
||||
}
|
||||
}
|
20
tests/Stubs/MultiFormTestStepThree.php
Normal file
20
tests/Stubs/MultiFormTestStepThree.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\MultiForm\Tests\Stubs;
|
||||
|
||||
use SilverStripe\Dev\TestOnly;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\TextField;
|
||||
use SilverStripe\MultiForm\Models\MultiFormStep;
|
||||
|
||||
class MultiFormTestStepThree extends MultiFormStep implements TestOnly
|
||||
{
|
||||
private static $is_final_step = true;
|
||||
|
||||
public function getFields()
|
||||
{
|
||||
return FieldList::create(
|
||||
new TextField('Test', 'Anything else you\'d like to tell us?')
|
||||
);
|
||||
}
|
||||
}
|
19
tests/Stubs/MultiFormTestStepTwo.php
Normal file
19
tests/Stubs/MultiFormTestStepTwo.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
namespace SilverStripe\MultiForm\Tests\Stubs;
|
||||
|
||||
use SilverStripe\Dev\TestOnly;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\TextareaField;
|
||||
use SilverStripe\MultiForm\Models\MultiFormStep;
|
||||
|
||||
class MultiFormTestStepTwo extends MultiFormStep implements TestOnly
|
||||
{
|
||||
private static $next_steps = MultiFormTestStepThree::class;
|
||||
|
||||
public function getFields()
|
||||
{
|
||||
return new FieldList(
|
||||
new TextareaField('Comments', 'Tell us a bit about yourself...')
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user