]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - includes/specials/SpecialMovepage.php
MediaWiki 1.14.0
[autoinstallsdev/mediawiki.git] / includes / specials / SpecialMovepage.php
1 <?php
2 /**
3  * @file
4  * @ingroup SpecialPage
5  */
6
7 /**
8  * Constructor
9  */
10 function wfSpecialMovepage( $par = null ) {
11         global $wgUser, $wgOut, $wgRequest, $action;
12
13         # Check for database lock
14         if ( wfReadOnly() ) {
15                 $wgOut->readOnlyPage();
16                 return;
17         }
18
19         $target = isset( $par ) ? $par : $wgRequest->getVal( 'target' );
20         $oldTitleText = $wgRequest->getText( 'wpOldTitle', $target );
21         $newTitleText = $wgRequest->getText( 'wpNewTitle' );
22
23         $oldTitle = Title::newFromText( $oldTitleText );
24         $newTitle = Title::newFromText( $newTitleText );
25
26         if( is_null( $oldTitle ) ) {
27                 $wgOut->showErrorPage( 'notargettitle', 'notargettext' );
28                 return;
29         }
30         if( !$oldTitle->exists() ) {
31                 $wgOut->showErrorPage( 'nopagetitle', 'nopagetext' );
32                 return;
33         }
34
35         # Check rights
36         $permErrors = $oldTitle->getUserPermissionsErrors( 'move', $wgUser );
37         if( !empty( $permErrors ) ) {
38                 $wgOut->showPermissionsErrorPage( $permErrors );
39                 return;
40         }
41
42         $form = new MovePageForm( $oldTitle, $newTitle );
43
44         if ( 'submit' == $action && $wgRequest->wasPosted()
45                 && $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) {
46                 $form->doSubmit();
47         } else {
48                 $form->showForm( '' );
49         }
50 }
51
52 /**
53  * HTML form for Special:Movepage
54  * @ingroup SpecialPage
55  */
56 class MovePageForm {
57         var $oldTitle, $newTitle; # Objects
58         var $reason; # Text input
59         var $moveTalk, $deleteAndMove, $moveSubpages, $fixRedirects, $leaveRedirect; # Checks
60
61         private $watch = false;
62
63         function __construct( $oldTitle, $newTitle ) {
64                 global $wgRequest;
65                 $target = isset($par) ? $par : $wgRequest->getVal( 'target' );
66                 $this->oldTitle = $oldTitle;
67                 $this->newTitle = $newTitle;
68                 $this->reason = $wgRequest->getText( 'wpReason' );
69                 if ( $wgRequest->wasPosted() ) {
70                         $this->moveTalk = $wgRequest->getBool( 'wpMovetalk', false );
71                         $this->fixRedirects = $wgRequest->getBool( 'wpFixRedirects', false );
72                         $this->leaveRedirect = $wgRequest->getBool( 'wpLeaveRedirect', false );
73                 } else {
74                         $this->moveTalk = $wgRequest->getBool( 'wpMovetalk', true );
75                         $this->fixRedirects = $wgRequest->getBool( 'wpFixRedirects', true );
76                         $this->leaveRedirect = $wgRequest->getBool( 'wpLeaveRedirect', true );
77                 }
78                 $this->moveSubpages = $wgRequest->getBool( 'wpMovesubpages', false );
79                 $this->deleteAndMove = $wgRequest->getBool( 'wpDeleteAndMove' ) && $wgRequest->getBool( 'wpConfirm' );
80                 $this->watch = $wgRequest->getCheck( 'wpWatch' );
81         }
82
83         /**
84          * Show the form
85          * @param mixed $err Error message. May either be a string message name or 
86          *    array message name and parameters, like the second argument to 
87          *    OutputPage::wrapWikiMsg(). 
88          */
89         function showForm( $err ) {
90                 global $wgOut, $wgUser, $wgFixDoubleRedirects;
91
92                 $skin = $wgUser->getSkin();
93
94                 $oldTitleLink = $skin->makeLinkObj( $this->oldTitle );
95
96                 $wgOut->setPagetitle( wfMsg( 'move-page', $this->oldTitle->getPrefixedText() ) );
97                 $wgOut->setSubtitle( wfMsg( 'move-page-backlink', $oldTitleLink ) );
98
99                 $newTitle = $this->newTitle;
100
101                 if( !$newTitle ) {
102                         # Show the current title as a default
103                         # when the form is first opened.
104                         $newTitle = $this->oldTitle;
105                 }
106                 else {
107                         if( empty($err) ) {
108                                 # If a title was supplied, probably from the move log revert
109                                 # link, check for validity. We can then show some diagnostic
110                                 # information and save a click.
111                                 $newerr = $this->oldTitle->isValidMoveOperation( $newTitle );
112                                 if( $newerr ) {
113                                         $err = $newerr[0];
114                                 }
115                         }
116                 }
117
118                 if ( !empty($err) && $err[0] == 'articleexists' && $wgUser->isAllowed( 'delete' ) ) {
119                         $wgOut->addWikiMsg( 'delete_and_move_text', $newTitle->getPrefixedText() );
120                         $movepagebtn = wfMsg( 'delete_and_move' );
121                         $submitVar = 'wpDeleteAndMove';
122                         $confirm = "
123                                 <tr>
124                                         <td></td>
125                                         <td class='mw-input'>" .
126                                                 Xml::checkLabel( wfMsg( 'delete_and_move_confirm' ), 'wpConfirm', 'wpConfirm' ) .
127                                         "</td>
128                                 </tr>";
129                         $err = '';
130                 } else {
131                         $wgOut->addWikiMsg( 'movepagetext' );
132                         $movepagebtn = wfMsg( 'movepagebtn' );
133                         $submitVar = 'wpMove';
134                         $confirm = false;
135                 }
136
137                 $oldTalk = $this->oldTitle->getTalkPage();
138                 $considerTalk = ( !$this->oldTitle->isTalkPage() && $oldTalk->exists() );
139
140                 $dbr = wfGetDB( DB_SLAVE );
141                 if ( $wgFixDoubleRedirects ) {
142                         $hasRedirects = $dbr->selectField( 'redirect', '1', 
143                                 array( 
144                                         'rd_namespace' => $this->oldTitle->getNamespace(),
145                                         'rd_title' => $this->oldTitle->getDBkey(),
146                                 ) , __METHOD__ );
147                 } else {
148                         $hasRedirects = false;
149                 }
150
151                 if ( $considerTalk ) {
152                         $wgOut->addWikiMsg( 'movepagetalktext' );
153                 }
154
155                 $titleObj = SpecialPage::getTitleFor( 'Movepage' );
156                 $token = htmlspecialchars( $wgUser->editToken() );
157
158                 if ( !empty($err) ) {
159                         $wgOut->setSubtitle( wfMsg( 'formerror' ) );
160                         if( $err[0] == 'hookaborted' ) {
161                                 $hookErr = $err[1];
162                                 $errMsg = "<p><strong class=\"error\">$hookErr</strong></p>\n";
163                                 $wgOut->addHTML( $errMsg );
164                         } else {
165                                 $wgOut->wrapWikiMsg( '<p><strong class="error">$1</strong></p>', $err );
166                         }
167                 }
168
169                 $wgOut->addHTML(
170                          Xml::openElement( 'form', array( 'method' => 'post', 'action' => $titleObj->getLocalURL( 'action=submit' ), 'id' => 'movepage' ) ) .
171                          Xml::openElement( 'fieldset' ) .
172                          Xml::element( 'legend', null, wfMsg( 'move-page-legend' ) ) .
173                          Xml::openElement( 'table', array( 'border' => '0', 'id' => 'mw-movepage-table' ) ) .
174                          "<tr>
175                                 <td class='mw-label'>" .
176                                         wfMsgHtml( 'movearticle' ) .
177                                 "</td>
178                                 <td class='mw-input'>
179                                         <strong>{$oldTitleLink}</strong>
180                                 </td>
181                         </tr>
182                         <tr>
183                                 <td class='mw-label'>" .
184                                         Xml::label( wfMsg( 'newtitle' ), 'wpNewTitle' ) .
185                                 "</td>
186                                 <td class='mw-input'>" .
187                                         Xml::input( 'wpNewTitle', 40, $newTitle->getPrefixedText(), array( 'type' => 'text', 'id' => 'wpNewTitle' ) ) .
188                                         Xml::hidden( 'wpOldTitle', $this->oldTitle->getPrefixedText() ) .
189                                 "</td>
190                         </tr>
191                         <tr>
192                                 <td class='mw-label'>" .
193                                         Xml::label( wfMsg( 'movereason' ), 'wpReason' ) .
194                                 "</td>
195                                 <td class='mw-input'>" .
196                                         Xml::tags( 'textarea', array( 'name' => 'wpReason', 'id' => 'wpReason', 'cols' => 60, 'rows' => 2 ), htmlspecialchars( $this->reason ) ) .
197                                 "</td>
198                         </tr>"
199                 );
200
201                 if( $considerTalk ) {
202                         $wgOut->addHTML( "
203                                 <tr>
204                                         <td></td>
205                                         <td class='mw-input'>" .
206                                                 Xml::checkLabel( wfMsg( 'movetalk' ), 'wpMovetalk', 'wpMovetalk', $this->moveTalk ) .
207                                         "</td>
208                                 </tr>"
209                         );
210                 }
211
212                 if ( $wgUser->isAllowed( 'suppressredirect' ) ) {
213                         $wgOut->addHTML( "
214                                 <tr>
215                                         <td></td>
216                                         <td class='mw-input' >" .
217                                                 Xml::checkLabel( wfMsg( 'move-leave-redirect' ), 'wpLeaveRedirect', 
218                                                         'wpLeaveRedirect', $this->leaveRedirect ) .
219                                         "</td>
220                                 </tr>"
221                         );
222                 }
223
224                 if ( $hasRedirects ) {
225                         $wgOut->addHTML( "
226                                 <tr>
227                                         <td></td>
228                                         <td class='mw-input' >" .
229                                                 Xml::checkLabel( wfMsg( 'fix-double-redirects' ), 'wpFixRedirects', 
230                                                         'wpFixRedirects', $this->fixRedirects ) .
231                                         "</td>
232                                 </tr>"
233                         );
234                 }
235
236                 if( ($this->oldTitle->hasSubpages() || $this->oldTitle->getTalkPage()->hasSubpages())
237                 && $this->oldTitle->userCan( 'move-subpages' ) ) {
238                         $wgOut->addHTML( "
239                                 <tr>
240                                         <td></td>
241                                         <td class=\"mw-input\">" .
242                                 Xml::checkLabel( wfMsg(
243                                                 $this->oldTitle->hasSubpages()
244                                                 ? 'move-subpages'
245                                                 : 'move-talk-subpages'
246                                         ),
247                                         'wpMovesubpages', 'wpMovesubpages',
248                                         # Don't check the box if we only have talk subpages to
249                                         # move and we aren't moving the talk page.
250                                         $this->moveSubpages && ($this->oldTitle->hasSubpages() || $this->moveTalk)
251                                 ) .
252                                         "</td>
253                                 </tr>"
254                         );
255                 }
256
257                 $watchChecked = $this->watch || $wgUser->getBoolOption( 'watchmoves' ) 
258                         || $this->oldTitle->userIsWatching();
259                 $wgOut->addHTML( "
260                         <tr>
261                                 <td></td>
262                                 <td class='mw-input'>" .
263                                         Xml::checkLabel( wfMsg( 'move-watch' ), 'wpWatch', 'watch', $watchChecked ) .
264                                 "</td>
265                         </tr>
266                                 {$confirm}
267                         <tr>
268                                 <td>&nbsp;</td>
269                                 <td class='mw-submit'>" .
270                                         Xml::submitButton( $movepagebtn, array( 'name' => $submitVar ) ) .
271                                 "</td>
272                         </tr>" .
273                         Xml::closeElement( 'table' ) .
274                         Xml::hidden( 'wpEditToken', $token ) .
275                         Xml::closeElement( 'fieldset' ) .
276                         Xml::closeElement( 'form' ) .
277                         "\n"
278                 );
279
280                 $this->showLogFragment( $this->oldTitle, $wgOut );
281
282         }
283
284         function doSubmit() {
285                 global $wgOut, $wgUser, $wgRequest, $wgMaximumMovedPages, $wgLang;
286                 global $wgFixDoubleRedirects;
287
288                 if ( $wgUser->pingLimiter( 'move' ) ) {
289                         $wgOut->rateLimited();
290                         return;
291                 }
292
293                 $ot = $this->oldTitle;
294                 $nt = $this->newTitle;
295
296                 # Delete to make way if requested
297                 if ( $wgUser->isAllowed( 'delete' ) && $this->deleteAndMove ) {
298                         $article = new Article( $nt );
299
300                         # Disallow deletions of big articles
301                         $bigHistory = $article->isBigDeletion();
302                         if( $bigHistory && !$nt->userCan( 'bigdelete' ) ) {
303                                 global $wgLang, $wgDeleteRevisionsLimit;
304                                 $this->showForm( array('delete-toobig', $wgLang->formatNum( $wgDeleteRevisionsLimit ) ) );
305                                 return;
306                         }
307
308                         // Delete an associated image if there is
309                         $file = wfLocalFile( $nt );
310                         if( $file->exists() ) {
311                                 $file->delete( wfMsgForContent( 'delete_and_move_reason' ), false );
312                         }
313
314                         // This may output an error message and exit
315                         $article->doDelete( wfMsgForContent( 'delete_and_move_reason' ) );
316                 }
317
318                 # don't allow moving to pages with # in
319                 if ( !$nt || $nt->getFragment() != '' ) {
320                         $this->showForm( 'badtitletext' );
321                         return;
322                 }
323
324                 if ( $wgUser->isAllowed( 'suppressredirect' ) ) {
325                         $createRedirect = $this->leaveRedirect;
326                 } else {
327                         $createRedirect = true;
328                 }
329
330                 $error = $ot->moveTo( $nt, true, $this->reason, $createRedirect );
331                 if ( $error !== true ) {
332                         # FIXME: show all the errors in a list, not just the first one
333                         $this->showForm( reset( $error ) );
334                         return;
335                 }
336
337                 if ( $wgFixDoubleRedirects && $this->fixRedirects ) {
338                         DoubleRedirectJob::fixRedirects( 'move', $ot, $nt );
339                 }
340
341                 wfRunHooks( 'SpecialMovepageAfterMove', array( &$this , &$ot , &$nt ) ) ;
342
343                 $wgOut->setPagetitle( wfMsg( 'pagemovedsub' ) );
344
345                 $oldUrl = $ot->getFullUrl( 'redirect=no' );
346                 $newUrl = $nt->getFullUrl();
347                 $oldText = $ot->getPrefixedText();
348                 $newText = $nt->getPrefixedText();
349                 $oldLink = "<span class='plainlinks'>[$oldUrl $oldText]</span>";
350                 $newLink = "<span class='plainlinks'>[$newUrl $newText]</span>";
351
352                 $msgName = $createRedirect ? 'movepage-moved-redirect' : 'movepage-moved-noredirect';
353                 $wgOut->addWikiMsg( 'movepage-moved', $oldLink, $newLink, $oldText, $newText );
354                 $wgOut->addWikiMsg( $msgName );
355
356                 # Now we move extra pages we've been asked to move: subpages and talk
357                 # pages.  First, if the old page or the new page is a talk page, we
358                 # can't move any talk pages: cancel that.
359                 if( $ot->isTalkPage() || $nt->isTalkPage() ) {
360                         $this->moveTalk = false;
361                 }
362
363                 if( !$ot->userCan( 'move-subpages' ) ) {
364                         $this->moveSubpages = false;
365                 }
366
367                 # Next make a list of id's.  This might be marginally less efficient
368                 # than a more direct method, but this is not a highly performance-cri-
369                 # tical code path and readable code is more important here.
370                 #
371                 # Note: this query works nicely on MySQL 5, but the optimizer in MySQL
372                 # 4 might get confused.  If so, consider rewriting as a UNION.
373                 #
374                 # If the target namespace doesn't allow subpages, moving with subpages
375                 # would mean that you couldn't move them back in one operation, which
376                 # is bad.  FIXME: A specific error message should be given in this
377                 # case.
378                 $dbr = wfGetDB( DB_MASTER );
379                 if( $this->moveSubpages && (
380                         MWNamespace::hasSubpages( $nt->getNamespace() ) || (
381                                 $this->moveTalk &&
382                                 MWNamespace::hasSubpages( $nt->getTalkPage()->getNamespace() )
383                         )
384                 ) ) {
385                         $conds = array(
386                                 'page_title LIKE '.$dbr->addQuotes( $dbr->escapeLike( $ot->getDBkey() ) . '/%' )
387                                         .' OR page_title = ' . $dbr->addQuotes( $ot->getDBkey() )
388                         );
389                         $conds['page_namespace'] = array();
390                         if( MWNamespace::hasSubpages( $nt->getNamespace() ) ) {
391                                 $conds['page_namespace'] []= $ot->getNamespace();
392                         }
393                         if( $this->moveTalk && MWNamespace::hasSubpages( $nt->getTalkPage()->getNamespace() ) ) {
394                                 $conds['page_namespace'] []= $ot->getTalkPage()->getNamespace();
395                         }
396                 } elseif( $this->moveTalk ) {
397                         $conds = array(
398                                 'page_namespace' => $ot->getTalkPage()->getNamespace(),
399                                 'page_title' => $ot->getDBKey()
400                         );
401                 } else {
402                         # Skip the query
403                         $conds = null;
404                 }
405
406                 $extraPages = array();
407                 if( !is_null( $conds ) ) {
408                         $extraPages = TitleArray::newFromResult(
409                                 $dbr->select( 'page',
410                                         array( 'page_id', 'page_namespace', 'page_title' ),
411                                         $conds,
412                                         __METHOD__
413                                 )
414                         );
415                 }
416
417                 $extraOutput = array();
418                 $skin = $wgUser->getSkin();
419                 $count = 1;
420                 foreach( $extraPages as $oldSubpage ) {
421                         if( $oldSubpage->getArticleId() == $ot->getArticleId() ) {
422                                 # Already did this one.
423                                 continue;
424                         }
425
426                         $newPageName = preg_replace(
427                                 '#^'.preg_quote( $ot->getDBKey(), '#' ).'#',
428                                 $nt->getDBKey(),
429                                 $oldSubpage->getDBKey()
430                         );
431                         if( $oldSubpage->isTalkPage() ) {
432                                 $newNs = $nt->getTalkPage()->getNamespace();
433                         } else {
434                                 $newNs = $nt->getSubjectPage()->getNamespace();
435                         }
436                         # Bug 14385: we need makeTitleSafe because the new page names may
437                         # be longer than 255 characters.
438                         $newSubpage = Title::makeTitleSafe( $newNs, $newPageName );
439                         if( !$newSubpage ) {
440                                 $oldLink = $skin->makeKnownLinkObj( $oldSubpage );
441                                 $extraOutput []= wfMsgHtml( 'movepage-page-unmoved', $oldLink,
442                                         htmlspecialchars(Title::makeName( $newNs, $newPageName )));
443                                 continue;
444                         }
445
446                         # This was copy-pasted from Renameuser, bleh.
447                         if ( $newSubpage->exists() && !$oldSubpage->isValidMoveTarget( $newSubpage ) ) {
448                                 $link = $skin->makeKnownLinkObj( $newSubpage );
449                                 $extraOutput []= wfMsgHtml( 'movepage-page-exists', $link );
450                         } else {
451                                 $success = $oldSubpage->moveTo( $newSubpage, true, $this->reason, $createRedirect );
452                                 if( $success === true ) {
453                                         if ( $this->fixRedirects ) {
454                                                 DoubleRedirectJob::fixRedirects( 'move', $oldSubpage, $newSubpage );
455                                         }
456                                         $oldLink = $skin->makeKnownLinkObj( $oldSubpage, '', 'redirect=no' );
457                                         $newLink = $skin->makeKnownLinkObj( $newSubpage );
458                                         $extraOutput []= wfMsgHtml( 'movepage-page-moved', $oldLink, $newLink );
459                                 } else {
460                                         $oldLink = $skin->makeKnownLinkObj( $oldSubpage );
461                                         $newLink = $skin->makeLinkObj( $newSubpage );
462                                         $extraOutput []= wfMsgHtml( 'movepage-page-unmoved', $oldLink, $newLink );
463                                 }
464                         }
465
466                         ++$count;
467                         if( $count >= $wgMaximumMovedPages ) {
468                                 $extraOutput []= wfMsgExt( 'movepage-max-pages', array( 'parsemag', 'escape' ), $wgLang->formatNum( $wgMaximumMovedPages ) );
469                                 break;
470                         }
471                 }
472
473                 if( $extraOutput !== array() ) {
474                         $wgOut->addHTML( "<ul>\n<li>" . implode( "</li>\n<li>", $extraOutput ) . "</li>\n</ul>" );
475                 }
476
477                 # Deal with watches (we don't watch subpages)
478                 if( $this->watch ) {
479                         $wgUser->addWatch( $ot );
480                         $wgUser->addWatch( $nt );
481                 } else {
482                         $wgUser->removeWatch( $ot );
483                         $wgUser->removeWatch( $nt );
484                 }
485         }
486
487         function showLogFragment( $title, &$out ) {
488                 $out->addHTML( Xml::element( 'h2', NULL, LogPage::logName( 'move' ) ) );
489                 LogEventsList::showLogExtract( $out, 'move', $title->getPrefixedText() );
490         }
491
492 }