Completed
Pull Request — master (#12)
by Simon
08:01 queued 06:43
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
        return false;
174
    }
175
176
    /**
177
     * @param Member
178
     *
179
     * @return boolean|string
180
     */
181
    public function canView($member = null)
182
    {
183
        if ($this->UserDefinedForm()) {
184
            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 184 which is incompatible with the return type of the parent method SilverStripe\UserForms\M...\SubmittedForm::canView of type boolean.
Loading history...
185
        }
186
187
        return parent::canView($member);
188
    }
189
190
    /**
191
     * @param Member
192
     *
193
     * @return boolean|string
194
     */
195
    public function canEdit($member = null)
196
    {
197
        if ($this->UserDefinedForm()) {
198
            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 198 which is incompatible with the return type of the parent method SilverStripe\UserForms\M...\SubmittedForm::canEdit of type boolean.
Loading history...
199
        }
200
201
        return parent::canEdit($member);
202
    }
203
204
    /**
205
     * @param Member
206
     *
207
     * @return boolean|string
208
     */
209
    public function canDelete($member = null)
210
    {
211
        if ($this->UserDefinedForm()) {
212
            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 212 which is incompatible with the return type of the parent method SilverStripe\UserForms\M...ubmittedForm::canDelete of type boolean.
Loading history...
213
        }
214
215
        return parent::canDelete($member);
216
    }
217
218
    /**
219
     * Get the share link of the form
220
     *
221
     * @return string
222
     * @throws \Exception
223
     */
224
    public function getPartialLink()
225
    {
226
        if (!$this->isInDB()) {
227
            return '(none)';
228
        }
229
230
        $token = $this->Token;
231
232
        return Controller::join_links(
233
            Director::absoluteBaseURL(),
234
            'partial',
235
            $this->generateKey($token),
0 ignored issues
show
Bug introduced by
It seems like $token defined by $this->Token on line 230 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...
236
            $token
237
        );
238
    }
239
240
    /**
241
     * Get the unique token for the share link
242
     *
243
     * @return bool|string|null
244
     * @throws \Exception
245
     */
246
    protected function getPartialToken()
247
    {
248
        if (!$this->TokenSalt) {
249
            $this->TokenSalt = $this->generateToken();
250
            $this->Token = $this->generateToken();
251
        }
252
253
        return $this->Token;
254
    }
255
256
    /**
257
     * Generate a new token
258
     *
259
     * @return bool|string
260
     * @throws \Exception
261
     */
262
    protected function generateToken()
263
    {
264
        $generator = new RandomGenerator();
265
266
        return substr($generator->randomToken('sha256'), 0, 16);
267
    }
268
269
    /**
270
     * Generate a key based on the share token salt
271
     *
272
     * @param string $token
273
     * @return mixed
274
     */
275
    public function generateKey($token)
276
    {
277
        return hash_pbkdf2('sha256', $token, $this->TokenSalt, 1000, 16);
278
    }
279
280
    /**
281
     * @return void
282
     */
283
    protected function generatePassword()
284
    {
285
        if (!$this->Password &&
286
            $this->getParent() &&
287
            $this->getParent()->PasswordProtected
288
        ) {
289
            $chars = range('A', 'Z');
290
            $chars = array_merge($chars, range('a', 'z'));
291
            $chars = array_merge($chars, range(0, 9));
292
            $chars = array_merge($chars, self::$special_characters);
293
            shuffle($chars);
294
            $pwd = implode(array_slice($chars, 0, 10));
295
            $this->Password = hash_pbkdf2('SHA256', $pwd, $this->TokenSalt, 1000);
296
            Controller::curr()->getRequest()->getSession()->set('FormPassword', $pwd);
297
        }
298
    }
299
}
300