Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 149 |
|
0.00% |
0 / 13 |
CRAP | |
0.00% |
0 / 1 |
SpecialPageLanguage | |
0.00% |
0 / 148 |
|
0.00% |
0 / 13 |
1056 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
2 | |||
doesWrites | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
preHtml | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getFormFields | |
0.00% |
0 / 45 |
|
0.00% |
0 / 1 |
56 | |||
postHtml | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
getDisplayFormat | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
alterForm | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
onSubmit | |
0.00% |
0 / 22 |
|
0.00% |
0 / 1 |
30 | |||
changePageLanguage | |
0.00% |
0 / 59 |
|
0.00% |
0 / 1 |
90 | |||
onSuccess | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
showLogFragment | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
2 | |||
prefixSearchSubpages | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getGroupName | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | /** |
3 | * This program is free software; you can redistribute it and/or modify |
4 | * it under the terms of the GNU General Public License as published by |
5 | * the Free Software Foundation; either version 2 of the License, or |
6 | * (at your option) any later version. |
7 | * |
8 | * This program is distributed in the hope that it will be useful, |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | * GNU General Public License for more details. |
12 | * |
13 | * You should have received a copy of the GNU General Public License along |
14 | * with this program; if not, write to the Free Software Foundation, Inc., |
15 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
16 | * http://www.gnu.org/copyleft/gpl.html |
17 | * |
18 | * @file |
19 | */ |
20 | |
21 | namespace MediaWiki\Specials; |
22 | |
23 | use LogEventsList; |
24 | use LogPage; |
25 | use ManualLogEntry; |
26 | use MediaWiki\Api\ApiMessage; |
27 | use MediaWiki\Content\IContentHandlerFactory; |
28 | use MediaWiki\Context\IContextSource; |
29 | use MediaWiki\HTMLForm\HTMLForm; |
30 | use MediaWiki\Language\RawMessage; |
31 | use MediaWiki\Languages\LanguageNameUtils; |
32 | use MediaWiki\MainConfigNames; |
33 | use MediaWiki\MediaWikiServices; |
34 | use MediaWiki\Permissions\PermissionStatus; |
35 | use MediaWiki\SpecialPage\FormSpecialPage; |
36 | use MediaWiki\Status\Status; |
37 | use MediaWiki\Title\MalformedTitleException; |
38 | use MediaWiki\Title\Title; |
39 | use MediaWiki\Xml\Xml; |
40 | use SearchEngineFactory; |
41 | use Wikimedia\Rdbms\IConnectionProvider; |
42 | use Wikimedia\Rdbms\IDatabase; |
43 | |
44 | /** |
45 | * Special page for changing the content language of a page |
46 | * |
47 | * @ingroup SpecialPage |
48 | * @author Kunal Grover |
49 | * @since 1.24 |
50 | */ |
51 | class SpecialPageLanguage extends FormSpecialPage { |
52 | /** |
53 | * @var string URL to go to if language change successful |
54 | */ |
55 | private $goToUrl; |
56 | |
57 | private IContentHandlerFactory $contentHandlerFactory; |
58 | private LanguageNameUtils $languageNameUtils; |
59 | private IConnectionProvider $dbProvider; |
60 | private SearchEngineFactory $searchEngineFactory; |
61 | |
62 | /** |
63 | * @param IContentHandlerFactory $contentHandlerFactory |
64 | * @param LanguageNameUtils $languageNameUtils |
65 | * @param IConnectionProvider $dbProvider |
66 | * @param SearchEngineFactory $searchEngineFactory |
67 | */ |
68 | public function __construct( |
69 | IContentHandlerFactory $contentHandlerFactory, |
70 | LanguageNameUtils $languageNameUtils, |
71 | IConnectionProvider $dbProvider, |
72 | SearchEngineFactory $searchEngineFactory |
73 | ) { |
74 | parent::__construct( 'PageLanguage', 'pagelang' ); |
75 | $this->contentHandlerFactory = $contentHandlerFactory; |
76 | $this->languageNameUtils = $languageNameUtils; |
77 | $this->dbProvider = $dbProvider; |
78 | $this->searchEngineFactory = $searchEngineFactory; |
79 | } |
80 | |
81 | public function doesWrites() { |
82 | return true; |
83 | } |
84 | |
85 | protected function preHtml() { |
86 | $this->getOutput()->addModules( 'mediawiki.misc-authed-ooui' ); |
87 | return parent::preHtml(); |
88 | } |
89 | |
90 | protected function getFormFields() { |
91 | // Get default from the subpage of Special page |
92 | $defaultName = $this->par; |
93 | $title = $defaultName ? Title::newFromText( $defaultName ) : null; |
94 | if ( $title ) { |
95 | $defaultPageLanguage = $this->contentHandlerFactory->getContentHandler( $title->getContentModel() ) |
96 | ->getPageLanguage( $title ); |
97 | |
98 | $hasCustomLanguageSet = !$defaultPageLanguage->equals( $title->getPageLanguage() ); |
99 | } else { |
100 | $hasCustomLanguageSet = false; |
101 | } |
102 | |
103 | $page = []; |
104 | $page['pagename'] = [ |
105 | 'type' => 'title', |
106 | 'label-message' => 'pagelang-name', |
107 | 'default' => $title ? $title->getPrefixedText() : $defaultName, |
108 | 'autofocus' => $defaultName === null, |
109 | 'exists' => true, |
110 | ]; |
111 | |
112 | // Options for whether to use the default language or select language |
113 | $selectoptions = [ |
114 | (string)$this->msg( 'pagelang-use-default' )->escaped() => 1, |
115 | (string)$this->msg( 'pagelang-select-lang' )->escaped() => 2, |
116 | ]; |
117 | $page['selectoptions'] = [ |
118 | 'id' => 'mw-pl-options', |
119 | 'type' => 'radio', |
120 | 'options' => $selectoptions, |
121 | 'default' => $hasCustomLanguageSet ? 2 : 1 |
122 | ]; |
123 | |
124 | // Building a language selector |
125 | $userLang = $this->getLanguage()->getCode(); |
126 | $languages = $this->languageNameUtils->getLanguageNames( $userLang, LanguageNameUtils::SUPPORTED ); |
127 | $options = []; |
128 | foreach ( $languages as $code => $name ) { |
129 | $options["$code - $name"] = $code; |
130 | } |
131 | |
132 | $page['language'] = [ |
133 | 'id' => 'mw-pl-languageselector', |
134 | 'cssclass' => 'mw-languageselector', |
135 | 'type' => 'select', |
136 | 'options' => $options, |
137 | 'label-message' => 'pagelang-language', |
138 | 'default' => $title ? |
139 | $title->getPageLanguage()->getCode() : |
140 | $this->getConfig()->get( MainConfigNames::LanguageCode ), |
141 | ]; |
142 | |
143 | // Allow user to enter a comment explaining the change |
144 | $page['reason'] = [ |
145 | 'type' => 'text', |
146 | 'label-message' => 'pagelang-reason' |
147 | ]; |
148 | |
149 | return $page; |
150 | } |
151 | |
152 | protected function postHtml() { |
153 | if ( $this->par ) { |
154 | return $this->showLogFragment( $this->par ); |
155 | } |
156 | return ''; |
157 | } |
158 | |
159 | protected function getDisplayFormat() { |
160 | return 'ooui'; |
161 | } |
162 | |
163 | public function alterForm( HTMLForm $form ) { |
164 | $this->getHookRunner()->onLanguageSelector( $this->getOutput(), 'mw-languageselector' ); |
165 | $form->setSubmitTextMsg( 'pagelang-submit' ); |
166 | } |
167 | |
168 | /** |
169 | * @param array $data |
170 | * @return Status |
171 | */ |
172 | public function onSubmit( array $data ) { |
173 | $pageName = $data['pagename']; |
174 | |
175 | // Check if user wants to use default language |
176 | if ( $data['selectoptions'] == 1 ) { |
177 | $newLanguage = 'default'; |
178 | } else { |
179 | $newLanguage = $data['language']; |
180 | } |
181 | |
182 | try { |
183 | $title = Title::newFromTextThrow( $pageName ); |
184 | } catch ( MalformedTitleException $ex ) { |
185 | return Status::newFatal( $ex->getMessageObject() ); |
186 | } |
187 | |
188 | // Check permissions and make sure the user has permission to edit the page |
189 | $status = PermissionStatus::newEmpty(); |
190 | if ( !$this->getAuthority()->authorizeWrite( 'edit', $title, $status ) ) { |
191 | $wikitext = $this->getOutput()->formatPermissionStatus( $status ); |
192 | // Hack to get our wikitext parsed |
193 | return Status::newFatal( new RawMessage( '$1', [ $wikitext ] ) ); |
194 | } |
195 | |
196 | // Url to redirect to after the operation |
197 | $this->goToUrl = $title->getFullUrlForRedirect( |
198 | $title->isRedirect() ? [ 'redirect' => 'no' ] : [] |
199 | ); |
200 | |
201 | return self::changePageLanguage( |
202 | $this->getContext(), |
203 | $title, |
204 | $newLanguage, |
205 | $data['reason'] ?? '', |
206 | [], |
207 | $this->dbProvider->getPrimaryDatabase() |
208 | ); |
209 | } |
210 | |
211 | /** |
212 | * @since 1.36 Added $dbw parameter |
213 | * |
214 | * @param IContextSource $context |
215 | * @param Title $title |
216 | * @param string $newLanguage Language code |
217 | * @param string $reason Reason for the change |
218 | * @param string[] $tags Change tags to apply to the log entry |
219 | * @param IDatabase|null $dbw |
220 | * @return Status |
221 | */ |
222 | public static function changePageLanguage( IContextSource $context, Title $title, |
223 | $newLanguage, $reason = "", array $tags = [], ?IDatabase $dbw = null ) { |
224 | // Get the default language for the wiki |
225 | $defLang = $context->getConfig()->get( MainConfigNames::LanguageCode ); |
226 | |
227 | $pageId = $title->getArticleID(); |
228 | |
229 | // Check if article exists |
230 | if ( !$pageId ) { |
231 | return Status::newFatal( |
232 | 'pagelang-nonexistent-page', |
233 | wfEscapeWikiText( $title->getPrefixedText() ) |
234 | ); |
235 | } |
236 | |
237 | // Load the page language from DB |
238 | $dbw ??= MediaWikiServices::getInstance()->getConnectionProvider()->getPrimaryDatabase(); |
239 | $oldLanguage = $dbw->newSelectQueryBuilder() |
240 | ->select( 'page_lang' ) |
241 | ->from( 'page' ) |
242 | ->where( [ 'page_id' => $pageId ] ) |
243 | ->caller( __METHOD__ )->fetchField(); |
244 | |
245 | // Check if user wants to use the default language |
246 | if ( $newLanguage === 'default' ) { |
247 | $newLanguage = null; |
248 | } |
249 | |
250 | // No change in language |
251 | if ( $newLanguage === $oldLanguage ) { |
252 | // Check if old language does not exist |
253 | if ( !$oldLanguage ) { |
254 | return Status::newFatal( ApiMessage::create( |
255 | [ |
256 | 'pagelang-unchanged-language-default', |
257 | wfEscapeWikiText( $title->getPrefixedText() ) |
258 | ], |
259 | 'pagelang-unchanged-language' |
260 | ) ); |
261 | } |
262 | return Status::newFatal( |
263 | 'pagelang-unchanged-language', |
264 | wfEscapeWikiText( $title->getPrefixedText() ), |
265 | $oldLanguage |
266 | ); |
267 | } |
268 | |
269 | // Hardcoded [def] if the language is set to null |
270 | $logOld = $oldLanguage ?: $defLang . '[def]'; |
271 | $logNew = $newLanguage ?: $defLang . '[def]'; |
272 | |
273 | // Writing new page language to database |
274 | $dbw->newUpdateQueryBuilder() |
275 | ->update( 'page' ) |
276 | ->set( [ 'page_lang' => $newLanguage ] ) |
277 | ->where( [ |
278 | 'page_id' => $pageId, |
279 | 'page_lang' => $oldLanguage, |
280 | ] ) |
281 | ->caller( __METHOD__ )->execute(); |
282 | |
283 | if ( !$dbw->affectedRows() ) { |
284 | return Status::newFatal( 'pagelang-db-failed' ); |
285 | } |
286 | |
287 | // Logging change of language |
288 | $logParams = [ |
289 | '4::oldlanguage' => $logOld, |
290 | '5::newlanguage' => $logNew |
291 | ]; |
292 | $entry = new ManualLogEntry( 'pagelang', 'pagelang' ); |
293 | $entry->setPerformer( $context->getUser() ); |
294 | $entry->setTarget( $title ); |
295 | $entry->setParameters( $logParams ); |
296 | $entry->setComment( is_string( $reason ) ? $reason : "" ); |
297 | $entry->addTags( $tags ); |
298 | |
299 | $logid = $entry->insert(); |
300 | $entry->publish( $logid ); |
301 | |
302 | // Force re-render so that language-based content (parser functions etc.) gets updated |
303 | $title->invalidateCache(); |
304 | |
305 | return Status::newGood( (object)[ |
306 | 'oldLanguage' => $logOld, |
307 | 'newLanguage' => $logNew, |
308 | 'logId' => $logid, |
309 | ] ); |
310 | } |
311 | |
312 | public function onSuccess() { |
313 | // Success causes a redirect |
314 | $this->getOutput()->redirect( $this->goToUrl ); |
315 | } |
316 | |
317 | private function showLogFragment( $title ) { |
318 | $moveLogPage = new LogPage( 'pagelang' ); |
319 | $out1 = Xml::element( 'h2', null, $moveLogPage->getName()->text() ); |
320 | $out2 = ''; |
321 | LogEventsList::showLogExtract( $out2, 'pagelang', $title ); |
322 | return $out1 . $out2; |
323 | } |
324 | |
325 | /** |
326 | * Return an array of subpages beginning with $search that this special page will accept. |
327 | * |
328 | * @param string $search Prefix to search for |
329 | * @param int $limit Maximum number of results to return (usually 10) |
330 | * @param int $offset Number of results to skip (usually 0) |
331 | * @return string[] Matching subpages |
332 | */ |
333 | public function prefixSearchSubpages( $search, $limit, $offset ) { |
334 | return $this->prefixSearchString( $search, $limit, $offset, $this->searchEngineFactory ); |
335 | } |
336 | |
337 | protected function getGroupName() { |
338 | return 'pagetools'; |
339 | } |
340 | } |
341 | |
342 | /** |
343 | * Retain the old class name for backwards compatibility. |
344 | * @deprecated since 1.41 |
345 | */ |
346 | class_alias( SpecialPageLanguage::class, 'SpecialPageLanguage' ); |