3 * Implements Special:PageLanguage
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
21 * @ingroup SpecialPage
22 * @author Kunal Grover
27 * Special page for changing the content language of a page
29 * @ingroup SpecialPage
31 class SpecialPageLanguage
extends FormSpecialPage
{
33 * @var string URL to go to if language change successful
37 public function __construct() {
38 parent
::__construct( 'PageLanguage', 'pagelang' );
41 public function doesWrites() {
45 protected function preText() {
46 $this->getOutput()->addModules( 'mediawiki.special.pageLanguage' );
49 protected function getFormFields() {
50 // Get default from the subpage of Special page
51 $defaultName = $this->par
;
52 $title = $defaultName ? Title
::newFromText( $defaultName ) : null;
54 $defaultPageLanguage =
55 ContentHandler
::getForTitle( $title )->getPageLanguage( $title );
56 $hasCustomLanguageSet = !$defaultPageLanguage->equals( $title->getPageLanguage() );
58 $hasCustomLanguageSet = false;
64 'label-message' => 'pagelang-name',
65 'default' => $title ?
$title->getPrefixedText() : $defaultName,
66 'autofocus' => $defaultName === null,
70 // Options for whether to use the default language or select language
72 (string)$this->msg( 'pagelang-use-default' )->escaped() => 1,
73 (string)$this->msg( 'pagelang-select-lang' )->escaped() => 2,
75 $page['selectoptions'] = [
76 'id' => 'mw-pl-options',
78 'options' => $selectoptions,
79 'default' => $hasCustomLanguageSet ?
2 : 1
82 // Building a language selector
83 $userLang = $this->getLanguage()->getCode();
84 $languages = Language
::fetchLanguageNames( $userLang, 'mwfile' );
86 foreach ( $languages as $code => $name ) {
87 $options["$code - $name"] = $code;
91 'id' => 'mw-pl-languageselector',
92 'cssclass' => 'mw-languageselector',
94 'options' => $options,
95 'label-message' => 'pagelang-language',
97 $title->getPageLanguage()->getCode() :
98 $this->getConfig()->get( 'LanguageCode' ),
101 // Allow user to enter a comment explaining the change
104 'label-message' => 'pagelang-reason'
110 protected function postText() {
112 return $this->showLogFragment( $this->par
);
117 protected function getDisplayFormat() {
121 public function alterForm( HTMLForm
$form ) {
122 Hooks
::run( 'LanguageSelector', [ $this->getOutput(), 'mw-languageselector' ] );
123 $form->setSubmitTextMsg( 'pagelang-submit' );
131 public function onSubmit( array $data ) {
132 $pageName = $data['pagename'];
134 // Check if user wants to use default language
135 if ( $data['selectoptions'] == 1 ) {
136 $newLanguage = 'default';
138 $newLanguage = $data['language'];
142 $title = Title
::newFromTextThrow( $pageName );
143 } catch ( MalformedTitleException
$ex ) {
144 return Status
::newFatal( $ex->getMessageObject() );
147 // Check permissions and make sure the user has permission to edit the page
148 $errors = $title->getUserPermissionsErrors( 'edit', $this->getUser() );
151 $out = $this->getOutput();
152 $wikitext = $out->formatPermissionsErrorMessage( $errors );
153 // Hack to get our wikitext parsed
154 return Status
::newFatal( new RawMessage( '$1', [ $wikitext ] ) );
157 // Url to redirect to after the operation
158 $this->goToUrl
= $title->getFullUrlForRedirect(
159 $title->isRedirect() ?
[ 'redirect' => 'no' ] : []
162 return self
::changePageLanguage(
166 $data['reason'] ??
''
171 * @param IContextSource $context
172 * @param Title $title
173 * @param string $newLanguage Language code
174 * @param string $reason Reason for the change
175 * @param array $tags Change tags to apply to the log entry
178 public static function changePageLanguage( IContextSource
$context, Title
$title,
179 $newLanguage, $reason, array $tags = [] ) {
180 // Get the default language for the wiki
181 $defLang = $context->getConfig()->get( 'LanguageCode' );
183 $pageId = $title->getArticleID();
185 // Check if article exists
187 return Status
::newFatal(
188 'pagelang-nonexistent-page',
189 wfEscapeWikiText( $title->getPrefixedText() )
193 // Load the page language from DB
194 $dbw = wfGetDB( DB_MASTER
);
195 $oldLanguage = $dbw->selectField(
198 [ 'page_id' => $pageId ],
202 // Check if user wants to use the default language
203 if ( $newLanguage === 'default' ) {
207 // No change in language
208 if ( $newLanguage === $oldLanguage ) {
209 // Check if old language does not exist
210 if ( !$oldLanguage ) {
211 return Status
::newFatal( ApiMessage
::create(
213 'pagelang-unchanged-language-default',
214 wfEscapeWikiText( $title->getPrefixedText() )
216 'pagelang-unchanged-language'
219 return Status
::newFatal(
220 'pagelang-unchanged-language',
221 wfEscapeWikiText( $title->getPrefixedText() ),
226 // Hardcoded [def] if the language is set to null
227 $logOld = $oldLanguage ?
: $defLang . '[def]';
228 $logNew = $newLanguage ?
: $defLang . '[def]';
230 // Writing new page language to database
233 [ 'page_lang' => $newLanguage ],
235 'page_id' => $pageId,
236 'page_lang' => $oldLanguage
241 if ( !$dbw->affectedRows() ) {
242 return Status
::newFatal( 'pagelang-db-failed' );
245 // Logging change of language
247 '4::oldlanguage' => $logOld,
248 '5::newlanguage' => $logNew
250 $entry = new ManualLogEntry( 'pagelang', 'pagelang' );
251 $entry->setPerformer( $context->getUser() );
252 $entry->setTarget( $title );
253 $entry->setParameters( $logParams );
254 $entry->setComment( $reason );
255 $entry->setTags( $tags );
257 $logid = $entry->insert();
258 $entry->publish( $logid );
260 // Force re-render so that language-based content (parser functions etc.) gets updated
261 $title->invalidateCache();
263 return Status
::newGood( (object)[
264 'oldLanguage' => $logOld,
265 'newLanguage' => $logNew,
270 public function onSuccess() {
271 // Success causes a redirect
272 $this->getOutput()->redirect( $this->goToUrl
);
275 function showLogFragment( $title ) {
276 $moveLogPage = new LogPage( 'pagelang' );
277 $out1 = Xml
::element( 'h2', null, $moveLogPage->getName()->text() );
279 LogEventsList
::showLogExtract( $out2, 'pagelang', $title );
280 return $out1 . $out2;
284 * Return an array of subpages beginning with $search that this special page will accept.
286 * @param string $search Prefix to search for
287 * @param int $limit Maximum number of results to return (usually 10)
288 * @param int $offset Number of results to skip (usually 0)
289 * @return string[] Matching subpages
291 public function prefixSearchSubpages( $search, $limit, $offset ) {
292 return $this->prefixSearchString( $search, $limit, $offset );
295 protected function getGroupName() {