Localisation updates from https://translatewiki.net.
[mediawiki.git] / includes / specials / SpecialPageLanguage.php
blobec9df198655cce430bc57fba34d1a2ab8679cfcc
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.
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.
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
18 * @file
21 namespace MediaWiki\Specials;
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;
44 /**
45 * Special page for changing the content language of a page
47 * @ingroup SpecialPage
48 * @author Kunal Grover
49 * @since 1.24
51 class SpecialPageLanguage extends FormSpecialPage {
52 /**
53 * @var string URL to go to if language change successful
55 private $goToUrl;
57 private IContentHandlerFactory $contentHandlerFactory;
58 private LanguageNameUtils $languageNameUtils;
59 private IConnectionProvider $dbProvider;
60 private SearchEngineFactory $searchEngineFactory;
62 /**
63 * @param IContentHandlerFactory $contentHandlerFactory
64 * @param LanguageNameUtils $languageNameUtils
65 * @param IConnectionProvider $dbProvider
66 * @param SearchEngineFactory $searchEngineFactory
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;
81 public function doesWrites() {
82 return true;
85 protected function preHtml() {
86 $this->getOutput()->addModules( 'mediawiki.misc-authed-ooui' );
87 return parent::preHtml();
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 );
98 $hasCustomLanguageSet = !$defaultPageLanguage->equals( $title->getPageLanguage() );
99 } else {
100 $hasCustomLanguageSet = false;
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,
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,
117 $page['selectoptions'] = [
118 'id' => 'mw-pl-options',
119 'type' => 'radio',
120 'options' => $selectoptions,
121 'default' => $hasCustomLanguageSet ? 2 : 1
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;
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 ),
143 // Allow user to enter a comment explaining the change
144 $page['reason'] = [
145 'type' => 'text',
146 'label-message' => 'pagelang-reason'
149 return $page;
152 protected function postHtml() {
153 if ( $this->par ) {
154 return $this->showLogFragment( $this->par );
156 return '';
159 protected function getDisplayFormat() {
160 return 'ooui';
163 public function alterForm( HTMLForm $form ) {
164 $this->getHookRunner()->onLanguageSelector( $this->getOutput(), 'mw-languageselector' );
165 $form->setSubmitTextMsg( 'pagelang-submit' );
169 * @param array $data
170 * @return Status
172 public function onSubmit( array $data ) {
173 $pageName = $data['pagename'];
175 // Check if user wants to use default language
176 if ( $data['selectoptions'] == 1 ) {
177 $newLanguage = 'default';
178 } else {
179 $newLanguage = $data['language'];
182 try {
183 $title = Title::newFromTextThrow( $pageName );
184 } catch ( MalformedTitleException $ex ) {
185 return Status::newFatal( $ex->getMessageObject() );
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 ] ) );
196 // Url to redirect to after the operation
197 $this->goToUrl = $title->getFullUrlForRedirect(
198 $title->isRedirect() ? [ 'redirect' => 'no' ] : []
201 return self::changePageLanguage(
202 $this->getContext(),
203 $title,
204 $newLanguage,
205 $data['reason'] ?? '',
207 $this->dbProvider->getPrimaryDatabase()
212 * @since 1.36 Added $dbw parameter
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
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 );
227 $pageId = $title->getArticleID();
229 // Check if article exists
230 if ( !$pageId ) {
231 return Status::newFatal(
232 'pagelang-nonexistent-page',
233 wfEscapeWikiText( $title->getPrefixedText() )
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();
245 // Check if user wants to use the default language
246 if ( $newLanguage === 'default' ) {
247 $newLanguage = null;
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(
256 'pagelang-unchanged-language-default',
257 wfEscapeWikiText( $title->getPrefixedText() )
259 'pagelang-unchanged-language'
260 ) );
262 return Status::newFatal(
263 'pagelang-unchanged-language',
264 wfEscapeWikiText( $title->getPrefixedText() ),
265 $oldLanguage
269 // Hardcoded [def] if the language is set to null
270 $logOld = $oldLanguage ?: $defLang . '[def]';
271 $logNew = $newLanguage ?: $defLang . '[def]';
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,
281 ->caller( __METHOD__ )->execute();
283 if ( !$dbw->affectedRows() ) {
284 return Status::newFatal( 'pagelang-db-failed' );
287 // Logging change of language
288 $logParams = [
289 '4::oldlanguage' => $logOld,
290 '5::newlanguage' => $logNew
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 );
299 $logid = $entry->insert();
300 $entry->publish( $logid );
302 // Force re-render so that language-based content (parser functions etc.) gets updated
303 $title->invalidateCache();
305 return Status::newGood( (object)[
306 'oldLanguage' => $logOld,
307 'newLanguage' => $logNew,
308 'logId' => $logid,
309 ] );
312 public function onSuccess() {
313 // Success causes a redirect
314 $this->getOutput()->redirect( $this->goToUrl );
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;
326 * Return an array of subpages beginning with $search that this special page will accept.
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
333 public function prefixSearchSubpages( $search, $limit, $offset ) {
334 return $this->prefixSearchString( $search, $limit, $offset, $this->searchEngineFactory );
337 protected function getGroupName() {
338 return 'pagetools';
343 * Retain the old class name for backwards compatibility.
344 * @deprecated since 1.41
346 class_alias( SpecialPageLanguage::class, 'SpecialPageLanguage' );