Completed
Push — irc-link-update ( 5c98e1 )
by Simon
17s queued 13s
created

PageCustomClose::doCustomClose()   C

Complexity

Conditions 13
Paths 17

Size

Total Lines 87
Code Lines 43

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 182

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 43
c 1
b 0
f 0
dl 0
loc 87
ccs 0
cts 55
cp 0
rs 6.6166
cc 13
nc 17
nop 3
crap 182

How to fix   Long Method    Complexity   

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());
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();
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