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

PartialFormSubmission::canEdit()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 1
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
            $this->getParent() &&
229
            $this->getParent()->PasswordProtected
230
        ) {
231
            $chars = range('A', 'Z');
232
            $chars = array_merge($chars, range('a', 'z'));
233
            $chars = array_merge($chars, range(0, 9));
234
            $chars = array_merge($chars, self::$special_characters);
235
            shuffle($chars);
236
            $pwd = implode(array_slice($chars, 0, 10));
237
            $this->Password = hash_pbkdf2('SHA256', $pwd, $this->TokenSalt, 1000);
238
            Controller::curr()->getRequest()->getSession()->set('FormPassword', $pwd);
239
        }
240
    }
241
242
    /**
243
     * @return DataObject|UserDefinedForm
244
     */
245
    public function getParent()
246
    {
247
        return $this->UserDefinedForm();
248
    }
249
250
    /**
251
     * @param Member
252
     *
253
     * @return boolean|string
254
     */
255
    public function canCreate($member = null, $context = [])
256
    {
257
        return false;
258
    }
259
260
    /**
261
     * @param Member $member
262
     *
263
     * @return boolean|string
264
     */
265
    public function canView($member = null)
266
    {
267
        if ($this->UserDefinedForm()) {
268
            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 268 which is incompatible with the return type of the parent method SilverStripe\UserForms\M...\SubmittedForm::canView of type boolean.
Loading history...
269
        }
270
271
        return parent::canView($member);
272
    }
273
274
    /**
275
     * @param Member $member
276
     *
277
     * @return boolean|string
278
     */
279
    public function canEdit($member = null)
280
    {
281
        if ($this->UserDefinedForm()) {
282
            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 282 which is incompatible with the return type of the parent method SilverStripe\UserForms\M...\SubmittedForm::canEdit of type boolean.
Loading history...
283
        }
284
285
        return parent::canEdit($member);
286
    }
287
288
    /**
289
     * @param Member $member
290
     *
291
     * @return boolean|string
292
     */
293
    public function canDelete($member = null)
294
    {
295
        if ($this->UserDefinedForm()) {
296
            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 296 which is incompatible with the return type of the parent method SilverStripe\UserForms\M...ubmittedForm::canDelete of type boolean.
Loading history...
297
        }
298
299
        return parent::canDelete($member);
300
    }
301
}
302