MediaWiki master
SpecialPageLanguage.php
Go to the documentation of this file.
1<?php
21namespace MediaWiki\Specials;
22
43
55 private $goToUrl;
56
57 private IContentHandlerFactory $contentHandlerFactory;
58 private LanguageNameUtils $languageNameUtils;
59 private IConnectionProvider $dbProvider;
60 private SearchEngineFactory $searchEngineFactory;
61
62 public function __construct(
63 IContentHandlerFactory $contentHandlerFactory,
64 LanguageNameUtils $languageNameUtils,
65 IConnectionProvider $dbProvider,
66 SearchEngineFactory $searchEngineFactory
67 ) {
68 parent::__construct( 'PageLanguage', 'pagelang' );
69 $this->contentHandlerFactory = $contentHandlerFactory;
70 $this->languageNameUtils = $languageNameUtils;
71 $this->dbProvider = $dbProvider;
72 $this->searchEngineFactory = $searchEngineFactory;
73 }
74
75 public function doesWrites() {
76 return true;
77 }
78
79 protected function preHtml() {
80 $this->getOutput()->addModules( 'mediawiki.misc-authed-ooui' );
81 return parent::preHtml();
82 }
83
84 protected function getFormFields() {
85 // Get default from the subpage of Special page
86 $defaultName = $this->par;
87 $title = $defaultName ? Title::newFromText( $defaultName ) : null;
88 if ( $title ) {
89 $defaultPageLanguage = $this->contentHandlerFactory->getContentHandler( $title->getContentModel() )
90 ->getPageLanguage( $title );
91
92 $hasCustomLanguageSet = !$defaultPageLanguage->equals( $title->getPageLanguage() );
93 } else {
94 $hasCustomLanguageSet = false;
95 }
96
97 $page = [];
98 $page['pagename'] = [
99 'type' => 'title',
100 'label-message' => 'pagelang-name',
101 'default' => $title ? $title->getPrefixedText() : $defaultName,
102 'autofocus' => $defaultName === null,
103 'exists' => true,
104 ];
105
106 // Options for whether to use the default language or select language
107 $selectoptions = [
108 (string)$this->msg( 'pagelang-use-default' )->escaped() => 1,
109 (string)$this->msg( 'pagelang-select-lang' )->escaped() => 2,
110 ];
111 $page['selectoptions'] = [
112 'id' => 'mw-pl-options',
113 'type' => 'radio',
114 'options' => $selectoptions,
115 'default' => $hasCustomLanguageSet ? 2 : 1
116 ];
117
118 // Building a language selector
119 $userLang = $this->getLanguage()->getCode();
120 $languages = $this->languageNameUtils->getLanguageNames( $userLang, LanguageNameUtils::SUPPORTED );
121 $options = [];
122 foreach ( $languages as $code => $name ) {
123 $options["$code - $name"] = $code;
124 }
125
126 $page['language'] = [
127 'id' => 'mw-pl-languageselector',
128 'cssclass' => 'mw-languageselector',
129 'type' => 'select',
130 'options' => $options,
131 'label-message' => 'pagelang-language',
132 'default' => $title ?
133 $title->getPageLanguage()->getCode() :
135 ];
136
137 // Allow user to enter a comment explaining the change
138 $page['reason'] = [
139 'type' => 'text',
140 'label-message' => 'pagelang-reason'
141 ];
142
143 return $page;
144 }
145
146 protected function postHtml() {
147 if ( $this->par ) {
148 return $this->showLogFragment( $this->par );
149 }
150 return '';
151 }
152
153 protected function getDisplayFormat() {
154 return 'ooui';
155 }
156
157 public function alterForm( HTMLForm $form ) {
158 $this->getHookRunner()->onLanguageSelector( $this->getOutput(), 'mw-languageselector' );
159 $form->setSubmitTextMsg( 'pagelang-submit' );
160 }
161
166 public function onSubmit( array $data ) {
167 $pageName = $data['pagename'];
168
169 // Check if user wants to use default language
170 if ( $data['selectoptions'] == 1 ) {
171 $newLanguage = 'default';
172 } else {
173 $newLanguage = $data['language'];
174 }
175
176 try {
177 $title = Title::newFromTextThrow( $pageName );
178 } catch ( MalformedTitleException $ex ) {
179 return Status::newFatal( $ex->getMessageObject() );
180 }
181
182 // Check permissions and make sure the user has permission to edit the page
183 $status = PermissionStatus::newEmpty();
184 if ( !$this->getAuthority()->authorizeWrite( 'edit', $title, $status ) ) {
185 $wikitext = $this->getOutput()->formatPermissionStatus( $status );
186 // Hack to get our wikitext parsed
187 return Status::newFatal( new RawMessage( '$1', [ $wikitext ] ) );
188 }
189
190 // Url to redirect to after the operation
191 $this->goToUrl = $title->getFullUrlForRedirect(
192 $title->isRedirect() ? [ 'redirect' => 'no' ] : []
193 );
194
196 $this->getContext(),
197 $title,
198 $newLanguage,
199 $data['reason'] ?? '',
200 [],
201 $this->dbProvider->getPrimaryDatabase()
202 );
203 }
204
216 public static function changePageLanguage( IContextSource $context, Title $title,
217 $newLanguage, $reason = "", array $tags = [], ?IDatabase $dbw = null ) {
218 // Get the default language for the wiki
219 $defLang = $context->getConfig()->get( MainConfigNames::LanguageCode );
220
221 $pageId = $title->getArticleID();
222
223 // Check if article exists
224 if ( !$pageId ) {
225 return Status::newFatal(
226 'pagelang-nonexistent-page',
228 );
229 }
230
231 // Load the page language from DB
232 $dbw ??= MediaWikiServices::getInstance()->getConnectionProvider()->getPrimaryDatabase();
233 $oldLanguage = $dbw->newSelectQueryBuilder()
234 ->select( 'page_lang' )
235 ->from( 'page' )
236 ->where( [ 'page_id' => $pageId ] )
237 ->caller( __METHOD__ )->fetchField();
238
239 // Check if user wants to use the default language
240 if ( $newLanguage === 'default' ) {
241 $newLanguage = null;
242 }
243
244 // No change in language
245 if ( $newLanguage === $oldLanguage ) {
246 // Check if old language does not exist
247 if ( !$oldLanguage ) {
248 return Status::newFatal( ApiMessage::create(
249 [
250 'pagelang-unchanged-language-default',
252 ],
253 'pagelang-unchanged-language'
254 ) );
255 }
256 return Status::newFatal(
257 'pagelang-unchanged-language',
259 $oldLanguage
260 );
261 }
262
263 // Hardcoded [def] if the language is set to null
264 $logOld = $oldLanguage ?: $defLang . '[def]';
265 $logNew = $newLanguage ?: $defLang . '[def]';
266
267 // Writing new page language to database
268 $dbw->newUpdateQueryBuilder()
269 ->update( 'page' )
270 ->set( [ 'page_lang' => $newLanguage ] )
271 ->where( [
272 'page_id' => $pageId,
273 'page_lang' => $oldLanguage,
274 ] )
275 ->caller( __METHOD__ )->execute();
276
277 if ( !$dbw->affectedRows() ) {
278 return Status::newFatal( 'pagelang-db-failed' );
279 }
280
281 // Logging change of language
282 $logParams = [
283 '4::oldlanguage' => $logOld,
284 '5::newlanguage' => $logNew
285 ];
286 $entry = new ManualLogEntry( 'pagelang', 'pagelang' );
287 $entry->setPerformer( $context->getUser() );
288 $entry->setTarget( $title );
289 $entry->setParameters( $logParams );
290 $entry->setComment( is_string( $reason ) ? $reason : "" );
291 $entry->addTags( $tags );
292
293 $logid = $entry->insert();
294 $entry->publish( $logid );
295
296 // Force re-render so that language-based content (parser functions etc.) gets updated
297 $title->invalidateCache();
298
299 return Status::newGood( (object)[
300 'oldLanguage' => $logOld,
301 'newLanguage' => $logNew,
302 'logId' => $logid,
303 ] );
304 }
305
306 public function onSuccess() {
307 // Success causes a redirect
308 $this->getOutput()->redirect( $this->goToUrl );
309 }
310
311 private function showLogFragment( string $title ): string {
312 $moveLogPage = new LogPage( 'pagelang' );
313 $out1 = Html::element( 'h2', [], $moveLogPage->getName()->text() );
314 $out2 = '';
315 LogEventsList::showLogExtract( $out2, 'pagelang', $title );
316 return $out1 . $out2;
317 }
318
327 public function prefixSearchSubpages( $search, $limit, $offset ) {
328 return $this->prefixSearchString( $search, $limit, $offset, $this->searchEngineFactory );
329 }
330
331 protected function getGroupName() {
332 return 'pagetools';
333 }
334}
335
340class_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.
Object handling generic submission, CSRF protection, layout and other logic for UI forms in a reusabl...
Definition HTMLForm.php:209
setSubmitTextMsg( $msg)
Set the text for the submit button to a message.
This class is a collection of static functions that serve two purposes:
Definition Html.php:57
Variant of the Message class.
A service that provides utilities to do with language names and codes.
Class to simplify the use of log pages.
Definition LogPage.php:50
Class for creating new log entries and inserting them into the database.
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 subpage of the special page.
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)
static changePageLanguage(IContextSource $context, Title $title, $newLanguage, $reason="", array $tags=[], ?IDatabase $dbw=null)
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...
doesWrites()
Indicates whether POST requests to this special page require write access to the wiki.
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:2582
invalidateCache( $purgeTime=null)
Updates page_touched for this page; called from LinksUpdate.php.
Definition Title.php:3312
getPrefixedText()
Get the prefixed title with spaces.
Definition Title.php:1855
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.
Interface to a relational database.
Definition IDatabase.php:45
element(SerializerNode $parent, SerializerNode $node, $contents)