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 ) ) ) { // Revision ID is not an integer.
79            $this->id = '0';
80            return;
81        }
82
83        $this->id = $par;
84    }
85
86    /**
87     * HTMLForm fields
88     * @return string[][]
89     */
90    protected function getFormFields(): array {
91        return [
92            'id' => [
93                'id' => 'mw-thanks-form-id',
94                'name' => 'id',
95                'type' => 'hidden',
96                'default' => $this->id ?? ''
97            ],
98            'type' => [
99                'id' => 'mw-thanks-form-type',
100                'name' => 'type',
101                'type' => 'hidden',
102                'default' => $this->type ?? '',
103            ],
104        ];
105    }
106
107    /**
108     * Return the confirmation or error message.
109     */
110    protected function preHtml(): string {
111        if ( $this->type === null ) {
112            $msgKey = 'thanks-error-no-id-specified';
113        } elseif ( $this->type === 'rev' && $this->id === '0' ) {
114            $msgKey = 'thanks-error-invalidrevision';
115        } elseif ( $this->type === 'log' && $this->id === '0' ) {
116            $msgKey = 'thanks-error-invalid-log-id';
117        } elseif ( $this->type === 'flow' ) {
118            $msgKey = 'flow-thanks-confirmation-special';
119        } else {
120            // The following messages are used here
121            // * thanks-confirmation-special-rev
122            // * thanks-confirmation-special-log
123            $msgKey = 'thanks-confirmation-special-' . $this->type;
124        }
125        return '<p>' . $this->msg( $msgKey )->escaped() . '</p>';
126    }
127
128    /**
129     * Format the submission form.
130     * @param HTMLForm $form The form object to modify.
131     */
132    protected function alterForm( HTMLForm $form ) {
133        if ( $this->type === null
134            || ( in_array( $this->type, [ 'rev', 'log', ] ) && $this->id === '0' )
135        ) {
136            $form->suppressDefaultSubmit( true );
137        } else {
138            $form->setSubmitText( $this->msg( 'thanks-submit' )->escaped() );
139        }
140    }
141
142    protected function getDisplayFormat(): string {
143        return 'ooui';
144    }
145
146    /**
147     * Call the API internally.
148     * @param string[] $data The form data.
149     */
150    public function onSubmit( array $data ): Status {
151        if ( !isset( $data['id'] ) ) {
152            return Status::newFatal( 'thanks-error-invalidrevision' );
153        }
154
155        if ( in_array( $this->type, [ 'rev', 'log' ] ) ) {
156            $requestData = [
157                'action' => 'thank',
158                $this->type => (int)$data['id'],
159                'source' => 'specialpage',
160                'token' => $this->getOutput()->getCsrfTokenSet()->getToken(),
161            ];
162        } else {
163            $requestData = [
164                'action' => 'flowthank',
165                'postid' => $data['id'],
166                'token' => $this->getOutput()->getCsrfTokenSet()->getToken(),
167            ];
168        }
169
170        $request = new DerivativeRequest(
171            $this->getRequest(),
172            $requestData,
173            true // posted
174        );
175
176        $api = new ApiMain(
177            $request,
178            true // enable write mode
179        );
180
181        try {
182            $api->execute();
183        } catch ( ApiUsageException $e ) {
184            return Status::wrap( $e->getStatusValue() );
185        }
186
187        $this->result = $api->getResult()->getResultData( [ 'result' ] );
188        return Status::newGood();
189    }
190
191    /**
192     * Display a message to the user.
193     */
194    public function onSuccess() {
195        $sender = $this->getUser();
196        $recipient = $this->userFactory->newFromName( $this->result['recipient'], UserRigorOptions::RIGOR_NONE );
197        $link = Linker::userLink( $recipient->getId(), $recipient->getName() );
198
199        if ( in_array( $this->type, [ 'rev', 'log' ] ) ) {
200            $msgKey = 'thanks-thanked-notice';
201        } else {
202            $msgKey = 'flow-thanks-thanked-notice';
203        }
204        $msg = $this->msg( $msgKey )
205            ->rawParams( $link )
206            ->params( $recipient->getName(), $sender->getName() );
207        $this->getOutput()->addHTML( $msg->parse() );
208    }
209
210    public function isListed(): bool {
211        return false;
212    }
213}