Passed
Push — multiproject/db ( 16c0c4...9dd649 )
by Simon
15:28 queued 11:05
created

PageCustomClose::showCustomCloseForm()   B

Complexity

Conditions 5
Paths 7

Size

Total Lines 71
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 3
Bugs 1 Features 0
Metric Value
eloc 39
c 3
b 1
f 0
dl 0
loc 71
ccs 0
cts 45
cp 0
rs 8.9848
cc 5
nc 7
nop 2
crap 30

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/******************************************************************************
3
 * Wikipedia Account Creation Assistance tool                                 *
4
 *                                                                            *
5
 * All code in this file is released into the public domain by the ACC        *
6
 * Development Team. Please see team.json for a list of contributors.         *
7
 ******************************************************************************/
8
9
namespace Waca\Pages\RequestAction;
10
11
use Exception;
12
use Waca\Background\Task\BotCreationTask;
13
use Waca\Background\Task\UserCreationTask;
14
use Waca\DataObjects\EmailTemplate;
15
use Waca\DataObjects\JobQueue;
16
use Waca\DataObjects\Request;
17
use Waca\DataObjects\User;
18
use Waca\Exceptions\AccessDeniedException;
19
use Waca\Exceptions\ApplicationLogicException;
20
use Waca\Exceptions\OptimisticLockFailedException;
21
use Waca\Fragments\RequestData;
22
use Waca\Helpers\Logger;
23
use Waca\Helpers\OAuthUserHelper;
24
use Waca\PdoDatabase;
25
use Waca\RequestStatus;
26
use Waca\SessionAlert;
27
use Waca\WebRequest;
28
29
class PageCustomClose extends PageCloseRequest
30
{
31
    use RequestData;
32
33
    public const CREATE_OAUTH = 'created-oauth';
34
    public const CREATE_BOT = 'created-bot';
35
36
    protected function main()
37
    {
38
        $database = $this->getDatabase();
39
40
        $request = $this->getRequest($database);
41
        $currentUser = User::getCurrent($this->getDatabase());
42
43
        if ($request->getStatus() === 'Closed') {
44
            throw new ApplicationLogicException('Request is already closed');
45
        }
46
47
        // Dual-mode page
48
        if (WebRequest::wasPosted()) {
49
            $this->validateCSRFToken();
50
            $success = $this->doCustomClose($currentUser, $request, $database);
51
52
            if ($success) {
53
                $this->redirect();
54
            }
55
        }
56
        else {
57
            $this->assignCSRFToken();
58
            $this->showCustomCloseForm($database, $request);
59
        }
60
    }
61
62
    /**
63
     * @param $database
64
     *
65
     * @return Request
66
     * @throws ApplicationLogicException
67
     */
68
    protected function getRequest(PdoDatabase $database)
69
    {
70
        $requestId = WebRequest::getInt('request');
71
        if ($requestId === null) {
72
            throw new ApplicationLogicException('Request ID not found');
73
        }
74
75
        /** @var Request $request */
76
        $request = Request::getById($requestId, $database);
77
78
        if ($request === false) {
0 ignored issues
show
introduced by
The condition $request === false is always false.
Loading history...
79
            throw new ApplicationLogicException('Request not found');
80
        }
81
82
        return $request;
83
    }
84
85
    /**
86
     * @param PdoDatabase $database
87
     *
88
     * @return EmailTemplate|null
89
     */
90
    protected function getTemplate(PdoDatabase $database)
91
    {
92
        $templateId = WebRequest::getInt('template');
93
        if ($templateId === null) {
94
            return null;
95
        }
96
97
        /** @var EmailTemplate $template */
98
        $template = EmailTemplate::getById($templateId, $database);
99
        if ($template === false || !$template->getActive()) {
100
            return null;
101
        }
102
103
        return $template;
104
    }
105
106
    /**
107
     * @param $database
108
     * @param $request
109
     *
110
     * @throws Exception
111
     */
112
    protected function showCustomCloseForm(PdoDatabase $database, Request $request)
113
    {
114
        $this->setHtmlTitle("Custom close");
115
116
        $currentUser = User::getCurrent($database);
117
        $config = $this->getSiteConfiguration();
118
119
        $allowedPrivateData = $this->isAllowedPrivateData($request, $currentUser);
120
        if (!$allowedPrivateData) {
121
            // we probably shouldn't be showing the user this form if they're not allowed to access private data...
122
            throw new AccessDeniedException($this->getSecurityManager());
123
        }
124
125
        $template = $this->getTemplate($database);
126
127
        // Preload data
128
        $this->assign('defaultAction', '');
129
        $this->assign('preloadText', '');
130
        $this->assign('preloadTitle', '');
131
132
        if ($template !== null) {
133
            $this->assign('defaultAction', $template->getDefaultAction());
134
            $this->assign('preloadText', $template->getText());
135
            $this->assign('preloadTitle', $template->getName());
136
        }
137
138
        // Static data
139
        $this->assign('requeststates', $config->getRequestStates());
0 ignored issues
show
Deprecated Code introduced by
The function Waca\SiteConfiguration::getRequestStates() has been deprecated: To be removed after dynamic queues hit production. This will need to be major point release. ( Ignorable by Annotation )

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

139
        $this->assign('requeststates', /** @scrutinizer ignore-deprecated */ $config->getRequestStates());

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
140
141
        // request data
142
        $this->assign('requestId', $request->getIp());
143
        $this->assign('updateVersion', $request->getUpdateVersion());
144
        $this->setupBasicData($request, $config);
145
        $this->setupReservationDetails($request->getReserved(), $database, $currentUser);
146
        $this->setupPrivateData($request, $config);
147
        $this->setupRelatedRequests($request, $config, $database);
148
149
        // IP location
150
        $trustedIp = $this->getXffTrustProvider()->getTrustedClientIp($request->getIp(), $request->getForwardedIp());
151
        $this->assign('iplocation', $this->getLocationProvider()->getIpLocation($trustedIp));
152
153
        // Confirmations
154
        $this->assign('confirmEmailAlreadySent', $this->checkEmailAlreadySent($request));
155
156
        $this->assign('canSkipCcMailingList', $this->barrierTest('skipCcMailingList', $currentUser));
157
158
        $this->assign('allowWelcomeSkip', false);
159
        $this->assign('forceWelcomeSkip', false);
160
161
        $canOauthCreate = $this->barrierTest(User::CREATION_OAUTH, $currentUser, 'RequestCreation');
162
        $canBotCreate = $this->barrierTest(User::CREATION_BOT, $currentUser, 'RequestCreation');
163
164
        $oauth = new OAuthUserHelper($currentUser, $this->getDatabase(), $this->getOAuthProtocolHelper(), $config);
165
166
        if ($currentUser->getWelcomeTemplate() != 0) {
167
            $this->assign('allowWelcomeSkip', true);
168
169
            if (!$oauth->canWelcome()) {
170
                $this->assign('forceWelcomeSkip', true);
171
            }
172
        }
173
174
        // disable options if there's a misconfiguration.
175
        $canOauthCreate &= $oauth->canCreateAccount();
176
        $canBotCreate &= $this->getSiteConfiguration()->getCreationBotPassword() !== null;
177
178
        $this->assign('canOauthCreate', $canOauthCreate);
179
        $this->assign('canBotCreate', $canBotCreate);
180
181
        // template
182
        $this->setTemplate('custom-close.tpl');
183
    }
184
185
    /**
186
     * @param User        $currentUser
187
     * @param Request     $request
188
     * @param PdoDatabase $database
189
     *
190
     * @throws ApplicationLogicException
191
     */
192
    protected function doCustomClose(User $currentUser, Request $request, PdoDatabase $database) : bool
193
    {
194
        $messageBody = WebRequest::postString('msgbody');
195
        if ($messageBody === null || trim($messageBody) === '') {
196
            throw new ApplicationLogicException('Message body cannot be blank');
197
        }
198
199
        $ccMailingList = true;
200
        if ($this->barrierTest('skipCcMailingList', $currentUser)) {
201
            $ccMailingList = WebRequest::postBoolean('ccMailingList');
202
        }
203
204
        if ($request->getStatus() === 'Closed') {
205
            throw new ApplicationLogicException('Request is already closed');
206
        }
207
208
        if (!(WebRequest::postBoolean('confirmEmailAlreadySent'))
209
        ) {
210
            throw new ApplicationLogicException('Not all confirmations checked');
211
        }
212
213
        $action = WebRequest::postString('action');
214
        $availableRequestStates = $this->getSiteConfiguration()->getRequestStates();
0 ignored issues
show
Deprecated Code introduced by
The function Waca\SiteConfiguration::getRequestStates() has been deprecated: To be removed after dynamic queues hit production. This will need to be major point release. ( Ignorable by Annotation )

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

214
        $availableRequestStates = /** @scrutinizer ignore-deprecated */ $this->getSiteConfiguration()->getRequestStates();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
215
216
        if ($action === EmailTemplate::CREATED || $action === EmailTemplate::NOT_CREATED) {
217
218
            if ($action === EmailTemplate::CREATED) {
219
                if ($this->checkAccountCreated($request)) {
220
                    $this->assignCSRFToken();
221
                    $this->showCustomCloseForm($database, $request);
222
223
                    $this->assign("preloadText", $messageBody);
224
                    $this->assign('preloadAction', $action);
225
                    $this->assign('ccMailingList', $ccMailingList);
226
                    $this->assign('showNonExistentAccountWarning', true);
227
                    $this->assign('skipAutoWelcome', WebRequest::postBoolean('skipAutoWelcome'));
228
229
                    return false;
230
                }
231
            }
232
233
            // Close request
234
            $this->closeRequest($request, $database, $action, $messageBody);
235
236
            $this->processWelcome($action, null);
237
238
            // Send the mail after the save, since save can be rolled back
239
            $this->sendMail($request, $messageBody, $currentUser, $ccMailingList);
240
241
            return true;
242
        }
243
244
        if ($action === self::CREATE_OAUTH || $action === self::CREATE_BOT) {
245
            $this->processAutoCreation($currentUser, $action, $request, $messageBody, $ccMailingList);
246
247
            return true;
248
        }
249
250
        // If action is a state key, defer to other state
251
        if (array_key_exists($action, $availableRequestStates)) {
252
            $this->deferRequest($request, $database, $action, $availableRequestStates, $messageBody);
253
254
            // Send the mail after the save, since save can be rolled back
255
            $this->sendMail($request, $messageBody, $currentUser, $ccMailingList);
256
257
            return true;
258
        }
259
260
        // Any other scenario, just send the email.
261
262
        $request->setReserved(null);
263
        $request->setUpdateVersion(WebRequest::postInt('updateversion'));
264
        $request->save();
265
266
        // Perform the notifications and stuff *after* we've successfully saved, since the save can throw an OLE
267
        // and be rolled back.
268
269
        // Send mail
270
        $this->sendMail($request, $messageBody, $currentUser, $ccMailingList);
271
272
        Logger::sentMail($database, $request, $messageBody);
273
        Logger::unreserve($database, $request);
274
275
        $this->getNotificationHelper()->sentMail($request);
276
        SessionAlert::success("Sent mail to Request {$request->getId()}");
277
278
        return true;
279
    }
280
281
    /**
282
     * @param Request     $request
283
     * @param PdoDatabase $database
284
     * @param string      $action
285
     * @param string      $messageBody
286
     *
287
     * @throws Exception
288
     * @throws OptimisticLockFailedException
289
     */
290
    protected function closeRequest(Request $request, PdoDatabase $database, $action, $messageBody)
291
    {
292
        $request->setStatus('Closed');
293
        $request->setReserved(null);
294
        $request->setUpdateVersion(WebRequest::postInt('updateversion'));
295
        $request->save();
296
297
        // Perform the notifications and stuff *after* we've successfully saved, since the save can throw an OLE and
298
        // be rolled back.
299
300
        if ($action == EmailTemplate::CREATED) {
301
            $logCloseType = 'custom-y';
302
            $notificationCloseType = "Custom, Created";
303
        }
304
        else {
305
            $logCloseType = 'custom-n';
306
            $notificationCloseType = "Custom, Not Created";
307
        }
308
309
        Logger::closeRequest($database, $request, $logCloseType, $messageBody);
0 ignored issues
show
Bug introduced by
$logCloseType of type string is incompatible with the type integer expected by parameter $target of Waca\Helpers\Logger::closeRequest(). ( Ignorable by Annotation )

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

309
        Logger::closeRequest($database, $request, /** @scrutinizer ignore-type */ $logCloseType, $messageBody);
Loading history...
310
        $this->getNotificationHelper()->requestClosed($request, $notificationCloseType);
311
312
        $requestName = htmlentities($request->getName(), ENT_COMPAT, 'UTF-8');
313
        SessionAlert::success("Request {$request->getId()} ({$requestName}) closed as {$notificationCloseType}.");
314
    }
315
316
    /**
317
     * @param Request     $request
318
     * @param PdoDatabase $database
319
     * @param string      $action
320
     * @param             $availableRequestStates
321
     * @param string      $messageBody
322
     *
323
     * @throws Exception
324
     * @throws OptimisticLockFailedException
325
     */
326
    protected function deferRequest(
327
        Request $request,
328
        PdoDatabase $database,
329
        $action,
330
        $availableRequestStates,
331
        $messageBody
332
    ) {
333
        $request->setStatus($action);
334
        $request->setReserved(null);
335
        $request->setUpdateVersion(WebRequest::postInt('updateversion'));
336
        $request->save();
337
338
        // Perform the notifications and stuff *after* we've successfully saved, since the save can throw an OLE
339
        // and be rolled back.
340
341
        $deferToLog = $availableRequestStates[$action]['defertolog'];
342
        Logger::sentMail($database, $request, $messageBody);
343
        Logger::deferRequest($database, $request, $deferToLog);
344
345
        $this->getNotificationHelper()->requestDeferredWithMail($request);
346
347
        $deferTo = $availableRequestStates[$action]['deferto'];
348
        SessionAlert::success("Request {$request->getId()} deferred to $deferTo, sending an email.");
349
    }
350
351
    /**
352
     * @param User    $currentUser
353
     * @param string  $action
354
     * @param Request $request
355
     * @param string  $messageBody
356
     * @param bool    $ccMailingList
357
     *
358
     * @throws AccessDeniedException
359
     * @throws ApplicationLogicException
360
     * @throws OptimisticLockFailedException
361
     */
362
    protected function processAutoCreation(User $currentUser, string $action, Request $request, string $messageBody, bool $ccMailingList): void
363
    {
364
        $db = $this->getDatabase();
365
        $oauth = new OAuthUserHelper($currentUser, $db, $this->getOAuthProtocolHelper(), $this->getSiteConfiguration());
366
        $canOauthCreate = $this->barrierTest(User::CREATION_OAUTH, $currentUser, 'RequestCreation');
367
        $canBotCreate = $this->barrierTest(User::CREATION_BOT, $currentUser, 'RequestCreation');
368
        $canOauthCreate &= $oauth->canCreateAccount();
369
        $canBotCreate &= $this->getSiteConfiguration()->getCreationBotPassword() !== null;
370
371
        $creationTaskClass = null;
372
373
        if ($action === self::CREATE_OAUTH) {
374
            if(!$canOauthCreate) {
375
                throw new AccessDeniedException($this->getSecurityManager());
376
            }
377
378
            $creationTaskClass = UserCreationTask::class;
379
        }
380
381
        if ($action === self::CREATE_BOT) {
382
            if (!$canBotCreate) {
383
                throw new AccessDeniedException($this->getSecurityManager());
384
            }
385
386
            $creationTaskClass = BotCreationTask::class;
387
        }
388
389
        if ($creationTaskClass === null) {
390
            throw new ApplicationLogicException('Cannot determine creation mode');
391
        }
392
393
        $request->setStatus(RequestStatus::JOBQUEUE);
394
        $request->setReserved(null);
395
        $request->save();
396
397
        $parameters = [
398
            'emailText' => $messageBody,
399
            'ccMailingList' => $ccMailingList
400
        ];
401
402
        $creationTask = new JobQueue();
403
        $creationTask->setTask($creationTaskClass);
404
        $creationTask->setRequest($request->getId());
405
        $creationTask->setTriggerUserId($currentUser->getId());
406
        $creationTask->setParameters(json_encode($parameters));
407
        $creationTask->setDatabase($db);
408
        $creationTask->save();
409
410
        $creationTaskId = $creationTask->getId();
411
412
        Logger::enqueuedJobQueue($db, $request);
413
        $this->getNotificationHelper()->requestCloseQueued($request, 'Custom, Created');
414
415
        SessionAlert::success("Request {$request->getId()} has been queued for autocreation");
416
417
        // forge this since it is actually a creation.
418
        $this->processWelcome(EmailTemplate::CREATED, $creationTaskId);
419
    }
420
}
421