Completed
Pull Request — master (#16)
by Simon
01:18
created

PartialUserFormController::partial()   B

Complexity

Conditions 7
Paths 4

Size

Total Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 41
rs 8.3306
c 0
b 0
f 0
cc 7
nc 4
nop 1
1
<?php
2
3
namespace Firesphere\PartialUserforms\Controllers;
4
5
use Exception;
6
use Firesphere\PartialUserforms\Models\PartialFieldSubmission;
7
use Firesphere\PartialUserforms\Models\PartialFileFieldSubmission;
8
use Firesphere\PartialUserforms\Models\PartialFormSubmission;
9
use SilverStripe\Assets\File;
10
use SilverStripe\Assets\Upload;
11
use SilverStripe\CMS\Controllers\ContentController;
12
use SilverStripe\Control\HTTPRequest;
13
use SilverStripe\Control\HTTPResponse;
14
use SilverStripe\Control\HTTPResponse_Exception;
15
use SilverStripe\ORM\DataObject;
16
use SilverStripe\ORM\FieldType\DBHTMLText;
17
use SilverStripe\ORM\ValidationException;
18
use SilverStripe\UserForms\Control\UserDefinedFormController;
19
use SilverStripe\UserForms\Model\EditableFormField;
20
use SilverStripe\UserForms\Model\UserDefinedForm;
21
use SilverStripe\View\Requirements;
22
23
/**
24
 * Class PartialUserFormController
25
 *
26
 * @package Firesphere\PartialUserforms\Controllers
27
 */
28
class PartialUserFormController extends ContentController
29
{
30
    /**
31
     * Session key name
32
     */
33
    public const SESSION_KEY = 'PartialSubmissionID';
34
35
    /**
36
     * @var array
37
     */
38
    private static $url_handlers = [
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
39
        'save'        => 'savePartialSubmission',
40
        '$Key/$Token' => 'partial',
41
    ];
42
43
    /**
44
     * @var array
45
     */
46
    private static $allowed_actions = [
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
47
        'savePartialSubmission',
48
        'partial',
49
    ];
50
51
    /**
52
     * @param HTTPRequest $request
53
     * @return int|mixed|void
54
     * @throws ValidationException
55
     * @throws HTTPResponse_Exception
56
     */
57
    public function savePartialSubmission(HTTPRequest $request)
58
    {
59
        if (!$request->isPOST()) {
60
            return $this->httpError(404);
61
        }
62
63
        $postVars = $request->postVars();
64
        $editableField = null;
65
66
        // We don't want SecurityID and/or the process Action stored as a thing
67
        unset($postVars['SecurityID'], $postVars['action_process']);
68
        $submissionID = $request->getSession()->get(self::SESSION_KEY);
69
70
        /** @var PartialFormSubmission $partialSubmission */
71
        $partialSubmission = PartialFormSubmission::get()->byID($submissionID);
72
73
        if (!$submissionID || !$partialSubmission) {
74
            $partialSubmission = PartialFormSubmission::create();
75
            $submissionID = $partialSubmission->write();
76
        }
77
        $request->getSession()->set(self::SESSION_KEY, $submissionID);
78
        foreach ($postVars as $field => $value) {
79
            /** @var EditableFormField $editableField */
80
            $editableField = $this->createOrUpdateSubmission([
81
                'Name'            => $field,
82
                'Value'           => $value,
83
                'SubmittedFormID' => $submissionID
84
            ]);
85
        }
86
87
        // @todo this is an ugly hack to get the ID and ClassName out of the Fields.
88
        if ($editableField instanceof EditableFormField && !$partialSubmission->UserDefinedFormID) {
89
            $partialSubmission->update([
90
                'UserDefinedFormID'    => $editableField->Parent()->ID,
91
                'ParentID'             => $editableField->Parent()->ID,
92
                'ParentClass'          => $editableField->Parent()->ClassName,
93
                'UserDefinedFormClass' => $editableField->Parent()->ClassName
94
            ]);
95
            $partialSubmission->write();
96
        }
97
98
        return new HTTPResponse($submissionID, 201);
99
    }
100
101
    /**
102
     * @param $formData
103
     * @return DataObject|EditableFormField
104
     * @throws ValidationException
105
     */
106
    protected function createOrUpdateSubmission($formData)
107
    {
108
        $filter = [
109
            'Name'            => $formData['Name'],
110
            'SubmittedFormID' => $formData['SubmittedFormID'],
111
        ];
112
113
        /** @var EditableFormField $editableField */
114
        $editableField = EditableFormField::get()->filter(['Name' => $formData['Name']])->first();
115
        if ($editableField instanceof EditableFormField\EditableFileField) {
116
            $this->savePartialFile($formData, $filter, $editableField);
117
        } elseif ($editableField instanceof EditableFormField) {
118
            $this->savePartialField($formData, $filter, $editableField);
119
        }
120
121
        // Return the ParentID to link the PartialSubmission to it's proper thingy
122
        return $editableField;
123
    }
124
125
    /**
126
     * @param $formData
127
     * @param array $filter
128
     * @param EditableFormField\EditableFileField $editableField
129
     * @throws ValidationException
130
     * @throws Exception
131
     */
132
    protected function savePartialFile($formData, array $filter, EditableFormField\EditableFileField $editableField)
133
    {
134
        $partialFileSubmission = PartialFileFieldSubmission::get()->filter($filter)->first();
135
        $partialData = [];
136
        if (!$partialFileSubmission && $editableField) {
137
            $partialData['Title'] = $editableField->Title;
138
            $partialData['ParentClass'] = $editableField->Parent()->ClassName;
139
        }
140
        // Don't overwrite existing uploads
141
        if (!$partialFileSubmission ||
142
            (!$partialFileSubmission->UploadedFileID && is_array($formData['Value']))
143
        ) {
144
            $file = $this->uploadFile($partialData, $editableField);
145
            $formData['UploadedFileID'] = $file->ID ?? 0;
146
            $partialFileSubmission = PartialFileFieldSubmission::create($partialData);
147
        }
148
        $partialFileSubmission->write();
149
    }
150
151
    /**
152
     * @param array $formData
153
     * @param EditableFormField\EditableFileField $field
154
     * @return bool|File
155
     * @throws Exception
156
     */
157
    protected function uploadFile($formData, $field)
158
    {
159
        if (!empty($formData['Value']['name'])) {
160
            $foldername = $field->getFormField()->getFolderName();
161
162
            // create the file from post data
163
            $upload = Upload::create();
164
            $file = File::create();
165
            $file->ShowInSearch = 0;
166
            if ($upload->loadIntoFile($formData['Value'], $file, $foldername)) {
167
                return $file;
168
            }
169
        }
170
171
        return false;
172
    }
173
174
    /**
175
     * @param $formData
176
     * @param array $filter
177
     * @param EditableFormField $editableField
178
     * @throws ValidationException
179
     */
180
    protected function savePartialField($formData, array $filter, EditableFormField $editableField)
181
    {
182
        $exists = PartialFieldSubmission::get()->filter($filter)->first();
183
        if (is_array($formData['Value'])) {
184
            $formData['Value'] = implode(', ', $formData['Value']);
185
        }
186
        if ($editableField) {
187
            $formData['Title'] = $editableField->Title;
188
            $formData['ParentClass'] = $editableField->Parent()->ClassName;
189
        }
190
        if (!$exists) {
191
            $exists = PartialFieldSubmission::create($formData);
192
        } else {
193
            $exists->update($formData);
194
        }
195
        $exists->write();
196
    }
197
198
    /**
199
     * Partial form
200
     *
201
     * @param HTTPRequest $request
202
     * @return DBHTMLText|void
203
     * @throws HTTPResponse_Exception
204
     * @throws Exception
205
     */
206
    public function partial(HTTPRequest $request)
207
    {
208
        $key = $request->param('Key');
209
        $token = $request->param('Token');
210
        if (!$key || !$token) {
211
            return $this->httpError(404);
212
        }
213
214
        /** @var PartialFormSubmission $partial */
215
        $partial = PartialFormSubmission::get()->find('Token', $token);
216
        if (!$partial ||
217
            !$partial->UserDefinedFormID ||
218
            !hash_equals($partial->generateKey($token), $key)
219
        ) {
220
            return $this->httpError(404);
221
        }
222
223
        // Set the session if the last session has expired
224
        if (!$request->getSession()->get(self::SESSION_KEY)) {
225
            $request->getSession()->set(self::SESSION_KEY, $partial->ID);
226
        }
227
228
        // TODO: Recognize visitor with the password
229
        // TODO: Populate form values
230
231
        $class = $partial->UserDefinedFormClass;
0 ignored issues
show
Documentation introduced by
The property UserDefinedFormClass does not exist on object<Firesphere\Partia...\PartialFormSubmission>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
232
        /** @var UserDefinedForm $record */
233
        $record = $class::get()->byID($partial->UserDefinedFormID);
234
        $controller = UserDefinedFormController::create($record);
235
        $controller->init();
236
237
        Requirements::javascript('firesphere/partialuserforms:client/dist/main.js');
238
239
        return $this->customise([
240
            'Title'       => $record->Title,
241
            'Breadcrumbs' => $record->Breadcrumbs(),
242
            'Content'     => $this->obj('Content'),
243
            'Form'        => $controller->Form(),
244
            'Link'        => $partial->getPartialLink()
245
        ])->renderWith(['PartialUserForm', 'Page']);
246
    }
247
}
248