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

PartialFormSubmission::generatePassword()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

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