Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 147 |
|
0.00% |
0 / 8 |
CRAP | |
0.00% |
0 / 1 |
ApiWikiLove | |
0.00% |
0 / 147 |
|
0.00% |
0 / 8 |
552 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
execute | |
0.00% |
0 / 66 |
|
0.00% |
0 / 1 |
156 | |||
saveInDb | |
0.00% |
0 / 26 |
|
0.00% |
0 / 1 |
30 | |||
emailUser | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
2 | |||
getAllowedParams | |
0.00% |
0 / 31 |
|
0.00% |
0 / 1 |
2 | |||
needsToken | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
isWriteMode | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getExamplesMessages | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\WikiLove; |
4 | |
5 | use LqtDispatch; |
6 | use MediaWiki\Api\ApiBase; |
7 | use MediaWiki\Api\ApiMain; |
8 | use MediaWiki\Api\ApiMessage; |
9 | use MediaWiki\Context\DerivativeContext; |
10 | use MediaWiki\Deferred\DeferredUpdates; |
11 | use MediaWiki\MediaWikiServices; |
12 | use MediaWiki\Parser\ParserFactory; |
13 | use MediaWiki\Parser\Sanitizer; |
14 | use MediaWiki\Permissions\PermissionManager; |
15 | use MediaWiki\Registration\ExtensionRegistry; |
16 | use MediaWiki\Request\DerivativeRequest; |
17 | use MediaWiki\Title\Title; |
18 | use MediaWiki\User\User; |
19 | use Wikimedia\ParamValidator\ParamValidator; |
20 | use Wikimedia\Rdbms\DBQueryError; |
21 | use Wikimedia\Rdbms\IConnectionProvider; |
22 | |
23 | class ApiWikiLove extends ApiBase { |
24 | private IConnectionProvider $dbProvider; |
25 | private ParserFactory $parserFactory; |
26 | private PermissionManager $permissionManager; |
27 | |
28 | public function __construct( |
29 | ApiMain $main, |
30 | string $action, |
31 | IConnectionProvider $dbProvider, |
32 | ParserFactory $parserFactory, |
33 | PermissionManager $permissionManager |
34 | ) { |
35 | parent::__construct( $main, $action ); |
36 | $this->dbProvider = $dbProvider; |
37 | $this->parserFactory = $parserFactory; |
38 | $this->permissionManager = $permissionManager; |
39 | } |
40 | |
41 | /** @inheritDoc */ |
42 | public function execute() { |
43 | $params = $this->extractRequestParams(); |
44 | |
45 | // In some cases we need the wiki mark-up stripped from the subject |
46 | $strippedSubject = $this->parserFactory->getInstance()->stripSectionName( $params['subject'] ); |
47 | |
48 | $title = Title::newFromText( $params['title'] ); |
49 | if ( $title === null ) { |
50 | $this->dieWithError( [ 'nosuchusershort', $params['title'] ], 'nosuchuser' ); |
51 | } |
52 | |
53 | $talk = Hooks::getUserTalkPage( $this->permissionManager, $title, $this->getUser() ); |
54 | // getUserTalkPage() returns an ApiMessage on error |
55 | if ( $talk instanceof ApiMessage ) { |
56 | $this->dieWithError( $talk ); |
57 | } |
58 | |
59 | if ( $this->getConfig()->get( 'WikiLoveLogging' ) ) { |
60 | $this->saveInDb( $talk, $params['subject'], $params['message'], $params['type'], |
61 | isset( $params['email'] ) ? 1 : 0 ); |
62 | } |
63 | |
64 | // Create edit summary |
65 | $summary = $this->msg( 'wikilove-summary', $strippedSubject )->inContentLanguage() |
66 | ->text(); |
67 | |
68 | $extReg = ExtensionRegistry::getInstance(); |
69 | |
70 | // If LQT is installed and enabled, use it. |
71 | if ( $extReg->isLoaded( 'Liquid Threads' ) && LqtDispatch::isLqtPage( $talk ) ) { |
72 | $apiParamArray = [ |
73 | 'action' => 'threadaction', |
74 | 'threadaction' => 'newthread', |
75 | 'talkpage' => $talk->getFullText(), |
76 | 'subject' => $params['subject'], |
77 | 'reason' => $summary, |
78 | 'text' => $params['text'], |
79 | 'token' => $params['token'] |
80 | ]; |
81 | // If Flow is installed and enabled, use it. |
82 | } elseif ( $extReg->isLoaded( 'Flow' ) && $talk->hasContentModel( CONTENT_MODEL_FLOW_BOARD ) ) { |
83 | $apiParamArray = [ |
84 | 'action' => 'flow', |
85 | 'submodule' => 'new-topic', |
86 | 'page' => $talk->getFullText(), |
87 | 'nttopic' => $params['subject'], |
88 | 'ntcontent' => $params['text'], |
89 | 'token' => $params['token'] |
90 | ]; |
91 | } else { |
92 | // Requires MediaWiki 1.19 or later |
93 | $apiParamArray = [ |
94 | 'action' => 'edit', |
95 | 'title' => $talk->getFullText(), |
96 | 'section' => 'new', |
97 | 'sectiontitle' => $params['subject'], |
98 | 'text' => $params['text'], |
99 | 'token' => $params['token'], |
100 | 'summary' => $summary, |
101 | 'tags' => implode( '|', $params['tags'] ?? [] ), |
102 | 'notminor' => true |
103 | ]; |
104 | } |
105 | |
106 | $api = new ApiMain( |
107 | new DerivativeRequest( |
108 | $this->getRequest(), |
109 | $apiParamArray, |
110 | /* $wasPosted */ true |
111 | ), |
112 | /* $enableWrite */ true |
113 | ); |
114 | |
115 | $api->execute(); |
116 | |
117 | $result = $api->getResult()->getResultData(); |
118 | if ( isset( $result['edit'] ) && $result['edit']['result'] === "Success" ) { |
119 | $revId = $result['edit']['newrevid']; |
120 | DeferredUpdates::addCallableUpdate( static function () use ( $revId ) { |
121 | MediaWikiServices::getInstance()->getChangeTagsStore() |
122 | ->addTags( [ 'wikilove' ], null, $revId ); |
123 | } ); |
124 | } |
125 | |
126 | if ( isset( $params['email'] ) ) { |
127 | $this->emailUser( $talk, $strippedSubject, $params['email'], $params['token'] ); |
128 | } |
129 | |
130 | $this->getResult()->addValue( 'redirect', 'pageName', $talk->getPrefixedDBkey() ); |
131 | $this->getResult()->addValue( 'redirect', 'fragment', |
132 | Sanitizer::escapeIdForLink( $strippedSubject ) |
133 | ); |
134 | // note that we cannot use Title::makeTitle here as it doesn't sanitize the fragment |
135 | } |
136 | |
137 | private function saveInDb( Title $talk, string $subject, string $message, string $type, int $email ): void { |
138 | $dbw = $this->dbProvider->getPrimaryDatabase(); |
139 | $receiver = User::newFromName( $talk->getSubjectPage()->getBaseText() ); |
140 | if ( $receiver === false || $receiver->isAnon() || $receiver->isTemp() ) { |
141 | $this->addWarning( 'apiwarn-wikilove-ignoringunregistered' ); |
142 | return; |
143 | } |
144 | |
145 | $user = $this->getUser(); |
146 | $values = [ |
147 | 'wll_timestamp' => $dbw->timestamp(), |
148 | 'wll_sender' => $user->getId(), |
149 | 'wll_sender_editcount' => $user->getEditCount(), |
150 | 'wll_sender_registration' => $user->getRegistration(), |
151 | 'wll_receiver' => $receiver->getId(), |
152 | 'wll_receiver_editcount' => $receiver->getEditCount(), |
153 | 'wll_receiver_registration' => $receiver->getRegistration(), |
154 | 'wll_type' => $type, |
155 | 'wll_subject' => $subject, |
156 | 'wll_message' => $message, |
157 | 'wll_email' => $email, |
158 | ]; |
159 | |
160 | try { |
161 | $dbw->newInsertQueryBuilder() |
162 | ->insertInto( 'wikilove_log' ) |
163 | ->row( $values ) |
164 | ->caller( __METHOD__ ) |
165 | ->execute(); |
166 | } catch ( DBQueryError $dbqe ) { |
167 | $this->addWarning( 'Action was not logged' ); |
168 | } |
169 | } |
170 | |
171 | private function emailUser( Title $talk, string $subject, string $text, string $token ): void { |
172 | $context = new DerivativeContext( $this->getContext() ); |
173 | $context->setRequest( new DerivativeRequest( |
174 | $this->getRequest(), |
175 | [ |
176 | 'action' => 'emailuser', |
177 | 'target' => User::newFromName( $talk->getSubjectPage()->getBaseText() )->getName(), |
178 | 'subject' => $subject, |
179 | 'text' => $text, |
180 | 'token' => $token |
181 | ], |
182 | true |
183 | ) ); |
184 | $api = new ApiMain( $context, true ); |
185 | $api->execute(); |
186 | } |
187 | |
188 | /** @inheritDoc */ |
189 | public function getAllowedParams() { |
190 | return [ |
191 | 'title' => [ |
192 | ParamValidator::PARAM_TYPE => 'string', |
193 | ParamValidator::PARAM_REQUIRED => true, |
194 | ], |
195 | 'text' => [ |
196 | ParamValidator::PARAM_TYPE => 'string', |
197 | ParamValidator::PARAM_REQUIRED => true, |
198 | ], |
199 | 'message' => [ |
200 | ParamValidator::PARAM_TYPE => 'string', |
201 | ], |
202 | 'token' => [ |
203 | ParamValidator::PARAM_TYPE => 'string', |
204 | ParamValidator::PARAM_REQUIRED => true, |
205 | ], |
206 | 'subject' => [ |
207 | ParamValidator::PARAM_TYPE => 'string', |
208 | ParamValidator::PARAM_REQUIRED => true, |
209 | ], |
210 | 'type' => [ |
211 | ParamValidator::PARAM_TYPE => 'string', |
212 | ], |
213 | 'email' => [ |
214 | ParamValidator::PARAM_TYPE => 'string', |
215 | ], |
216 | 'tags' => [ |
217 | ParamValidator::PARAM_TYPE => 'tags', |
218 | ParamValidator::PARAM_ISMULTI => true, |
219 | ], |
220 | ]; |
221 | } |
222 | |
223 | /** @inheritDoc */ |
224 | public function needsToken() { |
225 | return 'csrf'; |
226 | } |
227 | |
228 | /** @inheritDoc */ |
229 | public function isWriteMode() { |
230 | return true; |
231 | } |
232 | |
233 | /** @inheritDoc */ |
234 | protected function getExamplesMessages() { |
235 | return [ |
236 | 'action=wikilove&title=User:Dummy&text=Love&subject=Hi&token=123ABC' |
237 | => 'apihelp-wikilove-example-1', |
238 | ]; |
239 | } |
240 | } |