MediaWiki master
SpecialPageLanguage.php
Go to the documentation of this file.
1<?php
21namespace MediaWiki\Specials;
22
23use ApiMessage;
25use LogPage;
43
55 private $goToUrl;
56
57 private IContentHandlerFactory $contentHandlerFactory;
58 private LanguageNameUtils $languageNameUtils;
59 private IConnectionProvider $dbProvider;
60 private SearchEngineFactory $searchEngineFactory;
61
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() :
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
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
202 $this->getContext(),
203 $title,
204 $newLanguage,
205 $data['reason'] ?? '',
206 [],
207 $this->dbProvider->getPrimaryDatabase()
208 );
209 }
210
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',
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',
258 ],
259 'pagelang-unchanged-language'
260 ) );
261 }
262 return Status::newFatal(
263 'pagelang-unchanged-language',
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
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
346class_alias( SpecialPageLanguage::class, 'SpecialPageLanguage' );
wfEscapeWikiText( $input)
Escapes the given text so that it may be output using addWikiText() without any linking,...
Extension of Message implementing IApiMessage.
Class to simplify the use of log pages.
Definition LogPage.php:44
Class for creating new log entries and inserting them into the database.
Object handling generic submission, CSRF protection, layout and other logic for UI forms in a reusabl...
Definition HTMLForm.php:206
setSubmitTextMsg( $msg)
Set the text for the submit button to a message.
static element( $element, $attribs=[], $contents='')
Identical to rawElement(), but HTML-escapes $contents (like Xml::element()).
Definition Html.php:216
Variant of the Message class.
A service that provides utilities to do with language names and codes.
A class containing constants representing the names of configuration variables.
const LanguageCode
Name constant for the LanguageCode setting, for use with Config::get()
Service locator for MediaWiki core services.
static getInstance()
Returns the global default instance of the top level service locator.
A StatusValue for permission errors.
Special page which uses an HTMLForm to handle processing.
string null $par
The sub-page of the special page.
prefixSearchString( $search, $limit, $offset, SearchEngineFactory $searchEngineFactory=null)
Perform a regular substring search for prefixSearchSubpages.
getConfig()
Shortcut to get main config object.
getContext()
Gets the context this SpecialPage is executed in.
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
getOutput()
Get the OutputPage being used for this instance.
getAuthority()
Shortcut to get the Authority executing this instance.
getLanguage()
Shortcut to get user's language.
Special page for changing the content language of a page.
getDisplayFormat()
Get display format for the form.
__construct(IContentHandlerFactory $contentHandlerFactory, LanguageNameUtils $languageNameUtils, IConnectionProvider $dbProvider, SearchEngineFactory $searchEngineFactory)
onSuccess()
Do something exciting on successful processing of the form, most likely to show a confirmation messag...
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
static changePageLanguage(IContextSource $context, Title $title, $newLanguage, $reason="", array $tags=[], IDatabase $dbw=null)
doesWrites()
Indicates whether this special page may perform database writes.
alterForm(HTMLForm $form)
Play with the HTMLForm if you need to more substantially.
getFormFields()
Get an HTMLForm descriptor array.
prefixSearchSubpages( $search, $limit, $offset)
Return an array of subpages beginning with $search that this special page will accept.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition Status.php:54
MalformedTitleException is thrown when a TitleParser is unable to parse a title string.
Represents a title within MediaWiki.
Definition Title.php:78
getArticleID( $flags=0)
Get the article ID for this Title from the link cache, adding it if necessary.
Definition Title.php:2590
invalidateCache( $purgeTime=null)
Updates page_touched for this page; called from LinksUpdate.php.
Definition Title.php:3324
getPrefixedText()
Get the prefixed title with spaces.
Definition Title.php:1861
Module of static functions for generating XML.
Definition Xml.php:37
Factory class for SearchEngine.
Interface for objects which can provide a MediaWiki context on request.
getConfig()
Get the site configuration.
Provide primary and replica IDatabase connections.
Basic database interface for live and lazy-loaded relation database handles.
Definition IDatabase.php:36