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
21 namespace MediaWiki\Specials
;
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
;
45 * Special page for changing the content language of a page
47 * @ingroup SpecialPage
48 * @author Kunal Grover
51 class SpecialPageLanguage
extends FormSpecialPage
{
53 * @var string URL to go to if language change successful
57 private IContentHandlerFactory
$contentHandlerFactory;
58 private LanguageNameUtils
$languageNameUtils;
59 private IConnectionProvider
$dbProvider;
60 private SearchEngineFactory
$searchEngineFactory;
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
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() {
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;
95 $defaultPageLanguage = $this->contentHandlerFactory
->getContentHandler( $title->getContentModel() )
96 ->getPageLanguage( $title );
98 $hasCustomLanguageSet = !$defaultPageLanguage->equals( $title->getPageLanguage() );
100 $hasCustomLanguageSet = false;
104 $page['pagename'] = [
106 'label-message' => 'pagelang-name',
107 'default' => $title ?
$title->getPrefixedText() : $defaultName,
108 'autofocus' => $defaultName === null,
112 // Options for whether to use the default language or select language
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',
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
);
128 foreach ( $languages as $code => $name ) {
129 $options["$code - $name"] = $code;
132 $page['language'] = [
133 'id' => 'mw-pl-languageselector',
134 'cssclass' => 'mw-languageselector',
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
146 'label-message' => 'pagelang-reason'
152 protected function postHtml() {
154 return $this->showLogFragment( $this->par
);
159 protected function getDisplayFormat() {
163 public function alterForm( HTMLForm
$form ) {
164 $this->getHookRunner()->onLanguageSelector( $this->getOutput(), 'mw-languageselector' );
165 $form->setSubmitTextMsg( 'pagelang-submit' );
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';
179 $newLanguage = $data['language'];
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(
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
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
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' )
242 ->where( [ 'page_id' => $pageId ] )
243 ->caller( __METHOD__
)->fetchField();
245 // Check if user wants to use the default language
246 if ( $newLanguage === 'default' ) {
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'
262 return Status
::newFatal(
263 'pagelang-unchanged-language',
264 wfEscapeWikiText( $title->getPrefixedText() ),
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()
276 ->set( [ 'page_lang' => $newLanguage ] )
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
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,
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() );
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() {
343 * Retain the old class name for backwards compatibility.
344 * @deprecated since 1.41
346 class_alias( SpecialPageLanguage
::class, 'SpecialPageLanguage' );