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

PartialFormSubmission   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 270
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 15

Importance

Changes 0
Metric Value
wmc 18
lcom 2
cbo 15
dl 0
loc 270
rs 10
c 0
b 0
f 0

12 Methods

Rating   Name   Duplication   Size   Complexity  
A getPartialLink() 0 15 2
A getCMSFields() 0 45 1
A generateKey() 0 4 1
A onBeforeWrite() 0 8 2
A getPartialToken() 0 9 2
A generateToken() 0 6 1
A generatePassword() 0 12 1
A getParent() 0 4 1
A canCreate() 0 4 1
A canView() 0 8 2
A canEdit() 0 8 2
A canDelete() 0 8 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\DataList;
18
use SilverStripe\ORM\DataObject;
19
use SilverStripe\Security\Member;
20
use SilverStripe\Security\RandomGenerator;
21
use SilverStripe\UserForms\Model\Submission\SubmittedForm;
22
use SilverStripe\UserForms\Model\UserDefinedForm;
23
24
/**
25
 * Class \Firesphere\PartialUserforms\Models\PartialFormSubmission
26
 *
27
 * @property boolean $IsSend
28
 * @property string $TokenSalt
29
 * @property string $Token
30
 * @property string $Password
31
 * @property int $UserDefinedFormID
32
 * @method DataObject UserDefinedForm()
33
 * @method DataList|PartialFieldSubmission[] PartialFields()
34
 */
35
class PartialFormSubmission extends SubmittedForm
36
{
37
38
    /**
39
     * @var string
40
     */
41
    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...
42
43
    /**
44
     * @var array
45
     */
46
    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...
47
        'IsSend'    => 'Boolean(false)',
48
        'TokenSalt' => 'Varchar(16)',
49
        'Token'     => 'Varchar(16)',
50
        'Password'  => 'Varchar(64)',
51
    ];
52
53
    /**
54
     * @var array
55
     */
56
    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...
57
        'UserDefinedForm' => DataObject::class
58
    ];
59
60
    /**
61
     * @var array
62
     */
63
    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...
64
        'PartialFields' => PartialFieldSubmission::class
65
    ];
66
67
    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...
68
        'PartialFields'
69
    ];
70
71
    /**
72
     * @var array
73
     */
74
    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...
75
        'ID'          => 'ID',
76
        'PartialLink' => 'Link',
77
        'Created'     => 'Created',
78
        'LastEdited'  => 'Last Edited',
79
    ];
80
81
    private static $special_characters = [
82
        '!',
83
        '@',
84
        '#',
85
        '$',
86
        '%',
87
        '^',
88
        '&',
89
        '*',
90
        '(',
91
        ')',
92
        '_',
93
        '-',
94
        '=',
95
        '+',
96
        ';',
97
        ':',
98
        ',',
99
        '.',
100
        '?'
101
    ];
102
103
    /**
104
     * @return FieldList
105
     * @throws Exception
106
     */
107
    public function getCMSFields()
108
    {
109
        /** @var FieldList $fields */
110
        $fields = parent::getCMSFields();
111
        $fields->removeByName(
112
            [
113
                'Values',
114
                'IsSend',
115
                'PartialFields',
116
                'TokenSalt',
117
                'Token',
118
                'UserDefinedFormID',
119
                'Submitter',
120
                'Password'
121
            ]
122
        );
123
124
        $partialFields = GridField::create(
125
            'PartialFields',
126
            _t(static::class . '.PARTIALFIELDS', 'Partial fields'),
127
            $this->PartialFields()->sort('Created', 'ASC')
128
        );
129
130
        $exportColumns = [
131
            'Title'       => 'Title',
132
            'ExportValue' => 'Value'
133
        ];
134
135
        $config = new GridFieldConfig();
136
        $config->addComponent(new GridFieldDataColumns());
137
        $config->addComponent(new GridFieldButtonRow('after'));
138
        $config->addComponent(new GridFieldExportButton('buttons-after-left', $exportColumns));
139
        $config->addComponent(new GridFieldPrintButton('buttons-after-left'));
140
        $partialFields->setConfig($config);
141
142
        $fields->addFieldsToTab(
143
            'Root.Main',
144
            [
145
                ReadonlyField::create('ReadonlyPartialLink', 'Partial Link', $this->getPartialLink()),
146
                $partialFields
147
            ]
148
        );
149
150
        return $fields;
151
    }
152
153
    /**
154
     * Get the share link of the form
155
     *
156
     * @return string
157
     * @throws Exception
158
     */
159
    public function getPartialLink()
160
    {
161
        if (!$this->isInDB()) {
162
            return '(none)';
163
        }
164
165
        $token = $this->Token;
166
167
        return Controller::join_links(
168
            Director::absoluteBaseURL(),
169
            'partial',
170
            $this->generateKey($token),
0 ignored issues
show
Bug introduced by
It seems like $token defined by $this->Token on line 165 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...
171
            $token
172
        );
173
    }
174
175
    /**
176
     * Generate a key based on the share token salt
177
     *
178
     * @param string $token
179
     * @return mixed
180
     */
181
    public function generateKey($token)
182
    {
183
        return hash_pbkdf2('sha256', $token, $this->TokenSalt, 1000, 16);
184
    }
185
186
    /**
187
     * Generate the partial tokens
188
     * If the submission is password protected, generate a password.
189
     * @throws Exception
190
     */
191
    public function onBeforeWrite()
192
    {
193
        parent::onBeforeWrite();
194
        $this->getPartialToken();
195
        if (!$this->Password) {
196
            $this->Password = $this->generatePassword();
197
        }
198
    }
199
200
    /**
201
     * Get the unique token for the share link
202
     *
203
     * @return bool|string|null
204
     * @throws Exception
205
     */
206
    protected function getPartialToken()
207
    {
208
        if (!$this->TokenSalt) {
209
            $this->TokenSalt = $this->generateToken();
210
            $this->Token = $this->generateToken();
211
        }
212
213
        return $this->Token;
214
    }
215
216
    /**
217
     * Generate a new token
218
     *
219
     * @return bool|string
220
     * @throws Exception
221
     */
222
    protected function generateToken()
223
    {
224
        $generator = new RandomGenerator();
225
226
        return substr($generator->randomToken('sha256'), 0, 16);
227
    }
228
229
    /**
230
     * @return string
231
     */
232
    protected function generatePassword()
233
    {
234
        $chars = range('A', 'Z');
235
        $chars = array_merge($chars, range('a', 'z'));
236
        $chars = array_merge($chars, range(0, 9));
237
        $chars = array_merge($chars, self::$special_characters);
238
        shuffle($chars);
239
        $pwd = implode(array_slice($chars, 0, 10));
240
        Controller::curr()->getRequest()->getSession()->set(PartialUserFormVerifyController::PASSWORD_KEY, $pwd);
241
242
        return hash_pbkdf2('SHA256', $pwd, $this->TokenSalt, 1000);
243
    }
244
245
    /**
246
     * @return DataObject|UserDefinedForm
247
     */
248
    public function getParent()
249
    {
250
        return $this->UserDefinedForm();
251
    }
252
253
    /**
254
     * @param Member
255
     *
256
     * @return boolean|string
257
     */
258
    public function canCreate($member = null, $context = [])
259
    {
260
        return false;
261
    }
262
263
    /**
264
     * @param Member $member
265
     *
266
     * @return boolean|string
267
     */
268
    public function canView($member = null)
269
    {
270
        if ($this->UserDefinedForm()) {
271
            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 271 which is incompatible with the return type of the parent method SilverStripe\UserForms\M...\SubmittedForm::canView of type boolean.
Loading history...
272
        }
273
274
        return parent::canView($member);
275
    }
276
277
    /**
278
     * @param Member $member
279
     *
280
     * @return boolean|string
281
     */
282
    public function canEdit($member = null)
283
    {
284
        if ($this->UserDefinedForm()) {
285
            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 285 which is incompatible with the return type of the parent method SilverStripe\UserForms\M...\SubmittedForm::canEdit of type boolean.
Loading history...
286
        }
287
288
        return parent::canEdit($member);
289
    }
290
291
    /**
292
     * @param Member $member
293
     *
294
     * @return boolean|string
295
     */
296
    public function canDelete($member = null)
297
    {
298
        if ($this->UserDefinedForm()) {
299
            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 299 which is incompatible with the return type of the parent method SilverStripe\UserForms\M...ubmittedForm::canDelete of type boolean.
Loading history...
300
        }
301
302
        return parent::canDelete($member);
303
    }
304
}
305