Passed
Push — manual-account-multidomain ( 71d0c9 )
by Simon
05:02
created

PageViewRequest::getCreationUrl()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
c 0
b 0
f 0
dl 0
loc 12
rs 10
cc 1
nc 1
nop 0
1
<?php
2
/******************************************************************************
3
 * Wikipedia Account Creation Assistance tool                                 *
4
 * ACC Development Team. Please see team.json for a list of contributors.     *
5
 *                                                                            *
6
 * This is free and unencumbered software released into the public domain.    *
7
 * Please see LICENSE.md for the full licencing statement.                    *
8
 ******************************************************************************/
9
10
namespace Waca\Pages;
11
12
use Exception;
13
use DateTime;
14
use Waca\DataObjects\Comment;
15
use Waca\DataObjects\Domain;
16
use Waca\DataObjects\EmailTemplate;
17
use Waca\DataObjects\JobQueue;
18
use Waca\DataObjects\Log;
19
use Waca\DataObjects\Request;
20
use Waca\DataObjects\RequestQueue;
21
use Waca\DataObjects\User;
22
use Waca\Exceptions\ApplicationLogicException;
23
use Waca\Fragments\RequestData;
24
use Waca\Helpers\LogHelper;
25
use Waca\Helpers\OAuthUserHelper;
26
use Waca\Helpers\PreferenceManager;
27
use Waca\Pages\RequestAction\PageManuallyConfirm;
28
use Waca\PdoDatabase;
29
use Waca\Security\RoleConfigurationBase;
30
use Waca\RequestStatus;
31
use Waca\Tasks\InternalPageBase;
32
use Waca\WebRequest;
33
34
class PageViewRequest extends InternalPageBase
35
{
36
    use RequestData;
37
38
    const STATUS_SYMBOL_OPEN = '&#927';
39
    const STATUS_SYMBOL_ACCEPTED = '&#x2611';
40
    const STATUS_SYMBOL_REJECTED = '&#x2612';
41
42
    /**
43
     * Main function for this page, when no specific actions are called.
44
     * @throws ApplicationLogicException
45
     */
46
    protected function main()
47
    {
48
        // set up csrf protection
49
        $this->assignCSRFToken();
50
51
        // get some useful objects
52
        $database = $this->getDatabase();
53
        $request = $this->getRequest($database, WebRequest::getInt('id'));
54
        $config = $this->getSiteConfiguration();
55
        $currentUser = User::getCurrent($database);
56
57
        // FIXME: domains!
58
        /** @var Domain $domain */
59
        $domain = Domain::getById(1, $this->getDatabase());
60
        $this->assign('mediawikiScriptPath', $domain->getWikiArticlePath());
61
62
        // Shows a page if the email is not confirmed.
63
        if ($request->getEmailConfirm() !== 'Confirmed') {
64
            // Show a banner if the user can manually confirm the request
65
            $viewConfirm = $this->barrierTest(RoleConfigurationBase::MAIN, $currentUser, PageManuallyConfirm::class);
66
67
            // If the request is purged, there's nothing to confirm!
68
            if ($request->getEmail() === $this->getSiteConfiguration()->getDataClearEmail()) {
69
                $viewConfirm = false;
70
            }
71
72
            // Render
73
            $this->setTemplate("view-request/not-confirmed.tpl");
74
            $this->assign("requestId", $request->getId());
75
            $this->assign("requestVersion", $request->getUpdateVersion());
76
            $this->assign('canViewConfirmButton', $viewConfirm);
77
78
            // Make sure to return, to prevent the leaking of other information.
79
            return;
80
        }
81
82
        $this->setupBasicData($request, $config);
83
84
        $this->setupUsernameData($request);
85
86
        $this->setupTitle($request);
87
88
        $this->setupReservationDetails($request->getReserved(), $database, $currentUser);
89
        $this->setupGeneralData($database);
90
91
        $this->assign('requestDataCleared', false);
92
        if ($request->getEmail() === $this->getSiteConfiguration()->getDataClearEmail()) {
93
            $this->assign('requestDataCleared', true);
94
        }
95
96
        $allowedPrivateData = $this->isAllowedPrivateData($request, $currentUser);
97
98
        $this->setupCreationTypes($currentUser);
99
100
        $this->setupLogData($request, $database, $allowedPrivateData);
101
102
        $this->addJs("/api.php?action=templates&targetVariable=templateconfirms");
103
104
        $this->assign('showRevealLink', false);
105
        if ($request->getReserved() === $currentUser->getId() ||
106
            $this->barrierTest('alwaysSeeHash', $currentUser, 'RequestData')
107
        ) {
108
            $this->assign('showRevealLink', true);
109
            $this->assign('revealHash', $request->getRevealHash());
110
        }
111
112
        $this->assign('canSeeRelatedRequests', false);
113
        if ($allowedPrivateData || $this->barrierTest('seeRelatedRequests', $currentUser, 'RequestData')) {
114
            $this->setupRelatedRequests($request, $config, $database);
115
        }
116
117
        $this->assign('canCreateLocalAccount', $this->barrierTest('createLocalAccount', $currentUser, 'RequestData'));
118
119
        $closureDate = $request->getClosureDate();
120
        $date = new DateTime();
121
        $date->modify("-7 days");
122
        if ($request->getStatus() == "Closed" && $closureDate < $date) {
123
            $this->assign('isOldRequest', true);
124
        }
125
        $this->assign('canResetOldRequest', $this->barrierTest('reopenOldRequest', $currentUser, 'RequestData'));
126
        $this->assign('canResetPurgedRequest', $this->barrierTest('reopenClearedRequest', $currentUser, 'RequestData'));
127
128
        $this->assign('requestEmailSent', $request->getEmailSent());
129
130
        if ($allowedPrivateData) {
131
            $this->assign('manualCreationUrl', $this->getCreationUrl());
132
133
            $this->setTemplate('view-request/main-with-data.tpl');
134
            $this->setupPrivateData($request, $config);
135
            $this->assign('canSetBan', $this->barrierTest('set', $currentUser, PageBan::class));
136
            $this->assign('canSeeCheckuserData', $this->barrierTest('seeUserAgentData', $currentUser, 'RequestData'));
137
138
            if ($this->barrierTest('seeUserAgentData', $currentUser, 'RequestData')) {
139
                $this->setTemplate('view-request/main-with-checkuser-data.tpl');
140
                $this->setupCheckUserData($request);
141
            }
142
        }
143
        else {
144
            $this->setTemplate('view-request/main.tpl');
145
        }
146
    }
147
148
    /**
149
     * @param Request $request
150
     */
151
    protected function setupTitle(Request $request)
152
    {
153
        $statusSymbol = self::STATUS_SYMBOL_OPEN;
154
        if ($request->getStatus() === RequestStatus::CLOSED) {
155
            if ($request->getWasCreated()) {
156
                $statusSymbol = self::STATUS_SYMBOL_ACCEPTED;
157
            }
158
            else {
159
                $statusSymbol = self::STATUS_SYMBOL_REJECTED;
160
            }
161
        }
162
163
        $this->setHtmlTitle($statusSymbol . ' #' . $request->getId());
164
    }
165
166
    /**
167
     * Sets up data unrelated to the request, such as the email template information
168
     *
169
     * @param PdoDatabase $database
170
     */
171
    protected function setupGeneralData(PdoDatabase $database)
172
    {
173
        $this->assign('createAccountReason', 'Requested account at [[WP:ACC]], request #');
174
175
        // FIXME: domains
176
        /** @var Domain $domain */
177
        $domain = Domain::getById(1, $database);
178
        $this->assign('defaultRequestState', RequestQueue::getDefaultQueue($database, 1)->getApiName());
179
        $this->assign('activeRequestQueues', RequestQueue::getEnabledQueues($database));
180
181
        /** @var EmailTemplate $createdTemplate */
182
        $createdTemplate = EmailTemplate::getById($domain->getDefaultClose(), $database);
183
184
        $this->assign('createdHasJsQuestion', $createdTemplate->getJsquestion() != '');
185
        $this->assign('createdId', $createdTemplate->getId());
186
        $this->assign('createdName', $createdTemplate->getName());
187
188
        $preferenceManager = PreferenceManager::getForCurrent($database);
189
        $skipJsAborts = $preferenceManager->getPreference(PreferenceManager::PREF_SKIP_JS_ABORT);
190
        $preferredCreationMode = (int)$preferenceManager->getPreference(PreferenceManager::PREF_CREATION_MODE);
191
        $this->assign('skipJsAborts', $skipJsAborts);
192
        $this->assign('preferredCreationMode', $preferredCreationMode);
193
194
        $createReasons = EmailTemplate::getActiveNonpreloadTemplates(
195
            EmailTemplate::ACTION_CREATED,
196
            $database,
197
            $domain->getId(),
198
            $domain->getDefaultClose());
199
        $this->assign("createReasons", $createReasons);
200
201
        $declineReasons = EmailTemplate::getActiveNonpreloadTemplates(
202
            EmailTemplate::ACTION_NOT_CREATED,
203
            $database,
204
            $domain->getId());
205
        $this->assign("declineReasons", $declineReasons);
206
207
        $allCreateReasons = EmailTemplate::getAllActiveTemplates(
208
            EmailTemplate::ACTION_CREATED,
209
            $database,
210
            $domain->getId());
211
        $this->assign("allCreateReasons", $allCreateReasons);
212
213
        $allDeclineReasons = EmailTemplate::getAllActiveTemplates(
214
            EmailTemplate::ACTION_NOT_CREATED,
215
            $database,
216
            $domain->getId());
217
        $this->assign("allDeclineReasons", $allDeclineReasons);
218
219
        $allOtherReasons = EmailTemplate::getAllActiveTemplates(
220
            false,
221
            $database,
222
            $domain->getId());
223
        $this->assign("allOtherReasons", $allOtherReasons);
224
    }
225
226
    private function setupLogData(Request $request, PdoDatabase $database, bool $allowedPrivateData)
227
    {
228
        $currentUser = User::getCurrent($database);
229
230
        $logs = LogHelper::getRequestLogsWithComments($request->getId(), $database, $this->getSecurityManager());
231
        $requestLogs = array();
232
233
        /** @var User[] $nameCache */
234
        $nameCache = array();
235
236
        $editableComments = $this->barrierTest('editOthers', $currentUser, PageEditComment::class);
237
238
        $canFlag = $this->barrierTest(RoleConfigurationBase::MAIN, $currentUser, PageFlagComment::class);
239
        $canUnflag = $this->barrierTest('unflag', $currentUser, PageFlagComment::class);
240
241
        /** @var Log|Comment $entry */
242
        foreach ($logs as $entry) {
243
            // both log and comment have a 'user' field
244
            if (!array_key_exists($entry->getUser(), $nameCache)) {
245
                $entryUser = User::getById($entry->getUser(), $database);
246
                $nameCache[$entry->getUser()] = $entryUser;
247
            }
248
249
            if ($entry instanceof Comment) {
250
                // Determine if the comment contains private information.
251
                // Private defined as flagged or restricted visibility, but only when the user isn't allowed
252
                // to see private data
253
                $commentIsRestricted =
254
                    ($entry->getFlagged()
255
                        || $entry->getVisibility() == 'admin' || $entry->getVisibility() == 'checkuser')
256
                    && !$allowedPrivateData;
257
258
                // Only allow comment editing if the user is able to edit comments or this is the user's own comment,
259
                // but only when they're allowed to see the comment itself.
260
                $commentIsEditable = ($editableComments || $entry->getUser() == $currentUser->getId())
261
                    && !$commentIsRestricted;
262
263
                // Flagging/unflagging can only be done if you can see the comment
264
                $canFlagThisComment = $canFlag
265
                    && (
266
                        (!$entry->getFlagged() && !$commentIsRestricted)
267
                        || ($entry->getFlagged() && $canUnflag && $commentIsEditable)
268
                    );
269
270
                $requestLogs[] = array(
271
                    'type'          => 'comment',
272
                    'security'      => $entry->getVisibility(),
273
                    'user'          => $entry->getVisibility() == 'requester' ? $request->getName() : $nameCache[$entry->getUser()]->getUsername(),
274
                    'userid'        => $entry->getUser() == -1 ? null : $entry->getUser(),
275
                    'entry'         => null,
276
                    'time'          => $entry->getTime(),
277
                    'canedit'       => $commentIsEditable,
278
                    'id'            => $entry->getId(),
279
                    'comment'       => $entry->getComment(),
280
                    'flagged'       => $entry->getFlagged(),
281
                    'canflag'       => $canFlagThisComment,
282
                    'updateversion' => $entry->getUpdateVersion(),
283
                    'edited'        => $entry->getEdited(),
284
                    'hidden'        => $commentIsRestricted
285
                );
286
            }
287
288
            if ($entry instanceof Log) {
289
                $invalidUserId = $entry->getUser() === -1 || $entry->getUser() === 0;
290
                $entryUser = $invalidUserId ? User::getCommunity() : $nameCache[$entry->getUser()];
291
292
                $entryComment = $entry->getComment();
293
294
                if ($entry->getAction() === 'JobIssueRequest' || $entry->getAction() === 'JobCompletedRequest') {
295
                    $data = unserialize($entry->getComment());
0 ignored issues
show
Bug introduced by
It seems like $entry->getComment() can also be of type null; however, parameter $data of unserialize() does only seem to accept string, 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

295
                    $data = unserialize(/** @scrutinizer ignore-type */ $entry->getComment());
Loading history...
296
                    /** @var JobQueue $job */
297
                    $job = JobQueue::getById($data['job'], $database);
298
                    $requestLogs[] = array(
299
                        'type'     => 'joblog',
300
                        'security' => 'user',
301
                        'userid'   => $entry->getUser() == -1 ? null : $entry->getUser(),
302
                        'user'     => $entryUser->getUsername(),
303
                        'entry'    => LogHelper::getLogDescription($entry),
304
                        'time'     => $entry->getTimestamp(),
305
                        'canedit'  => false,
306
                        'id'       => $entry->getId(),
307
                        'jobId'    => $job->getId(),
308
                        'jobDesc'  => JobQueue::getTaskDescriptions()[$job->getTask()],
309
                    );
310
                }
311
                else {
312
                    $requestLogs[] = array(
313
                        'type'     => 'log',
314
                        'security' => 'user',
315
                        'userid'   => $entry->getUser() == -1 ? null : $entry->getUser(),
316
                        'user'     => $entryUser->getUsername(),
317
                        'entry'    => LogHelper::getLogDescription($entry),
318
                        'time'     => $entry->getTimestamp(),
319
                        'canedit'  => false,
320
                        'id'       => $entry->getId(),
321
                        'comment'  => $entryComment,
322
                    );
323
                }
324
            }
325
        }
326
327
        $this->addJs("/api.php?action=users&targetVariable=typeaheaddata");
328
329
        $this->assign("requestLogs", $requestLogs);
330
    }
331
332
    /**
333
     * @param Request $request
334
     */
335
    protected function setupUsernameData(Request $request)
336
    {
337
        $blacklistData = $this->getBlacklistHelper()->isBlacklisted($request->getName());
338
339
        $this->assign('requestIsBlacklisted', $blacklistData !== false);
340
        $this->assign('requestBlacklist', $blacklistData);
341
342
        try {
343
            $spoofs = $this->getAntiSpoofProvider()->getSpoofs($request->getName());
344
        }
345
        catch (Exception $ex) {
346
            $spoofs = $ex->getMessage();
347
        }
348
349
        $this->assign("spoofs", $spoofs);
350
    }
351
352
    private function setupCreationTypes(User $user)
353
    {
354
        $this->assign('allowWelcomeSkip', false);
355
        $this->assign('forceWelcomeSkip', false);
356
357
        $database = $this->getDatabase();
358
        $preferenceManager = PreferenceManager::getForCurrent($database);
359
360
        $oauth = new OAuthUserHelper($user, $database, $this->getOAuthProtocolHelper(), $this->getSiteConfiguration());
361
362
        $welcomeTemplate = $preferenceManager->getPreference(PreferenceManager::PREF_WELCOMETEMPLATE);
363
364
        if ($welcomeTemplate != null) {
365
            $this->assign('allowWelcomeSkip', true);
366
367
            if (!$oauth->canWelcome()) {
368
                $this->assign('forceWelcomeSkip', true);
369
            }
370
        }
371
372
        // test credentials
373
        $canManualCreate = $this->barrierTest(PreferenceManager::CREATION_MANUAL, $user, 'RequestCreation');
374
        $canOauthCreate = $this->barrierTest(PreferenceManager::CREATION_OAUTH, $user, 'RequestCreation');
375
        $canBotCreate = $this->barrierTest(PreferenceManager::CREATION_BOT, $user, 'RequestCreation');
376
377
        $this->assign('canManualCreate', $canManualCreate);
378
        $this->assign('canOauthCreate', $canOauthCreate);
379
        $this->assign('canBotCreate', $canBotCreate);
380
381
        // show/hide the type radio buttons
382
        $creationHasChoice = count(array_filter([$canManualCreate, $canOauthCreate, $canBotCreate])) > 1;
383
384
        $creationModePreference = $preferenceManager->getPreference(PreferenceManager::PREF_CREATION_MODE);
385
        if (!$this->barrierTest($creationModePreference, $user, 'RequestCreation')) {
386
            // user is not allowed to use their default. Force a choice.
387
            $creationHasChoice = true;
388
        }
389
390
        $this->assign('creationHasChoice', $creationHasChoice);
391
392
        // determine problems in creation types
393
        $this->assign('botProblem', false);
394
        if ($canBotCreate && $this->getSiteConfiguration()->getCreationBotPassword() === null) {
0 ignored issues
show
introduced by
The condition $this->getSiteConfigurat...nBotPassword() === null is always false.
Loading history...
395
            $this->assign('botProblem', true);
396
        }
397
398
        $this->assign('oauthProblem', false);
399
        if ($canOauthCreate && !$oauth->canCreateAccount()) {
400
            $this->assign('oauthProblem', true);
401
        }
402
    }
403
404
    private function getCreationUrl(): string
405
    {
406
        $template = $this->getSiteConfiguration()->getCreateAccountLink();
407
408
        // FIXME: domains!
409
        /** @var Domain $domain */
410
        $domain = Domain::getById(1, $this->getDatabase());
411
412
        $template = str_replace('{articlePath}', $domain->getWikiArticlePath() , $template);
413
        $template = str_replace('{wikiId}', $domain->getShortName() , $template);
414
415
        return $template;
416
    }
417
}
418