Passed
Push — dependabot/composer/doctrine/d... ( bc1a80...bcc5a8 )
by
unknown
41:27 queued 36:06
created

Edit   A

Complexity

Total Complexity 41

Size/Duplication

Total Lines 344
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 175
c 0
b 0
f 0
dl 0
loc 344
rs 9.1199
wmc 41

7 Methods

Rating   Name   Duplication   Size   Complexity  
A execute() 0 16 3
A getData() 0 9 1
A loadForm() 0 56 2
A parse() 0 23 4
A loadGroups() 0 38 2
F validateForm() 0 155 28
A loadDeleteForm() 0 8 1

How to fix   Complexity   

Complex Class

Complex classes like Edit often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Edit, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Backend\Modules\Profiles\Actions;
4
5
use Backend\Core\Engine\Base\ActionEdit as BackendBaseActionEdit;
6
use Backend\Core\Engine\Authentication as BackendAuthentication;
7
use Backend\Core\Engine\DataGridDatabase as BackendDataGridDatabase;
8
use Backend\Core\Engine\DataGridFunctions as BackendDataGridFunctions;
9
use Backend\Core\Engine\Form as BackendForm;
10
use Backend\Core\Language\Language as BL;
11
use Backend\Core\Engine\Model as BackendModel;
12
use Backend\Form\Type\DeleteType;
13
use Backend\Modules\Profiles\Engine\Model as BackendProfilesModel;
14
use Symfony\Component\Intl\Intl as Intl;
15
16
/**
17
 * This is the edit-action, it will display a form to edit an existing profile.
18
 */
19
class Edit extends BackendBaseActionEdit
20
{
21
    /**
22
     * Groups data grid.
23
     *
24
     * @var BackendDataGridDatabase
25
     */
26
    private $dgGroups;
27
28
    /**
29
     * @var bool
30
     */
31
    private $notifyProfile;
32
33
    /**
34
     * Info about the current profile.
35
     *
36
     * @var array
37
     */
38
    private $profile;
39
40
    public function execute(): void
41
    {
42
        $this->id = $this->getRequest()->query->getInt('id');
43
44
        // does the item exist?
45
        if ($this->id !== 0 && BackendProfilesModel::exists($this->id)) {
46
            parent::execute();
47
            $this->getData();
48
            $this->loadGroups();
49
            $this->loadForm();
50
            $this->validateForm();
51
            $this->loadDeleteForm();
52
            $this->parse();
53
            $this->display();
54
        } else {
55
            $this->redirect(BackendModel::createUrlForAction('Index') . '&error=non-existing');
56
        }
57
    }
58
59
    private function getData(): void
60
    {
61
        // get general info
62
        $this->profile = BackendProfilesModel::get($this->id);
63
64
        $this->notifyProfile = $this->get('fork.settings')->get(
65
            $this->url->getModule(),
66
            'send_new_profile_mail',
67
            false
68
        );
69
    }
70
71
    private function loadForm(): void
72
    {
73
        // gender dropdown values
74
        $genderValues = [
75
            'male' => \SpoonFilter::ucfirst(BL::getLabel('Male')),
76
            'female' => \SpoonFilter::ucfirst(BL::getLabel('Female')),
77
        ];
78
79
        // birthdate dropdown values
80
        $days = range(1, 31);
81
        $months = \SpoonLocale::getMonths(BL::getInterfaceLanguage());
82
        $years = range(date('Y'), 1900);
83
84
        // get settings
85
        $birthDate = BackendProfilesModel::getSetting($this->id, 'birth_date');
86
87
        // get day, month and year
88
        if ($birthDate) {
89
            list($birthYear, $birthMonth, $birthDay) = explode('-', $birthDate);
90
        } else {
91
            // no birth date setting
92
            $birthDay = '';
93
            $birthMonth = '';
94
            $birthYear = '';
95
        }
96
97
        // create form
98
        $this->form = new BackendForm('edit');
99
100
        // create elements
101
        $this->form->addCheckbox('new_email');
102
        $this->form->addText('email', $this->profile['email']);
103
        $this->form->addCheckbox('new_password');
104
        $this->form->addPassword('password');
105
        $this->form->addPassword('password_repeat');
106
        $this->form->addText('display_name', $this->profile['display_name'])->makeRequired();
107
        $this->form->addText('first_name', BackendProfilesModel::getSetting($this->id, 'first_name'));
108
        $this->form->addText('last_name', BackendProfilesModel::getSetting($this->id, 'last_name'));
109
        $this->form->addText('city', BackendProfilesModel::getSetting($this->id, 'city'));
110
        $this->form->addDropdown('gender', $genderValues, BackendProfilesModel::getSetting($this->id, 'gender'));
111
        $this->form->addDropdown('day', array_combine($days, $days), $birthDay);
0 ignored issues
show
Bug introduced by
It seems like array_combine($days, $days) can also be of type false; however, parameter $values of Common\Core\Form::addDropdown() does only seem to accept array|null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

111
        $this->form->addDropdown('day', /** @scrutinizer ignore-type */ array_combine($days, $days), $birthDay);
Loading history...
112
        $this->form->addDropdown('month', $months, $birthMonth);
113
        $this->form->addDropdown('year', array_combine($years, $years), (int) $birthYear);
114
        $this->form->addDropdown(
115
            'country',
116
            Intl::getRegionBundle()->getCountryNames(BL::getInterfaceLanguage()),
117
            BackendProfilesModel::getSetting($this->id, 'country')
118
        );
119
        $this->form->addTextarea('about', BackendProfilesModel::getSetting($this->id, 'about'));
120
121
        // set default elements dropdowns
122
        $this->form->getField('gender')->setDefaultElement('');
123
        $this->form->getField('day')->setDefaultElement('');
124
        $this->form->getField('month')->setDefaultElement('');
125
        $this->form->getField('year')->setDefaultElement('');
126
        $this->form->getField('country')->setDefaultElement('');
127
    }
128
129
    private function loadGroups(): void
130
    {
131
        // create the data grid
132
        $this->dgGroups = new BackendDataGridDatabase(
133
            BackendProfilesModel::QUERY_DATAGRID_BROWSE_PROFILE_GROUPS,
134
            [$this->profile['id']]
135
        );
136
        $this->dgGroups->setColumnFunction('htmlspecialchars', ['[group_name]'], 'group_name', false);
137
138
        // sorting columns
139
        $this->dgGroups->setSortingColumns(['group_name'], 'group_name');
140
141
        // disable paging
142
        $this->dgGroups->setPaging(false);
143
144
        // set column function
145
        $this->dgGroups->setColumnFunction(
146
            [new BackendDataGridFunctions(), 'getLongDate'],
147
            ['[expires_on]'],
148
            'expires_on',
149
            true
150
        );
151
152
        // check if this action is allowed
153
        if (BackendAuthentication::isAllowedAction('EditProfileGroup')) {
154
            // set column URLs
155
            $this->dgGroups->setColumnURL(
156
                'group_name',
157
                BackendModel::createUrlForAction('EditProfileGroup') . '&amp;id=[id]&amp;profile_id=' . $this->id
158
            );
159
160
            // edit column
161
            $this->dgGroups->addColumn(
162
                'edit',
163
                null,
164
                BL::getLabel('Edit'),
165
                BackendModel::createUrlForAction('EditProfileGroup') . '&amp;id=[id]&amp;profile_id=' . $this->id,
166
                BL::getLabel('Edit')
167
            );
168
        }
169
    }
170
171
    protected function parse(): void
172
    {
173
        parent::parse();
174
175
        $this->template->assign('notifyProfile', $this->notifyProfile);
176
177
        // assign the active record and additional variables
178
        $this->template->assign('profile', $this->profile);
179
180
        // parse data grids
181
        $this->template->assign('dgGroups', ($this->dgGroups->getNumResults() != 0) ? $this->dgGroups->getContent() : false);
182
183
        // show delete or undelete button?
184
        if ($this->profile['status'] === 'deleted') {
185
            $this->template->assign('deleted', true);
186
        }
187
188
        // show block or unblock button?
189
        if ($this->profile['status'] === 'blocked') {
190
            $this->template->assign('blocked', true);
191
        }
192
193
        $this->header->appendDetailToBreadcrumbs($this->profile['display_name']);
194
    }
195
196
    private function validateForm(): void
197
    {
198
        // is the form submitted?
199
        if ($this->form->isSubmitted()) {
200
            // cleanup the submitted fields, ignore fields that were added by hackers
201
            $this->form->cleanupFields();
202
203
            // get fields
204
            $chkNewEmail = $this->form->getField('new_email');
205
            $txtEmail = $this->form->getField('email');
206
            $txtDisplayName = $this->form->getField('display_name');
207
            $chkNewPassword = $this->form->getField('new_password');
208
            $txtPassword = $this->form->getField('password');
209
            $txtPasswordRepeat = $this->form->getField('password_repeat');
210
            $txtFirstName = $this->form->getField('first_name');
211
            $txtLastName = $this->form->getField('last_name');
212
            $txtCity = $this->form->getField('city');
213
            $ddmGender = $this->form->getField('gender');
214
            $ddmDay = $this->form->getField('day');
215
            $ddmMonth = $this->form->getField('month');
216
            $ddmYear = $this->form->getField('year');
217
            $ddmCountry = $this->form->getField('country');
218
            $txtAbout = $this->form->getField('about');
219
220
            // email filled in?
221
            if ($chkNewEmail->isChecked() && $txtEmail->isFilled(BL::getError('EmailIsRequired'))) {
222
                // email must not be the same as previous one
223
                if ($txtEmail->getValue() == $this->profile['email']) {
224
                    $txtEmail->addError(BL::getError('EmailMatchesPrevious'));
225
                }
226
227
                // valid email?
228
                if ($txtEmail->isEmail(BL::getError('EmailIsInvalid'))) {
229
                    // email already exists?
230
                    if (BackendProfilesModel::existsByEmail($txtEmail->getValue(), $this->id)) {
231
                        // set error
232
                        $txtEmail->addError(BL::getError('EmailExists'));
233
                    }
234
                }
235
            }
236
237
            // display name filled in?
238
            if ($txtDisplayName->isFilled(BL::getError('DisplayNameIsRequired'))) {
239
                // display name already exists?
240
                if (BackendProfilesModel::existsDisplayName($txtDisplayName->getValue(), $this->id)) {
241
                    // set error
242
                    $txtDisplayName->addError(BL::getError('DisplayNameExists'));
243
                }
244
            }
245
246
            // new_password is checked, so verify new password (only if profile should not be notified)
247
            // because then if the password field is empty, it will generate a new password
248
            if ($chkNewPassword->isChecked() && !$this->notifyProfile) {
249
                $txtPassword->isFilled(BL::err('FieldIsRequired'));
250
                $txtPasswordRepeat->isFilled(BL::err('FieldIsRequired'));
251
252
                // both password fields are filled in and should match
253
                if ($txtPassword->isFilled() && $txtPasswordRepeat->isFilled()
254
                    && ($txtPassword->getValue() != $txtPasswordRepeat->getValue())) {
255
                    $txtPasswordRepeat->addError(BL::err('PasswordRepeatIsRequired'));
256
                }
257
            }
258
259
            // one of the bday fields are filled in
260
            if ($ddmDay->isFilled() || $ddmMonth->isFilled() || $ddmYear->isFilled()) {
261
                // valid date?
262
                if (!checkdate($ddmMonth->getValue(), $ddmDay->getValue(), $ddmYear->getValue())) {
263
                    // set error
264
                    $ddmYear->addError(BL::getError('DateIsInvalid'));
265
                }
266
            }
267
268
            // no errors?
269
            if ($this->form->isCorrect()) {
270
                // build item
271
                $values = ['email' => $chkNewEmail->isChecked() ? $txtEmail->getValue() : $this->profile['email']];
272
                $password = BL::lbl('YourExistingPassword');
273
274
                // only update if display name changed
275
                if ($txtDisplayName->getValue() != $this->profile['display_name']) {
276
                    $values['display_name'] = $txtDisplayName->getValue();
277
                    $values['url'] = BackendProfilesModel::getUrl(
278
                        $txtDisplayName->getValue(),
279
                        $this->id
280
                    );
281
                }
282
283
                // new password filled in?
284
                if ($chkNewPassword->isChecked()) {
285
                    // new password filled in? otherwise generate a password
286
                    $password = ($txtPassword->isFilled()) ?
287
                        $txtPassword->getValue() : BackendModel::generatePassword(8);
288
289
                    // build password
290
                    $values['password'] = BackendProfilesModel::encryptPassword($password);
291
                }
292
293
                // update values
294
                BackendProfilesModel::update($this->id, $values);
295
296
                // birthday is filled in
297
                if ($ddmYear->isFilled()) {
298
                    // mysql format
299
                    $birthDate = $ddmYear->getValue() . '-';
300
                    $birthDate .= str_pad($ddmMonth->getValue(), 2, '0', STR_PAD_LEFT) . '-';
301
                    $birthDate .= str_pad($ddmDay->getValue(), 2, '0', STR_PAD_LEFT);
302
                } else {
303
                    $birthDate = null;
304
                }
305
306
                // update settings
307
                BackendProfilesModel::setSetting($this->id, 'first_name', $txtFirstName->getValue());
308
                BackendProfilesModel::setSetting($this->id, 'last_name', $txtLastName->getValue());
309
                BackendProfilesModel::setSetting($this->id, 'gender', $ddmGender->getValue());
310
                BackendProfilesModel::setSetting($this->id, 'birth_date', $birthDate);
311
                BackendProfilesModel::setSetting($this->id, 'city', $txtCity->getValue());
312
                BackendProfilesModel::setSetting($this->id, 'country', $ddmCountry->getValue());
313
                BackendProfilesModel::setSetting($this->id, 'about', $txtAbout->getValue());
314
315
                $displayName = $values['display_name'] ?? $this->profile['display_name'];
316
317
                $redirectUrl = BackendModel::createUrlForAction('Index') .
318
                               '&var=' . rawurlencode($values['email']) .
319
                    '&highlight=row-' . $this->id .
320
                    '&var=' . rawurlencode($displayName) .
321
                    '&report='
322
                ;
323
324
                if ($this->notifyProfile &&
325
                    ($chkNewEmail->isChecked() || $chkNewPassword->isChecked())
326
                ) {
327
                    // notify values
328
                    $notifyValues = array_merge(
329
                        $values,
330
                        [
331
                            'id' => $this->id,
332
                            'first_name' => $txtFirstName->getValue(),
333
                            'last_name' => $txtLastName->getValue(),
334
                            'unencrypted_password' => $password,
335
                        ]
336
                    );
337
338
                    if (!isset($notifyValues['display_name'])) {
339
                        $notifyValues['display_name'] = $this->profile['display_name'];
340
                    }
341
342
                    BackendProfilesModel::notifyProfile($notifyValues, true);
343
344
                    $redirectUrl .= 'saved-and-notified';
345
                } else {
346
                    $redirectUrl .= 'saved';
347
                }
348
349
                // everything is saved, so redirect to the overview
350
                $this->redirect($redirectUrl);
351
            }
352
        }
353
    }
354
355
    private function loadDeleteForm(): void
356
    {
357
        $deleteForm = $this->createForm(
358
            DeleteType::class,
359
            ['id' => $this->profile['id']],
360
            ['module' => $this->getModule()]
361
        );
362
        $this->template->assign('deleteForm', $deleteForm->createView());
363
    }
364
}
365