Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 95
0.00% covered (danger)
0.00%
0 / 10
CRAP
0.00% covered (danger)
0.00%
0 / 1
SpecialThanks
0.00% covered (danger)
0.00%
0 / 95
0.00% covered (danger)
0.00%
0 / 10
1122
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 doesWrites
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setParameter
0.00% covered (danger)
0.00%
0 / 23
0.00% covered (danger)
0.00%
0 / 1
132
 getFormFields
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
2
 preHtml
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
56
 alterForm
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
20
 getDisplayFormat
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 onSubmit
0.00% covered (danger)
0.00%
0 / 28
0.00% covered (danger)
0.00%
0 / 1
20
 onSuccess
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
6
 isListed
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace MediaWiki\Extension\Thanks;
4
5use MediaWiki\Api\ApiMain;
6use MediaWiki\Api\ApiUsageException;
7use MediaWiki\HTMLForm\HTMLForm;
8use MediaWiki\Linker\Linker;
9use MediaWiki\Request\DerivativeRequest;
10use MediaWiki\SpecialPage\FormSpecialPage;
11use MediaWiki\Status\Status;
12use MediaWiki\User\UserFactory;
13use MediaWiki\User\UserRigorOptions;
14
15class SpecialThanks extends FormSpecialPage {
16
17    /**
18     * API result
19     */
20    protected array $result;
21
22    /**
23     * 'rev' for revision, 'log' for log entry, or 'flow' for Flow comment,
24     * null if no ID is specified
25     */
26    protected ?string $type;
27
28    /**
29     * Revision or Log ID ('0' = invalid) or Flow UUID
30     */
31    protected ?string $id;
32
33    private UserFactory $userFactory;
34
35    public function __construct( UserFactory $userFactory ) {
36        parent::__construct( 'Thanks' );
37        $this->userFactory = $userFactory;
38        $this->id = null;
39    }
40
41    public function doesWrites(): bool {
42        return true;
43    }
44
45    /**
46     * Set the type and ID or UUID of the request.
47     * @param string $par The subpage name.
48     */
49    protected function setParameter( $par ) {
50        if ( $par === null || $par === '' ) {
51            $this->type = null;
52            return;
53        }
54
55        $tokens = explode( '/', $par );
56        if ( $tokens[0] === 'Flow' ) {
57            if ( count( $tokens ) === 1 || $tokens[1] === '' ) {
58                $this->type = null;
59                return;
60            }
61            $this->type = 'flow';
62            $this->id = $tokens[1];
63            return;
64        }
65
66        if ( strtolower( $tokens[0] ) === 'log' ) {
67            $this->type = 'log';
68            // Make sure there's a numeric ID specified as the subpage.
69            if ( count( $tokens ) === 1 || $tokens[1] === '' || !( ctype_digit( $tokens[1] ) ) ) {
70                $this->id = '0';
71                return;
72            }
73            $this->id = $tokens[1];
74            return;
75        }
76
77        $this->type = 'rev';
78        if ( !( ctype_digit( $par ) ) ) {
79            // Revision ID is not an integer.
80            $this->id = '0';
81            return;
82        }
83
84        $this->id = $par;
85    }
86
87    /**
88     * HTMLForm fields
89     * @return string[][]
90     */
91    protected function getFormFields(): array {
92        return [
93            'id' => [
94                'id' => 'mw-thanks-form-id',
95                'name' => 'id',
96                'type' => 'hidden',
97                'default' => $this->id ?? '',
98            ],
99            'type' => [
100                'id' => 'mw-thanks-form-type',
101                'name' => 'type',
102                'type' => 'hidden',
103                'default' => $this->type ?? '',
104            ],
105        ];
106    }
107
108    /**
109     * Return the confirmation or error message.
110     */
111    protected function preHtml(): string {
112        if ( $this->type === null ) {
113            $msgKey = 'thanks-error-no-id-specified';
114        } elseif ( $this->type === 'rev' && $this->id === '0' ) {
115            $msgKey = 'thanks-error-invalidrevision';
116        } elseif ( $this->type === 'log' && $this->id === '0' ) {
117            $msgKey = 'thanks-error-invalid-log-id';
118        } elseif ( $this->type === 'flow' ) {
119            $msgKey = 'flow-thanks-confirmation-special';
120        } else {
121            // The following messages are used here
122            // * thanks-confirmation-special-rev
123            // * thanks-confirmation-special-log
124            $msgKey = 'thanks-confirmation-special-' . $this->type;
125        }
126        return '<p>' . $this->msg( $msgKey )->escaped() . '</p>';
127    }
128
129    /**
130     * Format the submission form.
131     * @param HTMLForm $form The form object to modify.
132     */
133    protected function alterForm( HTMLForm $form ) {
134        if ( $this->type === null
135            || ( in_array( $this->type, [ 'rev', 'log' ] ) && $this->id === '0' )
136        ) {
137            $form->suppressDefaultSubmit( true );
138        } else {
139            $form->setSubmitText( $this->msg( 'thanks-submit' )->escaped() );
140        }
141    }
142
143    protected function getDisplayFormat(): string {
144        return 'ooui';
145    }
146
147    /**
148     * Call the API internally.
149     * @param string[] $data The form data.
150     */
151    public function onSubmit( array $data ): Status {
152        if ( !isset( $data['id'] ) ) {
153            return Status::newFatal( 'thanks-error-invalidrevision' );
154        }
155
156        if ( in_array( $this->type, [ 'rev', 'log' ] ) ) {
157            $requestData = [
158                'action' => 'thank',
159                $this->type => (int)$data['id'],
160                'source' => 'specialpage',
161                'token' => $this->getOutput()->getCsrfTokenSet()->getToken(),
162            ];
163        } else {
164            $requestData = [
165                'action' => 'flowthank',
166                'postid' => $data['id'],
167                'token' => $this->getOutput()->getCsrfTokenSet()->getToken(),
168            ];
169        }
170
171        $request = new DerivativeRequest(
172            $this->getRequest(),
173            $requestData,
174            true
175        );
176
177        $api = new ApiMain(
178            $request,
179            true
180        );
181
182        try {
183            $api->execute();
184        } catch ( ApiUsageException $e ) {
185            return Status::wrap( $e->getStatusValue() );
186        }
187
188        $this->result = $api->getResult()->getResultData( [ 'result' ] );
189        return Status::newGood();
190    }
191
192    /**
193     * Display a message to the user.
194     */
195    public function onSuccess() {
196        $sender = $this->getUser();
197        $recipient = $this->userFactory->newFromName( $this->result['recipient'], UserRigorOptions::RIGOR_NONE );
198        $link = Linker::userLink( $recipient->getId(), $recipient->getName() );
199
200        if ( in_array( $this->type, [ 'rev', 'log' ] ) ) {
201            $msgKey = 'thanks-thanked-notice';
202        } else {
203            $msgKey = 'flow-thanks-thanked-notice';
204        }
205        $msg = $this->msg( $msgKey )
206            ->rawParams( $link )
207            ->params( $recipient->getName(), $sender->getName() );
208        $this->getOutput()->addHTML( $msg->parse() );
209    }
210
211    public function isListed(): bool {
212        return false;
213    }
214}