Completed
Pull Request — master (#12)
by Simon
09:20
created

PartialFormSubmission::getParent()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 0
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 2
1
<?php
2
3
namespace Firesphere\PartialUserforms\Models;
4
5
use Exception;
6
use Firesphere\PartialUserforms\Controllers\PartialUserFormVerifyController;
7
use SilverStripe\Control\Controller;
8
use SilverStripe\Control\Director;
9
use SilverStripe\Forms\FieldList;
10
use SilverStripe\Forms\GridField\GridField;
11
use SilverStripe\Forms\GridField\GridFieldButtonRow;
12
use SilverStripe\Forms\GridField\GridFieldConfig;
13
use SilverStripe\Forms\GridField\GridFieldDataColumns;
14
use SilverStripe\Forms\GridField\GridFieldExportButton;
15
use SilverStripe\Forms\GridField\GridFieldPrintButton;
16
use SilverStripe\Forms\ReadonlyField;
17
use SilverStripe\ORM\ArrayList;
18
use SilverStripe\ORM\DataList;
19
use SilverStripe\ORM\DataObject;
20
use SilverStripe\Security\Member;
21
use SilverStripe\Security\RandomGenerator;
22
use SilverStripe\UserForms\Model\Submission\SubmittedForm;
23
use SilverStripe\UserForms\Model\UserDefinedForm;
24
25
/**
26
 * Class \Firesphere\PartialUserforms\Models\PartialFormSubmission
27
 *
28
 * @property boolean $IsSend
29
 * @property string $TokenSalt
30
 * @property string $Token
31
 * @property string $Password
32
 * @property int $UserDefinedFormID
33
 * @method DataObject UserDefinedForm()
34
 * @method DataList|PartialFieldSubmission[] PartialFields()
35
 * @method DataList|PartialFileFieldSubmission[] PartialUploads()
36
 */
37
class PartialFormSubmission extends SubmittedForm
38
{
39
40
    /**
41
     * @var string
42
     */
43
    private static $table_name = 'PartialFormSubmission';
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...
44
45
    /**
46
     * @var array
47
     */
48
    private static $db = [
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...
49
        'IsSend'    => 'Boolean(false)',
50
        'TokenSalt' => 'Varchar(16)',
51
        'Token'     => 'Varchar(16)',
52
        'Password'  => 'Varchar(64)',
53
    ];
54
55
    /**
56
     * @var array
57
     */
58
    private static $has_one = [
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...
59
        'UserDefinedForm' => DataObject::class
60
    ];
61
62
    /**
63
     * @var array
64
     */
65
    private static $has_many = [
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...
66
        'PartialFields'  => PartialFieldSubmission::class,
67 1
        'PartialUploads' => PartialFileFieldSubmission::class
68
    ];
69
70 1
    private static $cascade_deletes = [
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...
71 1
        'PartialFields'
72 1
    ];
73
74
    /**
75
     * @var array
76
     */
77
    private static $summary_fields = [
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...
78
        'ID'          => 'ID',
79
        'PartialLink' => 'Link',
80
        'Created'     => 'Created',
81
        'LastEdited'  => 'Last Edited',
82 1
    ];
83 1
84 1
    private static $special_characters = [
85 1
        '!',
86 1
        '@',
87
        '#',
88 1
        '$',
89 1
        '%',
90 1
        '^',
91 1
        '&',
92
        '*',
93
        '(',
94
        ')',
95 1
        '_',
96
        '-',
97
        '=',
98
        '+',
99 1
        ';',
100 1
        ':',
101 1
        ',',
102 1
        '.',
103 1
        '?'
104 1
    ];
105
106 1
    /**
107 1
     * @return FieldList
108
     * @throws Exception
109 1
     */
110 1
    public function getCMSFields()
111
    {
112
        /** @var FieldList $fields */
113
        $fields = parent::getCMSFields();
114 1
        $fields->removeByName([
115
            'Values',
116
            'IsSend',
117 43
            'PartialFields',
118
            'TokenSalt',
119 43
            'Token',
120
            'UserDefinedFormID',
121
            'Submitter',
122
            'PartialUploads'
123
        ]);
124
125
        $partialFields = $this->PartialFields();
126
        $fileFields = $this->PartialUploads();
127 1
        $list = ArrayList::create();
128
        $list->merge($partialFields);
129 1
        $list->merge($fileFields);
130
131
        $partialFields = GridField::create(
132
            'PartialFields',
133
            _t(static::class . '.PARTIALFIELDS', 'Partial fields'),
134
            $list->sort('Created', 'ASC')
135
        );
136
137 40
        $exportColumns = [
138
            'Title'       => 'Title',
139 40
            'ExportValue' => 'Value'
140 37
        ];
141
142
        $config = new GridFieldConfig();
143 19
        $config->addComponent(new GridFieldDataColumns());
144
        $config->addComponent(new GridFieldButtonRow('after'));
145
        $config->addComponent(new GridFieldExportButton('buttons-after-left', $exportColumns));
146
        $config->addComponent(new GridFieldPrintButton('buttons-after-left'));
147
        $partialFields->setConfig($config);
148
149
        $fields->addFieldsToTab(
150
            'Root.Main',
151 3
            [
152
                ReadonlyField::create('ReadonlyPartialLink', 'Partial Link', $this->getPartialLink()),
153 3
                $partialFields
154 3
            ]
155
        );
156
157
        return $fields;
158
    }
159
160
    /**
161
     * Get the share link of the form
162
     *
163
     * @return string
164
     * @throws Exception
165 3
     */
166
    public function getPartialLink()
167 3
    {
168 3
        if (!$this->isInDB()) {
169
            return '(none)';
170
        }
171
172
        $token = $this->Token;
173
174
        return Controller::join_links(
175
            Director::absoluteBaseURL(),
176
            'partial',
177
            $this->generateKey($token),
0 ignored issues
show
Bug introduced by
It seems like $token defined by $this->Token on line 172 can also be of type boolean; however, Firesphere\PartialUserfo...bmission::generateKey() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
178
            $token
179
        );
180 2
    }
181
182 2
    /**
183 1
     * Generate a key based on the share token salt
184
     *
185
     * @param string $token
186 2
     * @return mixed
187
     */
188 2
    public function generateKey($token)
189 2
    {
190 2
        return hash_pbkdf2('sha256', $token, $this->TokenSalt, 1000, 16);
191 2
    }
192 2
193
    /**
194
     * Generate the partial tokens
195
     * If the submission is password protected, generate a password.
196
     * @throws Exception
197
     */
198
    public function onBeforeWrite()
199
    {
200
        parent::onBeforeWrite();
201
        $this->getPartialToken();
202 3
        if (!$this->Password) {
203
            $this->Password = $this->generatePassword();
204 3
        }
205 3
    }
206 3
207 3
    /**
208
     * Get the unique token for the share link
209
     *
210 3
     * @return bool|string|null
211
     * @throws Exception
212
     */
213
    protected function getPartialToken()
214
    {
215
        if (!$this->TokenSalt) {
216
            $this->TokenSalt = $this->generateToken();
217
            $this->Token = $this->generateToken();
218
        }
219 4
220
        return $this->Token;
221 4
    }
222
223 4
    /**
224
     * Generate a new token
225
     *
226
     * @return bool|string
227
     * @throws Exception
228
     */
229
    protected function generateToken()
230
    {
231
        $generator = new RandomGenerator();
232 4
233
        return substr($generator->randomToken('sha256'), 0, 16);
234 4
    }
235
236
    /**
237
     * @return string
238
     * @throws Exception
239
     */
240
    protected function generatePassword()
241
    {
242
        $chars = range('A', 'Z');
243
        $chars = array_merge($chars, range('a', 'z'));
244
        $chars = array_merge($chars, range(0, 9));
245
        $chars = array_merge($chars, self::$special_characters);
246
        shuffle($chars);
247
        $pwd = implode(array_slice($chars, 0, 10));
248
        Controller::curr()->getRequest()->getSession()->set(PartialUserFormVerifyController::PASSWORD_KEY, $pwd);
249
250
        return hash_pbkdf2('SHA256', $pwd, $this->TokenSalt, 1000);
251
    }
252
253
    /**
254
     * @return DataObject|UserDefinedForm
255
     */
256
    public function getParent()
257
    {
258
        return $this->UserDefinedForm();
259
    }
260
261
    /**
262
     * @param Member
263
     *
264
     * @return bool|string|null
265
     * @throws Exception
266
     */
267
    public function canCreate($member = null, $context = [])
268
    {
269
        return false;
270
    }
271
272
    /**
273
     * @param Member $member
274
     *
275
     * @return boolean|string
276
     */
277
    public function canView($member = null)
278
    {
279
        if ($this->UserDefinedForm()) {
280
            return $this->UserDefinedForm()->canView($member);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->UserDefinedForm()->canView($member); of type boolean|string adds the type string to the return on line 280 which is incompatible with the return type of the parent method SilverStripe\UserForms\M...\SubmittedForm::canView of type boolean.
Loading history...
281
        }
282
283
        return parent::canView($member);
284
    }
285
286
    /**
287
     * @param Member $member
288
     *
289
     * @throws Exception
290
     * @return boolean|string
291
     */
292
    public function canEdit($member = null)
293
    {
294
        if ($this->UserDefinedForm()) {
295
            return $this->UserDefinedForm()->canEdit($member);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->UserDefinedForm()->canEdit($member); of type boolean|string adds the type string to the return on line 295 which is incompatible with the return type of the parent method SilverStripe\UserForms\M...\SubmittedForm::canEdit of type boolean.
Loading history...
296
        }
297
298
        return parent::canEdit($member);
299
    }
300
301
    /**
302
     * @param Member $member
303
     *
304
     * @return boolean|string
305
     */
306
    public function canDelete($member = null)
307
    {
308
        if ($this->UserDefinedForm()) {
309
            return $this->UserDefinedForm()->canDelete($member);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->UserDefinedForm()->canDelete($member); of type boolean|string adds the type string to the return on line 309 which is incompatible with the return type of the parent method SilverStripe\UserForms\M...ubmittedForm::canDelete of type boolean.
Loading history...
310
        }
311
312
        return parent::canDelete($member);
313
    }
314
}
315