Passed
Push — master ( ddbf8b...086098 )
by Robbie
11:17
created

Form::buildFieldList()   B

Complexity

Conditions 7
Paths 18

Size

Total Lines 38
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 17
nc 18
nop 1
dl 0
loc 38
rs 8.8333
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Security\Confirmation;
4
5
use SilverStripe\Core\Injector\Injector;
6
use SilverStripe\Control\RequestHandler;
7
use SilverStripe\Forms\Form as BaseForm;
8
use SilverStripe\Forms\FormAction;
9
use SilverStripe\Forms\FieldGroup;
10
use SilverStripe\Forms\FieldList;
11
use SilverStripe\Forms\HeaderField;
12
use SilverStripe\Forms\HiddenField;
13
use SilverStripe\Forms\LabelField;
14
use SilverStripe\ORM\ValidationException;
15
16
/**
17
 * Basic confirmation form implementation.
18
 *
19
 * Renders the list of confirmation items on the screen
20
 * and reconciles those with the confirmation storage.
21
 *
22
 * If the user confirms the action, marks the storage as confirmed
23
 * and redirects to the success url (kept in the storage).
24
 *
25
 * If the user declines the action, cleans up the storage and
26
 * redirects to the failure url (kept in the storage).
27
 */
28
class Form extends BaseForm
29
{
30
    /**
31
     * Confirmation storage instance
32
     *
33
     * @var Storage
34
     */
35
    private $storage;
36
37
    /**
38
     * @param string $storageId confirmation storage identifier
39
     * @param RequestHandler $controller active request handler
40
     * @param string $formConstructor form constructor name
41
     */
42
    public function __construct($storageId, RequestHandler $controller, $formConstructor)
43
    {
44
        $request = $controller->getRequest();
45
        $storage = Injector::inst()->createWithArgs(Storage::class, [$request->getSession(), $storageId, false]);
46
47
        if (count($storage->getItems())) {
48
            $fieldList = $this->buildFieldList($storage);
49
            $actionList = $this->buildActionList($storage);
50
        } else {
51
            $fieldList = $this->buildEmptyFieldList();
52
            $actionList = null;
53
        }
54
55
        parent::__construct($controller, $formConstructor, $fieldList, $actionList);
56
57
        if ($storage->getHttpMethod() !== 'POST') {
58
            $this->enableSecurityToken();
59
        }
60
61
        $this->storage = $storage;
62
    }
63
64
    /**
65
     * The form refusal handler. Cleans up the confirmation storage
66
     * and returns the failure redirection (kept in the storage)
67
     *
68
     * @return HTTPResponse redirect
0 ignored issues
show
Bug introduced by
The type SilverStripe\Security\Confirmation\HTTPResponse was not found. Did you mean HTTPResponse? If so, make sure to prefix the type with \.
Loading history...
69
     */
70
    public function doRefuse()
71
    {
72
        $url = $this->storage->getFailureUrl();
73
        $this->storage->cleanup();
74
        return $this->controller->redirect($url);
75
    }
76
77
    /**
78
     * The form confirmation handler. Checks all the items in the storage
79
     * has been confirmed and marks them as such. Returns a redirect
80
     * when all the storage items has been verified and marked as confirmed.
81
     *
82
     * @return HTTPResponse success url
83
     *
84
     * @throws ValidationException when the confirmation storage has an item missing on the form
85
     */
86
    public function doConfirm()
87
    {
88
        $storage = $this->storage;
89
        $data = $this->getData();
90
91
        if (!$storage->confirm($data)) {
92
            throw new ValidationException('Sorry, we could not verify the parameters');
93
        }
94
95
        $url = $storage->getSuccessUrl();
96
97
        return $this->controller->redirect($url);
98
    }
99
100
    protected function buildActionList(Storage $storage)
101
    {
102
        $cancel = FormAction::create('doRefuse', _t(__CLASS__.'.REFUSE', 'Cancel'));
103
        $confirm = FormAction::create('doConfirm', _t(__CLASS__.'.CONFIRM', 'Run the action'))->setAutofocus(true);
104
105
        if ($storage->getHttpMethod() === 'POST') {
106
            $confirm->setAttribute('formaction', htmlspecialchars($storage->getSuccessUrl()));
107
        }
108
109
        return FieldList::create($cancel, $confirm);
110
    }
111
112
    /**
113
     * Builds the form fields taking the confirmation items from the storage
114
     *
115
     * @param Storage $storage Confirmation storage instance
116
     *
117
     * @return FieldList
118
     */
119
    protected function buildFieldList(Storage $storage)
120
    {
121
        $fields = [];
122
123
        foreach ($storage->getItems() as $item) {
124
            $group = [];
125
126
            $group[] = HeaderField::create(null, $item->getName());
127
128
            if ($item->getDescription()) {
129
                $group[] = LabelField::create($item->getDescription());
130
            }
131
132
            $fields[] = FieldGroup::create(...$group);
133
        }
134
135
        foreach ($storage->getHashedItems() as $key => $val) {
136
            $fields[] = HiddenField::create($key, null, $val);
137
        }
138
139
        if ($storage->getHttpMethod() === 'POST') {
140
            // add the storage CSRF token
141
            $fields[] = HiddenField::create($storage->getCsrfToken(), null, '1');
142
143
            // replicate the original POST request parameters
144
            // so that the new confirmed POST request has those
145
            $data = $storage->getSuccessPostVars();
146
147
            if (is_null($data)) {
148
                throw new ValidationException('Sorry, your cookies seem to have expired. Try to repeat the initial action.');
149
            }
150
151
            foreach ($data as $key => $value) {
152
                $fields[] = HiddenField::create($key, null, $value);
153
            }
154
        }
155
156
        return FieldList::create(...$fields);
157
    }
158
159
    /**
160
     * Builds the fields showing the form is empty and there's nothing
161
     * to confirm
162
     *
163
     * @return FieldList
164
     */
165
    protected function buildEmptyFieldList()
166
    {
167
        return FieldList::create(
168
            HeaderField::create(null, _t(__CLASS__.'.EMPTY_TITLE', 'Nothing to confirm'))
169
        );
170
    }
171
}
172