Completed
Pull Request — master (#12)
by Simon
02:17 queued 01:03
created

PartialFormSubmission::onBeforeWrite()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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