Merge "Make DBAccessBase use DBConnRef, rename $wiki, and hide getLoadBalancer()"
[lhc/web/wiklou.git] / includes / specials / SpecialPageLanguage.php
1 <?php
2 /**
3 * Implements Special:PageLanguage
4 *
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.
9 *
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.
14 *
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
19 *
20 * @file
21 * @ingroup SpecialPage
22 * @author Kunal Grover
23 * @since 1.24
24 */
25
26 /**
27 * Special page for changing the content language of a page
28 *
29 * @ingroup SpecialPage
30 */
31 class SpecialPageLanguage extends FormSpecialPage {
32 /**
33 * @var string URL to go to if language change successful
34 */
35 private $goToUrl;
36
37 public function __construct() {
38 parent::__construct( 'PageLanguage', 'pagelang' );
39 }
40
41 public function doesWrites() {
42 return true;
43 }
44
45 protected function preText() {
46 $this->getOutput()->addModules( 'mediawiki.misc-authed-ooui' );
47 return parent::preText();
48 }
49
50 protected function getFormFields() {
51 // Get default from the subpage of Special page
52 $defaultName = $this->par;
53 $title = $defaultName ? Title::newFromText( $defaultName ) : null;
54 if ( $title ) {
55 $defaultPageLanguage =
56 ContentHandler::getForTitle( $title )->getPageLanguage( $title );
57 $hasCustomLanguageSet = !$defaultPageLanguage->equals( $title->getPageLanguage() );
58 } else {
59 $hasCustomLanguageSet = false;
60 }
61
62 $page = [];
63 $page['pagename'] = [
64 'type' => 'title',
65 'label-message' => 'pagelang-name',
66 'default' => $title ? $title->getPrefixedText() : $defaultName,
67 'autofocus' => $defaultName === null,
68 'exists' => true,
69 ];
70
71 // Options for whether to use the default language or select language
72 $selectoptions = [
73 (string)$this->msg( 'pagelang-use-default' )->escaped() => 1,
74 (string)$this->msg( 'pagelang-select-lang' )->escaped() => 2,
75 ];
76 $page['selectoptions'] = [
77 'id' => 'mw-pl-options',
78 'type' => 'radio',
79 'options' => $selectoptions,
80 'default' => $hasCustomLanguageSet ? 2 : 1
81 ];
82
83 // Building a language selector
84 $userLang = $this->getLanguage()->getCode();
85 $languages = Language::fetchLanguageNames( $userLang, 'mwfile' );
86 $options = [];
87 foreach ( $languages as $code => $name ) {
88 $options["$code - $name"] = $code;
89 }
90
91 $page['language'] = [
92 'id' => 'mw-pl-languageselector',
93 'cssclass' => 'mw-languageselector',
94 'type' => 'select',
95 'options' => $options,
96 'label-message' => 'pagelang-language',
97 'default' => $title ?
98 $title->getPageLanguage()->getCode() :
99 $this->getConfig()->get( 'LanguageCode' ),
100 ];
101
102 // Allow user to enter a comment explaining the change
103 $page['reason'] = [
104 'type' => 'text',
105 'label-message' => 'pagelang-reason'
106 ];
107
108 return $page;
109 }
110
111 protected function postText() {
112 if ( $this->par ) {
113 return $this->showLogFragment( $this->par );
114 }
115 return '';
116 }
117
118 protected function getDisplayFormat() {
119 return 'ooui';
120 }
121
122 public function alterForm( HTMLForm $form ) {
123 Hooks::run( 'LanguageSelector', [ $this->getOutput(), 'mw-languageselector' ] );
124 $form->setSubmitTextMsg( 'pagelang-submit' );
125 }
126
127 /**
128 *
129 * @param array $data
130 * @return Status
131 */
132 public function onSubmit( array $data ) {
133 $pageName = $data['pagename'];
134
135 // Check if user wants to use default language
136 if ( $data['selectoptions'] == 1 ) {
137 $newLanguage = 'default';
138 } else {
139 $newLanguage = $data['language'];
140 }
141
142 try {
143 $title = Title::newFromTextThrow( $pageName );
144 } catch ( MalformedTitleException $ex ) {
145 return Status::newFatal( $ex->getMessageObject() );
146 }
147
148 // Check permissions and make sure the user has permission to edit the page
149 $errors = $title->getUserPermissionsErrors( 'edit', $this->getUser() );
150
151 if ( $errors ) {
152 $out = $this->getOutput();
153 $wikitext = $out->formatPermissionsErrorMessage( $errors );
154 // Hack to get our wikitext parsed
155 return Status::newFatal( new RawMessage( '$1', [ $wikitext ] ) );
156 }
157
158 // Url to redirect to after the operation
159 $this->goToUrl = $title->getFullUrlForRedirect(
160 $title->isRedirect() ? [ 'redirect' => 'no' ] : []
161 );
162
163 return self::changePageLanguage(
164 $this->getContext(),
165 $title,
166 $newLanguage,
167 $data['reason'] ?? ''
168 );
169 }
170
171 /**
172 * @param IContextSource $context
173 * @param Title $title
174 * @param string $newLanguage Language code
175 * @param string $reason Reason for the change
176 * @param array $tags Change tags to apply to the log entry
177 * @return Status
178 */
179 public static function changePageLanguage( IContextSource $context, Title $title,
180 $newLanguage, $reason, array $tags = [] ) {
181 // Get the default language for the wiki
182 $defLang = $context->getConfig()->get( 'LanguageCode' );
183
184 $pageId = $title->getArticleID();
185
186 // Check if article exists
187 if ( !$pageId ) {
188 return Status::newFatal(
189 'pagelang-nonexistent-page',
190 wfEscapeWikiText( $title->getPrefixedText() )
191 );
192 }
193
194 // Load the page language from DB
195 $dbw = wfGetDB( DB_MASTER );
196 $oldLanguage = $dbw->selectField(
197 'page',
198 'page_lang',
199 [ 'page_id' => $pageId ],
200 __METHOD__
201 );
202
203 // Check if user wants to use the default language
204 if ( $newLanguage === 'default' ) {
205 $newLanguage = null;
206 }
207
208 // No change in language
209 if ( $newLanguage === $oldLanguage ) {
210 // Check if old language does not exist
211 if ( !$oldLanguage ) {
212 return Status::newFatal( ApiMessage::create(
213 [
214 'pagelang-unchanged-language-default',
215 wfEscapeWikiText( $title->getPrefixedText() )
216 ],
217 'pagelang-unchanged-language'
218 ) );
219 }
220 return Status::newFatal(
221 'pagelang-unchanged-language',
222 wfEscapeWikiText( $title->getPrefixedText() ),
223 $oldLanguage
224 );
225 }
226
227 // Hardcoded [def] if the language is set to null
228 $logOld = $oldLanguage ?: $defLang . '[def]';
229 $logNew = $newLanguage ?: $defLang . '[def]';
230
231 // Writing new page language to database
232 $dbw->update(
233 'page',
234 [ 'page_lang' => $newLanguage ],
235 [
236 'page_id' => $pageId,
237 'page_lang' => $oldLanguage
238 ],
239 __METHOD__
240 );
241
242 if ( !$dbw->affectedRows() ) {
243 return Status::newFatal( 'pagelang-db-failed' );
244 }
245
246 // Logging change of language
247 $logParams = [
248 '4::oldlanguage' => $logOld,
249 '5::newlanguage' => $logNew
250 ];
251 $entry = new ManualLogEntry( 'pagelang', 'pagelang' );
252 $entry->setPerformer( $context->getUser() );
253 $entry->setTarget( $title );
254 $entry->setParameters( $logParams );
255 $entry->setComment( $reason );
256 $entry->addTags( $tags );
257
258 $logid = $entry->insert();
259 $entry->publish( $logid );
260
261 // Force re-render so that language-based content (parser functions etc.) gets updated
262 $title->invalidateCache();
263
264 return Status::newGood( (object)[
265 'oldLanguage' => $logOld,
266 'newLanguage' => $logNew,
267 'logId' => $logid,
268 ] );
269 }
270
271 public function onSuccess() {
272 // Success causes a redirect
273 $this->getOutput()->redirect( $this->goToUrl );
274 }
275
276 function showLogFragment( $title ) {
277 $moveLogPage = new LogPage( 'pagelang' );
278 $out1 = Xml::element( 'h2', null, $moveLogPage->getName()->text() );
279 $out2 = '';
280 LogEventsList::showLogExtract( $out2, 'pagelang', $title );
281 return $out1 . $out2;
282 }
283
284 /**
285 * Return an array of subpages beginning with $search that this special page will accept.
286 *
287 * @param string $search Prefix to search for
288 * @param int $limit Maximum number of results to return (usually 10)
289 * @param int $offset Number of results to skip (usually 0)
290 * @return string[] Matching subpages
291 */
292 public function prefixSearchSubpages( $search, $limit, $offset ) {
293 return $this->prefixSearchString( $search, $limit, $offset );
294 }
295
296 protected function getGroupName() {
297 return 'pagetools';
298 }
299 }