Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
TranslatorSandboxActionApi.php
1<?php
2declare( strict_types = 1 );
3
4namespace MediaWiki\Extension\Translate\TranslatorSandbox;
5
6use ApiBase;
7use ApiMain;
8use CommentStoreComment;
9use ContentHandler;
10use FormatJson;
11use ManualLogEntry;
12use MediaWiki\Config\ServiceOptions;
13use MediaWiki\Page\WikiPageFactory;
14use MediaWiki\Revision\SlotRecord;
15use MediaWiki\User\UserFactory;
16use MediaWiki\User\UserNameUtils;
17use MediaWiki\User\UserOptionsLookup;
18use MediaWiki\User\UserOptionsManager;
19use RuntimeException;
20use Sanitizer;
22use User;
23use Wikimedia\ParamValidator\ParamValidator;
24
31class TranslatorSandboxActionApi extends ApiBase {
33 private $userFactory;
35 private $userNameUtils;
37 private $userOptionsManager;
39 private $wikiPageFactory;
41 private $userOptionsLookup;
43 private $options;
44
45 public const CONSTRUCTOR_OPTIONS = [
46 'TranslateUseSandbox',
47 ];
48
49 public function __construct(
50 ApiMain $mainModule,
51 string $moduleName,
52 UserFactory $userFactory,
53 UserNameUtils $userNameUtils,
54 UserOptionsManager $userOptionsManager,
55 WikiPageFactory $wikiPageFactory,
56 UserOptionsLookup $userOptionsLookup,
57 ServiceOptions $options
58 ) {
59 parent::__construct( $mainModule, $moduleName );
60 $this->userFactory = $userFactory;
61 $this->userNameUtils = $userNameUtils;
62 $this->userOptionsManager = $userOptionsManager;
63 $this->wikiPageFactory = $wikiPageFactory;
64 $this->userOptionsLookup = $userOptionsLookup;
65 $this->options = $options;
66 }
67
68 public function execute(): void {
69 if ( !$this->options->get( 'TranslateUseSandbox' ) ) {
70 $this->dieWithError( 'apierror-translate-sandboxdisabled', 'sandboxdisabled' );
71 }
72
73 $params = $this->extractRequestParams();
74 switch ( $params['do'] ) {
75 case 'create':
76 $this->doCreate();
77 break;
78 case 'delete':
79 $this->doDelete();
80 break;
81 case 'promote':
82 $this->doPromote();
83 break;
84 case 'remind':
85 $this->doRemind();
86 break;
87 default:
88 $this->dieWithError( [ 'apierror-badparameter', 'do' ] );
89 }
90 }
91
92 private function doCreate(): void {
93 $params = $this->extractRequestParams();
94
95 // Do validations
96 foreach ( explode( '|', 'username|password|email' ) as $field ) {
97 if ( !isset( $params[$field] ) ) {
98 $this->dieWithError( [ 'apierror-missingparam', $field ], 'missingparam' );
99 }
100 }
101
102 $username = $params['username'];
103
104 $canonicalName = $this->userNameUtils->getCanonical( $username, UserNameUtils::RIGOR_CREATABLE );
105
106 if ( $canonicalName === false ) {
107 $this->dieWithError( 'noname', 'invalidusername' );
108 }
109
110 $user = $this->userFactory->newFromName( $username );
111 if ( $user->getId() !== 0 ) {
112 $this->dieWithError( 'userexists', 'nonfreeusername' );
113 }
114
115 $password = $params['password'];
116 $passwordValidityStatus = $user->checkPasswordValidity( $password );
117 if ( !$passwordValidityStatus->isGood() ) {
118 $this->dieStatus( $passwordValidityStatus );
119 }
120
121 $email = $params['email'];
122 if ( !Sanitizer::validateEmail( $email ) ) {
123 $this->dieWithError( 'invalidemailaddress', 'invalidemail' );
124 }
125
126 try {
127 $user = TranslateSandbox::addUser( $username, $email, $password );
128 } catch ( RuntimeException $e ) {
129 // Do not log this error as it might leak private information
130 if ( $e->getCode() === TranslateSandbox::USER_CREATION_FAILURE ) {
131 $this->dieWithError( 'apierror-translate-sandbox-user-add' );
132 }
133
134 throw $e;
135 }
136
137 $output = [ 'user' => [
138 'name' => $user->getName(),
139 'id' => $user->getId(),
140 ] ];
141
142 $this->userOptionsManager->setOption( $user, 'language', $this->getContext()->getLanguage()->getCode() );
143 $this->userOptionsManager->saveOptions( $user );
144
145 $this->getResult()->addValue( null, $this->getModuleName(), $output );
146 }
147
148 private function doDelete(): void {
149 $this->checkUserRightsAny( 'translate-sandboxmanage' );
150
151 $params = $this->extractRequestParams();
152
153 foreach ( $params['userid'] as $userId ) {
154 $user = $this->userFactory->newFromId( $userId );
155 $userpage = $user->getUserPage();
156
157 TranslateSandbox::sendEmail( $this->getUser(), $user, 'rejection' );
158
159 try {
161 } catch ( UserNotSandboxedException $e ) {
162 $this->dieWithError(
163 [ 'apierror-translate-sandbox-invalidparam', wfEscapeWikiText( $e->getMessage() ) ],
164 'invalidparam'
165 );
166 }
167
168 $logEntry = new ManualLogEntry( 'translatorsandbox', 'rejected' );
169 $logEntry->setPerformer( $this->getUser() );
170 $logEntry->setTarget( $userpage );
171 $logid = $logEntry->insert();
172 $logEntry->publish( $logid );
173 }
174 }
175
176 private function doPromote(): void {
177 $this->checkUserRightsAny( 'translate-sandboxmanage' );
178
179 $params = $this->extractRequestParams();
180
181 foreach ( $params['userid'] as $userId ) {
182 $user = $this->userFactory->newFromId( $userId );
183
184 try {
186 } catch ( UserNotSandboxedException $e ) {
187 $this->dieWithError(
188 [ 'apierror-translate-sandbox-invalidparam', wfEscapeWikiText( $e->getMessage() ) ],
189 'invalidparam'
190 );
191 }
192
193 TranslateSandbox::sendEmail( $this->getUser(), $user, 'promotion' );
194
195 $logEntry = new ManualLogEntry( 'translatorsandbox', 'promoted' );
196 $logEntry->setPerformer( $this->getUser() );
197 $logEntry->setTarget( $user->getUserPage() );
198 $logEntry->setParameters( [
199 '4::userid' => $user->getId(),
200 ] );
201 $logid = $logEntry->insert();
202 $logEntry->publish( $logid );
203
204 $this->createUserPage( $user );
205 }
206 }
207
208 private function doRemind(): void {
209 $params = $this->extractRequestParams();
210
211 foreach ( $params['userid'] as $userId ) {
212 $target = $this->userFactory->newFromId( $userId );
213
214 try {
215 TranslateSandbox::sendEmail( $this->getUser(), $target, 'reminder' );
216 } catch ( UserNotSandboxedException $e ) {
217 $this->dieWithError(
218 [ 'apierror-translate-sandbox-invalidparam', wfEscapeWikiText( $e->getMessage() ) ],
219 'invalidparam'
220 );
221 }
222 }
223 }
224
226 private function createUserPage( User $user ): void {
227 $userpage = $user->getUserPage();
228
229 if ( $userpage->exists() ) {
230 return;
231 }
232
233 $languagePrefs = FormatJson::decode(
234 $this->userOptionsLookup->getOption( $user, 'translate-sandbox' ),
235 true
236 );
237 $languages = implode( '|', $languagePrefs[ 'languages' ] ?? [] );
238 $babeltext = "{{#babel:$languages}}";
239 $summary = $this->msg( 'tsb-create-user-page' )->inContentLanguage()->text();
240
241 $page = $this->wikiPageFactory->newFromTitle( $userpage );
242 $content = ContentHandler::makeContent( $babeltext, $userpage );
243
244 $page->newPageUpdater( $user )
245 ->setContent( SlotRecord::MAIN, $content )
246 ->saveRevision( CommentStoreComment::newUnsavedComment( trim( $summary ) ), EDIT_NEW );
247 }
248
249 public function isWriteMode(): bool {
250 return true;
251 }
252
253 public function needsToken(): string {
254 return 'csrf';
255 }
256
257 protected function getAllowedParams(): array {
258 return [
259 'do' => [
260 ParamValidator::PARAM_TYPE => [ 'create', 'delete', 'promote', 'remind' ],
261 ParamValidator::PARAM_REQUIRED => true,
262 ],
263 'userid' => [
264 ParamValidator::PARAM_TYPE => 'integer',
265 ParamValidator::PARAM_DEFAULT => 0,
266 ParamValidator::PARAM_ISMULTI => true,
267 ],
268 'token' => [
269 ParamValidator::PARAM_TYPE => 'string',
270 ParamValidator::PARAM_REQUIRED => true,
271 ],
272 'username' => [ ParamValidator::PARAM_TYPE => 'string' ],
273 'password' => [ ParamValidator::PARAM_TYPE => 'string' ],
274 'email' => [ ParamValidator::PARAM_TYPE => 'string' ],
275 ];
276 }
277}
Utility class for the sandbox feature of Translate.
static sendEmail(User $sender, User $target, $type)
Sends a reminder to the user.
static addUser( $name, $email, $password)
Adds a new user without doing much validation.
static promoteUser(User $user)
Removes the user from the sandbox.
static deleteUser(User $user, $force='')
Deletes a sandboxed user without doing much validation.
const USER_CREATION_FAILURE
Custom exception code used when user creation fails in order to differentiate between other exception...