Completed
Pull Request — master (#12)
by Simon
01:19
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
     * @throws \Exception
100
     */
101
    public function getCMSFields()
102
    {
103
        /** @var FieldList $fields */
104
        $fields = parent::getCMSFields();
105
        $fields->removeByName(
106
            [
107
                'Values',
108
                'IsSend',
109
                'PartialFields',
110
                'TokenSalt',
111
                'Token',
112
                'UserDefinedFormID',
113
                'Submitter',
114
                'Password'
115
            ]
116
        );
117
118
        $partialFields = GridField::create(
119
            'PartialFields',
120
            _t(static::class . '.PARTIALFIELDS', 'Partial fields'),
121
            $this->PartialFields()->sort('Created', 'ASC')
122
        );
123
124
        $exportColumns = [
125
            'Title'       => 'Title',
126
            'ExportValue' => 'Value'
127
        ];
128
129
        $config = new GridFieldConfig();
130
        $config->addComponent(new GridFieldDataColumns());
131
        $config->addComponent(new GridFieldButtonRow('after'));
132
        $config->addComponent(new GridFieldExportButton('buttons-after-left', $exportColumns));
133
        $config->addComponent(new GridFieldPrintButton('buttons-after-left'));
134
        $partialFields->setConfig($config);
135
136
        $fields->addFieldsToTab(
137
            'Root.Main',
138
            [
139
                ReadonlyField::create('ReadonlyPartialLink', 'Partial Link', $this->getPartialLink()),
140
                $partialFields
141
            ]
142
        );
143
144
        return $fields;
145
    }
146
147
    /**
148
     * @return DataObject|UserDefinedForm
149
     */
150
    public function getParent()
151
    {
152
        return $this->UserDefinedForm();
153
    }
154
155
    /**
156
     * Generate the partial tokens
157
     * If the submission is password protected, generate a password.
158
     * @throws \Exception
159
     */
160
    public function onBeforeWrite()
161
    {
162
        parent::onBeforeWrite();
163
        $this->getPartialToken();
164
        $this->generatePassword();
165
    }
166
167
    /**
168
     * @param Member
169
     *
170
     * @return boolean|string
171
     */
172
    public function canCreate($member = null, $context = [])
173
    {
174
        return false;
175
    }
176
177
    /**
178
     * @param Member
179
     *
180
     * @return boolean|string
181
     */
182
    public function canView($member = null)
183
    {
184
        if ($this->UserDefinedForm()) {
185
            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 185 which is incompatible with the return type of the parent method SilverStripe\UserForms\M...\SubmittedForm::canView of type boolean.
Loading history...
186
        }
187
188
        return parent::canView($member);
189
    }
190
191
    /**
192
     * @param Member
193
     *
194
     * @return boolean|string
195
     */
196
    public function canEdit($member = null)
197
    {
198
        if ($this->UserDefinedForm()) {
199
            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 199 which is incompatible with the return type of the parent method SilverStripe\UserForms\M...\SubmittedForm::canEdit of type boolean.
Loading history...
200
        }
201
202
        return parent::canEdit($member);
203
    }
204
205
    /**
206
     * @param Member
207
     *
208
     * @return boolean|string
209
     */
210
    public function canDelete($member = null)
211
    {
212
        if ($this->UserDefinedForm()) {
213
            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 213 which is incompatible with the return type of the parent method SilverStripe\UserForms\M...ubmittedForm::canDelete of type boolean.
Loading history...
214
        }
215
216
        return parent::canDelete($member);
217
    }
218
219
    /**
220
     * Get the share link of the form
221
     *
222
     * @return string
223
     * @throws \Exception
224
     */
225
    public function getPartialLink()
226
    {
227
        if (!$this->isInDB()) {
228
            return '(none)';
229
        }
230
231
        $token = $this->Token;
232
233
        return Controller::join_links(
234
            Director::absoluteBaseURL(),
235
            'partial',
236
            $this->generateKey($token),
0 ignored issues
show
Bug introduced by
It seems like $token defined by $this->Token on line 231 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...
237
            $token
238
        );
239
    }
240
241
    /**
242
     * Get the unique token for the share link
243
     *
244
     * @return bool|string|null
245
     * @throws \Exception
246
     */
247
    protected function getPartialToken()
248
    {
249
        if (!$this->TokenSalt) {
250
            $this->TokenSalt = $this->generateToken();
251
            $this->Token = $this->generateToken();
252
        }
253
254
        return $this->Token;
255
    }
256
257
    /**
258
     * Generate a new token
259
     *
260
     * @return bool|string
261
     * @throws \Exception
262
     */
263
    protected function generateToken()
264
    {
265
        $generator = new RandomGenerator();
266
267
        return substr($generator->randomToken('sha256'), 0, 16);
268
    }
269
270
    /**
271
     * Generate a key based on the share token salt
272
     *
273
     * @param string $token
274
     * @return mixed
275
     */
276
    public function generateKey($token)
277
    {
278
        return hash_pbkdf2('sha256', $token, $this->TokenSalt, 1000, 16);
279
    }
280
281
    /**
282
     * @return void
283
     */
284
    protected function generatePassword()
285
    {
286
        if (!$this->Password &&
287
            $this->getParent() &&
288
            $this->getParent()->PasswordProtected
289
        ) {
290
            $chars = range('A', 'Z');
291
            $chars = array_merge($chars, range('a', 'z'));
292
            $chars = array_merge($chars, range(0, 9));
293
            $chars = array_merge($chars, self::$special_characters);
294
            shuffle($chars);
295
            $pwd = implode(array_slice($chars, 0, 10));
296
            $this->Password = hash_pbkdf2('SHA256', $pwd, $this->TokenSalt, 1000);
297
            Controller::curr()->getRequest()->getSession()->set('FormPassword', $pwd);
298
        }
299
    }
300
}
301