PermissionManager doesn't exist in 1.33, so we cannot use it in 1.31
[lhc/web/wiklou.git] / includes / specials / SpecialRedirect.php
1 <?php
2 /**
3 * Implements Special:Redirect
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 */
23
24 /**
25 * A special page that redirects to: the user for a numeric user id,
26 * the file for a given filename, or the page for a given revision id.
27 *
28 * @ingroup SpecialPage
29 * @since 1.22
30 */
31 class SpecialRedirect extends FormSpecialPage {
32
33 /**
34 * The type of the redirect (user/file/revision)
35 *
36 * Example value: `'user'`
37 *
38 * @var string $mType
39 */
40 protected $mType;
41
42 /**
43 * The identifier/value for the redirect (which id, which file)
44 *
45 * Example value: `'42'`
46 *
47 * @var string $mValue
48 */
49 protected $mValue;
50
51 function __construct() {
52 parent::__construct( 'Redirect' );
53 $this->mType = null;
54 $this->mValue = null;
55 }
56
57 /**
58 * Set $mType and $mValue based on parsed value of $subpage.
59 * @param string $subpage
60 */
61 function setParameter( $subpage ) {
62 // parse $subpage to pull out the parts
63 $parts = explode( '/', $subpage, 2 );
64 $this->mType = count( $parts ) > 0 ? $parts[0] : null;
65 $this->mValue = count( $parts ) > 1 ? $parts[1] : null;
66 }
67
68 /**
69 * Handle Special:Redirect/user/xxxx (by redirecting to User:YYYY)
70 *
71 * @return Status A good status contains the url to redirect to
72 */
73 function dispatchUser() {
74 if ( !ctype_digit( $this->mValue ) ) {
75 // Message: redirect-not-numeric
76 return Status::newFatal( $this->getMessagePrefix() . '-not-numeric' );
77 }
78 $user = User::newFromId( (int)$this->mValue );
79 $username = $user->getName(); // load User as side-effect
80 if ( $user->isAnon() ) {
81 // Message: redirect-not-exists
82 return Status::newFatal( $this->getMessagePrefix() . '-not-exists' );
83 }
84 if ( $user->isHidden() && !$this->getUser()->isAllowed( 'hideuser' ) ) {
85 throw new PermissionsError( null, [ 'badaccess-group0' ] );
86 }
87 $userpage = Title::makeTitle( NS_USER, $username );
88
89 return Status::newGood( [
90 $userpage->getFullURL( '', false, PROTO_CURRENT ), 302
91 ] );
92 }
93
94 /**
95 * Handle Special:Redirect/file/xxxx
96 *
97 * @return Status A good status contains the url to redirect to
98 */
99 function dispatchFile() {
100 try {
101 $title = Title::newFromTextThrow( $this->mValue, NS_FILE );
102 if ( $title && !$title->inNamespace( NS_FILE ) ) {
103 // If the given value contains a namespace enforce file namespace
104 $title = Title::newFromTextThrow( Title::makeName( NS_FILE, $this->mValue ) );
105 }
106 } catch ( MalformedTitleException $e ) {
107 return Status::newFatal( $e->getMessageObject() );
108 }
109 $file = wfFindFile( $title );
110
111 if ( !$file || !$file->exists() ) {
112 // Message: redirect-not-exists
113 return Status::newFatal( $this->getMessagePrefix() . '-not-exists' );
114 }
115 // Default behavior: Use the direct link to the file.
116 $url = $file->getUrl();
117 $request = $this->getRequest();
118 $width = $request->getInt( 'width', -1 );
119 $height = $request->getInt( 'height', -1 );
120
121 // If a width is requested...
122 if ( $width != -1 ) {
123 $mto = $file->transform( [ 'width' => $width, 'height' => $height ] );
124 // ... and we can
125 if ( $mto && !$mto->isError() ) {
126 // ... change the URL to point to a thumbnail.
127 // Note: This url is more temporary as can change
128 // if file is reuploaded and has different aspect ratio.
129 $url = [ $mto->getUrl(), $height === -1 ? 301 : 302 ];
130 }
131 }
132
133 return Status::newGood( $url );
134 }
135
136 /**
137 * Handle Special:Redirect/revision/xxx
138 * (by redirecting to index.php?oldid=xxx)
139 *
140 * @return Status A good status contains the url to redirect to
141 */
142 function dispatchRevision() {
143 $oldid = $this->mValue;
144 if ( !ctype_digit( $oldid ) ) {
145 // Message: redirect-not-numeric
146 return Status::newFatal( $this->getMessagePrefix() . '-not-numeric' );
147 }
148 $oldid = (int)$oldid;
149 if ( $oldid === 0 ) {
150 // Message: redirect-not-exists
151 return Status::newFatal( $this->getMessagePrefix() . '-not-exists' );
152 }
153
154 return Status::newGood( wfAppendQuery( wfScript( 'index' ), [
155 'oldid' => $oldid
156 ] ) );
157 }
158
159 /**
160 * Handle Special:Redirect/page/xxx (by redirecting to index.php?curid=xxx)
161 *
162 * @return Status A good status contains the url to redirect to
163 */
164 function dispatchPage() {
165 $curid = $this->mValue;
166 if ( !ctype_digit( $curid ) ) {
167 // Message: redirect-not-numeric
168 return Status::newFatal( $this->getMessagePrefix() . '-not-numeric' );
169 }
170 $curid = (int)$curid;
171 if ( $curid === 0 ) {
172 // Message: redirect-not-exists
173 return Status::newFatal( $this->getMessagePrefix() . '-not-exists' );
174 }
175
176 return Status::newGood( wfAppendQuery( wfScript( 'index' ), [
177 'curid' => $curid
178 ] ) );
179 }
180
181 /**
182 * Handle Special:Redirect/logid/xxx
183 * (by redirecting to index.php?title=Special:Log&logid=xxx)
184 *
185 * @since 1.27
186 * @return Status A good status contains the url to redirect to
187 */
188 function dispatchLog() {
189 $logid = $this->mValue;
190 if ( !ctype_digit( $logid ) ) {
191 // Message: redirect-not-numeric
192 return Status::newFatal( $this->getMessagePrefix() . '-not-numeric' );
193 }
194 $logid = (int)$logid;
195 if ( $logid === 0 ) {
196 // Message: redirect-not-exists
197 return Status::newFatal( $this->getMessagePrefix() . '-not-exists' );
198 }
199 $query = [ 'title' => 'Special:Log', 'logid' => $logid ];
200 return Status::newGood( wfAppendQuery( wfScript( 'index' ), $query ) );
201 }
202
203 /**
204 * Use appropriate dispatch* method to obtain a redirection URL,
205 * and either: redirect, set a 404 error code and error message,
206 * or do nothing (if $mValue wasn't set) allowing the form to be
207 * displayed.
208 *
209 * @return Status|bool True if a redirect was successfully handled.
210 */
211 function dispatch() {
212 // the various namespaces supported by Special:Redirect
213 switch ( $this->mType ) {
214 case 'user':
215 $status = $this->dispatchUser();
216 break;
217 case 'file':
218 $status = $this->dispatchFile();
219 break;
220 case 'revision':
221 $status = $this->dispatchRevision();
222 break;
223 case 'page':
224 $status = $this->dispatchPage();
225 break;
226 case 'logid':
227 $status = $this->dispatchLog();
228 break;
229 default:
230 $status = null;
231 break;
232 }
233 if ( $status && $status->isGood() ) {
234 // These urls can sometimes be linked from prominent places,
235 // so varnish cache.
236 $value = $status->getValue();
237 if ( is_array( $value ) ) {
238 list( $url, $code ) = $value;
239 } else {
240 $url = $value;
241 $code = 301;
242 }
243 if ( $code === 301 ) {
244 $this->getOutput()->setCdnMaxage( 60 * 60 );
245 } else {
246 $this->getOutput()->setCdnMaxage( 10 );
247 }
248 $this->getOutput()->redirect( $url, $code );
249
250 return true;
251 }
252 if ( !is_null( $this->mValue ) ) {
253 $this->getOutput()->setStatusCode( 404 );
254
255 return $status;
256 }
257
258 return false;
259 }
260
261 protected function getFormFields() {
262 $mp = $this->getMessagePrefix();
263 $ns = [
264 // subpage => message
265 // Messages: redirect-user, redirect-page, redirect-revision,
266 // redirect-file, redirect-logid
267 'user' => $mp . '-user',
268 'page' => $mp . '-page',
269 'revision' => $mp . '-revision',
270 'file' => $mp . '-file',
271 'logid' => $mp . '-logid',
272 ];
273 $a = [];
274 $a['type'] = [
275 'type' => 'select',
276 'label-message' => $mp . '-lookup', // Message: redirect-lookup
277 'options' => [],
278 'default' => current( array_keys( $ns ) ),
279 ];
280 foreach ( $ns as $n => $m ) {
281 $m = $this->msg( $m )->text();
282 $a['type']['options'][$m] = $n;
283 }
284 $a['value'] = [
285 'type' => 'text',
286 'label-message' => $mp . '-value' // Message: redirect-value
287 ];
288 // set the defaults according to the parsed subpage path
289 if ( !empty( $this->mType ) ) {
290 $a['type']['default'] = $this->mType;
291 }
292 if ( !empty( $this->mValue ) ) {
293 $a['value']['default'] = $this->mValue;
294 }
295
296 return $a;
297 }
298
299 public function onSubmit( array $data ) {
300 if ( !empty( $data['type'] ) && !empty( $data['value'] ) ) {
301 $this->setParameter( $data['type'] . '/' . $data['value'] );
302 }
303
304 /* if this returns false, will show the form */
305 return $this->dispatch();
306 }
307
308 public function onSuccess() {
309 /* do nothing, we redirect in $this->dispatch if successful. */
310 }
311
312 protected function alterForm( HTMLForm $form ) {
313 /* display summary at top of page */
314 $this->outputHeader();
315 // tweak label on submit button
316 // Message: redirect-submit
317 $form->setSubmitTextMsg( $this->getMessagePrefix() . '-submit' );
318 /* submit form every time */
319 $form->setMethod( 'get' );
320 }
321
322 protected function getDisplayFormat() {
323 return 'ooui';
324 }
325
326 /**
327 * Return an array of subpages that this special page will accept.
328 *
329 * @return string[] subpages
330 */
331 protected function getSubpagesForPrefixSearch() {
332 return [
333 'file',
334 'page',
335 'revision',
336 'user',
337 'logid',
338 ];
339 }
340
341 /**
342 * @return bool
343 */
344 public function requiresWrite() {
345 return false;
346 }
347
348 /**
349 * @return bool
350 */
351 public function requiresUnblock() {
352 return false;
353 }
354
355 protected function getGroupName() {
356 return 'redirects';
357 }
358 }